Spaces:
Running
on
Zero
Running
on
Zero
Ritvik
commited on
Commit
Β·
4a38fbc
1
Parent(s):
d097acf
Save local changes before pull
Browse files
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
|
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.
|
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,
|
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;
|
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;
|
21 |
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
22 |
color: #343a40;
|
23 |
}
|
24 |
.chatbot .message.user {
|
25 |
-
background: #ff8c00;
|
26 |
color: white;
|
27 |
-
margin-left: auto;
|
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;
|
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;
|
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;
|
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 |
-
#
|
87 |
-
|
88 |
-
|
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
|
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
|
103 |
-
submit_btn=gr.Button("βοΈ Send"
|
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"
|
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 |
-
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
-
|
|
|
|
|
|
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 |
-
#
|
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 |
-
#
|
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 |
-
|
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 |
-
"""
|
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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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}.
|
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=
|
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
|
117 |
-
- **Arrival
|
118 |
- **Duration:** {duration}
|
119 |
- **Stops:** {stops}
|
120 |
- **Price:** ${price}
|
121 |
-
- [Book Now]({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 |
-
#
|
134 |
tools = [
|
135 |
Tool(
|
136 |
name="Flight Booking",
|
137 |
func=search_flights,
|
138 |
-
description="Find
|
|
|
|
|
|
|
|
|
|
|
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 |
]
|