NSamson1 commited on
Commit
2620440
·
verified ·
1 Parent(s): b3274cc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -90
app.py CHANGED
@@ -6,7 +6,6 @@ import gradio as gr
6
  import openai
7
 
8
  # -------------------- CONFIGURATION --------------------
9
- # Use environment variables for security (set them before running)
10
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "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")
11
  OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY", "") # Optional, for live weather
12
 
@@ -50,8 +49,7 @@ def get_weather(city: str) -> dict:
50
  data = resp.json()
51
  if resp.status_code != 200:
52
  return {"error": f"Weather API error: {data.get('message', 'unknown')}"}
53
- # Extract relevant fields
54
- weather_info = {
55
  "city": city,
56
  "temperature": data["main"]["temp"],
57
  "condition": data["weather"][0]["description"],
@@ -59,7 +57,6 @@ def get_weather(city: str) -> dict:
59
  "wind_speed": data["wind"]["speed"],
60
  "precipitation": data.get("rain", {}).get("1h", 0)
61
  }
62
- return weather_info
63
  except Exception as e:
64
  return {"error": f"Weather service unavailable: {str(e)}"}
65
 
@@ -96,52 +93,6 @@ def get_attractions(city: str) -> dict:
96
  else:
97
  return {"error": f"No attractions data available for {city}.", "city": city}
98
 
99
- def compose_itinerary(city: str, weather: dict, attractions: list, budget: dict = None) -> str:
100
- """
101
- Generate a day trip itinerary using the tool outputs.
102
- Calls OpenAI to format the plan based on verified data.
103
- """
104
- # Build context string
105
- weather_text = f"{weather['condition'].capitalize()}, {weather['temperature']}°C"
106
- if weather.get('precipitation', 0) > 0:
107
- weather_text += f", chance of rain {weather['precipitation']}mm"
108
- if "note" in weather:
109
- weather_text += f" ({weather['note']})"
110
-
111
- attractions_text = "\n".join([
112
- f"- {a['name']} ({a['type']}, ~{a['duration_hours']} hrs, entry: {a['entry_fee']} {a.get('currency', '')})"
113
- for a in attractions
114
- ])
115
-
116
- budget_text = ""
117
- if budget and "error" not in budget:
118
- budget_text = f"Budget: {budget['amount']} {budget['from']} (approx. {budget['converted']} {budget['to']} in local currency)"
119
-
120
- prompt = f"""
121
- You are a travel assistant. Create a one-day itinerary for {city} based on the following verified information.
122
-
123
- Weather: {weather_text}
124
- Attractions available:
125
- {attractions_text}
126
- {budget_text}
127
-
128
- The itinerary should:
129
- - Be realistic given the weather (e.g., outdoor activities if sunny, indoor if rain).
130
- - Sequence attractions logically (considering location and opening hours if known).
131
- - Include estimated times for each activity.
132
- - Suggest meal times and local food options (use common knowledge, do not invent specific restaurants).
133
- - If a budget is provided, mention if attractions have entry fees and stay within budget.
134
- - Use markdown for readability (headings, bullet points, emojis).
135
-
136
- Return only the itinerary.
137
- """
138
- response = client.chat.completions.create(
139
- model="gpt-3.5-turbo",
140
- messages=[{"role": "user", "content": prompt}],
141
- temperature=0.7
142
- )
143
- return response.choices[0].message.content
144
-
145
  # -------------------- TOOL SCHEMAS FOR OPENAI --------------------
146
  tools = [
147
  {
@@ -188,63 +139,73 @@ tools = [
188
  }
189
  }
190
  }
191
- # Note: compose_itinerary is not exposed as a tool; it's called by the agent after gathering data.
192
  ]
193
 
194
  # -------------------- AGENT ORCHESTRATION --------------------
195
  def run_agent(user_message, history):
196
  """
197
- Main agent loop: maintains conversation, calls tools, and finally generates itinerary.
198
  """
199
- # System prompt that guides the agent
200
  system_prompt = """You are a helpful travel planning assistant. You have access to tools that can:
201
  - Get current weather for a city
202
  - Convert currency
203
  - Retrieve tourist attractions for a city
204
 
205
- Your goal is to help the user plan a day trip. If the user's request is missing information (e.g., city, budget, currency), ask clarifying questions.
206
- When you have gathered all necessary data (city, weather, attractions, and optionally budget), call the appropriate tools to fetch the data, then use that information to compose a final itinerary.
207
- Do not invent any data; only use the tool outputs.
208
- Be friendly, concise, and helpful."""
 
 
209
 
 
210
  messages = [{"role": "system", "content": system_prompt}]
