rchrdgwr commited on
Commit
46fc427
·
1 Parent(s): 81cdf35

updates to chainlit app - 2 vector stores

Browse files
Dockerfile CHANGED
@@ -8,4 +8,4 @@ COPY --chown=user . $HOME/app
8
  COPY ./requirements.txt ~/app/requirements.txt
9
  RUN pip install -r requirements.txt
10
  COPY . .
11
- CMD ["chainlit", "run", "solution_app.py", "--port", "7860"]
 
8
  COPY ./requirements.txt ~/app/requirements.txt
9
  RUN pip install -r requirements.txt
10
  COPY . .
11
+ CMD ["chainlit", "run", "app.py", "--port", "7860"]
app.py CHANGED
@@ -2,15 +2,15 @@ import os
2
  import chainlit as cl
3
  from dotenv import load_dotenv
4
  from operator import itemgetter
5
- from langchain_huggingface import HuggingFaceEndpoint
6
  from langchain_community.document_loaders import TextLoader
7
  from langchain_text_splitters import RecursiveCharacterTextSplitter
8
  from langchain_community.vectorstores import FAISS
9
- from langchain_huggingface import HuggingFaceEndpointEmbeddings
10
  from langchain_core.prompts import PromptTemplate
11
  from langchain.schema.output_parser import StrOutputParser
12
  from langchain.schema.runnable import RunnablePassthrough
13
  from langchain.schema.runnable.config import RunnableConfig
 
14
 
15
  # GLOBAL SCOPE - ENTIRE APPLICATION HAS ACCESS TO VALUES SET IN THIS SCOPE #
16
  # ---- ENV VARIABLES ---- #
@@ -19,77 +19,20 @@ This function will load our environment file (.env) if it is present.
19
 
20
  NOTE: Make sure that .env is in your .gitignore file - it is by default, but please ensure it remains there.
