""" FastAPI backend for the RAG pipeline. Requirements: pip install fastapi uvicorn[standard] Run with: uvicorn api:app --host 0.0.0.0 --port 8000 Then: curl -X POST http://localhost:8000/query \\ -H "Content-Type: application/json" \\ -d '{"query": "What is hallucination and cause?"}' The index (documents + FAISS + LLM + compressor) is built ONCE at startup via the "startup" event below, not per-request — pipeline_service caches it in module-level globals after the first _get_index() call, so every request after startup reuses it. """ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from pipeline_service import answer_query, _get_index app = FastAPI(title="Hybrid RAG API", version="1.0") # Adjust or remove this for production — wide open here for local dev # so a browser-based frontend on a different port can call the API. app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) class QueryRequest(BaseModel): query: str = Field(..., min_length=1, description="The user's question") class SourceInfo(BaseModel): source: str | None = None section: str | None = None page: int | None = None rerank_score: float | None = None class QueryResponse(BaseModel): query: str route: str answer: str cited_answer: str passed: bool decision: str attempts: int metrics: dict checks: dict sources: list[SourceInfo] citations: list hallucination_report: dict verification_report: dict @app.on_event("startup") def startup_event(): """ Build the index once when the server process starts, so the first real request doesn't pay for document loading + FAISS indexing + model loading. Subsequent requests reuse the cached index. """ _get_index() @app.get("/health") def health(): return {"status": "ok"} @app.post("/query", response_model=QueryResponse) def query(request: QueryRequest): try: result = answer_query(request.query) except Exception as exc: raise HTTPException(status_code=500, detail=str(exc)) return result