{"id":47597087,"url":"https://github.com/juandagalo/flight-report-agent","last_synced_at":"2026-04-01T18:18:43.582Z","repository":{"id":345448393,"uuid":"1163685504","full_name":"juandagalo/flight-report-agent","owner":"juandagalo","description":"Multi-provider LLM travel recommendation agent with RAG-enhanced suggestions, Qdrant vector search, MCP server, and Claude Code integration. Generates comparative PDF reports with real flight data.","archived":false,"fork":false,"pushed_at":"2026-03-19T05:28:35.000Z","size":369,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-19T21:14:36.662Z","etag":null,"topics":["amadeus-api","claude","fastapi","langgraph","mcp","openai","pdf-generation","python","qdrant","rag","sse","travel"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juandagalo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-22T01:32:53.000Z","updated_at":"2026-03-19T05:21:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/juandagalo/flight-report-agent","commit_stats":null,"previous_names":["juandagalo/flight-report-agent"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/juandagalo/flight-report-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juandagalo%2Fflight-report-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juandagalo%2Fflight-report-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juandagalo%2Fflight-report-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juandagalo%2Fflight-report-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juandagalo","download_url":"https://codeload.github.com/juandagalo/flight-report-agent/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juandagalo%2Fflight-report-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amadeus-api","claude","fastapi","langgraph","mcp","openai","pdf-generation","python","qdrant","rag","sse","travel"],"created_at":"2026-04-01T18:18:42.933Z","updated_at":"2026-04-01T18:18:43.568Z","avatar_url":"https://github.com/juandagalo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Travel Recommendation Agent\n\nA FastAPI + LangGraph agent that generates a **comparative PDF travel report** based on user preferences. Given an origin airport, budget, travel dates, and preferred activities, the agent suggests destinations, searches for real flights via the Amadeus API, enriches the results with weather data, and produces a downloadable PDF report.\n\nUsers interact via **natural-language messages** through the chat endpoint, which returns the PDF directly. The agent also exposes an **MCP server** for integration with Claude Code and other MCP clients.\n\n## v2 Features\n\n- **Multi-provider LLM support** -- Switch between OpenAI and Anthropic Claude via the `LLM_PROVIDER` environment variable. All LLM-using nodes (intake, suggest, enrich) go through a provider-agnostic factory function.\n- **RAG-enhanced recommendations** -- A Qdrant vector store (embedded mode, no external server) provides destination knowledge from WikiVoyage and user interaction history. The suggest and enrich nodes incorporate RAG context into their prompts when available, with graceful fallback to original prompts when Qdrant is empty or unavailable.\n- **WikiVoyage knowledge base ingestion** -- A scraper + chunker + ingestion pipeline fetches destination content from WikiVoyage, splits it into token-aware chunks, embeds them via OpenAI `text-embedding-3-small`, and stores them in the `travel_knowledge` Qdrant collection.\n- **User interaction history** -- After each pipeline run, a summary of the user's request and results is stored in the `interactions` Qdrant collection (when a `user_id` is provided). Future requests from the same user benefit from personalized context.\n- **MCP server** -- The agent is exposed as an MCP tool (`flight_report`) for integration with Claude Desktop, Claude Code, and other MCP-compatible clients.\n- **Claude Code skill** -- A skill definition at `skills/flight-report/SKILL.md` enables Claude Code to automatically invoke the flight report tool in response to travel-related requests.\n\n## Architecture\n\n```\nintake -\u003e validate -\u003e suggest -\u003e search_flights -\u003e enrich -\u003e generate_report -\u003e store_interaction -\u003e END\n                        |                            |                                |\n                        v                            v                                v\n                  [Qdrant RAG]                 [Qdrant RAG]                   [Qdrant Store]\n               travel_knowledge             travel_knowledge                  interactions\n                  interactions\n                        ^__________________________|\n                          (retry if no flights found)\n```\n\n1. **intake** -- extracts structured travel preferences from natural language (passthrough for JSON requests)\n2. **validate** -- checks date ordering and missing request (field constraints enforced by Pydantic schemas)\n3. **suggest** -- LLM proposes candidate destinations; queries Qdrant for destination knowledge and user history to enhance recommendations\n4. **search_flights** -- queries Amadeus API for real flight offers; retries with new destinations if none found\n5. **enrich** -- fetches weather data (Open-Meteo) and activity descriptions (LLM); queries Qdrant for destination-specific context\n6. **generate_report** -- builds a comparative PDF report with scores, flights, weather, and activities\n7. **store_interaction** -- persists a summary of the interaction to Qdrant for future personalization (requires `user_id`)\n\n## Tech Stack\n\n- **LangGraph** -- agent pipeline orchestration\n- **FastAPI** -- REST API with SSE streaming\n- **Amadeus API** -- real flight search\n- **OpenAI / Anthropic Claude** -- destination suggestion, enrichment, and natural-language intake (configurable via `LLM_PROVIDER`)\n- **Qdrant** -- vector store for RAG (embedded mode, disk-persisted)\n- **OpenAI Embeddings** -- `text-embedding-3-small` (1536 dimensions) for all vector operations\n- **ReportLab** -- PDF generation\n- **MCP SDK** -- Model Context Protocol server for tool exposure\n\n## Setup\n\n```bash\n# Install dependencies\nuv sync\n\n# Configure environment\ncp .env.example .env  # fill in your keys\n\n# Ingest WikiVoyage knowledge base (one-time, requires OPENAI_API_KEY)\nuv run ingest-wikivoyage\n\n# Run the FastAPI server\nuvicorn src.app.main:app --reload\n\n# Or run the MCP server (for Claude Code / Claude Desktop)\nuv run flight-report-mcp\n```\n\n### Environment Variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `OPENAI_API_KEY` | Yes | -- | OpenAI API key (used for LLM when provider is openai, and always for embeddings) |\n| `AMADEUS_CLIENT_ID` | Yes | -- | Amadeus API client ID |\n| `AMADEUS_CLIENT_SECRET` | Yes | -- | Amadeus API client secret |\n| `AMADEUS_ENV` | No | `test` | Amadeus environment: `test` or `production` |\n| `LLM_PROVIDER` | No | `openai` | LLM provider: `openai` or `claude` |\n| `OPENAI_MODEL` | No | `gpt-4o` | OpenAI model name |\n| `ANTHROPIC_API_KEY` | If claude | -- | Anthropic API key (required when `LLM_PROVIDER=claude`) |\n| `ANTHROPIC_MODEL` | No | `claude-sonnet-4-20250514` | Anthropic model name |\n| `QDRANT_PATH` | No | `data/qdrant` | Qdrant embedded storage path (local disk) |\n| `QDRANT_COLLECTION_KNOWLEDGE` | No | `travel_knowledge` | Qdrant collection for WikiVoyage content |\n| `QDRANT_COLLECTION_INTERACTIONS` | No | `interactions` | Qdrant collection for user interaction history |\n| `EMBEDDING_DIMENSION` | No | `1536` | Embedding vector dimension (matches text-embedding-3-small) |\n| `REPORT_OUTPUT_DIR` | No | `reports` | Directory for generated PDF reports |\n\n**Note:** `OPENAI_API_KEY` is always required, even when using Claude as the LLM provider, because embeddings use OpenAI's `text-embedding-3-small` model (Anthropic does not offer a standalone embedding API).\n\n### MCP Server Configuration\n\nTo use the flight report agent from Claude Code or Claude Desktop, add this to your MCP settings:\n\n```json\n{\n  \"mcpServers\": {\n    \"flight-report-agent\": {\n      \"command\": \"uv\",\n      \"args\": [\"--directory\", \"/path/to/flight-report-agent\", \"run\", \"flight-report-mcp\"],\n      \"env\": {\n        \"OPENAI_API_KEY\": \"${OPENAI_API_KEY}\",\n        \"ANTHROPIC_API_KEY\": \"${ANTHROPIC_API_KEY}\",\n        \"LLM_PROVIDER\": \"claude\",\n        \"AMADEUS_CLIENT_ID\": \"${AMADEUS_CLIENT_ID}\",\n        \"AMADEUS_CLIENT_SECRET\": \"${AMADEUS_CLIENT_SECRET}\"\n      }\n    }\n  }\n}\n```\n\n## Testing\n\n```bash\nuv run pytest -v\n```\n\n195 tests covering schemas, all graph nodes, services, pipeline logic, API endpoints, MCP server, RAG integration, and end-to-end integration. All external I/O is mocked; integration tests use real Qdrant in embedded mode with temporary directories.\n\n## Running\n\n```bash\nuvicorn src.app.main:app --reload\n```\n\nAPI available at `http://localhost:8000`. Interactive docs at `/docs`.\n\n## Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/health` | Health check |\n| `POST` | `/api/chat` | Natural-language travel request, returns PDF |\n| `POST` | `/api/chat/stream` | SSE streaming version -- emits node-by-node progress events |\n| `GET` | `/api/reports/{filename}` | Download a generated PDF report |\n| `GET` | `/api/graph/ascii` | ASCII diagram of the LangGraph pipeline |\n| `GET` | `/api/graph/viewer` | Interactive Mermaid graph viewer |\n\n## Example\n\n```bash\ncurl -X POST http://localhost:8000/api/chat \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"message\": \"Quiero ir a la playa desde Bogota en julio, presupuesto 1500 USD para 2 personas\"}' \\\n  --output informe.pdf\n```\n\nOn success the response is the PDF file directly (`application/pdf`).\n\n### With User ID (for personalized recommendations)\n\n```bash\ncurl -X POST http://localhost:8000/api/chat \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"message\": \"Quiero ir a la playa desde Bogota en julio\", \"user_id\": \"juan-123\"}' \\\n  --output informe.pdf\n```\n\n### SSE Streaming\n\nFor real-time progress updates, use the streaming endpoint:\n\n```bash\ncurl -N -X POST http://localhost:8000/api/chat/stream \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"message\": \"Quiero ir a la playa desde Bogota en julio, presupuesto 1500 USD para 2 personas\"}'\n```\n\nSample events:\n\n```\nevent: node_start\ndata: {\"node\": \"intake\", \"message\": \"Interpretando tu solicitud...\"}\n\nevent: node_end\ndata: {\"node\": \"intake\", \"message\": \"Solicitud interpretada\"}\n\nevent: node_start\ndata: {\"node\": \"validate\", \"message\": \"Validando datos de viaje...\"}\n\n...\n\nevent: node_end\ndata: {\"node\": \"store_interaction\", \"message\": \"Preferencias guardadas\"}\n\nevent: complete\ndata: {\"report_url\": \"/api/reports/report_abc123.pdf\", \"message\": \"Informe PDF listo para descargar\"}\n```\n\nThen download the PDF:\n\n```bash\ncurl http://localhost:8000/api/reports/report_abc123.pdf --output informe.pdf\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuandagalo%2Fflight-report-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuandagalo%2Fflight-report-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuandagalo%2Fflight-report-agent/lists"}