philmui commited on
Commit
eafa0ae
·
1 Parent(s): aa4f012

working mem

Browse files
.gitattributes CHANGED
@@ -17,6 +17,7 @@
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
 
20
  *.pickle filter=lfs diff=lfs merge=lfs -text
21
  *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
 
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pdf filter=lfs diff=lfs merge=lfs -text
21
  *.pickle filter=lfs diff=lfs merge=lfs -text
22
  *.pkl filter=lfs diff=lfs merge=lfs -text
23
  *.pt filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ wandb/
2
+ .env
3
+ __pycache__/data/qdrant/*
4
+ *.sqlite
5
+ __pycache__/*
6
+ data/.lock
7
+ data/qdrant
8
+ __pycache__
.vscode/launch.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": "chainlit",
9
+ "type": "debugpy",
10
+ "request": "launch",
11
+ "module": "chainlit",
12
+ "args": [
13
+ "run",
14
+ "/Users/pmui/SynologyDrive/projects/llmops/aie2/homework/midterm/meta-analysis/app.py",
15
+ "-w"
16
+ ],
17
+ "jinja": true
18
+ }
19
+ ]
20
+ }
__pycache__/app.cpython-311.pyc DELETED
Binary file (1.04 kB)
 
app.py CHANGED
@@ -1,17 +1,76 @@
1
  import chainlit as cl
 
 
2
 
3
- @cl.on_chat_start
4
- async def start():
5
- await cl.Message(
6
- content=f"Hello",
7
- ).send()
8
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  @cl.on_message
11
  async def main(message: cl.Message):
 
 
 
 
 
 
 
12
 
13
- content = message.content
14
-
15
  await cl.Message(
16
  content=f"{content}",
17
- ).send()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import chainlit as cl
2
+ import logging
3
+ import sys
4
 
5
+ _logger = logging.getLogger("lang-chat")
6
+
7
+ from langchain_core.prompts import ChatPromptTemplate
8
+ from langchain_core.vectorstores import VectorStore
9
+ from langchain_core.runnables.base import RunnableSequence
10
 
11
+ from globals import (
12
+ DEFAULT_QUESTION1,
13
+ DEFAULT_QUESTION2,
14
+ gpt35_model,
15
+ gpt4_model
16
+ )
17
+
18
+ from semantic import (
19
+ SemanticRAGChainFactory
20
+ )
21
+
22
+ _semantic_rag_chain: RunnableSequence = None
23
 
24
  @cl.on_message
25
  async def main(message: cl.Message):
26
+
27
+ content = ""
28
+ try:
29
+ response = _semantic_rag_chain.invoke({"question": message.content})
30
+ content += response["response"].content
31
+ except Exception as e:
32
+ print(f"chat error: {e}: {vars(_semantic_rag_chain)}")
33
 
34
+ # Send a response back to the user
 
35
  await cl.Message(
36
  content=f"{content}",
37
+ ).send()
38
+
39
+ @cl.on_chat_start
40
+ async def start():
41
+
42
+ print("==> starting ...")
43
+ global _semantic_rag_chain
44
+ _semantic_rag_chain = SemanticRAGChainFactory.get_semantic_rag_chain()
45
+
46
+ # await cl.Avatar(
47
+ # name="Chatbot",
48
+ # url="https://cdn-icons-png.flaticon.com/512/8649/8649595.png"
49
+ # ).send()
50
+ # await cl.Avatar(
51
+ # name="User",
52
+ # url="https://media.architecturaldigest.com/photos/5f241de2c850b2a36b415024/master/w_1600%2Cc_limit/Luke-logo.png"
53
+ # ).send()
54
+
55
+ print("\tsending message back: ready!!!")
56
+
57
+ content = ""
58
+ # if _semantic_rag_chain is not None:
59
+ # try:
60
+ # response1 = _semantic_rag_chain.invoke({"question": DEFAULT_QUESTION1})
61
+ # response2 = _semantic_rag_chain.invoke({"question": DEFAULT_QUESTION2})
62
+
63
+ # content = (
64
+ # f"**Question**: {DEFAULT_QUESTION1}\n\n"
65
+ # f"{response1['response'].content}\n\n"
66
+ # f"**Question**: {DEFAULT_QUESTION2}\n\n"
67
+ # f"{response2['response'].content}\n\n"
68
+ # )
69
+ # except Exception as e:
70
+ # _logger.error(f"init error: {e}")
71
+
72
+ cl.user_session.set("message_history", [{"role": "system", "content": "You are a helpful assistant. "}])
73
+ await cl.Message(
74
+ content=content + "\nHow can I help you with Meta's 2023 10K?"
75
+ ).send()
76
+ print(f"{20 * '*'}")
data/meta-10k-2023.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e8fadc2448e4f99ad0ec2dc2e41d13b864204955238cf1f7cd9c96839f274a6c
3
+ size 2481466
data/meta-1pager.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d6a1929035e18d38e69392a5b36e366efd8dba736fab2dcaae464326212083e
3
+ size 96532
globals.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import find_dotenv, load_dotenv
2
+ load_dotenv(find_dotenv())
3
+
4
+ from langchain_openai import ChatOpenAI
5
+ from langchain_openai import OpenAIEmbeddings
6
+
7
+ GPT4_MODEL_NAME = "gpt-4-turbo-2024-04-09"
8
+ GPT35_MODEL_NAME = "gpt-3.5-turbo-1106"
9
+
10
+ gpt35_model = ChatOpenAI(model=GPT35_MODEL_NAME, temperature=0.0)
11
+ gpt4_model = ChatOpenAI(model=GPT4_MODEL_NAME, temperature=0.0)
12
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
13
+
14
+ DEFAULT_QUESTION1 = "What was the total value of 'Cash and cash equivalents' as of December 31, 2023?"
15
+ DEFAULT_QUESTION2 = "Who are 'Directors' (i.e., members of the Board of Directors) for Meta?"
16
+
17
+ ROOT_PATH = "."
18
+ VECTOR_STORE_PATH = f"{ROOT_PATH}/data/qdrant"
19
+ # META_10K_FILE_PATH = f"{ROOT_PATH}/data/meta-10k-2023.pdf"
20
+ META_10K_FILE_PATH = f"{ROOT_PATH}/data/meta-1pager.pdf"
21
+ META_SEMANTIC_COLLECTION = "meta10k-semantic"
22
+
semantic.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ _logger = logging.getLogger("semantic")
5
+
6
+ from operator import itemgetter
7
+ from langchain_core.prompts import ChatPromptTemplate
8
+ from langchain_core.runnables.base import RunnableSequence
9
+ from langchain_core.vectorstores import VectorStore
10
+ from langchain.retrievers.multi_query import MultiQueryRetriever
11
+ from langchain_community.vectorstores import Qdrant
12
+ from langchain.schema.output_parser import StrOutputParser
13
+ from langchain.schema.runnable import RunnablePassthrough
14
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
15
+ from langchain_community.document_loaders import PyMuPDFLoader
16
+ from langchain_experimental.text_splitter import SemanticChunker
17
+
18
+ from globals import (
19
+ embeddings,
20
+ gpt35_model,
21
+ gpt4_model,
22
+ META_10K_FILE_PATH,
23
+ META_SEMANTIC_COLLECTION,
24
+ VECTOR_STORE_PATH
25
+ )
26
+
27
+
28
+ USE_MEMORY = True
29
+ from qdrant_client import QdrantClient
30
+
31
+ qclient: QdrantClient
32
+ if USE_MEMORY == True:
33
+ qclient = QdrantClient(":memory:")
34
+ else:
35
+ qclient = QdrantClient(path=VECTOR_STORE_PATH)
36
+
37
+
38
+ RAG_PROMPT = """
39
+ Reply the user's query thoughtfully and clearly.
40
+ You should only respond to user's query if the context is related to the query.
41
+ If you are not sure how to answer, please reply "I don't know".
42
+ Respond with structure in markdown.
43
+
44
+ CONTEXT:
45
+ {context}
46
+
47
+ QUERY:
48
+ {question}
49
+
50
+ YOUR REPLY: """
51
+
52
+ rag_prompt = ChatPromptTemplate.from_template(RAG_PROMPT)
53
+
54
+
55
+ class SemanticStoreFactory:
56
+ _semantic_vectorstore: VectorStore = None
57
+
58
+ @classmethod
59
+ def __load_semantic_store(
60
+ cls
61
+ ) -> VectorStore:
62
+ path = Path(VECTOR_STORE_PATH)
63
+ store = None
64
+ # check if path exists and if it is not empty
65
+ if path.exists() and path.is_dir() and any(path.iterdir()):
66
+ _logger.info(f"\tQdrant loading ...")
67
+ store = Qdrant(
68
+ client=qclient,
69
+ embeddings=embeddings,
70
+ collection_name=META_SEMANTIC_COLLECTION,
71
+ )
72
+ else:
73
+ _logger.info(f"\tQdrant creating ...")
74
+ store = cls.__create_semantic_store()
75
+ return store
76
+
77
+ @classmethod
78
+ def __create_semantic_store(
79
+ cls
80
+ ) -> VectorStore:
81
+
82
+ if USE_MEMORY == True:
83
+ _logger.info(f"creating semantic vector store: {USE_MEMORY}")
84
+ else:
85
+ _logger.info(f"creating semantic vector store: {VECTOR_STORE_PATH}")
86
+ path = Path(VECTOR_STORE_PATH)
87
+ if not path.exists():
88
+ path.mkdir(parents=True, exist_ok=True)
89
+ _logger.info(f"Directory '{path}' created.")
90
+
91
+ documents = PyMuPDFLoader(META_10K_FILE_PATH).load()
92
+ semantic_chunker = SemanticChunker(
93
+ embeddings=embeddings,
94
+ breakpoint_threshold_type="percentile"
95
+ )
96
+ semantic_chunks = semantic_chunker.create_documents(
97
+ [d.page_content for d in documents]
98
+ )
99
+ _logger.info(f"created semantic_chunks: {len(semantic_chunks)}")
100
+ if USE_MEMORY == True:
101
+ _logger.info(f"\t==> creating memory vectorstore ...")
102
+ semantic_chunk_vectorstore = Qdrant.from_documents(
103
+ semantic_chunks,
104
+ embeddings,
105
+ location=":memory:",
106
+ collection_name=META_SEMANTIC_COLLECTION,
107
+ force_recreate=True
108
+ )
109
+ _logger.info(f"\t==> finished constructing vectorstore")
110
+ else:
111
+ semantic_chunk_vectorstore = Qdrant.from_documents(
112
+ semantic_chunks,
113
+ embeddings,
114
+ path=VECTOR_STORE_PATH,
115
+ collection_name=META_SEMANTIC_COLLECTION,
116
+ force_recreate=True
117
+ )
118
+ _logger.info(f"\t==> return vectorstore {META_SEMANTIC_COLLECTION}")
119
+
120
+ return semantic_chunk_vectorstore
121
+
122
+ @classmethod
123
+ def get_semantic_store(
124
+ cls
125
+ ) -> VectorStore:
126
+ _logger.info(f"get_semantic_store")
127
+ if cls._semantic_vectorstore is None:
128
+ if USE_MEMORY == True:
129
+ cls._semantic_vectorstore = cls.__create_semantic_store()
130
+ _logger.info(f"received semantic_vectorstore")
131
+ else:
132
+ print(f"Loading semantic vectorstore {META_SEMANTIC_COLLECTION} from: {VECTOR_STORE_PATH}")
133
+ try:
134
+ # first try to load the store
135
+ cls._semantic_vectorstore = cls.__load_semantic_store()
136
+ except Exception as e:
137
+ _logger.warning(f"cannot load: {e}")
138
+ cls._semantic_vectorstore = cls.__create_semantic_store()
139
+
140
+ _logger.info(f"RETURNING get_semantic_store")
141
+ return cls._semantic_vectorstore
142
+
143
+ class SemanticRAGChainFactory:
144
+ _chain: RunnableSequence = None
145
+
146
+ @classmethod
147
+ def get_semantic_rag_chain(
148
+ cls
149
+ ) -> RunnableSequence:
150
+ if cls._chain is None:
151
+ _logger.info(f"creating SemanticRAGChainFactory")
152
+ semantic_store = SemanticStoreFactory.get_semantic_store()
153
+ if semantic_store is not None:
154
+ semantic_chunk_retriever = semantic_store.as_retriever()
155
+ semantic_mquery_retriever = MultiQueryRetriever.from_llm(
156
+ retriever=semantic_chunk_retriever,
157
+ llm=gpt4_model
158
+ )
159
+ cls._chain = (
160
+ # INVOKE CHAIN WITH: {"question" : "<<SOME USER QUESTION>>"}
161
+ # "question" : populated by getting the value of the "question" key
162
+ # "context" : populated by getting the value of the "question" key and chaining it into the base_retriever
163
+ {"context": itemgetter("question") | semantic_mquery_retriever, "question": itemgetter("question")}
164
+ # "context" : is assigned to a RunnablePassthrough object (will not be called or considered in the next step)
165
+ # by getting the value of the "context" key from the previous step
166
+ | RunnablePassthrough.assign(context=itemgetter("context"))
167
+ # "response" : the "context" and "question" values are used to format our prompt object and then piped
168
+ # into the LLM and stored in a key called "response"
169
+ # "context" : populated by getting the value of the "context" key from the previous step
170
+ | {"response": rag_prompt | gpt4_model, "context": itemgetter("context")}
171
+ )
172
+ _logger.info(f"\t_chain constructed")
173
+
174
+ return cls._chain