Shreyas094 commited on
Commit
4977c9a
·
verified ·
1 Parent(s): 7678968

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -218
app.py CHANGED
@@ -18,16 +18,6 @@ from huggingface_hub import InferenceClient
18
  import inspect
19
  import logging
20
  import shutil
21
- from sentence_transformers import CrossEncoder
22
- from datetime import datetime
23
- from dateutil import parser as date_parser
24
- from sklearn.feature_extraction.text import TfidfVectorizer
25
- from sklearn.metrics.pairwise import cosine_similarity
26
- from trafilatura import fetch_url, extract
27
- import json
28
- from requests.exceptions import RequestException
29
-
30
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
31
 
32
 
33
  # Set up basic configuration for logging
@@ -281,66 +271,10 @@ def generate_chunked_response(prompt, model, max_tokens=10000, num_calls=3, temp
281
  print(f"Final clean response: {final_response[:100]}...")
282
  return final_response
283
 
284
- class SimpleDDGSearch:
285
- def search(self, query: str, num_results: int = 5):
286
- results = []
287
- with DDGS() as ddgs:
288
- for r in ddgs.text(query, region='wt-wt', safesearch='off', max_results=num_results * 2): # Request more results than needed
289
- results.append(r["href"])
290
- if len(results) >= num_results:
291
- break
292
- return results
293
-
294
- class TrafilaturaWebCrawler:
295
- def get_website_content_from_url(self, url: str) -> str:
296
- try:
297
- downloaded = fetch_url(url)
298
- if downloaded is None:
299
- raise RequestException(f"Failed to fetch content from URL: {url}")
300
-
301
- result = extract(downloaded, output_format='json', include_comments=False, with_metadata=True, url=url)
302
- if result:
303
- result_dict = json.loads(result)
304
- title = result_dict.get('title', 'No title found')
305
- content = result_dict.get('text', 'No content extracted')
306
-
307
- if content == 'No content extracted':
308
- content = extract(downloaded, include_comments=False)
309
-
310
- return f'=========== Website Title: {title} ===========\n\n=========== Website URL: {url} ===========\n\n=========== Website Content ===========\n\n{content}\n\n=========== Website Content End ===========\n\n'
311
- else:
312
- raise ValueError(f"No content extracted from URL: {url}")
313
- except Exception as e:
314
- logging.error(f"An error occurred while processing {url}: {str(e)}")
315
- return None
316
-
317
- def search_and_crawl(query: str, num_results: int = 10):
318
- searcher = SimpleDDGSearch()
319
- search_results = searcher.search(query, num_results=num_results * 2) # Request more results than needed
320
-
321
- crawler = TrafilaturaWebCrawler()
322
- output = ""
323
- successful_crawls = 0
324
-
325
- for url in search_results:
326
- if successful_crawls >= num_results:
327
- break
328
-
329
- content = crawler.get_website_content_from_url(url)
330
- if content:
331
- output += f"Results for URL {successful_crawls + 1}: {url}\n\n"
332
- output += content + "\n"
333
- output += "------------------------------------------------------------\n\n"
334
- successful_crawls += 1
335
-
336
- if successful_crawls == 0:
337
- logging.warning(f"No successful crawls for query: {query}")
338
- return "No results could be fetched for the given query."
339
-
340
- return output
341
-
342
  def duckduckgo_search(query):
343
- return search_and_crawl(query, num_results=10)
 
 
344
 
345
  class CitingSources(BaseModel):
346
  sources: List[str] = Field(
@@ -373,19 +307,17 @@ def retry_last_response(history, use_web_search, model, temperature, num_calls):
373
 
374
  return chatbot_interface(last_user_msg, history, use_web_search, model, temperature, num_calls)
375
 
376
- def respond(message, history, model, temperature, num_calls, use_web_search, selected_docs):
 
 
377
  logging.info(f"User Query: {message}")
378
- logging.info(f"Model Used: {model}")
379
  logging.info(f"Search Type: {'Web Search' if use_web_search else 'PDF Search'}")
380
-
381
  logging.info(f"Selected Documents: {selected_docs}")
382
 
383
  try:
384
  if use_web_search:
385
  for main_content, sources in get_response_with_search(message, model, num_calls=num_calls, temperature=temperature):
386
  response = f"{main_content}\n\n{sources}"
387
- first_line = response.split('\n')[0] if response else ''
388
- # logging.info(f"Generated Response (first line): {first_line}")
389
  yield response
390
  else:
391
  embed = get_embeddings()
@@ -393,7 +325,6 @@ def respond(message, history, model, temperature, num_calls, use_web_search, sel
393
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
394
  retriever = database.as_retriever(search_kwargs={"k": 20})
395
 
396
- # Filter relevant documents based on user selection
397
  all_relevant_docs = retriever.get_relevant_documents(message)
398
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
399
 
@@ -407,26 +338,21 @@ def respond(message, history, model, temperature, num_calls, use_web_search, sel
407
  yield "No documents available. Please upload PDF documents to answer questions."
408
  return
409
 
410
- if model == "@cf/meta/llama-3.1-8b-instruct":
411
- # Use Cloudflare API
412
- for partial_response in get_response_from_cloudflare(prompt="", context=context_str, query=message, num_calls=num_calls, temperature=temperature, search_type="pdf"):
413
- first_line = partial_response.split('\n')[0] if partial_response else ''
414
- # logging.info(f"Generated Response (first line): {first_line}")
415
- yield partial_response
416
- else:
417
- # Use Hugging Face API
418
- for partial_response in get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature):
419
- first_line = partial_response.split('\n')[0] if partial_response else ''
420
- # logging.info(f"Generated Response (first line): {first_line}")
421
- yield partial_response
422
  except Exception as e:
423
- logging.error(f"Error with {model}: {str(e)}")
424
- if "microsoft/Phi-3-mini-4k-instruct" in model:
425
- logging.info("Falling back to Mistral model due to Phi-3 error")
426
- fallback_model = "mistralai/Mistral-7B-Instruct-v0.3"
427
- yield from respond(message, history, fallback_model, temperature, num_calls, use_web_search, selected_docs)
428
- else:
429
- yield f"An error occurred with the {model} model: {str(e)}. Please try again or select a different model."
430
 
431
  logging.basicConfig(level=logging.DEBUG)
432
 
@@ -485,119 +411,51 @@ After writing the document, please provide a list of sources used in your respon
485
  if not full_response:
486
  yield "I apologize, but I couldn't generate a response at this time. Please try again later."
487
 
488
- def rank_results(query, results):
489
- # Sort by date, most recent first
490
- results.sort(key=lambda x: x['date'], reverse=True)
491
-
492
- # Calculate relevance scores
493
- vectorizer = TfidfVectorizer().fit_transform([query] + [f"{r['title']} {r['body']}" for r in results])
494
- relevance_scores = cosine_similarity(vectorizer[0:1], vectorizer[1:])[0]
495
-
496
- # Combine date priority and relevance score
497
- for i, result in enumerate(results):
498
- days_old = (datetime.now() - result['date']).days
499
- date_score = 1 / (days_old + 1) # Newer articles get higher scores
500
- result['combined_score'] = (date_score + relevance_scores[i]) / 2
501
-
502
- # Sort by combined score and return top 3
503
- return sorted(results, key=lambda x: x['combined_score'], reverse=True)[:3]
504
-
505
  def create_web_search_vectors(search_results):
506
  embed = get_embeddings()
507
 
508
  documents = []
509
  for result in search_results:
510
  if 'body' in result:
511
- content = f"{result['title']}\n{result['body']}\nSource: {result['href']}\nDate: {result['date']}"
512
- documents.append(Document(page_content=content, metadata={"source": result['href'], "date": result['date']}))
513
 
514
  return FAISS.from_documents(documents, embed)
515
 
516
  def get_response_with_search(query, model, num_calls=3, temperature=0.2):
517
  search_results = duckduckgo_search(query)
 
518
 
519
- if not search_results:
520
- yield "No web search results available. Please try again.", ""
521
- return
522
-
523
- accumulated_response = ""
524
-
525
- # Split the search results into separate documents
526
- documents = search_results.split("------------------------------------------------------------")
527
-
528
- for i, doc in enumerate(documents, 1):
529
- if not doc.strip(): # Skip empty documents
530
- continue
531
-
532
- # Extract title, URL, and content from the document
533
- title_start = doc.find("Website Title:") + len("Website Title:")
534
- title_end = doc.find("===========", title_start)
535
- title = doc[title_start:title_end].strip()
536
-
537
- url_start = doc.find("Website URL:") + len("Website URL:")
538
- url_end = doc.find("===========", url_start)
539
- source = doc[url_start:url_end].strip()
540
-
541
- content_start = doc.find("Website Content") + len("Website Content ===========")
542
- content_end = doc.find("Website Content End")
543
- context = doc[content_start:content_end].strip()
544
-
545
- prompt = f"""Using the following context from a web search result:
546
  {context}
547
- This information is from the website titled: {title}
548
- You are an expert AI assistant. Write a detailed and complete research article that fulfills the following user request: '{query}'
549
- Base your summary strictly on the information from this source. Only include information that is directly supported by the given content.
550
- If any part of the information cannot be verified from this source, clearly state that it could not be confirmed."""
551
-
552
- if model == "@cf/meta/llama-3.1-8b-instruct":
553
- # Use Cloudflare API
554
- source_response = ""
555
- for response in get_response_from_cloudflare(prompt="", context=context, query=query, num_calls=1, temperature=temperature, search_type="web"):
556
- source_response += response
557
- accumulated_response += f"Source {i} ({source}):\n\n{source_response}\n\n"
558
- yield accumulated_response, ""
559
- else:
560
- # Use Hugging Face API
561
- client = InferenceClient(model, token=huggingface_token)
562
-
563
- source_response = ""
564
- for message in client.chat_completion(
565
- messages=[{"role": "user", "content": prompt}],
566
- max_tokens=10000,
567
- temperature=temperature,
568
- stream=True,
569
- ):
570
- if message.choices and message.choices[0].delta and message.choices[0].delta.content:
571
- chunk = message.choices[0].delta.content
572
- source_response += chunk
573
- accumulated_response += f"Source {i} ({source}):\n\n{source_response}\n\n"
574
- yield accumulated_response, ""
575
-
576
- # Generate an overall summary after processing all sources
577
- overall_prompt = f"""Based on the summaries you've generated for each source: '{accumulated_response}', provide a concise overall summary that addresses the user's query: '{query}'
578
- Highlight any conflicting information or gaps in the available data."""
579
 
580
- if model == "@cf/meta/llama-3.1-8b-instruct":
581
- # Use Cloudflare API for overall summary
582
- overall_response = ""
583
- for response in get_response_from_cloudflare(prompt="", context="", query=overall_prompt, num_calls=1, temperature=temperature, search_type="web"):
584
- overall_response += response
585
- accumulated_response += f"Overall Summary:\n\n{overall_response}\n\n"
586
- yield accumulated_response, ""
587
- else:
588
- # Use Hugging Face API for overall summary
589
- overall_summary = ""
590
  for message in client.chat_completion(
591
- messages=[{"role": "user", "content": overall_prompt}],
592
  max_tokens=10000,
593
  temperature=temperature,
594
  stream=True,
595
  ):
596
  if message.choices and message.choices[0].delta and message.choices[0].delta.content:
597
  chunk = message.choices[0].delta.content
598
- overall_summary += chunk
599
- accumulated_response += f"Overall Summary:\n\n{overall_summary}\n\n"
600
- yield accumulated_response, ""
601
 
602
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
603
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
@@ -611,7 +469,6 @@ def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=
611
  yield "No documents available. Please upload PDF documents to answer questions."
612
  return
613
 
614
- # Pre-filter the documents
615
  filtered_docs = []
616
  for doc_id, doc in database.docstore._dict.items():
617
  if isinstance(doc, Document) and doc.metadata.get("source") in selected_docs:
@@ -624,7 +481,6 @@ def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=
624
  yield "No relevant information found in the selected documents. Please try selecting different documents or rephrasing your query."
625
  return
626
 
627
- # Create a new FAISS index with only the selected documents
628
  filtered_db = FAISS.from_documents(filtered_docs, embed)
629
 
630
  retriever = filtered_db.as_retriever(search_kwargs={"k": 10})
@@ -632,40 +488,20 @@ def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=
632
  relevant_docs = retriever.get_relevant_documents(query)
633
  logging.info(f"Number of relevant documents retrieved: {len(relevant_docs)}")
634
 
635
- for doc in relevant_docs:
636
- logging.info(f"Document source: {doc.metadata['source']}")
637
- logging.info(f"Document content preview: {doc.page_content[:100]}...") # Log first 100 characters of each document
638
-
639
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
640
  logging.info(f"Total context length: {len(context_str)}")
641
 
642
- if model == "@cf/meta/llama-3.1-8b-instruct":
643
- logging.info("Using Cloudflare API")
644
- # Use Cloudflare API with the retrieved context
645
- for response in get_response_from_cloudflare(prompt="", context=context_str, query=query, num_calls=num_calls, temperature=temperature, search_type="pdf"):
646
- yield response
647
- else:
648
- logging.info("Using Hugging Face API")
649
- # Use Hugging Face API
650
- prompt = f"""Using the following context from the PDF documents:
651
  {context_str}
652
  Write a detailed and complete response that answers the following user question: '{query}'"""
653
-
654
- client = InferenceClient(model, token=huggingface_token)
655
-
656
- response = ""
657
- for i in range(num_calls):
658
- logging.info(f"API call {i+1}/{num_calls}")
659
- for message in client.chat_completion(
660
- messages=[{"role": "user", "content": prompt}],
661
- max_tokens=10000,
662
- temperature=temperature,
663
- stream=True,
664
- ):
665
- if message.choices and message.choices[0].delta and message.choices[0].delta.content:
666
- chunk = message.choices[0].delta.content
667
- response += chunk
668
- yield response # Yield partial response
669
 
670
  logging.info("Finished generating response")
671
 
@@ -807,4 +643,4 @@ with demo:
807
  )
808
 
809
  if __name__ == "__main__":
810
- demo.launch(share=True)
 
18
  import inspect
19
  import logging
20
  import shutil
 
 
 
 
 
 
 
 
 
 
21
 
22
 
23
  # Set up basic configuration for logging
 
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(
 
307
 
308
  return chatbot_interface(last_user_msg, history, use_web_search, model, temperature, num_calls)
309
 
310
+ from duckduckgo_search import DDGS
311
+
312
+ def respond(message, history, use_web_search, model, temperature, num_calls, selected_docs):
313
  logging.info(f"User Query: {message}")
 
314
  logging.info(f"Search Type: {'Web Search' if use_web_search else 'PDF Search'}")
 
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
  yield response
322
  else:
323
  embed = get_embeddings()
 
325
  database = FAISS.load_local("faiss_database", embed, allow_dangerous_deserialization=True)
326
  retriever = database.as_retriever(search_kwargs={"k": 20})
327
 
 
328
  all_relevant_docs = retriever.get_relevant_documents(message)
329
  relevant_docs = [doc for doc in all_relevant_docs if doc.metadata["source"] in selected_docs]
330
 
 
338
  yield "No documents available. Please upload PDF documents to answer questions."
339
  return
340
 
341
+ prompt = f"""Using the following context from the PDF documents:
342
+ {context_str}
343
+ Write a detailed and complete response that answers the following user question: '{message}'"""
344
+
345
+ try:
346
+ response = DDGS().chat(prompt, model="llama-3-70b")
347
+ yield response
348
+ except Exception as e:
349
+ logging.error(f"Error with DuckDuckGo chat API: {str(e)}")
350
+ logging.info("Falling back to Hugging Face API")
351
+ yield from get_response_from_pdf(message, model, selected_docs, num_calls=num_calls, temperature=temperature)
352
+
353
  except Exception as e:
354
+ logging.error(f"Error: {str(e)}")
355
+ yield f"An error occurred: {str(e)}. Please try again or select a different model."
 
 
 
 
 
356
 
357
  logging.basicConfig(level=logging.DEBUG)
358
 
 
411
  if not full_response:
412
  yield "I apologize, but I couldn't generate a response at this time. Please try again later."
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  def create_web_search_vectors(search_results):
415
  embed = get_embeddings()
416
 
417
  documents = []
418
  for result in search_results:
419
  if 'body' in result:
420
+ content = f"{result['title']}\n{result['body']}\nSource: {result['href']}"
421
+ documents.append(Document(page_content=content, metadata={"source": result['href']}))
422
 
423
  return FAISS.from_documents(documents, embed)
424
 
425
  def get_response_with_search(query, model, num_calls=3, temperature=0.2):
426
  search_results = duckduckgo_search(query)
427
+ context = "\n".join([f"{result['title']}\n{result['body']}" for result in search_results])
428
 
429
+ prompt = f"""Using the following context from web search results:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  {context}
431
+ You are an expert AI assistant, write a detailed and complete research document that fulfills the following user request: '{query}'
432
+ Base your entire response strictly on the information retrieved from trusted sources. Importantly, only include information that is directly supported by the retrieved content.
433
+ If any part of the information cannot be verified from the given sources, clearly state that it could not be confirmed.
434
+ After writing the document, please provide a list of sources used in your response."""
435
+
436
+ try:
437
+ response = DDGS().chat(prompt, model="llama-3-70b")
438
+ yield response, ""
439
+ except Exception as e:
440
+ logging.error(f"Error with DuckDuckGo chat API: {str(e)}")
441
+ logging.info("Falling back to Hugging Face API")
442
+ yield from get_response_from_huggingface(prompt, model, num_calls, temperature)
443
+
444
+ def get_response_from_huggingface(prompt, model, num_calls=3, temperature=0.2):
445
+ client = InferenceClient(model, token=huggingface_token)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
 
447
+ main_content = ""
448
+ for i in range(num_calls):
 
 
 
 
 
 
 
 
449
  for message in client.chat_completion(
450
+ messages=[{"role": "user", "content": prompt}],
451
  max_tokens=10000,
452
  temperature=temperature,
453
  stream=True,
454
  ):
455
  if message.choices and message.choices[0].delta and message.choices[0].delta.content:
456
  chunk = message.choices[0].delta.content
457
+ main_content += chunk
458
+ yield main_content, "" # Yield partial main content without sources
 
459
 
460
  def get_response_from_pdf(query, model, selected_docs, num_calls=3, temperature=0.2):
461
  logging.info(f"Entering get_response_from_pdf with query: {query}, model: {model}, selected_docs: {selected_docs}")
 
469
  yield "No documents available. Please upload PDF documents to answer questions."
470
  return
471
 
 
472
  filtered_docs = []
473
  for doc_id, doc in database.docstore._dict.items():
474
  if isinstance(doc, Document) and doc.metadata.get("source") in selected_docs:
 
481
  yield "No relevant information found in the selected documents. Please try selecting different documents or rephrasing your query."
482
  return
483
 
 
484
  filtered_db = FAISS.from_documents(filtered_docs, embed)
485
 
486
  retriever = filtered_db.as_retriever(search_kwargs={"k": 10})
 
488
  relevant_docs = retriever.get_relevant_documents(query)
489
  logging.info(f"Number of relevant documents retrieved: {len(relevant_docs)}")
490
 
 
 
 
 
491
  context_str = "\n".join([doc.page_content for doc in relevant_docs])
492
  logging.info(f"Total context length: {len(context_str)}")
493
 
494
+ prompt = f"""Using the following context from the PDF documents:
 
 
 
 
 
 
 
 
495
  {context_str}
496
  Write a detailed and complete response that answers the following user question: '{query}'"""
497
+
498
+ try:
499
+ response = DDGS().chat(prompt, model="llama-3-70b")
500
+ yield response
501
+ except Exception as e:
502
+ logging.error(f"Error with DuckDuckGo chat API: {str(e)}")
503
+ logging.info("Falling back to Hugging Face API")
504
+ yield from get_response_from_huggingface(prompt, model, num_calls, temperature)
 
 
 
 
 
 
 
 
505
 
506
  logging.info("Finished generating response")
507
 
 
643
  )
644
 
645
  if __name__ == "__main__":
646
+ demo.launch(share=True)