Skip to content

Commit 1408e44

Browse files
chore: browser_open (#3122)
Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
1 parent 5df0738 commit 1408e44

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,66 @@ class HybridBrowserToolkit(BaseToolkit):
2525
This wrapper allows users to choose between:
2626
- 'typescript': WebSocket-based implementation using TypeScript/Node.js
2727
- 'python': Pure Python implementation using Playwright directly
28+
29+
Args:
30+
mode (Literal["typescript", "python"]): Implementation mode. -
31+
'typescript': Uses WebSocket-based TypeScript implementation -
32+
'python': Uses pure Python Playwright implementation. Defaults to
33+
"typescript".
34+
headless (bool): Whether to run browser in headless mode.
35+
Defaults to True.
36+
user_data_dir (Optional[str]): Directory for user data
37+
persistence. Defaults to None.
38+
stealth (bool): Whether to enable stealth mode. Defaults to
39+
False.
40+
web_agent_model (Optional[BaseModelBackend]): Model for web
41+
agent operations. Defaults to None.
42+
cache_dir (str): Directory for caching. Defaults to "tmp/".
43+
enabled_tools (Optional[List[str]]): List of enabled tools.
44+
Defaults to None.
45+
browser_log_to_file (bool): Whether to log browser actions to
46+
file. Defaults to False.
47+
log_dir (Optional[str]): Custom directory path for log files.
48+
If None, defaults to "browser_log". Defaults to None.
49+
session_id (Optional[str]): Session identifier. Defaults to None.
50+
default_start_url (str): Default URL to start with. Defaults
51+
to "https://google.com/".
52+
default_timeout (Optional[int]): Default timeout in
53+
milliseconds. Defaults to None.
54+
short_timeout (Optional[int]): Short timeout in milliseconds.
55+
Defaults to None.
56+
navigation_timeout (Optional[int]): Navigation timeout in
57+
milliseconds. Defaults to None.
58+
network_idle_timeout (Optional[int]): Network idle timeout in
59+
milliseconds. Defaults to None.
60+
screenshot_timeout (Optional[int]): Screenshot timeout in
61+
milliseconds. Defaults to None.
62+
page_stability_timeout (Optional[int]): Page stability timeout
63+
in milliseconds. Defaults to None.
64+
dom_content_loaded_timeout (Optional[int]): DOM content loaded
65+
timeout in milliseconds. Defaults to None.
66+
viewport_limit (bool): Whether to filter page snapshot
67+
elements to only those visible in the current viewport.
68+
Defaults to False.
69+
connect_over_cdp (bool): Whether to connect to an existing
70+
browser via Chrome DevTools Protocol. Defaults to False.
71+
(Only supported in TypeScript mode)
72+
cdp_url (Optional[str]): WebSocket endpoint URL for CDP
73+
connection. Required when connect_over_cdp is True.
74+
Defaults to None. (Only supported in TypeScript mode)
75+
cdp_keep_current_page (bool): When True and using CDP mode,
76+
won't create new pages but use the existing one. Defaults to False.
77+
(Only supported in TypeScript mode)
78+
full_visual_mode (bool): When True, browser actions like click,
79+
browser_open, visit_page, etc. will return 'full visual mode'
80+
as snapshot instead of actual page content. The
81+
browser_get_page_snapshot method will still return the actual
82+
snapshot. Defaults to False.
83+
**kwargs: Additional keyword arguments passed to the
84+
implementation.
85+
86+
Returns:
87+
HybridBrowserToolkit instance of the specified implementation.
2888
"""
2989

3090
def __new__(

camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def __init__(self, config: Optional[Dict[str, Any]] = None):
109109
self._send_lock = asyncio.Lock()
110110
self._receive_task = None
111111
self._pending_responses: Dict[str, asyncio.Future[Dict[str, Any]]] = {}
112+
self._browser_opened = False
112113
self._server_ready_future = None
113114

114115
self.browser_log_to_file = (config or {}).get(
@@ -391,6 +392,9 @@ async def start(self):
391392

392393
await self._send_command('init', self.config)
393394

395+
if self.config.get('cdpUrl'):
396+
self._browser_opened = True
397+
394398
async def stop(self):
395399
"""Stop the WebSocket connection and server."""
396400
if self.websocket:
@@ -400,11 +404,12 @@ async def stop(self):
400404
timeout=2.0,
401405
)
402406

403-
# Close websocket connection
404407
with contextlib.suppress(Exception):
405408
await self.websocket.close()
406409
self.websocket = None
407410

411+
self._browser_opened = False
412+
408413
# Gracefully stop the Node process before cancelling the log reader
409414
if self.process:
410415
try:
@@ -448,12 +453,13 @@ async def disconnect_only(self):
448453
449454
This is useful for CDP mode where the browser should remain open.
450455
"""
451-
# Close websocket connection
452456
if self.websocket:
453457
with contextlib.suppress(Exception):
454458
await self.websocket.close()
455459
self.websocket = None
456460

461+
self._browser_opened = False
462+
457463
# Stop the Node process
458464
if self.process:
459465
try:
@@ -709,17 +715,31 @@ async def open_browser(
709715
response = await self._send_command(
710716
'open_browser', {'startUrl': start_url}
711717
)
718+
self._browser_opened = True
712719
return response
713720

714721
@action_logger
715722
async def close_browser(self) -> str:
716723
"""Close browser."""
717724
response = await self._send_command('close_browser', {})
725+
self._browser_opened = False
718726
return response['message']
719727

720728
@action_logger
721729
async def visit_page(self, url: str) -> Dict[str, Any]:
722-
"""Visit a page."""
730+
"""Visit a page.
731+
732+
In non-CDP mode, automatically opens browser if not already open.
733+
"""
734+
if not self._browser_opened:
735+
is_cdp_mode = bool(self.config.get('cdpUrl'))
736+
737+
if not is_cdp_mode:
738+
logger.info(
739+
"Browser not open, automatically opening browser..."
740+
)
741+
await self.open_browser()
742+
723743
response = await self._send_command('visit_page', {'url': url})
724744
return response
725745

examples/workforce/eigent.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ def search_agent_factory(
324324
"browser_get_som_screenshot",
325325
]
326326
web_toolkit_custom = HybridBrowserToolkit(
327-
mode="python",
328327
headless=False,
329328
enabled_tools=custom_tools,
330329
browser_log_to_file=True,

0 commit comments

Comments
 (0)