agarwalamit081 commited on
Commit
ebd304b
·
verified ·
1 Parent(s): 296726a

Update app.py

Browse files

updated tools: generate_destination_images, assemble_travel_catalogue

Files changed (1) hide show
  1. app.py +115 -214
app.py CHANGED
@@ -1,268 +1,160 @@
1
  #!/usr/bin/env python
2
  # coding=utf-8
3
- import os
4
  import re
5
  import requests
6
- import datetime
7
- import pytz
8
- from typing import List
9
  from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
10
  from tools.final_answer import FinalAnswerTool
11
  from Gradio_UI import GradioUI
12
  import yaml
13
 
14
  # ======================
15
- # CUSTOM TRAVEL TOOLS (FULLY COMPLIANT WITH SMOLAGENTS)
16
  # ======================
17
 
18
  @tool
19
  def get_weather_forecast(location: str, travel_dates: str) -> str:
20
  """
21
- Get weather forecast for a destination during travel dates using wttr.in.
22
 
23
  Args:
24
- location: Destination city name (e.g., "Paris", "Tokyo")
25
- travel_dates: Travel date range (e.g., "October 15-19" or "June 2026")
26
 
27
  Returns:
28
- Weather summary with temperature, conditions, and packing recommendations
29
  """
30
  try:
31
- clean_location = re.sub(r'[^a-zA-Z0-9\s]', '', location).replace(' ', '+')
32
- url = f"http://wttr.in/{clean_location}?format=j1"
33
- response = requests.get(url, timeout=10)
34
- response.raise_for_status()
35
- data = response.json()
36
-
37
- current = data['current_condition'][0]
38
- temp_c = int(current['temp_C'])
39
- feels_like = int(current['FeelsLikeC'])
40
- conditions = current['lang_en'][0]['value']
41
- humidity = current['humidity']
42
- wind = current['windspeedKmph']
43
-
44
- if temp_c > 25:
45
- packing = "Light clothing, sunscreen, hat, sunglasses"
46
- elif temp_c > 15:
47
- packing = "Light layers, light jacket, comfortable walking shoes"
48
- elif temp_c > 5:
49
- packing = "Warm layers, medium jacket, scarf"
50
  else:
51
- packing = "Heavy winter coat, thermal layers, hat, gloves"
52
-
53
- if "rain" in conditions.lower() or "shower" in conditions.lower():
54
- packing += ", waterproof jacket, umbrella"
55
-
56
- return (
57
- f"Weather in {location}:\n"
58
- f"• {temp_c}°C (feels like {feels_like}°C) | {conditions}\n"
59
- f"• Humidity: {humidity}% | Wind: {wind} km/h\n"
60
- f"• Packing tip: {packing}"
61
- )
62
- except Exception:
63
- return f"Typical {location} weather: Mild (15-25°C). Pack light layers + light jacket + umbrella."
64
 
65
 
66
  @tool
67
  def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
68
  """
69
- Convert currency using Frankfurter API (free ECB data).
70
 
71
  Args:
72
- amount: Amount to convert (e.g., 1000.0)
73
- from_currency: Source currency code (e.g., "USD", "EUR")
74
- to_currency: Target currency code (e.g., "JPY", "EUR")
75
 
76
  Returns:
77
- Conversion result with exchange rate and formatted amounts
78
  """
79
  try:
80
  from_currency = from_currency.upper()
81
  to_currency = to_currency.upper()
82
  if from_currency == to_currency:
83
- return f"{amount:,.2f} {from_currency} = {amount:,.2f} {to_currency}"
84
-
85
- url = f"https://api.frankfurter.app/latest?from={from_currency}&to={to_currency}"
86
- response = requests.get(url, timeout=10)
87
- response.raise_for_status()
88
- rate = response.json()['rates'][to_currency]
89
- converted = amount * rate
90
-
91
- return (
92
- f"💱 {amount:,.2f} {from_currency} = {converted:,.2f} {to_currency}\n"
93
- f" (Rate: 1 {from_currency} = {rate:.4f} {to_currency})"
94
- )
95
- except Exception:
96
- fallback = {("USD", "EUR"): 0.93, ("EUR", "USD"): 1.07}.get((from_currency, to_currency), 1.0)
97
- converted = amount * fallback
98
- return f"⚠️ Est: {amount:,.2f} {from_currency} ≈ {converted:,.2f} {to_currency}"
99
 
