Shreyas094 commited on
Commit
9b9a599
1 Parent(s): 59ce7af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -182
app.py CHANGED
@@ -4,7 +4,7 @@ import re
4
  import gradio as gr
5
  import requests
6
  from duckduckgo_search import DDGS
7
- from typing import List
8
  from pydantic import BaseModel, Field
9
  from tempfile import NamedTemporaryFile
10
  from langchain_community.vectorstores import FAISS
@@ -13,7 +13,6 @@ from langchain_core.documents import Document
13
  from langchain_community.document_loaders import PyPDFLoader
14
  from langchain_community.embeddings import HuggingFaceEmbeddings
15
  from llama_parse import LlamaParse
16
- from langchain_core.documents import Document
17
  from huggingface_hub import InferenceClient
18
  import inspect
19
  import logging
@@ -37,7 +36,12 @@ MODELS = [
37
  "mistralai/Mistral-7B-Instruct-v0.3",
38
  "mistralai/Mixtral-8x7B-Instruct-v0.1",
39
  "@cf/meta/llama-3.1-8b-instruct",
40
- "mistralai/Mistral-Nemo-Instruct-2407"
 
 
 
 
 
41
  ]
42
 
43
  # Initialize LlamaParse
@@ -67,7 +71,7 @@ def load_document(file: NamedTemporaryFile, parser: str = "llamaparse") -> List[
67
  raise ValueError("Invalid parser specified. Use 'pypdf' or 'llamaparse'.")
68
 
69
  def get_embeddings():
70
- return HuggingFaceEmbeddings(model_name="sentence-transformers/stsb-roberta-large")
71
 
72
  # Add this at the beginning of your script, after imports
73
  DOCUMENTS_FILE = "uploaded_documents.json"
@@ -140,8 +144,10 @@ def update_vectors(files, parser):
140
  # Save the updated list of documents
141
  save_documents(uploaded_documents)
142
 
 
143
  return f"Vector store updated successfully. Processed {total_chunks} chunks from {len(files)} files using {parser}.", display_documents()
144
 
 
145
  def delete_documents(selected_docs):
146
  global uploaded_documents
147
 
@@ -271,48 +277,14 @@ def generate_chunked_response(prompt, model, max_tokens=10000, num_calls=3, temp
271
  print(f"Final clean response: {final_response[:100]}...")
272
  return final_response
273
 
274
- class SimpleDDGSearch:
275
- def search(self, query: str, num_results: int = 3):
276
- results = DDGS().text(query, region='wt-wt', safesearch='off', max_results=num_results)
277
- return [res["href"] for res in results]
278
-
279
- class TrafilaturaWebCrawler:
280
- def get_website_content_from_url(self, url: str) -> str:
281
- try:
282
- downloaded = fetch_url(url)
283
- if downloaded is None:
284
- return f"Failed to fetch content from URL: {url}"
285
-
286
- result = extract(downloaded, output_format='json', include_comments=False, with_metadata=True, url=url)
287
-
288
- if result:
289
- result_dict = json.loads(result)
290
- title = result_dict.get('title', 'No title found')
291
- content = result_dict.get('text', 'No content extracted')
292
-
293
- if content == 'No content extracted':
294
- content = extract(downloaded, include_comments=False)
295
-
296
- return f'=========== Website Title: {title} ===========\n\n=========== Website URL: {url} ===========\n\n=========== Website Content ===========\n\n{content}\n\n=========== Website Content End ===========\n\n'
297
- else:
298
- return f"No content extracted from URL: {url}"
299
- except Exception as e:
300
- return f"An error occurred while processing {url}: {str(e)}"
301
-
302
- class CitingSources(BaseModel):
303
- sources: List[str] = Field(
304
- ...,
305
- description="List of sources to cite. Should be an URL of the source."
306
- )
307
-
308
- def chatbot_interface(message, history, use_web_search, model, temperature, num_calls):
309
  if not message.strip():
310
  return "", history
311
 
312
  history = history + [(message, "")]
313
 
314
  try:
315
- for response in respond(message, history, model, temperature, num_calls, use_web_search):
316
  history[-1] = (message, response)
317
  yield history
318
  except gr.CancelledError:
@@ -322,116 +294,181 @@ def chatbot_interface(message, history, use_web_search, model, temperature, num_
322
  history[-1] = (message, f"An unexpected error occurred: {str(e)}")
323
  yield history
324
 
325
- def retry_last_response(history, use_web_search, model, temperature, num_calls):
326
  if not history:
327
  return history
328
 
329
  last_user_msg = history[-1][0]
330
  history = history[:-1] # Remove the last response
331
 
332
- return chatbot_interface(last_user_msg, history, use_web_search, model, temperature, num_calls)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
- def respond(message, history, model, temperature, num_calls, use_web_search, selected_docs, instruction_key):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  logging.info(f"User Query: {message}")
336
  logging.info(f"Model Used: {model}")
337
- logging.info(f"Search Type: {'Web Search' if use_web_search else 'PDF Search'}")
338
  logging.info(f"Selected Documents: {selected_docs}")
339
- logging.info(f"Instruction Key: {instruction_key}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
 
341
- try:
342
- if instruction_key and instruction_key != "None":
343
- # This is a summary generation request
344
- instruction = INSTRUCTION_PROMPTS[instruction_key]
345
- context_str = get_context_for_summary(selected_docs)
346
- message = f"{instruction}\n\nUsing the following context from the PDF documents:\n{context_str}\nGenerate a detailed summary."
347
- use_web_search = False # Ensure we use PDF search for summaries
348
-
349
- if use_web_search:
350
- for main_content, sources in get_response_with_search(message, model, num_calls=num_calls, temperature=temperature):
351
- response = f"{main_content}\n\n{sources}"
352
- first_line = response.split('\n')[0] if response else ''
353
- # logging.info(f"Generated Response (first line): {first_line}")
354
- yield response
355
  else:
 
 
 
 
356
  embed = get_embeddings()
357
  if os.path.exists("faiss_database"):
358
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
359
- retriever = database.as_retriever()
360
 
361
- # Filter relevant documents based on user selection
362
  all_relevant_docs = retriever.get_relevant_documents(message)
363
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
364
 
365
  if not relevant_docs:
366
  yield "No relevant information found in the selected documents. Please try selecting different documents or rephrasing your query."
367
  return
368
-
369
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
 
370
  else:
371
  context_str = "No documents available."
372
  yield "No documents available. Please upload PDF documents to answer questions."
373
  return
374
 
375
- if model == "@cf/meta/llama-3.1-8b-instruct":
 
 
 
 
376
  # Use Cloudflare API
377
  for partial_response in get_response_from_cloudflare(prompt="", context=context_str, query=message, num_calls=num_calls, temperature=temperature, search_type="pdf"):
378
- first_line = partial_response.split('\n')[0] if partial_response else ''
379
- # logging.info(f"Generated Response (first line): {first_line}")
380
  yield partial_response
381
  else:
382
  # Use Hugging Face API
383
  for partial_response in get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature):
384
- first_line = partial_response.split('\n')[0] if partial_response else ''
385
- # logging.info(f"Generated Response (first line): {first_line}")
386
  yield partial_response
387
-
388
- except Exception as e:
389
- logging.error(f"Error with {model}: {str(e)}")
390
- if "microsoft/Phi-3-mini-4k-instruct" in model:
391
- logging.info("Falling back to Mistral model due to Phi-3 error")
392
- fallback_model = "mistralai/Mistral-7B-Instruct-v0.3"
393
- yield from respond(message, history, fallback_model, temperature, num_calls, use_web_search, selected_docs, instruction_key)
394
- else:
395
- yield f"An error occurred with the {model} model: {str(e)}. Please try again or select a different model."
396
-
397
- logging.basicConfig(level=logging.DEBUG)
398
-
399
- def get_context_for_summary(selected_docs):
400
- embed = get_embeddings()
401
- if os.path.exists("faiss_database"):
402
- database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
403
- retriever = database.as_retriever(search_kwargs={"k": 5}) # Retrieve top 5 most relevant chunks
404
-
405
- # Create a generic query that covers common financial summary topics
406
- generic_query = "financial performance revenue profit assets liabilities cash flow key metrics highlights"
407
-
408
- relevant_docs = retriever.get_relevant_documents(generic_query)
409
- filtered_docs = [doc for doc in relevant_docs if doc.metadata["source"] in selected_docs]
410
-
411
- if not filtered_docs:
412
- return "No relevant information found in the selected documents for summary generation."
413
-
414
- context_str = "\n".join([doc.page_content for doc in filtered_docs])
415
- return context_str
416
- else:
417
- return "No documents available for summary generation."
418
-
419
- def get_context_for_query(query, selected_docs):
420
- embed = get_embeddings()
421
- if os.path.exists("faiss_database"):
422
- database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
423
- retriever = database.as_retriever(search_kwargs={"k": 3}) # Retrieve top 3 most relevant chunks
424
-
425
- relevant_docs = retriever.get_relevant_documents(query)
426
- filtered_docs = [doc for doc in relevant_docs if doc.metadata["source"] in selected_docs]
427
-
428
- if not filtered_docs:
429
- return "No relevant information found in the selected documents for the given query."
430
 
431
- context_str = "\n".join([doc.page_content for doc in filtered_docs])
432
- return context_str
433
- else:
434
- return "No documents available to answer the query."
435
 
436
  def get_response_from_cloudflare(prompt, context, query, num_calls=3, temperature=0.2, search_type="pdf"):
437
  headers = {
@@ -448,8 +485,7 @@ Write a detailed and complete response that answers the following user question:
448
  instruction = f"""Using the following context:
449
  {context}
450
  Write a detailed and complete research document that fulfills the following user request: '{query}'
451
- Also the generated research document should include the source URL as a hyperlink within the text. Ensure that the hyperlink is embedded in a relevant phrase or word within the response.
452
- Format the hyperlink in Markdown (e.g., [relevant text](URL))."""
453
 
454
  inputs = [
455
  {"role": "system", "content": instruction},
@@ -500,51 +536,6 @@ def create_web_search_vectors(search_results):
500
 
501
  return FAISS.from_documents(documents, embed)
502
 
503
- def get_response_with_search(query, model, num_calls=3, temperature=0.2):
504
- searcher = SimpleDDGSearch()
505
- search_results = searcher.search(query, num_results=3)
506
-
507
- crawler = TrafilaturaWebCrawler()
508
- context = ""
509
-
510
- for url in search_results:
511
- context += crawler.get_website_content_from_url(url) + "\n"
512
-
513
- prompt = f"""You are an expert AI named Sentinel and have been given a task to create a detailed and complete research article using the following context from web search results:
514
- {context} that fulfills the following user request: '{query}'
515
- Cover all the key points discussed in the {context} provided to you. Also stick with the context provided to you do not report any misleading or out of context information.
516
- Also the research document should include the source URL as a hyperlink within the text. Ensure that the hyperlink is embedded in a relevant phrase or word within the response.
517
- Format the hyperlink in Markdown (e.g., [relevant text](URL))."""
518
-
519
- if model == "@cf/meta/llama-3.1-8b-instruct":
520
- # Use Cloudflare API
521
- for response in get_response_from_cloudflare(prompt="", context=context, query=query, num_calls=num_calls, temperature=temperature, search_type="web"):
522
- yield response, "" # Yield streaming response without sources
523
- else:
524
- # Use Hugging Face API
525
- client = InferenceClient(model, token=huggingface_token)
526
-
527
- main_content = ""
528
- for i in range(num_calls):
529
- for message in client.chat_completion(
530
- messages=[{"role": "user", "content": prompt}],
531
- max_tokens=10000,
532
- temperature=temperature,
533
- stream=True,
534
- ):
535
- if message.choices and message.choices[0].delta and message.choices[0].delta.content:
536
- chunk = message.choices[0].delta.content
537
- main_content += chunk
538
- yield main_content, "" # Yield partial main content without sources
539
-
540
-
541
- INSTRUCTION_PROMPTS = {
542
- "Asset Managers": "Summarize the key financial metrics, assets under management, and performance highlights for this asset management company.",
543
- "Consumer Finance Companies": "Provide a summary of the company's loan portfolio, interest income, credit quality, and key operational metrics.",
544
- "Mortgage REITs": "Summarize the REIT's mortgage-backed securities portfolio, net interest income, book value per share, and dividend yield.",
545
- # Add more instruction prompts as needed
546
- }
547
-
548
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
549
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
550
 
@@ -604,7 +595,7 @@ Write a detailed and complete response that answers the following user question:
604
  logging.info(f"API call {i+1}/{num_calls}")
605
  for message in client.chat_completion(
606
  messages=[{"role": "user", "content": prompt}],
607
- max_tokens=10000,
608
  temperature=temperature,
609
  stream=True,
610
  ):
@@ -660,12 +651,12 @@ def refresh_documents():
660
  # Define the checkbox outside the demo block
661
  document_selector = gr.CheckboxGroup(label="Select documents to query")
662
 
663
- use_web_search = gr.Checkbox(label="Use Web Search", value=True)
664
 
665
  custom_placeholder = "Ask a question (Note: You can toggle between Web Search and PDF Chat in Additional Inputs below)"
666
 
667
- instruction_choices = ["None"] + list(INSTRUCTION_PROMPTS.keys())
668
-
669
  demo = gr.ChatInterface(
670
  respond,
671
  additional_inputs_accordion=gr.Accordion(label="⚙️ Parameters", open=True, render=False),
@@ -673,12 +664,11 @@ demo = gr.ChatInterface(
673
  gr.Dropdown(choices=MODELS, label="Select Model", value=MODELS[3]),
674
  gr.Slider(minimum=0.1, maximum=1.0, value=0.2, step=0.1, label="Temperature"),
675
  gr.Slider(minimum=1, maximum=5, value=1, step=1, label="Number of API Calls"),
676
- use_web_search,
677
- document_selector,
678
- gr.Dropdown(choices=instruction_choices, label="Select Entity Type for Summary", value="None")
679
  ],
680
- title="AI-powered Web Search and PDF Chat Assistant",
681
- description="Chat with your PDFs, use web search to answer questions, or generate summaries. Select an Entity Type for Summary to generate a specific summary.",
682
  theme=gr.themes.Soft(
683
  primary_hue="orange",
684
  secondary_hue="amber",
@@ -701,11 +691,12 @@ demo = gr.ChatInterface(
701
  examples=[
702
  ["Tell me about the contents of the uploaded PDFs."],
703
  ["What are the main topics discussed in the documents?"],
704
- ["Can you summarize the key points from the PDFs?"]
 
705
  ],
706
  cache_examples=False,
707
  analytics_enabled=False,
708
- textbox=gr.Textbox(placeholder=custom_placeholder, container=False, scale=7),
709
  chatbot = gr.Chatbot(
710
  show_copy_button=True,
711
  likeable=True,
@@ -715,10 +706,10 @@ demo = gr.ChatInterface(
715
  )
716
  )
717
 
 
718
  # Add file upload functionality
719
  with demo:
720
  gr.Markdown("## Upload and Manage PDF Documents")
721
-
722
  with gr.Row():
723
  file_input = gr.Files(label="Upload your PDF documents", file_types=[".pdf"])
724
  parser_dropdown = gr.Dropdown(choices=["pypdf", "llamaparse"], label="Select PDF Parser", value="llamaparse")
@@ -729,19 +720,25 @@ with demo:
729
  delete_button = gr.Button("Delete Selected Documents")
730
 
731
  # Update both the output text and the document selector
732
- update_button.click(update_vectors,
733
- inputs=[file_input, parser_dropdown],
734
- outputs=[update_output, document_selector])
 
 
735
 
736
  # Add the refresh button functionality
737
- refresh_button.click(refresh_documents,
738
- inputs=[],
739
- outputs=[document_selector])
 
 
740
 
741
  # Add the delete button functionality
742
- delete_button.click(delete_documents,
743
- inputs=[document_selector],
744
- outputs=[update_output, document_selector])
 
 
745
 
746
  gr.Markdown(
747
  """
 
4
  import gradio as gr
5
  import requests
6
  from duckduckgo_search import DDGS
7
+ from typing import List, Dict
8
  from pydantic import BaseModel, Field
9
  from tempfile import NamedTemporaryFile
10
  from langchain_community.vectorstores import FAISS
 
13
  from langchain_community.document_loaders import PyPDFLoader
14
  from langchain_community.embeddings import HuggingFaceEmbeddings
15
  from llama_parse import LlamaParse
 
16
  from huggingface_hub import InferenceClient
17
  import inspect
18
  import logging
 
36
  "mistralai/Mistral-7B-Instruct-v0.3",
37
  "mistralai/Mixtral-8x7B-Instruct-v0.1",
38
  "@cf/meta/llama-3.1-8b-instruct",
39
+ "mistralai/Mistral-Nemo-Instruct-2407",
40
+ "meta-llama/Meta-Llama-3.1-8B-Instruct",
41
+ "duckduckgo/gpt-4o-mini",
42
+ "duckduckgo/claude-3-haiku",
43
+ "duckduckgo/llama-3.1-70b",
44
+ "duckduckgo/mixtral-8x7b"
45
  ]
46
 
47
  # Initialize LlamaParse
 
71
  raise ValueError("Invalid parser specified. Use 'pypdf' or 'llamaparse'.")
72
 
73
  def get_embeddings():
74
+ return HuggingFaceEmbeddings(model_name="avsolatorio/GIST-Embedding-v0")
75
 
76
  # Add this at the beginning of your script, after imports
77
  DOCUMENTS_FILE = "uploaded_documents.json"
 
144
  # Save the updated list of documents
145
  save_documents(uploaded_documents)
146
 
147
+ # Return a tuple with the status message and the updated document list
148
  return f"Vector store updated successfully. Processed {total_chunks} chunks from {len(files)} files using {parser}.", display_documents()
149
 
150
+
151
  def delete_documents(selected_docs):
152
  global uploaded_documents
153
 
 
277
  print(f"Final clean response: {final_response[:100]}...")
278
  return final_response
279
 
280
+ def chatbot_interface(message, history, model, temperature, num_calls):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  if not message.strip():
282
  return "", history
283
 
284
  history = history + [(message, "")]
285
 
286
  try:
287
+ for response in respond(message, history, model, temperature, num_calls):
288
  history[-1] = (message, response)
289
  yield history
290
  except gr.CancelledError:
 
294
  history[-1] = (message, f"An unexpected error occurred: {str(e)}")
295
  yield history
296
 
297
+ def retry_last_response(history, model, temperature, num_calls):
298
  if not history:
299
  return history
300
 
301
  last_user_msg = history[-1][0]
302
  history = history[:-1] # Remove the last response
303
 
304
+ return chatbot_interface(last_user_msg, history, model, temperature, num_calls)
305
+
306
+ def truncate_context(context, max_length=16000):
307
+ """Truncate the context to a maximum length."""
308
+ if len(context) <= max_length:
309
+ return context
310
+ return context[:max_length] + "..."
311
+
312
+ def get_response_from_duckduckgo(query, model, context, num_calls=1, temperature=0.2):
313
+ logging.info(f"Using DuckDuckGo chat with model: {model}")
314
+ ddg_model = model.split('/')[-1] # Extract the model name from the full string
315
+
316
+ # Truncate the context to avoid exceeding input limits
317
+ truncated_context = truncate_context(context)
318
+
319
+ full_response = ""
320
+ for _ in range(num_calls):
321
+ try:
322
+ # Include truncated context in the query
323
+ contextualized_query = f"Using the following context:\n{truncated_context}\n\nUser question: {query}"
324
+ results = DDGS().chat(contextualized_query, model=ddg_model)
325
+ full_response += results + "\n"
326
+ logging.info(f"DuckDuckGo API response received. Length: {len(results)}")
327
+ except Exception as e:
328
+ logging.error(f"Error in generating response from DuckDuckGo: {str(e)}")
329
+ yield f"An error occurred with the {model} model: {str(e)}. Please try again."
330
+ return
331
+
332
+ yield full_response.strip()
333
+
334
+ class ConversationManager:
335
+ def __init__(self):
336
+ self.history = []
337
+ self.current_context = None
338
+
339
+ def add_interaction(self, query, response):
340
+ self.history.append((query, response))
341
+ self.current_context = f"Previous query: {query}\nPrevious response summary: {response[:200]}..."
342
+
343
+ def get_context(self):
344
+ return self.current_context
345
 
346
+ conversation_manager = ConversationManager()
347
+
348
+ def get_web_search_results(query: str, max_results: int = 10) -> List[Dict[str, str]]:
349
+ try:
350
+ results = list(DDGS().text(query, max_results=max_results))
351
+ if not results:
352
+ print(f"No results found for query: {query}")
353
+ return results
354
+ except Exception as e:
355
+ print(f"An error occurred during web search: {str(e)}")
356
+ return [{"error": f"An error occurred during web search: {str(e)}"}]
357
+
358
+ def rephrase_query(original_query: str, conversation_manager: ConversationManager) -> str:
359
+ context = conversation_manager.get_context()
360
+ if context:
361
+ prompt = f"""You are a highly intelligent conversational chatbot. Your task is to analyze the given context and new query, then decide whether to rephrase the query with or without incorporating the context. Follow these steps:
362
+
363
+ 1. Determine if the new query is a continuation of the previous conversation or an entirely new topic.
364
+ 2. If it's a continuation, rephrase the query by incorporating relevant information from the context to make it more specific and contextual.
365
+ 3. If it's a new topic, rephrase the query to make it more appropriate for a web search, focusing on clarity and accuracy without using the previous context.
366
+ 4. Provide ONLY the rephrased query without any additional explanation or reasoning.
367
+
368
+ Context: {context}
369
+
370
+ New query: {original_query}
371
+
372
+ Rephrased query:"""
373
+ response = DDGS().chat(prompt, model="llama-3.1-70b")
374
+ rephrased_query = response.split('\n')[0].strip()
375
+ return rephrased_query
376
+ return original_query
377
+
378
+ def summarize_web_results(query: str, search_results: List[Dict[str, str]], conversation_manager: ConversationManager) -> str:
379
+ try:
380
+ context = conversation_manager.get_context()
381
+ search_context = "\n\n".join([f"Title: {result['title']}\nContent: {result['body']}" for result in search_results])
382
+
383
+ prompt = f"""You are a highly intelligent & expert analyst and your job is to skillfully articulate the web search results about '{query}' and considering the context: {context},
384
+ You have to create a comprehensive news summary FOCUSING on the context provided to you.
385
+ Include key facts, relevant statistics, and expert opinions if available.
386
+ Ensure the article is well-structured with an introduction, main body, and conclusion, IF NECESSARY.
387
+ Address the query in the context of the ongoing conversation IF APPLICABLE.
388
+ Cite sources directly within the generated text and not at the end of the generated text, integrating URLs where appropriate to support the information provided:
389
+
390
+ {search_context}
391
+
392
+ Article:"""
393
+
394
+ summary = DDGS().chat(prompt, model="llama-3.1-70b")
395
+ return summary
396
+ except Exception as e:
397
+ return f"An error occurred during summarization: {str(e)}"
398
+
399
+ # Modify the existing respond function to handle both PDF and web search
400
+ def respond(message, history, model, temperature, num_calls, use_web_search, selected_docs):
401
  logging.info(f"User Query: {message}")
402
  logging.info(f"Model Used: {model}")
 
403
  logging.info(f"Selected Documents: {selected_docs}")
404
+ logging.info(f"Use Web Search: {use_web_search}")
405
+
406
+ if use_web_search:
407
+ original_query = message
408
+ rephrased_query = rephrase_query(message, conversation_manager)
409
+ logging.info(f"Original query: {original_query}")
410
+ logging.info(f"Rephrased query: {rephrased_query}")
411
+
412
+ final_summary = ""
413
+ for _ in range(num_calls):
414
+ search_results = get_web_search_results(rephrased_query)
415
+ if not search_results:
416
+ final_summary += f"No search results found for the query: {rephrased_query}\n\n"
417
+ elif "error" in search_results[0]:
418
+ final_summary += search_results[0]["error"] + "\n\n"
419
+ else:
420
+ summary = summarize_web_results(rephrased_query, search_results, conversation_manager)
421
+ final_summary += summary + "\n\n"
422
 
423
+ if final_summary:
424
+ conversation_manager.add_interaction(original_query, final_summary)
425
+ yield final_summary
 
 
 
 
 
 
 
 
 
 
 
426
  else:
427
+ yield "Unable to generate a response. Please try a different query."
428
+ else:
429
+ # Existing PDF search logic
430
+ try:
431
  embed = get_embeddings()
432
  if os.path.exists("faiss_database"):
433
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
434
+ retriever = database.as_retriever(search_kwargs={"k": 20})
435
 
 
436
  all_relevant_docs = retriever.get_relevant_documents(message)
437
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
438
 
439
  if not relevant_docs:
440
  yield "No relevant information found in the selected documents. Please try selecting different documents or rephrasing your query."
441
  return
442
+
443
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
444
+ logging.info(f"Context length: {len(context_str)}")
445
  else:
446
  context_str = "No documents available."
447
  yield "No documents available. Please upload PDF documents to answer questions."
448
  return
449
 
450
+ if model.startswith("duckduckgo/"):
451
+ # Use DuckDuckGo chat with context
452
+ for partial_response in get_response_from_duckduckgo(message, model, context_str, num_calls, temperature):
453
+ yield partial_response
454
+ elif model == "@cf/meta/llama-3.1-8b-instruct":
455
  # Use Cloudflare API
456
  for partial_response in get_response_from_cloudflare(prompt="", context=context_str, query=message, num_calls=num_calls, temperature=temperature, search_type="pdf"):
 
 
457
  yield partial_response
458
  else:
459
  # Use Hugging Face API
460
  for partial_response in get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature):
 
 
461
  yield partial_response
462
+ except Exception as e:
463
+ logging.error(f"Error with {model}: {str(e)}")
464
+ if "microsoft/Phi-3-mini-4k-instruct" in model:
465
+ logging.info("Falling back to Mistral model due to Phi-3 error")
466
+ fallback_model = "mistralai/Mistral-7B-Instruct-v0.3"
467
+ yield from respond(message, history, fallback_model, temperature, num_calls, selected_docs)
468
+ else:
469
+ yield f"An error occurred with the {model} model: {str(e)}. Please try again or select a different model."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
+ logging.basicConfig(level=logging.DEBUG)
 
 
 
472
 
473
  def get_response_from_cloudflare(prompt, context, query, num_calls=3, temperature=0.2, search_type="pdf"):
474
  headers = {
 
485
  instruction = f"""Using the following context:
486
  {context}
487
  Write a detailed and complete research document that fulfills the following user request: '{query}'
488
+ After writing the document, please provide a list of sources used in your response."""
 
489
 
490
  inputs = [
491
  {"role": "system", "content": instruction},
 
536
 
537
  return FAISS.from_documents(documents, embed)
538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
540
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
541
 
 
595
  logging.info(f"API call {i+1}/{num_calls}")
596
  for message in client.chat_completion(
597
  messages=[{"role": "user", "content": prompt}],
598
+ max_tokens=20000,
599
  temperature=temperature,
600
  stream=True,
601
  ):
 
651
  # Define the checkbox outside the demo block
652
  document_selector = gr.CheckboxGroup(label="Select documents to query")
653
 
654
+ use_web_search = gr.Checkbox(label="Use Web Search", value=False)
655
 
656
  custom_placeholder = "Ask a question (Note: You can toggle between Web Search and PDF Chat in Additional Inputs below)"
657
 
658
+ # Update the demo interface
659
+ # Update the Gradio interface
660
  demo = gr.ChatInterface(
661
  respond,
662
  additional_inputs_accordion=gr.Accordion(label="⚙️ Parameters", open=True, render=False),
 
664
  gr.Dropdown(choices=MODELS, label="Select Model", value=MODELS[3]),
665
  gr.Slider(minimum=0.1, maximum=1.0, value=0.2, step=0.1, label="Temperature"),
666
  gr.Slider(minimum=1, maximum=5, value=1, step=1, label="Number of API Calls"),
667
+ gr.Checkbox(label="Use Web Search", value=True),
668
+ gr.CheckboxGroup(label="Select documents to query")
 
669
  ],
670
+ title="AI-powered PDF Chat and Web Search Assistant",
671
+ description="Chat with your PDFs or use web search to answer questions.",
672
  theme=gr.themes.Soft(
673
  primary_hue="orange",
674
  secondary_hue="amber",
 
691
  examples=[
692
  ["Tell me about the contents of the uploaded PDFs."],
693
  ["What are the main topics discussed in the documents?"],
694
+ ["Can you summarize the key points from the PDFs?"],
695
+ ["What's the latest news about artificial intelligence?"]
696
  ],
697
  cache_examples=False,
698
  analytics_enabled=False,
699
+ textbox=gr.Textbox(placeholder="Ask a question about the uploaded PDFs or any topic", container=False, scale=7),
700
  chatbot = gr.Chatbot(
701
  show_copy_button=True,
702
  likeable=True,
 
706
  )
707
  )
708
 
709
+ # Add file upload functionality
710
  # Add file upload functionality
711
  with demo:
712
  gr.Markdown("## Upload and Manage PDF Documents")
 
713
  with gr.Row():
714
  file_input = gr.Files(label="Upload your PDF documents", file_types=[".pdf"])
715
  parser_dropdown = gr.Dropdown(choices=["pypdf", "llamaparse"], label="Select PDF Parser", value="llamaparse")
 
720
  delete_button = gr.Button("Delete Selected Documents")
721
 
722
  # Update both the output text and the document selector
723
+ update_button.click(
724
+ update_vectors,
725
+ inputs=[file_input, parser_dropdown],
726
+ outputs=[update_output, demo.additional_inputs[-1]] # Use the CheckboxGroup from additional_inputs
727
+ )
728
 
729
  # Add the refresh button functionality
730
+ refresh_button.click(
731
+ refresh_documents,
732
+ inputs=[],
733
+ outputs=[demo.additional_inputs[-1]] # Use the CheckboxGroup from additional_inputs
734
+ )
735
 
736
  # Add the delete button functionality
737
+ delete_button.click(
738
+ delete_documents,
739
+ inputs=[demo.additional_inputs[-1]], # Use the CheckboxGroup from additional_inputs
740
+ outputs=[update_output, demo.additional_inputs[-1]]
741
+ )
742
 
743
  gr.Markdown(
744
  """