Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import os | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from typing import Any | |
| os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") | |
| os.environ.setdefault("OMP_NUM_THREADS", "1") | |
| os.environ.setdefault("MKL_NUM_THREADS", "1") | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.responses import HTMLResponse | |
| from pydantic import BaseModel, Field | |
| from src.real_qa.pipeline import RealQAPipeline | |
| from src.real_qa.settings import BuildConfig | |
| def resolve_project_root() -> Path: | |
| return Path(__file__).resolve().parents[2] | |
| def build_runtime_status(project_root: Path) -> dict[str, Any]: | |
| cfg = BuildConfig(project_root=project_root) | |
| manifest_path = cfg.reports_dir / "manifest.json" | |
| evaluation_path = cfg.reports_dir / "evaluation_report.json" | |
| required_artifacts = { | |
| "processed_chunks": cfg.processed_dir / "processed_chunks.parquet", | |
| "bm25_index": cfg.index_dir / "bm25_retrieval_index.pkl", | |
| "dense_faiss_index": cfg.index_dir / "dense_faiss_index.faiss", | |
| "dense_chunk_ids": cfg.index_dir / "dense_chunk_ids.json", | |
| } | |
| return { | |
| "project_root": str(project_root), | |
| "artifacts_dir": str(cfg.artifacts_dir), | |
| "manifest_exists": manifest_path.exists(), | |
| "evaluation_exists": evaluation_path.exists(), | |
| "required_artifacts": { | |
| name: {"path": str(path), "exists": path.exists()} | |
| for name, path in required_artifacts.items() | |
| }, | |
| } | |
| class AskRequest(BaseModel): | |
| question: str = Field(..., min_length=3, description="Question to answer from the QA corpus.") | |
| threshold: float = Field(0.01, ge=0.0, le=1.0, description="Confidence threshold for answer vs no_answer.") | |
| style: str = Field("auto", description="Answer style: auto, extractive, or explanatory.") | |
| class AskResponse(BaseModel): | |
| question: str | |
| question_style: str | |
| answer_type: str | |
| final_answer: str | |
| confidence: float | |
| evidence: list[dict[str, Any]] | |
| support_sentences: list[dict[str, Any]] | |
| def _read_ui_html() -> str: | |
| ui_path = Path(__file__).resolve().parent / "ui" / "index.html" | |
| return ui_path.read_text(encoding="utf-8") | |
| def get_pipeline() -> RealQAPipeline: | |
| cfg = BuildConfig(project_root=resolve_project_root()) | |
| return RealQAPipeline(cfg) | |
| def create_app() -> FastAPI: | |
| app = FastAPI( | |
| title="Real QA System API", | |
| version="1.0.0", | |
| description="API and browser interface for the technical documentation QA system.", | |
| ) | |
| async def index() -> str: | |
| return _read_ui_html() | |
| async def health() -> dict[str, str]: | |
| return {"status": "ok"} | |
| async def status() -> dict[str, Any]: | |
| return build_runtime_status(resolve_project_root()) | |
| async def ask(payload: AskRequest) -> AskResponse: | |
| style = payload.style if payload.style in {"auto", "extractive", "explanatory"} else "auto" | |
| try: | |
| result = get_pipeline().answer( | |
| payload.question, | |
| threshold=float(payload.threshold), | |
| style=style, | |
| ) | |
| except FileNotFoundError as exc: | |
| raise HTTPException( | |
| status_code=503, | |
| detail=( | |
| "QA artifacts are missing. Run the build step before starting the API. " | |
| f"Details: {exc}" | |
| ), | |
| ) from exc | |
| except Exception as exc: | |
| raise HTTPException(status_code=500, detail=f"QA inference failed: {exc}") from exc | |
| return AskResponse(**result) | |
| return app | |
| app = create_app() | |