acpotts commited on
Commit
d0a8589
1 Parent(s): 521766e
Files changed (3) hide show
  1. Dockerfile +11 -0
  2. app.py +131 -22
  3. requirements.txt +33 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+ RUN useradd -m -u 1000 user
3
+ USER user
4
+ ENV HOME=/home/user \
5
+ PATH=/home/user/.local/bin:$PATH
6
+ WORKDIR $HOME/app
7
+ 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", "app.py", "--port", "7860"]
app.py CHANGED
@@ -2,54 +2,163 @@
2
  """
3
  IMPORTS HERE
4
  """
5
- # Example Imports (adjust based on actual needs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  import chainlit as cl
7
- from langchain.chat_models import ChatOpenAI
8
- from langchain.chains import ConversationChain
9
- from langchain.prompts import ChatPromptTemplate
10
- from langchain.schema import StrOutputParser
11
- from langchain.schema.runnable import Runnable
12
- from langchain.schema.runnable.config import RunnableConfig
13
- from typing import cast
14
 
15
  ### Global Section ###
16
  """
17
  GLOBAL CODE HERE
18
  """
19
- # Initialize a language model or chain globally
20
- llm = ChatOpenAI(temperature=0.9)
21
- conversation_chain = ConversationChain(llm=llm)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- # Any global variables like API keys, configurations, etc.
24
- # API_KEY = "your_api_key_here"
 
 
 
 
 
 
 
 
 
 
25
 
26
 
27
  ### On Chat Start (Session Start) Section ###
28
  @cl.on_chat_start
29
  async def on_chat_start():
30
  """ SESSION SPECIFIC CODE HERE """
31
- await cl.Message(content="Welcome! How can I assist you today?").send()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  ### Rename Chains ###
34
  @cl.author_rename
35
  def rename(orig_author: str):
36
- if orig_author == "user":
37
- return "You"
38
- elif orig_author == "system":
39
- return "Assistant"
40
- return orig_author
41
 
42
  ### On Message Section ###
43
  @cl.on_message
44
- async def on_message(message: cl.Message):
45
- runnable = cast(Runnable, cl.user_session.get("runnable"))
 
 
 
46
 
47
  msg = cl.Message(content="")
48
 
 
 
49
  async for chunk in runnable.astream(
50
  {"question": message.content},
51
  config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]),
52
  ):
53
- await msg.stream_token(chunk)
54
 
55
  await msg.send()
 
2
  """
3
  IMPORTS HERE
4
  """
5
+ import os
6
+ import uuid
7
+ from dotenv import load_dotenv
8
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
9
+ from langchain_community.document_loaders import PyMuPDFLoader
10
+ from qdrant_client import QdrantClient
11
+ from qdrant_client.http.models import Distance, VectorParams
12
+ from langchain_openai.embeddings import OpenAIEmbeddings
13
+ from langchain.storage import LocalFileStore
14
+ from langchain_qdrant import QdrantVectorStore
15
+ from langchain.embeddings import CacheBackedEmbeddings
16
+ from langchain_core.prompts import ChatPromptTemplate
17
+ from chainlit.types import AskFileResponse
18
+ from langchain_core.globals import set_llm_cache
19
+ from langchain_openai import ChatOpenAI
20
+ from langchain_core.caches import InMemoryCache
21
+ from operator import itemgetter
22
+ from langchain_core.runnables.passthrough import RunnablePassthrough
23
  import chainlit as cl
24
+ from langchain_core.runnables.config import RunnableConfig
25
+
26
+ load_dotenv()
 
 
 
 
27
 
28
  ### Global Section ###
29
  """
30
  GLOBAL CODE HERE
31
  """
32
+ os.environ["LANGCHAIN_PROJECT"] = f"AIM Week 8 Assignment 1 - {uuid.uuid4().hex[0:8]}"
33
+ os.environ["LANGCHAIN_TRACING_V2"] = "true"
34
+ os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
35
+
36
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
37
+
38
+ rag_system_prompt_template = """\
39
+ You are a helpful assistant that uses the provided context to answer questions.
40
+ Never reference this prompt, or the existance of context.
41
+ """
42
+
43
+ rag_message_list = [
44
+ {"role" : "system", "content" : rag_system_prompt_template},
45
+ ]
46
+
47
+ rag_user_prompt_template = """\
48
+ Question:
49
+ {question}
50
+ Context:
51
+ {context}
52
+ """
53
+
54
+ chat_prompt = ChatPromptTemplate.from_messages([
55
+ ("system", rag_system_prompt_template),
56
+ ("human", rag_user_prompt_template)
57
+ ])
58
+
59
+ chat_model = ChatOpenAI(model="gpt-4o-mini")
60
+ # Typical Embedding Model
61
+ core_embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
62
+
63
+ def process_file(file: AskFileResponse):
64
+ import tempfile
65
 
