Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions bootstraprag/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,50 @@ def create_zip(project_name):

@click.command()
@click.argument('project_name')
@click.option('--template', type=click.Choice(['simple-search', 'hybrid-search', 'llamaindex-rag', 'rag-with-cot', 'rag-with-ReACT', 'rag-with-HyDE']),
prompt="Which template would you like to use?")
@click.option('--technology', type=click.Choice(['llamaindex', 'langchain', 'haystack', 'qdrant']),
@click.option('--framework', type=click.Choice(['llamaindex', 'langchain', 'haystack', 'None']),
prompt="Which technology would you like to use?")
@click.option('--observability', type=click.Choice(['Yes', 'No']), prompt="Would you like to set up observability?")
@click.option('--api_key', prompt="Please provide your OpenAI API key (leave blank to skip)", default='',
required=False)
@click.option('--data_source', type=click.Choice(['PDF', 'TXT']),
prompt="Which data source would you like to use?")
@click.option('--vector_db', type=click.Choice(['Yes', 'No']), prompt="Would you like to use a vector database?")
def create(project_name, template, technology, observability, api_key, data_source, vector_db):
"""Creates a new project with the specified type."""
template_path = Path(__file__).parent / 'templates' / technology / template
@click.option('--template', type=click.Choice([]), prompt=False)
@click.option('--observability', type=click.Choice([]), prompt=False)
def create(project_name, framework, template, observability):
template_choices = []
observability_choices = []

if framework == 'llamaindex' or framework == 'langchain' or framework == 'haystack':
template_choices = ['simple-rag', 'self-rag', 'rag-with-cot', 'rag-with-react', 'rag-with-hyde']
elif framework == 'None':
framework = 'qdrant'
template_choices = ['simple-search', 'hybrid-search']

template = click.prompt("Which template would you like to use?",
type=click.Choice(template_choices)
)
if framework == 'llamaindex' or framework == 'langchain' or framework == 'haystack':
observability_choices = ['Yes', 'No']
observability = click.prompt("Do you wish to enable observability?",
type=click.Choice(observability_choices)
)

click.echo(f'You have selected framework: {framework} and template: {template} and observability: {observability}')
download_and_extract_template(project_name, framework, template, observability)


def download_and_extract_template(project_name, framework, template, observability_selection):
if observability_selection == 'Yes':
folder_name = template.replace('-', '_') + '_observability'
base_path = Path(__file__).parent / 'templates' / framework / folder_name
else:
base_path = Path(__file__).parent / 'templates' / framework / str(template).replace('-', '_')
project_path = Path.cwd() / project_name

if project_path.exists():
click.echo(f"Error: Project directory {project_name} already exists!")
return

shutil.copytree(template_path, project_path)

# Optionally handle the API key and other parameters here if necessary

zip_path = create_zip(project_name)

click.echo(f"Created {template} project at {project_path}")
click.echo(f"Project archived as {zip_path}")
try:
shutil.copytree(base_path, project_path)
click.echo(f"Project {project_name} created successfully at {project_path}")
except Exception as e:
click.echo(f"Error: {e}")


cli.add_command(create)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
- Navigate to the root of the project and run the below command
- `pip install -r requirements.txt`
- In the data folder place your data preferably any ".pdf"
- change the data directory in `main.py`
#### Note: ensure your qdrant and ollama (if LLM models are pointing to local) are running
- run `python main.py`
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import os

from llama_index.core import (
SimpleDirectoryReader,
VectorStoreIndex,
StorageContext,
Settings,
get_response_synthesizer)
from llama_index.core.query_engine import RetrieverQueryEngine, TransformQueryEngine
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.schema import TextNode, MetadataMode
from llama_index.vector_stores.qdrant import QdrantVectorStore
from llama_index.embeddings.ollama import OllamaEmbedding
# enable if you are using openai
# from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.ollama import Ollama
# enable if you are using openai
# from llama_index.llms.openai import OpenAI
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.indices.query.query_transform import HyDEQueryTransform
from llama_index.core.base.response.schema import Response, StreamingResponse, AsyncStreamingResponse, PydanticResponse
import qdrant_client
import logging
from dotenv import load_dotenv, find_dotenv
from typing import Union
import phoenix as px
import llama_index

_ = load_dotenv(find_dotenv())

logging.basicConfig(level=int(os.environ['INFO']))
logger = logging.getLogger(__name__)

session = px.launch_app()
llama_index.core.set_global_handler("arize_phoenix")


class BaseRAG:
RESPONSE_TYPE = Union[
Response, StreamingResponse, AsyncStreamingResponse, PydanticResponse
]

def __init__(self, data_path: str, chunk_size: int = 512, chunk_overlap: int = 200,
required_exts: list[str] = ['.pdf'],
show_progress: bool = False, similarity_top_k: int = 3):
# load the local data directory and chunk the data for further processing
self.docs = SimpleDirectoryReader(input_dir=data_path, required_exts=required_exts).load_data(
show_progress=show_progress)
self.text_parser = SentenceSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)

