""" # AgentX-Travel India # ------------------ # An AI-powered travel assistant application tailored for the Indian market # Created by TechMatrix Solvers for IIITDMJ HackByte3.0 (April 4-6, 2025) # # Features: # - Personalized travel itinerary generation using AI agents # - Bilingual support (English and Hindi) # - India-specific travel recommendations # - Interactive maps and visualizations # - Downloadable travel plans # # Team: # - Abhay Gupta (Team Leader) # - Jay Kumar # - Kripanshu Gupta # - Aditi Soni # # This application uses: # - Streamlit for the frontend # - LangChain for AI orchestration # - Google Generative AI (Gemini) for language processing # - Geopy for location services # - Pydeck for map visualizations """ import os import sys # Initialize MongoDB, OpenAI and MCP availability flags as False by default # These will be set to True only if the imports succeed MONGODB_AVAILABLE = False OPENAI_AVAILABLE = False MCP_AVAILABLE = False # Debugging information for deployment troubleshooting print("Python version:", sys.version) print("Working directory:", os.getcwd()) print("Directory contents:", os.listdir()) print("Environment variables:", [(k, v) for k, v in os.environ.items() if 'SECRET' not in k.upper()]) try: import streamlit as st print("Streamlit version:", st.__version__) except Exception as e: print(f"Error importing streamlit: {str(e)}") sys.exit(1) # Exit if Streamlit isn't available - it's required # Continue with the rest of the imports try: import json from datetime import datetime, timedelta import base64 import pandas as pd import pydeck as pdk import requests from travel import ( destination_research_task, accommodation_task, transportation_task, activities_task, dining_task, itinerary_task, chatbot_task, run_task ) from geopy.geocoders import Nominatim # Try to import MongoDB modules - but make them optional try: from pymongo import MongoClient from bson import ObjectId MONGODB_AVAILABLE = True print("MongoDB integration available") except ImportError: MONGODB_AVAILABLE = False print("MongoDB integration not available") # Try to import OpenAI module - but make it optional try: from openai import OpenAI OPENAI_AVAILABLE = True print("OpenAI integration available") except ImportError: OPENAI_AVAILABLE = False print("OpenAI integration not available") # Try to import MCP module - but make it optional try: from mcp.client import ClientSession, WebSocketServerParameters import mcp.types as mcp_types import asyncio MCP_AVAILABLE = True print("MCP integration available") except ImportError: MCP_AVAILABLE = False print("MCP integration not available") except Exception as e: print(f"Error during imports: {str(e)}") # Don't exit here - we'll handle missing dependencies gracefully if 'run_task' not in globals(): def run_task(*args, **kwargs): return f"β οΈ Error: The travel module could not be loaded due to missing dependencies: {str(e)}" destination_research_task = "destination_research" accommodation_task = "accommodation" transportation_task = "transportation" activities_task = "activities" dining_task = "dining" itinerary_task = "itinerary" chatbot_task = "chatbot" st.set_page_config( page_title="Your AI Travel Assistant", page_icon="βοΈ", layout="wide", initial_sidebar_state="expanded" ) custom_css = """ """ st.markdown(custom_css, unsafe_allow_html=True) # ------------------------------------------ # Translation dictionary and helper functions # ------------------------------------------ translations = { "en": { "page_title": "Your AI Travel Assistant", "header": "Your AI Travel Assistant", "create_itinerary": "Create Your Itinerary", "trip_details": "Trip Details", "origin": "Origin", "destination": "Destination", "travel_dates": "Travel Dates", "duration": "Duration (days)", "preferences": "Preferences", "additional_preferences": "Additional Preferences", "interests": "Interests", "special_requirements": "Special Requirements", "submit": "π Create My Personal Travel Itinerary", "request_details": "Your Travel Request", "from": "From", "when": "When", "budget": "Budget", "travel_style": "Travel Style", "live_agent_outputs": "Live Agent Outputs", "full_itinerary": "Full Itinerary", "details": "Details", "download_share": "Download & Share", "save_itinerary": "Save Your Itinerary", "plan_another_trip": "π Plan Another Trip", "about": "About", "how_it_works": "How it works", "travel_agents": "Travel Agents", "share_itinerary": "Share Your Itinerary", "save_for_mobile": "Save for Mobile", "built_with": "Built with β€οΈ in India", "itinerary_ready": "Your Travel Itinerary is Ready! π", "personalized_experience": "We've created a personalized travel experience just for you. Explore your itinerary below.", "agent_activity": "Agent Activity", "error_origin_destination": "Please enter both origin and destination.", "your_itinerary_file": "Your Itinerary File", "text_format": "Text format - Can be opened in any text editor", "settings": "Settings", "map_view": "Map View", "chat": "Chat with AI", "download_itinerary": "Download Itinerary", "download_format": "Download Format", "copy_to_clipboard": "Copy to Clipboard", "copied": "Copied!", "gemini_api_key": "Google AI (Gemini) API Key", "enter_api_key": "Enter your Gemini API key", "api_key_updated": "API key updated!", "api_key_required": "Required for AI functionality. Get a key at https://ai.google.dev/" } } def t(key): return translations["en"].get(key, key) # Add logo to the header logo_col1, logo_col2 = st.columns([1, 4]) with logo_col1: # Check if logo file exists before trying to load it logo_path = os.path.join(os.path.dirname(__file__), "android-chrome-512x512.png") if os.path.exists(logo_path): try: st.image(logo_path, width=100) except Exception as e: # If image fails to load, show emoji as fallback st.markdown("# βοΈ") else: # If image doesn't exist, show emoji as fallback st.markdown("# βοΈ") with logo_col2: st.markdown("## " + t("page_title")) st.markdown("##### AI-powered travel assistant for India") # ------------------------------------------ # Initialize all session state variables # ------------------------------------------ def initialize_session_state(): """Initialize all required session state variables.""" if 'generated_itinerary' not in st.session_state: st.session_state.generated_itinerary = None # Set language to English always st.session_state.language = 'en' st.session_state.selected_language = "en" if 'step_results' not in st.session_state: st.session_state.step_results = {} # Ensure step_results has all required keys for key in ["destination_research", "accommodation", "transportation", "activities", "dining", "itinerary"]: if key not in st.session_state.step_results: st.session_state.step_results[key] = None if 'messages' not in st.session_state: st.session_state.messages = [] if "results" not in st.session_state: st.session_state.results = {} if "gemini_api_key" not in st.session_state: # Check for environment variables first env_key_1 = os.getenv("GEMINI_API_KEY_1") env_key_2 = os.getenv("GEMINI_API_KEY_2") fallback_key = os.getenv("GEMINI_API_KEY") # Use the first available key for session state if env_key_1: st.session_state.gemini_api_key = env_key_1 elif env_key_2: st.session_state.gemini_api_key = env_key_2 elif fallback_key: st.session_state.gemini_api_key = fallback_key else: st.session_state.gemini_api_key = "" if "tailvy_api_key" not in st.session_state: st.session_state.tailvy_api_key = "" if "mongodb_uri" not in st.session_state: st.session_state.mongodb_uri = "" if "openai_api_key" not in st.session_state: st.session_state.openai_api_key = "" if "tailvy_used" not in st.session_state: st.session_state.tailvy_used = False if "mongodb_used" not in st.session_state: st.session_state.mongodb_used = False if "active_tab" not in st.session_state: st.session_state.active_tab = "full_itinerary" # Run initialization initialize_session_state() # Helper function to download text file def get_download_link(text_content, filename): b64 = base64.b64encode(text_content.encode()).decode() href = f'π₯ {t("save_itinerary")}' return href # ------------------------------------------ # Tailvy API Integration # ------------------------------------------ def use_tailvy_api(query, api_key, endpoint="itinerary"): """ Call Tailvy API for travel planning Args: query (str): The travel query with trip details api_key (str): Tailvy API key endpoint (str): API endpoint to use Returns: dict: API response or None if failed """ try: base_url = "https://api.tailvy.com/v1" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } data = { "query": query, "format": "json" } # Add a timeout to prevent hanging on slow API responses response = requests.post(f"{base_url}/{endpoint}", headers=headers, json=data, timeout=30) if response.status_code == 200: try: result = response.json() # Validate that response has expected fields if endpoint == "travel" and not all(k in result for k in ["destination_info", "accommodations", "transportation"]): st.warning("Tailvy API response is missing expected fields. Falling back to default method.") return None return result except ValueError: st.warning("Tailvy API returned invalid JSON. Falling back to default method.") return None elif response.status_code == 401: st.error("Invalid Tailvy API key. Please check your credentials.") return None elif response.status_code == 429: st.warning("Tailvy API rate limit exceeded. Falling back to default method.") return None else: st.warning(f"Tailvy API returned status code {response.status_code}. Falling back to default method.") return None except requests.exceptions.Timeout: st.warning("Tailvy API request timed out. Falling back to default method.") return None except requests.exceptions.ConnectionError: st.warning("Could not connect to Tailvy API. Falling back to default method.") return None except Exception as e: st.warning(f"Error calling Tailvy API: {str(e)}. Falling back to default method.") return None # ------------------------------------------ # MongoDB Integration # ------------------------------------------ def find_nearby_attractions(destination, search_term, radius=5000): """ Find attractions near the specified destination using MongoDB vector search Args: destination (str): The destination name (e.g., "Agra") search_term (str): What to search for (e.g., "historical sites") radius (int): Search radius in meters Returns: dict: MongoDB search results or None if failed """ if not MONGODB_AVAILABLE or not OPENAI_AVAILABLE: st.warning("MongoDB or OpenAI package not installed. Can't use geo-based recommendations.") return None try: # Check if we have the required API keys if not st.session_state.mongodb_uri or not st.session_state.openai_api_key: return None # Connect to MongoDB client = MongoClient(st.session_state.mongodb_uri) db_name = 'travel_india' collection = client[db_name]['attractions'] # Get coordinates for the destination geolocator = Nominatim(user_agent="travel_app") location = geolocator.geocode(destination) if not location: st.warning(f"Could not find coordinates for {destination}.") return None # Create the geo query coordinates = [location.longitude, location.latitude] # Create a new search ID for this query search_id = ObjectId() # Set up pipeline for geospatial pre-filtering geo_pipeline = [ { "$geoNear": { "near": {"type": "Point", "coordinates": coordinates}, "distanceField": "distance", "maxDistance": radius, "spherical": True } }, { "$addFields": { "searchId": search_id } } ] # Execute the pre-filtering to narrow down candidates collection.aggregate(geo_pipeline) # Create OpenAI client and generate embeddings for the search term openai_client = OpenAI(api_key=st.session_state.openai_api_key) response = openai_client.embeddings.create( input=search_term, model="text-embedding-3-small", dimensions=256 ) search_embedding = response.data[0].embedding # Vector search among pre-filtered candidates vector_query = { "$vectorSearch": { "index": "vector_index", "queryVector": search_embedding, "path": "embedding", "numCandidates": 10, "limit": 5, "filter": {"searchId": search_id} } } # Execute vector search results = list(collection.aggregate([vector_query])) return { "results": results, "count": len(results), "destination": destination, "coordinates": coordinates } except Exception as e: st.warning(f"Error using MongoDB search: {str(e)}") return None # Add MongoDB initialization function def initialize_mongodb_collection(): """ Initialize MongoDB collection with sample attraction data This function creates a sample collection of Indian attractions with coordinates and descriptions if it doesn't exist yet """ if not MONGODB_AVAILABLE: st.error("MongoDB package not installed. Cannot initialize collection.") return False try: # Check if we have MongoDB connection details if not st.session_state.mongodb_uri: st.error("MongoDB connection URI is required.") return False # Connect to MongoDB client = MongoClient(st.session_state.mongodb_uri) db_name = 'travel_india' collection_name = 'attractions' # Create the database and collection if they don't exist db = client[db_name] # Check if collection exists and has documents if collection_name in db.list_collection_names() and db[collection_name].count_documents({}) > 0: st.success(f"Collection '{collection_name}' already exists with data.") return True # Create collection collection = db[collection_name] # Sample attraction data for India sample_attractions = [ { "name": "Taj Mahal", "description": "Iconic white marble mausoleum built by Emperor Shah Jahan.", "location": { "type": "Point", "coordinates": [78.0422, 27.1751] }, "city": "Agra", "type": "historical", "tags": ["monument", "UNESCO", "marble", "mughal"] }, { "name": "Agra Fort", "description": "UNESCO World Heritage site, a historical fort in the city of Agra.", "location": { "type": "Point", "coordinates": [78.0254, 27.1784] }, "city": "Agra", "type": "historical", "tags": ["fort", "UNESCO", "mughal", "red sandstone"] }, { "name": "Fatehpur Sikri", "description": "A city founded in the 16th century by a Mughal emperor.", "location": { "type": "Point", "coordinates": [77.6701, 27.0947] }, "city": "Agra", "type": "historical", "tags": ["UNESCO", "abandoned city", "mughal"] }, { "name": "Mehtab Bagh", "description": "Garden complex aligned with the Taj Mahal on the opposite side of the river.", "location": { "type": "Point", "coordinates": [78.0499, 27.1792] }, "city": "Agra", "type": "park", "tags": ["garden", "viewpoint", "taj mahal"] }, { "name": "India Gate", "description": "War memorial dedicated to soldiers who died in WWI.", "location": { "type": "Point", "coordinates": [77.2295, 28.6129] }, "city": "Delhi", "type": "monument", "tags": ["memorial", "war memorial", "landmark"] }, { "name": "Red Fort", "description": "Historic fort that served as the main residence of the Mughal Emperors.", "location": { "type": "Point", "coordinates": [77.2410, 28.6562] }, "city": "Delhi", "type": "historical", "tags": ["fort", "UNESCO", "mughal", "red sandstone"] }, { "name": "Humayun's Tomb", "description": "The tomb of the Mughal Emperor Humayun, commissioned by his wife.", "location": { "type": "Point", "coordinates": [77.2507, 28.5933] }, "city": "Delhi", "type": "historical", "tags": ["tomb", "UNESCO", "mughal", "garden"] }, { "name": "Gateway of India", "description": "An arch monument built during the 20th century in Mumbai.", "location": { "type": "Point", "coordinates": [72.8347, 18.9220] }, "city": "Mumbai", "type": "monument", "tags": ["arch", "colonial", "sea", "landmark"] }, { "name": "Marine Drive", "description": "A 3.6-kilometer-long boulevard in South Mumbai that offers scenic views.", "location": { "type": "Point", "coordinates": [72.8217, 18.9474] }, "city": "Mumbai", "type": "landmark", "tags": ["promenade", "sea view", "coast", "sunset"] }, { "name": "Elephanta Caves", "description": "A collection of cave temples predominantly dedicated to the Hindu god Shiva.", "location": { "type": "Point", "coordinates": [72.9311, 18.9633] }, "city": "Mumbai", "type": "historical", "tags": ["cave", "UNESCO", "hindu temple", "island"] } ] # If OPENAI_AVAILABLE, create embeddings for sample data if OPENAI_AVAILABLE and st.session_state.openai_api_key: openai_client = OpenAI(api_key=st.session_state.openai_api_key) with st.status("Creating vector embeddings..."): for attraction in sample_attractions: # Create embeddings for the attraction name and description embedding_text = f"{attraction['name']} {attraction['description']} {' '.join(attraction['tags'])}" response = openai_client.embeddings.create( input=embedding_text, model="text-embedding-3-small", dimensions=256 ) attraction["embedding"] = response.data[0].embedding # Insert sample data collection.insert_many(sample_attractions) # Create indexes collection.create_index([("location", "2dsphere")]) if OPENAI_AVAILABLE and st.session_state.openai_api_key: # Create vector index if embeddings were added db.command({ "createIndexes": collection_name, "indexes": [{ "name": "vector_index", "key": {"embedding": "vector"}, "vectorOptions": { "dimension": 256, "similarity": "cosine" } }] }) st.success(f"Successfully created collection with {len(sample_attractions)} sample attractions.") return True except Exception as e: st.error(f"Error initializing MongoDB collection: {str(e)}") return False # ------------------------------------------ # Start of Streamlit UI code # ------------------------------------------ # Sidebar for settings with st.sidebar: st.title("βοΈ " + t("settings")) # Gemini API Key input api_key = st.text_input( t("gemini_api_key"), placeholder=t("enter_api_key"), type="password", help=t("api_key_required") ) # Validate and save API key if api_key: if api_key.startswith("AI"): st.session_state.gemini_api_key = api_key st.success(t("api_key_updated")) else: st.error("Invalid API key. Gemini API keys start with 'AI'") # Check which environment variables are set env_key_1 = os.getenv("GEMINI_API_KEY_1") env_key_2 = os.getenv("GEMINI_API_KEY_2") # Show status of environment variables if env_key_1 or env_key_2: keys_detected = [] if env_key_1: keys_detected.append("GEMINI_API_KEY_1 β ") if env_key_2: keys_detected.append("GEMINI_API_KEY_2 β ") st.success(f"**API Keys Detected:**\n" + "\n".join([f"- {key}" for key in keys_detected])) if env_key_1 and env_key_2: st.info("π Automatic API key rotation is enabled! The system will switch between keys if rate limits are hit.") else: st.info(""" π‘ **Multiple API Keys Support** To avoid rate limits, you can set multiple API keys as environment variables: - `GEMINI_API_KEY_1` - Primary API key - `GEMINI_API_KEY_2` - Backup API key The system will automatically switch to the backup key if the primary hits rate limits. """) st.caption("**Current Model:** gemini-2.5-flash") # Add Tailvy API Key input (optional) st.markdown("### π§© Tailvy API (Optional)") tailvy_api_key = st.text_input( "Tailvy API Key", placeholder="Enter your Tailvy API key", type="password", help="Optional: Enhance travel recommendations with Tailvy API. Provides more detailed itineraries, local insights, and real-time availability of attractions and accommodations." ) # Save Tailvy API key to session state if tailvy_api_key: st.session_state.tailvy_api_key = tailvy_api_key st.success("Tailvy API key saved!") if 'tailvy_api_key' in st.session_state and st.session_state.tailvy_api_key: st.info("Tailvy API integration is active! You'll receive enhanced travel recommendations.") else: st.caption("π‘ Using Tailvy API provides better recommendations for Indian destinations with local expertise.") # Add MCP integration section ONLY if the modules are available if MCP_AVAILABLE: st.markdown("### π§ Model Context Protocol (Optional)") mcp_server_url = st.text_input( "MCP Server URL", placeholder="ws://localhost:3000", help="Optional: Connect to an MCP server for enhanced context-aware responses" ) # Save MCP server URL to session state if mcp_server_url: st.session_state.mcp_server_url = mcp_server_url st.success("MCP server URL saved!") # Add option to test MCP connection if st.button("Test MCP Connection"): with st.spinner("Testing MCP connection..."): connection_successful = test_mcp_connection(mcp_server_url) if connection_successful: st.session_state.mcp_connected = True st.success("Successfully connected to MCP server!") else: st.session_state.mcp_connected = False st.error("Failed to connect to MCP server. Please check the URL and ensure the server is running.") if st.session_state.get("mcp_connected", False): st.info("MCP integration is active! You'll receive context-aware travel recommendations.") else: # Show collapsed expander for optional MCP feature with st.expander("π§ Model Context Protocol (Optional)", expanded=False): st.caption(""" **Context-aware AI** - Enhance responses with Model Context Protocol To enable this optional feature: ```bash pip install mcp-python-sdk ``` Then restart the application. """) # Add MongoDB and OpenAI integration section ONLY if the modules are available if MONGODB_AVAILABLE and OPENAI_AVAILABLE: st.markdown("### πΊοΈ MongoDB Geo Search (Optional)") mongodb_uri = st.text_input( "MongoDB Connection URI", placeholder="mongodb+srv://username:password@cluster...", type="password", help="Optional: Add MongoDB connection string to enable location-based attraction search" ) openai_api_key = st.text_input( "OpenAI API Key", placeholder="sk-...", type="password", help="Required for MongoDB vector search to work properly" ) # Save MongoDB and OpenAI credentials to session state if mongodb_uri: st.session_state.mongodb_uri = mongodb_uri if openai_api_key: st.session_state.openai_api_key = openai_api_key st.success("MongoDB and OpenAI credentials saved!") st.info("Geo-based attraction recommendations are now enabled!") # Show option to initialize sample data if st.button("Initialize Sample Attractions Data"): initialize_mongodb_collection() else: st.warning("Please provide an OpenAI API key for vector search functionality.") else: # Show collapsed expander for optional MongoDB feature with st.expander("πΊοΈ MongoDB Geo Search (Optional)", expanded=False): missing_packages = [] if not MONGODB_AVAILABLE: missing_packages.append("pymongo") if not OPENAI_AVAILABLE: missing_packages.append("openai") st.caption(f""" **Location-based recommendations** - Find nearby attractions with MongoDB Atlas To enable this optional feature: ```bash pip install {' '.join(missing_packages)} ``` Then restart the application. """) # About section st.markdown("### βΉοΈ " + t("about")) st.info( "AgentX-Travel India is an AI-powered travel assistant application tailored for the Indian market. " "It uses specialized AI agents to create personalized travel itineraries." ) # Travel agents section st.markdown("### π§³ " + t("travel_agents")) st.write( "Our AI system uses specialized agents for destination research, accommodations, " "transportation, activities, dining, and itinerary creation." ) # Add travel form st.markdown("## " + t("create_itinerary")) st.markdown("### " + t("trip_details")) with st.form(key="travel_form"): # Basic trip information col1, col2 = st.columns(2) with col1: origin = st.text_input(t("origin"), "Delhi") destination = st.text_input(t("destination"), "Agra") preferences = st.text_input(t("preferences"), "Historical sites, Culture, Food") with col2: # Date selection today = datetime.today() start_date = st.date_input(t("travel_dates"), value=today + timedelta(days=7), min_value=today) duration = st.number_input(t("duration"), min_value=1, max_value=30, value=3) end_date = start_date + timedelta(days=duration) budget = st.selectbox(t("budget"), ["Budget", "Mid-range", "Luxury"]) # Special requirements, if any special_requirements = st.text_area(t("special_requirements"), "", height=100) # Submit form submitted = st.form_submit_button(t("submit")) # Create a dictionary of user inputs for later use user_input = { "origin": origin, "destination": destination, "start_date": start_date.strftime("%Y-%m-%d"), "end_date": end_date.strftime("%Y-%m-%d"), "duration": duration, "preferences": preferences, "budget": budget, "special_requirements": special_requirements } # Save destination to session state st.session_state.destination = destination # Save destination to session_state # Save user input to session state for later use in maps st.session_state.user_input = user_input # Save for later map usage # Process form submission if submitted: # Show the input summary st.markdown("### " + t("request_details")) input_summary = f""" - **{t('from')}:** {origin} - **{t('destination')}:** {destination} - **{t('when')}:** {start_date.strftime('%d %b %Y')} to {end_date.strftime('%d %b %Y')} - **{t('duration')}:** {duration} days - **{t('preferences')}:** {preferences} - **{t('budget')}:** {budget} """ st.markdown(input_summary) # Original travel request prompt input_text = f"Origin: {origin}, Destination: {destination}, Travel dates: {start_date} to {end_date}, Duration: {duration} days, Preferences: {preferences}, Budget: {budget}" # Check if API key is available if 'gemini_api_key' not in st.session_state or not st.session_state.gemini_api_key: st.error("Please enter your Gemini API key in the sidebar to generate an itinerary.") else: # Process the travel request with st.spinner("Generating your personalized travel itinerary..."): try: # Check if Tailvy API is available if 'tailvy_api_key' in st.session_state and st.session_state.tailvy_api_key: # Use Tailvy API for enhanced travel planning st.info("Using Tailvy API for enhanced travel recommendations...") tailvy_response = use_tailvy_api( input_text, st.session_state.tailvy_api_key, endpoint="travel" ) if tailvy_response: # If Tailvy API call was successful, use its results try: st.session_state.step_results["destination_research"] = tailvy_response.get("destination_info", "") st.session_state.step_results["accommodation"] = tailvy_response.get("accommodations", "") st.session_state.step_results["transportation"] = tailvy_response.get("transportation", "") st.session_state.step_results["activities"] = tailvy_response.get("activities", "") st.session_state.step_results["dining"] = tailvy_response.get("dining", "") # Generate final itinerary with Tailvy integration st.session_state.generated_itinerary = tailvy_response.get("itinerary", "") # Set tailvy_used flag to True st.session_state.tailvy_used = True # Success message st.success("Your Tailvy-enhanced travel itinerary has been successfully generated!") # Switch to the itinerary tab st.session_state.active_tab = "full_itinerary" except Exception as e: st.warning(f"Error processing Tailvy data: {str(e)}. Falling back to default method.") # If error in processing Tailvy data, fall back to the default method tailvy_response = None st.session_state.tailvy_used = False # If Tailvy API not available or failed, use default method if 'tailvy_api_key' not in st.session_state or not st.session_state.tailvy_api_key or not tailvy_response: # Reset tailvy_used flag since we're using the default method st.session_state.tailvy_used = False # Step 1: Destination Research with st.status("Researching destination..."): st.session_state.step_results["destination_research"] = run_task( destination_research_task, input_text, api_key=st.session_state.gemini_api_key ) # Step 2: Accommodation with st.status("Finding accommodations..."): st.session_state.step_results["accommodation"] = run_task( accommodation_task, input_text, api_key=st.session_state.gemini_api_key ) # Step 3: Transportation with st.status("Planning transportation..."): st.session_state.step_results["transportation"] = run_task( transportation_task, input_text, api_key=st.session_state.gemini_api_key ) # Step 4: Activities with st.status("Discovering activities..."): st.session_state.step_results["activities"] = run_task( activities_task, input_text, api_key=st.session_state.gemini_api_key ) # Step 5: Dining with st.status("Finding dining options..."): st.session_state.step_results["dining"] = run_task( dining_task, input_text, api_key=st.session_state.gemini_api_key ) # Step 6: Generate final itinerary with st.status("Creating final itinerary..."): # Combine results for the final itinerary combined_results = f""" Destination Research: {st.session_state.step_results['destination_research']} Accommodation: {st.session_state.step_results['accommodation']} Transportation: {st.session_state.step_results['transportation']} Activities: {st.session_state.step_results['activities']} Dining: {st.session_state.step_results['dining']} """ st.session_state.generated_itinerary = run_task( itinerary_task, f"{input_text}\n\n{combined_results}", api_key=st.session_state.gemini_api_key ) # Success message st.success("Your travel itinerary has been successfully generated!") # Switch to the itinerary tab st.session_state.active_tab = "full_itinerary" except Exception as e: st.error(f"Error generating itinerary: {str(e)}") st.info("Please check your API key and try again. Make sure you're using a valid API key.") else: # When form is not submitted yet, show a sample itinerary or instructions input_text = f"Origin: {origin}, Destination: {destination}, Travel dates: {start_date} to {end_date}, Duration: {duration} days, Preferences: {preferences}, Budget: {budget}" # Create tabs for the interface (including chatbot) tabs_list = [ t("full_itinerary"), t("details"), t("download_share"), "πΊοΈ " + t("map_view"), "π€ " + t("chat") ] # Check if we should activate a specific tab if 'active_tab' in st.session_state: active_tab_index = 0 # Default to first tab if st.session_state.active_tab == "full_itinerary": active_tab_index = 0 elif st.session_state.active_tab == "details": active_tab_index = 1 elif st.session_state.active_tab == "download_share": active_tab_index = 2 elif st.session_state.active_tab == "map_view": active_tab_index = 3 elif st.session_state.active_tab == "chat": active_tab_index = 4 # Store active tab index st.session_state.active_tab_index = active_tab_index tabs = st.tabs(tabs_list) # Itinerary tab with tabs[0]: if st.session_state.generated_itinerary: # Add Tailvy badge if Tailvy API was used if 'tailvy_api_key' in st.session_state and st.session_state.tailvy_api_key and 'tailvy_used' in st.session_state and st.session_state.tailvy_used: st.markdown( """
""" + t("built_with") + """
Created by TechMatrix Solvers for IIITDMJ HackByte3.0 (April 2025)