Spaces:
Sleeping
Sleeping
whymath
commited on
Commit
•
b6ce469
1
Parent(s):
af821b6
Adding base files for Chainlit app with RAG pipeline
Browse files- Dockerfile +11 -0
- app.py +37 -0
- chainlit.md +6 -0
- requirements.txt +13 -0
- utils.py +69 -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
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import chainlit as cl
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
import utils
|
5 |
+
|
6 |
+
|
7 |
+
load_dotenv()
|
8 |
+
|
9 |
+
|
10 |
+
@cl.on_chat_start
|
11 |
+
async def start_chat():
|
12 |
+
# Create the RAQA chain and store it in the user session
|
13 |
+
raqa_chain = utils.create_raqa_chain_from_docs()
|
14 |
+
settings = {
|
15 |
+
"chain": raqa_chain
|
16 |
+
}
|
17 |
+
cl.user_session.set("settings", settings)
|
18 |
+
|
19 |
+
|
20 |
+
@cl.on_message
|
21 |
+
async def main(message: cl.Message):
|
22 |
+
# Print the message content
|
23 |
+
user_query = message.content
|
24 |
+
print('user_query =', user_query)
|
25 |
+
|
26 |
+
# Get the chain from the user session
|
27 |
+
settings = cl.user_session.get("settings")
|
28 |
+
raqa_chain = settings["chain"]
|
29 |
+
|
30 |
+
# Generate the response from the chain
|
31 |
+
query_response = raqa_chain.invoke({"question" : user_query})
|
32 |
+
query_answer = query_response["response"].content
|
33 |
+
print('query_answer =', query_answer)
|
34 |
+
|
35 |
+
# Create and send the message stream
|
36 |
+
msg = cl.Message(content=query_answer)
|
37 |
+
await msg.send()
|
chainlit.md
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# RAG Pipeline Prototype
|
3 |
+
|
4 |
+
This app uses a RAG pipeline to prototype a virtual student for the teach-to-learn approach, as part of the AIM AIE2 Demo Day Project.
|
5 |
+
|
6 |
+
*By Yohan Mathew & Jerry Chiang*
|
requirements.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ipykernel
|
2 |
+
numpy
|
3 |
+
pandas
|
4 |
+
langchain
|
5 |
+
langchain-core
|
6 |
+
langchain-community
|
7 |
+
langchain-openai
|
8 |
+
qdrant-client
|
9 |
+
tiktoken
|
10 |
+
pymupdf
|
11 |
+
wandb
|
12 |
+
chainlit
|
13 |
+
huggingface_hub
|
utils.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import tiktoken
|
2 |
+
from langchain.document_loaders import PyMuPDFLoader
|
3 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
4 |
+
from langchain_openai.embeddings import OpenAIEmbeddings
|
5 |
+
from langchain_community.vectorstores import Qdrant
|
6 |
+
from langchain_core.prompts import ChatPromptTemplate
|
7 |
+
from langchain_openai import ChatOpenAI
|
8 |
+
from operator import itemgetter
|
9 |
+
# from langchain.schema.output_parser import StrOutputParser
|
10 |
+
from langchain.schema.runnable import RunnablePassthrough
|
11 |
+
|
12 |
+
|
13 |
+
def tiktoken_len(text):
|
14 |
+
tokens = tiktoken.encoding_for_model("gpt-3.5-turbo").encode(
|
15 |
+
text,
|
16 |
+
)
|
17 |
+
return len(tokens)
|
18 |
+
|
19 |
+
|
20 |
+
def chunk_documents(docs, tiktoken_len):
|
21 |
+
text_splitter = RecursiveCharacterTextSplitter(
|
22 |
+
chunk_size = 200,
|
23 |
+
chunk_overlap = 0,
|
24 |
+
length_function = tiktoken_len,
|
25 |
+
)
|
26 |
+
split_chunks = text_splitter.split_documents(docs)
|
27 |
+
print('len(split_chunks) =', len(split_chunks))
|
28 |
+
return split_chunks
|
29 |
+
|
30 |
+
|
31 |
+
def create_raqa_chain_from_docs():
|
32 |
+
|
33 |
+
# Load the documents from a PDF file using PyMuPDFLoader
|
34 |
+
docs = PyMuPDFLoader("https://d18rn0p25nwr6d.cloudfront.net/CIK-0001326801/c7318154-f6ae-4866-89fa-f0c589f2ee3d.pdf").load() # TODO: Update this to enable user to upload PDF
|
35 |
+
print("Loaded", len(docs), "documents")
|
36 |
+
print(docs[0])
|
37 |
+
|
38 |
+
# Create a Qdrant vector store from the split chunks and embedding model, and obtain its retriever
|
39 |
+
split_chunks = chunk_documents(docs, tiktoken_len)
|
40 |
+
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
|
41 |
+
qdrant_vectorstore = Qdrant.from_documents(
|
42 |
+
split_chunks,
|
43 |
+
embedding_model,
|
44 |
+
location=":memory:",
|
45 |
+
collection_name="LoadedPDF",
|
46 |
+
)
|
47 |
+
qdrant_retriever = qdrant_vectorstore.as_retriever()
|
48 |
+
|
49 |
+
# Define the RAG prompt template
|
50 |
+
RAG_PROMPT = """
|
51 |
+
CONTEXT:
|
52 |
+
{context}
|
53 |
+
|
54 |
+
QUERY:
|
55 |
+
{question}
|
56 |
+
|
57 |
+
Use the provided context to answer the provided user query. Only use the provided context to answer the query. If you do not know the answer, respond with "I don't know".
|
58 |
+
"""
|
59 |
+
rag_prompt = ChatPromptTemplate.from_template(RAG_PROMPT)
|
60 |
+
|
61 |
+
# Create the retrieval augmented QA chain using the Qdrant retriever, RAG prompt, and OpenAI chat model
|
62 |
+
openai_chat_model = ChatOpenAI(model="gpt-3.5-turbo")
|
63 |
+
retrieval_augmented_qa_chain = (
|
64 |
+
{"context": itemgetter("question") | qdrant_retriever, "question": itemgetter("question")}
|
65 |
+
| RunnablePassthrough.assign(context=itemgetter("context"))
|
66 |
+
| {"response": rag_prompt | openai_chat_model, "context": itemgetter("context")}
|
67 |
+
)
|
68 |
+
|
69 |
+
return retrieval_augmented_qa_chain
|