camel-ai 0.2.75a5__py3-none-any.whl → 0.2.76a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (47) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +298 -130
  3. camel/configs/__init__.py +6 -0
  4. camel/configs/amd_config.py +70 -0
  5. camel/configs/nebius_config.py +103 -0
  6. camel/interpreters/__init__.py +2 -0
  7. camel/interpreters/microsandbox_interpreter.py +395 -0
  8. camel/models/__init__.py +4 -0
  9. camel/models/amd_model.py +101 -0
  10. camel/models/model_factory.py +4 -0
  11. camel/models/nebius_model.py +83 -0
  12. camel/models/ollama_model.py +3 -3
  13. camel/models/openai_model.py +0 -6
  14. camel/runtimes/daytona_runtime.py +11 -12
  15. camel/societies/workforce/task_channel.py +120 -27
  16. camel/societies/workforce/workforce.py +35 -3
  17. camel/toolkits/__init__.py +5 -3
  18. camel/toolkits/code_execution.py +28 -1
  19. camel/toolkits/function_tool.py +6 -1
  20. camel/toolkits/github_toolkit.py +104 -17
  21. camel/toolkits/hybrid_browser_toolkit/config_loader.py +8 -0
  22. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +12 -0
  23. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +33 -14
  24. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +135 -40
  25. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +2 -0
  26. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +43 -207
  27. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  28. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +231 -0
  29. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  30. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  31. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +248 -58
  32. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +5 -1
  33. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  34. camel/toolkits/math_toolkit.py +64 -10
  35. camel/toolkits/mcp_toolkit.py +39 -14
  36. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  37. camel/toolkits/search_toolkit.py +13 -2
  38. camel/toolkits/terminal_toolkit.py +12 -2
  39. camel/toolkits/video_analysis_toolkit.py +16 -10
  40. camel/types/enums.py +42 -0
  41. camel/types/unified_model_type.py +5 -0
  42. camel/utils/commons.py +2 -0
  43. camel/utils/mcp.py +136 -2
  44. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/METADATA +5 -11
  45. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/RECORD +47 -38
  46. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/WHEEL +0 -0
  47. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76a0.dist-info}/licenses/LICENSE +0 -0
@@ -18,6 +18,7 @@ from camel.interpreters import (
18
18
  E2BInterpreter,
19
19
  InternalPythonInterpreter,
20
20
  JupyterKernelInterpreter,
21
+ MicrosandboxInterpreter,
21
22
  SubprocessInterpreter,
22
23
  )
23
24
  from camel.logger import get_logger
@@ -43,18 +44,31 @@ class CodeExecutionToolkit(BaseToolkit):
43
44
  (default: :obj:`None`)
44
45
  require_confirm (bool): Whether to require confirmation before
45
46
  executing code. (default: :obj:`False`)
