Spaces:
Running
Running
| """ | |
| main.py β FastAPI application entry point. | |
| This file is intentionally thin: lifespan (service wiring), app creation, | |
| CORS, MCP mount, router registration, and health check only. | |
| All route logic lives in backend/routers/: | |
| ingestion.py β /ingest, /repos | |
| query.py β /search, /query, /query/stream | |
| agent.py β /agent/* | |
| diagrams.py β /repos/{owner}/{name}/tour|diagram | |
| mcp_routes.py β /mcp-status, /mcp-prompt | |
| Shared service instances and dependency providers live in backend/dependencies.py. | |
| """ | |
| import os | |
| from contextlib import asynccontextmanager | |
| import posthog as _posthog | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from backend.config import settings | |
| from backend.dependencies import services | |
| from backend.mcp_server import mcp, init_services as init_mcp_services | |
| from backend.mcp_client import MCPClient | |
| from backend.services.ingestion_service import IngestionService | |
| from backend.services.generation import GenerationService | |
| from backend.services.agent import AgentService | |
| from backend.services.diagram_service import DiagramService | |
| from backend.services.repo_map_service import RepoMapService | |
| from backend.services.readme_service import ReadmeService | |
| from retrieval.retrieval import RetrievalService, Reranker | |
| from ingestion.qdrant_store import QdrantStore | |
| from ingestion.embedder import Embedder | |
| from backend.routers import ingestion, query, agent, diagrams, mcp_routes, sessions | |
| async def lifespan(app: FastAPI): | |
| """ | |
| Startup: initialise all services and wire MCP. | |
| Shutdown: flush analytics. | |
| Services are expensive to create (model loads, connection pools). | |
| We create them once here and store them on the `services` container | |
| imported by all routers via backend/dependencies.py. | |
| """ | |
| if settings.posthog_api_key: | |
| _posthog.api_key = settings.posthog_api_key | |
| _posthog.host = settings.posthog_host | |
| print("Starting up β loading models and connecting to Qdrant...") | |
| # One shared Embedder (600MB model) and one shared QdrantStore (connection pool). | |
| # Both are passed into every service that needs them β no duplicate loads. | |
| _embedder = Embedder() | |
| _qdrant_store = QdrantStore() | |
| _reranker = Reranker() | |
| services.generation = GenerationService() | |
| services.retrieval = RetrievalService( | |
| embedder=_embedder, store=_qdrant_store, | |
| reranker=_reranker, gen=services.generation, | |
| ) | |
| services.ingestion = IngestionService( | |
| store=_qdrant_store, embedder=_embedder, gen=services.generation, | |
| ) | |
| services.diagram = DiagramService(_qdrant_store, services.generation) | |
| services.repo_map = RepoMapService(_qdrant_store) | |
| services.readme = ReadmeService(services.repo_map, services.generation, _qdrant_store) | |
| init_mcp_services(services.retrieval, _qdrant_store) | |
| if any([ | |
| settings.cerebras_api_key, settings.groq_api_key, | |
| settings.gemini_api_key, settings.openrouter_api_key, | |
| settings.anthropic_api_key, | |
| ]): | |
| _port = int(os.getenv("PORT", "8000")) | |
| services.mcp_client = MCPClient(server_url=f"http://localhost:{_port}/mcp") | |
| services.agent = AgentService(services.mcp_client, repo_map_svc=services.repo_map) | |
| print("AgentService ready (MCP-powered agentic RAG enabled).") | |
| else: | |
| print("No LLM API key found β /agent/* endpoints disabled.") | |
| print("All services ready.\n") | |
| async with mcp.session_manager.run(): | |
| yield | |
| if settings.posthog_api_key: | |
| _posthog.flush() | |
| print("Shutting down.") | |
| # ββ App ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app = FastAPI( | |
| title="Cartographer", | |
| description="Ask questions about any GitHub repository. Powered by MCP.", | |
| version="0.2.0", | |
| lifespan=lifespan, | |
| ) | |
| # ββ CORS βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| _origins = [] | |
| if settings.frontend_url: | |
| _origins.append(settings.frontend_url) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=_origins, | |
| allow_origin_regex=r"http://localhost:\d+", | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ββ MCP sub-app ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Mounted before routers so /mcp is handled by FastMCP, not FastAPI. | |
| app.mount("/mcp", mcp.streamable_http_app()) | |
| # ββ Routers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.include_router(ingestion.router) | |
| app.include_router(query.router) | |
| app.include_router(agent.router) | |
| app.include_router(diagrams.router) | |
| app.include_router(mcp_routes.router) | |
| app.include_router(sessions.router) | |
| app.include_router(sessions.artifacts_router) | |
| # ββ Health check βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def health(): | |
| """Simple liveness check.""" | |
| return {"status": "ok"} | |