Skip to content

Commit dad8fc9

Browse files
committed
add deep research agent
1 parent 47b5b55 commit dad8fc9

File tree

10 files changed

+944
-1461
lines changed

10 files changed

+944
-1461
lines changed

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ json-repair
55
langchain-mistralai==0.2.4
66
MainContentExtractor==0.0.4
77
langchain-ibm==0.3.10
8-
langchain_mcp_adapters==0.0.9
8+
langchain_mcp_adapters==0.0.9
9+
langgraph==0.3.34
10+
langchain-community==0.3.23

src/agent/deep_research/deep_research_agent.py

Lines changed: 845 additions & 345 deletions
Large diffs are not rendered by default.

src/webui/components/agent_settings_tab.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from src.webui.webui_manager import WebuiManager
88
from src.utils import config
99
import logging
10+
from functools import partial
1011

1112
logger = logging.getLogger(__name__)
1213

@@ -23,10 +24,15 @@ def update_model_dropdown(llm_provider):
2324
return gr.Dropdown(choices=[], value="", interactive=True, allow_custom_value=True)
2425

2526

26-
def update_mcp_server(mcp_file: str):
27+
def update_mcp_server(mcp_file: str, webui_manager: WebuiManager):
2728
"""
2829
Update the MCP server.
2930
"""
31+
if hasattr(webui_manager, "bu_controller") and webui_manager.bu_controller:
32+
logger.warning("⚠️ Close controller because mcp file has changed!")
33+
webui_manager.bu_controller.close_mcp_client()
34+
webui_manager.bu_controller = None
35+
3036
if not mcp_file or not os.path.exists(mcp_file) or not mcp_file.endswith('.json'):
3137
logger.warning(f"{mcp_file} is not a valid MCP file.")
3238
return None, gr.update(visible=False)
@@ -37,7 +43,7 @@ def update_mcp_server(mcp_file: str):
3743
return json.dumps(mcp_server, indent=2), gr.update(visible=True)
3844

3945

40-
def create_agent_settings_tab(webui_manager: WebuiManager) -> dict[str, Component]:
46+
def create_agent_settings_tab(webui_manager: WebuiManager):
4147
"""
4248
Creates an agent settings tab.
4349
"""
@@ -252,7 +258,7 @@ def create_agent_settings_tab(webui_manager: WebuiManager) -> dict[str, Componen
252258
)
253259

254260
mcp_json_file.change(
255-
update_mcp_server,
261+
partial(update_mcp_server, webui_manager=webui_manager),
256262
inputs=[mcp_json_file],
257263
outputs=[mcp_server_config, mcp_server_config]
258264
)

src/webui/components/browser_settings_tab.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ async def close_browser(webui_manager: WebuiManager):
1414
if webui_manager.bu_current_task and not webui_manager.bu_current_task.done():
1515
webui_manager.bu_current_task.cancel()
1616
webui_manager.bu_current_task = None
17-
if webui_manager.bu_browser:
18-
await webui_manager.bu_browser.close()
19-
webui_manager.bu_browser = None
17+
2018
if webui_manager.bu_browser_context:
19+
logger.info("⚠️ Closing browser context when changing browser config.")
2120
await webui_manager.bu_browser_context.close()
2221
webui_manager.bu_browser_context = None
2322

23+
if webui_manager.bu_browser:
24+
logger.info("⚠️ Closing browser when changing browser config.")
25+
await webui_manager.bu_browser.close()
26+
webui_manager.bu_browser = None
2427