47
+ timeout (Optional[float]): General timeout for toolkit operations.
48
+ (default: :obj:`None`)
49
+ microsandbox_config (Optional[dict]): Configuration for microsandbox
50
+ interpreter. Available keys: 'server_url', 'api_key',
51
+ 'namespace', 'sandbox_name', 'timeout'.
52
+ If None, uses default configuration. (default: :obj:`None`)
46
53
  """
47
54
 
48
55
  def __init__(
49
56
  self,
50
57
  sandbox: Literal[
51
- "internal_python", "jupyter", "docker", "subprocess", "e2b"
58
+ "internal_python",
59
+ "jupyter",
60
+ "docker",
61
+ "subprocess",
62
+ "e2b",
63
+ "microsandbox",
52
64
  ] = "subprocess",
53
65
  verbose: bool = False,
54
66
  unsafe_mode: bool = False,
55
67
  import_white_list: Optional[List[str]] = None,
56
68
  require_confirm: bool = False,
57
69
  timeout: Optional[float] = None,
70
+ # Microsandbox configuration dictionary
71
+ microsandbox_config: Optional[dict] = None,
58
72
  ) -> None:
59
73
  super().__init__(timeout=timeout)
60
74
  self.verbose = verbose
@@ -68,6 +82,7 @@ class CodeExecutionToolkit(BaseToolkit):
68
82
  DockerInterpreter,
69
83
  SubprocessInterpreter,
70
84
  E2BInterpreter,
85
+ MicrosandboxInterpreter,
71
86
  ]
72
87
 
73
88
  if sandbox == "internal_python":
@@ -95,6 +110,18 @@ class CodeExecutionToolkit(BaseToolkit):
95
110
  )
96
111
  elif sandbox == "e2b":
97
112
  self.interpreter = E2BInterpreter(require_confirm=require_confirm)
113
+ elif sandbox == "microsandbox":
114
+ # Extract parameters with proper types for microsandbox
115
+ config = microsandbox_config or {}
116
+
117
+ self.interpreter = MicrosandboxInterpreter(
118
+ require_confirm=require_confirm,
119
+ server_url=config.get("server_url"),
120
+ api_key=config.get("api_key"),
121
+ namespace=config.get("namespace", "default"),
122
+ sandbox_name=config.get("sandbox_name"),
123
+ timeout=config.get("timeout", 30),
124
+ )
98
125
  else:
99
126
  raise RuntimeError(
100
127
  f"The sandbox type `{sandbox}` is not supported."
@@ -156,7 +156,12 @@ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
156
156
  if (name := param.arg_name) in parameters_dict["properties"] and (
157
157
  description := param.description
158
158
  ):
159
- parameters_dict["properties"][name]["description"] = description
159
+ # OpenAI does not allow descriptions on properties that use $ref.
160
+ # To avoid schema errors, we only add the description if "$ref" is
161
+ # not present.
162
+ prop = parameters_dict["properties"][name]
163
+ if "$ref" not in prop:
164
+ prop["description"] = description
160
165
 
161
166
  short_description = docstring.short_description or ""
162
167
  long_description = docstring.long_description or ""
@@ -14,6 +14,7 @@
14
14
 
15
15
  import logging
16
16
  import os
17
+ import warnings
17
18
  from typing import Dict, List, Literal, Optional, Union
18
19
 
19
20
  from camel.toolkits import FunctionTool
@@ -80,7 +81,7 @@ class GithubToolkit(BaseToolkit):
80
81
  )
81
82
  return GITHUB_ACCESS_TOKEN
82
83
 
83
- def create_pull_request(
84
+ def github_create_pull_request(
84
85
  self,
85
86
  repo_name: str,
86
87
  file_path: str,
@@ -150,7 +151,7 @@ class GithubToolkit(BaseToolkit):
150
151
  else:
151
152
  raise ValueError("PRs with multiple files aren't supported yet.")
152
153
 
153
- def get_issue_list(
154
+ def github_get_issue_list(
154
155
  self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
155
156
  ) -> List[Dict[str, object]]:
156
157
  r"""Retrieves all issues from the GitHub repository.
@@ -177,7 +178,9 @@ class GithubToolkit(BaseToolkit):
177
178
 
178
179
  return issues_info
179
180
 
180
- def get_issue_content(self, repo_name: str, issue_number: int) -> str:
181
+ def github_get_issue_content(
182
+ self, repo_name: str, issue_number: int
183
+ ) -> str:
181
184
  r"""Retrieves the content of a specific issue by its number.
182
185
 
183
186
  Args:
@@ -194,7 +197,7 @@ class GithubToolkit(BaseToolkit):
194
197
  except Exception as e:
195
198
  return f"can't get Issue number {issue_number}: {e!s}"
196
199
 
197
- def get_pull_request_list(
200
+ def github_get_pull_request_list(
198
201
  self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
199
202
  ) -> List[Dict[str, object]]:
200
203
  r"""Retrieves all pull requests from the GitHub repository.
@@ -221,7 +224,7 @@ class GithubToolkit(BaseToolkit):
221
224
 
222
225
  return pull_requests_info
223
226
 
