CoolFace
Apppublic

Vizz17/context-aware-rag

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
chat.py158 linesDownload Raw Back to api
1"""Chat endpoint — query the RAG pipeline."""2 3from __future__ import annotations4 5import logging6import time7import uuid8 9from fastapi import APIRouter, HTTPException, Query, Depends10 11from app.core.config import settings12from app.api.auth import get_current_user13from app.models.schemas import ChatRequest, ChatResponse, SourceCitation, ChatHistoryListResponse, ChatSessionResponse14from app.services.llm_chain import generate_answer15from app.services.reranker import rerank16from app.services.retriever import retrieve17from app.services.vector_store import get_vector_store18 19logger = logging.getLogger(__name__)20router = APIRouter()21 22 23@router.post("/chat", response_model=ChatResponse)24def chat(req: ChatRequest, user_id: str = Depends(get_current_user)):25    """Process a user query through the full RAG pipeline."""26    start = time.perf_counter()27 28    try:29        # 1. Retrieve candidates30        t0 = time.perf_counter()31        retrieval_query = req.query32        if req.history and len(req.query.split()) < 20:33            last_user_msgs = [m.get("content", "") for m in req.history if m.get("role") == "user"]34            if last_user_msgs:35                retrieval_query = f"{last_user_msgs[-1]} {req.query}"36                logger.info(f"Expanded retrieval query: '{retrieval_query}'")37                38        candidates = retrieve(39            query=retrieval_query,40            filters=req.filters,41            user_id=user_id,42        )43        logger.info("Retrieve: %.1fs (%d candidates)", time.perf_counter() - t0, len(candidates))44 45        # 2. Rerank46        t1 = time.perf_counter()47        top_chunks = rerank(query=retrieval_query, candidates=candidates)48        logger.info("Rerank: %.1fs (%d chunks)", time.perf_counter() - t1, len(top_chunks))49 50        # 3. Generate grounded answer51        t2 = time.perf_counter()52        answer = generate_answer(query=req.query, chunks=top_chunks, history=req.history)53        logger.info("LLM generate: %.1fs", time.perf_counter() - t2)54 55        # 4. Build source citations56        sources = [57            SourceCitation(58                document=c.get("metadata", {}).get("filename", "unknown"),59                page=c.get("metadata", {}).get("page_num", 0),60                chunk_index=c.get("metadata", {}).get("chunk_index", 0),61                text=c["text"][:300],  # truncate for response62                score=round(c.get("rerank_score", c.get("score", 0.0)), 4),63            )64            for c in top_chunks65        ]66        67        # If the LLM explicitly refused to answer due to lack of context, hide the citations68        if "I don't see the answer" in answer or "I don't have enough information" in answer:69            sources = []70 71        elapsed = (time.perf_counter() - start) * 100072        session_id = req.session_id or str(uuid.uuid4())73        model_name = f"{settings.llm_provider}/{settings.llm_model}"74 75        # 5. Save chat history to vector store76        title = req.query[:50]77        if req.history and len(req.history) > 0:78            title = req.history[0].get("content", req.query)[:50]79            80        updated_history = list(req.history) if req.history else []81        updated_history.append({"role": "user", "content": req.query})82        updated_history.append({83            "role": "assistant",84            "content": answer,85            "sources": [s.model_dump() for s in sources],86            "latency": round(elapsed, 1),87            "model": model_name88        })89        90        doc_scope_id = None91        if req.filters and isinstance(req.filters, dict):92            doc_scope_id = req.filters.get("doc_id")93 94        try:95            store = get_vector_store()96            store.save_chat(session_id, title, updated_history, user_id=user_id, doc_id=doc_scope_id)97        except Exception as e:98            logger.error(f"Failed to save chat history: {e}")99 100        return ChatResponse(101            answer=answer,102            sources=sources,103            session_id=session_id,104            model=model_name,105            latency_ms=round(elapsed, 1),106            retrieval_mode="optimized" if settings.enable_optimizer else "top_k",107        )108 109    except Exception as e:110        error_str = str(e)111        if "429" in error_str or "RESOURCE_EXHAUSTED" in error_str:112            raise HTTPException(113                status_code=429,114                detail=(115                    "Rate limit reached. Please wait 1-2 minutes and try again."116                ),117            )118        119        logger.error(f"Chat error: {e}", exc_info=True)120        raise HTTPException(status_code=500, detail=str(e))121 122@router.get("/chats", response_model=ChatHistoryListResponse)123def list_chats(124    doc_id: str | None = Query(default=None, description="Filter chat sessions by selected document scope"),125    user_id: str = Depends(get_current_user)126):127    """Return all past chat sessions."""128    try:129        store = get_vector_store()130        sessions = store.list_chats(user_id=user_id, doc_id=doc_id)131        return ChatHistoryListResponse(sessions=sessions)132    except Exception as e:133        raise HTTPException(status_code=500, detail=f"Failed to fetch chats: {e}")134 135@router.get("/chats/{session_id}", response_model=ChatSessionResponse)136def get_chat(session_id: str, user_id: str = Depends(get_current_user)):137    """Retrieve a specific chat history."""138    try:139        store = get_vector_store()140        chat = store.get_chat(session_id, user_id=user_id)141        if not chat:142            raise HTTPException(status_code=404, detail="Chat session not found")143        return ChatSessionResponse(**chat)144    except HTTPException:145        raise146    except Exception as e:147        raise HTTPException(status_code=500, detail=f"Failed to fetch chat: {e}")148 149@router.delete("/chats/{session_id}")150def delete_chat(session_id: str, user_id: str = Depends(get_current_user)):151    """Delete a specific chat history."""152    try:153        store = get_vector_store()154        store.delete_chat(session_id, user_id=user_id)155        return {"status": "deleted"}156    except Exception as e:157        raise HTTPException(status_code=500, detail=f"Failed to delete chat: {e}")158