File size: 2,243 Bytes
f565efa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
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