66
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tempfile:
67
+ with open(tempfile.name, "wb") as f:
68
+ f.write(file.content)
69
+
70
+ Loader = PyMuPDFLoader
71
+
72
+ loader = Loader(tempfile.name)
73
+ documents = loader.load()
74
+ docs = text_splitter.split_documents(documents)
75
+ for i, doc in enumerate(docs):
76
+ doc.metadata["source"] = f"source_{i}"
77
+ return docs
78
 
79
 
80
  ### On Chat Start (Session Start) Section ###
81
  @cl.on_chat_start
82
  async def on_chat_start():
83
  """ SESSION SPECIFIC CODE HERE """
84
+ files = None
85
+
86
+ while files == None:
87
+ # Async method: This allows the function to pause execution while waiting for the user to upload a file,
88
+ # without blocking the entire application. It improves responsiveness and scalability.
89
+ files = await cl.AskFileMessage(
90
+ content="Please upload a PDF file to begin!",
91
+ accept=["application/pdf"],
92
+ max_size_mb=20,
93
+ timeout=180,
94
+ max_files=1
95
+ ).send()
96
+
97
+ file = files[0]
98
+ msg = cl.Message(
99
+ content=f"Processing `{file.name}`...",
100
+ )
101
+ await msg.send()
102
+ docs = process_file(file)
103
+
104
+ # Typical QDrant Client Set-up
105
+ collection_name = f"pdf_to_parse_{uuid.uuid4()}"
106
+ client = QdrantClient(":memory:")
107
+ client.create_collection(
108
+ collection_name=collection_name,
109
+ vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
110
+ )
111
+
112
+ # Adding cache!
113
+ store = LocalFileStore("./cache/")
114
+ cached_embedder = CacheBackedEmbeddings.from_bytes_store(
115
+ core_embeddings, store, namespace=core_embeddings.model
116
+ )
117
+
118
+ # Typical QDrant Vector Store Set-up
119
+ vectorstore = QdrantVectorStore(
120
+ client=client,
121
+ collection_name=collection_name,
122
+ embedding=cached_embedder)
123
+ vectorstore.add_documents(docs)
124
+ retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={"k": 3})
125
+
126
+ retrieval_augmented_qa_chain = (
127
+ {"context": itemgetter("question") | retriever, "question": itemgetter("question")}
128
+ | RunnablePassthrough.assign(context=itemgetter("context"))
129
+ | chat_prompt | chat_model
130
+ )
131
+
132
+ # Let the user know that the system is ready
133
+ msg.content = f"Processing `{file.name}` done. You can now ask questions!"
134
+ await msg.update()
135
+
136
+ cl.user_session.set("chain", retrieval_augmented_qa_chain)
137
+
138
 
139
  ### Rename Chains ###
140
  @cl.author_rename
141
  def rename(orig_author: str):
142
+ """ RENAME CODE HERE """
143
+ rename_dict = {"ChatOpenAI": "the Generator...", "VectorStoreRetriever": "the Retriever..."}
144
+ return rename_dict.get(orig_author, orig_author)
 
 
145
 
146
  ### On Message Section ###
147
  @cl.on_message
148
+ async def main(message: cl.Message):
149
+ """
150
+ MESSAGE CODE HERE
151
+ """
152
+ runnable = cl.user_session.get("chain")
153
 
154
  msg = cl.Message(content="")
155
 
156
+ # Async method: Using astream allows for asynchronous streaming of the response,
157
+ # improving responsiveness and user experience by showing partial results as they become available.
158
  async for chunk in runnable.astream(
159
  {"question": message.content},
160
  config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]),
161
  ):
162
+ await msg.stream_token(chunk.content)
163
 
164
  await msg.send()
requirements.txt CHANGED
@@ -16,10 +16,13 @@ dataclasses-json==0.5.14
16
  Deprecated==1.2.14
17
  distro==1.9.0
18
  exceptiongroup==1.2.2
 
19
  fastapi==0.100.1
20
  fastapi-socketio==0.0.10
 
21
  filetype==1.2.0
22
  frozenlist==1.4.1
 
23
  googleapis-common-protos==1.65.0
24
  greenlet==3.1.1
25
  grpcio==1.66.2
@@ -29,25 +32,44 @@ h2==4.1.0
29
  hpack==4.0.0
30
  httpcore==0.17.3
31
  httpx==0.24.1
 
32
  hyperframe==6.0.1
33
  idna==3.10
34
  importlib_metadata==8.4.0
 
35
  jiter==0.5.0
 
36
  jsonpatch==1.33
37
  jsonpointer==3.0.0
38
  langchain==0.3.0
39
  langchain-community==0.3.0
