Spaces:
Sleeping
Sleeping
Arif
commited on
Commit
·
704b133
1
Parent(s):
92ab414
Demo data added for query test
Browse files- app/core/llm.py +35 -41
- app/core/vector_store.py +24 -17
- app/models/schemas.py +0 -4
- app/services/document_processor.py +0 -1
- data/documents/ai_basics.txt +1 -0
- data/documents/ml_concepts.txt +11 -0
- data/documents/rag_explanation.txt +16 -0
- main.py +141 -3
- pyproject.toml +1 -0
- scripts/demo_queries.py +56 -0
- uv.lock +43 -31
app/core/llm.py
CHANGED
|
@@ -1,22 +1,15 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
except ImportError:
|
| 5 |
-
from langchain_core.prompts import PromptTemplate
|
| 6 |
-
|
| 7 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 8 |
|
| 9 |
class OllamaLLM:
|
| 10 |
def __init__(self, base_url: str, model: str):
|
| 11 |
-
self.
|
| 12 |
-
|
| 13 |
-
model=model,
|
| 14 |
-
temperature=0.2, # Lower for more factual responses
|
| 15 |
-
)
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
|
| 21 |
Context:
|
| 22 |
{context}
|
|
@@ -29,30 +22,31 @@ Instructions:
|
|
| 29 |
- Keep your answer clear and concise (max 3-5 sentences)
|
| 30 |
- Cite specific parts of the context when relevant
|
| 31 |
|
| 32 |
-
Answer:"""
|
| 33 |
-
input_variables=["context", "question"]
|
| 34 |
-
)
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
from typing import Dict
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
class OllamaLLM:
|
| 6 |
def __init__(self, base_url: str, model: str):
|
| 7 |
+
self.base_url = base_url.rstrip('/')
|
| 8 |
+
self.model = model
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
def generate(self, question: str, context: str) -> str:
|
| 11 |
+
"""Generate answer using RAG context"""
|
| 12 |
+
prompt = f"""You are a helpful AI assistant. Use the following context to answer the question accurately and concisely.
|
| 13 |
|
| 14 |
Context:
|
| 15 |
{context}
|
|
|
|
| 22 |
- Keep your answer clear and concise (max 3-5 sentences)
|
| 23 |
- Cite specific parts of the context when relevant
|
| 24 |
|
| 25 |
+
Answer:"""
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
try:
|
| 28 |
+
response = requests.post(
|
| 29 |
+
f"{self.base_url}/api/generate",
|
| 30 |
+
json={
|
| 31 |
+
"model": self.model,
|
| 32 |
+
"prompt": prompt,
|
| 33 |
+
"stream": False,
|
| 34 |
+
"options": {
|
| 35 |
+
"temperature": 0.2,
|
| 36 |
+
"top_p": 0.9,
|
| 37 |
+
"top_k": 40
|
| 38 |
+
}
|
| 39 |
+
},
|
| 40 |
+
timeout=60
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
if response.status_code == 200:
|
| 44 |
+
result = response.json()
|
| 45 |
+
return result.get("response", "Error generating response")
|
| 46 |
+
else:
|
| 47 |
+
return f"Error: Ollama returned status {response.status_code}"
|
| 48 |
+
|
| 49 |
+
except requests.exceptions.RequestException as e:
|
| 50 |
+
return f"Error connecting to Ollama: {str(e)}"
|
| 51 |
+
except Exception as e:
|
| 52 |
+
return f"Unexpected error: {str(e)}"
|
app/core/vector_store.py
CHANGED
|
@@ -12,17 +12,20 @@ class VectorStore:
|
|
| 12 |
|
| 13 |
def _ensure_collection(self):
|
| 14 |
"""Create collection if it doesn't exist"""
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
self.
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
)
|
| 25 |
-
|
|
|
|
| 26 |
|
| 27 |
def add_documents(self, texts: List[str], embeddings: List[List[float]],
|
| 28 |
metadata: List[Dict] = None):
|
|
@@ -50,10 +53,14 @@ class VectorStore:
|
|
| 50 |
def search(self, query_embedding: List[float], limit: int = 5,
|
| 51 |
score_threshold: float = 0.7):
|
| 52 |
"""Search for similar documents"""
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
def _ensure_collection(self):
|
| 14 |
"""Create collection if it doesn't exist"""
|
| 15 |
+
try:
|
| 16 |
+
collections = self.client.get_collections().collections
|
| 17 |
+
collection_names = [col.name for col in collections]
|
| 18 |
+
|
| 19 |
+
if self.collection_name not in collection_names:
|
| 20 |
+
self.client.create_collection(
|
| 21 |
+
collection_name=self.collection_name,
|
| 22 |
+
vectors_config=VectorParams(
|
| 23 |
+
size=self.vector_size,
|
| 24 |
+
distance=Distance.COSINE
|
| 25 |
+
)
|
| 26 |
)
|
| 27 |
+
except Exception as e:
|
| 28 |
+
print(f"Warning: Could not connect to Qdrant: {e}")
|
| 29 |
|
| 30 |
def add_documents(self, texts: List[str], embeddings: List[List[float]],
|
| 31 |
metadata: List[Dict] = None):
|
|
|
|
| 53 |
def search(self, query_embedding: List[float], limit: int = 5,
|
| 54 |
score_threshold: float = 0.7):
|
| 55 |
"""Search for similar documents"""
|
| 56 |
+
try:
|
| 57 |
+
results = self.client.search(
|
| 58 |
+
collection_name=self.collection_name,
|
| 59 |
+
query_vector=query_embedding,
|
| 60 |
+
limit=limit,
|
| 61 |
+
score_threshold=score_threshold
|
| 62 |
+
)
|
| 63 |
+
return results
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f"Search error: {e}")
|
| 66 |
+
return []
|
app/models/schemas.py
CHANGED
|
@@ -1,10 +1,6 @@
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
from typing import List, Optional
|
| 3 |
|
| 4 |
-
class DocumentUpload(BaseModel):
|
| 5 |
-
filename: str
|
| 6 |
-
content: str
|
| 7 |
-
|
| 8 |
class QueryRequest(BaseModel):
|
| 9 |
question: str
|
| 10 |
top_k: Optional[int] = 5
|
|
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
from typing import List, Optional
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
class QueryRequest(BaseModel):
|
| 5 |
question: str
|
| 6 |
top_k: Optional[int] = 5
|
app/services/document_processor.py
CHANGED
|
@@ -4,7 +4,6 @@ import pypdf
|
|
| 4 |
from docx import Document
|
| 5 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 6 |
|
| 7 |
-
|
| 8 |
class DocumentProcessor:
|
| 9 |
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
|
| 10 |
self.text_splitter = RecursiveCharacterTextSplitter(
|
|
|
|
| 4 |
from docx import Document
|
| 5 |
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 6 |
|
|
|
|
| 7 |
class DocumentProcessor:
|
| 8 |
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
|
| 9 |
self.text_splitter = RecursiveCharacterTextSplitter(
|
data/documents/ai_basics.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Artificial Intelligence is transforming the world. Machine learning enables computers to learn from data. Deep learning uses neural networks with multiple layers. Natural language processing helps computers understand human language. Computer vision allows machines to interpret visual information.
|
data/documents/ml_concepts.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Machine Learning Fundamentals
|
| 2 |
+
|
| 3 |
+
Supervised Learning: Training models with labeled data. Examples include classification and regression tasks.
|
| 4 |
+
|
| 5 |
+
Unsupervised Learning: Finding patterns in unlabeled data. Clustering and dimensionality reduction are common techniques.
|
| 6 |
+
|
| 7 |
+
Reinforcement Learning: Learning through trial and error with rewards and penalties. Used in robotics and game playing.
|
| 8 |
+
|
| 9 |
+
Feature Engineering: The process of selecting and transforming variables to improve model performance.
|
| 10 |
+
|
| 11 |
+
Model Evaluation: Using metrics like accuracy, precision, recall, and F1-score to assess model quality.
|
data/documents/rag_explanation.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Retrieval-Augmented Generation (RAG) System
|
| 2 |
+
|
| 3 |
+
RAG combines retrieval and generation to create more accurate AI responses.
|
| 4 |
+
|
| 5 |
+
The process works in three steps:
|
| 6 |
+
1. Document Ingestion: Documents are split into chunks and converted to vector embeddings
|
| 7 |
+
2. Retrieval: When a query comes in, relevant chunks are found using similarity search
|
| 8 |
+
3. Generation: The LLM uses retrieved context to generate accurate, grounded answers
|
| 9 |
+
|
| 10 |
+
Benefits of RAG:
|
| 11 |
+
- Reduces hallucinations by grounding responses in actual documents
|
| 12 |
+
- Enables knowledge updates without retraining models
|
| 13 |
+
- Provides source citations for transparency
|
| 14 |
+
- Works with private, domain-specific data
|
| 15 |
+
|
| 16 |
+
RAG is ideal for enterprise knowledge bases, customer support, and research applications.
|
main.py
CHANGED
|
@@ -1,6 +1,144 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
if __name__ == "__main__":
|
| 6 |
-
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from app.config import get_settings
|
| 4 |
+
from app.core.embeddings import EmbeddingGenerator
|
| 5 |
+
from app.core.vector_store import VectorStore
|
| 6 |
+
from app.core.llm import OllamaLLM
|
| 7 |
+
from app.services.document_processor import DocumentProcessor
|
| 8 |
+
from app.services.rag_chain import RAGChain
|
| 9 |
+
from app.models.schemas import QueryRequest, QueryResponse, IngestResponse
|
| 10 |
+
import tempfile
|
| 11 |
+
import os
|
| 12 |
|
| 13 |
+
# Initialize FastAPI app
|
| 14 |
+
app = FastAPI(
|
| 15 |
+
title="RAG Portfolio Project",
|
| 16 |
+
description="Production-grade Retrieval-Augmented Generation system",
|
| 17 |
+
version="1.0.0"
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
# Add CORS middleware
|
| 21 |
+
app.add_middleware(
|
| 22 |
+
CORSMiddleware,
|
| 23 |
+
allow_origins=["*"],
|
| 24 |
+
allow_credentials=True,
|
| 25 |
+
allow_methods=["*"],
|
| 26 |
+
allow_headers=["*"],
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Initialize components
|
| 30 |
+
settings = get_settings()
|
| 31 |
+
|
| 32 |
+
try:
|
| 33 |
+
embedding_generator = EmbeddingGenerator(settings.embedding_model)
|
| 34 |
+
vector_store = VectorStore(
|
| 35 |
+
host=settings.qdrant_host,
|
| 36 |
+
port=settings.qdrant_port,
|
| 37 |
+
collection_name=settings.qdrant_collection_name,
|
| 38 |
+
vector_size=embedding_generator.dimension
|
| 39 |
+
)
|
| 40 |
+
llm = OllamaLLM(settings.ollama_base_url, settings.ollama_model)
|
| 41 |
+
document_processor = DocumentProcessor()
|
| 42 |
+
rag_chain = RAGChain(embedding_generator, vector_store, llm)
|
| 43 |
+
|
| 44 |
+
print("✅ All components initialized successfully!")
|
| 45 |
+
except Exception as e:
|
| 46 |
+
print(f"❌ Error initializing components: {e}")
|
| 47 |
+
# Create dummy components for now
|
| 48 |
+
embedding_generator = None
|
| 49 |
+
vector_store = None
|
| 50 |
+
llm = None
|
| 51 |
+
document_processor = None
|
| 52 |
+
rag_chain = None
|
| 53 |
+
|
| 54 |
+
@app.get("/")
|
| 55 |
+
async def root():
|
| 56 |
+
return {
|
| 57 |
+
"message": "RAG Portfolio Project API",
|
| 58 |
+
"status": "running",
|
| 59 |
+
"docs": "/docs"
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
@app.get("/health")
|
| 63 |
+
async def health_check():
|
| 64 |
+
# Check if services are running
|
| 65 |
+
ollama_status = True
|
| 66 |
+
qdrant_status = True
|
| 67 |
+
|
| 68 |
+
try:
|
| 69 |
+
if llm:
|
| 70 |
+
# Test Ollama connection
|
| 71 |
+
test_response = llm.generate("test", "test context")
|
| 72 |
+
ollama_status = "Error" not in test_response
|
| 73 |
+
except:
|
| 74 |
+
ollama_status = False
|
| 75 |
+
|
| 76 |
+
try:
|
| 77 |
+
if vector_store:
|
| 78 |
+
# Test Qdrant connection
|
| 79 |
+
vector_store.client.get_collections()
|
| 80 |
+
except:
|
| 81 |
+
qdrant_status = False
|
| 82 |
+
|
| 83 |
+
return {
|
| 84 |
+
"status": "healthy" if (ollama_status and qdrant_status) else "degraded",
|
| 85 |
+
"ollama_connected": ollama_status,
|
| 86 |
+
"qdrant_connected": qdrant_status
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
@app.post("/ingest/file", response_model=IngestResponse)
|
| 90 |
+
async def ingest_file(file: UploadFile = File(...)):
|
| 91 |
+
"""Upload and ingest a document into the RAG system"""
|
| 92 |
+
if not rag_chain:
|
| 93 |
+
raise HTTPException(status_code=503, detail="RAG system not initialized")
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
# Save uploaded file temporarily
|
| 97 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=file.filename) as tmp:
|
| 98 |
+
content = await file.read()
|
| 99 |
+
tmp.write(content)
|
| 100 |
+
tmp_path = tmp.name
|
| 101 |
+
|
| 102 |
+
# Process document
|
| 103 |
+
chunks = document_processor.process_document(tmp_path)
|
| 104 |
+
|
| 105 |
+
# Ingest into RAG system
|
| 106 |
+
result = rag_chain.ingest_documents(chunks)
|
| 107 |
+
|
| 108 |
+
# Clean up
|
| 109 |
+
os.unlink(tmp_path)
|
| 110 |
+
|
| 111 |
+
return IngestResponse(**result, message=f"Successfully ingested {file.filename}")
|
| 112 |
+
|
| 113 |
+
except Exception as e:
|
| 114 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 115 |
+
|
| 116 |
+
@app.post("/query", response_model=QueryResponse)
|
| 117 |
+
async def query(request: QueryRequest):
|
| 118 |
+
"""Query the RAG system"""
|
| 119 |
+
if not rag_chain:
|
| 120 |
+
raise HTTPException(status_code=503, detail="RAG system not initialized")
|
| 121 |
+
|
| 122 |
+
try:
|
| 123 |
+
result = rag_chain.query(request.question, request.top_k)
|
| 124 |
+
return QueryResponse(**result)
|
| 125 |
+
|
| 126 |
+
except Exception as e:
|
| 127 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 128 |
+
|
| 129 |
+
@app.delete("/reset")
|
| 130 |
+
async def reset_collection():
|
| 131 |
+
"""Reset the vector collection (delete all documents)"""
|
| 132 |
+
if not vector_store:
|
| 133 |
+
raise HTTPException(status_code=503, detail="Vector store not initialized")
|
| 134 |
+
|
| 135 |
+
try:
|
| 136 |
+
vector_store.client.delete_collection(settings.qdrant_collection_name)
|
| 137 |
+
vector_store._ensure_collection()
|
| 138 |
+
return {"status": "success", "message": "Collection reset successfully"}
|
| 139 |
+
except Exception as e:
|
| 140 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 141 |
|
| 142 |
if __name__ == "__main__":
|
| 143 |
+
import uvicorn
|
| 144 |
+
uvicorn.run(app, host=settings.app_host, port=settings.app_port)
|
pyproject.toml
CHANGED
|
@@ -16,6 +16,7 @@ dependencies = [
|
|
| 16 |
"qdrant-client>=1.15.1",
|
| 17 |
"ragas>=0.3.7",
|
| 18 |
"rank-bm25>=0.2.2",
|
|
|
|
| 19 |
"sentence-transformers>=5.1.2",
|
| 20 |
"unstructured>=0.18.15",
|
| 21 |
"uvicorn>=0.38.0",
|
|
|
|
| 16 |
"qdrant-client>=1.15.1",
|
| 17 |
"ragas>=0.3.7",
|
| 18 |
"rank-bm25>=0.2.2",
|
| 19 |
+
"requests>=2.32.5",
|
| 20 |
"sentence-transformers>=5.1.2",
|
| 21 |
"unstructured>=0.18.15",
|
| 22 |
"uvicorn>=0.38.0",
|
scripts/demo_queries.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
from typing import Dict
|
| 4 |
+
|
| 5 |
+
BASE_URL = "http://localhost:8000"
|
| 6 |
+
|
| 7 |
+
def query_rag(question: str, top_k: int = 5) -> Dict:
|
| 8 |
+
"""Query the RAG system"""
|
| 9 |
+
response = requests.post(
|
| 10 |
+
f"{BASE_URL}/query",
|
| 11 |
+
json={"question": question, "top_k": top_k}
|
| 12 |
+
)
|
| 13 |
+
return response.json()
|
| 14 |
+
|
| 15 |
+
def print_result(question: str, result: Dict):
|
| 16 |
+
"""Pretty print query results"""
|
| 17 |
+
print("\n" + "="*80)
|
| 18 |
+
print(f"❓ QUESTION: {question}")
|
| 19 |
+
print("="*80)
|
| 20 |
+
print(f"\n💡 ANSWER:\n{result['answer']}\n")
|
| 21 |
+
print(f"📚 SOURCES ({result['context_used']} chunks used):")
|
| 22 |
+
for idx, source in enumerate(result['sources'], 1):
|
| 23 |
+
print(f" {idx}. {source['source']} (chunk {source['chunk_index']}, score: {source['score']:.3f})")
|
| 24 |
+
print("="*80)
|
| 25 |
+
|
| 26 |
+
def main():
|
| 27 |
+
"""Run demo queries"""
|
| 28 |
+
demo_queries = [
|
| 29 |
+
"What is deep learning and how does it work?",
|
| 30 |
+
"Explain the RAG process in simple terms",
|
| 31 |
+
"What are the benefits of using RAG systems?",
|
| 32 |
+
"What is supervised learning?",
|
| 33 |
+
"How does reinforcement learning work?",
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
print("\n🚀 RAG SYSTEM DEMO")
|
| 37 |
+
print("="*80)
|
| 38 |
+
|
| 39 |
+
# Check health first
|
| 40 |
+
health = requests.get(f"{BASE_URL}/health").json()
|
| 41 |
+
print(f"\n✅ System Status: {health['status']}")
|
| 42 |
+
print(f" Ollama: {'✓' if health['ollama_connected'] else '✗'}")
|
| 43 |
+
print(f" Qdrant: {'✓' if health['qdrant_connected'] else '✗'}")
|
| 44 |
+
|
| 45 |
+
# Run queries
|
| 46 |
+
for question in demo_queries:
|
| 47 |
+
try:
|
| 48 |
+
result = query_rag(question)
|
| 49 |
+
print_result(question, result)
|
| 50 |
+
except Exception as e:
|
| 51 |
+
print(f"\n❌ Error: {e}")
|
| 52 |
+
|
| 53 |
+
print("\n✨ Demo complete!")
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
main()
|
uv.lock
CHANGED
|
@@ -159,6 +159,15 @@ wheels = [
|
|
| 159 |
{ url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
|
| 160 |
]
|
| 161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
[[package]]
|
| 163 |
name = "annotated-types"
|
| 164 |
version = "0.7.0"
|
|
@@ -548,7 +557,7 @@ wheels = [
|
|
| 548 |
|
| 549 |
[[package]]
|
| 550 |
name = "datasets"
|
| 551 |
-
version = "4.
|
| 552 |
source = { registry = "https://pypi.org/simple" }
|
| 553 |
dependencies = [
|
| 554 |
{ name = "dill" },
|
|
@@ -567,9 +576,9 @@ dependencies = [
|
|
| 567 |
{ name = "tqdm" },
|
| 568 |
{ name = "xxhash" },
|
| 569 |
]
|
| 570 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 571 |
wheels = [
|
| 572 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 573 |
]
|
| 574 |
|
| 575 |
[[package]]
|
|
@@ -631,16 +640,17 @@ wheels = [
|
|
| 631 |
|
| 632 |
[[package]]
|
| 633 |
name = "fastapi"
|
| 634 |
-
version = "0.
|
| 635 |
source = { registry = "https://pypi.org/simple" }
|
| 636 |
dependencies = [
|
|
|
|
| 637 |
{ name = "pydantic" },
|
| 638 |
{ name = "starlette" },
|
| 639 |
{ name = "typing-extensions" },
|
| 640 |
]
|
| 641 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 642 |
wheels = [
|
| 643 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 644 |
]
|
| 645 |
|
| 646 |
[[package]]
|
|
@@ -812,6 +822,7 @@ dependencies = [
|
|
| 812 |
{ name = "qdrant-client" },
|
| 813 |
{ name = "ragas" },
|
| 814 |
{ name = "rank-bm25" },
|
|
|
|
| 815 |
{ name = "sentence-transformers" },
|
| 816 |
{ name = "unstructured" },
|
| 817 |
{ name = "uvicorn" },
|
|
@@ -837,6 +848,7 @@ requires-dist = [
|
|
| 837 |
{ name = "qdrant-client", specifier = ">=1.15.1" },
|
| 838 |
{ name = "ragas", specifier = ">=0.3.7" },
|
| 839 |
{ name = "rank-bm25", specifier = ">=0.2.2" },
|
|
|
|
| 840 |
{ name = "sentence-transformers", specifier = ">=5.1.2" },
|
| 841 |
{ name = "unstructured", specifier = ">=0.18.15" },
|
| 842 |
{ name = "uvicorn", specifier = ">=0.38.0" },
|
|
@@ -1450,7 +1462,7 @@ wheels = [
|
|
| 1450 |
|
| 1451 |
[[package]]
|
| 1452 |
name = "langsmith"
|
| 1453 |
-
version = "0.4.
|
| 1454 |
source = { registry = "https://pypi.org/simple" }
|
| 1455 |
dependencies = [
|
| 1456 |
{ name = "httpx" },
|
|
@@ -1461,9 +1473,9 @@ dependencies = [
|
|
| 1461 |
{ name = "requests-toolbelt" },
|
| 1462 |
{ name = "zstandard" },
|
| 1463 |
]
|
| 1464 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 1465 |
wheels = [
|
| 1466 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1467 |
]
|
| 1468 |
|
| 1469 |
[[package]]
|
|
@@ -3469,28 +3481,28 @@ wheels = [
|
|
| 3469 |
|
| 3470 |
[[package]]
|
| 3471 |
name = "ruff"
|
| 3472 |
-
version = "0.14.
|
| 3473 |
-
source = { registry = "https://pypi.org/simple" }
|
| 3474 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 3475 |
-
wheels = [
|
| 3476 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3477 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3478 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3479 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3480 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3481 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3482 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3483 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3484 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3485 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3486 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3487 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3488 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3489 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3490 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3491 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3492 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3493 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 3494 |
]
|
| 3495 |
|
| 3496 |
[[package]]
|
|
|
|
| 159 |
{ url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
|
| 160 |
]
|
| 161 |
|
| 162 |
+
[[package]]
|
| 163 |
+
name = "annotated-doc"
|
| 164 |
+
version = "0.0.2"
|
| 165 |
+
source = { registry = "https://pypi.org/simple" }
|
| 166 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c4/92/2974dba489541ed4af531d00a4df075bc3a455557d3b54fd6932c51c95cc/annotated_doc-0.0.2.tar.gz", hash = "sha256:f25664061aee278227abfaec5aeb398298be579b934758c16205d48e896e149c", size = 4452, upload-time = "2025-10-22T18:38:52.597Z" }
|
| 167 |
+
wheels = [
|
| 168 |
+
{ url = "https://files.pythonhosted.org/packages/bd/ee/cc5109cdd46a6ccd3d923db3c5425383abe51b5c033647aad1b5e2452e82/annotated_doc-0.0.2-py3-none-any.whl", hash = "sha256:2188cb99e353fcb5c20f23b8bc6f5fa7c924b213fac733d4b44883f9edffa090", size = 4056, upload-time = "2025-10-22T18:38:51.24Z" },
|
| 169 |
+
]
|
| 170 |
+
|
| 171 |
[[package]]
|
| 172 |
name = "annotated-types"
|
| 173 |
version = "0.7.0"
|
|
|
|
| 557 |
|
| 558 |
[[package]]
|
| 559 |
name = "datasets"
|
| 560 |
+
version = "4.3.0"
|
| 561 |
source = { registry = "https://pypi.org/simple" }
|
| 562 |
dependencies = [
|
| 563 |
{ name = "dill" },
|
|
|
|
| 576 |
{ name = "tqdm" },
|
| 577 |
{ name = "xxhash" },
|
| 578 |
]
|
| 579 |
+
sdist = { url = "https://files.pythonhosted.org/packages/2a/47/325206ac160f7699ed9f1798afa8f8f8d5189b03bf3815654859ac1d5cba/datasets-4.3.0.tar.gz", hash = "sha256:bc9118ed9afd92346c5be7ed3aaa00177eb907c25467f9d072a0d22777efbd2b", size = 582801, upload-time = "2025-10-23T16:31:51.547Z" }
|
| 580 |
wheels = [
|
| 581 |
+
{ url = "https://files.pythonhosted.org/packages/ca/51/409a8184ed35453d9cbb3d6b20d524b1115c2c2d117b85d5e9b06cd70b45/datasets-4.3.0-py3-none-any.whl", hash = "sha256:0ea157e72138b3ca6c7d2415f19a164ecf7d4c4fa72da2a570da286882e96903", size = 506846, upload-time = "2025-10-23T16:31:49.965Z" },
|
| 582 |
]
|
| 583 |
|
| 584 |
[[package]]
|
|
|
|
| 640 |
|
| 641 |
[[package]]
|
| 642 |
name = "fastapi"
|
| 643 |
+
version = "0.120.0"
|
| 644 |
source = { registry = "https://pypi.org/simple" }
|
| 645 |
dependencies = [
|
| 646 |
+
{ name = "annotated-doc" },
|
| 647 |
{ name = "pydantic" },
|
| 648 |
{ name = "starlette" },
|
| 649 |
{ name = "typing-extensions" },
|
| 650 |
]
|
| 651 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f7/0e/7f29e8f7219e4526747db182e1afb5a4b6abc3201768fb38d81fa2536241/fastapi-0.120.0.tar.gz", hash = "sha256:6ce2c1cfb7000ac14ffd8ddb2bc12e62d023a36c20ec3710d09d8e36fab177a0", size = 337603, upload-time = "2025-10-23T20:56:34.743Z" }
|
| 652 |
wheels = [
|
| 653 |
+
{ url = "https://files.pythonhosted.org/packages/1d/60/7a639ceaba54aec4e1d5676498c568abc654b95762d456095b6cb529b1ca/fastapi-0.120.0-py3-none-any.whl", hash = "sha256:84009182e530c47648da2f07eb380b44b69889a4acfd9e9035ee4605c5cfc469", size = 108243, upload-time = "2025-10-23T20:56:33.281Z" },
|
| 654 |
]
|
| 655 |
|
| 656 |
[[package]]
|
|
|
|
| 822 |
{ name = "qdrant-client" },
|
| 823 |
{ name = "ragas" },
|
| 824 |
{ name = "rank-bm25" },
|
| 825 |
+
{ name = "requests" },
|
| 826 |
{ name = "sentence-transformers" },
|
| 827 |
{ name = "unstructured" },
|
| 828 |
{ name = "uvicorn" },
|
|
|
|
| 848 |
{ name = "qdrant-client", specifier = ">=1.15.1" },
|
| 849 |
{ name = "ragas", specifier = ">=0.3.7" },
|
| 850 |
{ name = "rank-bm25", specifier = ">=0.2.2" },
|
| 851 |
+
{ name = "requests", specifier = ">=2.32.5" },
|
| 852 |
{ name = "sentence-transformers", specifier = ">=5.1.2" },
|
| 853 |
{ name = "unstructured", specifier = ">=0.18.15" },
|
| 854 |
{ name = "uvicorn", specifier = ">=0.38.0" },
|
|
|
|
| 1462 |
|
| 1463 |
[[package]]
|
| 1464 |
name = "langsmith"
|
| 1465 |
+
version = "0.4.38"
|
| 1466 |
source = { registry = "https://pypi.org/simple" }
|
| 1467 |
dependencies = [
|
| 1468 |
{ name = "httpx" },
|
|
|
|
| 1473 |
{ name = "requests-toolbelt" },
|
| 1474 |
{ name = "zstandard" },
|
| 1475 |
]
|
| 1476 |
+
sdist = { url = "https://files.pythonhosted.org/packages/37/21/f1ba48412c64bf3bb8feb532fc9d247b396935b5d8242332d44a4195ec2d/langsmith-0.4.38.tar.gz", hash = "sha256:3aa57f9c16a5880256cd1eab0452533c1fb5ee14ec5250e23ed919cc2b07f6d3", size = 942789, upload-time = "2025-10-23T22:28:20.458Z" }
|
| 1477 |
wheels = [
|
| 1478 |
+
{ url = "https://files.pythonhosted.org/packages/b4/2b/7e0248f65e35800ea8e4e3dbb3bcc36c61b81f5b8abeddaceec8320ab491/langsmith-0.4.38-py3-none-any.whl", hash = "sha256:326232a24b1c6dd308a3188557cc023adf8fb14144263b2982c115a6be5141e7", size = 397341, upload-time = "2025-10-23T22:28:18.333Z" },
|
| 1479 |
]
|
| 1480 |
|
| 1481 |
[[package]]
|
|
|
|
| 3481 |
|
| 3482 |
[[package]]
|
| 3483 |
name = "ruff"
|
| 3484 |
+
version = "0.14.2"
|
| 3485 |
+
source = { registry = "https://pypi.org/simple" }
|
| 3486 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/34/8218a19b2055b80601e8fd201ec723c74c7fe1ca06d525a43ed07b6d8e85/ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96", size = 5539663, upload-time = "2025-10-23T19:37:00.956Z" }
|
| 3487 |
+
wheels = [
|
| 3488 |
+
{ url = "https://files.pythonhosted.org/packages/16/dd/23eb2db5ad9acae7c845700493b72d3ae214dce0b226f27df89216110f2b/ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1", size = 12533390, upload-time = "2025-10-23T19:36:18.044Z" },
|
| 3489 |
+
{ url = "https://files.pythonhosted.org/packages/5a/8c/5f9acff43ddcf3f85130d0146d0477e28ccecc495f9f684f8f7119b74c0d/ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11", size = 12887187, upload-time = "2025-10-23T19:36:22.664Z" },
|
| 3490 |
+
{ url = "https://files.pythonhosted.org/packages/99/fa/047646491479074029665022e9f3dc6f0515797f40a4b6014ea8474c539d/ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3", size = 11925177, upload-time = "2025-10-23T19:36:24.778Z" },
|
| 3491 |
+
{ url = "https://files.pythonhosted.org/packages/15/8b/c44cf7fe6e59ab24a9d939493a11030b503bdc2a16622cede8b7b1df0114/ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3", size = 12358285, upload-time = "2025-10-23T19:36:26.979Z" },
|
| 3492 |
+
{ url = "https://files.pythonhosted.org/packages/45/01/47701b26254267ef40369aea3acb62a7b23e921c27372d127e0f3af48092/ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8", size = 12303832, upload-time = "2025-10-23T19:36:29.192Z" },
|
| 3493 |
+
{ url = "https://files.pythonhosted.org/packages/2d/5c/ae7244ca4fbdf2bee9d6405dcd5bc6ae51ee1df66eb7a9884b77b8af856d/ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839", size = 13036995, upload-time = "2025-10-23T19:36:31.861Z" },
|
| 3494 |
+
{ url = "https://files.pythonhosted.org/packages/27/4c/0860a79ce6fd4c709ac01173f76f929d53f59748d0dcdd662519835dae43/ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7", size = 14512649, upload-time = "2025-10-23T19:36:33.915Z" },
|
| 3495 |
+
{ url = "https://files.pythonhosted.org/packages/7f/7f/d365de998069720a3abfc250ddd876fc4b81a403a766c74ff9bde15b5378/ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc", size = 14088182, upload-time = "2025-10-23T19:36:36.983Z" },
|
| 3496 |
+
{ url = "https://files.pythonhosted.org/packages/6c/ea/d8e3e6b209162000a7be1faa41b0a0c16a133010311edc3329753cc6596a/ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a", size = 13599516, upload-time = "2025-10-23T19:36:39.208Z" },
|
| 3497 |
+
{ url = "https://files.pythonhosted.org/packages/fa/ea/c7810322086db68989fb20a8d5221dd3b79e49e396b01badca07b433ab45/ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096", size = 13272690, upload-time = "2025-10-23T19:36:41.453Z" },
|
| 3498 |
+
{ url = "https://files.pythonhosted.org/packages/a9/39/10b05acf8c45786ef501d454e00937e1b97964f846bf28883d1f9619928a/ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df", size = 13496497, upload-time = "2025-10-23T19:36:43.61Z" },
|
| 3499 |
+
{ url = "https://files.pythonhosted.org/packages/59/a1/1f25f8301e13751c30895092485fada29076e5e14264bdacc37202e85d24/ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05", size = 12266116, upload-time = "2025-10-23T19:36:45.625Z" },
|
| 3500 |
+
{ url = "https://files.pythonhosted.org/packages/5c/fa/0029bfc9ce16ae78164e6923ef392e5f173b793b26cc39aa1d8b366cf9dc/ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5", size = 12281345, upload-time = "2025-10-23T19:36:47.618Z" },
|
| 3501 |
+
{ url = "https://files.pythonhosted.org/packages/a5/ab/ece7baa3c0f29b7683be868c024f0838770c16607bea6852e46b202f1ff6/ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e", size = 12629296, upload-time = "2025-10-23T19:36:49.789Z" },
|
| 3502 |
+
{ url = "https://files.pythonhosted.org/packages/a4/7f/638f54b43f3d4e48c6a68062794e5b367ddac778051806b9e235dfb7aa81/ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770", size = 13371610, upload-time = "2025-10-23T19:36:51.882Z" },
|
| 3503 |
+
{ url = "https://files.pythonhosted.org/packages/8d/35/3654a973ebe5b32e1fd4a08ed2d46755af7267da7ac710d97420d7b8657d/ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9", size = 12415318, upload-time = "2025-10-23T19:36:53.961Z" },
|
| 3504 |
+
{ url = "https://files.pythonhosted.org/packages/71/30/3758bcf9e0b6a4193a6f51abf84254aba00887dfa8c20aba18aa366c5f57/ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af", size = 13565279, upload-time = "2025-10-23T19:36:56.578Z" },
|
| 3505 |
+
{ url = "https://files.pythonhosted.org/packages/2e/5d/aa883766f8ef9ffbe6aa24f7192fb71632f31a30e77eb39aa2b0dc4290ac/ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a", size = 12554956, upload-time = "2025-10-23T19:36:58.714Z" },
|
| 3506 |
]
|
| 3507 |
|
| 3508 |
[[package]]
|