A sophisticated, production-ready AI-powered travel planning application that uses multi-agent architecture with LangGraph to provide intelligent flight and hotel booking assistance. Features real-time WebSocket communication, multi-leg flight support, and a beautiful dark/light mode interface.
- Natural Language Understanding: Uses Google Gemini 2.5-flash-lite for intent classification
- Multi-Agent Architecture: Built with LangGraph for stateful, graph-based workflows
- Context-Aware Responses: Maintains conversation history and user preferences
- Real-time Communication: WebSocket-based bidirectional streaming
- Voice Interface: Speech-to-Text input and Text-to-Speech responses for hands-free interaction
- Multi-Leg Flight Support: Handle complex itineraries (e.g., NYC → London → Paris → NYC)
- Layover Visualization: Display connection times and layover cities
- Realistic Flight Data: Mock data with actual airport codes, airlines, and flight numbers
- Price Comparison: Interactive charts showing price trends
- Interactive Maps: Real-time flight path visualization with React-Leaflet
- Smart Hotel Search: Location-based hotel recommendations
- Amenity Filtering: Search by facilities (WiFi, pool, gym, etc.)
- Guest Management: Flexible check-in/check-out date selection
- Room Selection: Multiple room types (Standard, Deluxe, Suite, Penthouse)
- Map Integration: Hotel location visualization
- Dark/Light Mode: Persistent theme switching with smooth transitions
- Responsive Design: Mobile-first approach, works on all devices
- Loading States: Skeleton loaders and smooth animations
- Real-time Updates: Live agent status indicators
- Accessibility: ARIA labels and keyboard navigation support
- Separate Booking Flows: Different workflows for flights vs. hotels
- HTML Itineraries: Beautiful, printable confirmation emails
- Passenger/Guest Information: Comprehensive data collection
- Payment Integration: Mock payment processing (ready for real integration)
┌─────────────────────────────────────────────────────────────────┐
│ USER INTERFACE │
│ (React + TypeScript) │
└─────────────────────────┬───────────────────────────────────────┘
│ HTTP/WebSocket
┌─────────────────────────▼───────────────────────────────────────┐
│ FASTAPI BACKEND │
│ ┌─────────────────┐ ┌─────────────────────────────────────┐ │
│ │ WebSocket │ │ HTTP Endpoints │ │
│ │ Manager │ │ │ │
│ └─────────────────┘ └─────────────────────────────────────┘ │
└─────────────┬─────────────────────────┬─────────────────────────┘
│ │
│ │
┌─────────────▼─────┐ ┌──────────▼─────────────────────┐
│ LangGraph Graph │ │ Services Layer │
│ │ │ ┌────────────────────────────┐ │
│ Coordinator │ │ │ Booking Service │ │
│ Agent │◄───────┤ │ Preferences Manager │ │
│ (Intent Route) │ │ │ Event Manager │ │
│ │ │ │ Summarization Service │ │
│ Flight Agent ────┼────────┼─►│ Travel Mock Data │ │
│ Hotel Agent ────┼────────┼─►│ - Mock Flights │ │
│ Response Agent ◄─┼────────┼─►│ - Mock Hotels │ │
│ │ │ └────────────────────────────┘ │
│ State Management │ └─────────────────────────────────┘
│ (SharedState) │
└───────────────────┘
1. Coordinator Agent - The Master Router
- Responsibilities: Intent classification, parameter extraction, preference learning
- Inputs: User messages, conversation history, learned preferences
- Outputs: Intent classification, flight/hotel search parameters
- Agent Flow:
User Message → LLM Classification → Parameter Extraction → Route Decision ↓ ┌─────────────────────────────────────┐ │ Routing Logic │ ├─────────────────────────────────────┤ │ FLIGHT → Flight Agent │ │ HOTEL → Hotel Agent │ │ BOTH → Flight → Hotel → Response │ │ OTHER → Response Agent │ └─────────────────────────────────────┘
2. Flight Agent - Flight Search Specialist
- Responsibilities: Multi-leg flight search, price comparison, route optimization
- Inputs: FlightSearchParams (origin, destination, dates, passengers, cabin class)
- Outputs: FlightResult objects with segments, layovers, pricing
- Data Sources: Mock flight generator + Tavily web search (with fallback)
3. Hotel Agent - Hotel Search Specialist
- Responsibilities: Location-based hotel search, amenity filtering, pricing
- Inputs: HotelSearchParams (city, dates, guests, budget, rating)
- Outputs: HotelResult objects with amenities, pricing, location data
- Data Sources: Mock hotel generator + Tavily web search (with fallback)
4. Response Agent - Natural Language Generator
- Responsibilities: Context-aware response generation, conversation flow management
- Inputs: Flight/hotel results, user intent, conversation context
- Outputs: Natural language responses, conversation history updates
LangGraph SharedState Pattern:
SharedState {
session_id: str,
conversation_history: [ConversationTurn],
user_preferences: UserPreferences,
current_intent: Intent,
flight_params: FlightSearchParams | None,
hotel_params: HotelSearchParams | None,
flight_results: [FlightResult],
hotel_results: [HotelResult],
selected_flight_id: str | None,
selected_hotel_id: str | None,
is_interrupted: bool,
current_run_id: str | None,
pending_flight_booking: str | None,
pending_hotel_booking: str | None
}
Context Transfer Mechanism:
- Conversation Compression: Older messages summarized for memory efficiency
- Preference Persistence: User preferences learned and stored across sessions
- Selection Preservation: Bookings and selections maintained during interruptions
- State Synchronization: WebSocket real-time state updates to frontend
- Primary Function: Intent classification and request routing
- Inputs:
- User message text
- Last 5 conversation turns (context window)
- Current user preferences
- Session metadata
- Processing:
- Gemini LLM classifies intent (FLIGHT/HOTEL/COMBINED/OTHER)
- Extracts search parameters from natural language
- Learns user preferences from conversation
- Determines optimal agent routing
- Outputs:
- Intent classification (Intent enum)
- FlightSearchParams or HotelSearchParams objects
- Updated user preferences
- Routing decision for LangGraph workflow
- Primary Function: Multi-leg flight search and price comparison
- Inputs:
- FlightSearchParams: origin, destination, depart_date, return_date, passengers, cabin_class, max_stops
- User preferences (preferred airlines, flight times, etc.)
- Processing:
- Tavily web search with fallback to mock data
- Multi-leg flight generation with realistic layovers
- Price calculation and filtering
- Airport coordinate mapping
- Outputs:
- FlightResult[] with segments, layovers, total journey duration
- Source tracking (live web search vs mock data)
- Partial results flag for interruption handling
- Primary Function: Location-based hotel search with amenity filtering
- Inputs:
- HotelSearchParams: city, check_in, check_out, guests, budget, min_rating
- Current flight destination (auto-derivation)
- Processing:
- Tavily web search with fallback to mock data
- Hotel amenity filtering and rating assessment
- Price per night calculation
- Location coordinate mapping
- Outputs:
- HotelResult[] with amenities, pricing, location data
- Source tracking and partial results flag
- Primary Function: Context-aware response generation
- Inputs:
- FlightResults[] and/or HotelResults[]
- Current user intent
- Conversation context and history
- User selections and pending bookings
- Processing:
- Gemini streaming response generation
- Context-aware response formatting
- Selection validation and confirmation prompts
- Outputs:
- Natural language response text
- Updated conversation history
- Streaming tokens for real-time display
1. Flight Search Workflow:
User: "Find flights from NYC to London"
↓
Coordinator: Intent=FLIGHT, extract params
↓
Flight Agent: Search flights, generate results
↓
Response Agent: Format and present results
2. Hotel Search Workflow:
User: "Hotels in Tokyo"
↓
Coordinator: Intent=HOTEL, extract params
↓
Hotel Agent: Search hotels, generate results
↓
Response Agent: Format and present results
3. Combined Search Workflow:
User: "Plan a trip to Paris - flights and hotels"
↓
Coordinator: Intent=COMBINED, extract both params
↓ (parallel or sequential)
Flight Agent → Hotel Agent → Response Agent
4. Refinement Workflow:
User: "Show me cheaper options"
↓
Coordinator: Intent=REFINE, modify existing params
↓
Relevant Agent: Re-search with updated criteria
↓
Response Agent: Present refined results
Our system implements robust interruption handling to support "double-texting" - when users send new requests while previous ones are still processing.
Active Run Registry:
# In main.py - Tracks active processing
active_runs: Dict[str, ActiveRun] = {}
active_tasks: Dict[str, asyncio.Task] = {}
class ActiveRun(BaseModel):
run_id: str
agent_type: AgentType
started_at: float
session_id: strInterruption Detection Flow:
New User Message Received
↓
Check session_id in active_runs
↓
If found → Interruption detected
↓
Mark session state as is_interrupted = True
↓
Cancel existing asyncio task
↓
Create new run with fresh request_id
Asyncio Task Cancellation:
# Cancel existing task with timeout
if session_id in active_tasks:
existing_task = active_tasks[session_id]
existing_task.cancel()
# Wait for cancellation with timeout
try:
await asyncio.wait_for(existing_task, timeout=2.0)
except asyncio.CancelledError:
print("Previous task cancelled successfully")
except asyncio.TimeoutError:
print("Timeout waiting for task cancellation")State Preservation During Cancellation:
# Clear results but preserve selections
session.shared_state.is_interrupted = True
session.shared_state.flight_results = [] # Clear search results
session.shared_state.hotel_results = [] # Clear search results
# PRESERVE: selected_flight_id, selected_hotel_id, pending bookingsResult Flagging System:
- All search results include
is_partial: boolflag - Partial results displayed with visual indicators (amber "PARTIAL" badge)
- Context includes which results are from interrupted runs
Context Transfer Mechanism:
def preserve_context_during_interruption(session):
# What gets preserved:
preserved_state = {
'selected_flight_id': session.selected_flight_id,
'selected_hotel_id': session.selected_hotel_id,
'pending_flight_booking': session.pending_flight_booking,
'pending_hotel_booking': session.pending_hotel_booking,
'conversation_history': session.conversation_history,
'user_preferences': session.user_preferences
}
# What gets cleared:
cleared_state = {
'flight_results': [], # Fresh search needed
'hotel_results': [], # Fresh search needed
'flight_params': None, # Extract new from user input
'hotel_params': None # Extract new from user input
}Core Interruption Libraries:
asyncio: Async task management and cancellationasyncio.wait_for(): Timeout handling for graceful shutdownFastAPI: Background task coordinationLangGraph: State persistence across agent boundaries
Implementation Details:
- Task Registry: Dictionary mapping session_id → asyncio.Task
- Run Tracking: Unique request_id for each user request
- State Synchronization: WebSocket real-time updates during interruption
- Timeout Handling: 2-second timeout for task cancellation
Visual Feedback:
- Status badge: "
⚠️ Interrupted - Updating..." - Loading skeleton continues with interruption indicator
- Partial results grayed out with warning styling
Seamless Continuation:
- User selections preserved across interruptions
- Conversation history maintained
- Preferences learned over time persist
- New search starts fresh but context-aware
React 19 + TypeScript
├── WebSocket Hook (Real-time Updates)
├── Theme Context (Dark/Light Mode)
├── Components
│ ├── MessageList (Chat Interface)
│ ├── BookingModal (Multi-step Forms)
│ ├── AgentStatus (Live Indicators)
│ ├── PreferencesPanel (User Settings)
│ └── Visualizations
│ ├── FlightMap (Route Visualization)
│ ├── FlightTimeline (Multi-leg Display)
│ ├── HotelMap (Location Markers)
│ └── PriceChart (Comparison Charts)
└── Types (TypeScript Definitions)
- Python 3.9+ (Backend)
- Node.js 18+ (Frontend)
- Google Gemini API Key (Required for NLU)
- Tavily API Key (Optional - for web search)
git clone <your-repo-url>
cd PECATHONcd backend
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txtCreate a .env file in the backend directory:
# Required
GOOGLE_API_KEY=your_gemini_api_key_here
# Optional
TAVILY_API_KEY=your_tavily_api_key_hereGetting API Keys:
- Google Gemini: Visit Google AI Studio
- Tavily (optional): Visit Tavily
cd frontend
# Install dependencies
npm install# From project root
chmod +x start.sh
./start.shTerminal 1 - Backend:
cd backend
source .venv/bin/activate
uvicorn app.main:app --reload --port 8000Terminal 2 - Frontend:
cd frontend
npm run devAccess the Application:
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/docs
# Stop all processes
chmod +x cleanup.sh
./cleanup.sh- Start a conversation: Type naturally, e.g., "I need a flight to Paris"
- Review results: Browse flights or hotels with detailed information
- Book: Click "Book Now" and complete the multi-step form
- Download itinerary: Receive a beautiful HTML confirmation
User: "Book a round-trip flight from New York to Paris with a layover in London"
Agent: [Displays multi-leg flight options with segment details and layover times]
User: "Find me a luxury hotel in Tokyo with a pool"
Agent: [Shows hotels with amenities, pricing, and location maps]
- Click the preferences icon in the input box
- Set budget ranges, preferred airlines, amenities
- Preferences persist across the session
Basic Flight Search:
"I need a flight from New York to Los Angeles"
"Find flights to Paris next weekend"
"Show me flights from JFK to LAX"
Advanced Flight Search:
"I need a round-trip flight from New York to Paris with a layover in London"
"Find me business class flights to Tokyo for 2 passengers"
"Look for direct flights to Miami under $400"
Multi-Leg Flight Example:
"Book a trip from New York to Paris to London and back to New York"
"Plan a multi-city European tour: NYC → London → Paris → NYC"
Basic Hotel Search:
"Find hotels in Tokyo"
"Show me hotels in Paris for next week"
"Hotels in London near the Tower Bridge"
Advanced Hotel Search:
"Find a 5-star hotel in Tokyo with a pool and spa"
"Looking for budget hotels in Miami under $150 per night"
"Hotels in Paris with WiFi and breakfast included"
Test Case 1: Flight Search Interruption
- Send: "Find flights to London" (wait 3-5 seconds)
- While processing, send: "Actually, show me hotels in Paris instead"
- Expected: Flight search cancels, hotel search starts
- Verify: Previous results cleared, new hotel results displayed
Test Case 2: Double-Texting During Results
- Search for flights: "Flights to Los Angeles"
- When results appear, immediately send: "Now find hotels in LA"
- Expected: Seamless transition, results updated appropriately
- Verify: No conflicting displays or partial results
Test Case 3: Partial Selection Preservation
- Search flights: "Flights to Paris"
- Click "Select" on a flight option
- While waiting to book, send: "Actually show me cheaper flights"
- Expected: Previous selection preserved, new search in background
- Verify: Selected flight still shows as chosen, new results available
Test Case 4: Context Transfer
- Search: "Hotels in Tokyo"
- Set preference: "I prefer hotels with pools"
- Send: "Now find flights to Tokyo"
- Expected: Hotel search should include pool preference
- Verify: Preferences learned and applied to new searches
- Start: "I need a flight to London"
- Review results with maps and price charts
- Click "Book Now" on preferred flight
- Complete passenger information form
- Select seat from seat map
- Enter payment details (mock)
- Result: Download HTML itinerary, confirmation message
- Start: "Plan a trip to Tokyo for next month"
- Review flight options with multi-leg support
- Review hotel options with amenities and maps
- Select flight: "I'll take the United Airlines flight"
- Select hotel: "Book the Hilton Tokyo Bay"
- Result: Both selections processed, individual booking flows
- Search: "Flights to Paris"
- User says: "I prefer morning flights and business class"
- Search: "Hotels in London"
- User says: "I need WiFi and prefer luxury hotels"
- Search: "Flights to London" (new request)
- Verify: Preferences automatically applied to search parameters
- Start long search: "Find multi-leg flights around Europe"
- Send interruption: "Wait, show me hotels in Paris instead"
- Complete hotel search and make selection
- Send: "Now continue with the Europe flights from Paris"
- Verify: System remembers Paris as starting point, previous context preserved
Core Functionality:
- Basic flight search returns results
- Basic hotel search returns results
- WebSocket connection establishes successfully
- Real-time status updates during processing
- Result selection works (flights and hotels)
- Booking modal opens with correct data
- Dark/light theme switching works
Advanced Features:
- Multi-leg flights display segments correctly
- Flight maps show route visualization
- Hotel maps display location markers
- Price charts render correctly
- Preferences panel shows learned preferences
- Conversation history maintained
Interruption Handling:
- New requests cancel previous processing
- Partial results show appropriate indicators
- User selections preserved across interruptions
- Context transfers correctly between agents
- Status messages clearly indicate interruption state
Error Handling:
- Graceful handling of API failures
- Fallback to mock data when web search unavailable
- WebSocket disconnection recovery
- Invalid input handling with helpful messages
| Technology | Purpose | Version |
|---|---|---|
| FastAPI | Web framework | Latest |
| Python | Core language | 3.9+ |
| LangGraph | Multi-agent orchestration | Latest |
| LangChain | LLM integrations | Latest |
| Google Gemini | Natural language processing | 2.5-flash-lite |
| Pydantic | Data validation | 2.x |
| Uvicorn | ASGI server | Latest |
| WebSockets | Real-time communication | Latest |
| Tavily | Web search (optional) | Latest |
| Technology | Purpose | Version |
|---|---|---|
| React | UI framework | 19.2.0 |
| TypeScript | Type safety | 5.9.3 |
| Vite | Build tool | 5.1.1 |
| Axios | HTTP client | Latest |
| React-Leaflet | Map visualization | Latest |
| Recharts | Data visualization | Latest |
| Lucide Icons | Icon library | Latest |
PECATHON/
├── backend/
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py # FastAPI entry point
│ │ ├── config.py # Configuration
│ │ ├── constants.py # Constants and enums
│ │ ├── models.py # Pydantic models
│ │ ├── llm.py # Gemini client
│ │ ├── tools.py # Agent tools
│ │ ├── graph/
│ │ │ └── travel_graph.py # LangGraph workflow
│ │ ├── services/
│ │ │ ├── booking_service.py
│ │ │ ├── event_manager.py
│ │ │ ├── preferences.py
│ │ │ └── summarization.py
│ │ ├── travel/
│ │ │ ├── mock_flights.py # Flight data generator
│ │ │ └── mock_hotels.py # Hotel data generator
│ │ └── websocket/
│ │ └── manager.py # WebSocket handler
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── App.tsx # Main app component
│ │ ├── main.tsx # Entry point
│ │ ├── types.ts # TypeScript types
│ │ ├── components/
│ │ │ ├── MessageList.tsx
│ │ │ ├── BookingModal.tsx
│ │ │ ├── AgentStatus.tsx
│ │ │ ├── PreferencesPanel.tsx
│ │ │ └── Visualizations/
│ │ ├── contexts/
│ │ │ └── ThemeContext.tsx
│ │ └── hooks/
│ │ └── useWebSocket.ts
│ ├── package.json
│ └── vite.config.ts
├── start.sh # Quick start script
├── cleanup.sh # Cleanup script
└── README.md
/ws/{session_id}- Real-time bidirectional communication
GET /- Health checkPOST /chat- Process user messagePOST /booking/complete- Complete booking and generate itineraryGET /sse/{session_id}- Server-sent events stream
The system generates realistic multi-leg flights with:
- Flight Segments: Individual legs with airline, flight number, duration
- Layover Calculation: Realistic connection times (45-180 minutes)
- Total Journey Duration: Sum of flight times + layovers
- Visual Indicators: Orange dots for each layover city
- Expandable Details: Click to see full segment breakdown
- Flights: Passenger Info → Seat Selection → Payment → Confirmation
- Hotels: Guest Info → Room Selection → Payment → Confirmation
- Different data models and validation for each type
- Persistent Storage: Saves preference in localStorage
- System Detection: Auto-detects OS dark mode preference
- Smooth Transitions: CSS transitions for theme changes
- Context-based: React Context for global state
GOOGLE_API_KEY=os.getenv("GOOGLE_API_KEY") # Required
TAVILY_API_KEY=os.getenv("TAVILY_API_KEY") # Optional
MODEL_NAME="gemini-2.0-flash-exp"
TEMPERATURE=0.7
MAX_TOKENS=8000server: {
port: 5173,
proxy: {
'/ws': 'ws://localhost:8000',
'/api': 'http://localhost:8000'
}
}Port Already in Use:
lsof -ti:8000 | xargs kill -9Missing API Key:
Error: GOOGLE_API_KEY not found
Solution: Create .env file with valid API key
Module Not Found:
pip install -r requirements.txtPort 5173 in Use:
lsof -ti:5173 | xargs kill -9Dependency Errors:
rm -rf node_modules package-lock.json
npm installWebSocket Connection Failed:
- Ensure backend is running on port 8000
- Check CORS settings in
main.py
# Example for Railway
railway login
railway init
railway up# Example for Vercel
vercel --prodSet these in your hosting platform:
GOOGLE_API_KEYTAVILY_API_KEY(optional)FRONTEND_URL(for CORS)
- Backend: Add tools in
tools.py, update graph intravel_graph.py - Frontend: Create component in
components/, add types intypes.ts - State: Update
models.pyfor new data structures
- Backend: PEP 8 compliant, type hints everywhere
- Frontend: ESLint configured, strict TypeScript
This project is licensed under the MIT License - see the LICENSE file for details.
- Google Gemini for powerful NLU capabilities
- LangGraph for multi-agent orchestration
- React-Leaflet for beautiful map visualizations
- Recharts for interactive data visualization
- FastAPI for high-performance async API framework
For issues, questions, or contributions:
- Open an issue on GitHub
Built with ❤️ for intelligent travel planning
Last updated: January 2025