Shreyas094 commited on
Commit
8fcdb06
·
verified ·
1 Parent(s): a6e04d0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -99
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,11 @@ 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
@@ -271,24 +274,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
- def duckduckgo_search(query):
275
- with DDGS() as ddgs:
276
- results = ddgs.text(query, max_results=5)
277
- return results
278
-
279
- class CitingSources(BaseModel):
280
- sources: List[str] = Field(
281
- ...,
282
- description="List of sources to cite. Should be an URL of the source."
283
- )
284
- def chatbot_interface(message, history, use_web_search, model, temperature, num_calls):
285
  if not message.strip():
286
  return "", history
287
 
288
  history = history + [(message, "")]
289
 
290
  try:
291
- for response in respond(message, history, model, temperature, num_calls, use_web_search):
292
  history[-1] = (message, response)
293
  yield history
294
  except gr.CancelledError:
@@ -298,36 +291,145 @@ def chatbot_interface(message, history, use_web_search, model, temperature, num_
298
  history[-1] = (message, f"An unexpected error occurred: {str(e)}")
299
  yield history
300
 
301
- def retry_last_response(history, use_web_search, model, temperature, num_calls):
302
  if not history:
303
  return history
304
 
305
  last_user_msg = history[-1][0]
306
  history = history[:-1] # Remove the last response
307
 
308
- return chatbot_interface(last_user_msg, history, use_web_search, model, temperature, num_calls)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
 
310
- def respond(message, history, model, temperature, num_calls, use_web_search, selected_docs):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  logging.info(f"User Query: {message}")
312
  logging.info(f"Model Used: {model}")
313
- logging.info(f"Search Type: {'Web Search' if use_web_search else 'PDF Search'}")
314
-
315
  logging.info(f"Selected Documents: {selected_docs}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
- try:
318
- if use_web_search:
319
- for main_content, sources in get_response_with_search(message, model, num_calls=num_calls, temperature=temperature):
320
- response = f"{main_content}\n\n{sources}"
321
- first_line = response.split('\n')[0] if response else ''
322
- # logging.info(f"Generated Response (first line): {first_line}")
323
- yield response
324
  else:
 
 
 
 
325
  embed = get_embeddings()
326
  if os.path.exists("faiss_database"):
327
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
328
  retriever = database.as_retriever(search_kwargs={"k": 20})
329
 
330
- # Filter relevant documents based on user selection
331
  all_relevant_docs = retriever.get_relevant_documents(message)
332
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
333
 
@@ -336,6 +438,7 @@ def respond(message, history, model, temperature, num_calls, use_web_search, sel
336
  return
337
 
338
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
 
339
  else:
340
  context_str = "No documents available."
341
  yield "No documents available. Please upload PDF documents to answer questions."
@@ -343,24 +446,20 @@ def respond(message, history, model, temperature, num_calls, use_web_search, sel
343
 
344
  if model == "@cf/meta/llama-3.1-8b-instruct":
345
  # Use Cloudflare API
346
- for partial_response in get_response_from_cloudflare(prompt="", context=context_str, query=message, num_calls=num_calls, temperature=temperature, search_type="pdf"):
347
- first_line = partial_response.split('\n')[0] if partial_response else ''
348
- # logging.info(f"Generated Response (first line): {first_line}")
349
- yield partial_response
350
  else:
351
  # Use Hugging Face API
352
- for partial_response in get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature):
353
- first_line = partial_response.split('\n')[0] if partial_response else ''
354
- # logging.info(f"Generated Response (first line): {first_line}")
355
- yield partial_response
356
- except Exception as e:
357
- logging.error(f"Error with {model}: {str(e)}")
358
- if "microsoft/Phi-3-mini-4k-instruct" in model:
359
- logging.info("Falling back to Mistral model due to Phi-3 error")
360
- fallback_model = "mistralai/Mistral-7B-Instruct-v0.3"
361
- yield from respond(message, history, fallback_model, temperature, num_calls, use_web_search, selected_docs)
362
- else:
363
- yield f"An error occurred with the {model} model: {str(e)}. Please try again or select a different model."
364
 
365
  logging.basicConfig(level=logging.DEBUG)
366
 
@@ -430,47 +529,6 @@ def create_web_search_vectors(search_results):
430
 
431
  return FAISS.from_documents(documents, embed)
432
 
433
- def get_response_with_search(query, model, num_calls=3, temperature=0.2):
434
- search_results = duckduckgo_search(query)
435
- web_search_database = create_web_search_vectors(search_results)
436
-
437
- if not web_search_database:
438
- yield "No web search results available. Please try again.", ""
439
- return
440
-
441
- retriever = web_search_database.as_retriever(search_kwargs={"k": 5})
442
- relevant_docs = retriever.get_relevant_documents(query)
443
-
444
- context = "\n".join([doc.page_content for doc in relevant_docs])
445
-
446
- prompt = f"""Using the following context from web search results:
447
- {context}
448
- You are an expert AI assistant, write a detailed and complete research document that fulfills the following user request: '{query}'
449
- Base your entire response strictly on the information retrieved from trusted sources. Importantly, only include information that is directly supported by the retrieved content.
450
- If any part of the information cannot be verified from the given sources, clearly state that it could not be confirmed.
451
- After writing the document, please provide a list of sources used in your response."""
452
-
453
- if model == "@cf/meta/llama-3.1-8b-instruct":
454
- # Use Cloudflare API
455
- for response in get_response_from_cloudflare(prompt="", context=context, query=query, num_calls=num_calls, temperature=temperature, search_type="web"):
456
- yield response, "" # Yield streaming response without sources
457
- else:
458
- # Use Hugging Face API
459
- client = InferenceClient(model, token=huggingface_token)
460
-
461
- main_content = ""
462
- for i in range(num_calls):
463
- for message in client.chat_completion(
464
- messages=[{"role": "user", "content": prompt}],
465
- max_tokens=10000,
466
- temperature=temperature,
467
- stream=True,
468
- ):
469
- if message.choices and message.choices[0].delta and message.choices[0].delta.content:
470
- chunk = message.choices[0].delta.content
471
- main_content += chunk
472
- yield main_content, "" # Yield partial main content without sources
473
-
474
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
475
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
476
 
@@ -530,7 +588,7 @@ Write a detailed and complete response that answers the following user question:
530
  logging.info(f"API call {i+1}/{num_calls}")
531
  for message in client.chat_completion(
532
  messages=[{"role": "user", "content": prompt}],
533
- max_tokens=10000,
534
  temperature=temperature,
535
  stream=True,
536
  ):
@@ -590,17 +648,20 @@ use_web_search = gr.Checkbox(label="Use Web Search", value=True)
590
 
591
  custom_placeholder = "Ask a question (Note: You can toggle between Web Search and PDF Chat in Additional Inputs below)"
592
 
 
 
593
  demo = gr.ChatInterface(
594
  respond,
 
595
  additional_inputs=[
596
  gr.Dropdown(choices=MODELS, label="Select Model", value=MODELS[3]),
597
  gr.Slider(minimum=0.1, maximum=1.0, value=0.2, step=0.1, label="Temperature"),
598
  gr.Slider(minimum=1, maximum=5, value=1, step=1, label="Number of API Calls"),
599
- use_web_search,
600
- document_selector
601
  ],
602
- title="AI-powered Web Search and PDF Chat Assistant",
603
- description="Chat with your PDFs or use web search to answer questions. Toggle between Web Search and PDF Chat in Additional Inputs below.",
604
  theme=gr.themes.Soft(
605
  primary_hue="orange",
606
  secondary_hue="amber",
@@ -623,18 +684,19 @@ demo = gr.ChatInterface(
623
  examples=[
624
  ["Tell me about the contents of the uploaded PDFs."],
625
  ["What are the main topics discussed in the documents?"],
626
- ["Can you summarize the key points from the PDFs?"]
 
627
  ],
628
  cache_examples=False,
629
  analytics_enabled=False,
630
- textbox=gr.Textbox(placeholder=custom_placeholder, container=False, scale=7),
631
  chatbot = gr.Chatbot(
632
- show_copy_button=True,
633
- likeable=True,
634
- layout="bubble",
635
- height=400,
636
- value=initial_conversation()
637
- )
638
  )
639
 
640
  # Add file upload functionality
@@ -679,4 +741,4 @@ with demo:
679
  )
680
 
681
  if __name__ == "__main__":
682
- demo.launch(share=True)
 
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
+ "duckduckgo/gpt-4o-mini",
41
+ "duckduckgo/claude-3-haiku",
42
+ "duckduckgo/llama-3.1-70b",
43
+ "duckduckgo/mixtral-8x7b"
44
  ]
45
 
46
  # Initialize LlamaParse
 
274
  print(f"Final clean response: {final_response[:100]}...")
275
  return final_response
276
 
277
+ def chatbot_interface(message, history, model, temperature, num_calls):
 
 
 
 
 
 
 
 
 
 
278
  if not message.strip():
279
  return "", history
280
 
281
  history = history + [(message, "")]
282
 
283
  try:
284
+ for response in respond(message, history, model, temperature, num_calls):
285
  history[-1] = (message, response)
286
  yield history
287
  except gr.CancelledError:
 
291
  history[-1] = (message, f"An unexpected error occurred: {str(e)}")
292
  yield history
293
 
294
+ def retry_last_response(history, model, temperature, num_calls):
295
  if not history:
296
  return history
297
 
298
  last_user_msg = history[-1][0]
299
  history = history[:-1] # Remove the last response
300
 
301
+ return chatbot_interface(last_user_msg, history, model, temperature, num_calls)
302
+
303
+ def truncate_context(context, max_length=16000):
304
+ """Truncate the context to a maximum length."""
305
+ if len(context) <= max_length:
306
+ return context
307
+ return context[:max_length] + "..."
308
+
309
+ def get_response_from_duckduckgo(query, model, context, num_calls=1, temperature=0.2):
310
+ logging.info(f"Using DuckDuckGo chat with model: {model}")
311
+ ddg_model = model.split('/')[-1] # Extract the model name from the full string
312
+
313
+ # Truncate the context to avoid exceeding input limits
314
+ truncated_context = truncate_context(context)
315
+
316
+ full_response = ""
317
+ for _ in range(num_calls):
318
+ try:
319
+ # Include truncated context in the query
320
+ contextualized_query = f"Using the following context:\n{truncated_context}\n\nUser question: {query}"
321
+ results = DDGS().chat(contextualized_query, model=ddg_model)
322
+ full_response += results + "\n"
323
+ logging.info(f"DuckDuckGo API response received. Length: {len(results)}")
324
+ except Exception as e:
325
+ logging.error(f"Error in generating response from DuckDuckGo: {str(e)}")
326
+ yield f"An error occurred with the {model} model: {str(e)}. Please try again."
327
+ return
328
+
329
+ yield full_response.strip()
330
+
331
+ class ConversationManager:
332
+ def __init__(self):
333
+ self.history = []
334
+ self.current_context = None
335
 
336
+ def add_interaction(self, query, response):
337
+ self.history.append((query, response))
338
+ self.current_context = f"Previous query: {query}\nPrevious response summary: {response[:200]}..."
339
+
340
+ def get_context(self):
341
+ return self.current_context
342
+
343
+ conversation_manager = ConversationManager()
344
+
345
+ def get_web_search_results(query: str, max_results: int = 10) -> List[Dict[str, str]]:
346
+ try:
347
+ results = list(DDGS().text(query, max_results=max_results))
348
+ if not results:
349
+ print(f"No results found for query: {query}")
350
+ return results
351
+ except Exception as e:
352
+ print(f"An error occurred during web search: {str(e)}")
353
+ return [{"error": f"An error occurred during web search: {str(e)}"}]
354
+
355
+ def rephrase_query(original_query: str, conversation_manager: ConversationManager) -> str:
356
+ context = conversation_manager.get_context()
357
+ if context:
358
+ 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:
359
+
360
+ 1. Determine if the new query is a continuation of the previous conversation or an entirely new topic.
361
+ 2. If it's a continuation, rephrase the query by incorporating relevant information from the context to make it more specific and contextual.
362
+ 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.
363
+ 4. Provide ONLY the rephrased query without any additional explanation or reasoning.
364
+
365
+ Context: {context}
366
+
367
+ New query: {original_query}
368
+
369
+ Rephrased query:"""
370
+ response = DDGS().chat(prompt, model="llama-3.1-70b")
371
+ rephrased_query = response.split('\n')[0].strip()
372
+ return rephrased_query
373
+ return original_query
374
+
375
+ def summarize_web_results(query: str, search_results: List[Dict[str, str]], conversation_manager: ConversationManager) -> str:
376
+ try:
377
+ context = conversation_manager.get_context()
378
+ search_context = "\n\n".join([f"Title: {result['title']}\nContent: {result['body']}" for result in search_results])
379
+
380
+ 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},
381
+ You have to create a comprehensive news summary FOCUSING on the context provided to you.
382
+ Include key facts, relevant statistics, and expert opinions if available.
383
+ Ensure the article is well-structured with an introduction, main body, and conclusion, IF NECESSARY.
384
+ Address the query in the context of the ongoing conversation IF APPLICABLE.
385
+ 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:
386
+
387
+ {search_context}
388
+
389
+ Article:"""
390
+
391
+ summary = DDGS().chat(prompt, model="llama-3.1-70b")
392
+ return summary
393
+ except Exception as e:
394
+ return f"An error occurred during summarization: {str(e)}"
395
+
396
+ # Modify the existing respond function to handle both PDF and web search
397
+ def respond(message, history, model, temperature, num_calls, selected_docs, use_web_search):
398
  logging.info(f"User Query: {message}")
399
  logging.info(f"Model Used: {model}")
 
 
400
  logging.info(f"Selected Documents: {selected_docs}")
401
+ logging.info(f"Use Web Search: {use_web_search}")
402
+
403
+ if use_web_search:
404
+ original_query = message
405
+ rephrased_query = rephrase_query(message, conversation_manager)
406
+ logging.info(f"Original query: {original_query}")
407
+ logging.info(f"Rephrased query: {rephrased_query}")
408
+
409
+ final_summary = ""
410
+ for _ in range(num_calls):
411
+ search_results = get_web_search_results(rephrased_query)
412
+ if not search_results:
413
+ final_summary += f"No search results found for the query: {rephrased_query}\n\n"
414
+ elif "error" in search_results[0]:
415
+ final_summary += search_results[0]["error"] + "\n\n"
416
+ else:
417
+ summary = summarize_web_results(rephrased_query, search_results, conversation_manager)
418
+ final_summary += summary + "\n\n"
419
 
420
+ if final_summary:
421
+ conversation_manager.add_interaction(original_query, final_summary)
422
+ yield final_summary
 
 
 
 
423
  else:
424
+ yield "Unable to generate a response. Please try a different query."
425
+ else:
426
+ # Existing PDF search logic
427
+ try:
428
  embed = get_embeddings()
429
  if os.path.exists("faiss_database"):
430
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
431
  retriever = database.as_retriever(search_kwargs={"k": 20})
432
 
 
433
  all_relevant_docs = retriever.get_relevant_documents(message)
434
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
435
 
 
438
  return
439
 
440
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
441
+ logging.info(f"Context length: {len(context_str)}")
442
  else:
443
  context_str = "No documents available."
444
  yield "No documents available. Please upload PDF documents to answer questions."
 
446
 
447
  if model == "@cf/meta/llama-3.1-8b-instruct":
448
  # Use Cloudflare API
449
+ for response in get_response_from_cloudflare(prompt="", context=context_str, query=message, num_calls=num_calls, temperature=temperature, search_type="pdf"):
450
+ yield response
 
 
451
  else:
452
  # Use Hugging Face API
453
+ for response in get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature):
454
+ yield response
455
+ except Exception as e:
456
+ logging.error(f"Error with {model}: {str(e)}")
457
+ if "microsoft/Phi-3-mini-4k-instruct" in model:
458
+ logging.info("Falling back to Mistral model due to Phi-3 error")
459
+ fallback_model = "mistralai/Mistral-7B-Instruct-v0.3"
460
+ yield from respond(message, history, fallback_model, temperature, num_calls, selected_docs, use_web_search)
461
+ else:
462
+ yield f"An error occurred with the {model} model: {str(e)}. Please try again or select a different model."
 
 
463
 
464
  logging.basicConfig(level=logging.DEBUG)
465
 
 
529
 
530
  return FAISS.from_documents(documents, embed)
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
533
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
534
 
 
588
  logging.info(f"API call {i+1}/{num_calls}")
589
  for message in client.chat_completion(
590
  messages=[{"role": "user", "content": prompt}],
591
+ max_tokens=20000,
592
  temperature=temperature,
593
  stream=True,
594
  ):
 
648
 
649
  custom_placeholder = "Ask a question (Note: You can toggle between Web Search and PDF Chat in Additional Inputs below)"
650
 
651
+ # Update the demo interface
652
+ # Update the Gradio interface
653
  demo = gr.ChatInterface(
654
  respond,
655
+ additional_inputs_accordion=gr.Accordion(label="⚙️ Parameters", open=True, render=False),
656
  additional_inputs=[
657
  gr.Dropdown(choices=MODELS, label="Select Model", value=MODELS[3]),
658
  gr.Slider(minimum=0.1, maximum=1.0, value=0.2, step=0.1, label="Temperature"),
659
  gr.Slider(minimum=1, maximum=5, value=1, step=1, label="Number of API Calls"),
660
+ gr.CheckboxGroup(label="Select documents to query", choices=[]),
661
+ gr.Checkbox(label="Use Web Search", value=True)
662
  ],
663
+ title="AI-powered PDF Chat and Web Search Assistant",
664
+ description="Chat with your PDFs or use web search to answer questions.",
665
  theme=gr.themes.Soft(
666
  primary_hue="orange",
667
  secondary_hue="amber",
 
684
  examples=[
685
  ["Tell me about the contents of the uploaded PDFs."],
686
  ["What are the main topics discussed in the documents?"],
687
+ ["Can you summarize the key points from the PDFs?"],
688
+ ["What's the latest news about artificial intelligence?"]
689
  ],
690
  cache_examples=False,
691
  analytics_enabled=False,
692
+ textbox=gr.Textbox(placeholder="Ask a question about the uploaded PDFs or any topic", container=False, scale=7),
693
  chatbot = gr.Chatbot(
694
+ show_copy_button=True,
695
+ likeable=True,
696
+ layout="bubble",
697
+ height=400,
698
+ value=initial_conversation()
699
+ )
700
  )
701
 
702
  # Add file upload functionality
 
741
  )
742
 
743
  if __name__ == "__main__":
744
+ demo.launch(share=True)