2528
def create_browser_settings_tab(webui_manager: WebuiManager):
2629
"""
@@ -43,6 +46,7 @@ def create_browser_settings_tab(webui_manager: WebuiManager):
4346
interactive=True,
4447
placeholder="Leave it empty if you use your default user data",
4548
)
49+
with gr.Group():
4650
with gr.Row():
4751
use_own_browser = gr.Checkbox(
4852
label="Use Own Browser",
@@ -64,11 +68,12 @@ def create_browser_settings_tab(webui_manager: WebuiManager):
6468
)
6569
disable_security = gr.Checkbox(
6670
label="Disable Security",
67-
value=True,
68-
info="Disable browser security features",
71+
value=False,
72+
info="Disable browser security",
6973
interactive=True
7074
)
7175

76+
with gr.Group():
7277
with gr.Row():
7378
window_w = gr.Number(
7479
label="Window Width",
@@ -82,7 +87,7 @@ def create_browser_settings_tab(webui_manager: WebuiManager):
8287
info="Browser window height",
8388
interactive=True
8489
)
85-
90+
with gr.Group():
8691
with gr.Row():
8792
cdp_url = gr.Textbox(
8893
label="CDP URL",
@@ -94,7 +99,7 @@ def create_browser_settings_tab(webui_manager: WebuiManager):
9499
info="WSS URL for browser remote debugging",
95100
interactive=True,
96101
)
97-
102+
with gr.Group():
98103
with gr.Row():
99104
save_recording_path = gr.Textbox(
100105
label="Recording Path",

src/webui/components/browser_use_agent_tab.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pdb
2+
13
import gradio as gr
24
from gradio.components import Component
35
import asyncio
@@ -388,7 +390,6 @@ async def ask_callback_wrapper(query: str, browser_context: BrowserContext) -> D
388390
extra_args += [f"--user-data-dir={chrome_user_data}"]
389391
else:
390392
browser_binary_path = None
391-
392393
webui_manager.bu_browser = CustomBrowser(
393394
config=BrowserConfig(
394395
headless=headless,
@@ -432,7 +433,6 @@ def done_callback_wrapper(history: AgentHistoryList):
432433
logger.info(f"Initializing new agent for task: {task}")
433434
if not webui_manager.bu_browser or not webui_manager.bu_browser_context:
434435
raise ValueError("Browser or Context not initialized, cannot create agent.")
435-
436436
webui_manager.bu_agent = BrowserUseAgent(
437437
task=task,
438438
llm=main_llm,
@@ -456,6 +456,9 @@ def done_callback_wrapper(history: AgentHistoryList):
456456
webui_manager.bu_agent.state.agent_id = webui_manager.bu_agent_task_id
457457
webui_manager.bu_agent.add_new_task(task)
458458
webui_manager.bu_agent.settings.generate_gif = gif_path
459+
webui_manager.bu_agent.browser = webui_manager.bu_browser
460+
webui_manager.bu_agent.browser_context = webui_manager.bu_browser_context
461+
webui_manager.bu_agent.controller = webui_manager.bu_controller
459462

460463
# --- 6. Run Agent Task and Stream Updates ---
461464
agent_run_coro = webui_manager.bu_agent.run(max_steps=max_steps)
@@ -832,15 +835,13 @@ def create_browser_use_agent_tab(webui_manager: WebuiManager):
832835

833836
async def submit_wrapper(components_dict: Dict[Component, Any]) -> AsyncGenerator[Dict[Component, Any], None]:
834837
"""Wrapper for handle_submit that yields its results."""
835-
# handle_submit is an async generator, iterate and yield
836838
async for update in handle_submit(webui_manager, components_dict):
837839
yield update
838840

839841
async def stop_wrapper() -> AsyncGenerator[Dict[Component, Any], None]:
840842
"""Wrapper for handle_stop."""
841-
# handle_stop is async def but returns a single dict. We yield it once.
842843
update_dict = await handle_stop(webui_manager)
843-
yield update_dict # Yield the final dictionary
844+
yield update_dict
844845

845846
async def pause_resume_wrapper() -> AsyncGenerator[Dict[Component, Any], None]:
846847
"""Wrapper for handle_pause_resume."""

src/webui/components/deep_research_agent_tab.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from src.utils import config
66

77

8-
def create_deep_research_agent_tab(webui_manager: WebuiManager) -> dict[str, Component]:
8+
def create_deep_research_agent_tab(webui_manager: WebuiManager):
99
"""
1010
Creates a deep research agent tab
1111
"""

src/webui/components/load_save_config_tab.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
from src.utils import config
66

77

8-
def create_load_save_config_tab(webui_manager: WebuiManager) -> dict[str, Component]:
8+
def create_load_save_config_tab(webui_manager: WebuiManager):
99
"""
1010
Creates a load and save config tab.
1111
"""
1212
input_components = set(webui_manager.get_components())
1313
tab_components = {}
1414

1515
config_file = gr.File(
16-
label="Load UI Settings from Config File",
16+
label="Load UI Settings from json",
1717
file_types=[".json"],
1818
interactive=True
1919
)

tests/test_agents.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ async def test_browser_use_parallel():
194194
# api_key=os.getenv("OPENAI_API_KEY", ""),
195195
# )
196196

197-
198197
# llm = utils.get_llm_model(
199198
# provider="google",
200199
# model_name="gemini-2.0-flash",
@@ -335,6 +334,70 @@ async def test_browser_use_parallel():
335334
await browser.close()
336335

337336

337+
async def test_deep_research_agent():
338+
from src.agent.deep_research.deep_research_agent import DeepSearchAgent
339+
from src.utils import llm_provider
340+
341+
llm = llm_provider.get_llm_model(
342+
provider="azure_openai",
343+
model_name="gpt-4o",
344+
temperature=0.5,
345+
base_url=os.getenv("AZURE_OPENAI_ENDPOINT", ""),
346+
api_key=os.getenv("AZURE_OPENAI_API_KEY", ""),
347+
)
348+
349+
mcp_server_config = {
350+
"mcpServers": {
351+
"desktop-commander": {
352+
"command": "npx",
353+
"args": [
354+
"-y",
355+
"@wonderwhy-er/desktop-commander"
356+
]
357+
},
358+
}
359+
}
360+
361+
browser_config = {"headless": False, "window_width": 1280, "window_height": 1100, "use_own_browser": False}
362+
agent = DeepSearchAgent(llm=llm, browser_config=browser_config, mcp_server_config=mcp_server_config)
363+
364+
research_topic = "Impact of Microplastics on Marine Ecosystems"
365+
task_id_to_resume = None # Set this to resume a previous task ID
366+
367+
print(f"Starting research on: {research_topic}")
368+
369+
try:
370+
# Call run and wait for the final result dictionary
371+
result = await agent.run(research_topic, task_id=task_id_to_resume)
372+
373+
print("\n--- Research Process Ended ---")
374+
print(f"Status: {result.get('status')}")
375+
print(f"Message: {result.get('message')}")
376+
print(f"Task ID: {result.get('task_id')}")
377+
378+
# Check the final state for the report
379+
final_state = result.get('final_state', {})
380+
if final_state:
381+
print("\n--- Final State Summary ---")
382+
print(
383+
f" Plan Steps Completed: {sum(1 for item in final_state.get('research_plan', []) if item.get('status') == 'completed')}")
384+
print(f" Total Search Results Logged: {len(final_state.get('search_results', []))}")
385+
if final_state.get("final_report"):
386+
print(" Final Report: Generated (content omitted). You can find it in the output directory.")
387+
# print("\n--- Final Report ---") # Optionally print report
388+
# print(final_state["final_report"])
389+
else:
390+
print(" Final Report: Not generated.")
391+
else:
392+
print("Final state information not available.")
393+
394+
395+
except Exception as e:
396+
print(f"\n--- An unhandled error occurred outside the agent run ---")
397+
print(e)
398+
399+
338400
if __name__ == "__main__":
339401
# asyncio.run(test_browser_use_agent())
340-
asyncio.run(test_browser_use_parallel())
402+
# asyncio.run(test_browser_use_parallel())
403+
asyncio.run(test_deep_research_agent())

tests/test_controller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ async def test_mcp_client():
3232
}
3333

3434
mcp_tools, mcp_client = await setup_mcp_client_and_tools(test_server_config)
35+
3536
for tool in mcp_tools:
3637
tool_param_model = create_tool_param_model(tool)
3738
print(tool.name)

0 commit comments

Comments
 (0)