Ritvik commited on
Commit
4a38fbc
Β·
1 Parent(s): d097acf

Save local changes before pull

Browse files
Files changed (4) hide show
  1. agent.py +4 -15
  2. app.py +14 -64
  3. chatbot.py +10 -8
  4. tools.py +168 -51
agent.py CHANGED
@@ -2,16 +2,12 @@ import os
2
  from langchain_groq import ChatGroq
3
  from langchain.agents import initialize_agent, AgentType
4
  from memory import memory
5
- from tools import tools # Import Flight Booking Tool
6
 
7
- # Load API Key
8
  API_KEY = os.getenv("API_KEY")
9
-
10
- # Ensure API Key is set
11
  if not API_KEY:
12
- raise ValueError("API_KEY is not set. Please define it in your environment variables.")
13
 
14
- # Initialize the LLM (Groq's Mixtral)
15
  llm = ChatGroq(
16
  groq_api_key=API_KEY,
17
  model_name="mixtral-8x7b-32768",
@@ -19,17 +15,10 @@ llm = ChatGroq(
19
  max_tokens=512,
20
  )
21
 
22
- # Initialize the conversational agent with Flight Booking Tool
23
  agent = initialize_agent(
24
  tools=tools,
25
  llm=llm,
26
  agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
27
- verbose=True, # Hide debug output
28
  memory=memory,
29
- )
30
-
31
- # πŸ›  Custom Logging Function to Improve Execution
32
- def log_agent_action(prompt, response):
33
- print(f"\n🟒 **User Query:** {prompt}")
34
- print(f"πŸ”΅ **Agent Thought Process:**")
35
- print(f"βœ… **Final Response:** {response}")
 
2
  from langchain_groq import ChatGroq
3
  from langchain.agents import initialize_agent, AgentType
4
  from memory import memory
5
+ from tools import tools
6
 
 
7
  API_KEY = os.getenv("API_KEY")
 
 
8
  if not API_KEY:
9
+ raise ValueError("API_KEY is not set.")
10
 
 
11
  llm = ChatGroq(
12
  groq_api_key=API_KEY,
13
  model_name="mixtral-8x7b-32768",
 
15
  max_tokens=512,
16
  )
17
 
 
18
  agent = initialize_agent(
19
  tools=tools,
20
  llm=llm,
21
  agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
22
+ verbose=True,
23
  memory=memory,
24
+ )
 
 
 
 
 
 
app.py CHANGED
@@ -1,11 +1,10 @@
1
  import gradio as gr
2
  from chatbot import respond
3
 
4
- # Custom CSS for a sleek, modern AI chatbot design
5
  custom_css = """
6
  .gradio-container {
7
  font-family: 'Arial', sans-serif;
8
- background: #1a202c; /* Dark, neutral background like Grok's interface */
9
  color: #e2e8f0;
10
  padding: 20px;
11
  }
@@ -14,100 +13,51 @@ custom_css = """
14
  padding: 15px;
15
  margin: 10px 0;
16
  max-width: 80%;
17
- transition: opacity 0.3s;
18
  }
19
  .chatbot .message.bot {
20
- background: #ffffff; /* Clean white for bot responses */
21
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
22
  color: #343a40;
23
  }