21
  """
22
- load_dotenv()
23
 
24
- """
25
- We will load our environment variables here.
26
- """
27
- HF_LLM_ENDPOINT = os.environ["HF_LLM_ENDPOINT"]
28
- HF_EMBED_ENDPOINT = os.environ["HF_EMBED_ENDPOINT"]
29
- HF_TOKEN = os.environ["HF_TOKEN"]
30
-
31
- # ---- GLOBAL DECLARATIONS ---- #
32
-
33
- # -- RETRIEVAL -- #
34
- """
35
- 1. Load Documents from Text File
36
- 2. Split Documents into Chunks
37
- 3. Load HuggingFace Embeddings (remember to use the URL we set above)
38
- 4. Index Files if they do not exist, otherwise load the vectorstore
39
- """
40
- ### 1. CREATE TEXT LOADER AND LOAD DOCUMENTS
41
- ### NOTE: PAY ATTENTION TO THE PATH THEY ARE IN.
42
- text_loader =
43
- documents =
44
-
45
- ### 2. CREATE TEXT SPLITTER AND SPLIT DOCUMENTS
46
- text_splitter =
47
- split_documents =
48
-
49
- ### 3. LOAD HUGGINGFACE EMBEDDINGS
50
- hf_embeddings =
51
-
52
- if os.path.exists("./data/vectorstore"):
53
- vectorstore = FAISS.load_local(
54
- "./data/vectorstore",
55
- hf_embeddings,
56
- allow_dangerous_deserialization=True # this is necessary to load the vectorstore from disk as it's stored as a `.pkl` file.
57
- )
58
- hf_retriever = vectorstore.as_retriever()
59
- print("Loaded Vectorstore")
60
- else:
61
- print("Indexing Files")
62
- os.makedirs("./data/vectorstore", exist_ok=True)
63
- ### 4. INDEX FILES
64
- ### NOTE: REMEMBER TO BATCH THE DOCUMENTS WITH MAXIMUM BATCH SIZE = 32
65
 
66
- hf_retriever = vectorstore.as_retriever()
 
67
 
68
- # -- AUGMENTED -- #
69
- """
70
- 1. Define a String Template
71
- 2. Create a Prompt Template from the String Template
72
- """
73
- ### 1. DEFINE STRING TEMPLATE
74
- RAG_PROMPT_TEMPLATE =
75
 
76
- ### 2. CREATE PROMPT TEMPLATE
77
- rag_prompt =
78
 
79
- # -- GENERATION -- #
80
- """
81
- 1. Create a HuggingFaceEndpoint for the LLM
82
- """
83
- ### 1. CREATE HUGGINGFACE ENDPOINT FOR LLM
84
- hf_llm =
85
 
86
  @cl.author_rename
87
  def rename(original_author: str):
88
- """
89
- This function can be used to rename the 'author' of a message.
90
-
91
- In this case, we're overriding the 'Assistant' author to be 'Paul Graham Essay Bot'.
92
- """
93
  rename_dict = {
94
  "Assistant" : "Paul Graham Essay Bot"
95
  }
@@ -97,36 +40,22 @@ def rename(original_author: str):
97
 
98
  @cl.on_chat_start
99
  async def start_chat():
100
- """
101
- This function will be called at the start of every user session.
102
-
103
- We will build our LCEL RAG chain here, and store it in the user session.
104
-
105
- The user session is a dictionary that is unique to each user session, and is stored in the memory of the server.
106
- """
107
-
108
- ### BUILD LCEL RAG CHAIN THAT ONLY RETURNS TEXT
109
- lcel_rag_chain =
110
-
111
  cl.user_session.set("lcel_rag_chain", lcel_rag_chain)
112
 
113
  @cl.on_message
114
  async def main(message: cl.Message):
115
- """
116
- This function will be called every time a message is recieved from a session.
117
-
118
- We will use the LCEL RAG chain to generate a response to the user query.
119
-
120
- The LCEL RAG chain is stored in the user session, and is unique to each user session - this is why we can access it here.
121
- """
122
  lcel_rag_chain = cl.user_session.get("lcel_rag_chain")
123
-
124
  msg = cl.Message(content="")
125
 
126
- async for chunk in lcel_rag_chain.astream(
127
- {"query": message.content},
128
- config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]),
129
- ):
130
- await msg.stream_token(chunk)
 
 
 
 
 
131
 
132
- await msg.send()
 
2
  import chainlit as cl
3
  from dotenv import load_dotenv
4
  from operator import itemgetter
5
+
6
  from langchain_community.document_loaders import TextLoader
7
  from langchain_text_splitters import RecursiveCharacterTextSplitter
8
  from langchain_community.vectorstores import FAISS
 
9
  from langchain_core.prompts import PromptTemplate
10
  from langchain.schema.output_parser import StrOutputParser
11
  from langchain.schema.runnable import RunnablePassthrough
12
  from langchain.schema.runnable.config import RunnableConfig
13
+ from utilities.utilities import process_documents
14
 
15
  # GLOBAL SCOPE - ENTIRE APPLICATION HAS ACCESS TO VALUES SET IN THIS SCOPE #
16
  # ---- ENV VARIABLES ---- #
 
19
 
20
  NOTE: Make sure that .env is in your .gitignore file - it is by default, but please ensure it remains there.
21
  """
 
22
 
23
+ load_dotenv()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ use_document = True
26
+ use_qdrant = False
27
 
28
+ lcel_rag_chain= None
 
 
 
 
 
 
29
 
30
+ if use_document:
31
+ lcel_rag_chain = process_documents(use_qdrant)
32
 
 
 
 
 
 
 
33
 
34
  @cl.author_rename
35
  def rename(original_author: str):
 
 
 
 
 
36
  rename_dict = {
37
  "Assistant" : "Paul Graham Essay Bot"
38
  }
 
40
 
41
  @cl.on_chat_start
42
  async def start_chat():
 
 
 
 
 
 
 
 
 
 
 
43
  cl.user_session.set("lcel_rag_chain", lcel_rag_chain)
44
 
45
  @cl.on_message
46
  async def main(message: cl.Message):
 
 
 
 
 
 
 
47
  lcel_rag_chain = cl.user_session.get("lcel_rag_chain")
 
