agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.1.6__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/engine/agents/agentscope_agent.py +447 -0
- agentscope_runtime/engine/agents/agno_agent.py +19 -18
- agentscope_runtime/engine/agents/autogen_agent.py +13 -8
- agentscope_runtime/engine/agents/utils.py +53 -0
- agentscope_runtime/engine/deployers/__init__.py +0 -13
- agentscope_runtime/engine/deployers/local_deployer.py +501 -356
- agentscope_runtime/engine/helpers/helper.py +60 -41
- agentscope_runtime/engine/runner.py +11 -36
- agentscope_runtime/engine/schemas/agent_schemas.py +2 -70
- agentscope_runtime/engine/services/sandbox_service.py +62 -70
- agentscope_runtime/engine/services/tablestore_memory_service.py +304 -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/tablestore_service_utils.py +352 -0
- agentscope_runtime/sandbox/__init__.py +2 -0
- agentscope_runtime/sandbox/box/base/__init__.py +4 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -3
- agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +8 -13
- agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +8 -6
- agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +80 -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 +10 -15
- agentscope_runtime/sandbox/build.py +143 -58
- agentscope_runtime/sandbox/client/http_client.py +43 -49
- agentscope_runtime/sandbox/client/training_client.py +0 -1
- agentscope_runtime/sandbox/constant.py +24 -1
- agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
- agentscope_runtime/sandbox/custom/example.py +2 -2
- agentscope_runtime/sandbox/enums.py +1 -0
- agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +11 -6
- agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
- agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
- agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1098 -0
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +33 -205
- agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +8 -555
- agentscope_runtime/sandbox/manager/sandbox_manager.py +187 -88
- agentscope_runtime/sandbox/manager/server/app.py +82 -14
- agentscope_runtime/sandbox/manager/server/config.py +50 -3
- agentscope_runtime/sandbox/model/container.py +6 -23
- agentscope_runtime/sandbox/model/manager_config.py +93 -5
- 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/utils.py +124 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/METADATA +168 -77
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/RECORD +59 -78
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/entry_points.txt +0 -1
- 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/deployers/adapter/responses/response_api_adapter_utils.py +0 -2886
- agentscope_runtime/engine/deployers/adapter/responses/response_api_agent_adapter.py +0 -51
- agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +0 -314
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +0 -184
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +0 -265
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +0 -677
- agentscope_runtime/engine/deployers/utils/deployment_modes.py +0 -14
- agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +0 -8
- agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +0 -429
- agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +0 -240
- agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +0 -297
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -932
- agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -9
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +0 -504
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +0 -157
- agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +0 -268
- agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
- agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +0 -389
- agentscope_runtime/engine/helpers/agent_api_builder.py +0 -651
- 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/engine/schemas/embedding.py +0 -37
- agentscope_runtime/engine/schemas/modelstudio_llm.py +0 -310
- agentscope_runtime/engine/schemas/oai_llm.py +0 -538
- agentscope_runtime/engine/schemas/realtime.py +0 -254
- /agentscope_runtime/engine/{deployers/adapter/responses → services/utils}/__init__.py +0 -0
- /agentscope_runtime/{engine/deployers/utils → sandbox/box/gui/box}/__init__.py +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/top_level.txt +0 -0
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
from urllib.parse import urlparse, urlunparse
|
|
5
5
|
|
|
6
|
-
from ...
|
|
6
|
+
from ...utils import build_image_uri
|
|
7
7
|
from ...registry import SandboxRegistry
|
|
8
8
|
from ...enums import SandboxType
|
|
9
|
-
from ...box.
|
|
9
|
+
from ...box.base import BaseSandbox
|
|
10
|
+
from ...box.gui import GUIMixin
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def http_to_ws(url, use_localhost=True):
|
|
@@ -27,35 +28,29 @@ def http_to_ws(url, use_localhost=True):
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
@SandboxRegistry.register(
|
|
30
|
-
|
|
31
|
+
build_image_uri("runtime-sandbox-browser"),
|
|
31
32
|
sandbox_type=SandboxType.BROWSER,
|
|
32
33
|
security_level="medium",
|
|
33
34
|
timeout=60,
|
|
34
35
|
description="Browser sandbox",
|
|
35
36
|
)
|
|
36
|
-
class BrowserSandbox(
|
|
37
|
-
def __init__(
|
|
37
|
+
class BrowserSandbox(GUIMixin, BaseSandbox):
|
|
38
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
38
39
|
self,
|
|
39
40
|
sandbox_id: Optional[str] = None,
|
|
40
41
|
timeout: int = 3000,
|
|
41
42
|
base_url: Optional[str] = None,
|
|
42
43
|
bearer_token: Optional[str] = None,
|
|
44
|
+
sandbox_type: SandboxType = SandboxType.BROWSER,
|
|
43
45
|
):
|
|
44
46
|
super().__init__(
|
|
45
47
|
sandbox_id,
|
|
46
48
|
timeout,
|
|
47
49
|
base_url,
|
|
48
50
|
bearer_token,
|
|
49
|
-
|
|
51
|
+
sandbox_type,
|
|
50
52
|
)
|
|
51
53
|
|
|
52
|
-
@property
|
|
53
|
-
def browser_ws(self):
|
|
54
|
-
if self.base_url is None:
|
|
55
|
-
# Local mode
|
|
56
|
-
return self.get_info()["front_browser_ws"]
|
|
57
|
-
return http_to_ws(f"{self.base_url}/browser/{self.sandbox_id}/cast")
|
|
58
|
-
|
|
59
54
|
def browser_close(self):
|
|
60
55
|
return self.call_tool("browser_close", {})
|
|
61
56
|
|
|
@@ -2,33 +2,35 @@
|
|
|
2
2
|
# pylint: disable=dangerous-default-value
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from ...
|
|
5
|
+
from ...utils import build_image_uri
|
|
6
6
|
from ...registry import SandboxRegistry
|
|
7
7
|
from ...enums import SandboxType
|
|
8
|
-
from ...box.
|
|
8
|
+
from ...box.base import BaseSandbox
|
|
9
|
+
from ...box.gui import GUIMixin
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@SandboxRegistry.register(
|
|
12
|
-
|
|
13
|
+
build_image_uri("runtime-sandbox-filesystem"),
|
|
13
14
|
sandbox_type=SandboxType.FILESYSTEM,
|
|
14
15
|
security_level="medium",
|
|
15
16
|
timeout=60,
|
|
16
17
|
description="Filesystem sandbox",
|
|
17
18
|
)
|
|
18
|
-
class FilesystemSandbox(
|
|
19
|
-
def __init__(
|
|
19
|
+
class FilesystemSandbox(GUIMixin, BaseSandbox):
|
|
20
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
20
21
|
self,
|
|
21
22
|
sandbox_id: Optional[str] = None,
|
|
22
23
|
timeout: int = 3000,
|
|
23
24
|
base_url: Optional[str] = None,
|
|
24
25
|
bearer_token: Optional[str] = None,
|
|
26
|
+
sandbox_type: SandboxType = SandboxType.FILESYSTEM,
|
|
25
27
|
):
|
|
26
28
|
super().__init__(
|
|
27
29
|
sandbox_id,
|
|
28
30
|
timeout,
|
|
29
31
|
base_url,
|
|
30
32
|
bearer_token,
|
|
31
|
-
|
|
33
|
+
sandbox_type,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
def read_file(self, path: str):
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, Union, Tuple, List
|
|
4
|
+
|
|
5
|
+
from urllib.parse import urljoin, urlencode
|
|
6
|
+
|
|
7
|
+
from ...utils import build_image_uri, get_platform
|
|
8
|
+
from ...registry import SandboxRegistry
|
|
9
|
+
from ...enums import SandboxType
|
|
10
|
+
from ...box.base import BaseSandbox
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GUIMixin:
|
|
16
|
+
@property
|
|
17
|
+
def desktop_url(self):
|
|
18
|
+
if not self.manager_api.check_health(identity=self.sandbox_id):
|
|
19
|
+
raise RuntimeError(f"Sandbox {self.sandbox_id} is not healthy")
|
|
20
|
+
|
|
21
|
+
info = self.get_info()
|
|
22
|
+
path = "/vnc/vnc_lite.html"
|
|
23
|
+
remote_path = "/vnc/vnc_relay.html"
|
|
24
|
+
params = {"password": info["runtime_token"]}
|
|
25
|
+
|
|
26
|
+
if self.base_url is None:
|
|
27
|
+
return urljoin(info["url"], path) + "?" + urlencode(params)
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
f"{self.base_url}/desktop/{self.sandbox_id}{remote_path}"
|
|
31
|
+
f"?{urlencode(params)}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@SandboxRegistry.register(
|
|
36
|
+
build_image_uri("runtime-sandbox-gui"),
|
|
37
|
+
sandbox_type=SandboxType.GUI,
|
|
38
|
+
security_level="high",
|
|
39
|
+
timeout=30,
|
|
40
|
+
description="GUI Sandbox",
|
|
41
|
+
)
|
|
42
|
+
class GuiSandbox(GUIMixin, BaseSandbox):
|
|
43
|
+
def __init__( # pylint: disable=useless-parent-delegation
|
|
44
|
+
self,
|
|
45
|
+
sandbox_id: Optional[str] = None,
|
|
46
|
+
timeout: int = 3000,
|
|
47
|
+
base_url: Optional[str] = None,
|
|
48
|
+
bearer_token: Optional[str] = None,
|
|
49
|
+
sandbox_type: SandboxType = SandboxType.GUI,
|
|
50
|
+
):
|
|
51
|
+
super().__init__(
|
|
52
|
+
sandbox_id,
|
|
53
|
+
timeout,
|
|
54
|
+
base_url,
|
|
55
|
+
bearer_token,
|
|
56
|
+
sandbox_type,
|
|
57
|
+
)
|
|
58
|
+
if get_platform() == "linux/arm64":
|
|
59
|
+
logger.warning(
|
|
60
|
+
"\nCompatibility Notice: This GUI Sandbox may have issues on "
|
|
61
|
+
"arm64 CPU architectures, due to the computer-use-mcp does "
|
|
62
|
+
"not provide linux/arm64 compatibility. It has been tested "
|
|
63
|
+
"to work on Apple M4 chips with Rosetta enabled. However, "
|
|
64
|
+
"on M1, M2, and M3 chips, chromium browser might crash due "
|
|
65
|
+
"to the missing SSE3 instruction set.",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def computer_use(
|
|
69
|
+
self,
|
|
70
|
+
action: str,
|
|
71
|
+
coordinate: Optional[Union[List[float], Tuple[float, float]]] = None,
|
|
72
|
+
text: Optional[str] = None,
|
|
73
|
+
):
|
|
74
|
+
payload = {"action": action}
|
|
75
|
+
if coordinate is not None:
|
|
76
|
+
payload["coordinate"] = coordinate
|
|
77
|
+
if text is not None:
|
|
78
|
+
payload["text"] = text
|
|
79
|
+
|
|
80
|
+
return self.call_tool("computer", payload)
|
|
@@ -49,7 +49,7 @@ class Sandbox:
|
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
sandbox_id = self.manager_api.create_from_pool(
|
|
52
|
-
sandbox_type=sandbox_type.value,
|
|
52
|
+
sandbox_type=SandboxType(sandbox_type).value,
|
|
53
53
|
)
|
|
54
54
|
if sandbox_id is None:
|
|
55
55
|
raise RuntimeError(
|
|
@@ -115,8 +115,11 @@ class Sandbox:
|
|
|
115
115
|
# Clean the specific sandbox
|
|
116
116
|
self.manager_api.release(self.sandbox_id)
|
|
117
117
|
except Exception as e:
|
|
118
|
+
import traceback
|
|
119
|
+
|
|
118
120
|
logger.error(
|
|
119
|
-
f"Cleanup {self.sandbox_id} error: {e}"
|
|
121
|
+
f"Cleanup {self.sandbox_id} error: {e}\n"
|
|
122
|
+
f"{traceback.format_exc()}",
|
|
120
123
|
)
|
|
121
124
|
|
|
122
125
|
def __enter__(self):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import io
|
|
3
|
+
import sys
|
|
3
4
|
import logging
|
|
4
5
|
import subprocess
|
|
5
6
|
import traceback
|
|
@@ -44,7 +45,25 @@ async def run_ipython_cell(
|
|
|
44
45
|
stderr_buf = io.StringIO()
|
|
45
46
|
|
|
46
47
|
with redirect_stdout(stdout_buf), redirect_stderr(stderr_buf):
|
|
47
|
-
|
|
48
|
+
preprocessing_exc_tuple = None
|
|
49
|
+
try:
|
|
50
|
+
transformed_cell = ipy.transform_cell(code)
|
|
51
|
+
except Exception:
|
|
52
|
+
transformed_cell = code
|
|
53
|
+
preprocessing_exc_tuple = sys.exc_info()
|
|
54
|
+
|
|
55
|
+
if transformed_cell is None:
|
|
56
|
+
raise HTTPException(
|
|
57
|
+
status_code=500,
|
|
58
|
+
detail="IPython cell transformation failed: "
|
|
59
|
+
"transformed_cell is None.",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
await ipy.run_cell_async(
|
|
63
|
+
code,
|
|
64
|
+
transformed_cell=transformed_cell,
|
|
65
|
+
preprocessing_exc_tuple=preprocessing_exc_tuple,
|
|
66
|
+
)
|
|
48
67
|
|
|
49
68
|
stdout_content = stdout_buf.getvalue()
|
|
50
69
|
stderr_content = stderr_buf.getvalue()
|
|
@@ -5,21 +5,13 @@ Module for the Training Sandbox implementation.
|
|
|
5
5
|
This module provides a sandbox environment for training tasks
|
|
6
6
|
with specific configuration and tool calling methods.
|
|
7
7
|
"""
|
|
8
|
-
import platform
|
|
9
8
|
from typing import Dict, Optional
|
|
10
9
|
import os
|
|
11
10
|
|
|
11
|
+
from ...utils import build_image_uri
|
|
12
12
|
from ...registry import SandboxRegistry
|
|
13
13
|
from ...enums import SandboxType
|
|
14
14
|
from ...box.sandbox import Sandbox
|
|
15
|
-
from ...constant import IMAGE_TAG
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def get_image_tag() -> str:
|
|
19
|
-
machine = platform.machine().lower()
|
|
20
|
-
if machine in ("arm64", "aarch64", "armv7l", "armv8"):
|
|
21
|
-
return f"{IMAGE_TAG}-arm64"
|
|
22
|
-
return IMAGE_TAG
|
|
23
15
|
|
|
24
16
|
|
|
25
17
|
class TrainingSandbox(Sandbox):
|
|
@@ -214,7 +206,7 @@ class TrainingSandbox(Sandbox):
|
|
|
214
206
|
|
|
215
207
|
|
|
216
208
|
@SandboxRegistry.register(
|
|
217
|
-
|
|
209
|
+
build_image_uri("runtime-sandbox-appworld", arm64_compatible=False),
|
|
218
210
|
sandbox_type=SandboxType.APPWORLD,
|
|
219
211
|
runtime_config={"shm_size": "5.06gb"},
|
|
220
212
|
security_level="medium",
|
|
@@ -235,6 +227,7 @@ class APPWorldSandbox(TrainingSandbox):
|
|
|
235
227
|
timeout: int = 3000,
|
|
236
228
|
base_url: Optional[str] = None,
|
|
237
229
|
bearer_token: Optional[str] = None,
|
|
230
|
+
sandbox_type: SandboxType = SandboxType.APPWORLD,
|
|
238
231
|
):
|
|
239
232
|
"""
|
|
240
233
|
Initialize the Training Sandbox.
|
|
@@ -250,7 +243,7 @@ class APPWorldSandbox(TrainingSandbox):
|
|
|
250
243
|
timeout,
|
|
251
244
|
base_url,
|
|
252
245
|
bearer_token,
|
|
253
|
-
|
|
246
|
+
sandbox_type,
|
|
254
247
|
)
|
|
255
248
|
|
|
256
249
|
|
|
@@ -258,7 +251,7 @@ DATASET_SUB_TYPE = os.environ.get("DATASET_SUB_TYPE", "multi_turn")
|
|
|
258
251
|
|
|
259
252
|
|
|
260
253
|
@SandboxRegistry.register(
|
|
261
|
-
|
|
254
|
+
build_image_uri("runtime-sandbox-bfcl", arm64_compatible=False),
|
|
262
255
|
sandbox_type=SandboxType.BFCL,
|
|
263
256
|
runtime_config={"shm_size": "8.06gb"},
|
|
264
257
|
security_level="medium",
|
|
@@ -289,6 +282,7 @@ class BFCLSandbox(TrainingSandbox):
|
|
|
289
282
|
timeout: int = 3000,
|
|
290
283
|
base_url: Optional[str] = None,
|
|
291
284
|
bearer_token: Optional[str] = None,
|
|
285
|
+
sandbox_type: SandboxType = SandboxType.BFCL,
|
|
292
286
|
):
|
|
293
287
|
"""
|
|
294
288
|
Initialize the Training Sandbox.
|
|
@@ -304,12 +298,12 @@ class BFCLSandbox(TrainingSandbox):
|
|
|
304
298
|
timeout,
|
|
305
299
|
base_url,
|
|
306
300
|
bearer_token,
|
|
307
|
-
|
|
301
|
+
sandbox_type,
|
|
308
302
|
)
|
|
309
303
|
|
|
310
304
|
|
|
311
305
|
@SandboxRegistry.register(
|
|
312
|
-
|
|
306
|
+
build_image_uri("runtime-sandbox-webshop", arm64_compatible=False),
|
|
313
307
|
sandbox_type=SandboxType.WEBSHOP,
|
|
314
308
|
runtime_config={"shm_size": "5.06gb"},
|
|
315
309
|
security_level="medium",
|
|
@@ -330,6 +324,7 @@ class WebShopSandbox(TrainingSandbox):
|
|
|
330
324
|
timeout: int = 3000,
|
|
331
325
|
base_url: Optional[str] = None,
|
|
332
326
|
bearer_token: Optional[str] = None,
|
|
327
|
+
sandbox_type: SandboxType = SandboxType.WEBSHOP,
|
|
333
328
|
):
|
|
334
329
|
"""
|
|
335
330
|
Initialize the Training Sandbox.
|
|
@@ -345,5 +340,5 @@ class WebShopSandbox(TrainingSandbox):
|
|
|
345
340
|
timeout,
|
|
346
341
|
base_url,
|
|
347
342
|
bearer_token,
|
|
348
|
-
|
|
343
|
+
sandbox_type,
|
|
349
344
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
# pylint: disable=too-many-statements
|
|
2
|
+
# pylint: disable=too-many-statements,too-many-branches
|
|
3
3
|
import argparse
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
@@ -11,6 +11,15 @@ import requests
|
|
|
11
11
|
|
|
12
12
|
from .enums import SandboxType
|
|
13
13
|
from .registry import SandboxRegistry
|
|
14
|
+
from .utils import dynamic_import, get_platform
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
DOCKER_PLATFORMS = [
|
|
20
|
+
"linux/amd64",
|
|
21
|
+
"linux/arm64",
|
|
22
|
+
]
|
|
14
23
|
|
|
15
24
|
|
|
16
25
|
def find_free_port(start_port, end_port):
|
|
@@ -18,7 +27,7 @@ def find_free_port(start_port, end_port):
|
|
|
18
27
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
19
28
|
if sock.connect_ex(("localhost", port)) != 0:
|
|
20
29
|
return port
|
|
21
|
-
|
|
30
|
+
logger.error(
|
|
22
31
|
f"No free ports available in the range {start_port}-{end_port}",
|
|
23
32
|
)
|
|
24
33
|
raise RuntimeError(
|
|
@@ -30,7 +39,7 @@ def check_health(url, secret_token, timeout=120, interval=5):
|
|
|
30
39
|
headers = {"Authorization": f"Bearer {secret_token}"}
|
|
31
40
|
spent_time = 0
|
|
32
41
|
while spent_time < timeout:
|
|
33
|
-
|
|
42
|
+
logger.info(
|
|
34
43
|
f"Attempting to connect to {url} (Elapsed time: {spent_time} "
|
|
35
44
|
f"seconds)...",
|
|
36
45
|
)
|
|
@@ -41,35 +50,52 @@ def check_health(url, secret_token, timeout=120, interval=5):
|
|
|
41
50
|
return True
|
|
42
51
|
except requests.exceptions.RequestException:
|
|
43
52
|
pass
|
|
44
|
-
|
|
53
|
+
logger.info(
|
|
45
54
|
f"Health check failed for {url}. Retrying in {interval} "
|
|
46
55
|
f"seconds...",
|
|
47
56
|
)
|
|
48
57
|
time.sleep(interval)
|
|
49
58
|
spent_time += interval
|
|
50
|
-
|
|
59
|
+
logger.error(f"Health check failed for {url} after {timeout} seconds.")
|
|
51
60
|
return False
|
|
52
61
|
|
|
53
62
|
|
|
54
|
-
def build_image(
|
|
63
|
+
def build_image(
|
|
64
|
+
build_type,
|
|
65
|
+
dockerfile_path=None,
|
|
66
|
+
platform_choice="linux/amd64",
|
|
67
|
+
):
|
|
68
|
+
assert platform_choice in DOCKER_PLATFORMS, (
|
|
69
|
+
f"Invalid platform: {platform_choice}. Valid options:"
|
|
70
|
+
f" {DOCKER_PLATFORMS}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
platform_tag = ""
|
|
74
|
+
if platform_choice == "linux/arm64":
|
|
75
|
+
platform_tag = "-arm64"
|
|
76
|
+
|
|
77
|
+
buildx_enable = platform_choice != get_platform()
|
|
78
|
+
|
|
55
79
|
if dockerfile_path is None:
|
|
56
80
|
dockerfile_path = (
|
|
57
81
|
f"src/agentscope_runtime/sandbox/box/{build_type}/Dockerfile"
|
|
58
82
|
)
|
|
59
83
|
|
|
60
|
-
|
|
84
|
+
logger.info(f"Building {build_type} with `{dockerfile_path}`...")
|
|
61
85
|
|
|
62
86
|
# Initialize and update Git submodule
|
|
63
|
-
|
|
87
|
+
logger.info("Initializing and updating Git submodule...")
|
|
64
88
|
subprocess.run(
|
|
65
89
|
["git", "submodule", "update", "--init", "--recursive"],
|
|
66
90
|
check=True,
|
|
67
91
|
)
|
|
68
92
|
|
|
69
93
|
secret_token = "secret_token123"
|
|
70
|
-
image_name = SandboxRegistry.get_image_by_type(build_type)
|
|
71
94
|
|
|
72
|
-
|
|
95
|
+
# Add platform tag
|
|
96
|
+
image_name = SandboxRegistry.get_image_by_type(build_type) + platform_tag
|
|
97
|
+
|
|
98
|
+
logger.info(f"Building Docker image {image_name}...")
|
|
73
99
|
|
|
74
100
|
# Check if image exists
|
|
75
101
|
result = subprocess.run(
|
|
@@ -87,7 +113,7 @@ def build_image(build_type, dockerfile_path=None):
|
|
|
87
113
|
f"you want to overwrite it? (y/N): ",
|
|
88
114
|
)
|
|
89
115
|
if choice.lower() != "y":
|
|
90
|
-
|
|
116
|
+
logger.info("Exiting without overwriting the existing image.")
|
|
91
117
|
return
|
|
92
118
|
|
|
93
119
|
if not os.path.exists(dockerfile_path):
|
|
@@ -97,70 +123,101 @@ def build_image(build_type, dockerfile_path=None):
|
|
|
97
123
|
)
|
|
98
124
|
|
|
99
125
|
# Build Docker image
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
if not buildx_enable:
|
|
127
|
+
subprocess.run(
|
|
128
|
+
[
|
|
129
|
+
"docker",
|
|
130
|
+
"build",
|
|
131
|
+
"-f",
|
|
132
|
+
dockerfile_path,
|
|
133
|
+
"-t",
|
|
134
|
+
f"{image_name}dev",
|
|
135
|
+
".",
|
|
136
|
+
],
|
|
137
|
+
check=False,
|
|
138
|
+
)
|
|
139
|
+
else:
|
|
140
|
+
subprocess.run(
|
|
141
|
+
[
|
|
142
|
+
"docker",
|
|
143
|
+
"buildx",
|
|
144
|
+
"build",
|
|
145
|
+
"--platform",
|
|
146
|
+
platform_choice,
|
|
147
|
+
"-f",
|
|
148
|
+
dockerfile_path,
|
|
149
|
+
"-t",
|
|
150
|
+
f"{image_name}dev",
|
|
151
|
+
"--load",
|
|
152
|
+
".",
|
|
153
|
+
],
|
|
154
|
+
check=False,
|
|
155
|
+
)
|
|
113
156
|
|
|
114
|
-
|
|
157
|
+
logger.info(f"Docker image {image_name}dev built successfully.")
|
|
158
|
+
|
|
159
|
+
logger.info(f"Start to build image {image_name}.")
|
|
115
160
|
|
|
116
161
|
# Run the container with port mapping and environment variable
|
|
117
162
|
free_port = find_free_port(8080, 8090)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
163
|
+
|
|
164
|
+
if not buildx_enable:
|
|
165
|
+
result = subprocess.run(
|
|
166
|
+
[
|
|
167
|
+
"docker",
|
|
168
|
+
"run",
|
|
169
|
+
"-d",
|
|
170
|
+
"-p",
|
|
171
|
+
f"{free_port}:80",
|
|
172
|
+
"-e",
|
|
173
|
+
f"SECRET_TOKEN={secret_token}",
|
|
174
|
+
f"{image_name}dev",
|
|
175
|
+
],
|
|
176
|
+
capture_output=True,
|
|
177
|
+
text=True,
|
|
178
|
+
check=False,
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
result = subprocess.run(
|
|
182
|
+
[
|
|
183
|
+
"docker",
|
|
184
|
+
"run",
|
|
185
|
+
"--platform",
|
|
186
|
+
platform_choice,
|
|
187
|
+
"-d",
|
|
188
|
+
"-p",
|
|
189
|
+
f"{free_port}:80",
|
|
190
|
+
"-e",
|
|
191
|
+
f"SECRET_TOKEN={secret_token}",
|
|
192
|
+
f"{image_name}dev",
|
|
193
|
+
],
|
|
194
|
+
capture_output=True,
|
|
195
|
+
text=True,
|
|
196
|
+
check=False,
|
|
197
|
+
)
|
|
198
|
+
|
|
133
199
|
container_id = result.stdout.strip()
|
|
134
|
-
|
|
200
|
+
logger.info(f"Running container {container_id} on port {free_port}")
|
|
135
201
|
|
|
136
202
|
# Check health endpoints
|
|
137
203
|
fastapi_health_url = f"http://localhost:{free_port}/fastapi/healthz"
|
|
138
|
-
steelapi_health_url = (
|
|
139
|
-
f"http://localhost:{free_port}/steel-api/{secret_token}/v1/health"
|
|
140
|
-
)
|
|
141
204
|
|
|
142
205
|
# Check health for FASTAPI
|
|
143
206
|
fastapi_healthy = check_health(fastapi_health_url, secret_token)
|
|
144
207
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
browser_healthy = check_health(steelapi_health_url, secret_token)
|
|
148
|
-
else:
|
|
149
|
-
browser_healthy = True
|
|
150
|
-
|
|
151
|
-
if browser_healthy and fastapi_healthy:
|
|
152
|
-
logging.info("Health checks passed.")
|
|
208
|
+
if fastapi_healthy:
|
|
209
|
+
logger.info("Health checks passed.")
|
|
153
210
|
subprocess.run(
|
|
154
211
|
["docker", "commit", container_id, f"{image_name}"],
|
|
155
212
|
check=True,
|
|
156
213
|
)
|
|
157
|
-
|
|
214
|
+
logger.info(
|
|
158
215
|
f"Docker image {image_name} committed successfully.",
|
|
159
216
|
)
|
|
160
217
|
subprocess.run(["docker", "stop", container_id], check=True)
|
|
161
218
|
subprocess.run(["docker", "rm", container_id], check=True)
|
|
162
219
|
else:
|
|
163
|
-
|
|
220
|
+
logger.error("Health checks failed.")
|
|
164
221
|
subprocess.run(["docker", "stop", container_id], check=True)
|
|
165
222
|
|
|
166
223
|
choice = input(
|
|
@@ -171,9 +228,9 @@ def build_image(build_type, dockerfile_path=None):
|
|
|
171
228
|
["docker", "rmi", "-f", f"{image_name}dev"],
|
|
172
229
|
check=True,
|
|
173
230
|
)
|
|
174
|
-
|
|
231
|
+
logger.info(f"Dev image {image_name}dev deleted.")
|
|
175
232
|
else:
|
|
176
|
-
|
|
233
|
+
logger.info(f"Dev image {image_name}dev retained.")
|
|
177
234
|
|
|
178
235
|
|
|
179
236
|
def main():
|
|
@@ -182,8 +239,8 @@ def main():
|
|
|
182
239
|
)
|
|
183
240
|
parser.add_argument(
|
|
184
241
|
"build_type",
|
|
242
|
+
nargs="?",
|
|
185
243
|
default="base",
|
|
186
|
-
choices=[x.value for x in SandboxType] + ["all"],
|
|
187
244
|
help="Specify the build type to execute.",
|
|
188
245
|
)
|
|
189
246
|
|
|
@@ -193,20 +250,48 @@ def main():
|
|
|
193
250
|
help="Specify the path for the Dockerfile.",
|
|
194
251
|
)
|
|
195
252
|
|
|
253
|
+
parser.add_argument(
|
|
254
|
+
"--extension",
|
|
255
|
+
action="append",
|
|
256
|
+
help="Path to a Python file or module name to load as an extension",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
parser.add_argument(
|
|
260
|
+
"--platform",
|
|
261
|
+
default=get_platform(),
|
|
262
|
+
choices=DOCKER_PLATFORMS,
|
|
263
|
+
help="Specify target platform for Docker image (default: current "
|
|
264
|
+
f"system platform: {get_platform()})",
|
|
265
|
+
)
|
|
266
|
+
|
|
196
267
|
args = parser.parse_args()
|
|
197
268
|
|
|
269
|
+
if args.extension:
|
|
270
|
+
for ext in args.extension:
|
|
271
|
+
logger.info(f"Loading extension: {ext}")
|
|
272
|
+
mod = dynamic_import(ext)
|
|
273
|
+
logger.info(f"Extension loaded: {mod.__name__}")
|
|
274
|
+
|
|
198
275
|
if args.build_type == "all":
|
|
199
276
|
# Only build the built-in images
|
|
200
277
|
for build_type in [x.value for x in SandboxType.get_builtin_members()]:
|
|
201
278
|
build_image(build_type)
|
|
202
279
|
else:
|
|
280
|
+
assert args.build_type in [
|
|
281
|
+
x.value for x in SandboxType
|
|
282
|
+
], f"Invalid build type: {args.build_type}"
|
|
283
|
+
|
|
203
284
|
if args.build_type not in [
|
|
204
285
|
x.value for x in SandboxType.get_builtin_members()
|
|
205
286
|
]:
|
|
206
287
|
assert (
|
|
207
288
|
args.dockerfile_path is not None
|
|
208
289
|
), "Dockerfile path is required for custom images"
|
|
209
|
-
build_image(
|
|
290
|
+
build_image(
|
|
291
|
+
args.build_type,
|
|
292
|
+
args.dockerfile_path,
|
|
293
|
+
platform_choice=args.platform,
|
|
294
|
+
)
|
|
210
295
|
|
|
211
296
|
|
|
212
297
|
if __name__ == "__main__":
|