from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from app.config import get_settings from app.routers import docs, ingest, query from app.state import AppState @asynccontextmanager async def lifespan(app: FastAPI): settings = get_settings() state = AppState.from_settings(settings) state.vector_store.load_existing() app.state.rag = state yield app = FastAPI( title="RAG Document Q&A API", description="A low-level RAG pipeline for document ingestion and grounded Q&A.", version="0.1.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) app.include_router(ingest.router) app.include_router(query.router) app.include_router(docs.router) frontend_dir = Path(__file__).resolve().parent.parent / "frontend" if frontend_dir.exists(): app.mount("/static", StaticFiles(directory=frontend_dir), name="static") @app.get("/", include_in_schema=False) def frontend() -> FileResponse: return FileResponse(frontend_dir / "index.html") @app.get("/health", tags=["health"]) def health() -> dict[str, str]: return {"status": "ok"}