rag-python-rag / main.py
viktor-hirenko
feat: migrate from Ollama to Hugging Face Inference API
5fd4bb2
Raw
History Blame Contribute Delete
6.74 kB
"""
Main RAG Application
Combines all components and provides a Gradio web interface
"""
import os
import gradio as gr
import logging
from typing import Generator
from config import GRADIO_CONFIG, DEFAULT_N_RESULTS, HF_TOKEN
from document_converter import download_test_document, convert_all_documents
from text_splitter import process_all_documents
from vector_store import VectorStore, retrieve_context
from llm_handler import stream_llm_answer, format_response
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class RAGSystem:
"""
Complete RAG (Retrieval-Augmented Generation) System
"""
def __init__(self):
"""
Initialize the RAG system
"""
self.vector_store = None
logger.info("RAG System initialized")
def setup_pipeline(self, force_rebuild: bool = False) -> bool:
"""
Set up the complete RAG pipeline
Args:
force_rebuild: Whether to force rebuild the vector store
Returns:
True if setup successful
"""
try:
# Step 1: Download test document
logger.info("Step 1: Downloading test document...")
test_doc = download_test_document()
if not test_doc:
logger.error("Failed to download test document")
return False
# Step 2: Convert documents to markdown
logger.info("Step 2: Converting documents to markdown...")
converted = convert_all_documents()
if not converted:
logger.error("No documents were converted")
return False
logger.info(f"Converted {len(converted)} documents")
# Step 3: Split documents into chunks
logger.info("Step 3: Splitting documents into chunks...")
chunks = process_all_documents()
if not chunks:
logger.error("No chunks were created")
return False
logger.info(f"Created {len(chunks)} chunks")
# Step 4: Initialize vector store
logger.info("Step 4: Initializing vector store...")
self.vector_store = VectorStore()
# Check if vector store is empty or force rebuild
stats = self.vector_store.get_collection_stats()
if stats["document_count"] == 0 or force_rebuild:
if force_rebuild and stats["document_count"] > 0:
logger.info("Clearing existing vector store...")
self.vector_store.clear_collection()
logger.info("Adding documents to vector store...")
self.vector_store.add_documents(chunks)
else:
logger.info(f"Vector store already contains {stats['document_count']} documents")
logger.info("✅ RAG pipeline setup complete!")
return True
except Exception as e:
logger.error(f"Error setting up pipeline: {e}")
return False
def query(
self,
question: str,
n_results: int = DEFAULT_N_RESULTS
) -> Generator[str, None, None]:
"""
Process a query and stream the response
Args:
question: User's question
n_results: Number of context chunks to retrieve
Yields:
Response text parts
"""
if not question.strip():
yield "❌ Please enter a question."
return
try:
# Retrieve context
logger.info(f"Processing query: {question}")
context, sources = retrieve_context(question, n_results)
if not context:
yield "❌ No relevant information found in the documents."
return
# Start response
response_start = f"**Question:** {question}\n\n**Answer:** "
answer = ""
# Stream the answer
for token in stream_llm_answer(question, context):
answer += token
yield response_start + answer
# Add sources at the end
final_response = format_response(question, answer, sources)
yield final_response
except Exception as e:
logger.error(f"Error processing query: {e}")
yield f"❌ Error: {str(e)}"
# Global RAG system instance
rag_system = RAGSystem()
def rag_interface(question: str) -> Generator[str, None, None]:
"""
Gradio interface function
Args:
question: User's question
Yields:
Response text parts
"""
yield from rag_system.query(question)
def create_gradio_interface() -> gr.Interface:
"""
Create the Gradio web interface
Returns:
Gradio Interface object
"""
interface = gr.Interface(
fn=rag_interface,
inputs=gr.Textbox(
label="Your Question",
placeholder="Ask anything about Python programming...",
lines=3
),
outputs=gr.Markdown(label="Answer"),
title=GRADIO_CONFIG["title"],
description=GRADIO_CONFIG["description"],
examples=GRADIO_CONFIG["examples"],
theme=GRADIO_CONFIG["theme"],
allow_flagging="never"
)
return interface
def main():
"""
Main entry point for the application
"""
logger.info("🚀 Starting RAG System...")
# Validate HF token
if not HF_TOKEN:
logger.warning(
"⚠️ HF_TOKEN not set. Please set the HF_TOKEN environment variable.\n"
"Get your token from: https://huggingface.co/settings/tokens"
)
logger.info("Continuing without HF token validation (may fail during LLM calls)...")
else:
logger.info("✅ HF_TOKEN found")
# Setup the pipeline
logger.info("Setting up RAG pipeline...")
success = rag_system.setup_pipeline(force_rebuild=False)
if not success:
logger.error("Failed to setup RAG pipeline. Please check the logs.")
return
# Create and launch Gradio interface
logger.info("Creating Gradio interface...")
interface = create_gradio_interface()
logger.info("Launching web interface...")
interface.queue().launch(
share=GRADIO_CONFIG["share"],
server_name="0.0.0.0",
server_port=7860,
)
if __name__ == "__main__":
main()