Spaces:
Runtime error
Runtime error
Leonardo
commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -11,6 +11,7 @@ import os
|
|
| 11 |
import re
|
| 12 |
import shutil
|
| 13 |
import datetime
|
|
|
|
| 14 |
|
| 15 |
from dotenv import load_dotenv
|
| 16 |
from huggingface_hub import login
|
|
@@ -38,9 +39,9 @@ from smolagents import (
|
|
| 38 |
TransformersModel,
|
| 39 |
GoogleSearchTool,
|
| 40 |
Tool,
|
|
|
|
| 41 |
)
|
| 42 |
-
from smolagents.
|
| 43 |
-
from smolagents.gradio_ui import pull_messages_from_step, handle_agent_output_types
|
| 44 |
|
| 45 |
# ------------------------ Configuration and Setup ------------------------
|
| 46 |
# Constants and configurations
|
|
@@ -247,6 +248,9 @@ def create_agent():
|
|
| 247 |
# Create tool instances with proper error handling
|
| 248 |
web_tools = ToolRegistry.load_web_tools(model, browser, text_limit)
|
| 249 |
|
|
|
|
|
|
|
|
|
|
| 250 |
try:
|
| 251 |
doc_tools = ToolRegistry.load_document_tools()
|
| 252 |
except AssertionError as e:
|
|
@@ -265,6 +269,7 @@ def create_agent():
|
|
| 265 |
tool
|
| 266 |
for tool in (
|
| 267 |
[visualizer]
|
|
|
|
| 268 |
+ web_tools
|
| 269 |
+ doc_tools
|
| 270 |
+ ([image_generator] if image_generator else [])
|
|
@@ -280,61 +285,44 @@ def create_agent():
|
|
| 280 |
return CodeAgent(
|
| 281 |
model=model,
|
| 282 |
tools=all_tools,
|
| 283 |
-
max_steps=12
|
| 284 |
verbosity_level=2,
|
| 285 |
additional_authorized_imports=AUTHORIZED_IMPORTS,
|
| 286 |
planning_interval=4,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
)
|
| 288 |
except Exception as e:
|
| 289 |
print(f"Failed to create agent: {e}")
|
| 290 |
raise RuntimeError(f"Agent creation failed: {e}") from e
|
| 291 |
|
| 292 |
|
| 293 |
-
def
|
| 294 |
-
"""
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
task, stream=True, reset=reset_agent_memory, additional_args=additional_args
|
| 298 |
-
):
|
| 299 |
-
yield from pull_messages_from_step(step_log)
|
| 300 |
|
| 301 |
-
|
| 302 |
-
|
| 303 |
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
|
|
|
| 307 |
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
role="assistant",
|
| 312 |
-
content=f"**Final answer:**\n{final_answer.to_string()}\n",
|
| 313 |
-
)
|
| 314 |
-
elif isinstance(final_answer, AgentImage):
|
| 315 |
-
yield gr.ChatMessage(
|
| 316 |
-
role="assistant",
|
| 317 |
-
content={"image": final_answer.to_string(), "type": "file"},
|
| 318 |
-
)
|
| 319 |
-
elif isinstance(final_answer, AgentAudio):
|
| 320 |
-
yield gr.ChatMessage(
|
| 321 |
-
role="assistant",
|
| 322 |
-
content={"audio": final_answer.to_string(), "type": "file"},
|
| 323 |
-
)
|
| 324 |
-
else:
|
| 325 |
-
yield gr.ChatMessage(
|
| 326 |
-
role="assistant", content=f"**Final answer:** {str(final_answer)}"
|
| 327 |
-
)
|
| 328 |
-
else:
|
| 329 |
-
yield gr.ChatMessage(
|
| 330 |
-
role="assistant",
|
| 331 |
-
content="No final answer was generated. Please try again.",
|
| 332 |
-
)
|
| 333 |
-
except Exception as e:
|
| 334 |
-
yield gr.ChatMessage(
|
| 335 |
-
role="assistant",
|
| 336 |
-
content=f"**Error occurred during processing**: {str(e)}\n\nPlease try again with a different query or check your inputs.",
|
| 337 |
-
)
|
| 338 |
|
| 339 |
|
| 340 |
# ------------------------ Gradio UI Components ------------------------
|
|
@@ -376,7 +364,19 @@ class GradioUI:
|
|
| 376 |
|
| 377 |
try:
|
| 378 |
# Check if agent should be reset (e.g., if too many requests)
|
| 379 |
-
reset_needed = session_state["request_count"] > 15
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
|
| 381 |
for msg in stream_to_gradio(
|
| 382 |
session_state["agent"], task=prompt, reset_agent_memory=reset_needed
|
|
@@ -418,6 +418,11 @@ class GradioUI:
|
|
| 418 |
|
| 419 |
# Check MIME type
|
| 420 |
mime_type, _ = mimetypes.guess_type(file.name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
if mime_type not in ALLOWED_FILE_TYPES:
|
| 422 |
allowed_extensions = [
|
| 423 |
t.rsplit("/", maxsplit=1)[-1] for t in ALLOWED_FILE_TYPES
|
|
@@ -462,20 +467,34 @@ class GradioUI:
|
|
| 462 |
category = "images"
|
| 463 |
elif ext in [".mp3", ".wav", ".ogg"]:
|
| 464 |
category = "audio"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
else:
|
| 466 |
category = "documents"
|
| 467 |
|
| 468 |
if category not in file_info:
|
| 469 |
file_info[category] = []
|
| 470 |
-
file_info[category].append(
|
|
|
|
|
|
|
| 471 |
|
| 472 |
# Format file information for the agent
|
| 473 |
file_message = "\nYou have been provided with these files:\n"
|
| 474 |
for category, files in file_info.items():
|
| 475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
|
| 477 |
message += file_message
|
| 478 |
-
message +=
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
|
| 480 |
return (
|
| 481 |
message,
|
|
@@ -533,7 +552,7 @@ class GradioUI:
|
|
| 533 |
with gr.Blocks(fill_height=True) as sidebar_demo:
|
| 534 |
with gr.Sidebar():
|
| 535 |
gr.Markdown(
|
| 536 |
-
"""#
|
| 537 |
### Smolagents + Document Tools
|
| 538 |
"""
|
| 539 |
)
|
|
@@ -554,7 +573,7 @@ class GradioUI:
|
|
| 554 |
# File upload section with better labeling
|
| 555 |
if self.file_upload_folder is not None:
|
| 556 |
with gr.Group():
|
| 557 |
-
gr.Markdown("
|
| 558 |
upload_file = gr.File(
|
| 559 |
label="Upload files for analysis",
|
| 560 |
file_types=[
|
|
@@ -592,6 +611,23 @@ class GradioUI:
|
|
| 592 |
[uploaded_files_display],
|
| 593 |
)
|
| 594 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
gr.HTML("<br><hr><h4><center>Powered by:</center></h4>")
|
| 596 |
with gr.Row():
|
| 597 |
gr.HTML(
|
|
@@ -648,14 +684,14 @@ class GradioUI:
|
|
| 648 |
def _create_mobile_layout(self):
|
| 649 |
"""Create the mobile layout (simpler without sidebar)."""
|
| 650 |
with gr.Blocks(fill_height=True) as simple_demo:
|
| 651 |
-
gr.Markdown("""#
|
| 652 |
# Add session state to store session-specific data
|
| 653 |
session_state = gr.State({})
|
| 654 |
stored_messages = gr.State([])
|
| 655 |
file_uploads_log = gr.State([])
|
| 656 |
|
| 657 |
chatbot = gr.Chatbot(
|
| 658 |
-
label="
|
| 659 |
type="messages",
|
| 660 |
avatar_images=(
|
| 661 |
None,
|
|
@@ -667,7 +703,10 @@ class GradioUI:
|
|
| 667 |
|
| 668 |
# If an upload folder is provided, enable the upload feature
|
| 669 |
if self.file_upload_folder is not None:
|
| 670 |
-
upload_file = gr.File(
|
|
|
|
|
|
|
|
|
|
| 671 |
upload_status = gr.Textbox(
|
| 672 |
label="Upload Status", interactive=False, visible=False
|
| 673 |
)
|
|
@@ -678,11 +717,21 @@ class GradioUI:
|
|
| 678 |
)
|
| 679 |
|
| 680 |
text_input = gr.Textbox(
|
| 681 |
-
lines=
|
| 682 |
-
label="
|
| 683 |
-
placeholder="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
)
|
| 685 |
-
launch_research_btn = gr.Button("Run", variant="primary")
|
| 686 |
|
| 687 |
self._connect_event_handlers(
|
| 688 |
text_input,
|
|
@@ -757,6 +806,9 @@ def main():
|
|
| 757 |
# Ensure downloads folder exists
|
| 758 |
os.makedirs(f"./{BROWSER_CONFIG['downloads_folder']}", exist_ok=True)
|
| 759 |
|
|
|
|
|
|
|
|
|
|
| 760 |
# Launch UI
|
| 761 |
GradioUI(file_upload_folder="uploaded_files").launch()
|
| 762 |
|
|
|
|
| 11 |
import re
|
| 12 |
import shutil
|
| 13 |
import datetime
|
| 14 |
+
from typing import Optional, List, Dict, Any
|
| 15 |
|
| 16 |
from dotenv import load_dotenv
|
| 17 |
from huggingface_hub import login
|
|
|
|
| 39 |
TransformersModel,
|
| 40 |
GoogleSearchTool,
|
| 41 |
Tool,
|
| 42 |
+
FinalAnswerTool,
|
| 43 |
)
|
| 44 |
+
from smolagents.gradio_ui import pull_messages_from_step, stream_to_gradio
|
|
|
|
| 45 |
|
| 46 |
# ------------------------ Configuration and Setup ------------------------
|
| 47 |
# Constants and configurations
|
|
|
|
| 248 |
# Create tool instances with proper error handling
|
| 249 |
web_tools = ToolRegistry.load_web_tools(model, browser, text_limit)
|
| 250 |
|
| 251 |
+
# Add FinalAnswerTool explicitly to ensure it's available
|
| 252 |
+
final_answer_tool = FinalAnswerTool()
|
| 253 |
+
|
| 254 |
try:
|
| 255 |
doc_tools = ToolRegistry.load_document_tools()
|
| 256 |
except AssertionError as e:
|
|
|
|
| 269 |
tool
|
| 270 |
for tool in (
|
| 271 |
[visualizer]
|
| 272 |
+
+ [final_answer_tool] # Added explicitly
|
| 273 |
+ web_tools
|
| 274 |
+ doc_tools
|
| 275 |
+ ([image_generator] if image_generator else [])
|
|
|
|
| 285 |
return CodeAgent(
|
| 286 |
model=model,
|
| 287 |
tools=all_tools,
|
| 288 |
+
max_steps=15, # Increased from 12 to give more room for complex tasks
|
| 289 |
verbosity_level=2,
|
| 290 |
additional_authorized_imports=AUTHORIZED_IMPORTS,
|
| 291 |
planning_interval=4,
|
| 292 |
+
prompt_templates={
|
| 293 |
+
"system_prompt": """You are a helpful AI assistant with access to various tools.
|
| 294 |
+
Always think step by step, carefully planning your approach to the task.
|
| 295 |
+
When using Python code:
|
| 296 |
+
- Keep your code simple and readable
|
| 297 |
+
- Use the final_answer tool to provide your final response
|
| 298 |
+
Example of how to provide a final answer:
|
| 299 |
+
```python
|
| 300 |
+
final_answer("This is my final answer based on my analysis.")
|
| 301 |
+
```
|
| 302 |
+
"""
|
| 303 |
+
},
|
| 304 |
)
|
| 305 |
except Exception as e:
|
| 306 |
print(f"Failed to create agent: {e}")
|
| 307 |
raise RuntimeError(f"Agent creation failed: {e}") from e
|
| 308 |
|
| 309 |
|
| 310 |
+
def detect_agent_loop(agent):
|
| 311 |
+
"""Check if agent is stuck in a loop of similar errors"""
|
| 312 |
+
if not hasattr(agent, "memory") or not hasattr(agent.memory, "steps"):
|
| 313 |
+
return False
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
+
if len(agent.memory.steps) < 4:
|
| 316 |
+
return False
|
| 317 |
|
| 318 |
+
recent_steps = agent.memory.steps[-4:]
|
| 319 |
+
error_count = sum(
|
| 320 |
+
1 for step in recent_steps if hasattr(step, "error") and step.error is not None
|
| 321 |
+
)
|
| 322 |
|
| 323 |
+
if error_count >= 3:
|
| 324 |
+
return True
|
| 325 |
+
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
|
| 327 |
|
| 328 |
# ------------------------ Gradio UI Components ------------------------
|
|
|
|
| 364 |
|
| 365 |
try:
|
| 366 |
# Check if agent should be reset (e.g., if too many requests)
|
| 367 |
+
reset_needed = session_state["request_count"] > 15 or detect_agent_loop(
|
| 368 |
+
session_state["agent"]
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
# If agent is in a loop, provide a hint
|
| 372 |
+
if detect_agent_loop(session_state["agent"]):
|
| 373 |
+
messages.append(
|
| 374 |
+
gr.ChatMessage(
|
| 375 |
+
role="assistant",
|
| 376 |
+
content="I notice I'm having trouble executing some commands. Let me try a different approach...",
|
| 377 |
+
)
|
| 378 |
+
)
|
| 379 |
+
yield messages
|
| 380 |
|
| 381 |
for msg in stream_to_gradio(
|
| 382 |
session_state["agent"], task=prompt, reset_agent_memory=reset_needed
|
|
|
|
| 418 |
|
| 419 |
# Check MIME type
|
| 420 |
mime_type, _ = mimetypes.guess_type(file.name)
|
| 421 |
+
|
| 422 |
+
# Ensure Markdown files are recognized properly
|
| 423 |
+
if file.name.lower().endswith(".md"):
|
| 424 |
+
mime_type = "text/markdown"
|
| 425 |
+
|
| 426 |
if mime_type not in ALLOWED_FILE_TYPES:
|
| 427 |
allowed_extensions = [
|
| 428 |
t.rsplit("/", maxsplit=1)[-1] for t in ALLOWED_FILE_TYPES
|
|
|
|
| 467 |
category = "images"
|
| 468 |
elif ext in [".mp3", ".wav", ".ogg"]:
|
| 469 |
category = "audio"
|
| 470 |
+
elif ext in [".md"]:
|
| 471 |
+
category = "markdown"
|
| 472 |
+
elif ext in [".pdf"]:
|
| 473 |
+
category = "pdf"
|
| 474 |
else:
|
| 475 |
category = "documents"
|
| 476 |
|
| 477 |
if category not in file_info:
|
| 478 |
file_info[category] = []
|
| 479 |
+
file_info[category].append(
|
| 480 |
+
file_path
|
| 481 |
+
) # Store full path for easier access
|
| 482 |
|
| 483 |
# Format file information for the agent
|
| 484 |
file_message = "\nYou have been provided with these files:\n"
|
| 485 |
for category, files in file_info.items():
|
| 486 |
+
# Convert to filename-only for display
|
| 487 |
+
file_names = [os.path.basename(f) for f in files]
|
| 488 |
+
file_message += f"- {category.capitalize()}: {', '.join(file_names)}\n"
|
| 489 |
+
# Add full paths after names
|
| 490 |
+
file_message += f" Paths: {', '.join(files)}\n"
|
| 491 |
|
| 492 |
message += file_message
|
| 493 |
+
message += (
|
| 494 |
+
"\nUse inspect_file_as_text for documents/markdown/pdf, "
|
| 495 |
+
"visualizer for images, and the appropriate tools for audio files. "
|
| 496 |
+
"Remember to use the full file path when accessing the files."
|
| 497 |
+
)
|
| 498 |
|
| 499 |
return (
|
| 500 |
message,
|
|
|
|
| 552 |
with gr.Blocks(fill_height=True) as sidebar_demo:
|
| 553 |
with gr.Sidebar():
|
| 554 |
gr.Markdown(
|
| 555 |
+
"""# 🔍 OpenDeepResearch
|
| 556 |
### Smolagents + Document Tools
|
| 557 |
"""
|
| 558 |
)
|
|
|
|
| 573 |
# File upload section with better labeling
|
| 574 |
if self.file_upload_folder is not None:
|
| 575 |
with gr.Group():
|
| 576 |
+
gr.Markdown("**📎 Upload Documents**")
|
| 577 |
upload_file = gr.File(
|
| 578 |
label="Upload files for analysis",
|
| 579 |
file_types=[
|
|
|
|
| 611 |
[uploaded_files_display],
|
| 612 |
)
|
| 613 |
|
| 614 |
+
# Add helpful tool usage examples
|
| 615 |
+
with gr.Accordion("Tool Usage Examples", open=False):
|
| 616 |
+
gr.Markdown(
|
| 617 |
+
"""
|
| 618 |
+
### Document Tools
|
| 619 |
+
- "Extract metadata from this document" - Uses frontmatter generator
|
| 620 |
+
- "Clean and format this text" - Uses text cleaner
|
| 621 |
+
|
| 622 |
+
### File Analysis
|
| 623 |
+
- "Analyze this PDF and summarize the key points" - Uses inspect_file_as_text
|
| 624 |
+
- "What's in this image?" - Uses visualizer
|
| 625 |
+
|
| 626 |
+
### Web Search
|
| 627 |
+
- "Find information about XYZ" - Uses search tools
|
| 628 |
+
"""
|
| 629 |
+
)
|
| 630 |
+
|
| 631 |
gr.HTML("<br><hr><h4><center>Powered by:</center></h4>")
|
| 632 |
with gr.Row():
|
| 633 |
gr.HTML(
|
|
|
|
| 684 |
def _create_mobile_layout(self):
|
| 685 |
"""Create the mobile layout (simpler without sidebar)."""
|
| 686 |
with gr.Blocks(fill_height=True) as simple_demo:
|
| 687 |
+
gr.Markdown("""# 🔍 OpenDeepResearch""")
|
| 688 |
# Add session state to store session-specific data
|
| 689 |
session_state = gr.State({})
|
| 690 |
stored_messages = gr.State([])
|
| 691 |
file_uploads_log = gr.State([])
|
| 692 |
|
| 693 |
chatbot = gr.Chatbot(
|
| 694 |
+
label="OpenDeepResearch Assistant",
|
| 695 |
type="messages",
|
| 696 |
avatar_images=(
|
| 697 |
None,
|
|
|
|
| 703 |
|
| 704 |
# If an upload folder is provided, enable the upload feature
|
| 705 |
if self.file_upload_folder is not None:
|
| 706 |
+
upload_file = gr.File(
|
| 707 |
+
label="Upload a file",
|
| 708 |
+
file_types=["pdf", "docx", "txt", "md", "jpg", "png"],
|
| 709 |
+
)
|
| 710 |
upload_status = gr.Textbox(
|
| 711 |
label="Upload Status", interactive=False, visible=False
|
| 712 |
)
|
|
|
|
| 717 |
)
|
| 718 |
|
| 719 |
text_input = gr.Textbox(
|
| 720 |
+
lines=2,
|
| 721 |
+
label="Your question",
|
| 722 |
+
placeholder="Enter your question here",
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
with gr.Row():
|
| 726 |
+
clear_btn = gr.Button("Clear", variant="secondary")
|
| 727 |
+
launch_research_btn = gr.Button("Run", variant="primary")
|
| 728 |
+
|
| 729 |
+
# Connect clear button
|
| 730 |
+
clear_btn.click(
|
| 731 |
+
lambda: ([], [], {"agent": session_state.get("agent")}),
|
| 732 |
+
None,
|
| 733 |
+
[chatbot, stored_messages, session_state],
|
| 734 |
)
|
|
|
|
| 735 |
|
| 736 |
self._connect_event_handlers(
|
| 737 |
text_input,
|
|
|
|
| 806 |
# Ensure downloads folder exists
|
| 807 |
os.makedirs(f"./{BROWSER_CONFIG['downloads_folder']}", exist_ok=True)
|
| 808 |
|
| 809 |
+
# Ensure uploads folder exists
|
| 810 |
+
os.makedirs("uploaded_files", exist_ok=True)
|
| 811 |
+
|
| 812 |
# Launch UI
|
| 813 |
GradioUI(file_upload_folder="uploaded_files").launch()
|
| 814 |
|