# Create a local Qdrant vector store
logger.info("initializing the vector store related objects")
client = qdrant_client.QdrantClient(url=os.environ['DB_URL'], api_key=os.environ['DB_API_KEY'])
self.vector_store = QdrantVectorStore(client=client, collection_name=os.environ['COLLECTION_NAME'])

# use your prefered vector embeddings model
logger.info("initializing the OllamaEmbedding")
embed_model = OllamaEmbedding(model_name=os.environ['OLLMA_EMBED_MODEL'],
base_url=os.environ['OLLAMA_BASE_URL'])
# openai embeddings, embedding_model_name="text-embedding-3-large"
# embed_model = OpenAIEmbedding(embed_batch_size=10, model=embedding_model_name)

# use your prefered llm
llm = Ollama(model=os.environ['OLLMA_LLM_MODEL'], base_url=os.environ['OLLAMA_BASE_URL'], request_timeout=600)
# llm = OpenAI(model="gpt-4o")

logger.info("initializing the global settings")
Settings.embed_model = embed_model
Settings.llm = llm

Settings.transformations = [self.text_parser]

self.text_chunks = []
self.doc_ids = []
self.nodes = []

self.similarity_top_k = similarity_top_k
self.hyde_query_engine = None

# preprocess the data like chunking, nodes, metadata etc
self._pre_process()

def _pre_process(self):
logger.info("enumerating docs")
for doc_idx, doc in enumerate(self.docs):
curr_text_chunks = self.text_parser.split_text(doc.text)
self.text_chunks.extend(curr_text_chunks)
self.doc_ids.extend([doc_idx] * len(curr_text_chunks))

logger.info("enumerating text_chunks")
for idx, text_chunk in enumerate(self.text_chunks):
node = TextNode(text=text_chunk)
src_doc = self.docs[self.doc_ids[idx]]
node.metadata = src_doc.metadata
self.nodes.append(node)

logger.info("enumerating nodes")
for node in self.nodes:
node_embedding = Settings.embed_model.get_text_embedding(
node.get_content(metadata_mode=MetadataMode.ALL)
)
node.embedding = node_embedding

# create vector store, index documents and creates retriever
self._create_index_and_retriever()

def _create_index_and_retriever(self):
logger.info("initializing the storage context")
storage_context = StorageContext.from_defaults(vector_store=self.vector_store)
logger.info("indexing the nodes in VectorStoreIndex")
index = VectorStoreIndex(
nodes=self.nodes,
storage_context=storage_context,
transformations=Settings.transformations,
)

logger.info("initializing the VectorIndexRetriever with top_k as 5")
vector_retriever = VectorIndexRetriever(index=index, similarity_top_k=self.similarity_top_k)
response_synthesizer = get_response_synthesizer()
logger.info("creating the RetrieverQueryEngine instance")
vector_query_engine = RetrieverQueryEngine(
retriever=vector_retriever,
response_synthesizer=response_synthesizer,
)
logger.info("creating the HyDEQueryTransform instance")
hyde = HyDEQueryTransform(include_original=True)
hyde_query_engine = TransformQueryEngine(vector_query_engine, hyde)

self.hyde_query_engine = hyde_query_engine

def query(self, query_string: str) -> RESPONSE_TYPE:
response = self.hyde_query_engine.query(str_or_query_bundle=query_string)
return response
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from base_rag import BaseRAG

# this step will do pre processing, indexing in vector store, creating retriever (hyDE).
# this may take some time based on your document size and chunk strategy.
base_rag = BaseRAG(show_progress=True,
data_path='<YOUR_DATA_PATH>') # leaving all the defaults. if needed override them in constructor
# Start a loop to continually get input from the user
while True:
# Get a query from the user
user_query = input("Enter your query [type 'bye' to 'exit']: ")

# Check if the user wants to terminate the loop
if user_query.lower() == "bye" or user_query.lower() == "exit":
break

response = base_rag.query(query_string=user_query)
print(response)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Instructions to run the code

- Navigate to the root of the project and run the below command
- `pip install -r requirements.txt`
- In the data folder place your data preferably any ".pdf"
- change the data directory in `main.py`
#### Note: ensure your qdrant and ollama (if LLM models are pointing to local) are running
- run `python main.py`
- visit http://localhost:6006/ for all the observability
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
qdrant-client==1.10.1
python-dotenv==1.0.1
qdrant-client==1.10.1
arize-phoenix==4.14.1
llama-index==0.10.58
llama-index-llms-openai==0.1.27
llama-index-llms-ollama==0.2.0
llama-index-embeddings-openai==0.1.11
llama-index-embeddings-ollama==0.1.2
llama-index-vector-stores-qdrant==0.2.14
llama-index-callbacks-arize-phoenix==0.1.6