24
  .chatbot .message.user {
25
- background: #ff8c00; /* Vibrant orange for user messages, matching Grok's branding */
26
  color: white;
27
- margin-left: auto; /* Align user messages to the right */
28
- }
29
- .footer {
30
- font-size: 12px;
31
- color: #a0aec0;
32
- text-align: center;
33
- margin-top: 20px;
34
- }
35
- .header {
36
- background: linear-gradient(to right, #ff8c00, #ff4500); /* Orange gradient for branding */
37
- padding: 20px;
38
- border-radius: 15px;
39
- margin-bottom: 20px;
40
- color: white;
41
- text-align: center;
42
- box-shadow: 0 4px 12px rgba(255, 140, 0, 0.3);
43
- font-family: 'Arial Black', sans-serif;
44
  }
45
  .textbox {
46
- background: #2d3748; /* Dark input background */
47
  border: 2px solid #4a5568;
48
  border-radius: 15px;
49
  color: #e2e8f0;
50
- padding: 10px;
51
- font-size: 14px;
52
- transition: border-color 0.3s;
53
- }
54
- .textbox:focus {
55
- border-color: #ff8c00; /* Orange highlight on focus */
56
- outline: none;
57
  }
58
  .button {
59
- background: #ff8c00; /* Orange button for consistency */
60
  color: white;
61
- border: none;
62
- padding: 12px 24px;
63
  border-radius: 15px;
64
- box-shadow: 0 4px 6px rgba(255, 140, 0, 0.3);
65
- font-weight: bold;
66
- transition: transform 0.3s, background 0.3s;
67
  }
68
  .button:hover {
69
- background: #ff4500; /* Darker orange on hover */
70
- transform: scale(1.05);
71
- }
72
- .slider, .dropdown {
73
- background: #2d3748;
74
- border: 2px solid #4a5568;
75
- border-radius: 15px;
76
- color: #e2e8f0;
77
- padding: 8px;
78
- font-size: 14px;
79
- }
80
- .slider:focus, .dropdown:focus {
81
- border-color: #ff8c00;
82
- outline: none;
83
  }
84
  """
85
 
86
- # Welcome message with sleek, modern design
87
-
88
- def enhanced_respond(message, history, system_message, max_tokens, travel_preference):
89
- # Call the existing respond function with fixed temperature/top-p for simplicity
90
- response = respond(message, history, system_message, max_tokens, temperature=0.7, top_p=0.95)
91
-
92
- # Add a friendly touch if no specific response is generated
93
  if "❌" in response or "not found" in response.lower():
94
- response += "\n\nπŸ’‘ **Tip:** Try something like 'Flights from Chicago to Miami in June' or ask me for travel ideas!"
95
-
96
  return response
97
 
98
- # Gradio Interface with Sleek, Modern AI Chatbot UI/UX
99
  gr.ChatInterface(
100
  fn=enhanced_respond,
101
  chatbot=gr.Chatbot(height=700, label="Chat with Travelo"),
102
- textbox=gr.Textbox(placeholder="Type your travel query here...", lines=1, container=True, elem_classes="textbox"),
103
- submit_btn=gr.Button("✈️ Send", elem_classes="button"),
104
  additional_inputs=[
105
  gr.Textbox(
106
  value="You are an AI-powered travel assistant named Travelo. You are developed by Travelo LLC. Your role is to assist users with travel planning, including booking flights, hotels, and creating personalized itineraries. Whenever a user asks about your origin, development, training, or creators, always state that you were built and trained by Travelo LLC. Do not mention OpenAI or any third-party AI providers. Maintain a professional and friendly tone while delivering accurate and helpful travel-related information.",
107
  label="System Message",
108
  visible=False
109
  ),
110
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max Response Length", elem_classes="slider"),
111
  ],
112
  title="Travelo - Your Ultimate Travel AI",
113
  css=custom_css
 
1
  import gradio as gr
2
  from chatbot import respond
3
 
 
4
  custom_css = """
5
  .gradio-container {
6
  font-family: 'Arial', sans-serif;
7
+ background: #1a202c;
8
  color: #e2e8f0;
9
  padding: 20px;
10
  }
 
13
  padding: 15px;
14
  margin: 10px 0;
15
  max-width: 80%;
 
16
  }
17
  .chatbot .message.bot {
18
+ background: #ffffff;
 
19
  color: #343a40;
20
  }
