Spaces:
Build error
Build error
| """ | |
| POST /api/ask — streaming SSE cited answer (with cache + trace) | |
| POST /api/quiz — generate MCQs | |
| POST /api/exam — generate exam questions | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from typing import Optional | |
| from fastapi import APIRouter, Request | |
| from fastapi.responses import StreamingResponse | |
| from pydantic import BaseModel | |
| from app.generation.answer import generate_answer, stream_answer | |
| from app.generation.quiz import generate_mcqs, generate_short_answers | |
| from app.generation.exam import generate_exam_questions | |
| from app.retrieval.retriever import retrieve | |
| from app.middleware.tracing import Trace, traced_stage | |
| router = APIRouter() | |
| class AskRequest(BaseModel): | |
| query: str | |
| workspace_id: str = "uofg-msds-demo" | |
| module: Optional[str] = None | |
| week: Optional[int] = None | |
| doc_type: Optional[str] = None | |
| stream: bool = False | |
| class QuizRequest(BaseModel): | |
| topic: str | |
| workspace_id: str = "uofg-msds-demo" | |
| module: Optional[str] = None | |
| week: Optional[int] = None | |
| n: int = 5 | |
| quiz_type: str = "mcq" | |
| class ExamRequest(BaseModel): | |
| topic: str | |
| workspace_id: str = "uofg-msds-demo" | |
| module: Optional[str] = None | |
| difficulty: str = "medium" | |
| n: int = 3 | |
| async def ask(req: AskRequest): | |
| trace = Trace(query=req.query, workspace_id=req.workspace_id) | |
| # --- Semantic cache lookup --- | |
| try: | |
| from app.middleware.cache import lookup, store | |
| with traced_stage(trace, "cache_lookup"): | |
| cached = lookup(req.query, req.workspace_id) | |
| if cached: | |
| trace.emit(answer_length=len(cached.get("answer", "")), cached=True) | |
| return cached | |
| except Exception: | |
| cached = None | |
| filters: dict = {} | |
| if req.module: | |
| filters["module"] = req.module | |
| if req.week: | |
| filters["week"] = req.week | |
| if req.doc_type: | |
| filters["doc_type"] = req.doc_type | |
| # --- Retrieval --- | |
| with traced_stage(trace, "retrieval", filters=filters): | |
| chunks = retrieve(req.query, req.workspace_id, filters=filters or None) | |
| chunk_ids = [c.get("chunk_id", "") for c in chunks] | |
| if req.stream: | |
| async def event_generator(): | |
| async for token_json in stream_answer(req.query, chunks): | |
| yield f"data: {token_json}\n\n" | |
| trace.end_stage("generation", chunk_ids=chunk_ids) | |
| trace.emit() | |
| trace.start_stage("generation") | |
| return StreamingResponse(event_generator(), media_type="text/event-stream") | |
| # --- Generation --- | |
| with traced_stage(trace, "generation", chunk_ids=chunk_ids): | |
| result = generate_answer(req.query, chunks) | |
| # Cache successful answers | |
| try: | |
| if result.get("answer") and "unavailable" not in result["answer"].lower(): | |
| store(req.query, req.workspace_id, result) | |
| except Exception: | |
| pass | |
| trace.emit(answer_length=len(result.get("answer", ""))) | |
| return result | |
| def quiz(req: QuizRequest): | |
| filters: dict = {} | |
| if req.module: | |
| filters["module"] = req.module | |
| if req.week: | |
| filters["week"] = req.week | |
| chunks = retrieve( | |
| req.topic, req.workspace_id, | |
| filters=filters or None, | |
| rerank_top_k=8, | |
| ) | |
| if req.quiz_type == "short_answer": | |
| questions = generate_short_answers(chunks, n=req.n) | |
| else: | |
| questions = generate_mcqs(chunks, n=req.n) | |
| return {"questions": questions, "source_chunks": len(chunks)} | |
| def exam(req: ExamRequest): | |
| questions = generate_exam_questions( | |
| topic=req.topic, | |
| workspace_id=req.workspace_id, | |
| module=req.module, | |
| difficulty=req.difficulty, | |
| n=req.n, | |
| ) | |
| return {"questions": questions} | |