himel06 commited on
Commit
2584853
·
verified ·
1 Parent(s): c02e3be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -152
app.py CHANGED
@@ -5,12 +5,10 @@ from langchain_community.document_loaders import PyPDFLoader
5
  from langchain.text_splitter import RecursiveCharacterTextSplitter
6
  from langchain_community.vectorstores import Chroma
7
  from langchain.chains import ConversationalRetrievalChain
8
- #from langchain_community.embeddings import HuggingFaceEmbeddings
9
  from langchain_huggingface import HuggingFaceEmbeddings
10
  from langchain_community.llms import HuggingFacePipeline
11
  from langchain.chains import ConversationChain
12
  from langchain.memory import ConversationBufferMemory
13
- #from langchain_community.llms import HuggingFaceEndpoint
14
  from langchain_huggingface import HuggingFaceEndpoint
15
 
16
  from pathlib import Path
@@ -24,28 +22,16 @@ import tqdm
24
  import accelerate
25
  import re
26
 
27
- # default_persist_directory = './chroma_HF/'
28
- list_llm = ["mistralai/Mistral-7B-Instruct-v0.2", "mistralai/Mixtral-8x7B-Instruct-v0.1",
29
- "mistralai/Mistral-7B-Instruct-v0.1", \
30
- "google/gemma-7b-it", "google/gemma-2b-it", \
31
- "HuggingFaceH4/zephyr-7b-beta", "HuggingFaceH4/zephyr-7b-gemma-v0.1", \
32
- "meta-llama/Llama-2-7b-chat-hf", "microsoft/phi-2", \
33
- "TinyLlama/TinyLlama-1.1B-Chat-v1.0", "mosaicml/mpt-7b-instruct", "tiiuae/falcon-7b-instruct", \
34
- "google/flan-t5-xxl"
35
- ]
36
- list_llm_simple = [os.path.basename(llm) for llm in list_llm]
37
 
38
 
39
  # Load PDF document and create doc splits
40
  def load_doc(list_file_path, chunk_size, chunk_overlap):
41
- # Processing for one document only
42
- # loader = PyPDFLoader(file_path)
43
- # pages = loader.load()
44
  loaders = [PyPDFLoader(x) for x in list_file_path]
45
  pages = []
46
  for loader in loaders:
47
  pages.extend(loader.load())
48
- # text_splitter = RecursiveCharacterTextSplitter(chunk_size = 600, chunk_overlap = 50)
49
  text_splitter = RecursiveCharacterTextSplitter(
50
  chunk_size=chunk_size,
51
  chunk_overlap=chunk_overlap)
@@ -56,14 +42,12 @@ def load_doc(list_file_path, chunk_size, chunk_overlap):
56
  # Create vector database
57
  def create_db(splits, collection_name):
58
  embedding = HuggingFaceEmbeddings()
59
- #new_client = chromadb.EphemeralClient()
60
  new_client = chromadb.PersistentClient()
61
  vectordb = Chroma.from_documents(
62
  documents=splits,
63
  embedding=embedding,
64
  client=new_client,
65
  collection_name=collection_name,
66
- # persist_directory=default_persist_directory
67
  )
68
  return vectordb
69
 
@@ -72,93 +56,20 @@ def create_db(splits, collection_name):
72
  def load_db():
73
  embedding = HuggingFaceEmbeddings()
74
  vectordb = Chroma(
75
- # persist_directory=default_persist_directory,
76
  embedding_function=embedding)
77
  return vectordb
78
 
79
 
80
  # Initialize langchain LLM chain
81
  def initialize_llmchain(llm_model, temperature, max_tokens, top_k, vector_db, progress=gr.Progress()):
82
- progress(0.1, desc="Initializing HF tokenizer...")
83
- # HuggingFacePipeline uses local model
84
- # Note: it will download model locally...
85
- # tokenizer=AutoTokenizer.from_pretrained(llm_model)
86
- # progress(0.5, desc="Initializing HF pipeline...")
87
- # pipeline=transformers.pipeline(
88
- # "text-generation",
89
- # model=llm_model,
90
- # tokenizer=tokenizer,
91
- # torch_dtype=torch.bfloat16,
92
- # trust_remote_code=True,
93
- # device_map="auto",
94
- # # max_length=1024,
95
- # max_new_tokens=max_tokens,
96
- # do_sample=True,
97
- # top_k=top_k,
98
- # num_return_sequences=1,
99
- # eos_token_id=tokenizer.eos_token_id
100
- # )
101
- # llm = HuggingFacePipeline(pipeline=pipeline, model_kwargs={'temperature': temperature})
102
-
103
- # HuggingFaceHub uses HF inference endpoints
104
  progress(0.5, desc="Initializing HF Hub...")
