File size: 4,676 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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """
schemas.py — Pydantic models for FastAPI request/response validation
Defines the complete API contract for the NEET Biology Q&A system.
"""
from pydantic import BaseModel, Field
from typing import Optional
# ==============================
# REQUEST MODELS
# ==============================
class QuestionRequest(BaseModel):
"""Request body for the /qa endpoint."""
question: str = Field(
...,
min_length=5,
max_length=500,
description="The NEET Biology question to answer.",
examples=["What is the role of cofactors in enzyme activity?"],
)
chapter_filter: Optional[str] = Field(
default=None,
description="Optional: manually specify a chapter to filter retrieval. "
"If omitted, BERT classifier auto-detects the chapter.",
examples=["Biomolecules"],
)
top_k: int = Field(
default=5,
ge=1,
le=20,
description="Number of NCERT chunks to retrieve for context.",
)
use_classifier: bool = Field(
default=True,
description="Whether to use the BERT classifier for auto chapter detection.",
)
# ==============================
# RESPONSE MODELS
# ==============================
class TopicInfo(BaseModel):
"""A predicted topic/subtopic from the NCERT syllabus."""
section_code: str = Field(..., description="Section code (e.g., '9.8')")
section_title: str = Field(..., description="Section title (e.g., 'Enzymes')")
similarity: float = Field(..., description="Cosine similarity score (0-1)")
class ClassificationResult(BaseModel):
"""BERT classification output for the question."""
chapter: str = Field(..., description="Predicted NCERT chapter name")
chapter_id: int = Field(..., description="Chapter ID (0-30)")
chapter_confidence: float = Field(..., description="BERT confidence score (0-1)")
topics: list[TopicInfo] = Field(
default=[],
description="Top 1-3 relevant topics/subtopics within the chapter",
)
rejected: bool = Field(default=False, description="True if rejected by OOD gating")
rejection_reason: str = Field(default="", description="Reason for rejection")
entropy: float = Field(default=0.0, description="Normalized entropy of prediction")
margin: float = Field(default=0.0, description="Confidence margin between top-1 and top-2")
class IsNeetBioRequest(BaseModel):
"""Request body for the /is-neet-bio validation endpoint."""
question: str = Field(
...,
min_length=3,
max_length=500,
description="The question to validate."
)
class IsNeetBioResponse(BaseModel):
"""Response indicating whether the query is valid NEET biology."""
is_valid: bool
rejected: bool
reason: str
class SourceChunk(BaseModel):
"""A retrieved NCERT text chunk used as context."""
chapter: Optional[str] = None
section: Optional[str] = None
section_title: Optional[str] = None
text_content: Optional[str] = None
similarity: float = 0.0
class QAResponse(BaseModel):
"""Full response from the Q&A pipeline."""
question: str = Field(..., description="The original question")
classification: Optional[ClassificationResult] = Field(
default=None,
description="BERT classification result (chapter + topics)",
)
answer: str = Field(..., description="Generated answer from NCERT context")
justification: list[str] = Field(
default=[],
description="Step-by-step justification of the answer",
)
sources: list[SourceChunk] = Field(
default=[],
description="Retrieved NCERT chunks used as evidence",
)
confidence: float = Field(
...,
ge=0.0,
le=1.0,
description="Overall confidence score (0-1)",
)
verified: bool = Field(..., description="Whether the answer passed verification")
verifier_note: str = Field(
default="",
description="Explanation from the verifier",
)
class ClassifyOnlyRequest(BaseModel):
"""Request body for the /classify endpoint (no RAG, just classification)."""
question: str = Field(
...,
min_length=5,
max_length=500,
description="The NEET Biology question to classify.",
examples=["What is the role of mitochondria in ATP synthesis?"],
)
class ClassifyOnlyResponse(BaseModel):
"""Response from the /classify endpoint."""
question: str
classification: ClassificationResult
class HealthResponse(BaseModel):
"""Health check response."""
status: str = "ok"
version: str = "1.0.0"
components: dict = {}
|