File size: 4,395 Bytes
3bfb998
71c4c81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30a4f31
71c4c81
 
467fc86
3bfb998
71c4c81
 
 
 
 
 
3bfb998
71c4c81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bfb998
71c4c81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c20890b
467fc86
3bfb998
467fc86
 
3bfb998
71c4c81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a8ee01e
 
71c4c81
 
 
 
467fc86
3bfb998
467fc86
 
3bfb998
71c4c81
 
467fc86
3bfb998
467fc86
 
 
 
71c4c81
 
 
 
 
3bfb998
71c4c81
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

import os
import uuid
from dotenv import load_dotenv
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyMuPDFLoader
from qdrant_client import QdrantClient
from qdrant_client.http.models import Distance, VectorParams
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain.storage import LocalFileStore
from langchain_qdrant import QdrantVectorStore
from langchain.embeddings import CacheBackedEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from chainlit.types import AskFileResponse
from langchain_core.globals import set_llm_cache
from langchain_openai import ChatOpenAI
from langchain_core.caches import InMemoryCache
from operator import itemgetter
from langchain_core.runnables.passthrough import RunnablePassthrough
import chainlit as cl
from langchain_core.runnables.config import RunnableConfig
import time

load_dotenv()

os.environ["LANGCHAIN_PROJECT"] = f"AIM W8D1 - {uuid.uuid4().hex[0:8]}"
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)

rag_system_prompt_template = """\
You are a helpful assistant. Think through your answers carefully using a step-by-step approach.
"""

rag_message_list = [
    {"role" : "system", "content" : rag_system_prompt_template},
]

rag_user_prompt_template = """\
Question:
{question}
Context:
{context}
"""

chat_prompt = ChatPromptTemplate.from_messages([
    ("system", rag_system_prompt_template),
    ("human", rag_user_prompt_template)
])

chat_model = ChatOpenAI(model="gpt-4o-mini")

core_embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

def process_file(file: AskFileResponse):
    import tempfile

    with tempfile.NamedTemporaryFile(mode="w", delete=False) as tempfile:
        with open(tempfile.name, "wb") as f:
            f.write(file.content)

    Loader = PyMuPDFLoader

    loader = Loader(tempfile.name)
    documents = loader.load()
    docs = text_splitter.split_documents(documents)
    for i, doc in enumerate(docs):
        doc.metadata["source"] = f"source_{i}"
    return docs




@cl.on_chat_start
async def on_chat_start():

    files = None

    while files == None:
        files = await cl.AskFileMessage(
            content="Please upload a PDF file to begin!",
            accept=["application/pdf"],
            max_size_mb=20,
            timeout=180,
            max_files=1
        ).send()

    file = files[0]
    msg = cl.Message(
        content=f"Processing `{file.name}`...",
    )
    await msg.send()
    docs = process_file(file)

    # Typical QDrant Client Set-up
    collection_name = f"pdf_to_parse_{uuid.uuid4()}"
    client = QdrantClient(":memory:")
    client.create_collection(
        collection_name=collection_name,
        vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
    )

    # Adding cache!
    store = LocalFileStore("./cache/")
    cached_embedder = CacheBackedEmbeddings.from_bytes_store(
        core_embeddings, store, namespace=core_embeddings.model
    )

    # Typical QDrant Vector Store Set-up
    vectorstore = QdrantVectorStore(
        client=client,
        collection_name=collection_name,
        embedding=cached_embedder)
    vectorstore.add_documents(docs)
    retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={"k": 3})

    retrieval_augmented_qa_chain = (
        {"context": itemgetter("question") | retriever, "question": itemgetter("question")}
        | RunnablePassthrough.assign(context=itemgetter("context"))
        | chat_prompt | chat_model
    )

    msg.content = f"Processing `{file.name}` done. You can now ask questions!"
    await msg.update()

    cl.user_session.set("chain", retrieval_augmented_qa_chain)
    


@cl.author_rename
def rename(orig_author: str):

    rename_dict = {"ChatOpenAI": "the Generator...", "VectorStoreRetriever": "the Retriever..."}
    return rename_dict.get(orig_author, orig_author)


@cl.on_message
async def main(message: cl.Message):
    """
    MESSAGE CODE HERE
    """
    runnable = cl.user_session.get("chain")

    msg = cl.Message(content="")


    async for chunk in runnable.astream(
        {"question": message.content},
    ):
        await msg.stream_token(chunk.content)

    await msg.send()