Skip to content

Commit 3c69544

Browse files
authored
Merge pull request #248 from vvincent1234/fix/deep-research
add stop button and use own browser
2 parents 037f8e5 + fbd748e commit 3c69544

File tree

2 files changed

+139
-67
lines changed

2 files changed

+139
-67
lines changed

src/utils/deep_research.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
from src.utils import utils
1414
from src.agent.custom_agent import CustomAgent
1515
import json
16+
import re
1617
from browser_use.agent.service import Agent
1718
from browser_use.browser.browser import BrowserConfig, Browser
1819
from langchain.schema import SystemMessage, HumanMessage
1920
from json_repair import repair_json
2021
from src.agent.custom_prompts import CustomSystemPrompt, CustomAgentMessagePrompt
2122
from src.controller.custom_controller import CustomController
23+
from src.browser.custom_browser import CustomBrowser
2224

2325
logger = logging.getLogger(__name__)
2426

25-
async def deep_research(task, llm, **kwargs):
27+
async def deep_research(task, llm, agent_state, **kwargs):
2628
task_id = str(uuid4())
2729
save_dir = kwargs.get("save_dir", os.path.join(f"./tmp/deep_research/{task_id}"))
2830
logger.info(f"Save Deep Research at: {save_dir}")
@@ -113,12 +115,20 @@ async def deep_research(task, llm, **kwargs):
113115
"""
114116
record_messages = [SystemMessage(content=record_system_prompt)]
115117

116-
browser = Browser(
117-
config=BrowserConfig(
118-
disable_security=True,
119-
headless=kwargs.get("headless", False), # Set to False to see browser actions
120-
)
121-
)
118+
use_own_browser = kwargs.get("use_own_browser", False)
119+
extra_chromium_args = []
120+
if use_own_browser:
121+
# if use own browser, max query num should be 1 per iter
122+
max_query_num = 1
123+
chrome_path = os.getenv("CHROME_PATH", None)
124+
if chrome_path == "":
125+
chrome_path = None
126+
chrome_user_data = os.getenv("CHROME_USER_DATA", None)
127+
if chrome_user_data:
128+
extra_chromium_args += [f"--user-data-dir={chrome_user_data}"]
129+
else:
130+
chrome_path = None
131+
browser = None
122132
controller = CustomController()
123133

124134
search_iteration = 0
@@ -151,6 +161,7 @@ async def deep_research(task, llm, **kwargs):
151161
if not query_tasks:
152162
break
153163
else:
164+
query_tasks = query_tasks[:max_query_num]
154165
history_query.extend(query_tasks)
155166
logger.info("Query tasks:")
156167
logger.info(query_tasks)
@@ -159,6 +170,15 @@ async def deep_research(task, llm, **kwargs):
159170
# Paralle BU agents
160171
add_infos = "1. Please click on the most relevant link to get information and go deeper, instead of just staying on the search page. \n" \
161172
"2. When opening a PDF file, please remember to extract the content using extract_content instead of simply opening it for the user to view."
173+
if use_own_browser:
174+
browser = CustomBrowser(
175+
config=BrowserConfig(
176+
headless=kwargs.get("headless", False),
177+
disable_security=kwargs.get("disable_security", True),
178+
chrome_instance_path=chrome_path,
179+
extra_chromium_args=extra_chromium_args,
180+
)
181+
)
162182
agents = [CustomAgent(
163183
task=task,
164184
llm=llm,
@@ -168,15 +188,24 @@ async def deep_research(task, llm, **kwargs):
168188
system_prompt_class=CustomSystemPrompt,
169189
agent_prompt_class=CustomAgentMessagePrompt,
170190
max_actions_per_step=5,
171-
controller=controller
191+
controller=controller,
192+
agent_state=agent_state
172193
) for task in query_tasks]
173194
query_results = await asyncio.gather(*[agent.run(max_steps=kwargs.get("max_steps", 10)) for agent in agents])
174-
195+
if browser:
196+
await browser.close()
197+
browser = None
198+
logger.info("Browser closed.")
199+
if agent_state and agent_state.is_stop_requested():
200+
# Stop
201+
break
175202
# 3. Summarize Search Result
176203
query_result_dir = os.path.join(save_dir, "query_results")
177204
os.makedirs(query_result_dir, exist_ok=True)
178205
for i in range(len(query_tasks)):
179206
query_result = query_results[i].final_result()
207+
if not query_result:
208+
continue
180209
querr_save_path = os.path.join(query_result_dir, f"{search_iteration}-{i}.md")
181210
logger.info(f"save query: {query_tasks[i]} at {querr_save_path}")
182211
with open(querr_save_path, "w", encoding="utf-8") as fw:
@@ -244,7 +273,9 @@ async def deep_research(task, llm, **kwargs):
244273
logger.info(ai_report_msg.reasoning_content)
245274
logger.info("🤯 End Report Deep Thinking")
246275
report_content = ai_report_msg.content
247-
276+
# Remove ```markdown or ``` at the *very beginning* and ``` at the *very end*, with optional whitespace
277+
report_content = re.sub(r"^```\s*markdown\s*|^\s*```|```\s*$", "", report_content, flags=re.MULTILINE)
278+
report_content = report_content.strip()
248279
report_file_path = os.path.join(save_dir, "final_report.md")
249280
with open(report_file_path, "w", encoding="utf-8") as f:
250281
f.write(report_content)
@@ -257,4 +288,5 @@ async def deep_research(task, llm, **kwargs):
257288
finally:
258289
if browser:
259290
await browser.close()
291+
browser = None
260292
logger.info("Browser closed.")

webui.py

Lines changed: 97 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,31 @@ async def stop_agent():
6969
gr.update(value="Stop", interactive=True),
7070
gr.update(interactive=True)
7171
)
72+
73+
async def stop_research_agent():
74+
"""Request the agent to stop and update UI with enhanced feedback"""
75+
global _global_agent_state, _global_browser_context, _global_browser
76+
77+
try:
78+
# Request stop
79+
_global_agent_state.request_stop()
80+
81+
# Update UI immediately
82+
message = "Stop requested - the agent will halt at the next safe point"
83+
logger.info(f"🛑 {message}")
84+
85+
# Return UI updates
86+
return ( # errors_output
87+
gr.update(value="Stopping...", interactive=False), # stop_button
88+
gr.update(interactive=False), # run_button
89+
)
90+
except Exception as e:
91+
error_msg = f"Error during stop: {str(e)}"
92+
logger.error(error_msg)
93+
return (
94+
gr.update(value="Stop", interactive=True),
95+
gr.update(interactive=True)
96+
)
7297

7398
async def run_browser_agent(
7499
agent_type,
@@ -598,8 +623,12 @@ async def close_global_browser():
598623
await _global_browser.close()
599624
_global_browser = None
600625

601-
async def run_deep_search(research_task, max_search_iteration_input, max_query_per_iter_input, llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key, use_vision, headless):
626+
async def run_deep_search(research_task, max_search_iteration_input, max_query_per_iter_input, llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key, use_vision, use_own_browser, headless):
602627
from src.utils.deep_research import deep_research
628+
global _global_agent_state
629+
630+
# Clear any previous stop request
631+
_global_agent_state.clear_stop()
603632

604633
llm = utils.get_llm_model(
605634
provider=llm_provider,
@@ -608,12 +637,15 @@ async def run_deep_search(research_task, max_search_iteration_input, max_query_p
608637
base_url=llm_base_url,
609638
api_key=llm_api_key,
610639
)
611-
markdown_content, file_path = await deep_research(research_task, llm,
640+
markdown_content, file_path = await deep_research(research_task, llm, _global_agent_state,
612641
max_search_iterations=max_search_iteration_input,
613642
max_query_num=max_query_per_iter_input,
614643
use_vision=use_vision,
615-
headless=headless)
616-
return markdown_content, file_path
644+
headless=headless,
645+
use_own_browser=use_own_browser
646+
)
647+
648+
return markdown_content, file_path, gr.update(value="Stop", interactive=True), gr.update(interactive=True)
617649

618650

619651
def create_ui(config, theme_name="Ocean"):
@@ -815,57 +847,17 @@ def create_ui(config, theme_name="Ocean"):
815847
label="Live Browser View",
816848
)
817849

818-
with gr.TabItem("🧐 Deep Research"):
819-
with gr.Group():
820-
research_task_input = gr.Textbox(label="Research Task", lines=5, value="Compose a report on the use of Reinforcement Learning for training Large Language Models, encompassing its origins, current advancements, and future prospects, substantiated with examples of relevant models and techniques. The report should reflect original insights and analysis, moving beyond mere summarization of existing literature.")
821-
with gr.Row():
822-
max_search_iteration_input = gr.Number(label="Max Search Iteration", value=20, precision=0) # precision=0 确保是整数
823-
max_query_per_iter_input = gr.Number(label="Max Query per Iteration", value=5, precision=0) # precision=0 确保是整数
824-
research_button = gr.Button("Run Deep Research")
825-
markdown_output_display = gr.Markdown(label="Research Report")
826-
markdown_download = gr.File(label="Download Research Report")
827-
828-
829-
with gr.TabItem("📁 Configuration", id=5):
830-
with gr.Group():
831-
config_file_input = gr.File(
832-
label="Load Config File",
833-
file_types=[".pkl"],
834-
interactive=True
835-
)
836-
837-
load_config_button = gr.Button("Load Existing Config From File", variant="primary")
838-
save_config_button = gr.Button("Save Current Config", variant="primary")
839-
840-
config_status = gr.Textbox(
841-
label="Status",
842-
lines=2,
843-
interactive=False
844-
)
845-
846-
load_config_button.click(
847-
fn=update_ui_from_config,
848-
inputs=[config_file_input],
849-
outputs=[
850-
agent_type, max_steps, max_actions_per_step, use_vision, tool_calling_method,
851-
llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key,
852-
use_own_browser, keep_browser_open, headless, disable_security, enable_recording,
853-
window_w, window_h, save_recording_path, save_trace_path, save_agent_history_path,
854-
task, config_status
855-
]
856-
)
850+
with gr.TabItem("🧐 Deep Research", id=5):
851+
research_task_input = gr.Textbox(label="Research Task", lines=5, value="Compose a report on the use of Reinforcement Learning for training Large Language Models, encompassing its origins, current advancements, and future prospects, substantiated with examples of relevant models and techniques. The report should reflect original insights and analysis, moving beyond mere summarization of existing literature.")
852+
with gr.Row():
853+
max_search_iteration_input = gr.Number(label="Max Search Iteration", value=20, precision=0) # precision=0 确保是整数
854+
max_query_per_iter_input = gr.Number(label="Max Query per Iteration", value=5, precision=0) # precision=0 确保是整数
855+
with gr.Row():
856+
research_button = gr.Button("▶️ Run Deep Research", variant="primary", scale=2)
857+
stop_research_button = gr.Button("⏹️ Stop", variant="stop", scale=1)
858+
markdown_output_display = gr.Markdown(label="Research Report")
859+
markdown_download = gr.File(label="Download Research Report")
857860

858-
save_config_button.click(
859-
fn=save_current_config,
860-
inputs=[
861-
agent_type, max_steps, max_actions_per_step, use_vision, tool_calling_method,
862-
llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key,
863-
use_own_browser, keep_browser_open, headless, disable_security,
864-
enable_recording, window_w, window_h, save_recording_path, save_trace_path,
865-
save_agent_history_path, task,
866-
],
867-
outputs=[config_status]
868-
)
869861

870862
with gr.TabItem("📊 Results", id=6):
871863
with gr.Group():
@@ -929,9 +921,15 @@ def create_ui(config, theme_name="Ocean"):
929921
# Run Deep Research
930922
research_button.click(
931923
fn=run_deep_search,
932-
inputs=[research_task_input, max_search_iteration_input, max_query_per_iter_input, llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key, use_vision, headless],
933-
outputs=[markdown_output_display, markdown_download]
934-
)
924+
inputs=[research_task_input, max_search_iteration_input, max_query_per_iter_input, llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key, use_vision, use_own_browser, headless],
925+
outputs=[markdown_output_display, markdown_download, stop_research_button, research_button]
926+
)
927+
# Bind the stop button click event after errors_output is defined
928+
stop_research_button.click(
929+
fn=stop_research_agent,
930+
inputs=[],
931+
outputs=[stop_research_button, research_button],
932+
)
935933

936934
with gr.TabItem("🎥 Recordings", id=7):
937935
def list_recordings(save_recording_path):
@@ -966,6 +964,48 @@ def list_recordings(save_recording_path):
966964
inputs=save_recording_path,
967965
outputs=recordings_gallery
968966
)
967+
968+
with gr.TabItem("📁 Configuration", id=8):
969+
with gr.Group():
970+
config_file_input = gr.File(
971+
label="Load Config File",
972+
file_types=[".pkl"],
973+
interactive=True
974+
)
975+
976+
load_config_button = gr.Button("Load Existing Config From File", variant="primary")
977+
save_config_button = gr.Button("Save Current Config", variant="primary")
978+
979+
config_status = gr.Textbox(
980+
label="Status",
981+
lines=2,
982+
interactive=False
983+
)
984+
985+
load_config_button.click(
986+
fn=update_ui_from_config,
987+
inputs=[config_file_input],
988+
outputs=[
989+
agent_type, max_steps, max_actions_per_step, use_vision, tool_calling_method,
990+
llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key,
991+
use_own_browser, keep_browser_open, headless, disable_security, enable_recording,
992+
window_w, window_h, save_recording_path, save_trace_path, save_agent_history_path,
993+
task, config_status
994+
]
995+
)
996+
997+
save_config_button.click(
998+
fn=save_current_config,
999+
inputs=[
1000+
agent_type, max_steps, max_actions_per_step, use_vision, tool_calling_method,
1001+
llm_provider, llm_model_name, llm_temperature, llm_base_url, llm_api_key,
1002+
use_own_browser, keep_browser_open, headless, disable_security,
1003+
enable_recording, window_w, window_h, save_recording_path, save_trace_path,
1004+
save_agent_history_path, task,
1005+
],
1006+
outputs=[config_status]
1007+
)
1008+
9691009

9701010
# Attach the callback to the LLM provider dropdown
9711011
llm_provider.change(

0 commit comments

Comments
 (0)