100
 
101
  @tool
102
- def generate_packing_list(destination: str, weather_summary: str, trip_duration_days: int, trip_type: str) -> str:
103
  """
104
- Generate a customized packing list based on weather and trip type.
105
 
106
  Args:
107
- destination: Travel destination name (e.g., "Barcelona")
108
- weather_summary: Weather conditions from get_weather_forecast tool
109
- trip_duration_days: Length of trip in days (e.g., 4)
110
- trip_type: Type of trip: "sightseeing", "beach", "hiking", or "business"
111
 
112
  Returns:
113
- Formatted packing checklist with quantities
114
  """
115
- has_rain = "rain" in weather_summary.lower() or "umbrella" in weather_summary.lower()
116
- has_cold = "cold" in weather_summary.lower() or "jacket" in weather_summary.lower()
117
-
118
- # FIXED: Proper quantity calculation (was broken string interpolation)
119
- tshirt_qty = trip_duration_days // 2 + 1
120
- pants_qty = trip_duration_days // 3 + 1
121
- socks_qty = trip_duration_days // 2 + 1
122
 
123
- essentials = [
124
- "• Passport + copies",
125
- "• Travel insurance",
126
- "• Credit cards + small local cash",
127
- "• Universal power adapter",
128
- "• Phone + charger + power bank",
129
- ]
130
 
131
- clothing = []
132
- if has_cold:
133
- clothing = [
134
- f"• Warm base layers ({trip_duration_days // 3 + 1})",
135
- "• Insulated jacket",
136
- f"• Warm socks ({socks_qty})",
137
- "• Beanie + gloves",
138
- ]
139
  else:
140
- clothing = [
141
- f"• T-shirts ({tshirt_qty})",
142
- f"• Comfortable pants ({pants_qty})",
143
- "• Light jacket (essential for layering)",
144
- ]
145
-
146
- if has_rain:
147
- clothing.extend(["• Compact umbrella", "• Light waterproof jacket"])
148
-
149
  if trip_type == "beach":
150
- clothing.extend(["• Swimsuit", "• Beach towel", "• Flip-flops"])
151
- elif trip_type == "hiking":
152
- clothing.extend(["• Hiking boots", "• Moisture-wicking socks", "• Daypack"])
153
-
154
- toiletries = [
155
- "• Travel-sized shampoo/conditioner",
156
- "• Toothbrush + toothpaste",
157
- "• Deodorant",
158
- "• Prescription meds",
159
- "• Basic first-aid kit",
160
- ]
161
 
162
  return (
163
- f"🎒 PACKING LIST: {destination} ({trip_duration_days}-day {trip_type})\n"
164
- f"{'─'*56}\nESSENTIALS\n" + "\n".join(essentials) + "\n\nCLOTHING\n" + "\n".join(clothing) + "\n\nTOILETRIES\n" + "\n".join(toiletries)
 
 
165
  )
166
 
167
 
168
  @tool
169
- def build_itinerary(destination: str, attractions: str, weather_summary: str, budget_local_currency: float, trip_duration_days: int) -> str:
170
  """
171
- Create a day-by-day travel itinerary with budget allocation.
172
 
173
  Args:
174
- destination: Destination city name (e.g., "Barcelona")
175
- attractions: Comma-separated list of attractions (e.g., "Sagrada Familia, Park Guell")
176
- weather_summary: Weather conditions to optimize daily activities
177
- budget_local_currency: Total budget in destination currency (e.g., 1200.0)
178
- trip_duration_days: Length of trip in days (e.g., 4)
179
 
180
  Returns:
181
- Formatted day-by-day itinerary with time slots and cost estimates
182
  """
