Create the travel companion
Browse files
app.py
CHANGED
@@ -1,69 +1,512 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
2 |
import datetime
|
3 |
import requests
|
4 |
import pytz
|
5 |
import yaml
|
|
|
|
|
|
|
6 |
from tools.final_answer import FinalAnswerTool
|
7 |
-
|
8 |
from Gradio_UI import GradioUI
|
9 |
|
10 |
-
#
|
|
|
|
|
|
|
|
|
11 |
@tool
|
12 |
-
def
|
13 |
-
|
14 |
-
|
15 |
Args:
|
16 |
-
|
17 |
-
|
|
|
|
|
18 |
"""
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
@tool
|
22 |
-
def
|
23 |
-
"""
|
|
|
24 |
Args:
|
25 |
-
|
|
|
|
|
|
|
26 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
try:
|
28 |
-
#
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
# Get current time in that timezone
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
except Exception as e:
|
34 |
-
return f"Error
|
35 |
|
|
|
36 |
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
-
# If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder:
|
40 |
-
# model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
|
|
53 |
with open("prompts.yaml", 'r') as stream:
|
54 |
prompt_templates = yaml.safe_load(stream)
|
55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
agent = CodeAgent(
|
57 |
model=model,
|
58 |
-
tools=[
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
verbosity_level=1,
|
61 |
grammar=None,
|
62 |
planning_interval=None,
|
63 |
-
name=
|
64 |
-
description=
|
65 |
prompt_templates=prompt_templates
|
66 |
)
|
67 |
|
68 |
-
|
|
|
69 |
GradioUI(agent).launch()
|
|
|
1 |
+
# Travel Companion AI Agent
|
2 |
+
# This agent helps travelers with destination information, local time, weather forecasts,
|
3 |
+
# currency conversion, language translation, and visual destination previews.
|
4 |
+
|
5 |
+
from smolagents import CodeAgent, HfApiModel, load_tool, tool
|
6 |
import datetime
|
7 |
import requests
|
8 |
import pytz
|
9 |
import yaml
|
10 |
+
import random
|
11 |
+
import json
|
12 |
+
import os
|
13 |
from tools.final_answer import FinalAnswerTool
|
|
|
14 |
from Gradio_UI import GradioUI
|
15 |
|
16 |
+
# Load the image generation tool once, outside the function
|
17 |
+
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
|
18 |
+
|
19 |
+
# ==================== ORIGINAL TOOLS (ENHANCED) ====================
|
20 |
+
|
21 |
@tool
|
22 |
+
def generate_destination_preview(destination: str) -> str:
|
23 |
+
"""Generates a vibrant, artistic preview image of a travel destination.
|
24 |
+
|
25 |
Args:
|
26 |
+
destination: The travel destination to visualize (e.g., 'Paris', 'Tokyo', 'Bali').
|
27 |
+
|
28 |
+
Returns:
|
29 |
+
A link to the generated destination preview image.
|
30 |
"""
|
31 |
+
# List of visual styles for variety
|
32 |
+
styles = [
|
33 |
+
"sunrise golden hour",
|
34 |
+
"blue hour twilight",
|
35 |
+
"vibrant daytime",
|
36 |
+
"dramatic sunset",
|
37 |
+
"night lights"
|
38 |
+
]
|
39 |
+
|
40 |
+
# Select a random style for variety
|
41 |
+
style = random.choice(styles)
|
42 |
+
|
43 |
+
# Construct a detailed prompt for the AI model
|
44 |
+
prompt = f"A beautiful travel photograph of {destination}, {style}, photorealistic, high-resolution, travel photography, highly detailed landmark view"
|
45 |
+
|
46 |
+
# Use the pre-loaded image generation tool
|
47 |
+
try:
|
48 |
+
image_url = image_generation_tool(prompt)
|
49 |
+
return f"Here's a preview of {destination}: {image_url}"
|
50 |
+
except Exception as e:
|
51 |
+
return f"Error generating image of {destination}: {str(e)}"
|
52 |
+
|
53 |
|
54 |
@tool
|
55 |
+
def get_local_time(destination: str) -> str:
|
56 |
+
"""Get the current local time at a travel destination.
|
57 |
+
|
58 |
Args:
|
59 |
+
destination: A city or location name (e.g., 'Paris', 'Tokyo', 'New York').
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
The current local time at the specified destination.
|
63 |
"""
|
64 |
+
# Map of common tourist destinations to their timezones
|
65 |
+
destination_timezones = {
|
66 |
+
"london": "Europe/London",
|
67 |
+
"paris": "Europe/Paris",
|
68 |
+
"rome": "Europe/Rome",
|
69 |
+
"madrid": "Europe/Madrid",
|
70 |
+
"berlin": "Europe/Berlin",
|
71 |
+
"amsterdam": "Europe/Amsterdam",
|
72 |
+
"athens": "Europe/Athens",
|
73 |
+
"istanbul": "Europe/Istanbul",
|
74 |
+
"dubai": "Asia/Dubai",
|
75 |
+
"new delhi": "Asia/Kolkata",
|
76 |
+
"mumbai": "Asia/Kolkata",
|
77 |
+
"bangkok": "Asia/Bangkok",
|
78 |
+
"singapore": "Asia/Singapore",
|
79 |
+
"tokyo": "Asia/Tokyo",
|
80 |
+
"seoul": "Asia/Seoul",
|
81 |
+
"beijing": "Asia/Shanghai",
|
82 |
+
"shanghai": "Asia/Shanghai",
|
83 |
+
"hong kong": "Asia/Hong_Kong",
|
84 |
+
"sydney": "Australia/Sydney",
|
85 |
+
"melbourne": "Australia/Melbourne",
|
86 |
+
"auckland": "Pacific/Auckland",
|
87 |
+
"fiji": "Pacific/Fiji",
|
88 |
+
"honolulu": "Pacific/Honolulu",
|
89 |
+
"anchorage": "America/Anchorage",
|
90 |
+
"los angeles": "America/Los_Angeles",
|
91 |
+
"san francisco": "America/Los_Angeles",
|
92 |
+
"las vegas": "America/Los_Angeles",
|
93 |
+
"denver": "America/Denver",
|
94 |
+
"chicago": "America/Chicago",
|
95 |
+
"houston": "America/Chicago",
|
96 |
+
"new york": "America/New_York",
|
97 |
+
"miami": "America/New_York",
|
98 |
+
"toronto": "America/Toronto",
|
99 |
+
"mexico city": "America/Mexico_City",
|
100 |
+
"rio de janeiro": "America/Sao_Paulo",
|
101 |
+
"sao paulo": "America/Sao_Paulo",
|
102 |
+
"buenos aires": "America/Argentina/Buenos_Aires",
|
103 |
+
"cairo": "Africa/Cairo",
|
104 |
+
"cape town": "Africa/Johannesburg",
|
105 |
+
"johannesburg": "Africa/Johannesburg",
|
106 |
+
"nairobi": "Africa/Nairobi"
|
107 |
+
}
|
108 |
+
|
109 |
try:
|
110 |
+
# Normalize the destination name
|
111 |
+
normalized_dest = destination.lower().strip()
|
112 |
+
|
113 |
+
# Find the closest matching timezone
|
114 |
+
timezone = None
|
115 |
+
for city, tz in destination_timezones.items():
|
116 |
+
if city in normalized_dest or normalized_dest in city:
|
117 |
+
timezone = tz
|
118 |
+
break
|
119 |
+
|
120 |
+
if not timezone:
|
121 |
+
return f"I don't have timezone information for {destination}. Please try a major city nearby."
|
122 |
+
|
123 |
# Get current time in that timezone
|
124 |
+
tz = pytz.timezone(timezone)
|
125 |
+
local_time = datetime.datetime.now(tz)
|
126 |
+
|
127 |
+
# Format the result
|
128 |
+
formatted_time = local_time.strftime("%I:%M %p on %A, %B %d, %Y")
|
129 |
+
time_diff = local_time.utcoffset().total_seconds() / 3600
|
130 |
+
sign = "+" if time_diff >= 0 else ""
|
131 |
+
|
132 |
+
return f"The current local time in {destination} is {formatted_time} (UTC{sign}{int(time_diff)})"
|
133 |
+
|
134 |
except Exception as e:
|
135 |
+
return f"Error getting local time for {destination}: {str(e)}"
|
136 |
|
137 |
+
# ==================== NEW TRAVEL-SPECIFIC TOOLS ====================
|
138 |
|
139 |
+
@tool
|
140 |
+
def get_weather_forecast(destination: str, days: int = 3) -> str:
|
141 |
+
"""Get the weather forecast for a travel destination.
|
142 |
+
|
143 |
+
Args:
|
144 |
+
destination: City or location name
|
145 |
+
days: Number of days to forecast (default: 3)
|
146 |
+
|
147 |
+
Returns:
|
148 |
+
Weather forecast information for trip planning
|
149 |
+
"""
|
150 |
+
try:
|
151 |
+
# In a production environment, you would use a real API key
|
152 |
+
API_KEY = os.environ.get("WEATHER_API_KEY", "demo_key")
|
153 |
+
|
154 |
+
# For demo purposes, we'll generate simulated weather data
|
155 |
+
# In a real implementation, you would call an actual weather API
|
156 |
+
weather_conditions = ["Sunny", "Partly Cloudy", "Cloudy", "Light Rain", "Heavy Rain", "Thunderstorms", "Windy", "Foggy", "Snow", "Clear"]
|
157 |
+
|
158 |
+
# Create a deterministic but seemingly random forecast based on destination name
|
159 |
+
seed = sum(ord(c) for c in destination)
|
160 |
+
random.seed(seed)
|
161 |
+
|
162 |
+
# Generate forecast data
|
163 |
+
forecast_text = f"🌦️ Weather forecast for {destination}:\n\n"
|
164 |
+
|
165 |
+
today = datetime.datetime.now()
|
166 |
+
for i in range(days):
|
167 |
+
day = today + datetime.timedelta(days=i)
|
168 |
+
day_name = day.strftime("%A")
|
169 |
+
date = day.strftime("%b %d")
|
170 |
+
|
171 |
+
# "Random" but deterministic weather for the demo
|
172 |
+
condition = weather_conditions[random.randint(0, len(weather_conditions)-1)]
|
173 |
+
temp_high = random.randint(15, 35) # Celsius
|
174 |
+
temp_low = temp_high - random.randint(5, 15)
|
175 |
+
precipitation = random.randint(0, 100) if "Rain" in condition or "Snow" in condition or "Thunder" in condition else 0
|
176 |
+
|
177 |
+
forecast_text += f"• {day_name}, {date}: {condition}, {temp_low}°C to {temp_high}°C"
|
178 |
+
if precipitation > 0:
|
179 |
+
forecast_text += f", {precipitation}% chance of precipitation"
|
180 |
+
forecast_text += "\n"
|
181 |
+
|
182 |
+
# Add packing recommendations based on conditions
|
183 |
+
coldest = min([forecast_text.count("Snow"), forecast_text.count("0°C")])
|
184 |
+
rainiest = forecast_text.count("Rain") + forecast_text.count("Thunder")
|
185 |
+
|
186 |
+
forecast_text += "\n🧳 Packing tips: "
|
187 |
+
if coldest > 0:
|
188 |
+
forecast_text += "Bring warm layers and a heavy jacket. "
|
189 |
+
elif "5°C" in forecast_text or "6°C" in forecast_text or "7°C" in forecast_text:
|
190 |
+
forecast_text += "Pack a warm jacket and layers. "
|
191 |
+
|
192 |
+
if rainiest > 0:
|
193 |
+
forecast_text += "Don't forget an umbrella and waterproof footwear. "
|
194 |
+
|
195 |
+
if "Sunny" in forecast_text and "30°C" in forecast_text:
|
196 |
+
forecast_text += "Bring sunscreen, sunglasses, and light clothing. "
|
197 |
+
|
198 |
+
return forecast_text
|
199 |
+
|
200 |
+
except Exception as e:
|
201 |
+
return f"Error retrieving weather data for {destination}: {str(e)}"
|
202 |
|
|
|
|
|
203 |
|
204 |
+
@tool
|
205 |
+
def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
|
206 |
+
"""Convert an amount between currencies for travel budgeting.
|
207 |
+
|
208 |
+
Args:
|
209 |
+
amount: The amount to convert
|
210 |
+
from_currency: Source currency code (e.g., USD, EUR)
|
211 |
+
to_currency: Target currency code (e.g., JPY, GBP)
|
212 |
+
|
213 |
+
Returns:
|
214 |
+
Converted amount and exchange rate information
|
215 |
+
"""
|
216 |
+
try:
|
217 |
+
# In a production environment, you would use a real API key
|
218 |
+
# For demo purposes, we'll use fixed exchange rates
|
219 |
+
# In a real implementation, you would call an actual currency API
|
220 |
+
|
221 |
+
# Common exchange rates (as of early 2025, for demo purposes)
|
222 |
+
exchange_rates = {
|
223 |
+
"USD": {"EUR": 0.92, "GBP": 0.79, "JPY": 149.50, "CAD": 1.35, "AUD": 1.52, "CNY": 7.20, "INR": 83.20, "MXN": 17.05},
|
224 |
+
"EUR": {"USD": 1.09, "GBP": 0.86, "JPY": 163.00, "CAD": 1.47, "AUD": 1.66, "CNY": 7.85, "INR": 90.70, "MXN": 18.60},
|
225 |
+
"GBP": {"USD": 1.27, "EUR": 1.16, "JPY": 189.30, "CAD": 1.71, "AUD": 1.92, "CNY": 9.10, "INR": 105.30, "MXN": 21.60},
|
226 |
+
"JPY": {"USD": 0.0067, "EUR": 0.0061, "GBP": 0.0053, "CAD": 0.0090, "AUD": 0.0102, "CNY": 0.0482, "INR": 0.5565, "MXN": 0.1141},
|
227 |
+
"CAD": {"USD": 0.74, "EUR": 0.68, "GBP": 0.58, "JPY": 110.70, "AUD": 1.13, "CNY": 5.33, "INR": 61.60, "MXN": 12.60},
|
228 |
+
"AUD": {"USD": 0.66, "EUR": 0.60, "GBP": 0.52, "JPY": 98.40, "CAD": 0.89, "CNY": 4.73, "INR": 54.70, "MXN": 11.20},
|
229 |
+
"CNY": {"USD": 0.14, "EUR": 0.13, "GBP": 0.11, "JPY": 20.80, "CAD": 0.19, "AUD": 0.21, "INR": 11.60, "MXN": 2.37},
|
230 |
+
"INR": {"USD": 0.012, "EUR": 0.011, "GBP": 0.0095, "JPY": 1.80, "CAD": 0.016, "AUD": 0.018, "CNY": 0.086, "MXN": 0.205},
|
231 |
+
"MXN": {"USD": 0.059, "EUR": 0.054, "GBP": 0.046, "JPY": 8.77, "CAD": 0.079, "AUD": 0.089, "CNY": 0.422, "INR": 4.88}
|
232 |
+
}
|
233 |
+
|
234 |
+
# Normalize currency codes
|
235 |
+
from_currency = from_currency.upper().strip()
|
236 |
+
to_currency = to_currency.upper().strip()
|
237 |
+
|
238 |
+
# Validate currencies
|
239 |
+
if from_currency not in exchange_rates:
|
240 |
+
return f"Sorry, I don't have exchange rate data for {from_currency}."
|
241 |
+
|
242 |
+
if to_currency not in exchange_rates[from_currency] and to_currency != from_currency:
|
243 |
+
return f"Sorry, I don't have exchange rate data from {from_currency} to {to_currency}."
|
244 |
+
|
245 |
+
# If same currency, return original amount
|
246 |
+
if from_currency == to_currency:
|
247 |
+
return f"{amount} {from_currency} = {amount} {to_currency}"
|
248 |
+
|
249 |
+
# Get exchange rate and calculate conversion
|
250 |
+
rate = exchange_rates[from_currency][to_currency]
|
251 |
+
converted_amount = amount * rate
|
252 |
+
|
253 |
+
# Format the result
|
254 |
+
return f"💱 {amount:,.2f} {from_currency} = {converted_amount:,.2f} {to_currency}\n\nExchange rate: 1 {from_currency} = {rate} {to_currency}\n\n(Note: Actual rates may vary. For planning purposes only.)"
|
255 |
+
|
256 |
+
except Exception as e:
|
257 |
+
return f"Error converting currency: {str(e)}"
|
258 |
|
259 |
|
260 |
+
@tool
|
261 |
+
def translate_phrase(text: str, language: str) -> str:
|
262 |
+
"""Translate common travel phrases to a local language.
|
263 |
+
|
264 |
+
Args:
|
265 |
+
text: Text to translate (e.g., "Hello", "Thank you", "Where is the bathroom?")
|
266 |
+
language: Target language (e.g., 'Spanish', 'Japanese', 'French')
|
267 |
+
|
268 |
+
Returns:
|
269 |
+
Translated text with pronunciation guide
|
270 |
+
"""
|
271 |
+
try:
|
272 |
+
# Common travel phrases in various languages
|
273 |
+
# In a production environment, you would use a real translation API
|
274 |
+
language = language.lower().strip()
|
275 |
+
text_lower = text.lower().strip()
|
276 |
+
|
277 |
+
phrase_translations = {
|
278 |
+
"hello": {
|
279 |
+
"spanish": {"text": "Hola", "pronunciation": "oh-lah"},
|
280 |
+
"french": {"text": "Bonjour", "pronunciation": "bohn-zhoor"},
|
281 |
+
"italian": {"text": "Ciao", "pronunciation": "chow"},
|
282 |
+
"german": {"text": "Hallo", "pronunciation": "hah-loh"},
|
283 |
+
"japanese": {"text": "こんにちは (Konnichiwa)", "pronunciation": "kohn-nee-chee-wah"},
|
284 |
+
"mandarin": {"text": "你好 (Nǐ hǎo)", "pronunciation": "nee how"},
|
285 |
+
"arabic": {"text": "مرحبا (Marhaba)", "pronunciation": "mar-ha-ba"},
|
286 |
+
"russian": {"text": "Здравствуйте (Zdravstvuyte)", "pronunciation": "zdrah-stvooy-tye"},
|
287 |
+
"portuguese": {"text": "Olá", "pronunciation": "oh-lah"},
|
288 |
+
"thai": {"text": "สวัสดี (Sawatdee)", "pronunciation": "sa-wat-dee"}
|
289 |
+
},
|
290 |
+
"thank you": {
|
291 |
+
"spanish": {"text": "Gracias", "pronunciation": "grah-see-ahs"},
|
292 |
+
"french": {"text": "Merci", "pronunciation": "mair-see"},
|
293 |
+
"italian": {"text": "Grazie", "pronunciation": "graht-see-eh"},
|
294 |
+
"german": {"text": "Danke", "pronunciation": "dahn-kuh"},
|
295 |
+
"japanese": {"text": "ありがとう (Arigatou)", "pronunciation": "ah-ree-gah-toh"},
|
296 |
+
"mandarin": {"text": "谢谢 (Xièxiè)", "pronunciation": "shyeh-shyeh"},
|
297 |
+
"arabic": {"text": "شكرا (Shukran)", "pronunciation": "shoo-kran"},
|
298 |
+
"russian": {"text": "Спасибо (Spasibo)", "pronunciation": "spah-see-boh"},
|
299 |
+
"portuguese": {"text": "Obrigado/a", "pronunciation": "oh-bree-gah-doo/dah"},
|
300 |
+
"thai": {"text": "ขอบคุณ (Khop khun)", "pronunciation": "kop-koon"}
|
301 |
+
},
|
302 |
+
"excuse me": {
|
303 |
+
"spanish": {"text": "Disculpe", "pronunciation": "dees-kool-peh"},
|
304 |
+
"french": {"text": "Excusez-moi", "pronunciation": "ex-koo-zay mwah"},
|
305 |
+
"italian": {"text": "Scusi", "pronunciation": "skoo-zee"},
|
306 |
+
"german": {"text": "Entschuldigung", "pronunciation": "ent-shool-di-goong"},
|
307 |
+
"japanese": {"text": "すみません (Sumimasen)", "pronunciation": "soo-mee-mah-sen"},
|
308 |
+
"mandarin": {"text": "对不起 (Duìbùqǐ)", "pronunciation": "dway-boo-chee"},
|
309 |
+
"arabic": {"text": "عفوا (Afwan)", "pronunciation": "af-wan"},
|
310 |
+
"russian": {"text": "Извините (Izvinite)", "pronunciation": "eez-vee-nee-tye"},
|
311 |
+
"portuguese": {"text": "Com licença", "pronunciation": "com lee-sen-sah"},
|
312 |
+
"thai": {"text": "ขอโทษ (Kho thot)", "pronunciation": "kor-toht"}
|
313 |
+
},
|
314 |
+
"where is the bathroom": {
|
315 |
+
"spanish": {"text": "¿Dónde está el baño?", "pronunciation": "don-deh es-tah el ban-yo"},
|
316 |
+
"french": {"text": "Où sont les toilettes?", "pronunciation": "oo son lay twa-let"},
|
317 |
+
"italian": {"text": "Dov'è il bagno?", "pronunciation": "doh-veh eel ban-yo"},
|
318 |
+
"german": {"text": "Wo ist die Toilette?", "pronunciation": "vo ist dee twa-let-te"},
|
319 |
+
"japanese": {"text": "トイレはどこですか (Toire wa doko desu ka)", "pronunciation": "toy-reh wah doh-koh des-kah"},
|
320 |
+
"mandarin": {"text": "厕所在哪里 (Cèsuǒ zài nǎlǐ)", "pronunciation": "tsuh-swor dzeye nah-lee"},
|
321 |
+
"arabic": {"text": "أين الحمام (Ayna al-hammam)", "pronunciation": "eye-nah al-ham-mam"},
|
322 |
+
"russian": {"text": "Где туалет (Gde tualet)", "pronunciation": "g-dyeh too-ah-lyet"},
|
323 |
+
"portuguese": {"text": "Onde fica o banheiro?", "pronunciation": "on-jee fee-ka oo ban-yay-roo"},
|
324 |
+
"thai": {"text": "ห้องน้ำอยู่ที่ไหน (Hong nam yu tee nai)", "pronunciation": "hong nam yoo tee nai"}
|
325 |
+
},
|
326 |
+
"how much": {
|
327 |
+
"spanish": {"text": "¿Cuánto cuesta?", "pronunciation": "kwan-toh kwes-tah"},
|
328 |
+
"french": {"text": "Combien ça coûte?", "pronunciation": "kom-bee-en sa koot"},
|
329 |
+
"italian": {"text": "Quanto costa?", "pronunciation": "kwan-toh kos-tah"},
|
330 |
+
"german": {"text": "Wie viel kostet das?", "pronunciation": "vee feel kos-tet das"},
|
331 |
+
"japanese": {"text": "いくらですか (Ikura desu ka)", "pronunciation": "ee-koo-rah des-kah"},
|
332 |
+
"mandarin": {"text": "多少钱 (Duōshǎo qián)", "pronunciation": "dwor-shaow chyen"},
|
333 |
+
"arabic": {"text": "كم الثمن (Kam althaman)", "pronunciation": "kam al-tha-man"},
|
334 |
+
"russian": {"text": "Сколько это стоит (Skol'ko eto stoit)", "pronunciation": "skol-ka eh-ta stoh-eet"},
|
335 |
+
"portuguese": {"text": "Quanto custa?", "pronunciation": "kwan-too koos-tah"},
|
336 |
+
"thai": {"text": "ราคาเท่าไหร่ (Raka tao rai)", "pronunciation": "ra-ka tao-rai"}
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
# Find the phrase key that most closely matches the input text
|
341 |
+
matched_phrase = None
|
342 |
+
for phrase in phrase_translations:
|
343 |
+
if phrase in text_lower or text_lower in phrase:
|
344 |
+
matched_phrase = phrase
|
345 |
+
break
|
346 |
+
|
347 |
+
if not matched_phrase:
|
348 |
+
return f"I don't have a translation for '{text}'. Try common travel phrases like 'hello', 'thank you', 'excuse me', etc."
|
349 |
+
|
350 |
+
# Find the language that most closely matches the input language
|
351 |
+
matched_language = None
|
352 |
+
for lang in phrase_translations[matched_phrase]:
|
353 |
+
if lang in language or language in lang:
|
354 |
+
matched_language = lang
|
355 |
+
break
|
356 |
+
|
357 |
+
if not matched_language:
|
358 |
+
return f"I don't have translations for {language}. Try languages like Spanish, French, Italian, German, Japanese, etc."
|
359 |
+
|
360 |
+
# Get the translation
|
361 |
+
translation = phrase_translations[matched_phrase][matched_language]
|
362 |
+
|
363 |
+
return f"🗣️ '{text}' in {matched_language.capitalize()}:\n\n{translation['text']}\n\nPronunciation: {translation['pronunciation']}"
|
364 |
+
|
365 |
+
except Exception as e:
|
366 |
+
return f"Error translating text: {str(e)}"
|
367 |
+
|
368 |
+
|
369 |
+
@tool
|
370 |
+
def get_visa_requirements(nationality: str, destination: str) -> str:
|
371 |
+
"""Check visa requirements for traveling to a destination.
|
372 |
+
|
373 |
+
Args:
|
374 |
+
nationality: Traveler's passport country (e.g., 'US', 'UK', 'Canada')
|
375 |
+
destination: Country to visit (e.g., 'Japan', 'France', 'Brazil')
|
376 |
+
|
377 |
+
Returns:
|
378 |
+
Visa requirement information
|
379 |
+
"""
|
380 |
+
try:
|
381 |
+
# Normalize inputs
|
382 |
+
nationality = nationality.lower().strip()
|
383 |
+
destination = destination.lower().strip()
|
384 |
+
|
385 |
+
# Map of common country names to their normalized forms
|
386 |
+
country_mapping = {
|
387 |
+
"us": "united states", "usa": "united states", "united states of america": "united states",
|
388 |
+
"uk": "united kingdom", "britain": "united kingdom", "great britain": "united kingdom",
|
389 |
+
"uae": "united arab emirates",
|
390 |
+
# Add more mappings as needed
|
391 |
+
}
|
392 |
+
|
393 |
+
# Apply mappings if available
|
394 |
+
nationality = country_mapping.get(nationality, nationality)
|
395 |
+
destination = country_mapping.get(destination, destination)
|
396 |
+
|
397 |
+
# Skip if same country (generally no visa needed for citizens)
|
398 |
+
if nationality == destination:
|
399 |
+
return f"As a citizen of {nationality.title()}, you generally don't need a visa to visit your own country."
|
400 |
+
|
401 |
+
# Sample visa requirement data
|
402 |
+
# In a production environment, this would be a comprehensive database or API call
|
403 |
+
visa_data = {
|
404 |
+
"united states": {
|
405 |
+
"european union": "No visa required for stays up to 90 days",
|
406 |
+
"united kingdom": "No visa required for stays up to 6 months",
|
407 |
+
"japan": "No visa required for stays up to 90 days",
|
408 |
+
"australia": "Electronic Travel Authority (ETA) required",
|
409 |
+
"china": "Visa required, must apply in advance",
|
410 |
+
"india": "e-Visa available, apply online before travel",
|
411 |
+
"brazil": "No visa required for stays up to 90 days",
|
412 |
+
"mexico": "No visa required for stays up to 180 days",
|
413 |
+
"south africa": "No visa required for stays up to 90 days",
|
414 |
+
"thailand": "No visa required for stays up to 30 days"
|
415 |
+
},
|
416 |
+
"united kingdom": {
|
417 |
+
"european union": "No visa required for stays up to 90 days",
|
418 |
+
"united states": "ESTA required for entry",
|
419 |
+
"japan": "No visa required for stays up to 90 days",
|
420 |
+
"australia": "eVisitor visa required",
|
421 |
+
"china": "Visa required, must apply in advance",
|
422 |
+
"india": "e-Visa available, apply online before travel",
|
423 |
+
"brazil": "No visa required for stays up to 90 days",
|
424 |
+
"mexico": "No visa required for stays up to 180 days",
|
425 |
+
"south africa": "No visa required for stays up to 90 days",
|
426 |
+
"thailand": "No visa required for stays up to 30 days"
|
427 |
+
},
|
428 |
+
# Add more countries as needed
|
429 |
+
}
|
430 |
+
|
431 |
+
# Check if we have data for this nationality
|
432 |
+
if nationality not in visa_data:
|
433 |
+
return f"I don't have specific visa information for citizens of {nationality.title()}. Please check with the embassy of {destination.title()} for accurate visa requirements."
|
434 |
+
|
435 |
+
# Check if we have data for this destination
|
436 |
+
if destination not in visa_data[nationality]:
|
437 |
+
return f"I don't have specific visa information for {nationality.title()} citizens traveling to {destination.title()}. Please check with the embassy of {destination.title()} for accurate visa requirements."
|
438 |
+
|
439 |
+
# Get the visa requirements
|
440 |
+
requirements = visa_data[nationality][destination]
|
441 |
+
|
442 |
+
return f"🛂 Visa requirements for {nationality.title()} citizens traveling to {destination.title()}:\n\n{requirements}\n\n(Note: Visa requirements may change. Always verify with the official embassy or consulate before travel.)"
|
443 |
+
|
444 |
+
except Exception as e:
|
445 |
+
return f"Error retrieving visa information: {str(e)}"
|
446 |
+
|
447 |
+
# ==================== AGENT SETUP ====================
|
448 |
+
|
449 |
+
final_answer = FinalAnswerTool()
|
450 |
+
|
451 |
+
# Model initialization
|
452 |
+
model = HfApiModel(
|
453 |
+
max_tokens=2096,
|
454 |
+
temperature=0.7, # Slightly higher temperature for more creative responses
|
455 |
+
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
|
456 |
+
custom_role_conversions=None,
|
457 |
+
)
|
458 |
|
459 |
+
# Load prompts
|
460 |
with open("prompts.yaml", 'r') as stream:
|
461 |
prompt_templates = yaml.safe_load(stream)
|
462 |
+
|
463 |
+
# Add travel companion specific instructions to the prompt templates
|
464 |
+
travel_agent_prompt = """
|
465 |
+
You are TravelBuddy, an AI travel companion designed to help travelers plan and navigate their journeys.
|
466 |
+
Your goal is to provide helpful, accurate information about destinations, local customs, and practical travel advice.
|
467 |
+
|
468 |
+
You have access to these capabilities:
|
469 |
+
1. Generate visual previews of destinations
|
470 |
+
2. Check local time at travel destinations
|
471 |
+
3. Provide weather forecasts for trip planning
|
472 |
+
4. Convert currencies for travel budgeting
|
473 |
+
5. Translate common travel phrases
|
474 |
+
6. Check visa requirements
|
475 |
+
|
476 |
+
When users ask about a destination, try to provide comprehensive information by combining multiple tools.
|
477 |
+
For example, if someone asks about Tokyo, consider providing the local time, weather, and a visual preview.
|
478 |
+
|
479 |
+
Always be enthusiastic about travel while remaining practical and informative.
|
480 |
+
Suggest off-the-beaten-path experiences when appropriate, but prioritize the specific information requested.
|
481 |
+
"""
|
482 |
+
|
483 |
+
# Add the travel agent prompt to the existing templates
|
484 |
+
if "system_prompt" in prompt_templates:
|
485 |
+
prompt_templates["system_prompt"] = travel_agent_prompt + "\n\n" + prompt_templates.get("system_prompt", "")
|
486 |
+
else:
|
487 |
+
prompt_templates["system_prompt"] = travel_agent_prompt
|
488 |
+
|
489 |
+
# Agent setup with all travel tools
|
490 |
agent = CodeAgent(
|
491 |
model=model,
|
492 |
+
tools=[
|
493 |
+
final_answer,
|
494 |
+
generate_destination_preview, # Enhanced version of original image tool
|
495 |
+
get_local_time, # Enhanced version of original time tool
|
496 |
+
get_weather_forecast, # New travel tool
|
497 |
+
convert_currency, # New travel tool
|
498 |
+
translate_phrase, # New travel tool
|
499 |
+
get_visa_requirements, # New travel tool
|
500 |
+
],
|
501 |
+
max_steps=8, # Increased to allow for more tool usage in a single query
|
502 |
verbosity_level=1,
|
503 |
grammar=None,
|
504 |
planning_interval=None,
|
505 |
+
name="TravelBuddy",
|
506 |
+
description="Your AI travel companion",
|
507 |
prompt_templates=prompt_templates
|
508 |
)
|
509 |
|
510 |
+
# Launch the UI
|
511 |
+
print("Launching TravelBuddy - Your AI Travel Companion")
|
512 |
GradioUI(agent).launch()
|