105
- # Use of trust_remote_code as model_kwargs
106
- # Warning: langchain issue
107
- # URL: https://github.com/langchain-ai/langchain/issues/6080
108
- if llm_model == "mistralai/Mixtral-8x7B-Instruct-v0.1":
109
- llm = HuggingFaceEndpoint(
110
- repo_id=llm_model,
111
- # model_kwargs={"temperature": temperature, "max_new_tokens": max_tokens, "top_k": top_k, "load_in_8bit": True}
112
- temperature=temperature,
113
- max_new_tokens=max_tokens,
114
- top_k=top_k,
115
- load_in_8bit=True,
116
- )
117
- elif llm_model in ["HuggingFaceH4/zephyr-7b-gemma-v0.1", "mosaicml/mpt-7b-instruct"]:
118
- raise gr.Error("LLM model is too large to be loaded automatically on free inference endpoint")
119
- llm = HuggingFaceEndpoint(
120
- repo_id=llm_model,
121
- temperature=temperature,
122
- max_new_tokens=max_tokens,
123
- top_k=top_k,
124
- )
125
- elif llm_model == "microsoft/phi-2":
126
- # raise gr.Error("phi-2 model requires 'trust_remote_code=True', currently not supported by langchain HuggingFaceHub...")
127
- llm = HuggingFaceEndpoint(
128
- repo_id=llm_model,
129
- # model_kwargs={"temperature": temperature, "max_new_tokens": max_tokens, "top_k": top_k, "trust_remote_code": True, "torch_dtype": "auto"}
130
- temperature=temperature,
131
- max_new_tokens=max_tokens,
132
- top_k=top_k,
133
- trust_remote_code=True,
134
- torch_dtype="auto",
135
- )
136
- elif llm_model == "TinyLlama/TinyLlama-1.1B-Chat-v1.0":
137
- llm = HuggingFaceEndpoint(
138
- repo_id=llm_model,
139
- # model_kwargs={"temperature": temperature, "max_new_tokens": 250, "top_k": top_k}
140
- temperature=temperature,
141
- max_new_tokens=250,
142
- top_k=top_k,
143
- )
144
- elif llm_model == "meta-llama/Llama-2-7b-chat-hf":
145
- raise gr.Error("Llama-2-7b-chat-hf model requires a Pro subscription...")
146
- llm = HuggingFaceEndpoint(
147
- repo_id=llm_model,
148
- # model_kwargs={"temperature": temperature, "max_new_tokens": max_tokens, "top_k": top_k}
149
- temperature=temperature,
150
- max_new_tokens=max_tokens,
151
- top_k=top_k,
152
- )
153
- else:
154
- llm = HuggingFaceEndpoint(
155
- repo_id=llm_model,
156
- # model_kwargs={"temperature": temperature, "max_new_tokens": max_tokens, "top_k": top_k, "trust_remote_code": True, "torch_dtype": "auto"}
157
- # model_kwargs={"temperature": temperature, "max_new_tokens": max_tokens, "top_k": top_k}
158
- temperature=temperature,
159
- max_new_tokens=max_tokens,
160
- top_k=top_k,
161
- )
162
 
163
  progress(0.75, desc="Defining buffer memory...")
