"""Query router for RAG pipeline.""" from fastapi import APIRouter, HTTPException from app.core.config import get_settings from app.models.ingest import QueryRequest, QueryResponse, SourceMetadata from app.services.llm_client import LLMClient from app.services.query_decomposer import QueryDecomposer from app.services.relevance_filter import RelevanceFilter from app.services.rag import RAGService router = APIRouter(tags=["query"]) @router.post("/query", response_model=QueryResponse) async def query(request: QueryRequest): """Execute the 3-step RAG query pipeline. Pipeline: 1. QueryDecomposer: Extract keywords from question 2. RAGService.retrieve: Get relevant chunks from ChromaDB 3. RelevanceFilter: Score and filter chunks by relevance 4. RAGService.generate_response: Generate bullet-point answer """ settings = get_settings() if not request.question or not request.question.strip(): raise HTTPException(status_code=400, detail="Question is required") try: llm_client = LLMClient(settings) decomposer = QueryDecomposer(llm_client) keywords = decomposer.decompose(request.question) rag = RAGService(llm_client=llm_client) chunks = rag.retrieve(keywords, n_results=10) if not chunks: return QueryResponse( keywords=keywords, answer="I could not find any relevant information to answer your question.", sources=[], ) relevance_filter = RelevanceFilter(llm_client) filtered = relevance_filter.filter(request.question, chunks, threshold=7.0) if not filtered: return QueryResponse( keywords=keywords, answer="I could not find any relevant information to answer your question.", sources=[], ) chunk_texts = [chunk for chunk, _meta in filtered] chunk_metadata = [meta for _chunk, meta in filtered] answer = rag.generate_response(request.question, chunk_texts, chunk_metadata) sources = [] for meta in chunk_metadata: sources.append( SourceMetadata( filename=meta.get("filename", "unknown"), upload_date=meta.get("upload_date", ""), content_summary=meta.get("content_summary", ""), chunk_index=meta.get("chunk_index", 0), ) ) return QueryResponse( keywords=keywords, answer=answer, sources=sources, ) except Exception as e: raise HTTPException(status_code=500, detail=f"Query failed: {str(e)}")