nurasaki commited on
Commit
cb3dcae
·
1 Parent(s): 4c3a2c8

Initial commit (copied from hotel_tools space)

Browse files
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ venv/
2
+ .env
3
+ __pycache__
4
+ .qodo
5
+ data/
6
+ .DS_Store
7
+
app.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from gradio import ChatMessage
3
+
4
+ import json
5
+ from openai import OpenAI
6
+ from tools import tools, oitools
7
+ from dotenv import load_dotenv
8
+ from datetime import datetime
9
+ import os
10
+ import re
11
+
12
+ load_dotenv(".env")
13
+ HF_TOKEN = os.environ.get("HF_TOKEN")
14
+ BASE_URL = os.environ.get("BASE_URL")
15
+
16
+ SYSTEM_PROMPT_TEMPLATE = """You are an AI assistant designed to assist users with a **hotel booking and information system**. Your primary role is to provide detailed and accurate information about the hotel, including available accommodations, facilities, dining options, and reservation services. You can assist with **hotel room bookings**, modify or cancel reservations, and answer general inquiries about the hotel.
17
+
18
+ ### **Response Guidelines:**
19
+ - **Accuracy & Completeness:** Never provide information that is not explicitly available. If a required parameter (e.g., check-in date, check-out date, number of guests, or room type) is missing, ask the user to provide it before proceeding.
20
+ - **No Hallucinations:** Do not assume details. If you are unsure about something, ask the user for clarification.
21
+ - **Consistency:** Always respond in the same language as the user’s query.
22
+
23
+ ### **Booking Requirements:**
24
+ You **can only process hotel room reservations**.
25
+ If a user requests a **restaurant, flight, or any other type of reservation**, immediately inform them that you **cannot process those reservations**.
26
+ Do **not** ask for further details about restaurant or flight bookings. Simply clarify that you **only handle hotel room reservations**.
27
+
28
+ Today’s date is **{date}**."""
29
+
30
+
31
+ # print(json.dumps(oitools, indent=2))
32
+ client = OpenAI(
33
+ base_url=f"{BASE_URL}/v1",
34
+ api_key=HF_TOKEN
35
+ )
36
+
37
+ def today_date():
38
+ return datetime.today().strftime('%A, %B %d, %Y, %I:%M %p')
39
+
40
+
41
+ def clean_json_string(json_str):
42
+ return re.sub(r'[ ,}\s]+$', '', json_str) + '}'
43
+
44
+ def completion(history, model, system_prompt: str, tools=None):
45
+ messages = [{"role": "system", "content": system_prompt.format(date=today_date())}]
46
+ for msg in history:
47
+ if isinstance(msg, dict):
48
+ msg = ChatMessage(**msg)
49
+ if msg.role == "assistant" and hasattr(msg, "metadata") and msg.metadata:
50
+ tools_calls = json.loads(msg.metadata.get("title", "[]"))
51
+ # for tool_calls in tools_calls:
52
+ # tool_calls["function"]["arguments"] = json.loads(tool_calls["function"]["arguments"])
53
+ messages.append({"role": "assistant", "tool_calls": tools_calls, "content": ""})
54
+ messages.append({"role": "tool", "content": msg.content})
55
+ else:
56
+ messages.append({"role": msg.role, "content": msg.content})
57
+
58
+ request_params = {
59
+ "model": model,
60
+ "messages": messages,
61
+ "stream": True,
62
+ "max_tokens": 1000,
63
+ "temperature": 0.2,
64
+ "frequency_penalty": 1,
65
+ "extra_body": {"repetition_penalty": 1.1},
66
+ }
67
+ if tools:
68
+ request_params.update({"tool_choice": "auto", "tools": tools})
69
+
70
+ return client.chat.completions.create(**request_params)
71
+
72
+ def llm_in_loop(history, system_prompt, recursive):
73
+ try:
74
+ models = client.models.list()
75
+ model = models.data[0].id if models.data else "gpt-3.5-turbo"
76
+ except Exception as err:
77
+ gr.Warning("The model is initializing. Please wait; this may take 5 to 10 minutes ⏳.", duration=20)
78
+ raise err
79
+
80
+ arguments = ""
81
+ name = ""
82
+ chat_completion = completion(history=history, tools=oitools, model=model, system_prompt=system_prompt)
83
+ appended = False
84
+ # if chat_completion.choices and chat_completion.choices[0].message.tool_calls:
85
+ # call = chat_completion.choices[0].message.tool_calls[0]
86
+ # if hasattr(call.function, "name") and call.function.name:
87
+ # name = call.function.name
88
+ # if hasattr(call.function, "arguments") and call.function.arguments:
89
+ # arguments += call.function.arguments
90
+ # elif chat_completion.choices[0].message.content:
91
+ # if not appended:
92
+ # history.append(ChatMessage(role="assistant", content=""))
93
+ # appended = True
94
+ # history[-1].content += chat_completion.choices[0].message.content
95
+ # yield history[recursive:]
96
+ for chunk in chat_completion:
97
+ if chunk.choices and chunk.choices[0].delta.tool_calls:
98
+ call = chunk.choices[0].delta.tool_calls[0]
99
+ if hasattr(call.function, "name") and call.function.name:
100
+ name = call.function.name
101
+ if hasattr(call.function, "arguments") and call.function.arguments:
102
+ arguments += call.function.arguments
103
+ elif chunk.choices[0].delta.content:
104
+ if not appended:
105
+ history.append(ChatMessage(role="assistant", content=""))
106
+ appended = True
107
+ history[-1].content += chunk.choices[0].delta.content
108
+ yield history[recursive:]
109
+
110
+ arguments = clean_json_string(arguments) if arguments else "{}"
111
+ print(name, arguments)
112
+ arguments = json.loads(arguments)
113
+ print(name, arguments)
114
+ print("====================")
115
+ if appended:
116
+ recursive -= 1
117
+ if name:
118
+ result = f"💥 Error using tool {name}, tool doesn't exist" if name not in tools else str(tools[name].invoke(input=arguments))
119
+ result = json.dumps({name: result}, ensure_ascii=False)
120
+ # msg = ChatMessage(
121
+ # role="assistant",
122
+ # content="",
123
+ # metadata= {"title": f"🛠️ Using tool '{name}', arguments: {json.dumps(json_arguments, ensure_ascii=False)}"},
124
+ # options=[{"label":"tool_calls", "value": json.dumps([{"id": "call_FthC9qRpsL5kBpwwyw6c7j4k","function": {"arguments": arguments,"name": name},"type": "function"}])}]
125
+ # )
126
+ history.append(ChatMessage(role="assistant", content=result, metadata={"title": json.dumps([{"id": "call_id", "function": {"arguments": json.dumps(arguments, ensure_ascii=False), "name": name}, "type": "function"}], ensure_ascii=False)}))
127
+ yield history[recursive:]
128
+ yield from llm_in_loop(history, system_prompt, recursive - 1)
129
+
130
+ def respond(message, history, additional_inputs):
131
+ history.append(ChatMessage(role="user", content=message))
132
+ yield from llm_in_loop(history, additional_inputs, -1)
133
+
134
+ if __name__ == "__main__":
135
+ system_prompt = gr.Textbox(label="System prompt", value=SYSTEM_PROMPT_TEMPLATE, lines=3)
136
+ demo = gr.ChatInterface(respond, type="messages", additional_inputs=[system_prompt])
137
+ demo.launch()
managed_context/metadata.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"current_schema_version":"0.0.1"}
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio==5.23.0
2
+ openai==1.68.2
3
+ python-dotenv==1.1.0
4
+ langchain-community==0.3.20
5
+ langchain-core==0.3.48
6
+ faiss-cpu==1.10.0
7
+ faiss-gpu==1.7.2
8
+ sentence-transformers==3.4.1
test_suite_analysis/metadata.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"current_schema_version":"0.0.1"}
tools.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from abc import ABC, abstractmethod
3
+ from typing import get_origin, get_args
4
+ import os
5
+ # from langchain.tools import tool
6
+ import json
7
+ from pydantic import BaseModel, Field
8
+ from typing import Dict, Union
9
+ import random
10
+ import copy
11
+ from types import UnionType
12
+
13
+ from langchain_community.vectorstores import FAISS
14
+ from langchain_community.embeddings import HuggingFaceEmbeddings
15
+
16
+ class VectorStore:
17
+ def __init__(self, embeddings_model, vectorstore):
18
+ embeddings = HuggingFaceEmbeddings(model_name=embeddings_model)
19
+ self.vectore_store = FAISS.load_local(vectorstore, embeddings, allow_dangerous_deserialization=True)
20
+
21
+ def get_context(self, instruction, number_of_contexts=2):
22
+ documentos = self.vectore_store.similarity_search_with_score(instruction, k=number_of_contexts)
23
+ return self._beautiful_context(documentos)
24
+
25
+ def _beautiful_context(self, docs):
26
+ context = ""
27
+ for doc in docs:
28
+ context += doc[0].page_content + "\n"
29
+ return context
30
+
31
+ def read_json(data_path: str) -> tuple[list, dict]:
32
+ try:
33
+ with open(data_path, 'r', encoding="utf-8") as f:
34
+ data = [json.loads(line) for line in f.readlines()]
35
+ except:
36
+ with open(data_path, 'r', encoding="utf-8") as f:
37
+ data = json.loads(f.read())
38
+ return data
39
+
40
+ json_data = read_json("data/val_de_nuria.json")
41
+ reservations = {}
42
+
43
+ class ToolBase(BaseModel, ABC):
44
+ @abstractmethod
45
+ def invoke(cls, input: Dict):
46
+ pass
47
+
48
+ @classmethod
49
+ def to_openai_tool(cls):
50
+ """
51
+ Extracts function metadata from a Pydantic class, including function name, parameters, and descriptions.
52
+ Formats it into a structure similar to OpenAI's function metadata.
53
+ """
54
+ function_metadata = {
55
+ "type": "function",
56
+ "function": {
57
+ "name": cls.__name__, # Function name is same as the class name, in lowercase
58
+ "description": cls.__doc__.strip(),
59
+ "parameters": {
60
+ "type": "object",
61
+ "properties": {},
62
+ "required": [],
63
+ },
64
+ },
65
+ }
66
+
67
+ # Iterate over the fields to add them to the parameters
68
+ for field_name, field_info in cls.model_fields.items():
69
+ # Field properties
70
+ field_type = "string" # Default to string, will adjust if it's a different type
71
+ annotation = field_info.annotation.__args__[0] if getattr(field_info.annotation, "__origin__", None) is Union else field_info.annotation
72
+
73
+ has_none = False
74
+ if get_origin(annotation) is UnionType: # Check if it's a Union type
75
+ args = get_args(annotation)
76
+ if type(None) in args:
77
+ has_none = True
78
+ args = [arg for arg in args if type(None) != arg]
79
+ if len(args) > 1:
80
+ raise TypeError("It can be union of only a valid type (str, int, bool, etc) and None")
81
+ elif len(args) == 0:
82
+ raise TypeError("There must be a valid type (str, int, bool, etc) not only None")
83
+ else:
84
+ annotation = args[0]
85
+
86
+ if annotation == int:
87
+ field_type = "integer"
88
+ elif annotation == bool:
89
+ field_type = "boolean"
90
+
91
+ # Add the field's description and type to the properties
92
+ function_metadata["function"]["parameters"]["properties"][field_name] = {
93
+ "type": field_type,
94
+ "description": field_info.description,
95
+ }
96
+
97
+ # Determine if the field is required (not Optional or None)
98
+ if field_info.is_required():
99
+ function_metadata["function"]["parameters"]["required"].append(field_name)
100
+ has_none = True
101
+
102
+ # If there's an enum (like for `unit`), add it to the properties
103
+ if hasattr(field_info, 'default') and field_info.default is not None and isinstance(field_info.default, list):
104
+ function_metadata["function"]["parameters"]["properties"][field_name]["enum"] = field_info.default
105
+ if not has_none:
106
+ function_metadata["function"]["parameters"]["required"].append(field_name)
107
+
108
+ return function_metadata
109
+
110
+ tools: Dict[str, ToolBase] = {}
111
+ oitools = []
112
+ vector_store = VectorStore(embeddings_model="BAAI/bge-m3", vectorstore="data/vs")
113
+
114
+ def tool_register(cls: BaseModel):
115
+ oaitool = cls.to_openai_tool()
116
+
117
+ oitools.append(oaitool)
118
+ tools[oaitool["function"]["name"]] = cls
119
+
120
+ # @tool_register
121
+ class hotel_description(ToolBase):
122
+ """Retrieves basic information about the hotel, such as its name, address, contact details, and overall description."""
123
+
124
+ @classmethod
125
+ def invoke(cls, input: Dict) -> str:
126
+ return """### **Nou Vall de Núria – Brief Description**
127
+ Nestled in the stunning **Vall de Núria** in the Pyrenees, **Nou Vall de Núria** offers a perfect blend of comfort and adventure. Guests can enjoy breathtaking mountain views, premium accommodations, and excellent facilities, including an outdoor pool, gym, and sauna.
128
+ The hotel features **two dining options**, serving traditional Catalan cuisine and refreshing drinks. Accommodations range from **cozy standard rooms to luxurious suites and fully equipped apartments**, catering to couples, families, and groups.
129
+ For an unforgettable stay, guests can choose from **special packages**, including family-friendly stays, romantic getaways, ski adventures, and relaxation retreats. Outdoor enthusiasts can explore **hiking trails, ski slopes, and fishing spots** in the surrounding natural beauty.
130
+ Whether for relaxation or adventure, **Nou Vall de Núria** promises a unique and memorable experience."""
131
+
132
+
133
+ @tool_register
134
+ class get_documents(ToolBase):
135
+ """
136
+ Retrieves general information about a region, its cities, activities, tourism, or surrounding areas based on query.
137
+ """
138
+ query: str = Field(description="An enhanced user query optimized for retrieving information")
139
+
140
+ @classmethod
141
+ def invoke(cls, input: Dict) -> str:
142
+ query = input.get("query", None)
143
+ if not query:
144
+ return "Missing required argument: query."
145
+
146
+ # return "We are currently working on it. You can't use this tool right now—please try again later. Thank you for your patience!"
147
+ return vector_store.get_context(query)
148
+
149
+
150
+ @tool_register
151
+ class packs(ToolBase):
152
+ """Provides a list of available activity pack at the hotel."""
153
+
154
+ @classmethod
155
+ def invoke(cls, input: Dict) -> str:
156
+ return json_data["packs"]
157
+
158
+ @tool_register
159
+ class hotel_facilities(ToolBase):
160
+ """Provides a list of available general facilities at the hotel, which could include amenities like a spa, pool, gym, conference rooms, etc."""
161
+
162
+ @classmethod
163
+ def invoke(cls, input: Dict) -> str:
164
+ return json_data["general_facilities"]
165
+
166
+ @tool_register
167
+ class restaurants_details(ToolBase):
168
+ """Provides a list of available restaurants with their details."""
169
+
170
+ @classmethod
171
+ def invoke(cls, input: Dict) -> str:
172
+ """
173
+ Play a playlist by its name, starting with the first or a random song.
174
+ """
175
+
176
+ return json_data["restaurants"]
177
+
178
+
179
+ @tool_register
180
+ class restaurant_details(ToolBase):
181
+ """Retrieves detailed information about a specific restaurant in the hotel, including its menu, ambiance, operating hours, and special features."""
182
+
183
+ name: str = Field(default=[res["name"] for res in json_data["restaurants"]], description="Name of the resaturant")
184
+
185
+ @classmethod
186
+ def invoke(cls, input: Dict) -> str:
187
+ """
188
+ Play a playlist by its name, starting with the first or a random song.
189
+ """
190
+
191
+ instance = cls(**input)
192
+ name = instance.name
193
+
194
+ restaurante = [res for res in json_data["restaurants"] if res["name"] == name]
195
+ if restaurante:
196
+ return restaurante
197
+ else:
198
+ return f"We don't have any restaurante with the name: {name}"
199
+
200
+
201
+ @tool_register
202
+ class rooms_information(ToolBase):
203
+ """
204
+ Provides a list of available hotel rooms, including brief descriptions of each room type.
205
+ """
206
+ @classmethod
207
+ def invoke(cls, input: Dict) -> str:
208
+ return json_data["room_types"]
209
+
210
+
211
+ @tool_register
212
+ class check_room_availability(ToolBase):
213
+ """
214
+ Checks if a specified room type is available between the provided check-in and check-out dates for a given number of guests.
215
+ """
216
+ room_type: str = Field(default=list(json_data["room_types"].keys()), description="The type of room to check for availability.")
217
+ reservation_start_date: str = Field(description="The check-in date for the reservation, formatted as 'YYYY-MM-DD' (e.g., '2025-04-01')")
218
+ reservation_end_date: str = Field(description="The check-out date for the reservation, formatted as 'YYYY-MM-DD' (e.g., '2025-04-05')")
219
+ guests: int = Field(description="The number of guests that will occupy the room.")
220
+
221
+ @classmethod
222
+ def invoke(cls, input: Dict) -> str:
223
+
224
+ room_type = input.get("room_type", None)
225
+ reservation_start_date = input.get("reservation_start_date", None)
226
+ reservation_end_date = input.get("reservation_end_date", None)
227
+ guests = input.get("guests", None)
228
+
229
+ missing = []
230
+ if not room_type:
231
+ missing.append("room_type")
232
+ if not reservation_start_date:
233
+ missing.append("reservation_start_date")
234
+ if not reservation_end_date:
235
+ missing.append("reservation_end_date")
236
+ if not guests:
237
+ missing.append("guests")
238
+
239
+ if len(missing):
240
+ value = ", ".join(missing)
241
+ return f"Unable to check the room availability. The following required arguments are missing:{value}."
242
+
243
+ instance = cls(**input)
244
+ room_type = instance.room_type
245
+ reservation_start_date = instance.reservation_start_date
246
+ reservation_end_date = instance.reservation_end_date
247
+ guests = instance.guests
248
+
249
+ rooms = [room for room in json_data["accomodations"]["rooms"] if room_type in room["type"]]
250
+ if len(rooms) == 0:
251
+ return f"There is no room exists with room type {room_type}"
252
+
253
+ rooms2 = [room for room in rooms if guests <= room["number_of_guests"]]
254
+ if len(rooms2) == 0:
255
+ max_guests = json_data["room_types"][room_type]["number_of_guests"]
256
+ return f"The number of guest is superior then the availibilty, maximum is {max_guests}"
257
+
258
+ return rooms2
259
+
260
+
261
+
262
+
263
+ @tool_register
264
+ class make_reservation(ToolBase):
265
+ """
266
+ Creates a new reservation for the hotel by booking a room of the specified type for the desired dates, and associating the booking with a user.
267
+ """
268
+
269
+ user_name: str = Field(description="The name of user who is doing the reservation.")
270
+ room_type: str = Field(default=list(json_data["room_types"].keys()), description="The type of room being reserved.")
271
+ reservation_start_date: str = Field(description="The check-in date for the reservation, formatted as 'YYYY-MM-DD' (e.g., '2025-04-01')")
272
+ reservation_end_date: str = Field(description="The check-out date for the reservation, formatted as 'YYYY-MM-DD' (e.g., '2025-04-05')")
273
+ guests: int = Field(description="The total number of guests for the reservation. Must be a positive integer.")
274
+
275
+ @classmethod
276
+ def invoke(cls, input: Dict) -> str:
277
+
278
+ room_type = input.get("room_type", None)
279
+ reservation_start_date = input.get("reservation_start_date", None)
280
+ reservation_end_date = input.get("reservation_end_date", None)
281
+ guests = input.get("guests", None)
282
+ user_name = input.get("user_name", None)
283
+
284
+ missing = []
285
+ if not room_type:
286
+ missing.append("room_type")
287
+ if not reservation_start_date:
288
+ missing.append("reservation_start_date")
289
+ if not reservation_end_date:
290
+ missing.append("reservation_end_date")
291
+ if not guests:
292
+ missing.append("guests")
293
+ if not user_name:
294
+ missing.append("user_name")
295
+
296
+ if len(missing):
297
+ value = ", ".join(missing)
298
+ return f"Unable to complete the reservation. The following required arguments are missing:{value}."
299
+
300
+
301
+ instance = cls(**input)
302
+ room_type = instance.room_type
303
+ reservation_start_date = instance.reservation_start_date
304
+ reservation_end_date = instance.reservation_end_date
305
+ guests = instance.guests
306
+ user_name = instance.user_name.lower()
307
+
308
+ rooms = [room for room in json_data["accomodations"]["rooms"] if room_type in room["type"]]
309
+ if len(rooms) == 0:
310
+ return f"There is no room exists with room type {room_type}"
311
+
312
+ rooms2 = [room for room in rooms if guests <= room["number_of_guests"]]
313
+ if len(rooms2) == 0:
314
+ max_guests = json_data["room_types"][room_type]["number_of_guests"]
315
+ return f"The number of guest is superior then the availibilty, maximum is {max_guests}"
316
+
317
+ room = rooms2[random.randint(0, len(rooms2) - 1)]
318
+
319
+ rand = int(random.randint(0,10000000))
320
+ while rand in reservations:
321
+ rand = int(random.randint(0,10000000))
322
+
323
+ tmp_data = {
324
+ "status": "Reserved",
325
+ "room_number": room["room_number"],
326
+ "room_type": room_type,
327
+ "reservation_start_date": reservation_start_date,
328
+ "reservation_end_date": reservation_end_date,
329
+ "guests": guests,
330
+ "reservation_id": rand,
331
+ "user_name": user_name,
332
+ }
333
+
334
+ reservations[rand] = tmp_data
335
+
336
+ return json.dumps(tmp_data)
337
+
338
+ @tool_register
339
+ class cancel_reservation(ToolBase):
340
+ """Playing a specific playlist by its name."""
341
+
342
+ reservation_id: int = Field(description="The unique identifier of the reservation to be canceled.")
343
+
344
+ @classmethod
345
+ def invoke(cls, input: Dict) -> str:
346
+
347
+ reservation_id = input.get("reservation_id", None)
348
+
349
+ missing = []
350
+ if not reservation_id:
351
+ missing.append("reservation_id")
352
+
353
+ if len(missing):
354
+ value = ", ".join(missing)
355
+ return f"Unable to cancel the reservation. The following required arguments are missing:{value}."
356
+
357
+ instance = cls(**input)
358
+ reservation_id = instance.reservation_id
359
+
360
+
361
+
362
+ if reservation_id not in reservations:
363
+ return f"There is no reservations with the id: {reservation_id}"
364
+
365
+ reservations.pop(reservation_id)
366
+ return f"The reservation {reservation_id} is cancled correctly"
367
+
368
+ @tool_register
369
+ class modify_reservation(ToolBase):
370
+ """
371
+ Allows a user to modify an existing reservation by updating the check-in/check-out dates or changing the room type, subject to availability.
372
+ """
373
+
374
+
375
+ new_room_type: str | None = Field(default=list(json_data["room_types"].keys()), description=f"The type of new room to be modified, if {None} same room will be modified.")
376
+ new_reservation_start_date: str = Field(default=None, description="New check out date in format DD/MM/YYYY")
377
+ new_reservation_end_date: str = Field(default=None, description="New check out date in format DD/MM/YYYY")
378
+ guests: int = Field(default=None, description="New number of guests for the reservation.")
379
+ reservation_id: int = Field(description="The unique identifier of the reservation to be modified.")
380
+
381
+ @classmethod
382
+ def invoke(cls, input: Dict) -> str:
383
+
384
+ reservation_id = input.get("reservation_id", None)
385
+
386
+ missing = []
387
+ if not reservation_id:
388
+ missing.append("reservation_id")
389
+
390
+ instance = cls(**input)
391
+ new_room_type = instance.new_room_type
392
+ new_reservation_start_date = instance.new_reservation_start_date
393
+ new_reservation_end_date = instance.new_reservation_end_date
394
+ guests = instance.guests
395
+ reservation_id = instance.reservation_id
396
+
397
+ if len(missing):
398
+ value = ", ".join(missing)
399
+ return f"Unable to modify the reservation. The following required arguments are missing:{value}."
400
+
401
+ if not (new_room_type or new_reservation_start_date or new_reservation_end_date or guests):
402
+ return "Unable to modify the reservation. One of the following arguments must be passed: new_room_type, new_reservation_start_date, new_reservation_end_date, guests."
403
+
404
+
405
+ if reservation_id not in reservations:
406
+ return f"There is no reservations with the id: {reservation_id}"
407
+
408
+ if new_room_type or guests:
409
+ rooms = [room for room in json_data["room_types"] if new_room_type in room["type"]]
410
+ if len(rooms) == 0:
411
+ return f"There is no room exists with room type {new_room_type}"
412
+
413
+ rooms = [room for room in rooms if guests <= room["number_of_guests"]]
414
+ if len(rooms) == 0:
415
+ max_guests = json_data["room_types"][new_room_type]["number_of_guests"]
416
+ return f"The number of guest is superior then the availibilty, maximum is {max_guests}"
417
+
418
+ room = rooms[random.randint(0, len(rooms) - 1)]
419
+ room_number = room["room_number"]
420
+ else:
421
+ room_number = reservations[reservation_id]["room_number"]
422
+
423
+
424
+ reservations[reservation_id]["guests"] = guests if guests else reservations[reservation_id]["guests"]
425
+ reservations[reservation_id]["reservation_start_date"] = new_reservation_start_date if new_reservation_start_date else reservations[reservation_id]["reservation_start_date"]
426
+ reservations[reservation_id]["reservation_end_date"] = new_reservation_end_date if new_reservation_end_date else reservations[reservation_id]["reservation_end_date"]
427
+ reservations[reservation_id]["room_type"] = new_room_type if new_room_type else reservations[reservation_id]["room_type"]
428
+ reservations[reservation_id]["room_number"] = room_number
429
+ tmp_data = reservations[reservation_id]
430
+ return f"The reservation {reservation_id} is modified correctly: {json.dumps(tmp_data)}"
431
+
432
+ @tool_register
433
+ class reservation_details(ToolBase):
434
+ """Playing a specific playlist by its name."""
435
+
436
+ reservation_id: int = Field(description="Id of the reservation")
437
+
438
+ @classmethod
439
+ def invoke(cls, input: Dict) -> str:
440
+ reservation_id = input.get("reservation_id", None)
441
+
442
+ missing = []
443
+ if not reservation_id:
444
+ missing.append("reservation_id")
445
+
446
+ if len(missing):
447
+ value = ", ".join(missing)
448
+ return f"Unable to get the details. The following required arguments are missing:{value}."
449
+
450
+ instance = cls(**input)
451
+ reservation_id = instance.reservation_id
452
+
453
+ if reservation_id not in reservations:
454
+ return f"There is no reservations with the id: {reservation_id}"
455
+
456
+
457
+ tmp_data = copy.deepcopy(reservations[reservation_id])
458
+ return json.dumps(tmp_data)