183
- attraction_list = [a.strip() for a in attractions.split(",") if a.strip()]
184
- if not attraction_list:
185
- attraction_list = ["Old Town", "Local museum", "Scenic viewpoint", "Cultural market"]
186
-
187
- daily_budget = budget_local_currency / max(trip_duration_days, 1)
188
- food_budget = daily_budget * 0.35
189
-
190
- lines = [f"🗓️ {trip_duration_days}-DAY ITINERARY: {destination}", "─"*56]
191
-
192
- for day in range(1, trip_duration_days + 1):
193
- am_act = attraction_list[(day-1) % len(attraction_list)]
194
- pm_act = attraction_list[day % len(attraction_list)]
195
- lines.extend([
196
- f"\nDAY {day}",
197
- f" 09:00 {am_act}",
198
- f" 12:30 Lunch (~{food_budget * 0.4:,.0f})",
199
- f" 14:00 {pm_act}",
200
- f" 19:00 Dinner (~{food_budget * 0.6:,.0f})",
201
- ])
202
-
203
- lines.append(f"\n💰 Daily budget: ~{daily_budget:,.0f} (activities + food + transport)")
204
  return "\n".join(lines)
205
 
206
 
207
  @tool
208
- def generate_destination_images(destination: str, image_prompts: str) -> str:
209
  """
210
- Generate 3 representative images for the destination using text-to-image tool.
211
-
212
- Args:
213
- destination: Destination name (e.g., "Barcelona")
214
- image_prompts: Comma-separated prompts for images (e.g., "Sagrada Familia exterior, La Rambla street scene, Park Guell mosaic art")
215
-
216
- Returns:
217
- Instructions for the agent to call image_generation_tool 3 times with these prompts
218
- """
219
- prompts = [p.strip() for p in image_prompts.split(",")][:3]
220
- return (
221
- f"Generate 3 images for {destination} using image_generation_tool with these prompts:\n"
222
- + "\n".join([f"{i+1}. '{prompt}'" for i, prompt in enumerate(prompts)])
223
- )
224
-
225
-
226
- @tool
227
- def assemble_travel_catalogue(
228
- destination: str,
229
- origin: str,
230
- travel_dates: str,
231
- budget_summary: str,
232
- weather_info: str,
233
- itinerary: str,
234
- packing_list: str,
235
- image_urls: str,
236
- ) -> str:
237
- """
238
- Compile all travel information into a beautifully formatted Markdown catalogue.
239
 
240
  Args:
241
  destination: Destination city (e.g., "Barcelona")
242
- origin: Traveler's home city (e.g., "New York")
243
- travel_dates: Travel date range (e.g., "October 15-19, 2026")
244
- budget_summary: Budget conversion result (e.g., "$1,200 USD = €1,116 EUR")
245
- weather_info: Weather forecast summary
246
- itinerary: Day-by-day itinerary plan
247
- packing_list: Customized packing checklist
248
- image_urls: Comma-separated URLs of generated images
 
249
 
250
  Returns:
251
- Complete travel catalogue in Markdown format with embedded images
252
  """
253
- image_list = [url.strip() for url in image_urls.split(",") if url.strip()]
254
- image_section = "\n".join([
255
- f"![{destination} view]({url}) \n*Generated image {i+1}*"
256
- for i, url in enumerate(image_list[:3])
257
- ]) if image_list else "*Images will appear here after generation*"
258
-
259
- catalogue = f"""# 🌍 {destination} Travel Catalogue
260
- *From {origin} • {travel_dates} • {budget_summary}*
261
 
262
  ---
263
 
264
- ## 🌤️ Weather Forecast
265
- {weather_info}
266
 
267
  ---
268
 
@@ -272,65 +164,74 @@ def assemble_travel_catalogue(
272
  ---
273
 
274
  ## 🎒 Packing List
275
- {packing_list}
276
 
277
  ---
278
 
279
- ## 📸 Destination Gallery
280
- {image_section}
 
 
 
 
281
 
282
  ---
283
 
284
- > 💡 Pro Tips: Book attractions online to skip lines. Use public transport passes for savings.
285
  """
