Skip to content

Commit 7d59e8c

Browse files
pavanjavapavanmantha
andauthored
pushed advanced rag with langchain (#56)
Co-authored-by: pavanmantha <pavan.mantha@thevaslabs.io>
1 parent 0ee24af commit 7d59e8c

22 files changed

Lines changed: 673 additions & 2 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
COLLECTION_NAME="summaries"
2+
VECTOR_NAME="content"
3+
LLM_MODEL="llama3.1:latest"
4+
OLLAMA_BASE_URL="http://localhost:11434"
5+
EMBEDDING_MODEL="snowflake-arctic-embed:33m"
6+
QDRANT_URL="http://localhost:6333"
7+
QDRANT_API_KEY="th3s3cr3tk3y"
8+
9+
LIT_SERVER_PORT=8000
10+
LIT_SERVER_WORKERS_PER_DEVICE=2
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Use the official Python image from the Docker Hub
2+
FROM python:3.9-slim
3+
4+
# Set the working directory in the container
5+
WORKDIR /app
6+
7+
# Copy the requirements file to the container
8+
COPY requirements.txt .
9+
10+
# Install the required dependencies
11+
RUN pip install --no-cache-dir -r requirements.txt
12+
13+
# Copy the current directory contents into the container at /app
14+
COPY . .
15+
16+
# Set environment variables (you can replace these with values from your .env file or other configs)
17+
ENV QDRANT_URL='http://host.docker.internal:6333' \
18+
OLLAMA_BASE_URL='http://host.docker.internal:11434'
19+
20+
# Expose port 8000 for external access
21+
EXPOSE 8000
22+
23+
# Command to run your application
24+
CMD ["python", "api_server.py"]

bootstraprag/templates/langchain/rag_with_hyde/__init__.py

Whitespace-only changes.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from abc import ABC
2+
from dotenv import load_dotenv, find_dotenv
3+
from openai import base_url
4+
5+
from core_advanced_rag import RetrievalAugmentationGenerationUsingHyDE
6+
import litserve as ls
7+
import os
8+
9+
_ = load_dotenv(find_dotenv())
10+
11+
12+
class SimpleRAGServingAPI(ls.LitAPI, ABC):
13+
def __init__(self):
14+
self.advanced_rag: RetrievalAugmentationGenerationUsingHyDE = None
15+
self.FILE_PATH = 'data/mlops.pdf'
16+
self.COLLECTION_NAME = os.environ.get('COLLECTION_NAME')
17+
self.VECTOR_NAME = os.environ.get('VECTOR_NAME')
18+
self.PROMPT_TEMPLATE = """Answer the question based only on the following context:
19+
{context}
20+
21+
Question: {question}
22+
"""
23+
self.LLM_MODEL = os.environ.get('LLM_MODEL')
24+
self.EMBEDDING_MODEL = os.environ.get('EMBEDDING_MODEL')
25+
self.QDRANT_URL = os.environ.get('QDRANT_URL')
26+
self.QDRANT_API_KEY = os.environ.get('QDRANT_API_KEY')
27+
self.OLLAMA_BASE_URL = os.environ.get('OLLAMA_BASE_URL')
28+
29+
def setup(self, devices):
30+
self.advanced_rag = RetrievalAugmentationGenerationUsingHyDE(
31+
file_path=self.FILE_PATH,
32+
collection_name=self.COLLECTION_NAME,
33+
vector_name=self.VECTOR_NAME,
34+
prompt_template=self.PROMPT_TEMPLATE,
35+
llm_model=self.LLM_MODEL,
36+
embedding_model=self.EMBEDDING_MODEL,
37+
qdrant_url=self.QDRANT_URL,
38+
qdrant_api_key=self.QDRANT_API_KEY,
39+
base_url=self.OLLAMA_BASE_URL
40+
)
41+
# Load Documents
42+
documents = self.advanced_rag.load_documents()
43+
44+
# Get Embeddings
45+
embeddings = self.advanced_rag.get_embeddings()
46+
47+
# Setup Qdrant and Add Documents
48+
self.advanced_rag.setup_qdrant_collection(embeddings=embeddings, documents=documents)
49+
50+
def decode_request(self, request, **kwargs):
51+
return request["query"]
52+
53+
def predict(self, query: str):
54+
return self.advanced_rag.execute_pipeline(user_query=query)
55+
56+
def encode_response(self, output, **kwargs):
57+
return {'response': output}
58+
59+
60+
if __name__ == '__main__':
61+
api = SimpleRAGServingAPI()
62+
server = ls.LitServer(lit_api=api, api_path='/api/v1/chat-completion',
63+
workers_per_device=int(os.environ.get('LIT_SERVER_WORKERS_PER_DEVICE')))
64+
server.run(port=os.environ.get('LIT_SERVER_PORT'))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright The Lightning AI team.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import requests
15+
16+
response = requests.post("http://127.0.0.1:8000/predict", json={"input": 4.0})
17+
print(f"Status: {response.status_code}\nResponse:\n {response.text}")
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from langchain_core.output_parsers import StrOutputParser
2+
from langchain.chains.hyde.base import HypotheticalDocumentEmbedder
3+
from langchain.prompts import PromptTemplate
4+
from langchain_qdrant import QdrantVectorStore
5+
from langchain_community.document_loaders import PyMuPDFLoader
6+
from langchain_ollama import OllamaEmbeddings, ChatOllama
7+
from langchain_text_splitters import RecursiveCharacterTextSplitter
8+
from langchain_core.prompts import ChatPromptTemplate
9+
from langchain_core.runnables import RunnablePassthrough
10+
from qdrant_client.http.models import Distance, VectorParams
11+
import qdrant_client
12+
13+
14+
class RetrievalAugmentationGenerationUsingHyDE:
15+
def __init__(self, file_path, collection_name, vector_name, prompt_template, llm_model, embedding_model, qdrant_url,
16+
qdrant_api_key, base_url):
17+
self.file_path = file_path
18+
self.collection_name = collection_name
19+
self.vector_name = vector_name
20+
self.prompt_template = prompt_template
21+
self.qdrant_url = qdrant_url
22+
self.qdrant_api_key = qdrant_api_key
23+
self.llm = ChatOllama(model=llm_model, temperature=0.2, base_url=base_url)
24+
self.base_embeddings = OllamaEmbeddings(model=embedding_model)
25+
self.client = qdrant_client.QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
26+
self.vector_store = None
27+
28+
def load_documents(self):
29+
loader = PyMuPDFLoader(file_path=self.file_path)
30+
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
31+
chunk_size=200, chunk_overlap=30
32+
)
33+
return loader.load_and_split(text_splitter=text_splitter)
34+
35+
def get_embeddings(self):
36+
prompt = PromptTemplate(input_variables=["question"], template=self.prompt_template)
37+
llm_chain = self.llm | prompt
38+
return HypotheticalDocumentEmbedder(
39+
llm_chain=llm_chain,
40+
base_embeddings=self.base_embeddings
41+
).from_llm(llm=self.llm, base_embeddings=self.base_embeddings, prompt_key="web_search")
42+
43+
def setup_qdrant_collection(self, embeddings, documents):
44+
if not self.client.collection_exists(collection_name=self.collection_name):
45+
self.client.create_collection(
46+
collection_name=self.collection_name,
47+
vectors_config={
48+
"content": VectorParams(size=384, distance=Distance.COSINE)
49+
}
50+
)
51+
self.vector_store = QdrantVectorStore(
52+
client=self.client,
53+
collection_name=self.collection_name,
54+
embedding=embeddings,
55+
vector_name=self.vector_name
56+
)
57+
self.vector_store.add_documents(documents=documents)
58+
else:
59+
self.vector_store = QdrantVectorStore.from_existing_collection(
60+
collection_name=self.collection_name,
61+
url=self.qdrant_url,
62+
api_key=self.qdrant_api_key,
63+
vector_name=self.vector_name,
64+
embedding=embeddings
65+
)
66+
67+
def execute_pipeline(self, user_query):
68+
retriever = self.vector_store.as_retriever(
69+
search_type="similarity_score_threshold",
70+
search_kwargs={'score_threshold': 0.8}
71+
)
72+
prompt = ChatPromptTemplate.from_template(self.prompt_template)
73+
chain = (
74+
{"context": retriever, "question": RunnablePassthrough()}
75+
| prompt
76+
| self.llm
77+
| StrOutputParser()
78+
)
79+
return chain.invoke(user_query)
80+
81+
82+
616 KB
Binary file not shown.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import os
2+
3+
from core_advanced_rag import RetrievalAugmentationGenerationUsingHyDE
4+
from dotenv import load_dotenv, find_dotenv
5+
6+
load_dotenv(find_dotenv())
7+
8+
if __name__ == "__main__":
9+
# Configurations
10+
FILE_PATH = 'data/mlops.pdf'
11+
COLLECTION_NAME = os.environ.get('COLLECTION_NAME')
12+
VECTOR_NAME = os.environ.get('VECTOR_NAME')
13+
PROMPT_TEMPLATE = """Answer the question based only on the following context:
14+
{context}
15+
16+
Question: {question}
17+
"""
18+
LLM_MODEL = os.environ.get('LLM_MODEL')
19+
EMBEDDING_MODEL = os.environ.get('EMBEDDING_MODEL')
20+
QDRANT_URL = os.environ.get('QDRANT_URL')
21+
QDRANT_API_KEY = os.environ.get('QDRANT_API_KEY')
22+
OLLAMA_BASE_URL = os.environ.get('OLLAMA_BASE_URL')
23+
24+
# Initialize QnA Pipeline Handler
25+
pipeline_handler = RetrievalAugmentationGenerationUsingHyDE(
26+
file_path=FILE_PATH,
27+
collection_name=COLLECTION_NAME,
28+
vector_name=VECTOR_NAME,
29+
prompt_template=PROMPT_TEMPLATE,
30+
llm_model=LLM_MODEL,
31+
embedding_model=EMBEDDING_MODEL,
32+
qdrant_url=QDRANT_URL,
33+
qdrant_api_key=QDRANT_API_KEY,
34+
base_url=OLLAMA_BASE_URL
35+
)
36+
37+
# Load Documents
38+
documents = pipeline_handler.load_documents()
39+
40+
# Get Embeddings
41+
embeddings = pipeline_handler.get_embeddings()
42+
43+
# Setup Qdrant and Add Documents
44+
pipeline_handler.setup_qdrant_collection(embeddings=embeddings, documents=documents)
45+
46+
# Execute Retrieval Pipeline
47+
question = "what are the system and operational challenges of mlops?"
48+
output = pipeline_handler.execute_pipeline(user_query=question)
49+
print(output)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Advanced RAG with HyDE Project
2+
3+
This project implements a Advanced RAG with HyDE based Question-Answering system using LangChain, Ollama, and Qdrant.
4+
5+
## Prerequisites
6+
7+
- Python 3.8 or higher
8+
- Ollama running locally (for LLM)
9+
- Qdrant running locally (for vector storage)
10+
11+
## project structure
12+
```tree
13+
.
14+
├── Dockerfile
15+
├── __init__.py
16+
├── api_server.py
17+
├── client.py
18+
├── core_advanced_rag.py
19+
├── data
20+
│   └── mlops.pdf
21+
├── main.py
22+
├── readme.md
23+
└── requirements.txt
24+
```
25+
26+
## Installation
27+
28+
1. `pip install bootstrap-rag`
29+
30+
### Setting up Ollama and Qdrant
31+
Method 1:
32+
1. navigate to root_folder/setups
33+
2. run the docker-compose-dev.yml
34+
3. run the pull_model as per the underlying OS
35+
36+
Method 2:
37+
1. Install and run Ollama:
38+
- Follow the instructions at [Ollama's official website](https://ollama.ai/) to install Ollama.
39+
- Make sure Ollama is running and accessible at `http://localhost:11434`.
40+
41+
2. Install and run Qdrant:
42+
- Follow the instructions at [Qdrant's official website](https://qdrant.tech/documentation/quick-start/) to install Qdrant.
43+
- Make sure Qdrant is running and accessible at `http://localhost:6333`.
44+
45+
## How to Run
46+
1. Create a virtual environment (optional but recommended):
47+
```
48+
python -m venv venv
49+
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
50+
```
51+
2. run `bootstraprag create <your_poc_project_name>`
52+
53+
3. Install the required dependencies:
54+
```
55+
pip install -r requirements.txt
56+
```
57+
58+
### Usage
59+
60+
1. Prepare your MLOps PDF document and place it in the `data` directory.
61+
62+
2. Update the `.env` file with your specific configuration:
63+
- Update the `file_path` to point to your PDF document.
64+
- Update the `collection_name` if you want to use a different name for your Qdrant collection.
65+
- Update the `qdrant_url` and `qdrant_api_key` if your Qdrant setup is different.
66+
67+
3. Run the script:
68+
```
69+
python main.py
70+
```
71+
or
72+
```
73+
python api_server.py
74+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
langchain==0.3.3
2+
langchain-ollama==0.2.0
3+
langchain-community==0.3.2
4+
langchain-qdrant==0.1.4
5+
unstructured[all-docs]==0.15.14
6+
pydantic==2.9.2
7+
qdrant-client==1.12.0
8+
litserve==0.2.2

0 commit comments

Comments
 (0)