Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| from typing import Union, List, Dict, Optional | |
| from groq import Groq | |
| import os | |
| from duckduckgo_search import DDGS | |
| import json | |
| from datetime import datetime, timedelta | |
| import time | |
| # Set page configuration with fullscreen layout and custom theme | |
| st.set_page_config( | |
| page_title="SmolAgent Travel Planner", | |
| layout="wide", | |
| initial_sidebar_state="collapsed", | |
| page_icon="βοΈ" | |
| ) | |
| # Enhanced CSS for a beautiful travel-themed styling | |
| st.markdown(""" | |
| <style> | |
| /* Base styles */ | |
| html, body, .stApp, .main { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: #ffffff !important; | |
| } | |
| /* Typography */ | |
| h1, h2, h3, h4, h5, h6 { | |
| color: #ffffff !important; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.3); | |
| } | |
| /* Cards and containers */ | |
| .card { | |
| background: rgba(255, 255, 255, 0.1) !important; | |
| backdrop-filter: blur(10px) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.2) !important; | |
| border-radius: 15px !important; | |
| box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37) !important; | |
| padding: 2rem !important; | |
| margin-bottom: 2rem !important; | |
| } | |
| /* Agent status cards */ | |
| .agent-card { | |
| background: rgba(255, 255, 255, 0.15) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.3) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| margin: 1rem 0 !important; | |
| backdrop-filter: blur(5px) !important; | |
| } | |
| /* Input fields */ | |
| .stTextInput input, .stTextArea textarea, .stSelectbox div[data-baseweb="select"] { | |
| background: rgba(255, 255, 255, 0.2) !important; | |
| color: #ffffff !important; | |
| border: 1px solid rgba(255, 255, 255, 0.3) !important; | |
| border-radius: 8px !important; | |
| } | |
| .stTextInput input::placeholder, .stTextArea textarea::placeholder { | |
| color: rgba(255, 255, 255, 0.7) !important; | |
| } | |
| /* Buttons */ | |
| .stButton > button { | |
| background: linear-gradient(45deg, #ff6b6b, #ff8e8e) !important; | |
| color: #ffffff !important; | |
| border: none !important; | |
| border-radius: 25px !important; | |
| font-weight: bold !important; | |
| padding: 0.75rem 2rem !important; | |
| transition: all 0.3s ease !important; | |
| box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4) !important; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 8px 25px rgba(255, 107, 107, 0.6) !important; | |
| } | |
| /* Progress indicators */ | |
| .agent-status { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 10px; | |
| border-radius: 8px; | |
| margin: 5px 0; | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(5px); | |
| } | |
| .status-working { | |
| border-left: 4px solid #4CAF50; | |
| } | |
| .status-complete { | |
| border-left: 4px solid #2196F3; | |
| } | |
| .status-error { | |
| border-left: 4px solid #F44336; | |
| } | |
| /* Spinning animation */ | |
| .spinner { | |
| border: 2px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top: 2px solid #ffffff; | |
| width: 20px; | |
| height: 20px; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* Results styling */ | |
| .travel-result { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border-left: 4px solid #4CAF50 !important; | |
| border-radius: 8px !important; | |
| padding: 1.5rem !important; | |
| margin: 1rem 0 !important; | |
| } | |
| /* Tabs */ | |
| .stTabs [aria-selected="true"] { | |
| background: rgba(255, 255, 255, 0.2) !important; | |
| color: #ffffff !important; | |
| border-bottom: 2px solid #ff6b6b !important; | |
| } | |
| /* Logo styling */ | |
| .logo-text { | |
| font-size: 3rem !important; | |
| background: linear-gradient(45deg, #FFD700, #FFA500) !important; | |
| -webkit-background-clip: text !important; | |
| -webkit-text-fill-color: transparent !important; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important; | |
| } | |
| /* Success indicators */ | |
| .success-badge { | |
| background: linear-gradient(45deg, #4CAF50, #66BB6A) !important; | |
| color: white !important; | |
| padding: 0.5rem 1rem !important; | |
| border-radius: 20px !important; | |
| font-size: 0.9rem !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| class SmolAgent: | |
| """ | |
| Base agent class implementing the smol agent architecture. | |
| Each agent has a specific role and can collaborate with others. | |
| """ | |
| def __init__(self, name: str, role: str, llm_client, search_tool): | |
| self.name = name | |
| self.role = role | |
| self.llm = llm_client | |
| self.search = search_tool | |
| self.status = "idle" | |
| self.last_result = None | |
| def set_status(self, status: str, message: str = ""): | |
| self.status = status | |
| self.status_message = message | |
| def execute_task(self, task_prompt: str, search_query: str = None) -> str: | |
| """Execute a task with optional web search""" | |
| try: | |
| self.set_status("working", f"Processing {self.role.lower()} task...") | |
| # Perform search if needed | |
| search_results = "" | |
| if search_query: | |
| self.set_status("working", "Searching for relevant information...") | |
| search_results = self.search(search_query) | |
| # Create enhanced prompt with search results | |
| enhanced_prompt = f""" | |
| Role: {self.role} | |
| Task: {task_prompt} | |
| {f"Search Results: {search_results}" if search_results else ""} | |
| Please provide a detailed response based on your role as {self.role}. | |
| """ | |
| # Get response from LLM | |
| self.set_status("working", "Generating response...") | |
| result = self.llm(enhanced_prompt) | |
| self.last_result = result | |
| self.set_status("complete", "Task completed successfully") | |
| return result | |
| except Exception as e: | |
| self.set_status("error", f"Error: {str(e)}") | |
| return f"Error in {self.name}: {str(e)}" | |
| class TravelSearchTool: | |
| """Enhanced search tool specifically designed for travel planning""" | |
| def __init__(self): | |
| self.ddgs = DDGS() | |
| def __call__(self, query: str, search_type: str = "general", max_results: int = 5) -> str: | |
| try: | |
| # Customize search based on type | |
| if search_type == "flights": | |
| query = f"best flight deals {query} 2024 booking sites" | |
| elif search_type == "hotels": | |
| query = f"best hotels accommodation {query} reviews booking" | |
| elif search_type == "activities": | |
| query = f"top attractions activities things to do {query} tourist guide" | |
| elif search_type == "restaurants": | |
| query = f"best restaurants food dining {query} local cuisine" | |
| elif search_type == "weather": | |
| query = f"weather forecast climate {query} best time to visit" | |
| # Perform search | |
| search_results = list(self.ddgs.text( | |
| query, | |
| max_results=max_results, | |
| region='wt-wt', | |
| safesearch='on' | |
| )) | |
| if not search_results: | |
| return "No relevant information found. Please try a different search." | |
| # Format results | |
| formatted_results = [] | |
| for idx, result in enumerate(search_results, 1): | |
| title = result.get('title', 'No title') | |
| snippet = result.get('body', 'No description') | |
| url = result.get('href', 'No URL') | |
| formatted_results.append( | |
| f"{idx}. {title}\n" | |
| f" Description: {snippet}\n" | |
| f" Source: {url}\n" | |
| ) | |
| return "\n".join(formatted_results) | |
| except Exception as e: | |
| return f"Search error: {str(e)}" | |
| class GroqLLM: | |
| """Enhanced LLM client with travel-specific optimizations""" | |
| def __init__(self, model_name="llama-3.1-70b-versatile"): | |
| self.client = Groq(api_key=os.environ.get("GROQ_API_KEY")) | |
| self.model_name = model_name | |
| def __call__(self, prompt: Union[str, dict, List[Dict]]) -> str: | |
| try: | |
| completion = self.client.chat.completions.create( | |
| model=self.model_name, | |
| messages=[{ | |
| "role": "user", | |
| "content": str(prompt) | |
| }], | |
| temperature=0.7, | |
| max_tokens=2048, | |
| stream=False | |
| ) | |
| return completion.choices[0].message.content if completion.choices else "No response generated" | |
| except Exception as e: | |
| return f"LLM Error: {str(e)}" | |
| class TravelPlannerSystem: | |
| """Main orchestrator for the travel planning system""" | |
| def __init__(self): | |
| self.llm = GroqLLM() | |
| self.search_tool = TravelSearchTool() | |
| self.agents = self._initialize_agents() | |
| def _initialize_agents(self) -> Dict[str, SmolAgent]: | |
| """Initialize specialized travel agents""" | |
| return { | |
| "destination_expert": SmolAgent( | |
| "Destination Expert", | |
| "Travel destination researcher and recommender", | |
| self.llm, self.search_tool | |
| ), | |
| "itinerary_planner": SmolAgent( | |
| "Itinerary Planner", | |
| "Trip itinerary creator and scheduler", | |
| self.llm, self.search_tool | |
| ), | |
| "accommodation_specialist": SmolAgent( | |
| "Accommodation Specialist", | |
| "Hotel and lodging finder and recommender", | |
| self.llm, self.search_tool | |
| ), | |
| "activity_curator": SmolAgent( | |
| "Activity Curator", | |
| "Tourist attraction and activity recommender", | |
| self.llm, self.search_tool | |
| ), | |
| "budget_advisor": SmolAgent( | |
| "Budget Advisor", | |
| "Travel cost estimator and budget optimizer", | |
| self.llm, self.search_tool | |
| ) | |
| } | |
| def create_travel_plan(self, destination: str, duration: str, budget: str, | |
| travel_style: str, interests: str) -> Dict[str, str]: | |
| """Create a comprehensive travel plan using multiple agents""" | |
| # Prepare base context for all agents | |
| base_context = f""" | |
| Destination: {destination} | |
| Duration: {duration} | |
| Budget: {budget} | |
| Travel Style: {travel_style} | |
| Interests: {interests} | |
| """ | |
| results = {} | |
| # Agent 1: Destination Research | |
| dest_task = f""" | |
| Research {destination} as a travel destination. Include: | |
| - Best time to visit and current weather | |
| - Cultural highlights and local customs | |
| - Transportation options | |
| - Safety and travel requirements | |
| - Local currency and tipping customs | |
| Context: {base_context} | |
| """ | |
| results["destination_info"] = self.agents["destination_expert"].execute_task( | |
| dest_task, f"{destination} travel guide weather best time visit" | |
| ) | |
| # Agent 2: Accommodation Research | |
| accommodation_task = f""" | |
| Find accommodation options for {destination}. Consider: | |
| - Hotels, hostels, and alternative lodging based on budget: {budget} | |
| - Location preferences for {travel_style} travelers | |
| - Amenities and reviews | |
| - Booking tips and best deals | |
| Context: {base_context} | |
| """ | |
| results["accommodation"] = self.agents["accommodation_specialist"].execute_task( | |
| accommodation_task, f"{destination} hotels accommodation {budget} {travel_style}" | |
| ) | |
| # Agent 3: Activities and Attractions | |
| activity_task = f""" | |
| Curate activities and attractions for {destination} based on interests: {interests} | |
| Include: | |
| - Must-see attractions and hidden gems | |
| - Activities matching {travel_style} style | |
| - Local experiences and cultural activities | |
| - Restaurant and dining recommendations | |
| Context: {base_context} | |
| """ | |
| results["activities"] = self.agents["activity_curator"].execute_task( | |
| activity_task, f"{destination} attractions activities {interests} restaurants" | |
| ) | |
| # Agent 4: Itinerary Planning | |
| itinerary_task = f""" | |
| Create a detailed {duration} itinerary for {destination}. Include: | |
| - Day-by-day schedule optimized for {travel_style} travelers | |
| - Time management and logistics | |
| - Mix of planned activities and free time | |
| - Transportation between locations | |
| Use information about accommodations and activities to create a cohesive plan. | |
| Context: {base_context} | |
| """ | |
| results["itinerary"] = self.agents["itinerary_planner"].execute_task( | |
| itinerary_task, f"{destination} {duration} itinerary travel schedule" | |
| ) | |
| # Agent 5: Budget Planning | |
| budget_task = f""" | |
| Create a detailed budget breakdown for the {destination} trip: | |
| - Accommodation costs based on research | |
| - Transportation (flights, local transport) | |
| - Food and dining expenses | |
| - Activities and attraction fees | |
| - Miscellaneous expenses and emergency fund | |
| - Money-saving tips and deals | |
| Context: {base_context} | |
| """ | |
| results["budget_breakdown"] = self.agents["budget_advisor"].execute_task( | |
| budget_task, f"{destination} travel costs budget {duration} {budget}" | |
| ) | |
| return results | |
| def display_agent_status(agents: Dict[str, SmolAgent]): | |
| """Display the status of all agents""" | |
| st.markdown("### π€ Agent Status Dashboard") | |
| cols = st.columns(len(agents)) | |
| for idx, (name, agent) in enumerate(agents.items()): | |
| with cols[idx]: | |
| status_class = f"status-{agent.status}" if hasattr(agent, 'status') else "status-idle" | |
| status_message = getattr(agent, 'status_message', 'Ready') | |
| if agent.status == "working": | |
| icon = '<div class="spinner"></div>' | |
| elif agent.status == "complete": | |
| icon = 'β ' | |
| elif agent.status == "error": | |
| icon = 'β' | |
| else: | |
| icon = 'βΈοΈ' | |
| st.markdown(f""" | |
| <div class="agent-status {status_class}"> | |
| {icon} <strong>{agent.name}</strong><br> | |
| <small>{status_message}</small> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Main Application Interface | |
| st.markdown(""" | |
| <div class="card" style="text-align: center; padding: 3rem;"> | |
| <h1 class="logo-text">βοΈ SmolAgent Travel Planner</h1> | |
| <p style="font-size: 1.2rem; opacity: 0.9; margin-top: 1rem;"> | |
| AI-powered travel planning with specialized agents working together to create your perfect trip | |
| </p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Initialize the system | |
| try: | |
| if 'travel_system' not in st.session_state: | |
| st.session_state.travel_system = TravelPlannerSystem() | |
| travel_system = st.session_state.travel_system | |
| # Input Section | |
| st.markdown('<div class="card">', unsafe_allow_html=True) | |
| st.markdown("## π Plan Your Perfect Trip") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| destination = st.text_input( | |
| "π― Where do you want to go?", | |
| placeholder="e.g., Tokyo, Japan or Paris, France", | |
| help="Enter your dream destination" | |
| ) | |
| duration = st.selectbox( | |
| "π Trip Duration", | |
| ["3 days", "1 week", "2 weeks", "1 month", "Custom"], | |
| help="How long will you be traveling?" | |
| ) | |
| if duration == "Custom": | |
| duration = st.text_input("Specify custom duration:", placeholder="e.g., 10 days") | |
| with col2: | |
| budget = st.selectbox( | |
| "π° Budget Range", | |
| ["Budget ($50-100/day)", "Mid-range ($100-250/day)", "Luxury ($250+/day)", "No specific budget"], | |
| help="What's your daily budget range?" | |
| ) | |
| travel_style = st.selectbox( | |
| "π Travel Style", | |
| ["Adventure & Outdoor", "Cultural & Historical", "Relaxation & Spa", "Nightlife & Entertainment", | |
| "Food & Culinary", "Family-Friendly", "Business Travel", "Solo Traveler", "Romantic Getaway"], | |
| help="What kind of traveler are you?" | |
| ) | |
| interests = st.text_area( | |
| "π¨ Interests & Preferences", | |
| placeholder="Tell us about your interests: art, food, history, adventure, shopping, nightlife, nature, etc.", | |
| help="The more specific you are, the better recommendations we can provide!" | |
| ) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Planning Button | |
| col1, col2, col3 = st.columns([1, 2, 1]) | |
| with col2: | |
| plan_trip = st.button("π CREATE MY TRAVEL PLAN", type="primary") | |
| # Results Section | |
| if plan_trip and destination: | |
| with st.spinner(""): | |
| # Display agent status dashboard | |
| display_agent_status(travel_system.agents) | |
| # Create the travel plan | |
| st.markdown("### π Agents Working Together...") | |
| # Progress tracking | |
| progress_bar = st.progress(0) | |
| status_placeholder = st.empty() | |
| results = {} | |
| total_agents = len(travel_system.agents) | |
| # Update progress as each agent completes | |
| for i, (agent_name, agent) in enumerate(travel_system.agents.items()): | |
| progress = (i + 1) / total_agents | |
| progress_bar.progress(progress) | |
| status_placeholder.text(f"Agent {i+1}/{total_agents}: {agent.name} working...") | |
| time.sleep(0.5) # Brief delay for visual effect | |
| # Execute the travel planning | |
| results = travel_system.create_travel_plan( | |
| destination, duration, budget, travel_style, interests | |
| ) | |
| progress_bar.progress(1.0) | |
| status_placeholder.text("β All agents completed successfully!") | |
| # Display Results in Tabs | |
| st.markdown(f'<div class="card"><h2>π― Your {destination} Travel Plan</h2></div>', unsafe_allow_html=True) | |
| tab1, tab2, tab3, tab4, tab5 = st.tabs([ | |
| "π Destination Info", "π¨ Accommodation", "π Activities", | |
| "π Itinerary", "π° Budget" | |
| ]) | |
| with tab1: | |
| st.markdown('<div class="travel-result">', unsafe_allow_html=True) | |
| st.markdown(results.get("destination_info", "Information not available")) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| with tab2: | |
| st.markdown('<div class="travel-result">', unsafe_allow_html=True) | |
| st.markdown(results.get("accommodation", "Information not available")) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| with tab3: | |
| st.markdown('<div class="travel-result">', unsafe_allow_html=True) | |
| st.markdown(results.get("activities", "Information not available")) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| with tab4: | |
| st.markdown('<div class="travel-result">', unsafe_allow_html=True) | |
| st.markdown(results.get("itinerary", "Information not available")) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| with tab5: | |
| st.markdown('<div class="travel-result">', unsafe_allow_html=True) | |
| st.markdown(results.get("budget_breakdown", "Information not available")) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Final agent status | |
| st.markdown("### β Mission Complete!") | |
| display_agent_status(travel_system.agents) | |
| elif plan_trip: | |
| st.warning("π¨ Please enter a destination to start planning your trip!") | |
| # Tips and Examples Section | |
| if not plan_trip or not destination: | |
| st.markdown('<div class="card">', unsafe_allow_html=True) | |
| st.markdown("## π‘ How It Works") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.markdown(""" | |
| ### π Research Phase | |
| Our **Destination Expert** searches for: | |
| - Current weather & best times to visit | |
| - Local customs & cultural insights | |
| - Safety requirements & travel tips | |
| - Transportation options | |
| """) | |
| with col2: | |
| st.markdown(""" | |
| ### π¨ Planning Phase | |
| Our specialists find: | |
| - **Accommodation** matching your budget | |
| - **Activities** based on your interests | |
| - **Restaurants** & local dining spots | |
| - Hidden gems & local experiences | |
| """) | |
| with col3: | |
| st.markdown(""" | |
| ### π Organization Phase | |
| Our planners create: | |
| - **Day-by-day itinerary** optimized for you | |
| - **Budget breakdown** with cost estimates | |
| - **Logistics** and travel coordination | |
| - **Money-saving tips** and deals | |
| """) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Example destinations | |
| st.markdown('<div class="card">', unsafe_allow_html=True) | |
| st.markdown("## π Popular Destinations") | |
| example_cols = st.columns(4) | |
| examples = [ | |
| ("πΎ Tokyo, Japan", "tokyo japan"), | |
| ("π₯ Paris, France", "paris france"), | |
| ("ποΈ Rome, Italy", "rome italy"), | |
| ("ποΈ Bali, Indonesia", "bali indonesia") | |
| ] | |
| for i, (display_name, destination_value) in enumerate(examples): | |
| with example_cols[i]: | |
| if st.button(display_name, key=f"example_{i}"): | |
| st.session_state.destination_input = destination_value | |
| st.rerun() | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| except Exception as e: | |
| st.error(f""" | |
| <div style="display: flex; align-items: center; gap: 12px;"> | |
| <span>β Application Error: {str(e)}</span> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown(""" | |
| <div class="card"> | |
| <h3>π§ Troubleshooting</h3> | |
| <ul> | |
| <li>Ensure your GROQ_API_KEY is set in environment variables</li> | |
| <li>Check your internet connection for web searches</li> | |
| <li>Try refreshing the page or restarting the application</li> | |
| </ul> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Sidebar controls | |
| with st.sidebar: | |
| st.markdown("### π οΈ System Controls") | |
| if st.button("π Reset Application"): | |
| for key in list(st.session_state.keys()): | |
| if key.startswith('travel_'): | |
| del st.session_state[key] | |
| st.rerun() | |
| st.markdown("### π Agent Performance") | |
| if 'travel_system' in st.session_state: | |
| for name, agent in st.session_state.travel_system.agents.items(): | |
| status = getattr(agent, 'status', 'idle') | |
| st.metric(agent.name, status.title()) | |
| st.markdown("### π¬ Feedback") | |
| feedback = st.text_area("Share your experience:", placeholder="How was your travel plan?") | |
| if st.button("Submit Feedback"): | |
| st.success("Thank you for your feedback!") | |
| # Footer | |
| st.markdown(""" | |
| <div style="text-align: center; padding: 2rem; opacity: 0.8;"> | |
| <p>SmolAgent Travel Planner v2.0 | Powered by AI Collaboration</p> | |
| </div> | |
| """, unsafe_allow_html=True) |