224
- def get_pull_request_code(
227
+ def github_get_pull_request_code(
225
228
  self, repo_name: str, pr_number: int
226
229
  ) -> List[Dict[str, str]]:
227
230
  r"""Retrieves the code changes of a specific pull request.
@@ -253,7 +256,7 @@ class GithubToolkit(BaseToolkit):
253
256
 
254
257
  return files_changed
255
258
 
256
- def get_pull_request_comments(
259
+ def github_get_pull_request_comments(
257
260
  self, repo_name: str, pr_number: int
258
261
  ) -> List[Dict[str, str]]:
259
262
  r"""Retrieves the comments from a specific pull request.
@@ -278,7 +281,9 @@ class GithubToolkit(BaseToolkit):
278
281
 
279
282
  return comments
280
283
 
281
- def get_all_file_paths(self, repo_name: str, path: str = "") -> List[str]:
284
+ def github_get_all_file_paths(
285
+ self, repo_name: str, path: str = ""
286
+ ) -> List[str]:
282
287
  r"""Recursively retrieves all file paths in the GitHub repository.
283
288
 
284
289
  Args:
@@ -308,13 +313,15 @@ class GithubToolkit(BaseToolkit):
308
313
  for content in contents:
309
314
  if content.type == "dir":
310
315
  # If it's a directory, recursively retrieve its file paths
311
- files.extend(self.get_all_file_paths(content.path))
316
+ files.extend(self.github_get_all_file_paths(content.path))
312
317
  else:
313
318
  # If it's a file, add its path to the list
314
319
  files.append(content.path)
315
320
  return files
316
321
 
317
- def retrieve_file_content(self, repo_name: str, file_path: str) -> str:
322
+ def github_retrieve_file_content(
323
+ self, repo_name: str, file_path: str
324
+ ) -> str:
318
325
  r"""Retrieves the content of a file from the GitHub repository.
319
326
 
320
327
  Args:
@@ -343,12 +350,92 @@ class GithubToolkit(BaseToolkit):
343
350
  the functions in the toolkit.
344
351
  """
345
352
  return [
346
- FunctionTool(self.create_pull_request),
347
- FunctionTool(self.get_issue_list),
348
- FunctionTool(self.get_issue_content),
349
- FunctionTool(self.get_pull_request_list),
350
- FunctionTool(self.get_pull_request_code),
351
- FunctionTool(self.get_pull_request_comments),
352
- FunctionTool(self.get_all_file_paths),
353
- FunctionTool(self.retrieve_file_content),
353
+ FunctionTool(self.github_create_pull_request),
354
+ FunctionTool(self.github_get_issue_list),
355
+ FunctionTool(self.github_get_issue_content),
356
+ FunctionTool(self.github_get_pull_request_list),
357
+ FunctionTool(self.github_get_pull_request_code),
358
+ FunctionTool(self.github_get_pull_request_comments),
359
+ FunctionTool(self.github_get_all_file_paths),
360
+ FunctionTool(self.github_retrieve_file_content),
354
361
  ]
362
+
363
+ # Deprecated method aliases for backward compatibility
364
+ def create_pull_request(self, *args, **kwargs):
365
+ r"""Deprecated: Use github_create_pull_request instead."""
366
+ warnings.warn(
367
+ "create_pull_request is deprecated. Use "
368
+ "github_create_pull_request instead.",
369
+ DeprecationWarning,
370
+ stacklevel=2,
371
+ )
372
+ return self.github_create_pull_request(*args, **kwargs)
373
+
374
+ def get_issue_list(self, *args, **kwargs):
375
+ r"""Deprecated: Use github_get_issue_list instead."""
376
+ warnings.warn(
377
+ "get_issue_list is deprecated. Use github_get_issue_list instead.",
378
+ DeprecationWarning,
379
+ stacklevel=2,
380
+ )
381
+ return self.github_get_issue_list(*args, **kwargs)
382
+
383
+ def get_issue_content(self, *args, **kwargs):
384
+ r"""Deprecated: Use github_get_issue_content instead."""
385
+ warnings.warn(
386
+ "get_issue_content is deprecated. Use "
387
+ "github_get_issue_content instead.",
388
+ DeprecationWarning,
389
+ stacklevel=2,
390
+ )
391
+ return self.github_get_issue_content(*args, **kwargs)
392
+
393
+ def get_pull_request_list(self, *args, **kwargs):
394
+ r"""Deprecated: Use github_get_pull_request_list instead."""
395
+ warnings.warn(
396
+ "get_pull_request_list is deprecated. "
397
+ "Use github_get_pull_request_list instead.",
398
+ DeprecationWarning,
399
+ stacklevel=2,
400
+ )
401
+ return self.github_get_pull_request_list(*args, **kwargs)
402
+
403
+ def get_pull_request_code(self, *args, **kwargs):
404
+ r"""Deprecated: Use github_get_pull_request_code instead."""
405
+ warnings.warn(
406
+ "get_pull_request_code is deprecated. Use "
407
+ "github_get_pull_request_code instead.",
408
+ DeprecationWarning,
409
+ stacklevel=2,
410
+ )
411
+ return self.github_get_pull_request_code(*args, **kwargs)
412
+
413
+ def get_pull_request_comments(self, *args, **kwargs):
414
+ r"""Deprecated: Use github_get_pull_request_comments instead."""
415
+ warnings.warn(
416
+ "get_pull_request_comments is deprecated. "
417
+ "Use github_get_pull_request_comments instead.",
418
+ DeprecationWarning,
419
+ stacklevel=2,
420
+ )
421
+ return self.github_get_pull_request_comments(*args, **kwargs)
422
+
423
+ def get_all_file_paths(self, *args, **kwargs):
424
+ r"""Deprecated: Use github_get_all_file_paths instead."""
425
+ warnings.warn(
426
+ "get_all_file_paths is deprecated. Use "
427
+ "github_get_all_file_paths instead.",
428
+ DeprecationWarning,
429
+ stacklevel=2,
430
+ )
431
+ return self.github_get_all_file_paths(*args, **kwargs)
432
+
433
+ def retrieve_file_content(self, *args, **kwargs):
434
+ r"""Deprecated: Use github_retrieve_file_content instead."""
435
+ warnings.warn(
436
+ "retrieve_file_content is deprecated. "
437
+ "Use github_retrieve_file_content instead.",
438
+ DeprecationWarning,
439
+ stacklevel=2,
440
+ )
441
+ return self.github_retrieve_file_content(*args, **kwargs)
@@ -44,6 +44,9 @@ class BrowserConfig:
44
44
  connect_over_cdp: bool = False
45
45
  cdp_url: Optional[str] = None
46
46
 
47
+ # Full visual mode configuration
48
+ full_visual_mode: bool = False
49
+
47
50
 
48
51
  @dataclass
49
52
  class ToolkitConfig:
@@ -51,6 +54,7 @@ class ToolkitConfig:
51
54
 
52
55
  cache_dir: str = "tmp/"
53
56
  browser_log_to_file: bool = False
57
+ log_dir: Optional[str] = None
54
58
  session_id: Optional[str] = None
55
59
  enabled_tools: Optional[list] = None
56
60
 
@@ -116,6 +120,8 @@ class ConfigLoader:
116
120
  toolkit_kwargs["session_id"] = value
117
121
  elif key == "enabledTools":
118
122
  toolkit_kwargs["enabled_tools"] = value
123
+ elif key == "fullVisualMode":
124
+ browser_kwargs["full_visual_mode"] = value
119
125
 
120
126
  browser_config = BrowserConfig(**browser_kwargs)
121
127
  toolkit_config = ToolkitConfig(**toolkit_kwargs)
@@ -142,10 +148,12 @@ class ConfigLoader:
142
148
  "screenshotTimeout": self.browser_config.screenshot_timeout,
143
149
  "pageStabilityTimeout": self.browser_config.page_stability_timeout,
144
150
  "browser_log_to_file": self.toolkit_config.browser_log_to_file,
151
+ "log_dir": self.toolkit_config.log_dir,
145
152
  "session_id": self.toolkit_config.session_id,
146
153
  "viewport_limit": self.browser_config.viewport_limit,
147
154
  "connectOverCdp": self.browser_config.connect_over_cdp,
148
155
  "cdpUrl": self.browser_config.cdp_url,
156
+ "fullVisualMode": self.browser_config.full_visual_mode,
149
157
  }
150
158
 
151
159
  def get_timeout_config(self) -> Dict[str, Optional[int]]:
@@ -38,6 +38,7 @@ class HybridBrowserToolkit(BaseToolkit):
38
38
  cache_dir: str = "tmp/",
39
39
  enabled_tools: Optional[List[str]] = None,
40
40
  browser_log_to_file: bool = False,
41
+ log_dir: Optional[str] = None,
41
42
  session_id: Optional[str] = None,
42
43
  default_start_url: str = "https://google.com/",
43
44
  default_timeout: Optional[int] = None,
@@ -50,6 +51,7 @@ class HybridBrowserToolkit(BaseToolkit):
50
51
  viewport_limit: bool = False,
51
52
  connect_over_cdp: bool = False,
52
53
  cdp_url: Optional[str] = None,
54
+ full_visual_mode: bool = False,
53
55
  **kwargs: Any,
54
56
  ) -> Any:
55
57
  r"""Create a HybridBrowserToolkit instance with the specified mode.
@@ -72,6 +74,8 @@ class HybridBrowserToolkit(BaseToolkit):
72
74
  Defaults to None.
73
75
  browser_log_to_file (bool): Whether to log browser actions to
74
76
  file. Defaults to False.
77
+ log_dir (Optional[str]): Custom directory path for log files.
78
+ If None, defaults to "browser_log". Defaults to None.
75
79
  session_id (Optional[str]): Session identifier. Defaults to None.
76
80
  default_start_url (str): Default URL to start with. Defaults
77
81
  to "https://google.com/".
@@ -98,6 +102,11 @@ class HybridBrowserToolkit(BaseToolkit):
98
102
  cdp_url (Optional[str]): WebSocket endpoint URL for CDP
99
103
  connection. Required when connect_over_cdp is True.
100
104
  Defaults to None. (Only supported in TypeScript mode)
105
+ full_visual_mode (bool): When True, browser actions like click,
106
+ browser_open, visit_page, etc. will return 'full visual mode'
107
+ as snapshot instead of actual page content. The
108
+ browser_get_page_snapshot method will still return the actual
109
+ snapshot. Defaults to False.
101
110
  **kwargs: Additional keyword arguments passed to the
102
111
  implementation.
103
112
 
@@ -117,6 +126,7 @@ class HybridBrowserToolkit(BaseToolkit):
117
126
  cache_dir=cache_dir,
118
127
  enabled_tools=enabled_tools,
119
128
  browser_log_to_file=browser_log_to_file,
129
+ log_dir=log_dir,
120
130
  session_id=session_id,
121
131
  default_start_url=default_start_url,
122
132
  default_timeout=default_timeout,
@@ -129,6 +139,7 @@ class HybridBrowserToolkit(BaseToolkit):
129
139
  viewport_limit=viewport_limit,
130
140
  connect_over_cdp=connect_over_cdp,
131
141
  cdp_url=cdp_url,
142
+ full_visual_mode=full_visual_mode,
132
143
  **kwargs,
133
144
  )