40
  langchain-core==0.3.1
 
41
  langchain-openai==0.2.0
42
  langchain-qdrant==0.1.4
43
  langchain-text-splitters==0.3.0
44
  langsmith==0.1.121
45
  Lazify==0.4.0
 
46
  marshmallow==3.22.0
 
47
  multidict==6.1.0
48
  mypy-extensions==1.0.0
49
  nest-asyncio==1.6.0
 
50
  numpy==1.26.4
 
 
 
 
 
 
 
 
 
 
 
 
51
  openai==1.51.0
52
  opentelemetry-api==1.27.0
53
  opentelemetry-exporter-otlp==1.27.0
@@ -60,6 +82,7 @@ opentelemetry-sdk==1.27.0
60
  opentelemetry-semantic-conventions==0.48b0
61
  orjson==3.10.7
62
  packaging==23.2
 
63
  portalocker==2.10.1
64
  protobuf==4.25.5
65
  pydantic==2.9.2
@@ -77,15 +100,25 @@ PyYAML==6.0.2
77
  qdrant-client==1.11.2
78
  regex==2024.9.11
79
  requests==2.32.3
 
 
 
 
80
  simple-websocket==1.0.0
81
  sniffio==1.3.1
82
  SQLAlchemy==2.0.35
83
  starlette==0.27.0
 
84
  syncer==2.0.3
85
  tenacity==8.5.0
 
86
  tiktoken==0.7.0
 
87
  tomli==2.0.1
 
88
  tqdm==4.66.5
 
 
89
  typing-inspect==0.9.0
90
  typing_extensions==4.12.2
91
  uptrace==1.26.0
 
16
  Deprecated==1.2.14
17
  distro==1.9.0
18
  exceptiongroup==1.2.2
19
+ faiss-cpu==1.8.0.post1
20
  fastapi==0.100.1
21
  fastapi-socketio==0.0.10
22
+ filelock==3.16.1
23
  filetype==1.2.0
24
  frozenlist==1.4.1
25
+ fsspec==2024.9.0
26
  googleapis-common-protos==1.65.0
27
  greenlet==3.1.1
28
  grpcio==1.66.2
 
32
  hpack==4.0.0
33
  httpcore==0.17.3
34
  httpx==0.24.1
35
+ huggingface-hub==0.25.1
36
  hyperframe==6.0.1
37
  idna==3.10
38
  importlib_metadata==8.4.0
39
+ Jinja2==3.1.4
40
  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
55
+ mpmath==1.3.0
56
  multidict==6.1.0
57
  mypy-extensions==1.0.0
58
  nest-asyncio==1.6.0
59
+ networkx==3.2.1
60
  numpy==1.26.4
61
+ nvidia-cublas-cu12==12.1.3.1
62
+ nvidia-cuda-cupti-cu12==12.1.105
63
+ nvidia-cuda-nvrtc-cu12==12.1.105
64
+ nvidia-cuda-runtime-cu12==12.1.105
65
+ nvidia-cudnn-cu12==9.1.0.70
66
+ nvidia-cufft-cu12==11.0.2.54
67
+ nvidia-curand-cu12==10.3.2.106
68
+ nvidia-cusolver-cu12==11.4.5.107
69
+ nvidia-cusparse-cu12==12.1.0.106
70
+ nvidia-nccl-cu12==2.20.5
71
+ nvidia-nvjitlink-cu12==12.6.77
72
+ nvidia-nvtx-cu12==12.1.105
73
  openai==1.51.0
74
  opentelemetry-api==1.27.0
75
  opentelemetry-exporter-otlp==1.27.0
 
82
  opentelemetry-semantic-conventions==0.48b0
83
  orjson==3.10.7
84
  packaging==23.2
85
+ pillow==10.4.0
86
  portalocker==2.10.1
87
  protobuf==4.25.5
88
  pydantic==2.9.2
 
100
  qdrant-client==1.11.2
101
  regex==2024.9.11
102
  requests==2.32.3
103
+ safetensors==0.4.5
104
+ scikit-learn==1.5.2
105
+ scipy==1.13.1
106
+ sentence-transformers==3.1.1
107
  simple-websocket==1.0.0
108
  sniffio==1.3.1
109
  SQLAlchemy==2.0.35
110
  starlette==0.27.0
111
+ sympy==1.13.3
112
  syncer==2.0.3
113
  tenacity==8.5.0
114
+ threadpoolctl==3.5.0
115
  tiktoken==0.7.0
116
+ tokenizers==0.20.0
117
  tomli==2.0.1
118
+ torch==2.4.1
119
  tqdm==4.66.5
120
+ transformers==4.45.1
121
+ triton==3.0.0
122
  typing-inspect==0.9.0
123
  typing_extensions==4.12.2
124
  uptrace==1.26.0