Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -48,7 +48,7 @@ logging.basicConfig(level=logging.INFO)
|
|
| 48 |
logger = logging.getLogger(__name__)
|
| 49 |
|
| 50 |
# ===================================================================
|
| 51 |
-
# UTILITY CLASSES
|
| 52 |
# ===================================================================
|
| 53 |
|
| 54 |
class WebSearchTool:
|
|
@@ -111,7 +111,7 @@ class ConfigManager:
|
|
| 111 |
|
| 112 |
|
| 113 |
# ===================================================================
|
| 114 |
-
# DOCUMENT PROCESSING
|
| 115 |
# ===================================================================
|
| 116 |
|
| 117 |
class DocumentProcessor:
|
|
@@ -244,7 +244,7 @@ def build_embeddings_from_directory(data_directory: str, output_directory: str,
|
|
| 244 |
|
| 245 |
|
| 246 |
# ===================================================================
|
| 247 |
-
# RETRIEVER
|
| 248 |
# ===================================================================
|
| 249 |
|
| 250 |
class DocumentRetriever:
|
|
@@ -285,7 +285,7 @@ class DocumentRetriever:
|
|
| 285 |
|
| 286 |
|
| 287 |
# ===================================================================
|
| 288 |
-
# AGENTIC TOOLS
|
| 289 |
# ===================================================================
|
| 290 |
|
| 291 |
class AgenticTools:
|
|
@@ -455,7 +455,7 @@ class AgenticEvaluator:
|
|
| 455 |
|
| 456 |
|
| 457 |
# ===================================================================
|
| 458 |
-
# MAIN AGENT CLASS
|
| 459 |
# ===================================================================
|
| 460 |
|
| 461 |
class AgenticRAGAgent:
|
|
@@ -492,8 +492,11 @@ class AgenticRAGAgent:
|
|
| 492 |
print(f"β Error: {e}")
|
| 493 |
|
| 494 |
def clean_text_for_speech(self, text):
|
|
|
|
| 495 |
if not text:
|
| 496 |
return ""
|
|
|
|
|
|
|
| 497 |
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
|
| 498 |
text = re.sub(r'\*([^*]+)\*', r'\1', text)
|
| 499 |
text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
|
|
@@ -502,24 +505,43 @@ class AgenticRAGAgent:
|
|
| 502 |
text = re.sub(r'`([^`]+)`', r'\1', text)
|
| 503 |
text = re.sub(r'^[\s]*[-*+β’]\s+', '', text, flags=re.MULTILINE)
|
| 504 |
text = re.sub(r'^[\s]*\d+\.\s+', '', text, flags=re.MULTILINE)
|
| 505 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
text = emoji_pattern.sub('', text)
|
| 507 |
text = re.sub(r'\s+', ' ', text)
|
| 508 |
text = re.sub(r'\n+', '. ', text)
|
| 509 |
text = text.strip()
|
| 510 |
text = re.sub(r'\.+', '.', text)
|
|
|
|
| 511 |
return text
|
| 512 |
|
| 513 |
def generate_audio_response(self, text):
|
|
|
|
| 514 |
if not text or not GTTS_AVAILABLE:
|
| 515 |
return None
|
|
|
|
| 516 |
clean_text = self.clean_text_for_speech(text)
|
| 517 |
if not clean_text:
|
| 518 |
return None
|
|
|
|
| 519 |
try:
|
| 520 |
temp_dir = tempfile.gettempdir()
|
| 521 |
timestamp = int(time.time())
|
| 522 |
audio_file = os.path.join(temp_dir, f"response_{timestamp}.mp3")
|
|
|
|
| 523 |
tts = gTTS(text=clean_text, lang='en', slow=False)
|
| 524 |
tts.save(audio_file)
|
| 525 |
return audio_file
|
|
@@ -538,11 +560,13 @@ class AgenticRAGAgent:
|
|
| 538 |
def get_simple_answer(self, query, retrieved_docs):
|
| 539 |
if not self.groq_client:
|
| 540 |
return "Error: Groq API not configured"
|
|
|
|
| 541 |
context = "\n\n".join([doc.get('content', str(doc)) for doc in retrieved_docs[:5]])
|
| 542 |
prompt = f"""Based on this context, provide a clear answer.
|
| 543 |
Context: {context}
|
| 544 |
Question: {query}
|
| 545 |
Answer:"""
|
|
|
|
| 546 |
try:
|
| 547 |
response = self.groq_client.chat.completions.create(
|
| 548 |
model="llama-3.1-8b-instant",
|
|
@@ -571,8 +595,10 @@ Answer:"""
|
|
| 571 |
progress(0.5, desc="Generating response...")
|
| 572 |
response = self.get_greeting_response(query)
|
| 573 |
chat_history.append({"role": "assistant", "content": response})
|
|
|
|
| 574 |
progress(0.8, desc="π Generating voice...")
|
| 575 |
audio_file = self.generate_audio_response(response)
|
|
|
|
| 576 |
return chat_history, "", audio_file
|
| 577 |
|
| 578 |
progress(0.1, desc="π§ Planning...")
|
|
@@ -622,7 +648,7 @@ Answer:"""
|
|
| 622 |
if self.synthesizer:
|
| 623 |
final_answer = self.synthesizer.synthesize_results(query, results, self.temperature, self.max_tokens)
|
| 624 |
else:
|
| 625 |
-
successful = [r['result'] for r in results.values() if r.get(
|
| 626 |
final_answer = f"Based on available info: {' '.join(map(str, successful))}"
|
| 627 |
|
| 628 |
progress(0.9, desc="π Evaluating...")
|
|
@@ -654,7 +680,7 @@ Answer:"""
|
|
| 654 |
return chat_history, "", audio_file
|
| 655 |
|
| 656 |
except Exception as e:
|
| 657 |
-
|
| 658 |
chat_history.append({"role": "assistant", "content": error})
|
| 659 |
return chat_history, "", None
|
| 660 |
|
|
@@ -673,6 +699,7 @@ Answer:"""
|
|
| 673 |
dest = os.path.join("sample_data", original)
|
| 674 |
with open(dest, "wb") as dst:
|
| 675 |
dst.write(file.read())
|
|
|
|
| 676 |
uploaded.append(original)
|
| 677 |
|
| 678 |
if not uploaded:
|
|
@@ -721,12 +748,12 @@ Answer:"""
|
|
| 721 |
|
| 722 |
|
| 723 |
# ===================================================================
|
| 724 |
-
#
|
| 725 |
# ===================================================================
|
| 726 |
|
| 727 |
-
agent = AgenticRAGAgent() # β Important: global instance
|
| 728 |
-
|
| 729 |
def create_interface():
|
|
|
|
|
|
|
| 730 |
with gr.Blocks(title="π€ AI Research Agent", theme=gr.themes.Soft()) as interface:
|
| 731 |
gr.HTML("""
|
| 732 |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px;">
|
|
@@ -780,56 +807,57 @@ def create_interface():
|
|
| 780 |
gr.HTML(f"""
|
| 781 |
<div style="padding: 10px;">
|
| 782 |
<p><strong>Text-to-Speech (gTTS):</strong> {'β
Available' if GTTS_AVAILABLE else 'β Not Available'}</p>
|
| 783 |
-
<p><
|
| 784 |
<p><em>Voice output: Auto-plays with responses</em></p>
|
| 785 |
</div>
|
| 786 |
""")
|
| 787 |
|
| 788 |
-
#
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
concurrency_limit=1
|
| 803 |
-
)
|
| 804 |
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
)
|
| 809 |
|
| 810 |
-
file_upload.change(
|
| 811 |
-
fn=agent.upload_documents,
|
| 812 |
-
inputs=[file_upload],
|
| 813 |
-
outputs=[upload_status]
|
| 814 |
-
)
|
| 815 |
|
| 816 |
apply_btn.click(
|
| 817 |
-
|
| 818 |
-
inputs=[
|
| 819 |
-
|
| 820 |
-
|
|
|
|
|
|
|
| 821 |
outputs=[settings_status]
|
| 822 |
)
|
| 823 |
|
| 824 |
return interface
|
| 825 |
|
| 826 |
-
|
| 827 |
# ===================================================================
|
| 828 |
-
# MAIN
|
| 829 |
# ===================================================================
|
| 830 |
|
| 831 |
if __name__ == "__main__":
|
| 832 |
print("π Launching AI Research Agent on Hugging Face Spaces...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 833 |
app = create_interface()
|
| 834 |
-
app.
|
| 835 |
-
app.launch()
|
|
|
|
| 48 |
logger = logging.getLogger(__name__)
|
| 49 |
|
| 50 |
# ===================================================================
|
| 51 |
+
# UTILITY CLASSES
|
| 52 |
# ===================================================================
|
| 53 |
|
| 54 |
class WebSearchTool:
|
|
|
|
| 111 |
|
| 112 |
|
| 113 |
# ===================================================================
|
| 114 |
+
# DOCUMENT PROCESSING
|
| 115 |
# ===================================================================
|
| 116 |
|
| 117 |
class DocumentProcessor:
|
|
|
|
| 244 |
|
| 245 |
|
| 246 |
# ===================================================================
|
| 247 |
+
# RETRIEVER
|
| 248 |
# ===================================================================
|
| 249 |
|
| 250 |
class DocumentRetriever:
|
|
|
|
| 285 |
|
| 286 |
|
| 287 |
# ===================================================================
|
| 288 |
+
# AGENTIC TOOLS
|
| 289 |
# ===================================================================
|
| 290 |
|
| 291 |
class AgenticTools:
|
|
|
|
| 455 |
|
| 456 |
|
| 457 |
# ===================================================================
|
| 458 |
+
# MAIN AGENT CLASS
|
| 459 |
# ===================================================================
|
| 460 |
|
| 461 |
class AgenticRAGAgent:
|
|
|
|
| 492 |
print(f"β Error: {e}")
|
| 493 |
|
| 494 |
def clean_text_for_speech(self, text):
|
| 495 |
+
"""Clean text for TTS"""
|
| 496 |
if not text:
|
| 497 |
return ""
|
| 498 |
+
|
| 499 |
+
# Remove markdown formatting
|
| 500 |
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
|
| 501 |
text = re.sub(r'\*([^*]+)\*', r'\1', text)
|
| 502 |
text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
|
|
|
|
| 505 |
text = re.sub(r'`([^`]+)`', r'\1', text)
|
| 506 |
text = re.sub(r'^[\s]*[-*+β’]\s+', '', text, flags=re.MULTILINE)
|
| 507 |
text = re.sub(r'^[\s]*\d+\.\s+', '', text, flags=re.MULTILINE)
|
| 508 |
+
|
| 509 |
+
# Remove emojis
|
| 510 |
+
emoji_pattern = re.compile(
|
| 511 |
+
"["
|
| 512 |
+
"\U0001F600-\U0001F64F"
|
| 513 |
+
"\U0001F300-\U0001F5FF"
|
| 514 |
+
"\U0001F680-\U0001F6FF"
|
| 515 |
+
"\U0001F1E0-\U0001F1FF"
|
| 516 |
+
"\U00002702-\U000027B0"
|
| 517 |
+
"\U000024C2-\U0001F251"
|
| 518 |
+
"\U0001F900-\U0001F9FF"
|
| 519 |
+
"\U00002600-\U000026FF"
|
| 520 |
+
"\U00002700-\U000027BF"
|
| 521 |
+
"]+"
|
| 522 |
+
)
|
| 523 |
text = emoji_pattern.sub('', text)
|
| 524 |
text = re.sub(r'\s+', ' ', text)
|
| 525 |
text = re.sub(r'\n+', '. ', text)
|
| 526 |
text = text.strip()
|
| 527 |
text = re.sub(r'\.+', '.', text)
|
| 528 |
+
|
| 529 |
return text
|
| 530 |
|
| 531 |
def generate_audio_response(self, text):
|
| 532 |
+
"""Generate audio using gTTS"""
|
| 533 |
if not text or not GTTS_AVAILABLE:
|
| 534 |
return None
|
| 535 |
+
|
| 536 |
clean_text = self.clean_text_for_speech(text)
|
| 537 |
if not clean_text:
|
| 538 |
return None
|
| 539 |
+
|
| 540 |
try:
|
| 541 |
temp_dir = tempfile.gettempdir()
|
| 542 |
timestamp = int(time.time())
|
| 543 |
audio_file = os.path.join(temp_dir, f"response_{timestamp}.mp3")
|
| 544 |
+
|
| 545 |
tts = gTTS(text=clean_text, lang='en', slow=False)
|
| 546 |
tts.save(audio_file)
|
| 547 |
return audio_file
|
|
|
|
| 560 |
def get_simple_answer(self, query, retrieved_docs):
|
| 561 |
if not self.groq_client:
|
| 562 |
return "Error: Groq API not configured"
|
| 563 |
+
|
| 564 |
context = "\n\n".join([doc.get('content', str(doc)) for doc in retrieved_docs[:5]])
|
| 565 |
prompt = f"""Based on this context, provide a clear answer.
|
| 566 |
Context: {context}
|
| 567 |
Question: {query}
|
| 568 |
Answer:"""
|
| 569 |
+
|
| 570 |
try:
|
| 571 |
response = self.groq_client.chat.completions.create(
|
| 572 |
model="llama-3.1-8b-instant",
|
|
|
|
| 595 |
progress(0.5, desc="Generating response...")
|
| 596 |
response = self.get_greeting_response(query)
|
| 597 |
chat_history.append({"role": "assistant", "content": response})
|
| 598 |
+
|
| 599 |
progress(0.8, desc="π Generating voice...")
|
| 600 |
audio_file = self.generate_audio_response(response)
|
| 601 |
+
|
| 602 |
return chat_history, "", audio_file
|
| 603 |
|
| 604 |
progress(0.1, desc="π§ Planning...")
|
|
|
|
| 648 |
if self.synthesizer:
|
| 649 |
final_answer = self.synthesizer.synthesize_results(query, results, self.temperature, self.max_tokens)
|
| 650 |
else:
|
| 651 |
+
successful = [r['result'] for r in results.values() if r.get('success')]
|
| 652 |
final_answer = f"Based on available info: {' '.join(map(str, successful))}"
|
| 653 |
|
| 654 |
progress(0.9, desc="π Evaluating...")
|
|
|
|
| 680 |
return chat_history, "", audio_file
|
| 681 |
|
| 682 |
except Exception as e:
|
| 683 |
+
error = f"β Error: {str(e)}"
|
| 684 |
chat_history.append({"role": "assistant", "content": error})
|
| 685 |
return chat_history, "", None
|
| 686 |
|
|
|
|
| 699 |
dest = os.path.join("sample_data", original)
|
| 700 |
with open(dest, "wb") as dst:
|
| 701 |
dst.write(file.read())
|
| 702 |
+
|
| 703 |
uploaded.append(original)
|
| 704 |
|
| 705 |
if not uploaded:
|
|
|
|
| 748 |
|
| 749 |
|
| 750 |
# ===================================================================
|
| 751 |
+
# GRADIO INTERFACE (COMPATIBLE WITH GRADIO 4.27)
|
| 752 |
# ===================================================================
|
| 753 |
|
|
|
|
|
|
|
| 754 |
def create_interface():
|
| 755 |
+
agent = AgenticRAGAgent()
|
| 756 |
+
|
| 757 |
with gr.Blocks(title="π€ AI Research Agent", theme=gr.themes.Soft()) as interface:
|
| 758 |
gr.HTML("""
|
| 759 |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px;">
|
|
|
|
| 807 |
gr.HTML(f"""
|
| 808 |
<div style="padding: 10px;">
|
| 809 |
<p><strong>Text-to-Speech (gTTS):</strong> {'β
Available' if GTTS_AVAILABLE else 'β Not Available'}</p>
|
| 810 |
+
<p><strong>Speech-to-Text:</strong> {'β
Available' if STT_AVAILABLE else 'β Not Available (HF Spaces limitation)'}</p>
|
| 811 |
<p><em>Voice output: Auto-plays with responses</em></p>
|
| 812 |
</div>
|
| 813 |
""")
|
| 814 |
|
| 815 |
+
# -----------------------------
|
| 816 |
+
# Event Handlers (Sync wrapper for async)
|
| 817 |
+
# -----------------------------
|
| 818 |
+
def process_msg(message, history):
|
| 819 |
+
import asyncio
|
| 820 |
+
try:
|
| 821 |
+
loop = asyncio.get_event_loop()
|
| 822 |
+
if loop.is_running():
|
| 823 |
+
future = asyncio.run_coroutine_threadsafe(agent.process_agentic_query(message, history), loop)
|
| 824 |
+
return future.result()
|
| 825 |
+
else:
|
| 826 |
+
return loop.run_until_complete(agent.process_agentic_query(message, history))
|
| 827 |
+
except RuntimeError:
|
| 828 |
+
return asyncio.run(agent.process_agentic_query(message, history))
|
|
|
|
|
|
|
| 829 |
|
| 830 |
+
submit_btn.click(process_msg, inputs=[msg, chatbot], outputs=[chatbot, msg, audio_output])
|
| 831 |
+
msg.submit(process_msg, inputs=[msg, chatbot], outputs=[chatbot, msg, audio_output])
|
| 832 |
+
clear_btn.click(lambda: [], outputs=[chatbot])
|
|
|
|
| 833 |
|
| 834 |
+
file_upload.change(agent.upload_documents, inputs=[file_upload], outputs=[upload_status])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
|
| 836 |
apply_btn.click(
|
| 837 |
+
agent.update_settings,
|
| 838 |
+
inputs=[
|
| 839 |
+
temperature_slider, max_tokens_slider, chunk_size_slider,
|
| 840 |
+
chunk_overlap_slider, retrieval_k_slider, enable_web,
|
| 841 |
+
enable_calc, enable_fact, enable_analysis
|
| 842 |
+
],
|
| 843 |
outputs=[settings_status]
|
| 844 |
)
|
| 845 |
|
| 846 |
return interface
|
| 847 |
|
|
|
|
| 848 |
# ===================================================================
|
| 849 |
+
# MAIN
|
| 850 |
# ===================================================================
|
| 851 |
|
| 852 |
if __name__ == "__main__":
|
| 853 |
print("π Launching AI Research Agent on Hugging Face Spaces...")
|
| 854 |
+
print("β¨ Features:")
|
| 855 |
+
print(" β’ Multi-Tool Integration")
|
| 856 |
+
print(" β’ Intelligent Query Planning")
|
| 857 |
+
print(" β’ Multi-Step Reasoning")
|
| 858 |
+
print(" β’ Result Synthesis")
|
| 859 |
+
print(" β’ Quality Evaluation")
|
| 860 |
+
print(" β’ π Voice Output (Text-to-Speech)")
|
| 861 |
+
|
| 862 |
app = create_interface()
|
| 863 |
+
app.launch()
|
|
|