134
145
  elif mode == "python":
@@ -160,6 +171,7 @@ class HybridBrowserToolkit(BaseToolkit):
160
171
  cache_dir=cache_dir,
161
172
  enabled_tools=enabled_tools,
162
173
  browser_log_to_file=browser_log_to_file,
174
+ log_dir=log_dir,
163
175
  session_id=session_id,
164
176
  default_start_url=default_start_url,
165
177
  default_timeout=default_timeout,
@@ -86,6 +86,7 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
86
86
  cache_dir: str = "tmp/",
87
87
  enabled_tools: Optional[List[str]] = None,
88
88
  browser_log_to_file: bool = False,
89
+ log_dir: Optional[str] = None,
89
90
  session_id: Optional[str] = None,
90
91
  default_start_url: str = "https://google.com/",
91
92
  default_timeout: Optional[int] = None,
@@ -98,6 +99,7 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
98
99
  viewport_limit: bool = False,
99
100
  connect_over_cdp: bool = False,
100
101
  cdp_url: Optional[str] = None,
102
+ full_visual_mode: bool = False,
101
103
  ) -> None:
102
104
  r"""Initialize the HybridBrowserToolkit.
103
105
 
@@ -115,6 +117,8 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
115
117
  Defaults to None.
116
118
  browser_log_to_file (bool): Whether to log browser actions to
117
119
  file. Defaults to False.
120
+ log_dir (Optional[str]): Custom directory path for log files.
121
+ If None, defaults to "browser_log". Defaults to None.
118
122
  session_id (Optional[str]): Session identifier. Defaults to None.
119
123
  default_start_url (str): Default URL to start with. Defaults
120
124
  to "https://google.com/".
@@ -143,6 +147,9 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
143
147
  cdp_url (Optional[str]): WebSocket endpoint URL for CDP
144
148
  connection (e.g., 'ws://localhost:9222/devtools/browser/...').
145
149
  Required when connect_over_cdp is True. Defaults to None.
150
+ full_visual_mode (bool): When True, browser actions like click,
151
+ browser_open, visit_page, etc. will not return snapshots.
152
+ Defaults to False.
146
153
  """