211
- # Add conversation history (Gradio passes history as list of [user, assistant] pairs)
212
  for user_msg, asst_msg in history:
213
  messages.append({"role": "user", "content": user_msg})
214
  if asst_msg:
215
  messages.append({"role": "assistant", "content": asst_msg})
216
  messages.append({"role": "user", "content": user_message})
217
 
218
- # We'll collect tool outputs in a dict for later use
219
- tool_outputs = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
- # Max iterations to prevent infinite loops
222
- for _ in range(5):
223
- response = client.chat.completions.create(
224
- model="gpt-3.5-turbo",
225
- messages=messages,
226
- tools=tools,
227
- tool_choice="auto"
228
- )
229
  msg = response.choices[0].message
230
 
231
  # If the model wants to call tools
232
  if msg.tool_calls:
 
233
  # Add assistant message with tool calls to history
234
  messages.append(msg)
235
  for tool_call in msg.tool_calls:
236
  func_name = tool_call.function.name
237
  args = json.loads(tool_call.function.arguments)
 
 
238
  # Execute the tool
239
  if func_name == "get_weather":
240
  result = get_weather(args["city"])
241
- tool_outputs["weather"] = result
242
  elif func_name == "convert_currency":
243
  result = convert_currency(args["amount"], args["from_currency"], args["to_currency"])
244
- tool_outputs["budget"] = result
245
  elif func_name == "get_attractions":
246
  result = get_attractions(args["city"])
247
- tool_outputs["attractions"] = result
248
  else:
249
  result = {"error": "Unknown tool"}
250
 
@@ -254,27 +215,17 @@ Be friendly, concise, and helpful."""
254
  "tool_call_id": tool_call.id,
255
  "content": json.dumps(result)
256
  })
257
- # Continue loop to let model process tool responses
 
258
  continue
259
  else:
260
- # No tool calls: either final answer or clarification request
261
  final_text = msg.content
262
- # If we have all necessary data, we might want to generate the itinerary via compose_itinerary
263
- # But the model may already have done it if we instructed it to.
264
- # To be safe, we can check if we have weather and attractions, and then call compose_itinerary.
265
- if "weather" in tool_outputs and "attractions" in tool_outputs and "error" not in tool_outputs["attractions"]:
266
- # We have the data, generate itinerary
267
- city = tool_outputs["attractions"]["city"]
268
- weather = tool_outputs["weather"]
269
- attractions = tool_outputs["attractions"]["attractions"]
270
- budget = tool_outputs.get("budget")
271
- itinerary = compose_itinerary(city, weather, attractions, budget)
272
- return itinerary
273
- else:
274
- # Model's response is a clarification or intermediate answer
275
- return final_text
276
 
277
- return "I'm having trouble processing your request. Please try again."
 
278
 
279
  # -------------------- GRADIO INTERFACE --------------------
280
  def chat_interface(message, history):
@@ -293,7 +244,6 @@ with gr.Blocks(title="TouristGuide AI Agent") as demo:
293
  Your personal travel planner powered by AI and real-time tools.
294
  Ask for a day trip itinerary in any supported city (Kigali, Kampala, Nairobi, etc.) with optional budget and currency conversion.
295
  """)
296
- # Removed bubble_full_width (no longer supported)
297
  chatbot = gr.Chatbot(label="Conversation", height=500)
298
  msg = gr.Textbox(label="Your message", placeholder="e.g., Plan a day trip in Kigali with a budget of 150 USD", lines=2)
299
  clear = gr.Button("Clear conversation")
@@ -307,5 +257,4 @@ with gr.Blocks(title="TouristGuide AI Agent") as demo:
307
  clear.click(lambda: None, None, chatbot, queue=False)
308
 
309
  if __name__ == "__main__":
310
- # Pass css to launch() as per Gradio 6.0+
311
  demo.launch(share=False, server_name="0.0.0.0", css=css)
 
6
  import openai
7
 
8
  # -------------------- CONFIGURATION --------------------
 
9
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "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")
10
  OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY", "") # Optional, for live weather
11
 
 
49
  data = resp.json()
50
  if resp.status_code != 200:
51
  return {"error": f"Weather API error: {data.get('message', 'unknown')}"}
52
+ return {
 
53
  "city": city,
54
  "temperature": data["main"]["temp"],
55
  "condition": data["weather"][0]["description"],
 
57
  "wind_speed": data["wind"]["speed"],
58
  "precipitation": data.get("rain", {}).get("1h", 0)
59
  }
 
60
  except Exception as e:
61
  return {"error": f"Weather service unavailable: {str(e)}"}
62
 
 
93
  else:
