jeevan commited on
Commit
06597dd
·
1 Parent(s): 2316238

First commit

Browse files
Files changed (4) hide show
  1. .gitignore +2 -0
  2. Dockerfile +11 -0
  3. app.py +184 -0
  4. requirements.txt +100 -0
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ venv/
2
+ .vscode/
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 ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Import Section ###
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
+ from langchain_community.llms import HuggingFaceEndpoint
26
+ from langchain_huggingface.embeddings import HuggingFaceEndpointEmbeddings
27
+ from langchain_core.prompts import PromptTemplate
28
+
29
+ import numpy as np
30
+ from numpy.linalg import norm
31
+
32
+ load_dotenv()
33
+
34
+ ### Global Section ###
35
+ """
36
+ GLOBAL CODE HERE
37
+ """
38
+
39
+ RAG_PROMPT_TEMPLATE = """\
40
+ <|start_header_id|>system<|end_header_id|>
41
+ 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|>
42
+
43
+ <|start_header_id|>user<|end_header_id|>
44
+ User Query:
45
+ {query}
46
+
47
+ Context:
48
+ {context}<|eot_id|>
49
+
50
+ <|start_header_id|>assistant<|end_header_id|>
51
+ """
52
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
53
+ hf_llm = HuggingFaceEndpoint(
54
+ endpoint_url=f"{os.environ["YOUR_LLM_ENDPOINT_URL"]}",
55
+ max_new_tokens=512,
56
+ top_k=10,
57
+ top_p=0.95,
58
+ typical_p=0.95,
59
+ temperature=0.01,
60
+ repetition_penalty=1.03,
61
+ huggingfacehub_api_token=os.environ["HF_TOKEN"]
62
+ )
63
+ hf_embeddings = HuggingFaceEndpointEmbeddings(
64
+ model=os.environ["YOUR_EMBED_MODEL_URL"],
65
+ task="feature-extraction",
66
+ huggingfacehub_api_token=os.environ["HF_TOKEN"],
67
+ )
68
+
69
+ rag_prompt = PromptTemplate.from_template(RAG_PROMPT_TEMPLATE)
70
+ rag_chain = rag_prompt | hf_llm
71
+
72
+ def cosine_similarity(phrase_1, phrase_2):
73
+ vec_1 = hf_embeddings.embed_documents([phrase_1])[0]
74
+ vec2_2 = hf_embeddings.embed_documents([phrase_2])[0]
75
+ return np.dot(vec_1, vec2_2) / (norm(vec_1) * norm(vec2_2))
76
+
77
+ def process_file(file: AskFileResponse):
78
+ import tempfile
79
+
80
+ with tempfile.NamedTemporaryFile(mode="w", delete=False) as tempfile:
81
+ with open(tempfile.name, "wb") as f:
82
+ f.write(file.content)
83
+
84
+ Loader = PyMuPDFLoader
85
+
86
+ loader = Loader(tempfile.name)
87
+ documents = loader.load()
88
+ docs = text_splitter.split_documents(documents)
89
+ for i, doc in enumerate(docs):
90
+ doc.metadata["source"] = f"source_{i}"
91
+ return docs
92
+
93
+
94
+ ### On Chat Start (Session Start) Section ###
95
+ @cl.on_chat_start
96
+ async def on_chat_start():
97
+ """ SESSION SPECIFIC CODE HERE """
98
+ files = None
99
+
100
+ while files == None:
101
+ # Async method: This allows the function to pause execution while waiting for the user to upload a file,
102
+ # without blocking the entire application. It improves responsiveness and scalability.
103
+ files = await cl.AskFileMessage(
104
+ content="Please upload a PDF file to begin!",
105
+ accept=["application/pdf"],
106
+ max_size_mb=20,
107
+ timeout=180,
108
+ max_files=1
109
+ ).send()
110
+
111
+ file = files[0]
112
+ msg = cl.Message(
113
+ content=f"Processing `{file.name}`...",
114
+ )
115
+ await msg.send()
116
+ docs = process_file(file)
117
+
118
+ # Typical QDrant Client Set-up
119
+ collection_name = f"pdf_to_parse_{uuid.uuid4()}"
120
+ client = QdrantClient(":memory:")
121
+ client.create_collection(
122
+ collection_name=collection_name,
123
+ vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
124
+ )
125
+
126
+ # Adding cache!
127
+ store = LocalFileStore("./cache/")
128
+ cached_embedder = CacheBackedEmbeddings.from_bytes_store(
129
+ hf_embeddings, store, namespace=hf_embeddings.model
130
+ )
131
+
132
+ # Typical QDrant Vector Store Set-up
133
+ vectorstore = QdrantVectorStore(
134
+ client=client,
135
+ collection_name=collection_name,
136
+ embedding=cached_embedder)
137
+
138
+ for i in range(0, len(docs), 32):
139
+ if i == 0:
140
+ vectorstore = docs.from_documents(docs[i:i+32], hf_embeddings)
141
+ continue
142
+ vectorstore.add_documents(docs[i:i+32])
143
+
144
+ retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={"k": 3})
145
+
146
+ retrieval_augmented_qa_chain = (
147
+ {"context": itemgetter("question") | retriever, "question": itemgetter("question")}
148
+ | RunnablePassthrough.assign(context=itemgetter("context"))
149
+ | rag_prompt | hf_llm
150
+ )
151
+
152
+ # Let the user know that the system is ready
153
+ msg.content = f"Processing `{file.name}` done. You can now ask questions!"
154
+ await msg.update()
155
+
156
+ cl.user_session.set("chain", retrieval_augmented_qa_chain)
157
+
158
+
159
+ ### Rename Chains ###
160
+ @cl.author_rename
161
+ def rename(orig_author: str):
162
+ """ RENAME CODE HERE """
163
+ rename_dict = {"ChatOpenAI": "the Generator...", "VectorStoreRetriever": "the Retriever..."}
164
+ return rename_dict.get(orig_author, orig_author)
165
+
166
+ ### On Message Section ###
167
+ @cl.on_message
168
+ async def main(message: cl.Message):
169
+ """
170
+ MESSAGE CODE HERE
171
+ """
172
+ runnable = cl.user_session.get("chain")
173
+
174
+ msg = cl.Message(content="")
175
+
176
+ # Async method: Using astream allows for asynchronous streaming of the response,
177
+ # improving responsiveness and user experience by showing partial results as they become available.
178
+ async for chunk in runnable.astream(
179
+ {"question": message.content},
180
+ config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]),
181
+ ):
182
+ await msg.stream_token(chunk.content)
183
+
184
+ await msg.send()
requirements.txt ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain_huggingface==0.0.3
2
+ aiofiles==23.2.1
3
+ aiohappyeyeballs==2.4.3
4
+ aiohttp==3.10.8
5
+ aiosignal==1.3.1
6
+ annotated-types==0.7.0
7
+ anyio==3.7.1
8
+ async-timeout==4.0.3
9
+ asyncer==0.0.2
10
+ attrs==24.2.0
11
+ bidict==0.23.1
12
+ certifi==2024.8.30
13
+ chainlit==0.7.700
14
+ charset-normalizer==3.3.2
15
+ click==8.1.7
16
+ dataclasses-json==0.5.14
17
+ Deprecated==1.2.14
18
+ distro==1.9.0
19
+ exceptiongroup==1.2.2
20
+ fastapi==0.100.1
21
+ fastapi-socketio==0.0.10
22
+ filetype==1.2.0
23
+ frozenlist==1.4.1
24
+ googleapis-common-protos==1.65.0
25
+ greenlet==3.1.1
26
+ grpcio==1.66.2
27
+ grpcio-tools==1.62.3
28
+ h11==0.14.0
29
+ h2==4.1.0
30
+ hpack==4.0.0
31
+ httpcore==0.17.3
32
+ httpx==0.24.1
33
+ hyperframe==6.0.1
34
+ idna==3.10
35
+ importlib_metadata==8.4.0
36
+ jiter==0.5.0
37
+ jsonpatch==1.33
38
+ jsonpointer==3.0.0
39
+ langchain==0.3.0
40
+ langchain-community==0.3.0
41
+ langchain-core==0.3.1
42
+ langchain-openai==0.2.0
43
+ langchain-qdrant==0.1.4
44
+ langchain-text-splitters==0.3.0
45
+ langsmith==0.1.121
46
+ Lazify==0.4.0
47
+ marshmallow==3.22.0
48
+ multidict==6.1.0
49
+ mypy-extensions==1.0.0
50
+ nest-asyncio==1.6.0
51
+ numpy==1.26.4
52
+ openai==1.51.0
53
+ opentelemetry-api==1.27.0
54
+ opentelemetry-exporter-otlp==1.27.0
55
+ opentelemetry-exporter-otlp-proto-common==1.27.0
56
+ opentelemetry-exporter-otlp-proto-grpc==1.27.0
57
+ opentelemetry-exporter-otlp-proto-http==1.27.0
58
+ opentelemetry-instrumentation==0.48b0
59
+ opentelemetry-proto==1.27.0
60
+ opentelemetry-sdk==1.27.0
61
+ opentelemetry-semantic-conventions==0.48b0
62
+ orjson==3.10.7
63
+ packaging==23.2
64
+ portalocker==2.10.1
65
+ protobuf==4.25.5
66
+ pydantic==2.9.2
67
+ pydantic-settings==2.5.2
68
+ pydantic_core==2.23.4
69
+ PyJWT==2.9.0
70
+ PyMuPDF==1.24.10
71
+ PyMuPDFb==1.24.10
72
+ python-dotenv==1.0.1
73
+ python-engineio==4.9.1
74
+ python-graphql-client==0.4.3
75
+ python-multipart==0.0.6
76
+ python-socketio==5.11.4
77
+ PyYAML==6.0.2
78
+ qdrant-client==1.11.2
79
+ regex==2024.9.11
80
+ requests==2.32.3
81
+ simple-websocket==1.0.0
82
+ sniffio==1.3.1
83
+ SQLAlchemy==2.0.35
84
+ starlette==0.27.0
85
+ syncer==2.0.3
86
+ tenacity==8.5.0
87
+ tiktoken==0.7.0
88
+ tomli==2.0.1
89
+ tqdm==4.66.5
90
+ typing-inspect==0.9.0
91
+ typing_extensions==4.12.2
92
+ uptrace==1.26.0
93
+ urllib3==2.2.3
94
+ uvicorn==0.23.2
95
+ watchfiles==0.20.0
96
+ websockets==13.1
97
+ wrapt==1.16.0
98
+ wsproto==1.2.0
99
+ yarl==1.13.1
100
+ zipp==3.20.2