78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""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)}")
|