48
  msg = cl.Message(content="")
49
 
50
+ if lcel_rag_chain:
51
+
52
+ async for chunk in lcel_rag_chain.astream(
53
+ {"query": message.content},
54
+ config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]),
55
+ ):
56
+ await msg.stream_token(chunk)
57
+
58
+ await msg.send()
59
+ else:
60
 
61
+ await cl.Message(content=f"You entered: {message}").send()
chainlit.md CHANGED
@@ -1 +1,11 @@
1
- # FILL OUT YOUR CHAINLIT MD HERE WITH A DESCRIPTION OF YOUR APPLICATION
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Engineering Bootcamp Cohort 4
2
+
3
+ This chatbot uses 2 opensource models:
4
+ - LLM - NousResearch/Meta-Llama-3.1-8B-
5
+ - Embeddings - Snowflake/snowflake-arctic-embed-m
6
+
7
+ The models are housed on Hugging Face
8
+
9
+ The document is from the Paul Graham Essays
10
+
11
+ Please ask questions about the document
public/custom_styles.css ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ .message {
2
+ background-color: #E3F2FD !important; /* Light Blue background */
3
+ color: #1A237E !important; /* Dark Indigo text */
4
+ }
5
+
6
+ .MuiToolbar-root {
7
+ background-color: #b7dcf1 !important; /* Medium Blue background */
8
+ }
requirements.txt CHANGED
@@ -41,14 +41,14 @@ jiter==0.5.0
41
  joblib==1.4.2
42
  jsonpatch==1.33
43
  jsonpointer==3.0.0
44
- langchain==0.3.0
45
- langchain-community==0.3.0
46
- langchain-core==0.3.1
47
  langchain-huggingface==0.1.0
48
  langchain-openai==0.2.0
49
  langchain-qdrant==0.1.4
50
  langchain-text-splitters==0.3.0
51
- langsmith==0.1.121
52
  Lazify==0.4.0
53
  MarkupSafe==2.1.5
54
  marshmallow==3.22.0
 
41
  joblib==1.4.2
42
  jsonpatch==1.33
43
  jsonpointer==3.0.0
44
+ langchain==0.3.2
45
+ langchain-community==0.3.1
46
+ langchain-core==0.3.8
47
  langchain-huggingface==0.1.0
48
  langchain-openai==0.2.0
49
  langchain-qdrant==0.1.4
50
  langchain-text-splitters==0.3.0
51
+ # langsmith==0.1.121
52
  Lazify==0.4.0
53
  MarkupSafe==2.1.5
54
  marshmallow==3.22.0
