Spaces:
Runtime error
Runtime error
Charles Azam
commited on
Commit
·
892c58b
1
Parent(s):
1a1c419
clean: run linting and formating on repo
Browse files- gradio_app.py +4 -4
- src/deepengineer/backend/gradio_tools.py +10 -8
- src/deepengineer/deepsearch/main_agent.py +21 -9
- src/deepengineer/deepsearch/main_deep_search.py +8 -4
- src/deepengineer/deepsearch/scawl_web_agent.py +7 -4
- src/deepengineer/logging_tools.py +3 -1
- tests/deepsearch/test_main_agent.py +2 -2
- tests/deepsearch/test_web_agent.py +0 -1
gradio_app.py
CHANGED
|
@@ -4,12 +4,12 @@ from deepengineer.common_path import DATA_DIR
|
|
| 4 |
|
| 5 |
with gr.Blocks() as demo:
|
| 6 |
gr.Markdown("# Agent Interface with Real‑Time Tool Logging")
|
| 7 |
-
user_input
|
| 8 |
-
|
| 9 |
log_output = gr.Textbox(label="Tool Invocation Log", interactive=False)
|
| 10 |
-
|
| 11 |
agent_output = gr.Markdown(
|
| 12 |
-
label="Agent Response",
|
| 13 |
)
|
| 14 |
|
| 15 |
send = gr.Button("Send")
|
|
|
|
| 4 |
|
| 5 |
with gr.Blocks() as demo:
|
| 6 |
gr.Markdown("# Agent Interface with Real‑Time Tool Logging")
|
| 7 |
+
user_input = gr.Textbox(label="User Message")
|
| 8 |
+
|
| 9 |
log_output = gr.Textbox(label="Tool Invocation Log", interactive=False)
|
| 10 |
+
|
| 11 |
agent_output = gr.Markdown(
|
| 12 |
+
label="Agent Response",
|
| 13 |
)
|
| 14 |
|
| 15 |
send = gr.Button("Send")
|
src/deepengineer/backend/gradio_tools.py
CHANGED
|
@@ -15,14 +15,14 @@ def parse_markdown_images(markdown_text: str, image_dir: Path) -> str:
|
|
| 15 |
if not markdown_text or not image_dir:
|
| 16 |
return markdown_text
|
| 17 |
|
| 18 |
-
image_pattern = r
|
| 19 |
|
| 20 |
def replace_image_path(match):
|
| 21 |
alt_text = match.group(1)
|
| 22 |
image_name = match.group(2)
|
| 23 |
# Always use image_dir/image_name
|
| 24 |
-
new_path = "gradio_api/file="+str(Path(image_dir) / image_name)
|
| 25 |
-
return f
|
| 26 |
|
| 27 |
return re.sub(image_pattern, replace_image_path, markdown_text)
|
| 28 |
|
|
@@ -37,7 +37,7 @@ def run_agent_stream(user_input: str):
|
|
| 37 |
Yields tuples: (agent_output, log_output)
|
| 38 |
"""
|
| 39 |
log_queue = queue.Queue()
|
| 40 |
-
|
| 41 |
# empty queue before each run
|
| 42 |
while not log_queue.empty():
|
| 43 |
print("Emptying log queue")
|
|
@@ -47,7 +47,9 @@ def run_agent_stream(user_input: str):
|
|
| 47 |
done = threading.Event()
|
| 48 |
|
| 49 |
def _worker():
|
| 50 |
-
answer_container["text"], answer_container["image_dir"] = main_search(
|
|
|
|
|
|
|
| 51 |
done.set()
|
| 52 |
|
| 53 |
threading.Thread(target=_worker, daemon=True).start()
|
|
@@ -70,13 +72,13 @@ def run_agent_stream(user_input: str):
|
|
| 70 |
# Process the final answer to include images
|
| 71 |
final_answer = answer_container["text"]
|
| 72 |
image_dir = answer_container["image_dir"]
|
| 73 |
-
|
| 74 |
if final_answer and image_dir:
|
| 75 |
final_answer = parse_markdown_images(final_answer, image_dir)
|
| 76 |
-
|
| 77 |
final_answer = final_answer.replace("```python", "")
|
| 78 |
final_answer = final_answer.replace("```markdown", "")
|
| 79 |
final_answer = final_answer.replace("```", "")
|
| 80 |
|
| 81 |
# final yield: agent_output filled with processed markdown, log_output frozen
|
| 82 |
-
yield (final_answer, log_buffer.rstrip())
|
|
|
|
| 15 |
if not markdown_text or not image_dir:
|
| 16 |
return markdown_text
|
| 17 |
|
| 18 |
+
image_pattern = r"!\[([^\]]*)\]\(([^)]+)\)"
|
| 19 |
|
| 20 |
def replace_image_path(match):
|
| 21 |
alt_text = match.group(1)
|
| 22 |
image_name = match.group(2)
|
| 23 |
# Always use image_dir/image_name
|
| 24 |
+
new_path = "gradio_api/file=" + str(Path(image_dir) / image_name)
|
| 25 |
+
return f""
|
| 26 |
|
| 27 |
return re.sub(image_pattern, replace_image_path, markdown_text)
|
| 28 |
|
|
|
|
| 37 |
Yields tuples: (agent_output, log_output)
|
| 38 |
"""
|
| 39 |
log_queue = queue.Queue()
|
| 40 |
+
|
| 41 |
# empty queue before each run
|
| 42 |
while not log_queue.empty():
|
| 43 |
print("Emptying log queue")
|
|
|
|
| 47 |
done = threading.Event()
|
| 48 |
|
| 49 |
def _worker():
|
| 50 |
+
answer_container["text"], answer_container["image_dir"] = main_search(
|
| 51 |
+
user_input, log_queue
|
| 52 |
+
)
|
| 53 |
done.set()
|
| 54 |
|
| 55 |
threading.Thread(target=_worker, daemon=True).start()
|
|
|
|
| 72 |
# Process the final answer to include images
|
| 73 |
final_answer = answer_container["text"]
|
| 74 |
image_dir = answer_container["image_dir"]
|
| 75 |
+
|
| 76 |
if final_answer and image_dir:
|
| 77 |
final_answer = parse_markdown_images(final_answer, image_dir)
|
| 78 |
+
|
| 79 |
final_answer = final_answer.replace("```python", "")
|
| 80 |
final_answer = final_answer.replace("```markdown", "")
|
| 81 |
final_answer = final_answer.replace("```", "")
|
| 82 |
|
| 83 |
# final yield: agent_output filled with processed markdown, log_output frozen
|
| 84 |
+
yield (final_answer, log_buffer.rstrip())
|
src/deepengineer/deepsearch/main_agent.py
CHANGED
|
@@ -25,9 +25,11 @@ def create_output_image_path(random_name_images: int | None = None):
|
|
| 25 |
return output_image_path
|
| 26 |
|
| 27 |
|
| 28 |
-
|
| 29 |
def create_main_search_agent(
|
| 30 |
-
model_id="deepseek/deepseek-reasoner",
|
|
|
|
|
|
|
|
|
|
| 31 |
):
|
| 32 |
"""
|
| 33 |
Simple agent that can search the web and answer the question. This is much faster and better for simple questions that do not require deep research.
|
|
@@ -36,20 +38,26 @@ def create_main_search_agent(
|
|
| 36 |
model = LiteLLMModel(model_id=model_id)
|
| 37 |
if database is None:
|
| 38 |
database = DataBase()
|
| 39 |
-
|
| 40 |
output_image_path = output_image_path or DATA_DIR / "images"
|
| 41 |
output_image_path.mkdir(parents=True, exist_ok=True)
|
| 42 |
|
| 43 |
# Web search and crawling tools
|
| 44 |
WEB_SEARCH_TOOLS = [
|
| 45 |
-
SearchTool(
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
GetTableOfContentsTool(log_queue=log_queue, database=database),
|
| 49 |
GetMarkdownTool(log_queue=log_queue, database=database),
|
| 50 |
GetPagesContentTool(log_queue=log_queue, database=database),
|
| 51 |
FindInMarkdownTool(log_queue=log_queue, database=database),
|
| 52 |
-
SaveMatplotlibFigTool(log_queue=log_queue,output_dir=output_image_path),
|
| 53 |
]
|
| 54 |
|
| 55 |
search_agent = CodeAgent(
|
|
@@ -100,9 +108,13 @@ Failure or 'I cannot answer' or 'None found' will not be tolerated, success will
|
|
| 100 |
Run verification steps if that's needed, you must make sure you find the correct answer! Here is the task:
|
| 101 |
{task}
|
| 102 |
"""
|
| 103 |
-
agent = create_main_search_agent(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
answer = agent.run(MAIN_PROMPT.format(task=task))
|
| 105 |
-
|
| 106 |
return answer, output_image_path
|
| 107 |
|
| 108 |
|
|
|
|
| 25 |
return output_image_path
|
| 26 |
|
| 27 |
|
|
|
|
| 28 |
def create_main_search_agent(
|
| 29 |
+
model_id="deepseek/deepseek-reasoner",
|
| 30 |
+
database: DataBase | None = None,
|
| 31 |
+
log_queue: queue.Queue | None = None,
|
| 32 |
+
output_image_path: Path | None = None,
|
| 33 |
):
|
| 34 |
"""
|
| 35 |
Simple agent that can search the web and answer the question. This is much faster and better for simple questions that do not require deep research.
|
|
|
|
| 38 |
model = LiteLLMModel(model_id=model_id)
|
| 39 |
if database is None:
|
| 40 |
database = DataBase()
|
| 41 |
+
|
| 42 |
output_image_path = output_image_path or DATA_DIR / "images"
|
| 43 |
output_image_path.mkdir(parents=True, exist_ok=True)
|
| 44 |
|
| 45 |
# Web search and crawling tools
|
| 46 |
WEB_SEARCH_TOOLS = [
|
| 47 |
+
SearchTool(
|
| 48 |
+
log_queue=log_queue,
|
| 49 |
+
),
|
| 50 |
+
ArxivSearchTool(
|
| 51 |
+
log_queue=log_queue,
|
| 52 |
+
),
|
| 53 |
+
ScientificSearchTool(
|
| 54 |
+
log_queue=log_queue,
|
| 55 |
+
),
|
| 56 |
GetTableOfContentsTool(log_queue=log_queue, database=database),
|
| 57 |
GetMarkdownTool(log_queue=log_queue, database=database),
|
| 58 |
GetPagesContentTool(log_queue=log_queue, database=database),
|
| 59 |
FindInMarkdownTool(log_queue=log_queue, database=database),
|
| 60 |
+
SaveMatplotlibFigTool(log_queue=log_queue, output_dir=output_image_path),
|
| 61 |
]
|
| 62 |
|
| 63 |
search_agent = CodeAgent(
|
|
|
|
| 108 |
Run verification steps if that's needed, you must make sure you find the correct answer! Here is the task:
|
| 109 |
{task}
|
| 110 |
"""
|
| 111 |
+
agent = create_main_search_agent(
|
| 112 |
+
model_id="mistral/mistral-medium-latest",
|
| 113 |
+
log_queue=log_queue,
|
| 114 |
+
output_image_path=output_image_path,
|
| 115 |
+
)
|
| 116 |
answer = agent.run(MAIN_PROMPT.format(task=task))
|
| 117 |
+
|
| 118 |
return answer, output_image_path
|
| 119 |
|
| 120 |
|
src/deepengineer/deepsearch/main_deep_search.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
"""work in progress"""
|
|
|
|
| 2 |
from pathlib import Path
|
| 3 |
import random
|
| 4 |
from smolagents import CodeAgent, LiteLLMModel
|
|
@@ -7,11 +8,13 @@ from deepengineer.deepsearch.scawl_web_agent import create_web_search_agent
|
|
| 7 |
from deepengineer.deepsearch.draw_agent import SaveMatplotlibFigTool
|
| 8 |
from deepengineer.common_path import DATA_DIR
|
| 9 |
|
|
|
|
| 10 |
def _create_output_image_path(image_folder_suffix: int | None = None):
|
| 11 |
output_image_path = Path(DATA_DIR) / f"images_{image_folder_suffix}"
|
| 12 |
output_image_path.mkdir(parents=True, exist_ok=True)
|
| 13 |
return output_image_path
|
| 14 |
|
|
|
|
| 15 |
def create_main_deep_search_agent(
|
| 16 |
main_model_id="deepseek/deepseek-reasoner",
|
| 17 |
web_search_model_id="deepseek/deepseek-reasoner",
|
|
@@ -25,10 +28,11 @@ def create_main_deep_search_agent(
|
|
| 25 |
web_search_agent = create_web_search_agent(
|
| 26 |
model_id=web_search_model_id, database=database
|
| 27 |
)
|
| 28 |
-
|
| 29 |
-
|
| 30 |
image_folder_suffix = random.randint(1000000, 9999999)
|
| 31 |
-
output_image_path = _create_output_image_path(
|
|
|
|
|
|
|
| 32 |
|
| 33 |
manager_agent = CodeAgent(
|
| 34 |
model=main_model,
|
|
@@ -83,4 +87,4 @@ Run verification steps if that's needed, you must make sure you find the correct
|
|
| 83 |
|
| 84 |
answer = agent.run(MAIN_PROMPT.format(task=task))
|
| 85 |
|
| 86 |
-
print(answer)
|
|
|
|
| 1 |
"""work in progress"""
|
| 2 |
+
|
| 3 |
from pathlib import Path
|
| 4 |
import random
|
| 5 |
from smolagents import CodeAgent, LiteLLMModel
|
|
|
|
| 8 |
from deepengineer.deepsearch.draw_agent import SaveMatplotlibFigTool
|
| 9 |
from deepengineer.common_path import DATA_DIR
|
| 10 |
|
| 11 |
+
|
| 12 |
def _create_output_image_path(image_folder_suffix: int | None = None):
|
| 13 |
output_image_path = Path(DATA_DIR) / f"images_{image_folder_suffix}"
|
| 14 |
output_image_path.mkdir(parents=True, exist_ok=True)
|
| 15 |
return output_image_path
|
| 16 |
|
| 17 |
+
|
| 18 |
def create_main_deep_search_agent(
|
| 19 |
main_model_id="deepseek/deepseek-reasoner",
|
| 20 |
web_search_model_id="deepseek/deepseek-reasoner",
|
|
|
|
| 28 |
web_search_agent = create_web_search_agent(
|
| 29 |
model_id=web_search_model_id, database=database
|
| 30 |
)
|
| 31 |
+
|
|
|
|
| 32 |
image_folder_suffix = random.randint(1000000, 9999999)
|
| 33 |
+
output_image_path = _create_output_image_path(
|
| 34 |
+
image_folder_suffix=image_folder_suffix
|
| 35 |
+
)
|
| 36 |
|
| 37 |
manager_agent = CodeAgent(
|
| 38 |
model=main_model,
|
|
|
|
| 87 |
|
| 88 |
answer = agent.run(MAIN_PROMPT.format(task=task))
|
| 89 |
|
| 90 |
+
print(answer)
|
src/deepengineer/deepsearch/scawl_web_agent.py
CHANGED
|
@@ -20,6 +20,7 @@ from deepengineer.webcrawler.pdf_utils import (
|
|
| 20 |
from deepengineer.logging_tools import LoggingTool
|
| 21 |
import queue
|
| 22 |
|
|
|
|
| 23 |
class ToolNames(Enum):
|
| 24 |
# Search tools
|
| 25 |
SEARCH_TOOL = "web_search_tool"
|
|
@@ -54,7 +55,7 @@ class SearchTool(LoggingTool):
|
|
| 54 |
},
|
| 55 |
}
|
| 56 |
output_type = "object"
|
| 57 |
-
|
| 58 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 59 |
super().__init__(log_queue=log_queue)
|
| 60 |
|
|
@@ -80,7 +81,7 @@ class ArxivSearchTool(LoggingTool):
|
|
| 80 |
}
|
| 81 |
}
|
| 82 |
output_type = "object"
|
| 83 |
-
|
| 84 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 85 |
super().__init__(log_queue=log_queue)
|
| 86 |
|
|
@@ -102,7 +103,7 @@ class PubmedSearchTool(LoggingTool):
|
|
| 102 |
}
|
| 103 |
}
|
| 104 |
output_type = "object"
|
| 105 |
-
|
| 106 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 107 |
super().__init__(log_queue=log_queue)
|
| 108 |
|
|
@@ -225,7 +226,9 @@ class FindInMarkdownTool(LoggingTool):
|
|
| 225 |
|
| 226 |
|
| 227 |
def create_web_search_agent(
|
| 228 |
-
model_id="deepseek/deepseek-reasoner",
|
|
|
|
|
|
|
| 229 |
):
|
| 230 |
"""Create a web search agent with search, crawling, and PDF analysis capabilities."""
|
| 231 |
|
|
|
|
| 20 |
from deepengineer.logging_tools import LoggingTool
|
| 21 |
import queue
|
| 22 |
|
| 23 |
+
|
| 24 |
class ToolNames(Enum):
|
| 25 |
# Search tools
|
| 26 |
SEARCH_TOOL = "web_search_tool"
|
|
|
|
| 55 |
},
|
| 56 |
}
|
| 57 |
output_type = "object"
|
| 58 |
+
|
| 59 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 60 |
super().__init__(log_queue=log_queue)
|
| 61 |
|
|
|
|
| 81 |
}
|
| 82 |
}
|
| 83 |
output_type = "object"
|
| 84 |
+
|
| 85 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 86 |
super().__init__(log_queue=log_queue)
|
| 87 |
|
|
|
|
| 103 |
}
|
| 104 |
}
|
| 105 |
output_type = "object"
|
| 106 |
+
|
| 107 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 108 |
super().__init__(log_queue=log_queue)
|
| 109 |
|
|
|
|
| 226 |
|
| 227 |
|
| 228 |
def create_web_search_agent(
|
| 229 |
+
model_id="deepseek/deepseek-reasoner",
|
| 230 |
+
database: DataBase | None = None,
|
| 231 |
+
log_queue: queue.Queue | None = None,
|
| 232 |
):
|
| 233 |
"""Create a web search agent with search, crawling, and PDF analysis capabilities."""
|
| 234 |
|
src/deepengineer/logging_tools.py
CHANGED
|
@@ -2,14 +2,16 @@ import queue
|
|
| 2 |
from typing import Any
|
| 3 |
from smolagents import Tool
|
| 4 |
|
|
|
|
| 5 |
class LoggingTool(Tool):
|
| 6 |
"""
|
| 7 |
Base class for tools that can push logs to a queue.
|
| 8 |
"""
|
|
|
|
| 9 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 10 |
super().__init__()
|
| 11 |
self.log_queue = log_queue
|
| 12 |
|
| 13 |
def push_log(self, msg: str):
|
| 14 |
if self.log_queue:
|
| 15 |
-
self.log_queue.put(msg)
|
|
|
|
| 2 |
from typing import Any
|
| 3 |
from smolagents import Tool
|
| 4 |
|
| 5 |
+
|
| 6 |
class LoggingTool(Tool):
|
| 7 |
"""
|
| 8 |
Base class for tools that can push logs to a queue.
|
| 9 |
"""
|
| 10 |
+
|
| 11 |
def __init__(self, log_queue: queue.Queue | None = None):
|
| 12 |
super().__init__()
|
| 13 |
self.log_queue = log_queue
|
| 14 |
|
| 15 |
def push_log(self, msg: str):
|
| 16 |
if self.log_queue:
|
| 17 |
+
self.log_queue.put(msg)
|
tests/deepsearch/test_main_agent.py
CHANGED
|
@@ -15,6 +15,6 @@ def test_main_agent():
|
|
| 15 |
main_search(
|
| 16 |
task="""
|
| 17 |
Search a paper called "High Energy Physics Opportunities Using Reactor Antineutrinos" on arXiv, download it and extract the table of contents
|
| 18 |
-
""",
|
|
|
|
| 19 |
)
|
| 20 |
-
|
|
|
|
| 15 |
main_search(
|
| 16 |
task="""
|
| 17 |
Search a paper called "High Energy Physics Opportunities Using Reactor Antineutrinos" on arXiv, download it and extract the table of contents
|
| 18 |
+
""",
|
| 19 |
+
log_queue=log_queue,
|
| 20 |
)
|
|
|
tests/deepsearch/test_web_agent.py
CHANGED
|
@@ -15,4 +15,3 @@ def test_run_web_search_agent():
|
|
| 15 |
)
|
| 16 |
is not None
|
| 17 |
)
|
| 18 |
-
|
|
|
|
| 15 |
)
|
| 16 |
is not None
|
| 17 |
)
|
|
|