94
  return {"error": f"No attractions data available for {city}.", "city": city}
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  # -------------------- TOOL SCHEMAS FOR OPENAI --------------------
97
  tools = [
98
  {
 
139
  }
140
  }
141
  }
 
142
  ]
143
 
144
  # -------------------- AGENT ORCHESTRATION --------------------
145
  def run_agent(user_message, history):
146
  """
147
+ Main agent loop: maintains conversation, calls tools, and returns final answer.
148
  """
149
+ # System prompt with clear instructions
150
  system_prompt = """You are a helpful travel planning assistant. You have access to tools that can:
151
  - Get current weather for a city
152
  - Convert currency
153
  - Retrieve tourist attractions for a city
154
 
155
+ Your goal is to help the user plan a day trip. Follow these steps:
156
+ 1. If the user's request is missing essential information (city, budget, currency, etc.), ask clarifying questions.
157
+ 2. Once you have all necessary details, call the appropriate tools to fetch real-time data.
158
+ 3. After receiving tool results, use that information to compose a detailed, day-long itinerary.
159
+ 4. Do not invent any data; only use the tool outputs.
160
+ 5. Be friendly, concise, and helpful. Use markdown for readability (headings, bullet points, emojis)."""
161
 
162
+ # Build message list from history
163
  messages = [{"role": "system", "content": system_prompt}]
 
164
  for user_msg, asst_msg in history:
165
  messages.append({"role": "user", "content": user_msg})
166
  if asst_msg:
167
  messages.append({"role": "assistant", "content": asst_msg})
168
  messages.append({"role": "user", "content": user_message})
169
 
170
+ # Debug: print the conversation so far
171
+ print("\n" + "="*50)
172
+ print("USER MESSAGE:", user_message)
173
+ print("="*50)
174
+
175
+ # Iterate up to 5 turns (to prevent infinite loops)
176
+ for turn in range(5):
177
+ print(f"\n--- Turn {turn+1} ---")
178
+ try:
179
+ response = client.chat.completions.create(
180
+ model="gpt-3.5-turbo", # You can change to "gpt-4" if available
181
+ messages=messages,
182
+ tools=tools,
183
+ tool_choice="auto"
184
+ )
185
+ except Exception as e:
186
+ error_msg = f"OpenAI API error: {str(e)}"
187
+ print(error_msg)
188
+ return error_msg
189
 
 
 
 
 
 
 
 
 
190
  msg = response.choices[0].message
191
 
192
  # If the model wants to call tools
193
  if msg.tool_calls:
194
+ print(f"Model requested {len(msg.tool_calls)} tool call(s).")
195
  # Add assistant message with tool calls to history
196
  messages.append(msg)
197
  for tool_call in msg.tool_calls:
198
  func_name = tool_call.function.name
199
  args = json.loads(tool_call.function.arguments)
200
+ print(f" Calling {func_name} with args: {args}")
201
+
202
  # Execute the tool
203
  if func_name == "get_weather":
204
  result = get_weather(args["city"])
 
205
  elif func_name == "convert_currency":
206
  result = convert_currency(args["amount"], args["from_currency"], args["to_currency"])
 
207
  elif func_name == "get_attractions":
208
  result = get_attractions(args["city"])
 
209
  else:
210
  result = {"error": "Unknown tool"}
211
 
 
215
  "tool_call_id": tool_call.id,
216
  "content": json.dumps(result)
217
  })
218
+ print(f" Result: {result}")
219
+ # Continue the loop so the model can see the tool results
220
  continue
221
  else:
222
+ # No tool calls: this is the final answer (or a clarification)
223
  final_text = msg.content
224
+ print(f"Final answer: {final_text[:100]}...") # print first 100 chars
225
+ return final_text
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
+ # If we exit the loop without returning, something went wrong
228
+ return "I'm having trouble processing your request. Please try again or rephrase."
229
 
230
  # -------------------- GRADIO INTERFACE --------------------
231
  def chat_interface(message, history):
 
244
  Your personal travel planner powered by AI and real-time tools.
245
  Ask for a day trip itinerary in any supported city (Kigali, Kampala, Nairobi, etc.) with optional budget and currency conversion.
246
  """)
 
247
  chatbot = gr.Chatbot(label="Conversation", height=500)
248
  msg = gr.Textbox(label="Your message", placeholder="e.g., Plan a day trip in Kigali with a budget of 150 USD", lines=2)
249
  clear = gr.Button("Clear conversation")
 
257
  clear.click(lambda: None, None, chatbot, queue=False)
258
 
259
  if __name__ == "__main__":
 
260
  demo.launch(share=False, server_name="0.0.0.0", css=css)