test_chainlit.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import chainlit as cl
2
+
3
+ @cl.on_message
4
+ def main(message: str):
5
+ return cl.Message(content=f"You said: {message}")
utilities/utilities.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import os
3
+ from langchain_core.prompts import PromptTemplate
4
+ from langchain_huggingface.embeddings import HuggingFaceEndpointEmbeddings
5
+ from langchain_community.document_loaders import TextLoader
6
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
7
+ from langchain_community.vectorstores import FAISS
8
+ from operator import itemgetter
9
+ from langchain.schema.output_parser import StrOutputParser
10
+ from langchain.schema.runnable import RunnablePassthrough
11
+ from langchain_huggingface import HuggingFaceEndpoint
12
+ from uuid import uuid4
13
+ from qdrant_client import QdrantClient
14
+ from qdrant_client.http.models import Distance, VectorParams
15
+ from langchain_qdrant import QdrantVectorStore
16
+
17
+ from numpy.linalg import norm
18
+
19
+ def get_rag_prompt():
20
+ rp = """\
21
+ <|start_header_id|>system<|end_header_id|>
22
+ You are a helpful assistant. You answer user questions based on provided context. If you can't answer the question with the provided context, say you don't know.<|eot_id|>
23
+
24
+ <|start_header_id|>user<|end_header_id|>
25
+ User Query:
26
+ {query}
27
+
28
+ Context:
29
+ {context}<|eot_id|>
30
+
31
+ <|start_header_id|>assistant<|end_header_id|>
32
+ """
33
+
34
+ rag_prompt = PromptTemplate.from_template(rp)
35
+ return rag_prompt
36
+
37
+ def process_documents(use_qdrant=False):
38
+ HF_LLM_ENDPOINT= os.environ["HF_LLM_ENDPOINT"]
39
+ HF_EMBED_ENDPOINT = os.environ["HF_EMBED_ENDPOINT"]
40
+ HF_TOKEN = os.environ["HF_TOKEN"]
41
+
42
+ rag_prompt = get_rag_prompt()
43
+ document_loader = TextLoader("./data/paul_graham_essays.txt")
44
+ documents = document_loader.load()
45
+
46
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=30)
47
+ split_documents = text_splitter.split_documents(documents)
48
+
49
+ hf_llm = HuggingFaceEndpoint(
50
+ endpoint_url=HF_LLM_ENDPOINT,
51
+ max_new_tokens=512,
52
+ top_k=10,
53
+ top_p=0.95,
54
+ typical_p=0.95,
55
+ temperature=0.01,
56
+ repetition_penalty=1.03,
57
+ huggingfacehub_api_token=HF_TOKEN
58
+ )
59
+
60
+ hf_embeddings = HuggingFaceEndpointEmbeddings(
61
+ model=HF_EMBED_ENDPOINT,
62
+ task="feature-extraction",
63
+ huggingfacehub_api_token=os.environ["HF_TOKEN"],
64
+ )
65
+ if use_qdrant:
66
+ collection_name = f"pdf_to_parse_{uuid4()}"
67
+ client = QdrantClient(":memory:")
68
+ client.create_collection(
69
+ collection_name=collection_name,
70
+ vectors_config=VectorParams(size=768, distance=Distance.COSINE),
71
+ )
72
+
73
+ vectorstore = QdrantVectorStore(
74
+ client=client,
75
+ collection_name=collection_name,
76
+ embedding=hf_embeddings)
77
+
78
+ print(f"Number of batches: {len(split_documents)/32}")
79
+
80
+ for i in range(0, len(split_documents), 32):
81
+ print(f"processing batch {i/32}")
82
+ if i == 0:
83
+ vectorstore.add_documents(split_documents[i:i+32])
84
+ continue
85
+ vectorstore.add_documents(split_documents[i:i+32])
86
+
87
+ # vectorstore.add_documents(split_documents)
88
+ print("Loaded Vectorstore using Qdrant")
89
+ hf_retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={"k": 3})
90
+ else:
91
+ vectorstore_path = "./data/vectorstore"
92
+ if os.path.exists(vectorstore_path) and os.listdir(vectorstore_path):
93
+ print(f"Reading Faiss vector store from disk - {vectorstore_path}")
94
+ vectorstore = FAISS.load_local(
95
+ vectorstore_path,
96
+ hf_embeddings,
97
+ allow_dangerous_deserialization=True # this is necessary to load the vectorstore from disk as it's stored as a `.pkl` file.
98
+ )
99
+ hf_retriever = vectorstore.as_retriever()
100
+ print("Loaded Vectorstore using Faiss")
101
+ else:
102
+ print("Indexing Files")
103
+ os.makedirs(vectorstore_path, exist_ok=True)
104
+ print(f"Number of batches: {len(split_documents)/32}")
105
+ for i in range(0, len(split_documents), 32):
106
+ print(f"processing batch {i/32}")
107
+ if i == 0:
108
+ vectorstore = FAISS.from_documents(split_documents[i:i+32], hf_embeddings)
109
+ continue
110
+ vectorstore.add_documents(split_documents[i:i+32])
111
+ vectorstore.save_local(vectorstore_path)
112
+ print(f"Faiss vector store saved to disk - {vectorstore_path}")
113
+
114
+ hf_retriever = vectorstore.as_retriever()
115
+
116
+ lcel_rag_chain = {"context": itemgetter("query") | hf_retriever, "query": itemgetter("query")}| rag_prompt | hf_llm
117
+ return lcel_rag_chain
118
+
119
+