Spaces:
Sleeping
Sleeping
| """FastAPI service exposing the Python Q&A RAG pipeline.""" | |
| import logging | |
| import time | |
| from collections import OrderedDict | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.responses import RedirectResponse | |
| from groq import GroqError | |
| from app import config | |
| from app.rag import RAGPipeline, Retriever | |
| from app.schemas import AskRequest, AskResponse, HealthResponse | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # question -> response dict, evicted FIFO once full. | |
| _answer_cache: OrderedDict[str, dict] = OrderedDict() | |
| async def lifespan(app: FastAPI): | |
| retriever = Retriever() | |
| app.state.pipeline = RAGPipeline(retriever) | |
| logger.info("Index loaded: %d documents", retriever.count()) | |
| yield | |
| app = FastAPI( | |
| title="Python Programming Q&A Assistant", | |
| description=( | |
| "RAG-powered Q&A over the Stack Overflow Python dataset " | |
| "(Kaggle: stackoverflow/pythonquestions), answered by Llama on Groq." | |
| ), | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| ) | |
| async def root(): | |
| return RedirectResponse(url="/docs") | |
| async def health(): | |
| return HealthResponse( | |
| status="ok", | |
| index_size=app.state.pipeline.retriever.count(), | |
| model=config.GROQ_MODEL, | |
| ) | |
| async def ask(req: AskRequest): | |
| cache_key = f"{req.question.strip().lower()}|{req.top_k}" | |
| if cache_key in _answer_cache: | |
| cached = _answer_cache[cache_key] | |
| return AskResponse(**{**cached, "cached": True, "latency_ms": 0}) | |
| start = time.perf_counter() | |
| try: | |
| result = await app.state.pipeline.ask(req.question, top_k=req.top_k) | |
| except GroqError as e: | |
| logger.exception("LLM call failed") | |
| raise HTTPException(status_code=502, detail=f"LLM provider error: {e}") from e | |
| result["latency_ms"] = int((time.perf_counter() - start) * 1000) | |
| _answer_cache[cache_key] = result | |
| if len(_answer_cache) > config.ANSWER_CACHE_SIZE: | |
| _answer_cache.popitem(last=False) | |
| return AskResponse(**result) | |