agentscope-runtime 1.0.3__py3-none-any.whl → 1.0.4__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.
- agentscope_runtime/adapters/agentscope/stream.py +2 -9
- agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
- agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
- agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
- agentscope_runtime/adapters/utils.py +6 -0
- agentscope_runtime/cli/commands/deploy.py +371 -0
- agentscope_runtime/common/container_clients/knative_client.py +466 -0
- agentscope_runtime/engine/__init__.py +4 -0
- agentscope_runtime/engine/constant.py +1 -0
- agentscope_runtime/engine/deployers/__init__.py +12 -0
- agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +26 -51
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +19 -10
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +4 -201
- agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +134 -25
- agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
- agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
- agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
- agentscope_runtime/engine/runner.py +12 -0
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +2 -2
- agentscope_runtime/engine/services/memory/redis_memory_service.py +2 -2
- agentscope_runtime/engine/services/session_history/redis_session_history_service.py +2 -2
- agentscope_runtime/engine/tracing/wrapper.py +18 -4
- agentscope_runtime/sandbox/__init__.py +14 -6
- agentscope_runtime/sandbox/box/base/__init__.py +2 -2
- agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
- agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
- agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
- agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
- agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
- agentscope_runtime/sandbox/box/sandbox.py +98 -65
- agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
- agentscope_runtime/sandbox/client/__init__.py +6 -1
- agentscope_runtime/sandbox/client/async_http_client.py +339 -0
- agentscope_runtime/sandbox/client/base.py +74 -0
- agentscope_runtime/sandbox/client/http_client.py +108 -329
- agentscope_runtime/sandbox/enums.py +7 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +264 -4
- agentscope_runtime/sandbox/manager/server/app.py +7 -1
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/METADATA +102 -28
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/RECORD +49 -40
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/WHEEL +0 -0
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.3.dist-info → agentscope_runtime-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -6,8 +6,8 @@ from urllib.parse import urlparse, urlunparse
|
|
|
6
6
|
from ...utils import build_image_uri
|
|
7
7
|
from ...registry import SandboxRegistry
|
|
8
8
|
from ...enums import SandboxType
|
|
9
|
-
from ...box.base import BaseSandbox
|
|
10
|
-
from ...box.gui import GUIMixin
|
|
9
|
+
from ...box.base import BaseSandbox, BaseSandboxAsync
|
|
10
|
+
from ...box.gui import GUIMixin, AsyncGUIMixin
|
|
11
11
|
from ...constant import TIMEOUT
|
|
12
12
|
|
|
13
13
|
|
|
@@ -299,3 +299,199 @@ class BrowserSandbox(GUIMixin, BaseSandbox):
|
|
|
299
299
|
"textGone": text_gone,
|
|
300
300
|
},
|
|
301
301
|
)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@SandboxRegistry.register(
|
|
305
|
+
build_image_uri("runtime-sandbox-browser"),
|
|
306
|
+
sandbox_type=SandboxType.BROWSER_ASYNC,
|
|
307
|
+
security_level="medium",
|
|
308
|
+
timeout=TIMEOUT,
|
|
309
|
+
description="Browser sandbox (Async)",
|
|
310
|
+
)
|
|
311
|
+
class BrowserSandboxAsync(GUIMixin, AsyncGUIMixin, BaseSandboxAsync):
|
|
312
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
313
|
+
self,
|
|
314
|
+
sandbox_id: Optional[str] = None,
|
|
315
|
+
timeout: int = 3000,
|
|
316
|
+
base_url: Optional[str] = None,
|
|
317
|
+
bearer_token: Optional[str] = None,
|
|
318
|
+
sandbox_type: SandboxType = SandboxType.BROWSER_ASYNC,
|
|
319
|
+
):
|
|
320
|
+
super().__init__(
|
|
321
|
+
sandbox_id,
|
|
322
|
+
timeout,
|
|
323
|
+
base_url,
|
|
324
|
+
bearer_token,
|
|
325
|
+
sandbox_type,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
async def browser_close(self):
|
|
329
|
+
"""Close the current browser page."""
|
|
330
|
+
return await self.call_tool_async("browser_close", {})
|
|
331
|
+
|
|
332
|
+
async def browser_resize(self, width: int, height: int):
|
|
333
|
+
"""Resize the browser window."""
|
|
334
|
+
return await self.call_tool_async(
|
|
335
|
+
"browser_resize",
|
|
336
|
+
{"width": width, "height": height},
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
async def browser_console_messages(self):
|
|
340
|
+
"""Return all console messages from the browser."""
|
|
341
|
+
return await self.call_tool_async("browser_console_messages", {})
|
|
342
|
+
|
|
343
|
+
async def browser_handle_dialog(self, accept: bool, prompt_text: str = ""):
|
|
344
|
+
"""Handle a dialog popup."""
|
|
345
|
+
return await self.call_tool_async(
|
|
346
|
+
"browser_handle_dialog",
|
|
347
|
+
{"accept": accept, "promptText": prompt_text},
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
async def browser_file_upload(self, paths: list):
|
|
351
|
+
"""Upload one or multiple files."""
|
|
352
|
+
return await self.call_tool_async(
|
|
353
|
+
"browser_file_upload",
|
|
354
|
+
{"paths": paths},
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
async def browser_press_key(self, key: str):
|
|
358
|
+
"""Press a key in the browser."""
|
|
359
|
+
return await self.call_tool_async("browser_press_key", {"key": key})
|
|
360
|
+
|
|
361
|
+
async def browser_navigate(self, url: str):
|
|
362
|
+
"""Navigate to a URL."""
|
|
363
|
+
return await self.call_tool_async("browser_navigate", {"url": url})
|
|
364
|
+
|
|
365
|
+
async def browser_navigate_back(self):
|
|
366
|
+
"""Go back in browser history."""
|
|
367
|
+
return await self.call_tool_async("browser_navigate_back", {})
|
|
368
|
+
|
|
369
|
+
async def browser_navigate_forward(self):
|
|
370
|
+
"""Go forward in browser history."""
|
|
371
|
+
return await self.call_tool_async("browser_navigate_forward", {})
|
|
372
|
+
|
|
373
|
+
async def browser_network_requests(self):
|
|
374
|
+
"""Return network requests."""
|
|
375
|
+
return await self.call_tool_async("browser_network_requests", {})
|
|
376
|
+
|
|
377
|
+
async def browser_pdf_save(self, filename: str = ""):
|
|
378
|
+
"""Save page as a PDF."""
|
|
379
|
+
return await self.call_tool_async(
|
|
380
|
+
"browser_pdf_save",
|
|
381
|
+
{"filename": filename},
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
async def browser_take_screenshot(
|
|
385
|
+
self,
|
|
386
|
+
raw=False,
|
|
387
|
+
filename="",
|
|
388
|
+
element="",
|
|
389
|
+
ref="",
|
|
390
|
+
):
|
|
391
|
+
"""Take a screenshot."""
|
|
392
|
+
return await self.call_tool_async(
|
|
393
|
+
"browser_take_screenshot",
|
|
394
|
+
{"raw": raw, "filename": filename, "element": element, "ref": ref},
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
async def browser_snapshot(self):
|
|
398
|
+
"""Accessibility snapshot."""
|
|
399
|
+
return await self.call_tool_async("browser_snapshot", {})
|
|
400
|
+
|
|
401
|
+
async def browser_click(self, element: str, ref: str):
|
|
402
|
+
"""Click an element."""
|
|
403
|
+
return await self.call_tool_async(
|
|
404
|
+
"browser_click",
|
|
405
|
+
{"element": element, "ref": ref},
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
async def browser_drag(
|
|
409
|
+
self,
|
|
410
|
+
start_element: str,
|
|
411
|
+
start_ref: str,
|
|
412
|
+
end_element: str,
|
|
413
|
+
end_ref: str,
|
|
414
|
+
):
|
|
415
|
+
"""Drag and drop."""
|
|
416
|
+
return await self.call_tool_async(
|
|
417
|
+
"browser_drag",
|
|
418
|
+
{
|
|
419
|
+
"startElement": start_element,
|
|
420
|
+
"startRef": start_ref,
|
|
421
|
+
"endElement": end_element,
|
|
422
|
+
"endRef": end_ref,
|
|
423
|
+
},
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
async def browser_hover(self, element: str, ref: str):
|
|
427
|
+
"""Hover over an element."""
|
|
428
|
+
return await self.call_tool_async(
|
|
429
|
+
"browser_hover",
|
|
430
|
+
{"element": element, "ref": ref},
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
async def browser_type(
|
|
434
|
+
self,
|
|
435
|
+
element: str,
|
|
436
|
+
ref: str,
|
|
437
|
+
text: str,
|
|
438
|
+
submit=False,
|
|
439
|
+
slowly=False,
|
|
440
|
+
):
|
|
441
|
+
"""Type text into an element."""
|
|
442
|
+
return await self.call_tool_async(
|
|
443
|
+
"browser_type",
|
|
444
|
+
{
|
|
445
|
+
"element": element,
|
|
446
|
+
"ref": ref,
|
|
447
|
+
"text": text,
|
|
448
|
+
"submit": submit,
|
|
449
|
+
"slowly": slowly,
|
|
450
|
+
},
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
async def browser_select_option(
|
|
454
|
+
self,
|
|
455
|
+
element: str,
|
|
456
|
+
ref: str,
|
|
457
|
+
values: list,
|
|
458
|
+
):
|
|
459
|
+
"""Select options in a dropdown."""
|
|
460
|
+
return await self.call_tool_async(
|
|
461
|
+
"browser_select_option",
|
|
462
|
+
{"element": element, "ref": ref, "values": values},
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
async def browser_tab_list(self):
|
|
466
|
+
"""List all tabs."""
|
|
467
|
+
return await self.call_tool_async("browser_tab_list", {})
|
|
468
|
+
|
|
469
|
+
async def browser_tab_new(self, url: str = ""):
|
|
470
|
+
"""Open a new tab."""
|
|
471
|
+
return await self.call_tool_async("browser_tab_new", {"url": url})
|
|
472
|
+
|
|
473
|
+
async def browser_tab_select(self, index: int):
|
|
474
|
+
"""Select tab by index."""
|
|
475
|
+
return await self.call_tool_async(
|
|
476
|
+
"browser_tab_select",
|
|
477
|
+
{"index": index},
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
async def browser_tab_close(self, index: int = None):
|
|
481
|
+
"""Close a tab."""
|
|
482
|
+
return await self.call_tool_async(
|
|
483
|
+
"browser_tab_close",
|
|
484
|
+
{"index": index},
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
async def browser_wait_for(
|
|
488
|
+
self,
|
|
489
|
+
time: float = None,
|
|
490
|
+
text: str = None,
|
|
491
|
+
text_gone: str = None,
|
|
492
|
+
):
|
|
493
|
+
"""Wait for text or time."""
|
|
494
|
+
return await self.call_tool_async(
|
|
495
|
+
"browser_wait_for",
|
|
496
|
+
{"time": time, "text": text, "textGone": text_gone},
|
|
497
|
+
)
|
|
@@ -5,8 +5,8 @@ from typing import Optional
|
|
|
5
5
|
from ...utils import build_image_uri
|
|
6
6
|
from ...registry import SandboxRegistry
|
|
7
7
|
from ...enums import SandboxType
|
|
8
|
-
from ...box.base import BaseSandbox
|
|
9
|
-
from ...box.gui import GUIMixin
|
|
8
|
+
from ...box.base import BaseSandbox, BaseSandboxAsync
|
|
9
|
+
from ...box.gui import GUIMixin, AsyncGUIMixin
|
|
10
10
|
from ...constant import TIMEOUT
|
|
11
11
|
|
|
12
12
|
|
|
@@ -154,3 +154,100 @@ class FilesystemSandbox(GUIMixin, BaseSandbox):
|
|
|
154
154
|
Returns the list of directories that this serveris allowed to access.
|
|
155
155
|
"""
|
|
156
156
|
return self.call_tool("list_allowed_directories", {})
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@SandboxRegistry.register(
|
|
160
|
+
build_image_uri("runtime-sandbox-filesystem"),
|
|
161
|
+
sandbox_type=SandboxType.FILESYSTEM_ASYNC,
|
|
162
|
+
security_level="medium",
|
|
163
|
+
timeout=TIMEOUT,
|
|
164
|
+
description="Filesystem sandbox (Async)",
|
|
165
|
+
)
|
|
166
|
+
class FilesystemSandboxAsync(GUIMixin, AsyncGUIMixin, BaseSandboxAsync):
|
|
167
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
168
|
+
self,
|
|
169
|
+
sandbox_id: Optional[str] = None,
|
|
170
|
+
timeout: int = 3000,
|
|
171
|
+
base_url: Optional[str] = None,
|
|
172
|
+
bearer_token: Optional[str] = None,
|
|
173
|
+
sandbox_type: SandboxType = SandboxType.FILESYSTEM_ASYNC,
|
|
174
|
+
):
|
|
175
|
+
super().__init__(
|
|
176
|
+
sandbox_id,
|
|
177
|
+
timeout,
|
|
178
|
+
base_url,
|
|
179
|
+
bearer_token,
|
|
180
|
+
sandbox_type,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
async def read_file(self, path: str):
|
|
184
|
+
"""Read the complete contents of a file."""
|
|
185
|
+
return await self.call_tool_async("read_file", {"path": path})
|
|
186
|
+
|
|
187
|
+
async def read_multiple_files(self, paths: list):
|
|
188
|
+
"""Read the contents of multiple files simultaneously."""
|
|
189
|
+
return await self.call_tool_async(
|
|
190
|
+
"read_multiple_files",
|
|
191
|
+
{"paths": paths},
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
async def write_file(self, path: str, content: str):
|
|
195
|
+
"""Create or overwrite a file with new content."""
|
|
196
|
+
return await self.call_tool_async(
|
|
197
|
+
"write_file",
|
|
198
|
+
{"path": path, "content": content},
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
async def edit_file(self, path: str, edits: list, dry_run: bool = False):
|
|
202
|
+
"""Make line-based edits to a text file."""
|
|
203
|
+
return await self.call_tool_async(
|
|
204
|
+
"edit_file",
|
|
205
|
+
{
|
|
206
|
+
"path": path,
|
|
207
|
+
"edits": edits,
|
|
208
|
+
"dryRun": dry_run,
|
|
209
|
+
},
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
async def create_directory(self, path: str):
|
|
213
|
+
"""Create a new directory or ensure it exists."""
|
|
214
|
+
return await self.call_tool_async("create_directory", {"path": path})
|
|
215
|
+
|
|
216
|
+
async def list_directory(self, path: str):
|
|
217
|
+
"""Get a detailed listing of all files and directories."""
|
|
218
|
+
return await self.call_tool_async("list_directory", {"path": path})
|
|
219
|
+
|
|
220
|
+
async def directory_tree(self, path: str):
|
|
221
|
+
"""Get a recursive tree view of files and directories as JSON."""
|
|
222
|
+
return await self.call_tool_async("directory_tree", {"path": path})
|
|
223
|
+
|
|
224
|
+
async def move_file(self, source: str, destination: str):
|
|
225
|
+
"""Move or rename files and directories."""
|
|
226
|
+
return await self.call_tool_async(
|
|
227
|
+
"move_file",
|
|
228
|
+
{"source": source, "destination": destination},
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
async def search_files(
|
|
232
|
+
self,
|
|
233
|
+
path: str,
|
|
234
|
+
pattern: str,
|
|
235
|
+
exclude_patterns: list = [],
|
|
236
|
+
):
|
|
237
|
+
"""Recursively search for files and directories matching a pattern."""
|
|
238
|
+
return await self.call_tool_async(
|
|
239
|
+
"search_files",
|
|
240
|
+
{
|
|
241
|
+
"path": path,
|
|
242
|
+
"pattern": pattern,
|
|
243
|
+
"excludePatterns": exclude_patterns,
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
async def get_file_info(self, path: str):
|
|
248
|
+
"""Retrieve metadata about a file or directory."""
|
|
249
|
+
return await self.call_tool_async("get_file_info", {"path": path})
|
|
250
|
+
|
|
251
|
+
async def list_allowed_directories(self):
|
|
252
|
+
"""Returns directories this server can access."""
|
|
253
|
+
return await self.call_tool_async("list_allowed_directories", {})
|
|
@@ -8,7 +8,7 @@ from urllib.parse import urljoin, urlencode
|
|
|
8
8
|
from ...utils import build_image_uri, get_platform
|
|
9
9
|
from ...registry import SandboxRegistry
|
|
10
10
|
from ...enums import SandboxType
|
|
11
|
-
from ...box.base import BaseSandbox
|
|
11
|
+
from ...box.base import BaseSandbox, BaseSandboxAsync
|
|
12
12
|
from ...constant import TIMEOUT
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
@@ -34,6 +34,34 @@ class GUIMixin:
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
class AsyncGUIMixin:
|
|
38
|
+
async def get_desktop_url_async(self):
|
|
39
|
+
# Check sandbox health asynchronously
|
|
40
|
+
is_healthy = await self.manager_api.check_health_async(
|
|
41
|
+
identity=self.sandbox_id,
|
|
42
|
+
)
|
|
43
|
+
if not is_healthy:
|
|
44
|
+
raise RuntimeError(f"Sandbox {self.sandbox_id} is not healthy")
|
|
45
|
+
|
|
46
|
+
# Retrieve container information asynchronously
|
|
47
|
+
info = await self.get_info_async()
|
|
48
|
+
|
|
49
|
+
# Default local VNC path and remote VNC relay path
|
|
50
|
+
path = "/vnc/vnc_lite.html"
|
|
51
|
+
remote_path = "/vnc/vnc_relay.html"
|
|
52
|
+
params = {"password": info["runtime_token"]}
|
|
53
|
+
|
|
54
|
+
# If base_url is not set, construct the local URL
|
|
55
|
+
if self.base_url is None:
|
|
56
|
+
return urljoin(info["url"], path) + "?" + urlencode(params)
|
|
57
|
+
|
|
58
|
+
# Construct the remote URL with sandbox ID and VNC relay path
|
|
59
|
+
return (
|
|
60
|
+
f"{self.base_url}/desktop/{self.sandbox_id}{remote_path}"
|
|
61
|
+
f"?{urlencode(params)}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
37
65
|
@SandboxRegistry.register(
|
|
38
66
|
build_image_uri("runtime-sandbox-gui"),
|
|
39
67
|
sandbox_type=SandboxType.GUI,
|
|
@@ -121,3 +149,91 @@ class GuiSandbox(GUIMixin, BaseSandbox):
|
|
|
121
149
|
payload["text"] = text
|
|
122
150
|
|
|
123
151
|
return self.call_tool("computer", payload)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@SandboxRegistry.register(
|
|
155
|
+
build_image_uri("runtime-sandbox-gui"),
|
|
156
|
+
sandbox_type=SandboxType.GUI_ASYNC,
|
|
157
|
+
security_level="high",
|
|
158
|
+
timeout=TIMEOUT,
|
|
159
|
+
description="GUI Sandbox (Async)",
|
|
160
|
+
)
|
|
161
|
+
class GuiSandboxAsync(GUIMixin, AsyncGUIMixin, BaseSandboxAsync):
|
|
162
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
163
|
+
self,
|
|
164
|
+
sandbox_id: Optional[str] = None,
|
|
165
|
+
timeout: int = 3000,
|
|
166
|
+
base_url: Optional[str] = None,
|
|
167
|
+
bearer_token: Optional[str] = None,
|
|
168
|
+
sandbox_type: SandboxType = SandboxType.GUI_ASYNC,
|
|
169
|
+
):
|
|
170
|
+
super().__init__(
|
|
171
|
+
sandbox_id,
|
|
172
|
+
timeout,
|
|
173
|
+
base_url,
|
|
174
|
+
bearer_token,
|
|
175
|
+
sandbox_type,
|
|
176
|
+
)
|
|
177
|
+
# Architecture compatibility warning
|
|
178
|
+
if get_platform() == "linux/arm64":
|
|
179
|
+
logger.warning(
|
|
180
|
+
"\nCompatibility Notice: This GUI Sandbox may have issues on "
|
|
181
|
+
"arm64 CPU architectures, due to the computer-use-mcp not "
|
|
182
|
+
"providing linux/arm64 compatibility. It has been tested "
|
|
183
|
+
"on Apple M4 chips with Rosetta enabled. However, on M1, M2, "
|
|
184
|
+
"and M3 chips, Chromium browser might crash due to the missing "
|
|
185
|
+
"SSE3 instruction set.",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
async def computer_use(
|
|
189
|
+
self,
|
|
190
|
+
action: str,
|
|
191
|
+
coordinate: Optional[Union[List[float], Tuple[float, float]]] = None,
|
|
192
|
+
text: Optional[str] = None,
|
|
193
|
+
):
|
|
194
|
+
"""
|
|
195
|
+
Asynchronously use mouse and keyboard to interact with a desktop GUI.
|
|
196
|
+
|
|
197
|
+
This method interfaces with the sandbox's GUI environment.
|
|
198
|
+
You do not have access to a terminal or applications menu;
|
|
199
|
+
interaction is performed by clicking on desktop icons or using
|
|
200
|
+
keyboard shortcuts.
|
|
201
|
+
|
|
202
|
+
Guidelines:
|
|
203
|
+
* Prefer keyboard shortcuts where possible over cursor actions.
|
|
204
|
+
* If visual keyboard hints (two-letter boxes) are shown, typing
|
|
205
|
+
those letters will click the element — use this where possible.
|
|
206
|
+
* Applications or actions may require waiting; e.g., repeat
|
|
207
|
+
screenshots if windows don’t open immediately.
|
|
208
|
+
* Always determine cursor coordinates using screenshots before moving
|
|
209
|
+
the cursor to click on elements.
|
|
210
|
+
* If clicks fail to load content, try adjusting cursor coordinates to
|
|
211
|
+
center on the target element.
|
|
212
|
+
* Click with the cursor tip centered on elements, not on edges.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
action (str): The action to perform. Options include:
|
|
216
|
+
* `key` — Press a key or key combination.
|
|
217
|
+
* `type` — Type a string of text.
|
|
218
|
+
* `get_cursor_position` — Get current cursor coordinates (x, y).
|
|
219
|
+
* `mouse_move` — Move cursor to given (x, y).
|
|
220
|
+
* `left_click` — Left mouse click.
|
|
221
|
+
* `left_click_drag` — Click and drag to given (x, y).
|
|
222
|
+
* `right_click` — Right mouse click.
|
|
223
|
+
* `middle_click` — Middle mouse click.
|
|
224
|
+
* `double_click` — Double left mouse click.
|
|
225
|
+
* `get_screenshot` — Capture screen screenshot.
|
|
226
|
+
coordinate (list[float] | tuple[float, float], optional):
|
|
227
|
+
Pixel coordinates (x from left edge, y from top edge).
|
|
228
|
+
text (str, optional): String to type, or key-combination for `key` action.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Any: Tool execution result from the sandbox.
|
|
232
|
+
"""
|
|
233
|
+
payload = {"action": action}
|
|
234
|
+
if coordinate is not None:
|
|
235
|
+
payload["coordinate"] = coordinate
|
|
236
|
+
if text is not None:
|
|
237
|
+
payload["text"] = text
|
|
238
|
+
|
|
239
|
+
return await self.call_tool_async("computer", payload)
|