286
- return catalogue
287
 
288
 
289
  # ======================
290
- # AGENT SETUP
291
  # ======================
292
 
293
  final_answer = FinalAnswerTool()
294
  web_search = DuckDuckGoSearchTool()
295
- image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
296
 
297
  model = HfApiModel(
298
  max_tokens=2096,
299
- temperature=0.5,
300
  model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
301
  )
302
 
303
- with open("prompts.yaml", 'r') as stream:
304
- prompt_templates = yaml.safe_load(stream)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
  agent = CodeAgent(
307
  model=model,
308
  tools=[
309
  final_answer,
310
  web_search,
311
- image_generation_tool,
312
  get_weather_forecast,
313
  convert_currency,
314
  generate_packing_list,
315
  build_itinerary,
316
- generate_destination_images,
317
- assemble_travel_catalogue,
318
  ],
319
- max_steps=18, # Extra steps for image generation
320
  verbosity_level=1,
321
- grammar=None,
322
- planning_interval=None,
323
  name="TravelCatalogueCreator",
324
- description=(
325
- "Expert travel planner that creates beautiful catalogues. "
326
- "ALWAYS follow this sequence: 1) research 2) weather 3) currency 4) itinerary "
327
- "5) packing list 6) generate_destination_images 7) assemble_travel_catalogue. "
328
- "NEVER skip image generation."
329
- ),
330
  prompt_templates=prompt_templates,
331
  )
332
 
333
  if __name__ == "__main__":
334
- print("🚀 Travel Catalogue Creator Agent starting...")
335
- print("💡 Example: 'Create a 4-day Barcelona travel catalogue from New York with $1200 budget for October 15-19'")
336
  GradioUI(agent, file_upload_folder="./uploads").launch()
 
1
  #!/usr/bin/env python
2
  # coding=utf-8
 
3
  import re
4
  import requests
 
 
 
5
  from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
6
  from tools.final_answer import FinalAnswerTool
7
  from Gradio_UI import GradioUI
8
  import yaml
9
 
10
  # ======================
11
+ # MINIMAL TRAVEL TOOLS (SMOLAGENTS-COMPLIANT)
12
  # ======================
13
 
14
  @tool
15
  def get_weather_forecast(location: str, travel_dates: str) -> str:
16
  """
17
+ Get weather forecast for destination using wttr.in.
18
 
19
  Args:
20
+ location: Destination city name (e.g., "Barcelona")
21
+ travel_dates: Travel date range (e.g., "October 15-19")
22
 
23
  Returns:
24
+ Weather summary with temperature and packing advice
25
  """
26
  try:
27
+ clean = re.sub(r'[^a-zA-Z0-9\s]', '', location).replace(' ', '+')
28
+ data = requests.get(f"http://wttr.in/{clean}?format=j1", timeout=10).json()
29
+ cur = data['current_condition'][0]
30
+ temp = int(cur['temp_C'])
31
+ cond = cur['lang_en'][0]['value']
32
+ if temp > 20:
33
+ pack = "Light layers, sunscreen, hat"
 
 
 
 
 
 
 
 
 
 
 
 
34
  else:
35
+ pack = "Warm layers, light jacket"
36
+ if "rain" in cond.lower():
37
+ pack += " + umbrella"
38
+ return f"{location} weather: {temp}°C, {cond}. Packing: {pack}"
39
+ except:
40
+ return f"{location} typical weather: 15-25°C. Pack light layers + light jacket + umbrella."
 
 
 
 
 
 
 
41
 
42
 
43
  @tool
44
  def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
45
  """
46
+ Convert currency using Frankfurter API.
47
 
48
  Args:
49
+ amount: Amount to convert (e.g., 1200.0)
50
+ from_currency: Source currency code (e.g., "USD")
51
+ to_currency: Target currency code (e.g., "EUR")
52
 
53
  Returns:
54
+ Formatted conversion result
55
  """
