agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.2.0__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/common/__init__.py +0 -0
- agentscope_runtime/common/collections/in_memory_mapping.py +27 -0
- agentscope_runtime/common/collections/redis_mapping.py +42 -0
- agentscope_runtime/common/container_clients/__init__.py +0 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +1098 -0
- agentscope_runtime/common/container_clients/docker_client.py +250 -0
- agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +6 -13
- agentscope_runtime/engine/__init__.py +12 -0
- agentscope_runtime/engine/agents/agentscope_agent.py +567 -0
- agentscope_runtime/engine/agents/agno_agent.py +26 -27
- agentscope_runtime/engine/agents/autogen_agent.py +13 -8
- agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
- agentscope_runtime/engine/agents/utils.py +53 -0
- agentscope_runtime/engine/app/__init__.py +6 -0
- agentscope_runtime/engine/app/agent_app.py +239 -0
- agentscope_runtime/engine/app/base_app.py +181 -0
- agentscope_runtime/engine/app/celery_mixin.py +92 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -1
- agentscope_runtime/engine/deployers/base.py +1 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +39 -20
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +12 -5
- agentscope_runtime/engine/deployers/local_deployer.py +61 -3
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +201 -40
- agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +9 -0
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +234 -3
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +567 -7
- agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +1 -1
- agentscope_runtime/engine/helpers/helper.py +60 -41
- agentscope_runtime/engine/runner.py +40 -24
- agentscope_runtime/engine/schemas/agent_schemas.py +42 -0
- agentscope_runtime/engine/schemas/modelstudio_llm.py +14 -14
- agentscope_runtime/engine/services/sandbox_service.py +62 -70
- agentscope_runtime/engine/services/tablestore_memory_service.py +307 -0
- agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
- agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
- agentscope_runtime/engine/services/utils/__init__.py +0 -0
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
- agentscope_runtime/engine/tracing/__init__.py +9 -3
- agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
- agentscope_runtime/engine/tracing/base.py +66 -34
- agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
- agentscope_runtime/engine/tracing/message_util.py +528 -0
- agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
- agentscope_runtime/engine/tracing/tracing_util.py +130 -0
- agentscope_runtime/engine/tracing/wrapper.py +794 -169
- agentscope_runtime/sandbox/__init__.py +2 -0
- agentscope_runtime/sandbox/box/base/__init__.py +4 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +6 -4
- agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +10 -14
- agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
- agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +10 -7
- agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
- agentscope_runtime/sandbox/box/gui/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +81 -0
- agentscope_runtime/sandbox/box/sandbox.py +5 -2
- agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
- agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +7 -54
- agentscope_runtime/sandbox/build.py +143 -58
- agentscope_runtime/sandbox/client/http_client.py +87 -59
- agentscope_runtime/sandbox/client/training_client.py +0 -1
- agentscope_runtime/sandbox/constant.py +27 -1
- agentscope_runtime/sandbox/custom/custom_sandbox.py +7 -6
- agentscope_runtime/sandbox/custom/example.py +4 -3
- agentscope_runtime/sandbox/enums.py +1 -1
- agentscope_runtime/sandbox/manager/sandbox_manager.py +212 -106
- agentscope_runtime/sandbox/manager/server/app.py +82 -14
- agentscope_runtime/sandbox/manager/server/config.py +50 -3
- agentscope_runtime/sandbox/model/container.py +12 -23
- agentscope_runtime/sandbox/model/manager_config.py +93 -5
- agentscope_runtime/sandbox/registry.py +1 -1
- agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
- agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
- agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
- agentscope_runtime/sandbox/tools/tool.py +4 -0
- agentscope_runtime/sandbox/utils.py +124 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +246 -111
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +96 -80
- agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
- agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
- agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
- agentscope_runtime/engine/agents/llm_agent.py +0 -51
- agentscope_runtime/engine/llms/__init__.py +0 -3
- agentscope_runtime/engine/llms/base_llm.py +0 -60
- agentscope_runtime/engine/llms/qwen_llm.py +0 -47
- agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +0 -22
- agentscope_runtime/sandbox/manager/collections/redis_mapping.py +0 -26
- agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +0 -422
- /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -6,11 +6,13 @@ import logging
|
|
|
6
6
|
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
|
+
import httpx
|
|
9
10
|
import websockets
|
|
11
|
+
|
|
10
12
|
from fastapi import FastAPI, HTTPException, Request, Depends
|
|
11
13
|
from fastapi import WebSocket, WebSocketDisconnect
|
|
12
14
|
from fastapi.middleware.cors import CORSMiddleware
|
|
13
|
-
from fastapi.responses import JSONResponse
|
|
15
|
+
from fastapi.responses import JSONResponse, Response
|
|
14
16
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
15
17
|
|
|
16
18
|
from ...manager.server.config import get_settings
|
|
@@ -20,6 +22,7 @@ from ...manager.server.models import (
|
|
|
20
22
|
)
|
|
21
23
|
from ...manager.sandbox_manager import SandboxManager
|
|
22
24
|
from ...model.manager_config import SandboxManagerEnvConfig
|
|
25
|
+
from ...utils import dynamic_import, http_to_ws
|
|
23
26
|
from ....version import __version__
|
|
24
27
|
|
|
25
28
|
# Configure logging
|
|
@@ -61,6 +64,7 @@ def get_config() -> SandboxManagerEnvConfig:
|
|
|
61
64
|
redis_enabled=settings.REDIS_ENABLED,
|
|
62
65
|
container_deployment=settings.CONTAINER_DEPLOYMENT,
|
|
63
66
|
default_mount_dir=settings.DEFAULT_MOUNT_DIR,
|
|
67
|
+
readonly_mounts=settings.READONLY_MOUNTS,
|
|
64
68
|
storage_folder=settings.STORAGE_FOLDER,
|
|
65
69
|
port_range=settings.PORT_RANGE,
|
|
66
70
|
pool_size=settings.POOL_SIZE,
|
|
@@ -77,6 +81,18 @@ def get_config() -> SandboxManagerEnvConfig:
|
|
|
77
81
|
redis_container_pool_key=settings.REDIS_CONTAINER_POOL_KEY,
|
|
78
82
|
k8s_namespace=settings.K8S_NAMESPACE,
|
|
79
83
|
kubeconfig_path=settings.KUBECONFIG_PATH,
|
|
84
|
+
agent_run_access_key_id=settings.AGENT_RUN_ACCESS_KEY_ID,
|
|
85
|
+
agent_run_access_key_secret=settings.AGENT_RUN_ACCESS_KEY_SECRET,
|
|
86
|
+
agent_run_account_id=settings.AGENT_RUN_ACCOUNT_ID,
|
|
87
|
+
agent_run_region_id=settings.AGENT_RUN_REGION_ID,
|
|
88
|
+
agent_run_cpu=settings.AGENT_RUN_CPU,
|
|
89
|
+
agent_run_memory=settings.AGENT_RUN_MEMORY,
|
|
90
|
+
agent_run_vpc_id=settings.AGENT_RUN_VPC_ID,
|
|
91
|
+
agent_run_vswitch_ids=settings.AGENT_RUN_VSWITCH_IDS,
|
|
92
|
+
agent_run_security_group_id=settings.AGENT_RUN_SECURITY_GROUP_ID,
|
|
93
|
+
agent_run_prefix=settings.AGENT_RUN_PREFIX,
|
|
94
|
+
agentrun_log_project=settings.AGENT_RUN_LOG_PROJECT,
|
|
95
|
+
agentrun_log_store=settings.AGENT_RUN_LOG_STORE,
|
|
80
96
|
)
|
|
81
97
|
return _config
|
|
82
98
|
|
|
@@ -134,15 +150,21 @@ def create_endpoint(method):
|
|
|
134
150
|
logger.info(
|
|
135
151
|
f"Calling {method.__name__} with data: {data}",
|
|
136
152
|
)
|
|
137
|
-
|
|
153
|
+
|
|
154
|
+
# Check if the method is asynchronous
|
|
155
|
+
if inspect.iscoroutinefunction(method):
|
|
156
|
+
# If async, just await it
|
|
157
|
+
result = await method(**data)
|
|
158
|
+
else:
|
|
159
|
+
# If sync, run it in a thread to avoid blocking the event loop
|
|
160
|
+
result = await asyncio.to_thread(method, **data)
|
|
161
|
+
|
|
138
162
|
if hasattr(result, "model_dump_json"):
|
|
139
163
|
return JSONResponse(content={"data": result.model_dump_json()})
|
|
140
164
|
return JSONResponse(content={"data": result})
|
|
165
|
+
|
|
141
166
|
except Exception as e:
|
|
142
|
-
error = (
|
|
143
|
-
f"Error in {method.__name__}: {str(e)},"
|
|
144
|
-
# f" {traceback.format_exc()}"
|
|
145
|
-
)
|
|
167
|
+
error = f"Error in {method.__name__}: {str(e)}"
|
|
146
168
|
logger.error(error)
|
|
147
169
|
raise HTTPException(status_code=500, detail=error) from e
|
|
148
170
|
|
|
@@ -194,11 +216,38 @@ async def health_check():
|
|
|
194
216
|
"""Health check endpoint"""
|
|
195
217
|
return HealthResponse(
|
|
196
218
|
status="healthy",
|
|
197
|
-
version=_sandbox_manager.default_type
|
|
219
|
+
version=str(_sandbox_manager.default_type),
|
|
198
220
|
)
|
|
199
221
|
|
|
200
222
|
|
|
201
|
-
@app.
|
|
223
|
+
@app.get("/desktop/{sandbox_id}/{path:path}")
|
|
224
|
+
async def proxy_vnc_static(sandbox_id: str, path: str):
|
|
225
|
+
container_json = _sandbox_manager.container_mapping.get(sandbox_id)
|
|
226
|
+
if not container_json:
|
|
227
|
+
return Response(status_code=404)
|
|
228
|
+
|
|
229
|
+
base_url = container_json.get("url")
|
|
230
|
+
if not base_url:
|
|
231
|
+
return Response(status_code=404)
|
|
232
|
+
|
|
233
|
+
target_url = f"{base_url}/{path}"
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
async with httpx.AsyncClient() as client:
|
|
237
|
+
upstream = await client.get(target_url)
|
|
238
|
+
return Response(
|
|
239
|
+
content=upstream.content,
|
|
240
|
+
media_type=upstream.headers.get("content-type"),
|
|
241
|
+
)
|
|
242
|
+
except httpx.RequestError as exc:
|
|
243
|
+
logger.error(f"Upstream request to {target_url} failed: {repr(exc)}")
|
|
244
|
+
return JSONResponse(
|
|
245
|
+
status_code=502,
|
|
246
|
+
content={"detail": f"Upstream request failed: {str(exc)}"},
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@app.websocket("/desktop/{sandbox_id}")
|
|
202
251
|
async def websocket_endpoint(
|
|
203
252
|
websocket: WebSocket,
|
|
204
253
|
sandbox_id: str,
|
|
@@ -212,7 +261,7 @@ async def websocket_endpoint(
|
|
|
212
261
|
)
|
|
213
262
|
service_address = None
|
|
214
263
|
if container_json:
|
|
215
|
-
service_address = container_json.get("
|
|
264
|
+
service_address = http_to_ws(f"{container_json.get('url')}/websockify")
|
|
216
265
|
|
|
217
266
|
logger.debug(f"service_address: {service_address}")
|
|
218
267
|
|
|
@@ -237,8 +286,17 @@ async def websocket_endpoint(
|
|
|
237
286
|
# Forward messages from client to target server
|
|
238
287
|
async def forward_to_service():
|
|
239
288
|
try:
|
|
240
|
-
|
|
241
|
-
await
|
|
289
|
+
while True:
|
|
290
|
+
message = await websocket.receive()
|
|
291
|
+
|
|
292
|
+
if message["type"] == "websocket.receive":
|
|
293
|
+
if "bytes" in message:
|
|
294
|
+
await target_ws.send(message["bytes"])
|
|
295
|
+
elif "text" in message:
|
|
296
|
+
await target_ws.send(message["text"])
|
|
297
|
+
elif message["type"] == "websocket.disconnect":
|
|
298
|
+
break
|
|
299
|
+
|
|
242
300
|
except WebSocketDisconnect:
|
|
243
301
|
logger.debug(
|
|
244
302
|
f"WebSocket disconnected from client for sandbox"
|
|
@@ -266,9 +324,6 @@ async def websocket_endpoint(
|
|
|
266
324
|
await websocket.close()
|
|
267
325
|
|
|
268
326
|
|
|
269
|
-
# TODO: add socketio relay endpoint for filesystem
|
|
270
|
-
|
|
271
|
-
|
|
272
327
|
def setup_logging(log_level: str):
|
|
273
328
|
"""Setup logging configuration based on log level"""
|
|
274
329
|
# Convert string to logging level
|
|
@@ -311,8 +366,21 @@ def main():
|
|
|
311
366
|
default="INFO",
|
|
312
367
|
help="Set the logging level (default: INFO)",
|
|
313
368
|
)
|
|
369
|
+
|
|
370
|
+
parser.add_argument(
|
|
371
|
+
"--extension",
|
|
372
|
+
action="append",
|
|
373
|
+
help="Path to a Python file or module name to load as an extension",
|
|
374
|
+
)
|
|
375
|
+
|
|
314
376
|
args = parser.parse_args()
|
|
315
377
|
|
|
378
|
+
if args.extension:
|
|
379
|
+
for ext in args.extension:
|
|
380
|
+
logger.info(f"Loading extension: {ext}")
|
|
381
|
+
mod = dynamic_import(ext)
|
|
382
|
+
logger.info(f"Extension loaded: {mod.__name__}")
|
|
383
|
+
|
|
316
384
|
# Setup logging based on command line argument
|
|
317
385
|
setup_logging(args.log_level)
|
|
318
386
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import os
|
|
3
|
-
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Tuple, Literal, Dict, Union, List
|
|
4
6
|
from pydantic_settings import BaseSettings
|
|
5
7
|
from pydantic import field_validator, ConfigDict
|
|
6
8
|
from dotenv import load_dotenv
|
|
@@ -17,12 +19,22 @@ class Settings(BaseSettings):
|
|
|
17
19
|
BEARER_TOKEN: Optional[str] = None
|
|
18
20
|
|
|
19
21
|
# Runtime Manager settings
|
|
20
|
-
DEFAULT_SANDBOX_TYPE: str = "base"
|
|
22
|
+
DEFAULT_SANDBOX_TYPE: Union[str, List[str]] = "base"
|
|
21
23
|
POOL_SIZE: int = 1
|
|
22
24
|
AUTO_CLEANUP: bool = True
|
|
23
25
|
CONTAINER_PREFIX_KEY: str = "runtime_sandbox_container_"
|
|
24
|
-
CONTAINER_DEPLOYMENT: Literal[
|
|
26
|
+
CONTAINER_DEPLOYMENT: Literal[
|
|
27
|
+
"docker",
|
|
28
|
+
"cloud",
|
|
29
|
+
"k8s",
|
|
30
|
+
"agentrun",
|
|
31
|
+
] = "docker"
|
|
25
32
|
DEFAULT_MOUNT_DIR: str = "sessions_mount_dir"
|
|
33
|
+
# Read-only mounts (host_path -> container_path)
|
|
34
|
+
# Example in .env:
|
|
35
|
+
# READONLY_MOUNTS={"\/opt\/shared": "\/mnt\/shared", "\/etc\/timezone":
|
|
36
|
+
# "\/etc\/timezone"}
|
|
37
|
+
READONLY_MOUNTS: Optional[Dict[str, str]] = None
|
|
26
38
|
STORAGE_FOLDER: str = "runtime_sandbox_storage"
|
|
27
39
|
PORT_RANGE: Tuple[int, int] = (49152, 59152)
|
|
28
40
|
|
|
@@ -47,6 +59,24 @@ class Settings(BaseSettings):
|
|
|
47
59
|
K8S_NAMESPACE: str = "default"
|
|
48
60
|
KUBECONFIG_PATH: Optional[str] = None
|
|
49
61
|
|
|
62
|
+
# AgentRun settings
|
|
63
|
+
AGENT_RUN_ACCOUNT_ID: Optional[str] = None
|
|
64
|
+
AGENT_RUN_ACCESS_KEY_ID: Optional[str] = None
|
|
65
|
+
AGENT_RUN_ACCESS_KEY_SECRET: Optional[str] = None
|
|
66
|
+
AGENT_RUN_REGION_ID: str = "cn-hangzhou"
|
|
67
|
+
|
|
68
|
+
AGENT_RUN_CPU: float = 2.0
|
|
69
|
+
AGENT_RUN_MEMORY: int = 2048
|
|
70
|
+
|
|
71
|
+
AGENT_RUN_VPC_ID: Optional[str] = None
|
|
72
|
+
AGENT_RUN_VSWITCH_IDS: Optional[list[str]] = None
|
|
73
|
+
AGENT_RUN_SECURITY_GROUP_ID: Optional[str] = None
|
|
74
|
+
|
|
75
|
+
AGENT_RUN_PREFIX: str = "agentscope-sandbox"
|
|
76
|
+
|
|
77
|
+
AGENT_RUN_LOG_PROJECT: Optional[str] = None
|
|
78
|
+
AGENT_RUN_LOG_STORE: Optional[str] = None
|
|
79
|
+
|
|
50
80
|
model_config = ConfigDict(
|
|
51
81
|
case_sensitive=True,
|
|
52
82
|
extra="allow",
|
|
@@ -59,6 +89,23 @@ class Settings(BaseSettings):
|
|
|
59
89
|
return 1
|
|
60
90
|
return value
|
|
61
91
|
|
|
92
|
+
@field_validator("DEFAULT_SANDBOX_TYPE", mode="before")
|
|
93
|
+
@classmethod
|
|
94
|
+
def parse_default_type(cls, v):
|
|
95
|
+
if isinstance(v, str):
|
|
96
|
+
v = v.strip()
|
|
97
|
+
if v.startswith("[") and v.endswith("]"):
|
|
98
|
+
try:
|
|
99
|
+
return json.loads(v)
|
|
100
|
+
except json.JSONDecodeError:
|
|
101
|
+
return [
|
|
102
|
+
item.strip()
|
|
103
|
+
for item in v[1:-1].split(",")
|
|
104
|
+
if item.strip()
|
|
105
|
+
]
|
|
106
|
+
return [item.strip() for item in v.split(",") if item.strip()]
|
|
107
|
+
return v
|
|
108
|
+
|
|
62
109
|
|
|
63
110
|
_settings: Optional[Settings] = None
|
|
64
111
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import List
|
|
2
|
+
from typing import List, Optional, Dict
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
@@ -20,31 +20,12 @@ class ContainerModel(BaseModel):
|
|
|
20
20
|
description="Human-readable name for the container",
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
url: str = Field(
|
|
24
24
|
...,
|
|
25
|
-
description="
|
|
25
|
+
description="URL for accessing the container",
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
...,
|
|
30
|
-
description="URL for browser interface within the container",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
front_browser_ws: str = Field(
|
|
34
|
-
...,
|
|
35
|
-
description="WebSocket URL for the browser used by frontend",
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
client_browser_ws: str = Field(
|
|
39
|
-
...,
|
|
40
|
-
description="WebSocket URL for the browser used by runtime client",
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
artifacts_sio: str = Field(
|
|
44
|
-
...,
|
|
45
|
-
description="Socketio URL for the artifacts used by frontend",
|
|
46
|
-
)
|
|
47
|
-
ports: List[int] = Field(
|
|
28
|
+
ports: List[int | str] = Field(
|
|
48
29
|
...,
|
|
49
30
|
description="List of occupied port numbers",
|
|
50
31
|
)
|
|
@@ -70,5 +51,13 @@ class ContainerModel(BaseModel):
|
|
|
70
51
|
description="Image version of the container",
|
|
71
52
|
)
|
|
72
53
|
|
|
54
|
+
meta: Optional[Dict] = Field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
timeout: Optional[int] = Field(
|
|
57
|
+
None,
|
|
58
|
+
description="Timeout in seconds for container operations",
|
|
59
|
+
ge=0,
|
|
60
|
+
)
|
|
61
|
+
|
|
73
62
|
class Config:
|
|
74
63
|
extra = "allow"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# pylint: disable=no-self-argument
|
|
3
3
|
import os
|
|
4
|
-
from typing import Optional, Literal, Tuple
|
|
4
|
+
from typing import Optional, Literal, Tuple, Dict
|
|
5
5
|
from pydantic import BaseModel, Field, model_validator
|
|
6
6
|
|
|
7
7
|
|
|
@@ -27,10 +27,15 @@ class SandboxManagerEnvConfig(BaseModel):
|
|
|
27
27
|
...,
|
|
28
28
|
description="Indicates if Redis is enabled.",
|
|
29
29
|
)
|
|
30
|
-
container_deployment: Literal[
|
|
30
|
+
container_deployment: Literal[
|
|
31
|
+
"docker",
|
|
32
|
+
"cloud",
|
|
33
|
+
"k8s",
|
|
34
|
+
"agentrun",
|
|
35
|
+
] = Field(
|
|
31
36
|
...,
|
|
32
|
-
description="Container deployment backend: 'docker', 'cloud', "
|
|
33
|
-
"or '
|
|
37
|
+
description="Container deployment backend: 'docker', 'cloud', 'k8s'"
|
|
38
|
+
"or 'agentrun'.",
|
|
34
39
|
)
|
|
35
40
|
|
|
36
41
|
default_mount_dir: Optional[str] = Field(
|
|
@@ -38,6 +43,13 @@ class SandboxManagerEnvConfig(BaseModel):
|
|
|
38
43
|
description="Path for local file system storage.",
|
|
39
44
|
)
|
|
40
45
|
|
|
46
|
+
readonly_mounts: Optional[Dict[str, str]] = Field(
|
|
47
|
+
default=None,
|
|
48
|
+
description="Read-only mount mapping: host_path -> container_path. "
|
|
49
|
+
"Example: { '/data/shared': '/mnt/shared', '/etc/timezone': "
|
|
50
|
+
"'/etc/timezone' }",
|
|
51
|
+
)
|
|
52
|
+
|
|
41
53
|
port_range: Tuple[int, int] = Field(
|
|
42
54
|
(49152, 59152),
|
|
43
55
|
description="Range of ports to be used by the manager.",
|
|
@@ -112,8 +124,64 @@ class SandboxManagerEnvConfig(BaseModel):
|
|
|
112
124
|
"in-cluster config or default kubeconfig.",
|
|
113
125
|
)
|
|
114
126
|
|
|
127
|
+
# AgentRun settings
|
|
128
|
+
agent_run_access_key_id: Optional[str] = Field(
|
|
129
|
+
None,
|
|
130
|
+
description="Access key ID for AgentRun. Required if "
|
|
131
|
+
"container_deployment is 'agentrun'.",
|
|
132
|
+
)
|
|
133
|
+
agent_run_access_key_secret: Optional[str] = Field(
|
|
134
|
+
None,
|
|
135
|
+
description="Access key secret for AgentRun. "
|
|
136
|
+
"Required if container_deployment is 'agentrun'.",
|
|
137
|
+
)
|
|
138
|
+
agent_run_account_id: Optional[str] = Field(
|
|
139
|
+
None,
|
|
140
|
+
description="Account ID for AgentRun. Required if "
|
|
141
|
+
"container_deployment is 'agentrun'.",
|
|
142
|
+
)
|
|
143
|
+
agent_run_region_id: str = Field(
|
|
144
|
+
"cn-hangzhou",
|
|
145
|
+
description="Region ID for AgentRun.",
|
|
146
|
+
)
|
|
147
|
+
agent_run_cpu: float = Field(
|
|
148
|
+
2,
|
|
149
|
+
description="CPU allocation for AgentRun containers.",
|
|
150
|
+
)
|
|
151
|
+
agent_run_memory: int = Field(
|
|
152
|
+
2048,
|
|
153
|
+
description="Memory allocation for AgentRun containers in MB.",
|
|
154
|
+
)
|
|
155
|
+
agent_run_vpc_id: Optional[str] = Field(
|
|
156
|
+
None,
|
|
157
|
+
description="VPC ID for AgentRun. Required if container_deployment "
|
|
158
|
+
"is 'agentrun'.",
|
|
159
|
+
)
|
|
160
|
+
agent_run_vswitch_ids: Optional[list[str]] = Field(
|
|
161
|
+
None,
|
|
162
|
+
description="VSwitch IDs for AgentRun. Required if "
|
|
163
|
+
"container_deployment is 'agentrun'.",
|
|
164
|
+
)
|
|
165
|
+
agent_run_security_group_id: Optional[str] = Field(
|
|
166
|
+
None,
|
|
167
|
+
description="Security group ID for AgentRun. "
|
|
168
|
+
"Required if container_deployment is 'agentrun'.",
|
|
169
|
+
)
|
|
170
|
+
agent_run_prefix: str = Field(
|
|
171
|
+
"agentscope-sandbox_",
|
|
172
|
+
description="Prefix for AgentRun resources.",
|
|
173
|
+
)
|
|
174
|
+
agentrun_log_project: Optional[str] = Field(
|
|
175
|
+
None,
|
|
176
|
+
description="Log project for AgentRun.",
|
|
177
|
+
)
|
|
178
|
+
agentrun_log_store: Optional[str] = Field(
|
|
179
|
+
None,
|
|
180
|
+
description="Log store for AgentRun.",
|
|
181
|
+
)
|
|
182
|
+
|
|
115
183
|
@model_validator(mode="after")
|
|
116
|
-
def check_settings(
|
|
184
|
+
def check_settings(self):
|
|
117
185
|
if self.default_mount_dir:
|
|
118
186
|
os.makedirs(self.default_mount_dir, exist_ok=True)
|
|
119
187
|
|
|
@@ -161,4 +229,24 @@ class SandboxManagerEnvConfig(BaseModel):
|
|
|
161
229
|
f"{field_name} must be set when redis is enabled",
|
|
162
230
|
)
|
|
163
231
|
|
|
232
|
+
if self.container_deployment == "agentrun":
|
|
233
|
+
required_agentrun_fields = [
|
|
234
|
+
self.agent_run_access_key_id,
|
|
235
|
+
self.agent_run_access_key_secret,
|
|
236
|
+
self.agent_run_account_id,
|
|
237
|
+
]
|
|
238
|
+
for field_name, field_value in zip(
|
|
239
|
+
[
|
|
240
|
+
"agent_run_access_key_id",
|
|
241
|
+
"agent_run_access_key_secret",
|
|
242
|
+
"agent_run_account_id",
|
|
243
|
+
],
|
|
244
|
+
required_agentrun_fields,
|
|
245
|
+
):
|
|
246
|
+
if not field_value:
|
|
247
|
+
raise ValueError(
|
|
248
|
+
f"{field_name} must be set when "
|
|
249
|
+
f"container_deployment is 'agentrun'",
|
|
250
|
+
)
|
|
251
|
+
|
|
164
252
|
return self
|
|
@@ -42,7 +42,7 @@ class SandboxRegistry:
|
|
|
42
42
|
sandbox_type: SandboxType | str,
|
|
43
43
|
resource_limits: Dict = None,
|
|
44
44
|
security_level: str = "medium", # Not used for now
|
|
45
|
-
timeout: int =
|
|
45
|
+
timeout: int = 60,
|
|
46
46
|
description: str = "",
|
|
47
47
|
environment: Dict = None,
|
|
48
48
|
runtime_config: Optional[Dict] = None,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# flake8: noqa: E501
|
|
3
|
+
# pylint: disable=line-too-long
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
from ..sandbox_tool import SandboxTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ComputerUseTool(SandboxTool):
|
|
10
|
+
"""
|
|
11
|
+
Use a mouse and keyboard to interact with a computer, and take screenshots.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
_name: str = "computer"
|
|
15
|
+
_sandbox_type: str = "gui"
|
|
16
|
+
_tool_type: str = "computer_use"
|
|
17
|
+
_schema: Dict = {
|
|
18
|
+
"name": "computer",
|
|
19
|
+
"description": (
|
|
20
|
+
"Use a mouse and keyboard to interact with a computer, and take screenshots.\n"
|
|
21
|
+
"* This is an interface to a desktop GUI. You do not have access to a terminal or applications menu. You must click on desktop icons to start applications.\n"
|
|
22
|
+
"* Always prefer using keyboard shortcuts rather than clicking, where possible.\n"
|
|
23
|
+
"* If you see boxes with two letters in them, typing these letters will click that element. Use this instead of other shortcuts or clicking, where possible.\n"
|
|
24
|
+
"* Some applications may take time to start or process actions, so you may need to wait and take successive screenshots to see the results of your actions. E.g. if you click on Firefox and a window doesn't open, try taking another screenshot.\n"
|
|
25
|
+
"* Whenever you intend to move the cursor to click on an element like an icon, you should consult a screenshot to determine the coordinates of the element before moving the cursor.\n"
|
|
26
|
+
"* If you tried clicking on a program or link but it failed to load, even after waiting, try adjusting your cursor position so that the tip of the cursor visually falls on the element that you want to click.\n"
|
|
27
|
+
"* Make sure to click any buttons, links, icons, etc with the cursor tip in the center of the element. Don't click boxes on their edges unless asked."
|
|
28
|
+
),
|
|
29
|
+
"parameters": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {
|
|
32
|
+
"action": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"enum": [
|
|
35
|
+
"key",
|
|
36
|
+
"type",
|
|
37
|
+
"mouse_move",
|
|
38
|
+
"left_click",
|
|
39
|
+
"left_click_drag",
|
|
40
|
+
"right_click",
|
|
41
|
+
"middle_click",
|
|
42
|
+
"double_click",
|
|
43
|
+
"get_screenshot",
|
|
44
|
+
"get_cursor_position",
|
|
45
|
+
],
|
|
46
|
+
"description": (
|
|
47
|
+
"The action to perform. The available actions are:\n"
|
|
48
|
+
"* key: Press a key or key-combination on the keyboard.\n"
|
|
49
|
+
"* type: Type a string of text on the keyboard.\n"
|
|
50
|
+
"* get_cursor_position: Get the current (x, y) pixel coordinate of the cursor on the screen.\n"
|
|
51
|
+
"* mouse_move: Move the cursor to a specified (x, y) pixel coordinate on the screen.\n"
|
|
52
|
+
"* left_click: Click the left mouse button.\n"
|
|
53
|
+
"* left_click_drag: Click and drag the cursor to a specified (x, y) pixel coordinate on the screen.\n"
|
|
54
|
+
"* right_click: Click the right mouse button.\n"
|
|
55
|
+
"* middle_click: Click the middle mouse button.\n"
|
|
56
|
+
"* double_click: Double-click the left mouse button.\n"
|
|
57
|
+
"* get_screenshot: Take a screenshot of the screen."
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
"coordinate": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": {"type": "number"},
|
|
63
|
+
"minItems": 2,
|
|
64
|
+
"maxItems": 2,
|
|
65
|
+
"description": "(x, y): The x (pixels from the left edge) and y (pixels from the top edge) coordinates",
|
|
66
|
+
},
|
|
67
|
+
"text": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Text to type or key command to execute",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
"required": ["action"],
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
computer_use = ComputerUseTool()
|
|
@@ -119,6 +119,7 @@ class MCPConfigConverter:
|
|
|
119
119
|
self,
|
|
120
120
|
*,
|
|
121
121
|
sandbox: Optional[Sandbox] = None,
|
|
122
|
+
sandbox_type: Optional[SandboxType | str] = None,
|
|
122
123
|
) -> List[MCPTool]:
|
|
123
124
|
"""
|
|
124
125
|
Converts to a list of MCPTool instances.
|
|
@@ -130,7 +131,10 @@ class MCPConfigConverter:
|
|
|
130
131
|
if box is None:
|
|
131
132
|
from ..registry import SandboxRegistry
|
|
132
133
|
|
|
133
|
-
|
|
134
|
+
if sandbox_type is None:
|
|
135
|
+
sandbox_type = SandboxType.BASE
|
|
136
|
+
|
|
137
|
+
cls_ = SandboxRegistry.get_classes_by_type(sandbox_type)
|
|
134
138
|
# Use proper context manager
|
|
135
139
|
with cls_() as tmp_box:
|
|
136
140
|
return self._process_tools(tmp_box)
|
|
@@ -156,7 +160,7 @@ class MCPConfigConverter:
|
|
|
156
160
|
MCPTool(
|
|
157
161
|
sandbox=box,
|
|
158
162
|
name=tool_name,
|
|
159
|
-
sandbox_type=
|
|
163
|
+
sandbox_type=box.sandbox_type,
|
|
160
164
|
tool_type=server_name,
|
|
161
165
|
schema=tool_info["json_schema"]["function"],
|
|
162
166
|
server_configs={
|