21
  .chatbot .message.user {
22
+ background: #ff8c00;
23
  color: white;
24
+ margin-left: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  .textbox {
27
+ background: #2d3748;
28
  border: 2px solid #4a5568;
29
  border-radius: 15px;
30
  color: #e2e8f0;
 
 
 
 
 
 
 
31
  }
32
  .button {
33
+ background: #ff8c00;
34
  color: white;
 
 
35
  border-radius: 15px;
 
 
 
36
  }
37
  .button:hover {
38
+ background: #ff4500;
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
  """
41
 
42
+ # Fixed to match chatbot.respond signature (6 arguments)
43
+ def enhanced_respond(message, history, system_message, max_tokens, temperature=0.7, top_p=0.95):
44
+ response = respond(message, history, system_message, max_tokens, temperature, top_p)
 
 
 
 
45
  if "❌" in response or "not found" in response.lower():
46
+ response += "\n\nπŸ’‘ **Tip:** Try 'Flights from Chicago to Miami in June' or 'Hotels in Paris from February 25 2025 to February 28 2025'!"
 
47
  return response
48
 
 
49
  gr.ChatInterface(
50
  fn=enhanced_respond,
51
  chatbot=gr.Chatbot(height=700, label="Chat with Travelo"),
52
+ textbox=gr.Textbox(placeholder="Type your travel query here...", lines=1),
53
+ submit_btn=gr.Button("✈️ Send"),
54
  additional_inputs=[
55
  gr.Textbox(
56
  value="You are an AI-powered travel assistant named Travelo. You are developed by Travelo LLC. Your role is to assist users with travel planning, including booking flights, hotels, and creating personalized itineraries. Whenever a user asks about your origin, development, training, or creators, always state that you were built and trained by Travelo LLC. Do not mention OpenAI or any third-party AI providers. Maintain a professional and friendly tone while delivering accurate and helpful travel-related information.",
57
  label="System Message",
58
  visible=False
59
  ),
60
+ gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max Response Length")
61
  ],
62
  title="Travelo - Your Ultimate Travel AI",
63
  css=custom_css
chatbot.py CHANGED
@@ -4,20 +4,22 @@ from agent import agent
4
 
5
 
6
  def respond(message, history, system_message, max_tokens, temperature, top_p):
7
- # Ensure system message is in memory
8
  memory.chat_memory.add_message(SystemMessage(content=system_message))
9
-
10
- # Add conversation history to memory
11
  for user_input, bot_response in history:
12
  if user_input:
13
  memory.chat_memory.add_message(HumanMessage(content=user_input))
14
  if bot_response:
15
  memory.chat_memory.add_message(AIMessage(content=bot_response))
16
-
17
- # Process new message
18
  memory.chat_memory.add_message(HumanMessage(content=message))
19
 
20
- # Generate response using the agent
21
- response = agent.run(message)
 
 
 
 
 
22
 
23
- return response
 
 
 
4
 
5
 
6
  def respond(message, history, system_message, max_tokens, temperature, top_p):
 
7
  memory.chat_memory.add_message(SystemMessage(content=system_message))
 
 
8
  for user_input, bot_response in history:
9
  if user_input:
10
  memory.chat_memory.add_message(HumanMessage(content=user_input))
11
  if bot_response:
12
  memory.chat_memory.add_message(AIMessage(content=bot_response))
 
 
13
  memory.chat_memory.add_message(HumanMessage(content=message))
14
 
15
+ if "flight" in message.lower():
16
+ message = f"Use the 'Flight Booking' tool. {message}"
17
+ elif "hotel" in message.lower() or "stay" in message.lower():
18
+ message = f"Use the 'Hotel Booking' tool. {message}"
19
+
20
+ response_dict = agent.invoke({"input": message}) # Updated from .run to .invoke
21
+ response = response_dict["output"] if "output" in response_dict else str(response_dict)
22
 
23
+ if "❌" in response:
24
+ response += "\n\nWould you like me to retry with different dates (e.g., 'hotels in Paris from March 1 2025 to March 4 2025') or assist with something else (e.g., flights)?"
25
+ return response
tools.py CHANGED
@@ -1,61 +1,188 @@
1
  import os
2
  import dateparser
 
3
  from amadeus import Client, ResponseError
4
  from langchain.tools import Tool
 
 
 
 
5
 
6
- # Initialize Amadeus API Client
7
  AMADEUS_API_KEY = os.getenv("AMADEUS_API_KEY")
8
  AMADEUS_API_SECRET = os.getenv("AMADEUS_API_SECRET")
 
 
9
 
10
- amadeus = Client(
11
- client_id=AMADEUS_API_KEY,
12
- client_secret=AMADEUS_API_SECRET
13
- )
14
 
15
- # 🌍 Convert City Name to IATA Code
16
  def get_airport_code(city_name: str):
17
- """Find the IATA airport code for a given city."""
18
  try:
19
- response = amadeus.reference_data.locations.get(
20
- keyword=city_name,
21
- subType="AIRPORT,CITY"
22
- )
23
- if response.data:
24
- return response.data[0]["iataCode"]
25
- return None
26
  except ResponseError:
27
  return None
28
 
29
- # ✈️ Get Full Airline Name from Code
30
  def get_airline_name(airline_code: str):
31
- """Get full airline name using Amadeus API."""
32
  try:
33
  response = amadeus.reference_data.airlines.get(airlineCodes=airline_code)
34
- if response.data:
35
- return response.data[0]["businessName"]
36
- return airline_code # Fallback to code if not found
37
  except ResponseError:
38
  return airline_code
39
 
40
- # πŸ•‘ Format Flight Duration
41
  def format_duration(duration: str):
42
- """Convert ISO 8601 duration (PT20H25M) into readable format (20h 25m)."""
43
- duration = duration.replace("PT", "").replace("H", "h ").replace("M", "m")
44
- return duration.strip()
45
 
46
- # πŸ”— Generate Booking Link
47
  def generate_booking_link(from_iata: str, to_iata: str, departure_date: str):
48
- """Generate a booking link using Google Flights."""
49
  return f"https://www.google.com/flights?hl=en#flt={from_iata}.{to_iata}.{departure_date};c:USD;e:1;s:0;sd:1;t:f"
50
 
51
- # πŸ›« Flight Search Tool (Now Gradio-Compatible with Simple Ticket Style)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  def search_flights(query: str):
53
- """Search for flights using Amadeus API and return plain Markdown output for Gradio."""
54
  try:
55
  words = query.lower().split()
56
  from_city, to_city, date_phrase = None, None, None
57
 
58
- # Extract "from", "to", and date information
59
  if "from" in words and "to" in words:
60
  from_index = words.index("from") + 1
61
  to_index = words.index("to") + 1
@@ -64,41 +191,33 @@ def search_flights(query: str):
64
 
65
  date_phrase = " ".join(words[words.index("in") + 1:]) if "in" in words else None
66
 
67
- # Validate extracted details
68
  if not from_city or not to_city:
69
  return "❌ Could not detect valid departure and destination cities. Please use 'from <city> to <city>'."
70
 
71
- # Convert city names to IATA codes
72
  from_iata = get_airport_code(from_city)
73
  to_iata = get_airport_code(to_city)
74
-
75
  if not from_iata or not to_iata:
76
- return f"❌ Could not find airport codes for {from_city} or {to_city}. Please check spelling."
77
 
78
- # Convert date phrase to YYYY-MM-DD
79
  departure_date = dateparser.parse(date_phrase) if date_phrase else None
80
  if not departure_date:
81
  return "❌ Could not understand the travel date. Use formats like 'next week' or 'on May 15'."
82
 
83
  departure_date_str = departure_date.strftime("%Y-%m-%d")
84
 
85
- # Fetch flight offers from Amadeus
86
  response = amadeus.shopping.flight_offers_search.get(
87
  originLocationCode=from_iata,
88
  destinationLocationCode=to_iata,
89
  departureDate=departure_date_str,
90
  adults=1,
91
- max=5
92
  )
93
-
94
  flights = response.data
95
  if not flights:
96
  return f"❌ No flights found from {from_city} to {to_city} on {departure_date_str}."
97
 
98
- # πŸ† PLAIN TICKET OUTPUT FOR GRADIO MARKDOWN
99
  result = f"### Flight Information from {from_city} ({from_iata}) to {to_city} ({to_iata})\n"
100
-
101
- for flight in flights[:3]: # Show up to 5 results for simplicity
102
  airline_code = flight["validatingAirlineCodes"][0]
103
  airline_name = get_airline_name(airline_code)
104
  price = flight["price"]["total"]
@@ -106,35 +225,33 @@ def search_flights(query: str):
106
  departure_time = flight["itineraries"][0]["segments"][0]["departure"]["at"]
107
  arrival_time = flight["itineraries"][0]["segments"][-1]["arrival"]["at"]
108
  stops = len(flight["itineraries"][0]["segments"]) - 1
109
-
110
  booking_link = generate_booking_link(from_iata, to_iata, departure_date_str)
111
 
112
- # Plain ticket format without fancy styling
113
  result += f"""
114
  - **Airline:** {airline_name}
115
  - **Flight No:** {airline_code}123
116
- - **Departure Date and Time:** {departure_time} ({from_iata})
117
- - **Arrival Date and Time:** {arrival_time} ({to_iata})
118
  - **Duration:** {duration}
119
  - **Stops:** {stops}
120
  - **Price:** ${price}
121
- - [Book Now]({booking_link}) | [Show More Flights]({booking_link})
122
  ---
123
-
124
  """
125
-
126
- print(f"Debug - Flight Data: {flights}") # Debugging output to verify data
127
  return result
128
-
129
  except ResponseError as error:
130
- print(f"Debug - Amadeus API Error: {str(error)}") # Debugging output for API errors
131
  return f"❌ Error: {str(error)}"
132
 
133
- # βœ… Register as a Tool
134
  tools = [
135
  Tool(
136
  name="Flight Booking",
137
  func=search_flights,
138
- description="Find and book flights using natural language. Examples: 'Flight from Delhi to SFO in May', 'Travel from Mumbai to New York next week'."
 
 
 
 
 
139
  )
140
  ]
 
1
  import os
2
  import dateparser
3
+ from datetime import datetime, timedelta
4
  from amadeus import Client, ResponseError
5
  from langchain.tools import Tool
6
+ import http.client
7
+ import json
8
+ import urllib.parse
9
+ import traceback
10
 
11
+ # API Credentials
12
  AMADEUS_API_KEY = os.getenv("AMADEUS_API_KEY")
13
  AMADEUS_API_SECRET = os.getenv("AMADEUS_API_SECRET")
14
+ RAPIDAPI_KEY = os.getenv("RAPIDAPI_KEY")
15
+ RAPIDAPI_HOST = "booking-com15.p.rapidapi.com"
16
 
17
+ amadeus = Client(client_id=AMADEUS_API_KEY, client_secret=AMADEUS_API_SECRET)
 
 
 
18
 
19
+ # Utility Functions
20
  def get_airport_code(city_name: str):
 
21
  try:
22
+ response = amadeus.reference_data.locations.get(keyword=city_name, subType="AIRPORT,CITY")
23
+ return response.data[0]["iataCode"] if response.data else None
 
 
 
 
 
24
  except ResponseError:
25
  return None
26
 
 
27
  def get_airline_name(airline_code: str):
 
28
  try:
29
  response = amadeus.reference_data.airlines.get(airlineCodes=airline_code)
30
+ return response.data[0]["businessName"] if response.data else airline_code
 
 
31
  except ResponseError:
32
  return airline_code
33
 
 
34
  def format_duration(duration: str):
35
+ return duration.replace("PT", "").replace("H", "h ").replace("M", "m").strip()
 
 
36
 
 
37
  def generate_booking_link(from_iata: str, to_iata: str, departure_date: str):
 
38
  return f"https://www.google.com/flights?hl=en#flt={from_iata}.{to_iata}.{departure_date};c:USD;e:1;s:0;sd:1;t:f"
39
 
40
+ # 🏨 Hotel Search Tool (Fixed)
41
+ def search_hotels(query: str):
42
+ """Search for hotels in a city with start and end dates using Booking.com API."""
43
+ try:
44
+ print(f"DEBUG: Query received: {query}")
45
+ words = query.lower().split()
46
+ city, checkin_phrase, checkout_phrase = None, None, None
47
+
48
+ # Extract city
49
+ if "in" in words:
50
+ in_index = words.index("in") + 1
51
+ end_index = words.index("from") if "from" in words else words.index("for") if "for" in words else len(words)
52
+ city = " ".join(words[in_index:end_index]).title()
53
+ print(f"DEBUG: Parsed city: {city}")
54
+
55
+ # Extract dates
56
+ if "from" in words:
57
+ from_index = words.index("from") + 1
58
+ to_index = words.index("to") if "to" in words else len(words)
59
+ checkin_phrase = " ".join(words[from_index:to_index])
60
+ if "to" in words:
61
+ checkout_phrase = " ".join(words[words.index("to") + 1:])
62
+ print(f"DEBUG: Check-in phrase: {checkin_phrase}, Check-out phrase: {checkout_phrase if checkout_phrase else 'Not provided'}")
63
+
64
+ if not city:
65
+ return "❌ Could not detect a valid city. Use 'hotels in <city> from <date> to <date>'."
66
+
67
+ # Parse dates
68
+ checkin_date = dateparser.parse(checkin_phrase, settings={'PREFER_DATES_FROM': 'future'}) if checkin_phrase else None
69
+ if not checkin_date:
70
+ return "❌ Could not parse check-in date. Use formats like 'February 25 2025'."
71
+ print(f"DEBUG: Parsed check-in date: {checkin_date}")
72
+
73
+ checkout_date = dateparser.parse(checkout_phrase, settings={'PREFER_DATES_FROM': 'future'}) if checkout_phrase else checkin_date + timedelta(days=1)
74
+ if not checkout_date:
75
+ return "❌ Could not parse check-out date. Use formats like 'February 28 2025'."
76
+ print(f"DEBUG: Parsed check-out date: {checkout_date}")
77
+
78
+ if checkin_date >= checkout_date:
79
+ checkin_date_str = checkin_date.strftime("%Y-%m-%d")
80
+ checkout_date_str = checkout_date.strftime("%Y-%m-%d")
81
+ return f"❌ Invalid dates: Check-in ({checkin_date_str}) must be before check-out ({checkout_date_str})."
82
+
83
+ checkin_date_str = checkin_date.strftime("%Y-%m-%d")
84
+ checkout_date_str = checkout_date.strftime("%Y-%m-%d")
85
+ print(f"DEBUG: Formatted dates: {checkin_date_str} to {checkout_date_str}")
86
+
87
+ # Validate API key
88
+ if not RAPIDAPI_KEY:
89
+ return "❌ API key is missing. Please contact Travelo LLC support."
90
+ print(f"DEBUG: Using RapidAPI key: {RAPIDAPI_KEY[:5]}... (truncated)")
91
+
92
+ # Get destination ID
93
+ conn = http.client.HTTPSConnection(RAPIDAPI_HOST)
94
+ headers = {
95
+ "x-rapidapi-key": RAPIDAPI_KEY,
96
+ "x-rapidapi-host": RAPIDAPI_HOST
97
+ }
98
+ dest_url = f"/api/v1/hotels/searchDestination?query={urllib.parse.quote(city)}&locale=en-us"
99
+ print(f"DEBUG: Requesting destination ID with URL: {dest_url}")
100
+ conn.request("GET", dest_url, headers=headers)
101
+ res = conn.getresponse()
102
+ dest_raw = res.read().decode("utf-8")
103
+ print(f"DEBUG: Destination response - Status: {res.status}, Raw: {dest_raw}")
104
+ if res.status != 200:
105
+ conn.close()
106
+ return f"❌ Failed to fetch destination ID for {city}. HTTP Status: {res.status}. Response: {dest_raw}"
107
+
108
+ dest_data = json.loads(dest_raw)
109
+ if not dest_data.get("data") or "dest_id" not in dest_data["data"][0]:
110
+ conn.close()
111
+ return f"❌ No valid destination ID found for {city}. Response: {json.dumps(dest_data)}"
112
+
113
+ dest_id = next((item["dest_id"] for item in dest_data["data"] if item.get("search_type") == "city"), None)
114
+ if not dest_id:
115
+ conn.close()
116
+ return f"❌ No city-level destination ID found for {city}. Response: {json.dumps(dest_data)}"
117
+ print(f"DEBUG: Destination ID: {dest_id}")
118
+
119
+ # Search hotels
120
+ params = {
121
+ "dest_id": dest_id,
122
+ "search_type": "CITY",
123
+ "adults": "1",
124
+ "children_age": "0,17",
125
+ "room_qty": "1",
126
+ "page_number": "1",
127
+ "units": "metric",
128
+ "temperature_unit": "c",
129
+ "languagecode": "en-us",
130
+ "currency_code": "USD",
131
+ "arrival_date": checkin_date_str,
132
+ "departure_date": checkout_date_str
133
+ }
134
+ hotel_url = "/api/v1/hotels/searchHotels?" + urllib.parse.urlencode(params, safe=":/%")
135
+ print(f"DEBUG: Requesting hotels with URL: {hotel_url}")
136
+ conn.request("GET", hotel_url, headers=headers)
137
+ res = conn.getresponse()
138
+ hotel_raw = res.read().decode("utf-8")
139
+ print(f"DEBUG: Hotel response - Status: {res.status}, Raw: {hotel_raw}")
140
+ if res.status != 200:
141
+ conn.close()
142
+ return f"❌ Failed to fetch hotels. HTTP Status: {res.status}. Response: {hotel_raw}"
143
+
144
+ data = json.loads(hotel_raw)
145
+ if "data" not in data or "hotels" not in data["data"] or not data["data"]["hotels"]:
146
+ error_msg = data.get("message", "No hotels available")
147
+ conn.close()
148
+ return f"❌ No hotels found in {city} for {checkin_date_str} to {checkout_date_str}. The API reported: {json.dumps(error_msg)}. Try adjusting your dates or destination."
149
+
150
+ hotels = data["data"]["hotels"][:3]
151
+ result = f"### Hotels in {city} ({checkin_date_str} to {checkout_date_str})\n"
152
+ for hotel in hotels:
153
+ property_data = hotel.get("property", {})
154
+ name = property_data.get("name", "Unknown Hotel")
155
+ price = property_data.get("priceBreakdown", {}).get("grossPrice", {}).get("value", "N/A")
156
+ currency = property_data.get("priceBreakdown", {}).get("currency", "USD")
157
+ rating = property_data.get("reviewScore", "Not rated")
158
+ hotel_id = hotel.get("hotel_id", "")
159
+
160
+ # Ensure hotel_id corresponds to the correct format for Booking.com URLs
161
+ booking_link = f"https://www.booking.com/hotel/fr/{hotel_id}.html" if hotel_id else "https://www.booking.com"
162
+
163
+ result += f"""
164
+ - **Hotel:** {name}
165
+ - **Rating:** {rating} ⭐
166
+ - **Price:** {price} {currency}
167
+ - [Book Now]({booking_link})
168
+ ---
169
+ """
170
+ conn.close()
171
+ return result
172
+
173
+ except Exception as e:
174
+ error_details = traceback.format_exc()
175
+ print(f"DEBUG: Exception occurred: {error_details}")
176
+ if 'conn' in locals():
177
+ conn.close()
178
+ return f"❌ Error searching hotels: {str(e)}. Raw response (if any): {hotel_raw if 'hotel_raw' in locals() else 'N/A'}"
179
+
180
+ # ✈️ Flight Search Tool (Unchanged)
181
  def search_flights(query: str):
 
182
  try:
183
  words = query.lower().split()
184
  from_city, to_city, date_phrase = None, None, None
185
 
 
186
  if "from" in words and "to" in words:
187
  from_index = words.index("from") + 1
188
  to_index = words.index("to") + 1
 
191
 
192
  date_phrase = " ".join(words[words.index("in") + 1:]) if "in" in words else None
193
 
 
194
  if not from_city or not to_city:
195
  return "❌ Could not detect valid departure and destination cities. Please use 'from <city> to <city>'."
196
 
 
197
  from_iata = get_airport_code(from_city)
198
  to_iata = get_airport_code(to_city)
 
199
  if not from_iata or not to_iata:
200
+ return f"❌ Could not find airport codes for {from_city} or {to_city}."
201
 
 
202
  departure_date = dateparser.parse(date_phrase) if date_phrase else None
203
  if not departure_date:
204
  return "❌ Could not understand the travel date. Use formats like 'next week' or 'on May 15'."
205
 
206
  departure_date_str = departure_date.strftime("%Y-%m-%d")
207
 
 
208
  response = amadeus.shopping.flight_offers_search.get(
209
  originLocationCode=from_iata,
210
  destinationLocationCode=to_iata,
211
  departureDate=departure_date_str,
212
  adults=1,
213
+ max=3
214
  )
 
215
  flights = response.data
216
  if not flights:
217
  return f"❌ No flights found from {from_city} to {to_city} on {departure_date_str}."
218
 
 
219
  result = f"### Flight Information from {from_city} ({from_iata}) to {to_city} ({to_iata})\n"
220
+ for flight in flights:
 
221
  airline_code = flight["validatingAirlineCodes"][0]
222
  airline_name = get_airline_name(airline_code)
223
  price = flight["price"]["total"]
 
225
  departure_time = flight["itineraries"][0]["segments"][0]["departure"]["at"]
226
  arrival_time = flight["itineraries"][0]["segments"][-1]["arrival"]["at"]
227
  stops = len(flight["itineraries"][0]["segments"]) - 1
 
228
  booking_link = generate_booking_link(from_iata, to_iata, departure_date_str)
229
 
 
230
  result += f"""
231
  - **Airline:** {airline_name}
232
  - **Flight No:** {airline_code}123
233
+ - **Departure:** {departure_time} ({from_iata})
234
+ - **Arrival:** {arrival_time} ({to_iata})
235
  - **Duration:** {duration}
236
  - **Stops:** {stops}
237
  - **Price:** ${price}
238
+ - [Book Now]({booking_link})
239
  ---
 
240
  """
 
 
241
  return result
 
242
  except ResponseError as error:
 
243
  return f"❌ Error: {str(error)}"
244
 
245
+ # Register Tools
246
  tools = [
247
  Tool(
248
  name="Flight Booking",
249
  func=search_flights,
250
+ description="Find flights using natural language. Example: 'Flight from Delhi to SFO in May'"
251
+ ),
252
+ Tool(
253
+ name="Hotel Booking",
254
+ func=search_hotels,
255
+ description="Find hotels in a city with start and end dates. Example: 'Hotels in Paris from February 25 2025 to February 28 2025'"
256
  )
257
  ]