56
  try:
57
  from_currency = from_currency.upper()
58
  to_currency = to_currency.upper()
59
  if from_currency == to_currency:
60
+ return f"{amount:,.0f} {from_currency}"
61
+ rate = requests.get(
62
+ f"https://api.frankfurter.app/latest?from={from_currency}&to={to_currency}",
63
+ timeout=10
64
+ ).json()['rates'][to_currency]
65
+ return f"{amount:,.0f} {from_currency} = {amount*rate:,.0f} {to_currency} (1 {from_currency} = {rate:.2f} {to_currency})"
66
+ except:
67
+ return f"{amount:,.0f} {from_currency} (conversion unavailable)"
 
 
 
 
 
 
 
 
68
 
69
 
70
  @tool
71
+ def generate_packing_list(destination: str, weather_summary: str, trip_days: int, trip_type: str) -> str:
72
  """
73
+ Generate packing list based on weather and trip type.
74
 
75
  Args:
76
+ destination: Destination name (e.g., "Barcelona")
77
+ weather_summary: Weather conditions summary
78
+ trip_days: Number of travel days (e.g., 4)
79
+ trip_type: Trip type: "sightseeing", "beach", or "hiking"
80
 
81
  Returns:
82
+ Formatted packing checklist
83
  """
84
+ cold = "cold" in weather_summary.lower() or "jacket" in weather_summary.lower()
85
+ rain = "rain" in weather_summary.lower() or "umbrella" in weather_summary.lower()
 
 
 
 
 
86
 
87
+ tops = trip_days // 2 + 1
88
+ bottoms = trip_days // 3 + 1
 
 
 
 
 
89
 
90
+ clothes = [f"• Tops ({tops})", f"• Bottoms ({bottoms})"]
91
+ if cold:
92
+ clothes += ["• Warm jacket", "• Beanie + gloves"]
 
 
 
 
 
93
  else:
94
+ clothes += ["• Light jacket (essential)"]
95
+ if rain:
96
+ clothes += ["• Compact umbrella", "• Waterproof jacket"]
 
 
 
 
 
 
97
  if trip_type == "beach":
98
+ clothes += ["• Swimsuit", "• Flip-flops"]
 
 
 
 
 
 
 
 
 
 
99
 
100
  return (
101
+ f"🎒 PACKING LIST ({trip_days}-day {trip_type})\n"
102
+ "ESSENTIALS\n Passport + copies\n Cards + small cash\n Power adapter\n Phone + charger\n\n"
103
+ "CLOTHING\n" + "\n".join(clothes) + "\n\n"
104
+ "TOILETRIES\n• Travel toiletries\n• Medications\n• First-aid kit"
105
  )
106
 
107
 
108
  @tool
109
+ def build_itinerary(destination: str, attractions: str, budget: float, days: int) -> str:
110
  """
111
+ Create day-by-day itinerary.
112
 
113
  Args:
114
+ destination: Destination city (e.g., "Barcelona")
115
+ attractions: Comma-separated attractions list
116
+ budget: Total budget in local currency
117
+ days: Number of travel days
 
118
 
119
  Returns:
120
+ Formatted itinerary with daily schedule
121
  """