164
  memory = ConversationBufferMemory(
@@ -166,7 +77,6 @@ def initialize_llmchain(llm_model, temperature, max_tokens, top_k, vector_db, pr
166
  output_key='answer',
167
  return_messages=True
168
  )
169
- # retriever=vector_db.as_retriever(search_type="similarity", search_kwargs={'k': 3})
170
  retriever = vector_db.as_retriever()
171
  progress(0.8, desc="Defining retrieval chain...")
172
  qa_chain = ConversationalRetrievalChain.from_llm(
@@ -174,9 +84,7 @@ def initialize_llmchain(llm_model, temperature, max_tokens, top_k, vector_db, pr
174
  retriever=retriever,
175
  chain_type="stuff",
176
  memory=memory,
177
- # combine_docs_chain_kwargs={"prompt": your_prompt})
178
  return_source_documents=True,
179
- # return_generated_question=False,
180
  verbose=False,
181
  )
182
  progress(0.9, desc="Done!")
@@ -184,24 +92,14 @@ def initialize_llmchain(llm_model, temperature, max_tokens, top_k, vector_db, pr
184
 
185
 
186
  # Generate collection name for vector database
187
- # - Use filepath as input, ensuring unicode text
188
  def create_collection_name(filepath):
189
- # Extract filename without extension
190
  collection_name = Path(filepath).stem
191
- # Fix potential issues from naming convention
192
- ## Remove space
193
  collection_name = collection_name.replace(" ", "-")
194
- ## ASCII transliterations of Unicode text
195
  collection_name = unidecode(collection_name)
196
- ## Remove special characters
197
- # collection_name = re.findall("[\dA-Za-z]*", collection_name)[0]
198
  collection_name = re.sub('[^A-Za-z0-9]+', '-', collection_name)
199
- ## Limit length to 50 characters
200
  collection_name = collection_name[:50]
201
- ## Minimum length of 3 characters
202
  if len(collection_name) < 3:
203
  collection_name = collection_name + 'xyz'
204
- ## Enforce start and end as alphanumeric character
205
  if not collection_name[0].isalnum():
206
  collection_name = 'A' + collection_name[1:]
207
  if not collection_name[-1].isalnum():
@@ -213,27 +111,20 @@ def create_collection_name(filepath):
213
 
214
  # Initialize database
215
  def initialize_database(list_file_obj, chunk_size, chunk_overlap, progress=gr.Progress()):
216
- # Create list of documents (when valid)
217
  list_file_path = [x.name for x in list_file_obj if x is not None]
218
- # Create collection_name for vector database
219
  progress(0.1, desc="Creating collection name...")
220
  collection_name = create_collection_name(list_file_path[0])
221
  progress(0.25, desc="Loading document...")
222
- # Load document and create splits
223
  doc_splits = load_doc(list_file_path, chunk_size, chunk_overlap)
224
- # Create or load vector database
225
  progress(0.5, desc="Generating vector database...")
226
- # global vector_db
227
  vector_db = create_db(doc_splits, collection_name)
228
  progress(0.9, desc="Done!")
229
  return vector_db, collection_name, "Complete!"
230
 
231
 
232
- def initialize_LLM(llm_option, llm_temperature, max_tokens, top_k, vector_db, progress=gr.Progress()):
233
- # print("llm_option",llm_option)
234
- llm_name = list_llm[llm_option]
235
- print("llm_name: ", llm_name)
236
- qa_chain = initialize_llmchain(llm_name, llm_temperature, max_tokens, top_k, vector_db, progress)
237
  return qa_chain, "Complete!"
238
 
239
 
@@ -247,9 +138,6 @@ def format_chat_history(message, chat_history):
247
 
248
  def conversation(qa_chain, message, history):
249
  formatted_chat_history = format_chat_history(message, history)
250
- # print("formatted_chat_history",formatted_chat_history)
251
-
252
- # Generate response using QA chain
253
  response = qa_chain({"question": message, "chat_history": formatted_chat_history})
254
  response_answer = response["answer"]
255
  if response_answer.find("Helpful Answer:") != -1:
@@ -258,16 +146,10 @@ def conversation(qa_chain, message, history):
258
  response_source1 = response_sources[0].page_content.strip()
259
  response_source2 = response_sources[1].page_content.strip()
260
  response_source3 = response_sources[2].page_content.strip()
261
- # Langchain sources are zero-based
262
  response_source1_page = response_sources[0].metadata["page"] + 1
263
  response_source2_page = response_sources[1].metadata["page"] + 1
264
  response_source3_page = response_sources[2].metadata["page"] + 1
265
- # print ('chat response: ', response_answer)
266
- # print('DB source', response_sources)
267
-
268
- # Append user message and response to chat history
269
  new_history = history + [(message, response_answer)]
270
- # return gr.update(value=""), new_history, response_sources[0], response_sources[1]
271
  return qa_chain, gr.update(
272
  value=""), new_history, response_source1, response_source1_page, response_source2, response_source2_page, response_source3, response_source3_page
273
 
@@ -277,8 +159,6 @@ def upload_file(file_obj):
277
  for idx, file in enumerate(file_obj):
278
  file_path = file_obj.name
279
  list_file_path.append(file_path)
280
- # print(file_path)
281
- # initialize_database(file_path, progress)
282
  return list_file_path
283
 
284
 
@@ -293,7 +173,7 @@ def demo():
293
  <h3>Ask any questions about your PDF documents</h3>""")
294
  gr.Markdown(
295
  """<b>Note:</b> This AI assistant, using Langchain and open-source LLMs, performs retrieval-augmented generation (RAG) from your PDF documents. \
296
- The user interface explicitely shows multiple steps to help understand the RAG workflow.
297
  This chatbot takes past questions into account when generating answers (via conversational memory), and includes document references for clarity purposes.<br>
298
  <br><b>Warning:</b> This space uses the free CPU Basic hardware from Hugging Face. Some steps and LLM models used below (free inference endpoints) can take some time to generate a reply.
299
  """)
@@ -302,7 +182,6 @@ def demo():
302
  with gr.Row():
303
  document = gr.Files(height=100, file_count="multiple", file_types=["pdf"], interactive=True,
304
  label="Upload your PDF documents (single or multiple)")
305
- # upload_btn = gr.UploadButton("Loading document...", height=100, file_count="multiple", file_types=["pdf"], scale=1)
306
 
307
  with gr.Tab("Step 2 - Process document"):
308
  with gr.Row():
@@ -322,19 +201,14 @@ def demo():
322
 
323
  with gr.Tab("Step 3 - Initialize QA chain"):
324
  with gr.Row():
325
- llm_btn = gr.Radio(list_llm_simple, \
326
- label="LLM models", value=list_llm_simple[0], type="index",
327
- info="Choose your LLM model")
328
- with gr.Accordion("Advanced options - LLM model", open=False):
329
- with gr.Row():
330
- slider_temperature = gr.Slider(minimum=0.01, maximum=1.0, value=0.7, step=0.1, label="Temperature",
331
- info="Model temperature", interactive=True)
332
- with gr.Row():
333
- slider_maxtokens = gr.Slider(minimum=224, maximum=4096, value=1024, step=32, label="Max Tokens",
334
- info="Model max tokens", interactive=True)
335
- with gr.Row():
336
- slider_topk = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="top-k samples",
337
- info="Model top-k samples", interactive=True)
338
  with gr.Row():
339
  llm_progress = gr.Textbox(value="None", label="QA chain initialization")
340
  with gr.Row():
@@ -358,13 +232,11 @@ def demo():
358
  submit_btn = gr.Button("Submit message")
359
  clear_btn = gr.ClearButton([msg, chatbot], value="Clear conversation")
360
 
361
- # Preprocessing events
362
- # upload_btn.upload(upload_file, inputs=[upload_btn], outputs=[document])
363
  db_btn.click(initialize_database, \
364
  inputs=[document, slider_chunk_size, slider_chunk_overlap], \
365
  outputs=[vector_db, collection_name, db_progress])
366
  qachain_btn.click(initialize_LLM, \
367
- inputs=[llm_btn, slider_temperature, slider_maxtokens, slider_topk, vector_db], \
368
  outputs=[qa_chain, llm_progress]).then(lambda: [None, "", 0, "", 0, "", 0], \
369
  inputs=None, \
370
  outputs=[chatbot, doc_source1, source1_page,
@@ -372,7 +244,6 @@ def demo():
372
  source3_page], \
373
  queue=False)
374
 
375
- # Chatbot events
376
  msg.submit(conversation, \
377
  inputs=[qa_chain, msg, chatbot], \
378
  outputs=[qa_chain, msg, chatbot, doc_source1, source1_page, doc_source2, source2_page, doc_source3,
@@ -392,4 +263,4 @@ def demo():
392
 
393
 
394
  if __name__ == "__main__":
395
- demo()
 
5
  from langchain.text_splitter import RecursiveCharacterTextSplitter
6
  from langchain_community.vectorstores import Chroma
7
  from langchain.chains import ConversationalRetrievalChain
 
8
  from langchain_huggingface import HuggingFaceEmbeddings
9
  from langchain_community.llms import HuggingFacePipeline
10
  from langchain.chains import ConversationChain
11
  from langchain.memory import ConversationBufferMemory
 
12
  from langchain_huggingface import HuggingFaceEndpoint
13
 
14
  from pathlib import Path
 
22
  import accelerate
23
  import re
24
 
25
+ # LLM model to use
26
+ llm_model = "mistralai/Mistral-7B-Instruct-v0.2"
 
 
 
 
 
 
 
 
27
 
28
 
29
  # Load PDF document and create doc splits
30
  def load_doc(list_file_path, chunk_size, chunk_overlap):
 
 
 
31
  loaders = [PyPDFLoader(x) for x in list_file_path]
32
  pages = []
33
  for loader in loaders:
34
  pages.extend(loader.load())
 
35
  text_splitter = RecursiveCharacterTextSplitter(
36
  chunk_size=chunk_size,
37
  chunk_overlap=chunk_overlap)
 
42
  # Create vector database
43
  def create_db(splits, collection_name):
44
  embedding = HuggingFaceEmbeddings()
 
45
  new_client = chromadb.PersistentClient()
46
  vectordb = Chroma.from_documents(
47
  documents=splits,
48
  embedding=embedding,
49
  client=new_client,
50
  collection_name=collection_name,
 
51
  )
52
  return vectordb
53
 
 
56
  def load_db():
57
  embedding = HuggingFaceEmbeddings()
58
  vectordb = Chroma(
 
59
  embedding_function=embedding)
60
  return vectordb
61
 
62
 
63
  # Initialize langchain LLM chain
64
  def initialize_llmchain(llm_model, temperature, max_tokens, top_k, vector_db, progress=gr.Progress()):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  progress(0.5, desc="Initializing HF Hub...")
66
+ llm = HuggingFaceEndpoint(
67
+ repo_id=llm_model,
68
+ temperature=temperature,
69
+ max_new_tokens=max_tokens,
70
+ top_k=top_k,
71
+ load_in_8bit=True,
72
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  progress(0.75, desc="Defining buffer memory...")
75
  memory = ConversationBufferMemory(
 
77
  output_key='answer',
78
  return_messages=True
79
  )
 
80
  retriever = vector_db.as_retriever()
81
  progress(0.8, desc="Defining retrieval chain...")
82
  qa_chain = ConversationalRetrievalChain.from_llm(
 
84
  retriever=retriever,
85
  chain_type="stuff",
86
  memory=memory,
 
87
  return_source_documents=True,
 
88
  verbose=False,
89
  )
90
  progress(0.9, desc="Done!")
 
92
 
93
 
94
  # Generate collection name for vector database
 
95
  def create_collection_name(filepath):
 
96
  collection_name = Path(filepath).stem
 
 
97
  collection_name = collection_name.replace(" ", "-")
 
98
  collection_name = unidecode(collection_name)
 
 
99
  collection_name = re.sub('[^A-Za-z0-9]+', '-', collection_name)
 
100
  collection_name = collection_name[:50]
 
101
  if len(collection_name) < 3:
102
  collection_name = collection_name + 'xyz'
 
103
  if not collection_name[0].isalnum():
104
  collection_name = 'A' + collection_name[1:]
105
  if not collection_name[-1].isalnum():
 
111
 
112
  # Initialize database
113
  def initialize_database(list_file_obj, chunk_size, chunk_overlap, progress=gr.Progress()):
 
114
  list_file_path = [x.name for x in list_file_obj if x is not None]
 
115
  progress(0.1, desc="Creating collection name...")
116
  collection_name = create_collection_name(list_file_path[0])
117
  progress(0.25, desc="Loading document...")
 
118
  doc_splits = load_doc(list_file_path, chunk_size, chunk_overlap)
 
119
  progress(0.5, desc="Generating vector database...")
 
120
  vector_db = create_db(doc_splits, collection_name)
121
  progress(0.9, desc="Done!")
122
  return vector_db, collection_name, "Complete!"
123
 
124
 
125
+ def initialize_LLM(llm_temperature, max_tokens, top_k, vector_db, progress=gr.Progress()):
126
+ print("LLM model: ", llm_model)
127
+ qa_chain = initialize_llmchain(llm_model, llm_temperature, max_tokens, top_k, vector_db, progress)
 
 
128
  return qa_chain, "Complete!"
129
 
130
 
 
138
 
139
  def conversation(qa_chain, message, history):
140
  formatted_chat_history = format_chat_history(message, history)
 
 
 
141
  response = qa_chain({"question": message, "chat_history": formatted_chat_history})
142
  response_answer = response["answer"]
143
  if response_answer.find("Helpful Answer:") != -1:
 
146
  response_source1 = response_sources[0].page_content.strip()
147
  response_source2 = response_sources[1].page_content.strip()
148
  response_source3 = response_sources[2].page_content.strip()
 
149
  response_source1_page = response_sources[0].metadata["page"] + 1
150
  response_source2_page = response_sources[1].metadata["page"] + 1
151
  response_source3_page = response_sources[2].metadata["page"] + 1
 
 
 
 
152
  new_history = history + [(message, response_answer)]
 
153
  return qa_chain, gr.update(
154
  value=""), new_history, response_source1, response_source1_page, response_source2, response_source2_page, response_source3, response_source3_page
155
 
 
159
  for idx, file in enumerate(file_obj):
160
  file_path = file_obj.name
161
  list_file_path.append(file_path)
 
 
162
  return list_file_path
163
 
164
 
 
173
  <h3>Ask any questions about your PDF documents</h3>""")
174
  gr.Markdown(
175
  """<b>Note:</b> This AI assistant, using Langchain and open-source LLMs, performs retrieval-augmented generation (RAG) from your PDF documents. \
176
+ The user interface explicitly shows multiple steps to help understand the RAG workflow.
177
  This chatbot takes past questions into account when generating answers (via conversational memory), and includes document references for clarity purposes.<br>
178
  <br><b>Warning:</b> This space uses the free CPU Basic hardware from Hugging Face. Some steps and LLM models used below (free inference endpoints) can take some time to generate a reply.
179
  """)
 
182
  with gr.Row():
183
  document = gr.Files(height=100, file_count="multiple", file_types=["pdf"], interactive=True,
184
  label="Upload your PDF documents (single or multiple)")
 
185
 
186
  with gr.Tab("Step 2 - Process document"):
187
  with gr.Row():
 
201
 
202
  with gr.Tab("Step 3 - Initialize QA chain"):
203
  with gr.Row():
204
+ slider_temperature = gr.Slider(minimum=0.01, maximum=1.0, value=0.7, step=0.1, label="Temperature",
205
+ info="Model temperature", interactive=True)
206
+ with gr.Row():
207
+ slider_maxtokens = gr.Slider(minimum=224, maximum=4096, value=1024, step=32, label="Max Tokens",
208
+ info="Model max tokens", interactive=True)
209
+ with gr.Row():
210
+ slider_topk = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="top-k samples",
211
+ info="Model top-k samples", interactive=True)
 
 
 
 
 
212
  with gr.Row():
213
  llm_progress = gr.Textbox(value="None", label="QA chain initialization")
214
  with gr.Row():
 
232
  submit_btn = gr.Button("Submit message")
233
  clear_btn = gr.ClearButton([msg, chatbot], value="Clear conversation")
234
 
 
 
235
  db_btn.click(initialize_database, \
236
  inputs=[document, slider_chunk_size, slider_chunk_overlap], \
237
  outputs=[vector_db, collection_name, db_progress])
238
  qachain_btn.click(initialize_LLM, \
239
+ inputs=[slider_temperature, slider_maxtokens, slider_topk, vector_db], \
240
  outputs=[qa_chain, llm_progress]).then(lambda: [None, "", 0, "", 0, "", 0], \
241
  inputs=None, \
242
  outputs=[chatbot, doc_source1, source1_page,
 
244
  source3_page], \
245
  queue=False)
246
 
 
247
  msg.submit(conversation, \
248
  inputs=[qa_chain, msg, chatbot], \
249
  outputs=[qa_chain, msg, chatbot, doc_source1, source1_page, doc_source2, source2_page, doc_source3,
 
263
 
264
 
265
  if __name__ == "__main__":
266
+ demo()