rag-chatbot / app /core /pipeline.py
Abeshith's picture
RAG Chatbot with LangChain, FastAPI, and service layer architecture
64d7fdf
Raw
History Blame Contribute Delete
5.7 kB
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from app.core.retriever import hybrid_retriever
from app.core.reranker import document_reranker
from app.core.generator import llm_generator
from app.core.cache import semantic_cache
from app.core.memory import conversation_memory
from app.utils.prompts import get_rag_template, get_conversation_template, get_system_prompt
from app.utils.logger import logger
from app.config import config
from typing import AsyncIterator
class RAGPipeline:
def __init__(self):
self.retriever = hybrid_retriever
self.reranker = document_reranker
self.generator = llm_generator
self.cache = semantic_cache
self.memory = conversation_memory
self.use_cache = config["rag"]["cache"]["enabled"]
self.use_reranking = config["rag"]["retrieval"]["rerank"]
self.top_k = config["rag"]["retrieval"]["top_k"]
logger.info("RAG Pipeline initialized")
def _format_context(self, documents: list) -> str:
"""Format retrieved documents into context string."""
context_parts = []
for i, doc in enumerate(documents, 1):
context_parts.append(f"[{i}] {doc.page_content}")
return "\n\n".join(context_parts)
async def _retrieve_and_rerank(self, query: str) -> list:
"""Retrieve and optionally rerank documents."""
# Retrieve documents
documents = await self.retriever.ainvoke(query)
if not documents:
logger.warning("No documents retrieved")
return []
logger.info(f"Retrieved {len(documents)} documents")
# Rerank if enabled
if self.use_reranking:
documents = self.reranker.rerank(query, documents, top_k=self.top_k)
logger.info(f"Reranked to top {len(documents)} documents")
return documents[:self.top_k]
async def generate(
self,
query: str,
session_id: str = None,
use_context: bool = True
) -> str:
"""Generate response for query with optional RAG context."""
# Check cache first
if self.use_cache:
cached_response = await self.cache.get(query, use_context)
if cached_response:
logger.info("Cache hit")
return cached_response
# Get conversation history if session provided
history = []
if session_id:
history = self.memory.get_messages(session_id)
# Retrieve and rerank documents if context needed
context = ""
if use_context:
documents = await self._retrieve_and_rerank(query)
if documents:
context = self._format_context(documents)
# Build prompt
if context:
template = get_rag_template()
prompt = template.format(context=context, question=query)
else:
template = get_conversation_template()
prompt = template.format(question=query)
# Generate response
system_prompt = get_system_prompt()
response = await self.generator.agenerate(prompt, system_prompt)
# Save to memory if session provided
if session_id:
self.memory.add_message(session_id, "user", query)
self.memory.add_message(session_id, "assistant", response)
# Cache the response
if self.use_cache:
await self.cache.set(query, response, use_context)
logger.info("Response generated successfully")
return response
async def stream(
self,
query: str,
session_id: str = None,
use_context: bool = True
) -> AsyncIterator[str]:
"""Stream response for query with optional RAG context."""
# Check cache first
if self.use_cache:
cached_response = await self.cache.get(query, use_context)
if cached_response:
logger.info("Cache hit - streaming cached response")
yield cached_response
return
# Get conversation history if session provided
history = []
if session_id:
history = self.memory.get_messages(session_id)
# Retrieve and rerank documents if context needed
context = ""
if use_context:
documents = await self._retrieve_and_rerank(query)
if documents:
context = self._format_context(documents)
# Build prompt
if context:
template = get_rag_template()
prompt = template.format(context=context, question=query)
else:
template = get_conversation_template()
prompt = template.format(question=query)
# Stream response
system_prompt = get_system_prompt()
full_response = ""
async for chunk in self.generator.stream(prompt, system_prompt):
full_response += chunk
yield chunk
# Save to memory if session provided
if session_id:
self.memory.add_message(session_id, "user", query)
self.memory.add_message(session_id, "assistant", full_response)
# Cache the full response
if self.use_cache:
await self.cache.set(query, full_response, use_context)
logger.info("Response streamed successfully")
rag_pipeline = RAGPipeline()