Spaces:
Runtime error
Runtime error
JoshuaKelleyDs
commited on
Commit
•
c63c9d3
1
Parent(s):
75db28a
Update app.py
Browse files
app.py
CHANGED
@@ -10,6 +10,66 @@ from langchain_community.retrievers import BM25Retriever # for the BM25 retrieve
|
|
10 |
from langchain.retrievers.ensemble import EnsembleRetriever # for the ensemble retriever
|
11 |
from langchain_text_splitters import RecursiveCharacterTextSplitter # for the text splitter
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
async def create_youtube_transcription(youtube_url: str) -> List[langchain_core.documents.Document]:
|
14 |
"""
|
15 |
Create a youtube transcription from a youtube url
|
@@ -28,7 +88,10 @@ async def create_youtube_transcription(youtube_url: str) -> List[langchain_core.
|
|
28 |
except Exception as e:
|
29 |
await cl.Message(content=f"failed to load youtube video: {e} Please refresh the page").send() # display the error if we failed to load the youtube video
|
30 |
|
31 |
-
|
|
|
|
|
|
|
32 |
"""
|
33 |
Create a text splitter from a list of documents
|
34 |
More Info: ument_transformers/recursive_text_splitter/
|
@@ -88,58 +151,4 @@ async def create_ensemble_retriever(vector_db:FAISS, bm25:BM25Retriever) -> Ense
|
|
88 |
ensemble_retreiver = EnsembleRetriever(retrievers=[vector_db.as_retriever(), bm25], weights=[.3, .7]) # 30% semantic, 70% keyword retrieval
|
89 |
return ensemble_retreiver
|
90 |
except Exception as e:
|
91 |
-
await cl.Message(content=f"failed to create ensemble retriever: {e}").send() # display the error if we failed to create the ensemble retriever
|
92 |
-
|
93 |
-
@cl.on_chat_start
|
94 |
-
async def start():
|
95 |
-
"""
|
96 |
-
More info: https://docs.chainlit.io/api-reference/lifecycle-hooks/on-chat-start
|
97 |
-
This function is called when the chat starts. Under the hood it handles all the complicated stuff for loading the UI.
|
98 |
-
We explicitly load the model, embeddings, and retrievers.
|
99 |
-
Asks the user to provide the YouTube video link and loads the transcription.
|
100 |
-
With the transcription, it creates a vector store and a BM25 vector store. That is used to create an ensemble retriever combining the two.
|
101 |
-
"""
|
102 |
-
await cl.Message(content="Hello! I am your AI assistant. I can help you with your questions about the video you provide.").send()
|
103 |
-
try: # a try catch block prevents the app from crashing if have an error
|
104 |
-
llm = ChatTogether(model="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo") # initialize the LLM model
|
105 |
-
await cl.Message(content=f"model is successfully loaded").send() # we can send messages to be displayed with cl.Message().send()
|
106 |
-
cl.user_session.set("llm", llm) # we can store variables in a special memory called the user session, so we can use them in our on message function and more
|
107 |
-
embedding = TogetherEmbeddings(model="togethercomputer/m2-bert-80M-8k-retrieval") # initialize the embedding model
|
108 |
-
cl.user_session.set("embedding", embedding) # store the embedding model in the user session
|
109 |
-
await cl.Message(content="embedding model loaded").send()
|
110 |
-
youtube_link = await cl.AskUserMessage("Please provide the YouTube video link").send() # We can ask the user for input using cl.AskUserMessage().send() which does not affect cl.on_message()
|
111 |
-
# more on ask user message: https://docs.chainlit.io/api-reference/ask/ask-for-input
|
112 |
-
await cl.Message(content=f"youtube link: {youtube_link['content']}").send() # display and double check to make sure the link is correct
|
113 |
-
youtube_docs = await create_youtube_transcription(youtube_link['content']) # create the youtube transcription
|
114 |
-
transcription = youtube_docs # get the transcription of the first document
|
115 |
-
await cl.Message(content=f"youtube docs: {transcription}").send() # display the transcription of the first document to show that we have the correct data
|
116 |
-
split_docs = await create_text_splitter(youtube_docs) # split the documents into chunks
|
117 |
-
vector_db = await create_faiss_vector_store(split_docs) # create the vector db
|
118 |
-
bm25 = await create_bm25_retreiver(split_docs) # create the BM25 retreiver
|
119 |
-
ensemble_retriever = await create_ensemble_retriever(vector_db, bm25) # create the ensemble retriever
|
120 |
-
cl.user_session.set("ensemble_retriever", ensemble_retriever) # store the ensemble retriever in the user session for our on message function
|
121 |
-
except Exception as e:
|
122 |
-
await cl.Message(content=f"error that happened: {e}").send() # display the error if we failed to load the model
|
123 |
-
|
124 |
-
@cl.on_message
|
125 |
-
async def message(message: cl.Message):
|
126 |
-
"""
|
127 |
-
More info: https://docs.chainlit.io/api-reference/lifecycle-hooks/on-message
|
128 |
-
This function is called when the user sends a message. It uses the ensemble retriever to find the most relevant documents and feeds them into the LLM.
|
129 |
-
We can then display the answer and the relevant documents to the user.
|
130 |
-
"""
|
131 |
-
prompt_template = ChatPromptTemplate.from_template(template="""
|
132 |
-
You are a helpful assistant that can answer questions about the following video. Here is the appropriate chunks of context: {context}.
|
133 |
-
Answer the question: {question} but do not use any information outside of the video. Site the source or information you used to answer the question
|
134 |
-
""") # we create a prompt template that we will use to format our prompt
|
135 |
-
llm = cl.user_session.get("llm") # we get the LLM model we initialized in the start function
|
136 |
-
ensemble_retriever = cl.user_session.get("ensemble_retriever") # we get the ensemble retriever we initialized in the start function
|
137 |
-
relevant_docs = ensemble_retriever.invoke(message.content) # we use the ensemble retriever to find the most relevant documents
|
138 |
-
cl.Message(content=f"Displaying Relevant Docs").send() # we display the relevant documents to the user
|
139 |
-
for doc in relevant_docs: # loop through the relevant documents and display each one!
|
140 |
-
await cl.Message(content=doc.page_content).send()
|
141 |
-
await cl.Message(content="Done Displaying Relevant Docs").send()
|
142 |
-
# question -> retrieve relevant docs -> format the question and context and add it to the prompt template -> pass to LLM
|
143 |
-
rag_chain = RunnableSequence({"context": ensemble_retriever, "question": RunnablePassthrough()} | prompt_template | llm)
|
144 |
-
response = rag_chain.invoke(message.content) # we invoke the rag chain with the user's message
|
145 |
-
await cl.Message(content=f"LLM Response: {response.content}").send() # we display the response to the user
|
|
|
10 |
from langchain.retrievers.ensemble import EnsembleRetriever # for the ensemble retriever
|
11 |
from langchain_text_splitters import RecursiveCharacterTextSplitter # for the text splitter
|
12 |
|
13 |
+
|
14 |
+
|
15 |
+
######## Chainlit ########
|
16 |
+
@cl.on_chat_start
|
17 |
+
async def start():
|
18 |
+
"""
|
19 |
+
More info: https://docs.chainlit.io/api-reference/lifecycle-hooks/on-chat-start
|
20 |
+
This function is called when the chat starts. Under the hood it handles all the complicated stuff for loading the UI.
|
21 |
+
We explicitly load the model, embeddings, and retrievers.
|
22 |
+
Asks the user to provide the YouTube video link and loads the transcription.
|
23 |
+
With the transcription, it creates a vector store and a BM25 vector store. That is used to create an ensemble retriever combining the two.
|
24 |
+
"""
|
25 |
+
await cl.Message(content="Hello! I am your AI assistant. I can help you with your questions about the video you provide.").send()
|
26 |
+
try: # a try catch block prevents the app from crashing if have an error
|
27 |
+
llm = ChatTogether(model="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo") # initialize the LLM model
|
28 |
+
await cl.Message(content=f"model is successfully loaded").send() # we can send messages to be displayed with cl.Message().send()
|
29 |
+
cl.user_session.set("llm", llm) # we can store variables in a special memory called the user session, so we can use them in our on message function and more
|
30 |
+
embedding = TogetherEmbeddings(model="togethercomputer/m2-bert-80M-8k-retrieval") # initialize the embedding model
|
31 |
+
cl.user_session.set("embedding", embedding) # store the embedding model in the user session
|
32 |
+
await cl.Message(content="embedding model loaded").send()
|
33 |
+
youtube_link = await cl.AskUserMessage("Please provide the YouTube video link").send() # We can ask the user for input using cl.AskUserMessage().send() which does not affect cl.on_message()
|
34 |
+
# more on ask user message: https://docs.chainlit.io/api-reference/ask/ask-for-input
|
35 |
+
|
36 |
+
await cl.Message(content=f"youtube link: {youtube_link['content']}").send() # display and double check to make sure the link is correct
|
37 |
+
youtube_docs = await create_youtube_transcription(youtube_link['content']) # create the youtube transcription
|
38 |
+
transcription = youtube_docs # get the transcription of the first document
|
39 |
+
await cl.Message(content=f"youtube docs: {transcription}").send() # display the transcription of the first document to show that we have the correct data
|
40 |
+
split_docs = await create_text_splitter(youtube_docs) # split the documents into chunks
|
41 |
+
vector_db = await create_faiss_vector_store(split_docs) # create the vector db
|
42 |
+
bm25 = await create_bm25_retreiver(split_docs) # create the BM25 retreiver
|
43 |
+
ensemble_retriever = await create_ensemble_retriever(vector_db, bm25) # create the ensemble retriever
|
44 |
+
cl.user_session.set("ensemble_retriever", ensemble_retriever) # store the ensemble retriever in the user session for our on message function
|
45 |
+
except Exception as e:
|
46 |
+
await cl.Message(content=f"failed to load model: {e}").send() # display the error if we failed to load the model
|
47 |
+
|
48 |
+
@cl.on_message
|
49 |
+
async def message(message: cl.Message):
|
50 |
+
"""
|
51 |
+
More info: https://docs.chainlit.io/api-reference/lifecycle-hooks/on-message
|
52 |
+
This function is called when the user sends a message. It uses the ensemble retriever to find the most relevant documents and feeds them into the LLM.
|
53 |
+
We can then display the answer and the relevant documents to the user.
|
54 |
+
"""
|
55 |
+
prompt_template = ChatPromptTemplate.from_template(template="""
|
56 |
+
You are a helpful assistant that can answer questions about the following video. Here is the appropriate chunks of context: {context}.
|
57 |
+
Answer the question: {question} but do not use any information outside of the video. Site the source or information you used to answer the question
|
58 |
+
""") # we create a prompt template that we will use to format our prompt
|
59 |
+
llm = cl.user_session.get("llm") # we get the LLM model we initialized in the start function
|
60 |
+
ensemble_retriever = cl.user_session.get("ensemble_retriever") # we get the ensemble retriever we initialized in the start function
|
61 |
+
relevant_docs = ensemble_retriever.invoke(message.content) # we use the ensemble retriever to find the most relevant documents
|
62 |
+
cl.Message(content=f"Displaying Relevant Docs").send() # we display the relevant documents to the user
|
63 |
+
for doc in relevant_docs: # loop through the relevant documents and display each one!
|
64 |
+
await cl.Message(content=doc.page_content).send()
|
65 |
+
await cl.Message(content="Done Displaying Relevant Docs").send()
|
66 |
+
# question -> retrieve relevant docs -> format the question and context and add it to the prompt template -> pass to LLM
|
67 |
+
rag_chain = RunnableSequence({"context": ensemble_retriever, "question": RunnablePassthrough()} | prompt_template | llm)
|
68 |
+
response = rag_chain.invoke(message.content) # we invoke the rag chain with the user's message
|
69 |
+
await cl.Message(content=f"LLM Response: {response.content}").send() # we display the response to the user
|
70 |
+
|
71 |
+
######## Youtube ########
|
72 |
+
|
73 |
async def create_youtube_transcription(youtube_url: str) -> List[langchain_core.documents.Document]:
|
74 |
"""
|
75 |
Create a youtube transcription from a youtube url
|
|
|
88 |
except Exception as e:
|
89 |
await cl.Message(content=f"failed to load youtube video: {e} Please refresh the page").send() # display the error if we failed to load the youtube video
|
90 |
|
91 |
+
|
92 |
+
######## RAG ########
|
93 |
+
|
94 |
+
async def create_text_splitter(docs: List[langchain_core.documents.Document]) -> List[langchain_core.documents.Document]:
|
95 |
"""
|
96 |
Create a text splitter from a list of documents
|
97 |
More Info: ument_transformers/recursive_text_splitter/
|
|
|
151 |
ensemble_retreiver = EnsembleRetriever(retrievers=[vector_db.as_retriever(), bm25], weights=[.3, .7]) # 30% semantic, 70% keyword retrieval
|
152 |
return ensemble_retreiver
|
153 |
except Exception as e:
|
154 |
+
await cl.Message(content=f"failed to create ensemble retriever: {e}").send() # display the error if we failed to create the ensemble retriever
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|