# File Objective : Main FastAPI entry point with lifespan management. # Scope : apps/api — read-side API server (BFF). # What this file does: # 1. Sets up the FastAPI application with title and CORS middleware. # 2. Instantiates the LangGraph agent once during the lifespan startup. # 3. Exposes the agent reference via app state to avoid circular imports. # 4. Mounts the routers for chat, gaps, graph, and signals. # 5. Serves the built React frontend (apps/web/dist) as static files when present, # so a single container can host both API and UI. # What it does not do: # 1. Maintain DB connections directly (delegated to core adapters). from __future__ import annotations from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from vantage_core.agent.graph import build_agent from vantage_core.log import get_logger, timed from apps.api.routers import chat, gaps, graph, signals log = get_logger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): # Build the agent singleton with its in-memory checkpointer. # Wires heavy adapters (Qdrant, Neo4j, OpenAI) once at startup. log.info("API startup — building agent (models + adapters)") with timed("build_agent startup", log): app.state.agent = build_agent() log.info("API ready") yield # No explicit teardown required for memory saver checkpointer app = FastAPI( title="Vantage API", description="Agentic Market Intelligence Read-side API Layer", version="1.0.0", lifespan=lifespan, ) # Allow all origins for the local dashboard integration app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register endpoints app.include_router(chat.router, prefix="/api") app.include_router(gaps.router, prefix="/api") app.include_router(graph.router, prefix="/api") app.include_router(signals.router, prefix="/api") @app.get("/api/health") def health() -> dict[str, str]: """Lightweight liveness probe for uptime monitors and platform healthchecks.""" return {"status": "ok"} # Serve the built React app (apps/web/dist) as static files. Mounted LAST so it # never shadows the /api routes above. html=True serves index.html at "/". # Skipped gracefully in dev when the frontend hasn't been built yet. _WEB_DIST = Path(__file__).resolve().parents[2] / "apps" / "web" / "dist" if _WEB_DIST.is_dir(): app.mount("/", StaticFiles(directory=str(_WEB_DIST), html=True), name="web") log.info("Serving frontend from %s", _WEB_DIST) else: log.info("No frontend build at %s — API-only mode", _WEB_DIST)