Update app.py
Browse files
app.py
CHANGED
|
@@ -5,16 +5,19 @@ from datetime import datetime, timedelta
|
|
| 5 |
from functools import lru_cache
|
| 6 |
import gradio as gr
|
| 7 |
import openai
|
| 8 |
-
OPENAI_API_KEY
|
| 9 |
-
# -------------------- CONFIGURATION --------------------
|
| 10 |
-
# Use the provided OpenAI API key (lecturer paid for it)
|
| 11 |
-
OPENAI_API_KEY = "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"
|
| 12 |
-
|
| 13 |
-
# Optional: set OpenWeatherMap API key as environment variable (lecturer will provide)
|
| 14 |
-
OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY", "") # Set this in Hugging Face Secrets
|
| 15 |
-
SERP_API_KEY = os.getenv("SERPAPI_API_KEY", "") # Optional for realโtime attractions
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
# Local attractions database (can be expanded)
|
| 20 |
try:
|
|
@@ -28,7 +31,7 @@ except FileNotFoundError:
|
|
| 28 |
# -------------------- TOOL IMPLEMENTATIONS --------------------
|
| 29 |
def get_weather(city: str) -> dict:
|
| 30 |
"""
|
| 31 |
-
Return current weather for any city using
|
| 32 |
Returns a dict with keys: city, temperature, condition, humidity, wind_speed, precipitation.
|
| 33 |
On error, returns dict with 'error' key.
|
| 34 |
"""
|
|
@@ -56,7 +59,6 @@ def get_weather(city: str) -> dict:
|
|
| 56 |
if response.status_code != 200:
|
| 57 |
return {"error": f"Weather API error: {data.get('message', 'unknown')}"}
|
| 58 |
|
| 59 |
-
# Extract relevant fields
|
| 60 |
return {
|
| 61 |
"city": city,
|
| 62 |
"temperature": data['main']['temp'],
|
|
@@ -123,6 +125,13 @@ def fetch_attractions_via_serp(city: str) -> list:
|
|
| 123 |
|
| 124 |
def fetch_attractions_via_openai(city: str) -> list:
|
| 125 |
"""Use OpenAI to generate a list of top attractions for any city."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
try:
|
| 127 |
prompt = f"""
|
| 128 |
List the top 8 tourist attractions in {city}. For each, provide:
|
|
@@ -146,7 +155,6 @@ Do not include any other text.
|
|
| 146 |
if isinstance(attractions, dict) and "attractions" in attractions:
|
| 147 |
attractions = attractions["attractions"]
|
| 148 |
elif not isinstance(attractions, list):
|
| 149 |
-
# Try to extract list
|
| 150 |
attractions = [attractions]
|
| 151 |
# Normalize to our format
|
| 152 |
result = []
|
|
@@ -160,7 +168,6 @@ Do not include any other text.
|
|
| 160 |
return result
|
| 161 |
except Exception as e:
|
| 162 |
print(f"OpenAI attractions error: {e}")
|
| 163 |
-
# Fallback to a simple generic list
|
| 164 |
return [
|
| 165 |
{"name": f"City center", "type": "landmark", "entry_fee": "free", "duration_hours": 2},
|
| 166 |
{"name": f"Local museum", "type": "museum", "entry_fee": "varies", "duration_hours": 2},
|
|
@@ -180,9 +187,10 @@ def get_attractions(city: str) -> dict:
|
|
| 180 |
if serp_attractions:
|
| 181 |
return {"city": city, "attractions": serp_attractions, "source": "SerpAPI"}
|
| 182 |
|
| 183 |
-
# 3. Fallback to OpenAI (
|
| 184 |
openai_attractions = fetch_attractions_via_openai(city)
|
| 185 |
-
|
|
|
|
| 186 |
|
| 187 |
def get_tripadvisor_link(query):
|
| 188 |
"""Fetch a TripAdvisor link using SerpAPI (optional)."""
|
|
@@ -240,6 +248,10 @@ def generate_itinerary(destination, start_date, num_days, budget_type, departure
|
|
| 240 |
|
| 241 |
# 5. Attempt to call OpenAI for itinerary
|
| 242 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
prompt = f"""
|
| 244 |
You are an expert travel planner. Create a detailed, day-wise travel itinerary based on the verified information below.
|
| 245 |
|
|
@@ -297,26 +309,10 @@ Follow these rules:
|
|
| 297 |
break
|
| 298 |
itinerary = "\n".join(enhanced)
|
| 299 |
|
| 300 |
-
except openai.RateLimitError as e:
|
| 301 |
-
# Quota exceeded โ fallback to manual itinerary
|
| 302 |
-
itinerary = f"""
|
| 303 |
-
# ๐ Manual Itinerary for {destination} (OpenAI quota exceeded)
|
| 304 |
-
|
| 305 |
-
**Weather:** {weather_text}
|
| 306 |
-
**Attractions available:**
|
| 307 |
-
{attractions_list}
|
| 308 |
-
{source_note}
|
| 309 |
-
**Budget:** {budget_text if budget_text else 'Not specified'}
|
| 310 |
-
|
| 311 |
-
*Since the AI service is temporarily unavailable (quota exceeded), please use the above information to plan your trip manually. You can search for opening hours, entry fees, and book activities online.*
|
| 312 |
-
|
| 313 |
-
๐๏ธ [Search Hotels on Booking.com](https://www.booking.com/searchresults.html?ss={destination.replace(' ', '+')})
|
| 314 |
-
โ๏ธ [Search Flights on Skyscanner](https://www.skyscanner.co.in/)
|
| 315 |
-
"""
|
| 316 |
except Exception as e:
|
| 317 |
-
#
|
| 318 |
itinerary = f"""
|
| 319 |
-
#
|
| 320 |
|
| 321 |
**Weather:** {weather_text}
|
| 322 |
**Attractions available:**
|
|
@@ -324,7 +320,7 @@ Follow these rules:
|
|
| 324 |
{source_note}
|
| 325 |
**Budget:** {budget_text if budget_text else 'Not specified'}
|
| 326 |
|
| 327 |
-
|
| 328 |
|
| 329 |
๐๏ธ [Search Hotels on Booking.com](https://www.booking.com/searchresults.html?ss={destination.replace(' ', '+')})
|
| 330 |
โ๏ธ [Search Flights on Skyscanner](https://www.skyscanner.co.in/)
|
|
@@ -353,7 +349,7 @@ with gr.Blocks(css=css) as demo:
|
|
| 353 |
gr.Markdown("""
|
| 354 |
# ๐ TouristGuide AI Agent
|
| 355 |
Your personal travel planner powered by real-time weather, attractions, and OpenAI.
|
| 356 |
-
Works for **any city** โ if no local data
|
| 357 |
""")
|
| 358 |
with gr.Row():
|
| 359 |
destination = gr.Textbox(label="Destination (any city)", placeholder="e.g., Paris, Tokyo, New York", scale=2)
|
|
|
|
| 5 |
from functools import lru_cache
|
| 6 |
import gradio as gr
|
| 7 |
import openai
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
+
# -------------------- CONFIGURATION --------------------
|
| 10 |
+
# Read API keys from environment variables (set in Hugging Face Secrets)
|
| 11 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 12 |
+
OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY", "")
|
| 13 |
+
SERP_API_KEY = os.getenv("SERPAPI_API_KEY", "")
|
| 14 |
+
|
| 15 |
+
# Warn if OpenAI key is missing (but continue with fallbacks)
|
| 16 |
+
if not OPENAI_API_KEY:
|
| 17 |
+
print("โ ๏ธ OPENAI_API_KEY not set. AI itinerary generation will fall back to manual mode.")
|
| 18 |
+
client = None
|
| 19 |
+
else:
|
| 20 |
+
client = openai.OpenAI(api_key=OPENAI_API_KEY)
|
| 21 |
|
| 22 |
# Local attractions database (can be expanded)
|
| 23 |
try:
|
|
|
|
| 31 |
# -------------------- TOOL IMPLEMENTATIONS --------------------
|
| 32 |
def get_weather(city: str) -> dict:
|
| 33 |
"""
|
| 34 |
+
Return current weather for any city using OpenWeatherMap API (if key set).
|
| 35 |
Returns a dict with keys: city, temperature, condition, humidity, wind_speed, precipitation.
|
| 36 |
On error, returns dict with 'error' key.
|
| 37 |
"""
|
|
|
|
| 59 |
if response.status_code != 200:
|
| 60 |
return {"error": f"Weather API error: {data.get('message', 'unknown')}"}
|
| 61 |
|
|
|
|
| 62 |
return {
|
| 63 |
"city": city,
|
| 64 |
"temperature": data['main']['temp'],
|
|
|
|
| 125 |
|
| 126 |
def fetch_attractions_via_openai(city: str) -> list:
|
| 127 |
"""Use OpenAI to generate a list of top attractions for any city."""
|
| 128 |
+
if not client:
|
| 129 |
+
# If no OpenAI client, return generic list
|
| 130 |
+
return [
|
| 131 |
+
{"name": f"City center", "type": "landmark", "entry_fee": "free", "duration_hours": 2},
|
| 132 |
+
{"name": f"Local museum", "type": "museum", "entry_fee": "varies", "duration_hours": 2},
|
| 133 |
+
{"name": f"Popular park", "type": "nature", "entry_fee": "free", "duration_hours": 2}
|
| 134 |
+
]
|
| 135 |
try:
|
| 136 |
prompt = f"""
|
| 137 |
List the top 8 tourist attractions in {city}. For each, provide:
|
|
|
|
| 155 |
if isinstance(attractions, dict) and "attractions" in attractions:
|
| 156 |
attractions = attractions["attractions"]
|
| 157 |
elif not isinstance(attractions, list):
|
|
|
|
| 158 |
attractions = [attractions]
|
| 159 |
# Normalize to our format
|
| 160 |
result = []
|
|
|
|
| 168 |
return result
|
| 169 |
except Exception as e:
|
| 170 |
print(f"OpenAI attractions error: {e}")
|
|
|
|
| 171 |
return [
|
| 172 |
{"name": f"City center", "type": "landmark", "entry_fee": "free", "duration_hours": 2},
|
| 173 |
{"name": f"Local museum", "type": "museum", "entry_fee": "varies", "duration_hours": 2},
|
|
|
|
| 187 |
if serp_attractions:
|
| 188 |
return {"city": city, "attractions": serp_attractions, "source": "SerpAPI"}
|
| 189 |
|
| 190 |
+
# 3. Fallback to OpenAI (or generic if no key)
|
| 191 |
openai_attractions = fetch_attractions_via_openai(city)
|
| 192 |
+
source = "OpenAI" if client else "generic fallback"
|
| 193 |
+
return {"city": city, "attractions": openai_attractions, "source": source}
|
| 194 |
|
| 195 |
def get_tripadvisor_link(query):
|
| 196 |
"""Fetch a TripAdvisor link using SerpAPI (optional)."""
|
|
|
|
| 248 |
|
| 249 |
# 5. Attempt to call OpenAI for itinerary
|
| 250 |
try:
|
| 251 |
+
if not client:
|
| 252 |
+
# No OpenAI key โ show manual plan
|
| 253 |
+
raise Exception("OpenAI client not available")
|
| 254 |
+
|
| 255 |
prompt = f"""
|
| 256 |
You are an expert travel planner. Create a detailed, day-wise travel itinerary based on the verified information below.
|
| 257 |
|
|
|
|
| 309 |
break
|
| 310 |
itinerary = "\n".join(enhanced)
|
| 311 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
except Exception as e:
|
| 313 |
+
# Fallback to manual itinerary (no AI)
|
| 314 |
itinerary = f"""
|
| 315 |
+
# ๐ Manual Itinerary for {destination}
|
| 316 |
|
| 317 |
**Weather:** {weather_text}
|
| 318 |
**Attractions available:**
|
|
|
|
| 320 |
{source_note}
|
| 321 |
**Budget:** {budget_text if budget_text else 'Not specified'}
|
| 322 |
|
| 323 |
+
*Since the AI service is unavailable (API key missing or quota exceeded), please use the above information to plan your trip manually. You can search for opening hours, entry fees, and book activities online.*
|
| 324 |
|
| 325 |
๐๏ธ [Search Hotels on Booking.com](https://www.booking.com/searchresults.html?ss={destination.replace(' ', '+')})
|
| 326 |
โ๏ธ [Search Flights on Skyscanner](https://www.skyscanner.co.in/)
|
|
|
|
| 349 |
gr.Markdown("""
|
| 350 |
# ๐ TouristGuide AI Agent
|
| 351 |
Your personal travel planner powered by real-time weather, attractions, and OpenAI.
|
| 352 |
+
Works for **any city** โ if no local data, it uses SerpAPI or OpenAI to suggest attractions.
|
| 353 |
""")
|
| 354 |
with gr.Row():
|
| 355 |
destination = gr.Textbox(label="Destination (any city)", placeholder="e.g., Paris, Tokyo, New York", scale=2)
|