File size: 3,390 Bytes
4d67ec7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""
main.py β€” FastAPI application entry point for the NEET Biology Q&A system

Run:
    uvicorn Backend.main:app --reload --port 8000

Docs:
    http://localhost:8000/docs    (Swagger UI)
    http://localhost:8000/redoc   (ReDoc)
"""

import time
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from Backend.schemas import HealthResponse
from Backend.routers.qa import router as qa_router


# ==============================
# STARTUP / SHUTDOWN
# ==============================
@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    Pre-load heavy models at startup so the first request is fast.
    """
    print("\nπŸš€ Starting NEET Biology Q&A Backend...")
    start = time.time()

    # Pre-load BERT classifier (singleton)
    from Classification.classify_and_extract import get_predictor
    get_predictor()

    # Pre-load embedding model (singleton)
    from rag.retriever import get_embedding_model
    get_embedding_model()

    elapsed = time.time() - start
    print(f"βœ… All models loaded in {elapsed:.1f}s β€” server ready!\n")

    yield  # App runs here

    print("\nπŸ›‘ Shutting down NEET Biology Q&A Backend.")


# ==============================
# APP INSTANCE
# ==============================
app = FastAPI(
    title="NEET Biology Q&A API",
    description=(
        "AI-powered Question Answering system for NEET Biology. "
        "Uses BERT classification for chapter detection and a "
        "RAG pipeline with NCERT textbook context for accurate answers."
    ),
    version="1.0.0",
    lifespan=lifespan,
)


# ==============================
# CORS β€” Allow frontend access
# ==============================
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:3000",     # React/Next.js dev
        "http://localhost:5173",     # Vite dev
        "http://localhost:5500",     # Live Server
        "http://127.0.0.1:5500",
        "*",                         # Allow all for development
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ==============================
# ROUTES
# ==============================
app.include_router(qa_router)


@app.get(
    "/health",
    response_model=HealthResponse,
    tags=["System"],
    summary="Health check",
)
async def health_check():
    """Check if the server and all components are running."""
    from Classification.classify_and_extract import _predictor
    from rag.retriever import _embedding_model, _supabase_client

    return HealthResponse(
        status="ok",
        version="1.0.0",
        components={
            "bert_classifier": "loaded" if _predictor is not None else "not loaded",
            "embedding_model": "loaded" if _embedding_model is not None else "not loaded",
            "supabase": "connected" if _supabase_client is not None else "not connected",
        },
    )


@app.get("/", tags=["System"])
async def root():
    """API root β€” welcome message."""
    return {
        "message": "NEET Biology Q&A API",
        "version": "1.0.0",
        "docs": "/docs",
        "endpoints": {
            "POST /api/qa": "Full Q&A pipeline (classify + answer + verify)",
            "POST /api/classify": "Chapter classification + topic extraction only",
            "GET /health": "Health check",
        },
    }