Pranil Bankar
Deploy Application
4d67ec7
Raw History Blame Contribute Delete
6.11 kB
"""
qa.py β€” Question Answering API router
Endpoints:
POST /qa β†’ Full RAG pipeline (classify + retrieve + answer + verify)
POST /classify β†’ Classification only (BERT chapter + topic extraction)
"""
import traceback
from fastapi import APIRouter, HTTPException
from Backend.schemas import (
QuestionRequest,
QAResponse,
ClassifyOnlyRequest,
ClassifyOnlyResponse,
ClassificationResult,
TopicInfo,
SourceChunk,
IsNeetBioRequest,
IsNeetBioResponse
)
from rag.pipeline import run_pipeline
from Classification.classify_and_extract import classify_question, get_predictor
router = APIRouter(prefix="/api", tags=["Question Answering"])
# ==============================
# POST /api/qa
# ==============================
@router.post(
"/qa",
response_model=QAResponse,
summary="Answer a NEET Biology question",
description=(
"Runs the full pipeline: BERT classifies the chapter, "
"retrieves relevant NCERT chunks, generates an answer with "
"justification, and verifies correctness."
),
)
async def answer_question(req: QuestionRequest):
"""
Full Q&A pipeline:
1. BERT β†’ chapter prediction + topic extraction
2. Supabase β†’ retrieve NCERT chunks (filtered by chapter)
3. Groq LLM β†’ generate answer + justification
4. Groq LLM β†’ verify answer
5. Compute confidence score
"""
try:
result = run_pipeline(
question=req.question,
chapter_filter=req.chapter_filter,
top_k=req.top_k,
use_classifier=req.use_classifier,
)
# Build classification model if present
classification = None
if result.get("classification"):
raw = result["classification"]
classification = ClassificationResult(
chapter=raw["chapter"],
chapter_id=raw["chapter_id"],
chapter_confidence=raw["chapter_confidence"],
rejected=raw.get("rejected", False),
rejection_reason=raw.get("rejection_reason", ""),
entropy=raw.get("entropy", 0.0),
margin=raw.get("margin", 0.0),
topics=[
TopicInfo(
section_code=t["section_code"],
section_title=t["section_title"],
similarity=t["similarity"],
)
for t in raw.get("topics", [])
],
)
# Build sources
sources = [
SourceChunk(
chapter=s.get("chapter"),
section=s.get("section"),
section_title=s.get("section_title"),
text_content=s.get("text_content"),
similarity=s.get("similarity", 0.0),
)
for s in result.get("sources", [])
]
return QAResponse(
question=result["question"],
classification=classification,
answer=result["answer"],
justification=result.get("justification", []),
sources=sources,
confidence=result.get("confidence", 0.0),
verified=result.get("verified", False),
verifier_note=result.get("verifier_note", ""),
)
except Exception as e:
traceback.print_exc()
raise HTTPException(
status_code=500,
detail=f"Pipeline error: {str(e)}",
)
# ==============================
# POST /api/classify
# ==============================
@router.post(
"/classify",
response_model=ClassifyOnlyResponse,
summary="Classify a question (chapter + topics only)",
description=(
"Runs only the BERT classifier + topic extraction. "
"Does NOT generate an answer. Useful for quick categorization."
),
)
async def classify_only(req: ClassifyOnlyRequest):
"""
Classification only (no RAG):
1. BERT β†’ chapter prediction
2. Supabase retrieval β†’ topic extraction
"""
try:
raw = classify_question(req.question)
classification = ClassificationResult(
chapter=raw["chapter"],
chapter_id=raw["chapter_id"],
chapter_confidence=raw["chapter_confidence"],
rejected=raw.get("rejected", False),
rejection_reason=raw.get("rejection_reason", ""),
entropy=raw.get("entropy", 0.0),
margin=raw.get("margin", 0.0),
topics=[
TopicInfo(
section_code=t["section_code"],
section_title=t["section_title"],
similarity=t["similarity"],
)
for t in raw.get("topics", [])
],
)
return ClassifyOnlyResponse(
question=req.question,
classification=classification,
)
except Exception as e:
traceback.print_exc()
raise HTTPException(
status_code=500,
detail=f"Classification error: {str(e)}",
)
# ==============================
# POST /api/is-neet-bio
# ==============================
@router.post(
"/is-neet-bio",
response_model=IsNeetBioResponse,
summary="Quickly check if a string is a valid NEET Biology question",
)
async def is_neet_bio(req: IsNeetBioRequest):
"""
Runs only the BERT classifier prediction to check OOD (Out-Of-Domain) logic.
Fast path validation for frontend to prevent garbage from triggering the RAG pipeline.
"""
try:
predictor = get_predictor()
bert_result = predictor.predict(req.question)
rejected = bert_result.get("rejected", False)
reason = bert_result.get("rejection_reason", "")
return IsNeetBioResponse(
is_valid=not rejected,
rejected=rejected,
reason=reason
)
except Exception as e:
traceback.print_exc()
raise HTTPException(
status_code=500,
detail=f"Validation error: {str(e)}",
)