147
154
  super().__init__()
148
155
  RegisteredAgentToolkit.__init__(self)
@@ -163,10 +170,12 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
163
170
  viewport_limit=viewport_limit,
164
171
  cache_dir=cache_dir,
165
172
  browser_log_to_file=browser_log_to_file,
173
+ log_dir=log_dir,
166
174
  session_id=session_id,
167
175
  enabled_tools=enabled_tools,
168
176
  connect_over_cdp=connect_over_cdp,
169
177
  cdp_url=cdp_url,
178
+ full_visual_mode=full_visual_mode,
170
179
  )
171
180
 
172
181
  # Legacy attribute access for backward compatibility
@@ -182,6 +191,7 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
182
191
  self._default_start_url = browser_config.default_start_url
183
192
  self._session_id = toolkit_config.session_id or "default"
184
193
  self._viewport_limit = browser_config.viewport_limit
194
+ self._full_visual_mode = browser_config.full_visual_mode
185
195
 
186
196
  # Store timeout configuration for backward compatibility
187
197
  self._default_timeout = browser_config.default_timeout
@@ -648,22 +658,29 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
648
658
 
649
659
  # Add tab information
650
660
  tab_info = await ws_wrapper.get_tab_info()
651
- result.update(
652
- {
653
- "tabs": tab_info,
654
- "current_tab": next(
655
- (
656
- i
657
- for i, tab in enumerate(tab_info)
658
- if tab.get("is_current")
659
- ),
660
- 0,
661
+
662
+ response = {
663
+ "result": result.get("result", ""),
664
+ "snapshot": result.get("snapshot", ""),
665
+ "tabs": tab_info,
666
+ "current_tab": next(
667
+ (
668
+ i
669
+ for i, tab in enumerate(tab_info)
670
+ if tab.get("is_current")
661
671
  ),
662
- "total_tabs": len(tab_info),
663
- }
664
- )
672
+ 0,
673
+ ),
674
+ "total_tabs": len(tab_info),
675
+ }
665
676
 
666
- return result
677
+ if "newTabId" in result:
678
+ response["newTabId"] = result["newTabId"]
679
+
680
+ if "timing" in result:
681
+ response["timing"] = result["timing"]
682
+
683
+ return response
667
684
  except Exception as e:
668
685
  logger.error(f"Failed to click element: {e}")
669
686
  return {
@@ -1377,6 +1394,8 @@ class HybridBrowserToolkit(BaseToolkit, RegisteredAgentToolkit):
1377
1394
  screenshot_timeout=self._screenshot_timeout,
1378
1395
  page_stability_timeout=self._page_stability_timeout,
1379
1396
  dom_content_loaded_timeout=self._dom_content_loaded_timeout,
1397
+ viewport_limit=self._viewport_limit,
1398
+ full_visual_mode=self._full_visual_mode,
1380
1399
  )
1381
1400
 
1382
1401
  def get_tools(self) -> List[FunctionTool]: