"""Case-specific deterministic QA routes. These endpoints expose validated reproduction-case packages before they are merged into the general RAG chat route. They do not call an LLM. """ from functools import lru_cache from pathlib import Path from typing import Any from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field from src.rag.unified.case_qa import CasePackageQAEngine PROJECT_ROOT = Path(__file__).resolve().parents[1] FT_SPL_CASE_PACKAGE = PROJECT_ROOT / "data" / "ft_spl_reproduction_case_package.json" router = APIRouter() class CaseQARequest(BaseModel): query: str = Field(..., min_length=1) class CaseQAResponse(BaseModel): query: str intent: str answer: str citations: list[str] facts: dict[str, Any] class CaseStatusResponse(BaseModel): case_id: str package_path: str package_exists: bool supported: bool @lru_cache(maxsize=1) def get_ft_spl_engine() -> CasePackageQAEngine: if not FT_SPL_CASE_PACKAGE.exists(): raise FileNotFoundError(str(FT_SPL_CASE_PACKAGE)) return CasePackageQAEngine(FT_SPL_CASE_PACKAGE) @router.get("/ft-spl/status", response_model=CaseStatusResponse) async def ft_spl_case_status(): return CaseStatusResponse( case_id="ft_spl_reproduction_job_1777615271527", package_path=str(FT_SPL_CASE_PACKAGE), package_exists=FT_SPL_CASE_PACKAGE.exists(), supported=FT_SPL_CASE_PACKAGE.exists(), ) @router.post("/ft-spl/qa", response_model=CaseQAResponse) async def ft_spl_case_qa(request: CaseQARequest): try: response = get_ft_spl_engine().answer(request.query) except FileNotFoundError as exc: raise HTTPException(status_code=503, detail=f"Case package missing: {exc}") from exc return CaseQAResponse( query=response.query, intent=response.intent, answer=response.answer, citations=response.citations, facts=response.facts, )