import os import gradio as gr from dotenv import load_dotenv from crewai import Agent, Task, Crew # Firecrawl tool (from crewai tools package) from crewai_tools import FirecrawlSearchTool # ----------------------------- # Load environment variables # ----------------------------- load_dotenv() # reads local .env (HF Spaces uses Secrets as env vars) OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "").strip() FIRECRAWL_API_KEY = os.getenv("FIRECRAWL_API_KEY", "").strip() def _validate_env(): missing = [] if not OPENAI_API_KEY: missing.append("OPENAI_API_KEY") if not FIRECRAWL_API_KEY: missing.append("FIRECRAWL_API_KEY") if missing: return ( "❌ Missing environment variables: " + ", ".join(missing) + "\n\n" "On Hugging Face Spaces:\n" "Space → Settings → Secrets → add them as:\n" "- OPENAI_API_KEY\n" "- FIRECRAWL_API_KEY\n" ) return None # ----------------------------- # Build CrewAI setup # ----------------------------- def build_crew(): """ Builds tools, agents, tasks, and the crew. """ # Tool travel_search_tool = FirecrawlSearchTool(api_key=FIRECRAWL_API_KEY) # Agents flights_agent = Agent( role="Flight Search Specialist", goal="Find round-trip flight options that balance price, duration, and convenience.", backstory="Expert in comparing flights across airlines, booking sites, and schedules.", tools=[travel_search_tool], allow_delegation=False, ) hotels_agent = Agent( role="Hotel Research Specialist", goal="Find good hotel options near destination city center, balancing price, reviews, and amenities.", backstory="Knows how to compare hotels on different sites and summarize top picks.", tools=[travel_search_tool], allow_delegation=False, ) info_agent = Agent( role="Destination Info Specialist", goal="Collect useful information about attractions, safety tips, and transport options in the destination.", backstory="Experienced in giving local insights to travelers, focusing on practical tips.", tools=[travel_search_tool], allow_delegation=False, ) consultant_agent = Agent( role="Travel Consultant", goal="Summarize all findings (flights, hotels, destination info) into a final travel plan.", backstory="Expert travel planner who creates easy-to-follow itineraries.", allow_delegation=False, ) # Tasks search_flights = Task( description=( "Search for round-trip flights from {origin} to {destination}, departing around {departure_date} " "and returning around {return_date}, within budget {budget}." ), expected_output=""" Top 3 round-trip flight options: 1) Airline/Provider: ..., Route: ..., Depart/Arrive: ..., Return: ..., Total duration: ..., Stops: ..., Price: ... 2) ... 3) ... """, agent=flights_agent, ) search_hotels = Task( description=( "Find 3-4 hotel options in {destination}, near city center, within nightly budget {hotel_budget}. " "Include hotel name, location, rating, and price per night." ), expected_output=""" Top hotel options: 1) Name: ..., Area: ..., Rating: ..., Price/night: ..., Why it’s good: ... 2) ... 3) ... 4) ... """, agent=hotels_agent, ) get_destination_info = Task( description=( "Gather key travel info for {destination}: top attractions, safety advice, and transport options " "from airport to city center." ), expected_output=""" Destination info: - Attractions: ... - Safety: ... - Transport (airport -> city): ... - Local tips: ... """, agent=info_agent, ) create_summary = Task( description="Summarize the results from flights, hotels, and destination info into a structured final travel plan.", expected_output=""" Final Travel Plan: ✈️ Flights: - ... 🏨 Hotels: - ... 📍 Destination Tips: - ... """, agent=consultant_agent, ) crew = Crew( agents=[flights_agent, hotels_agent, info_agent, consultant_agent], tasks=[search_flights, search_hotels, get_destination_info, create_summary], verbose=True, ) return crew def run_travel_planner(origin, destination, departure_date, return_date, trip_budget, hotel_budget): env_error = _validate_env() if env_error: return env_error # Some light cleanup origin = (origin or "").strip() destination = (destination or "").strip() departure_date = (departure_date or "").strip() return_date = (return_date or "").strip() trip_budget = (trip_budget or "").strip() hotel_budget = (hotel_budget or "").strip() if not all([origin, destination, departure_date, return_date, trip_budget, hotel_budget]): return "❗ Please fill in all fields before running." try: crew = build_crew() result = crew.kickoff( inputs={ "origin": origin, "destination": destination, "departure_date": departure_date, "return_date": return_date, "budget": trip_budget, "hotel_budget": hotel_budget, } ) # CrewAI may return a string or richer object; stringify safely return str(result) except Exception as e: return f"❌ Error while running the crew:\n{type(e).__name__}: {e}" # ----------------------------- # Gradio UI # ----------------------------- with gr.Blocks(title="CrewAI Multi-Agent Travel Planner") as demo: gr.Markdown( "# ✈️🏨 CrewAI Multi-Agent Travel Planner\n" "Enter your trip details and run the multi-agent workflow (Flights + Hotels + Destination Info → Final Plan)." ) with gr.Row(): origin = gr.Textbox(label="Origin", value="Singapore") destination = gr.Textbox(label="Destination", value="Tokyo") with gr.Row(): departure_date = gr.Textbox(label="Departure Date", value="Mar 15th, 2026") return_date = gr.Textbox(label="Return Date", value="Mar 30th, 2026") with gr.Row(): trip_budget = gr.Textbox(label="Trip Budget (total flights budget)", value="$2000") hotel_budget = gr.Textbox(label="Hotel Budget (per night)", value="$150") run_btn = gr.Button("Run Multi-Agent Planner", variant="primary") output = gr.Textbox(label="Result", lines=22) run_btn.click( fn=run_travel_planner, inputs=[origin, destination, departure_date, return_date, trip_budget, hotel_budget], outputs=[output], ) gr.Markdown( "### 🔐 Secrets needed on Hugging Face\n" "- `OPENAI_API_KEY`\n" "- `FIRECRAWL_API_KEY`\n" ) if __name__ == "__main__": demo.launch()