Update app.py
Browse files
app.py
CHANGED
|
@@ -14,7 +14,7 @@ from playwright.async_api import async_playwright
|
|
| 14 |
# --- Langchain Imports ---
|
| 15 |
from langchain_core.prompts import ChatPromptTemplate
|
| 16 |
from langchain_core.output_parsers import JsonOutputParser
|
| 17 |
-
from
|
| 18 |
from langchain_openai import ChatOpenAI
|
| 19 |
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 20 |
from langchain_core.messages import SystemMessage, HumanMessage
|
|
@@ -23,34 +23,37 @@ from langchain_core.messages import SystemMessage, HumanMessage
|
|
| 23 |
# On Hugging Face Spaces, secrets are automatically available as environment variables.
|
| 24 |
load_dotenv()
|
| 25 |
|
| 26 |
-
# --- Playwright Browser Installation
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
PLAYWRIGHT_BROWSERS_PATH = os.path.expanduser("~/.cache/ms-playwright")
|
| 30 |
-
PLAYWRIGHT_INSTALLED_FLAG = "PLAYWRIGHT_BROWSERS_INSTALLED"
|
| 31 |
-
|
| 32 |
-
# Check if installation has been attempted during this container's lifetime
|
| 33 |
-
if not os.path.exists(os.path.join(PLAYWRIGHT_BROWSERS_PATH, "chromium")) and \
|
| 34 |
-
os.environ.get(PLAYWRIGHT_INSTALLED_FLAG, "false") == "false":
|
| 35 |
-
print("Playwright browsers not found. Attempting to install them now (this might take a moment)...")
|
| 36 |
try:
|
| 37 |
-
#
|
| 38 |
-
|
| 39 |
-
result = subprocess.run([
|
| 40 |
-
|
| 41 |
-
|
|
|
|
| 42 |
if result.stdout:
|
| 43 |
print("Playwright Install STDOUT:\n", result.stdout)
|
| 44 |
if result.stderr:
|
| 45 |
print("Playwright Install STDERR:\n", result.stderr)
|
| 46 |
|
|
|
|
|
|
|
|
|
|
| 47 |
except subprocess.CalledProcessError as e:
|
| 48 |
-
print(f"Error during Playwright browser installation: {e}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
except Exception as e:
|
| 50 |
print(f"Unexpected error during Playwright browser installation: {e}")
|
| 51 |
-
|
| 52 |
-
print("Playwright browsers already appear to be installed or installation skipped.")
|
| 53 |
|
|
|
|
|
|
|
| 54 |
|
| 55 |
# --- 1. API KEY CHECK ---
|
| 56 |
# This block ensures that the necessary API keys are set before the application starts.
|
|
@@ -59,9 +62,6 @@ try:
|
|
| 59 |
except KeyError:
|
| 60 |
raise EnvironmentError("Missing OPENAI_API_KEY. Please set it in a .env file locally, or in Hugging Face Space secrets.")
|
| 61 |
|
| 62 |
-
# Removed PERPLEXITY_API_KEY check as it's no longer used.
|
| 63 |
-
|
| 64 |
-
|
| 65 |
# --- 2. PYDANTIC DATA STRUCTURE DEFINITION ---
|
| 66 |
class TravelRequest(BaseModel):
|
| 67 |
departure_city: str = Field(description="The city or airport of departure. Infer if not specified.")
|
|
@@ -76,9 +76,6 @@ class TravelRequest(BaseModel):
|
|
| 76 |
activity_interests: str = Field(description="Specific interests for activities (e.g., 'museums', 'hiking').")
|
| 77 |
|
| 78 |
# --- 3. ASYNC SEARCH FUNCTIONS (Some Simulated, Some Live) ---
|
| 79 |
-
# All these functions are designed to return a LIST of dictionaries,
|
| 80 |
-
# even if only one dictionary is returned. This is crucial for `all_results.extend()`.
|
| 81 |
-
|
| 82 |
async def search_skyscanner(details):
|
| 83 |
"""Simulates searching for flights on Skyscanner."""
|
| 84 |
print(f"Searching Skyscanner for: {details['destination_city']}")
|
|
@@ -105,115 +102,107 @@ async def search_get_your_guide(details):
|
|
| 105 |
|
| 106 |
async def scrape_flight_vouchers(details):
|
| 107 |
"""
|
| 108 |
-
Performs
|
| 109 |
-
This function navigates to common deal sites and attempts to extract voucher information.
|
| 110 |
-
Note: Web scraping is highly dependent on website structure and may require frequent updates.
|
| 111 |
"""
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
-
|
| 116 |
target_urls = [
|
| 117 |
"https://www.retailmenot.com/coupons/flights",
|
| 118 |
]
|
| 119 |
|
| 120 |
-
browser = None # Initialize browser variable outside the try block
|
| 121 |
try:
|
| 122 |
async with async_playwright() as p:
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
-
# Add a human-like User-Agent to try and bypass basic bot detection.
|
| 127 |
context = await browser.new_context(
|
| 128 |
-
user_agent="Mozilla/5.0 (
|
| 129 |
)
|
| 130 |
page = await context.new_page()
|
| 131 |
|
| 132 |
for url in target_urls:
|
| 133 |
try:
|
| 134 |
print(f"Navigating to: {url}")
|
| 135 |
-
|
| 136 |
-
await page.goto(url, wait_until='domcontentloaded', timeout=60000)
|
| 137 |
-
|
| 138 |
-
# --- Generic scraping strategy ---
|
| 139 |
-
deals_locator = page.locator('div:has-text("coupon code"), div:has-text("promo code"), div:has-text("discount"), '
|
| 140 |
-
'a[href*="deal"], a[href*="promo"], a[href*="discount"], '
|
| 141 |
-
'[class*="deal"], [class*="promo"]')
|
| 142 |
|
|
|
|
|
|
|
| 143 |
deals = await deals_locator.all()
|
| 144 |
|
| 145 |
-
for i, deal_elem in enumerate(deals):
|
| 146 |
-
if len(vouchers_found) >= 3: # Limit the number of vouchers found
|
| 147 |
-
break
|
| 148 |
try:
|
| 149 |
text_content = await deal_elem.inner_text()
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
if len(text_content.strip()) > 20 and \
|
| 153 |
-
("flight" in text_content.lower() or \
|
| 154 |
-
"travel" in text_content.lower() or \
|
| 155 |
-
details.get('destination_city', '').lower() in text_content.lower()):
|
| 156 |
-
|
| 157 |
code_match = re.search(r'\b[A-Z0-9]{4,10}\b', text_content)
|
| 158 |
code = code_match.group(0) if code_match else "N/A"
|
| 159 |
|
| 160 |
vouchers_found.append({
|
| 161 |
"source": f"Scraped from {url.split('/')[2]}",
|
| 162 |
"type": "voucher",
|
| 163 |
-
"details": f"{text_content[:
|
| 164 |
"price": f"Code: {code}",
|
| 165 |
-
"link":
|
| 166 |
})
|
| 167 |
except Exception as e_deal:
|
| 168 |
-
print(f"Error processing deal element
|
| 169 |
continue
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
"source": f"Scraper Error for {url.split('/')[2]}",
|
| 174 |
-
"type": "voucher",
|
| 175 |
-
"details": f"Could not retrieve vouchers from {url.split('/')[2]} due to navigation error. (Error: {e_url_nav})",
|
| 176 |
-
"price": "N/A",
|
| 177 |
-
"link": url
|
| 178 |
-
})
|
| 179 |
continue
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
"details": f"Failed to start web scraper: {e_browser_launch}. Please ensure Playwright browsers are installed locally and on Hugging Face.",
|
| 186 |
-
"price": "N/A",
|
| 187 |
-
"link": "#"
|
| 188 |
-
}]
|
| 189 |
-
except Exception as e_general:
|
| 190 |
-
print(f"An unexpected error occurred during scraping: {e_general}")
|
| 191 |
return [{
|
| 192 |
-
"source": "
|
| 193 |
"type": "voucher",
|
| 194 |
-
"details": f"
|
| 195 |
"price": "N/A",
|
| 196 |
"link": "#"
|
| 197 |
}]
|
| 198 |
-
finally:
|
| 199 |
-
if browser:
|
| 200 |
-
await browser.close()
|
| 201 |
|
| 202 |
if not vouchers_found:
|
| 203 |
-
|
| 204 |
return [{
|
| 205 |
-
"source": "
|
| 206 |
"type": "voucher",
|
| 207 |
-
"details": "
|
| 208 |
-
"price": "
|
| 209 |
-
"link": "
|
| 210 |
}]
|
|
|
|
| 211 |
return vouchers_found
|
| 212 |
|
| 213 |
-
|
| 214 |
-
# search_perplexity_web function is removed
|
| 215 |
-
|
| 216 |
-
|
| 217 |
async def search_llm_redundancy(details, llm, llm_name):
|
| 218 |
"""
|
| 219 |
Queries another LLM for additional, general travel ideas or insights.
|
|
@@ -222,15 +211,18 @@ async def search_llm_redundancy(details, llm, llm_name):
|
|
| 222 |
prompt = f"""As a helpful travel assistant, provide some brief travel suggestions for a trip based on these details: {details}.
|
| 223 |
Focus on general tips, hidden gems, or activity ideas. Do not suggest specific prices or flights."""
|
| 224 |
messages = [SystemMessage(content=prompt), HumanMessage(content="What are your suggestions?")]
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
# --- 4. MAIN BOT LOGIC (ask_bot function) ---
|
| 229 |
async def ask_bot(question):
|
| 230 |
"""
|
| 231 |
This is the main ASYNCHRONOUS function for the bot.
|
| 232 |
-
It orchestrates the extraction of user intent, concurrent searching,
|
| 233 |
-
data processing, and final output formatting.
|
| 234 |
"""
|
| 235 |
# Part 1: Extract structured data from the user's natural language request using an LLM.
|
| 236 |
try:
|
|
@@ -248,19 +240,15 @@ async def ask_bot(question):
|
|
| 248 |
|
| 249 |
# Part 2: Gather all search tasks to run concurrently.
|
| 250 |
llm_openai_creative = ChatOpenAI(model="gpt-4o", temperature=0.7)
|
| 251 |
-
# Uncomment the line below and ensure GOOGLE_API_KEY is set in your .env for Gemini redundancy.
|
| 252 |
-
# llm_gemini_search = ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0.7)
|
| 253 |
|
| 254 |
# Compile all the asynchronous search functions into a list of tasks.
|
| 255 |
tasks = [
|
| 256 |
-
search_skyscanner(structured_request),
|
| 257 |
-
search_expedia(structured_request),
|
| 258 |
-
scrape_Google_Flights(structured_request),
|
| 259 |
-
search_get_your_guide(structured_request),
|
| 260 |
-
scrape_flight_vouchers(structured_request),
|
| 261 |
-
search_llm_redundancy(structured_request, llm_openai_creative, "ChatGPT (Creative)"),
|
| 262 |
-
# search_perplexity_web(structured_request) # Removed Perplexity AI task
|
| 263 |
-
# search_llm_redundancy(structured_request, llm_gemini_search, "Gemini"), # Uncomment for Gemini insights
|
| 264 |
]
|
| 265 |
|
| 266 |
# Part 3: Run all tasks concurrently and collect their results.
|
|
@@ -327,8 +315,8 @@ async def ask_bot(question):
|
|
| 327 |
output_text += "_No specific flight vouchers or codes found at this time._\n"
|
| 328 |
output_text += "\n"
|
| 329 |
|
| 330 |
-
# Format AI Insights section
|
| 331 |
-
output_text += "### 💡 Additional AI Insights
|
| 332 |
if insights:
|
| 333 |
for insight in insights:
|
| 334 |
output_text += f"* **{insight['source']} says:** {insight['details']}\n"
|
|
@@ -340,12 +328,17 @@ async def ask_bot(question):
|
|
| 340 |
|
| 341 |
return output_text
|
| 342 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
# --- 5. DEFINE THE GRADIO INTERFACE ---
|
| 344 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
| 345 |
gr.Markdown(
|
| 346 |
"""
|
| 347 |
# ✨ The Ultimate Global Travel Planner Bot - Bazinga Edition ✨
|
| 348 |
-
Your smart AI assistant for finding the best flights, hotels, and activities worldwide
|
| 349 |
"""
|
| 350 |
)
|
| 351 |
with gr.Row():
|
|
@@ -364,8 +357,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
|
| 364 |
inputs=user_input
|
| 365 |
)
|
| 366 |
|
| 367 |
-
submit_button.click(fn=
|
| 368 |
|
| 369 |
# --- 6. LAUNCH THE APP ---
|
| 370 |
if __name__ == "__main__":
|
| 371 |
-
iface.launch()
|
|
|
|
| 14 |
# --- Langchain Imports ---
|
| 15 |
from langchain_core.prompts import ChatPromptTemplate
|
| 16 |
from langchain_core.output_parsers import JsonOutputParser
|
| 17 |
+
from pydantic import BaseModel, Field
|
| 18 |
from langchain_openai import ChatOpenAI
|
| 19 |
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 20 |
from langchain_core.messages import SystemMessage, HumanMessage
|
|
|
|
| 23 |
# On Hugging Face Spaces, secrets are automatically available as environment variables.
|
| 24 |
load_dotenv()
|
| 25 |
|
| 26 |
+
# --- Improved Playwright Browser Installation for Hugging Face Spaces ---
|
| 27 |
+
async def ensure_playwright_browsers():
|
| 28 |
+
"""Ensures Playwright browsers are installed and available."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
try:
|
| 30 |
+
# First, install Playwright browsers
|
| 31 |
+
print("Installing Playwright browsers...")
|
| 32 |
+
result = subprocess.run([
|
| 33 |
+
sys.executable, "-m", "playwright", "install", "chromium"
|
| 34 |
+
], check=True, capture_output=True, text=True)
|
| 35 |
+
|
| 36 |
if result.stdout:
|
| 37 |
print("Playwright Install STDOUT:\n", result.stdout)
|
| 38 |
if result.stderr:
|
| 39 |
print("Playwright Install STDERR:\n", result.stderr)
|
| 40 |
|
| 41 |
+
print("Playwright browsers installed successfully!")
|
| 42 |
+
return True
|
| 43 |
+
|
| 44 |
except subprocess.CalledProcessError as e:
|
| 45 |
+
print(f"Error during Playwright browser installation: {e}")
|
| 46 |
+
if e.stdout:
|
| 47 |
+
print("STDOUT:", e.stdout)
|
| 48 |
+
if e.stderr:
|
| 49 |
+
print("STDERR:", e.stderr)
|
| 50 |
+
return False
|
| 51 |
except Exception as e:
|
| 52 |
print(f"Unexpected error during Playwright browser installation: {e}")
|
| 53 |
+
return False
|
|
|
|
| 54 |
|
| 55 |
+
# Initialize browsers on startup
|
| 56 |
+
browsers_ready = False
|
| 57 |
|
| 58 |
# --- 1. API KEY CHECK ---
|
| 59 |
# This block ensures that the necessary API keys are set before the application starts.
|
|
|
|
| 62 |
except KeyError:
|
| 63 |
raise EnvironmentError("Missing OPENAI_API_KEY. Please set it in a .env file locally, or in Hugging Face Space secrets.")
|
| 64 |
|
|
|
|
|
|
|
|
|
|
| 65 |
# --- 2. PYDANTIC DATA STRUCTURE DEFINITION ---
|
| 66 |
class TravelRequest(BaseModel):
|
| 67 |
departure_city: str = Field(description="The city or airport of departure. Infer if not specified.")
|
|
|
|
| 76 |
activity_interests: str = Field(description="Specific interests for activities (e.g., 'museums', 'hiking').")
|
| 77 |
|
| 78 |
# --- 3. ASYNC SEARCH FUNCTIONS (Some Simulated, Some Live) ---
|
|
|
|
|
|
|
|
|
|
| 79 |
async def search_skyscanner(details):
|
| 80 |
"""Simulates searching for flights on Skyscanner."""
|
| 81 |
print(f"Searching Skyscanner for: {details['destination_city']}")
|
|
|
|
| 102 |
|
| 103 |
async def scrape_flight_vouchers(details):
|
| 104 |
"""
|
| 105 |
+
Performs web scraping for flight vouchers with better error handling for Hugging Face Spaces.
|
|
|
|
|
|
|
| 106 |
"""
|
| 107 |
+
global browsers_ready
|
| 108 |
+
|
| 109 |
+
print(f"Starting web scraping for flight vouchers related to: {details.get('destination_city', 'general travel')}")
|
| 110 |
+
|
| 111 |
+
# Check if browsers are ready, if not try to install them
|
| 112 |
+
if not browsers_ready:
|
| 113 |
+
browsers_ready = await ensure_playwright_browsers()
|
| 114 |
+
|
| 115 |
+
if not browsers_ready:
|
| 116 |
+
return [{
|
| 117 |
+
"source": "Web Scraping (Disabled)",
|
| 118 |
+
"type": "voucher",
|
| 119 |
+
"details": "Web scraping is temporarily disabled due to browser installation issues. This is common on Hugging Face Spaces. The app will still work with simulated data.",
|
| 120 |
+
"price": "N/A",
|
| 121 |
+
"link": "#"
|
| 122 |
+
}]
|
| 123 |
|
| 124 |
+
vouchers_found = []
|
| 125 |
target_urls = [
|
| 126 |
"https://www.retailmenot.com/coupons/flights",
|
| 127 |
]
|
| 128 |
|
|
|
|
| 129 |
try:
|
| 130 |
async with async_playwright() as p:
|
| 131 |
+
browser = await p.chromium.launch(
|
| 132 |
+
headless=True,
|
| 133 |
+
args=[
|
| 134 |
+
'--no-sandbox',
|
| 135 |
+
'--disable-setuid-sandbox',
|
| 136 |
+
'--disable-dev-shm-usage',
|
| 137 |
+
'--disable-accelerated-2d-canvas',
|
| 138 |
+
'--no-first-run',
|
| 139 |
+
'--no-zygote',
|
| 140 |
+
'--disable-gpu',
|
| 141 |
+
'--disable-web-security',
|
| 142 |
+
'--disable-features=VizDisplayCompositor'
|
| 143 |
+
]
|
| 144 |
+
)
|
| 145 |
|
|
|
|
| 146 |
context = await browser.new_context(
|
| 147 |
+
user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
| 148 |
)
|
| 149 |
page = await context.new_page()
|
| 150 |
|
| 151 |
for url in target_urls:
|
| 152 |
try:
|
| 153 |
print(f"Navigating to: {url}")
|
| 154 |
+
await page.goto(url, wait_until='domcontentloaded', timeout=30000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
+
# Simple scraping strategy
|
| 157 |
+
deals_locator = page.locator('div:has-text("coupon"), div:has-text("promo"), div:has-text("discount")')
|
| 158 |
deals = await deals_locator.all()
|
| 159 |
|
| 160 |
+
for i, deal_elem in enumerate(deals[:3]): # Limit to 3 deals
|
|
|
|
|
|
|
| 161 |
try:
|
| 162 |
text_content = await deal_elem.inner_text()
|
| 163 |
+
if len(text_content.strip()) > 20:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
code_match = re.search(r'\b[A-Z0-9]{4,10}\b', text_content)
|
| 165 |
code = code_match.group(0) if code_match else "N/A"
|
| 166 |
|
| 167 |
vouchers_found.append({
|
| 168 |
"source": f"Scraped from {url.split('/')[2]}",
|
| 169 |
"type": "voucher",
|
| 170 |
+
"details": f"{text_content[:100]}..." if len(text_content) > 100 else text_content,
|
| 171 |
"price": f"Code: {code}",
|
| 172 |
+
"link": url
|
| 173 |
})
|
| 174 |
except Exception as e_deal:
|
| 175 |
+
print(f"Error processing deal element: {e_deal}")
|
| 176 |
continue
|
| 177 |
+
|
| 178 |
+
except Exception as e_url:
|
| 179 |
+
print(f"Navigation error for {url}: {e_url}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
continue
|
| 181 |
+
|
| 182 |
+
await browser.close()
|
| 183 |
+
|
| 184 |
+
except Exception as e:
|
| 185 |
+
print(f"Playwright error: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
return [{
|
| 187 |
+
"source": "Web Scraping (Error)",
|
| 188 |
"type": "voucher",
|
| 189 |
+
"details": f"Web scraping encountered an error: {str(e)[:100]}... This is common on cloud platforms. Using simulated data instead.",
|
| 190 |
"price": "N/A",
|
| 191 |
"link": "#"
|
| 192 |
}]
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
if not vouchers_found:
|
| 195 |
+
# Return some simulated voucher data as fallback
|
| 196 |
return [{
|
| 197 |
+
"source": "Travel Deals (Simulated)",
|
| 198 |
"type": "voucher",
|
| 199 |
+
"details": "Get 10% off your next flight booking with major airlines - check airline websites directly for current promotions",
|
| 200 |
+
"price": "Code: SAVE10",
|
| 201 |
+
"link": "https://www.google.com/flights"
|
| 202 |
}]
|
| 203 |
+
|
| 204 |
return vouchers_found
|
| 205 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
async def search_llm_redundancy(details, llm, llm_name):
|
| 207 |
"""
|
| 208 |
Queries another LLM for additional, general travel ideas or insights.
|
|
|
|
| 211 |
prompt = f"""As a helpful travel assistant, provide some brief travel suggestions for a trip based on these details: {details}.
|
| 212 |
Focus on general tips, hidden gems, or activity ideas. Do not suggest specific prices or flights."""
|
| 213 |
messages = [SystemMessage(content=prompt), HumanMessage(content="What are your suggestions?")]
|
| 214 |
+
|
| 215 |
+
try:
|
| 216 |
+
response = await llm.ainvoke(messages)
|
| 217 |
+
return [{"source": llm_name, "type": "insight", "details": response.content}]
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"Error querying {llm_name}: {e}")
|
| 220 |
+
return [{"source": f"{llm_name} (Error)", "type": "insight", "details": f"Unable to get insights from {llm_name} due to: {str(e)[:100]}"}]
|
| 221 |
|
| 222 |
# --- 4. MAIN BOT LOGIC (ask_bot function) ---
|
| 223 |
async def ask_bot(question):
|
| 224 |
"""
|
| 225 |
This is the main ASYNCHRONOUS function for the bot.
|
|
|
|
|
|
|
| 226 |
"""
|
| 227 |
# Part 1: Extract structured data from the user's natural language request using an LLM.
|
| 228 |
try:
|
|
|
|
| 240 |
|
| 241 |
# Part 2: Gather all search tasks to run concurrently.
|
| 242 |
llm_openai_creative = ChatOpenAI(model="gpt-4o", temperature=0.7)
|
|
|
|
|
|
|
| 243 |
|
| 244 |
# Compile all the asynchronous search functions into a list of tasks.
|
| 245 |
tasks = [
|
| 246 |
+
search_skyscanner(structured_request),
|
| 247 |
+
search_expedia(structured_request),
|
| 248 |
+
scrape_Google_Flights(structured_request),
|
| 249 |
+
search_get_your_guide(structured_request),
|
| 250 |
+
scrape_flight_vouchers(structured_request),
|
| 251 |
+
search_llm_redundancy(structured_request, llm_openai_creative, "ChatGPT (Creative)"),
|
|
|
|
|
|
|
| 252 |
]
|
| 253 |
|
| 254 |
# Part 3: Run all tasks concurrently and collect their results.
|
|
|
|
| 315 |
output_text += "_No specific flight vouchers or codes found at this time._\n"
|
| 316 |
output_text += "\n"
|
| 317 |
|
| 318 |
+
# Format AI Insights section
|
| 319 |
+
output_text += "### 💡 Additional AI Insights\n"
|
| 320 |
if insights:
|
| 321 |
for insight in insights:
|
| 322 |
output_text += f"* **{insight['source']} says:** {insight['details']}\n"
|
|
|
|
| 328 |
|
| 329 |
return output_text
|
| 330 |
|
| 331 |
+
# Wrapper function to handle the async call
|
| 332 |
+
def bot_wrapper(question):
|
| 333 |
+
"""Synchronous wrapper for the async ask_bot function."""
|
| 334 |
+
return asyncio.run(ask_bot(question))
|
| 335 |
+
|
| 336 |
# --- 5. DEFINE THE GRADIO INTERFACE ---
|
| 337 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
| 338 |
gr.Markdown(
|
| 339 |
"""
|
| 340 |
# ✨ The Ultimate Global Travel Planner Bot - Bazinga Edition ✨
|
| 341 |
+
Your smart AI assistant for finding the best flights, hotels, and activities worldwide!
|
| 342 |
"""
|
| 343 |
)
|
| 344 |
with gr.Row():
|
|
|
|
| 357 |
inputs=user_input
|
| 358 |
)
|
| 359 |
|
| 360 |
+
submit_button.click(fn=bot_wrapper, inputs=user_input, outputs=output_display)
|
| 361 |
|
| 362 |
# --- 6. LAUNCH THE APP ---
|
| 363 |
if __name__ == "__main__":
|
| 364 |
+
iface.launch()
|