122
+ atts = [a.strip() for a in attractions.split(",") if a.strip()] or ["Old Town", "Museum", "Viewpoint", "Market"]
123
+ daily = budget / max(days, 1)
124
+ lines = [f"🗓️ {days}-DAY ITINERARY: {destination}"]
125
+ for d in range(1, days+1):
126
+ am = atts[(d-1) % len(atts)]
127
+ pm = atts[d % len(atts)]
128
+ lines += [f"DAY {d}", f" 09:00 {am}", f" 14:00 {pm}", f" 💰 Daily budget: ~{daily:,.0f}"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  return "\n".join(lines)
130
 
131
 
132
  @tool
133
+ def assemble_catalogue(destination: str, origin: str, dates: str, budget_str: str, weather: str, itinerary: str, packing: str, image_url_1: str, image_url_2: str) -> str:
134
  """
135
+ Compile final travel catalogue with embedded images.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  Args:
138
  destination: Destination city (e.g., "Barcelona")
139
+ origin: Home city (e.g., "New York")
140
+ dates: Travel dates (e.g., "Oct 15-19")
141
+ budget_str: Budget conversion result
142
+ weather: Weather forecast summary
143
+ itinerary: Day-by-day plan
144
+ packing: Packing list
145
+ image_url_1: First generated image URL from image_generation_tool
146
+ image_url_2: Second generated image URL from image_generation_tool
147
 
148
  Returns:
149
+ Beautiful Markdown travel catalogue with embedded images
150
  """
151
+ return f"""# 🌍 {destination} Travel Catalogue
152
+ *From {origin} • {dates} • Budget: {budget_str}*
 
 
 
 
 
 
153
 
154
  ---
155
 
156
+ ## 🌤️ Weather
157
+ {weather}
158
 
159
  ---
160
 
 
164
  ---
165
 
166
  ## 🎒 Packing List
167
+ {packing}
168
 
169
  ---
170
 
171
+ ## 📸 Destination Views
172
+ ![{destination} landmark]({image_url_1})
173
+ *Generated view #1*
174
+
175
+ ![{destination} street scene]({image_url_2})
176
+ *Generated view #2*
177
 
178
  ---
179
 
180
+ > 💡 Book attractions online to skip lines. Use public transport passes for savings.
181
  """
 
182
 
183
 
184
  # ======================
185
+ # AGENT SETUP (IMAGE GENERATION FORCED)
186
  # ======================
187
 
188
  final_answer = FinalAnswerTool()
189
  web_search = DuckDuckGoSearchTool()
190
+ image_gen = load_tool("agents-course/text-to-image", trust_remote_code=True)
191
 
192
  model = HfApiModel(
193
  max_tokens=2096,
194
+ temperature=0.3, # Lower temp for more reliable tool usage
195
  model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
196
  )
197
 
198
+ # CRITICAL: System prompt that FORCES image generation steps
199
+ prompt_templates = {
200
+ "system_prompt": (
201
+ "You are TravelCatalogueCreator. ALWAYS follow this EXACT workflow:\n"
202
+ "1. Use DuckDuckGoSearchTool to research destination attractions\n"
203
+ "2. Use get_weather_forecast for weather during travel dates\n"
204
+ "3. Use convert_currency to show budget in local currency\n"
205
+ "4. Use build_itinerary to create day-by-day plan\n"
206
+ "5. Use generate_packing_list for packing advice\n"
207
+ "6. CALL image_generation_tool EXACTLY TWICE with prompts:\n"
208
+ " - First call: '{destination} landmark photorealistic travel photo'\n"
209
+ " - Second call: '{destination} street scene vibrant travel photo'\n"
210
+ "7. Use assemble_catalogue with BOTH image URLs from step 6\n\n"
211
+ "NEVER skip image generation. NEVER call FinalAnswerTool directly. "
212
+ "You MUST complete all 7 steps before finishing."
213
+ )
214
+ }
215
 
216
  agent = CodeAgent(
217
  model=model,
218
  tools=[
219
  final_answer,
220
  web_search,
221
+ image_gen, # Must be available for step 6
222
  get_weather_forecast,
223
  convert_currency,
224
  generate_packing_list,
225
  build_itinerary,
226
+ assemble_catalogue, # Requires real image URLs as inputs
 
227
  ],
228
+ max_steps=20, # Allow room for 2 image generation steps
229
  verbosity_level=1,
 
 
230
  name="TravelCatalogueCreator",
231
+ description="Creates beautiful travel catalogues with mandatory image generation",
 
 
 
 
 
232
  prompt_templates=prompt_templates,
233
  )
234
 
235
  if __name__ == "__main__":
236
+ print("🚀 Travel Catalogue Creator with guaranteed image generation")
 
237
  GradioUI(agent, file_upload_folder="./uploads").launch()