agentscope-runtime 0.1.5b1__py3-none-any.whl → 0.2.0b1__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/engine/__init__.py +12 -0
- agentscope_runtime/engine/agents/agentscope_agent.py +488 -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/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/base.py +1 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +72 -12
- 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 +77 -27
- agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +3 -3
- 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 +35 -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 +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/__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 +10 -15
- 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 -0
- 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.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/METADATA +209 -101
- {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/RECORD +95 -79
- 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/{sandbox/manager → common}/container_clients/kubernetes_client.py +0 -0
- {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.2.0b1.dist-info}/top_level.txt +0 -0
|
@@ -3,19 +3,20 @@ import os
|
|
|
3
3
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
-
from ..
|
|
6
|
+
from ..utils import build_image_uri
|
|
7
7
|
from ..registry import SandboxRegistry
|
|
8
8
|
from ..enums import SandboxType
|
|
9
9
|
from ..box.sandbox import Sandbox
|
|
10
|
+
from ..constant import TIMEOUT
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
SANDBOX_TYPE = "custom_sandbox"
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@SandboxRegistry.register(
|
|
15
|
-
f"
|
|
16
|
-
sandbox_type=
|
|
16
|
+
build_image_uri(f"runtime-sandbox-{SANDBOX_TYPE}"),
|
|
17
|
+
sandbox_type=SANDBOX_TYPE,
|
|
17
18
|
security_level="medium",
|
|
18
|
-
timeout=
|
|
19
|
+
timeout=TIMEOUT,
|
|
19
20
|
description="my sandbox",
|
|
20
21
|
environment={
|
|
21
22
|
"TAVILY_API_KEY": os.getenv("TAVILY_API_KEY", ""),
|
|
@@ -35,5 +36,5 @@ class CustomSandbox(Sandbox):
|
|
|
35
36
|
timeout,
|
|
36
37
|
base_url,
|
|
37
38
|
bearer_token,
|
|
38
|
-
SandboxType(
|
|
39
|
+
SandboxType(SANDBOX_TYPE),
|
|
39
40
|
)
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from ..
|
|
4
|
+
from ..utils import build_image_uri
|
|
5
5
|
from ..registry import SandboxRegistry
|
|
6
6
|
from ..enums import SandboxType
|
|
7
7
|
from ..box.sandbox import Sandbox
|
|
8
|
+
from ..constant import TIMEOUT
|
|
8
9
|
|
|
9
10
|
SANDBOX_TYPE = "example"
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@SandboxRegistry.register(
|
|
13
|
-
f"
|
|
14
|
+
build_image_uri(f"runtime-sandbox-{SANDBOX_TYPE}"),
|
|
14
15
|
sandbox_type=SANDBOX_TYPE,
|
|
15
16
|
security_level="medium",
|
|
16
|
-
timeout=
|
|
17
|
+
timeout=TIMEOUT,
|
|
17
18
|
description="Example sandbox",
|
|
18
19
|
)
|
|
19
20
|
class ExampleSandbox(Sandbox):
|
|
@@ -1,39 +1,36 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
# pylint: disable=redefined-outer-name, protected-access
|
|
3
|
+
# pylint: disable=too-many-branches, too-many-statements
|
|
2
4
|
# pylint: disable=redefined-outer-name, protected-access, too-many-branches
|
|
5
|
+
import inspect
|
|
6
|
+
import json
|
|
3
7
|
import logging
|
|
4
8
|
import os
|
|
5
|
-
import json
|
|
6
9
|
import secrets
|
|
7
|
-
import inspect
|
|
8
10
|
import traceback
|
|
9
|
-
|
|
10
11
|
from functools import wraps
|
|
11
|
-
from typing import Optional, Dict
|
|
12
|
-
from urllib.parse import urlparse, urlunparse
|
|
12
|
+
from typing import Optional, Dict, Union, List
|
|
13
13
|
|
|
14
|
-
import shortuuid
|
|
15
14
|
import requests
|
|
15
|
+
import shortuuid
|
|
16
16
|
|
|
17
|
+
from ..client import SandboxHttpClient, TrainingSandboxClient
|
|
18
|
+
from ..enums import SandboxType
|
|
19
|
+
from ..manager.storage import (
|
|
20
|
+
LocalStorage,
|
|
21
|
+
OSSStorage,
|
|
22
|
+
)
|
|
17
23
|
from ..model import (
|
|
18
24
|
ContainerModel,
|
|
19
25
|
SandboxManagerEnvConfig,
|
|
20
26
|
)
|
|
21
|
-
from ..enums import SandboxType
|
|
22
27
|
from ..registry import SandboxRegistry
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
from ..manager.collections import (
|
|
28
|
+
from ...common.collections import (
|
|
26
29
|
RedisMapping,
|
|
27
30
|
RedisQueue,
|
|
28
31
|
InMemoryMapping,
|
|
29
32
|
InMemoryQueue,
|
|
30
33
|
)
|
|
31
|
-
from ..manager.storage import (
|
|
32
|
-
LocalStorage,
|
|
33
|
-
OSSStorage,
|
|
34
|
-
)
|
|
35
|
-
from ..manager.container_clients import DockerClient, KubernetesClient
|
|
36
|
-
from ..constant import BROWSER_SESSION_ID
|
|
37
34
|
|
|
38
35
|
logging.basicConfig(level=logging.INFO)
|
|
39
36
|
logger = logging.getLogger(__name__)
|
|
@@ -85,7 +82,11 @@ class SandboxManager:
|
|
|
85
82
|
config: Optional[SandboxManagerEnvConfig] = None,
|
|
86
83
|
base_url=None,
|
|
87
84
|
bearer_token=None,
|
|
88
|
-
default_type:
|
|
85
|
+
default_type: Union[
|
|
86
|
+
SandboxType,
|
|
87
|
+
str,
|
|
88
|
+
List[Union[SandboxType, str]],
|
|
89
|
+
] = SandboxType.BASE,
|
|
89
90
|
):
|
|
90
91
|
if base_url:
|
|
91
92
|
# Initialize HTTP session for remote mode with bearer token
|
|
@@ -112,17 +113,24 @@ class SandboxManager:
|
|
|
112
113
|
default_mount_dir="sessions_mount_dir",
|
|
113
114
|
)
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
# Support multi sandbox pool
|
|
117
|
+
if isinstance(default_type, (SandboxType, str)):
|
|
118
|
+
self.default_type = [SandboxType(default_type)]
|
|
119
|
+
else:
|
|
120
|
+
self.default_type = [SandboxType(x) for x in list(default_type)]
|
|
121
|
+
|
|
116
122
|
self.workdir = "/workspace"
|
|
117
123
|
|
|
118
124
|
self.config = config
|
|
119
125
|
self.pool_size = self.config.pool_size
|
|
120
126
|
self.prefix = self.config.container_prefix_key
|
|
121
127
|
self.default_mount_dir = self.config.default_mount_dir
|
|
128
|
+
self.readonly_mounts = self.config.readonly_mounts
|
|
122
129
|
self.storage_folder = (
|
|
123
130
|
self.config.storage_folder or self.default_mount_dir
|
|
124
131
|
)
|
|
125
132
|
|
|
133
|
+
self.pool_queues = {}
|
|
126
134
|
if self.config.redis_enabled:
|
|
127
135
|
import redis
|
|
128
136
|
|
|
@@ -142,21 +150,44 @@ class SandboxManager:
|
|
|
142
150
|
) from e
|
|
143
151
|
|
|
144
152
|
self.container_mapping = RedisMapping(redis_client)
|
|
145
|
-
self.
|
|
153
|
+
self.session_mapping = RedisMapping(
|
|
146
154
|
redis_client,
|
|
147
|
-
|
|
155
|
+
prefix="session_mapping",
|
|
148
156
|
)
|
|
157
|
+
|
|
158
|
+
# Init multi sand box pool
|
|
159
|
+
for t in self.default_type:
|
|
160
|
+
queue_key = f"{self.config.redis_container_pool_key}:{t.value}"
|
|
161
|
+
self.pool_queues[t] = RedisQueue(redis_client, queue_key)
|
|
149
162
|
else:
|
|
150
163
|
self.container_mapping = InMemoryMapping()
|
|
151
|
-
self.
|
|
164
|
+
self.session_mapping = InMemoryMapping()
|
|
165
|
+
|
|
166
|
+
# Init multi sand box pool
|
|
167
|
+
for t in self.default_type:
|
|
168
|
+
self.pool_queues[t] = InMemoryQueue()
|
|
152
169
|
|
|
153
170
|
self.container_deployment = self.config.container_deployment
|
|
154
171
|
|
|
155
172
|
if base_url is None:
|
|
156
173
|
if self.container_deployment == "docker":
|
|
174
|
+
from ...common.container_clients.docker_client import (
|
|
175
|
+
DockerClient,
|
|
176
|
+
)
|
|
177
|
+
|
|
157
178
|
self.client = DockerClient(config=self.config)
|
|
158
179
|
elif self.container_deployment == "k8s":
|
|
180
|
+
from ...common.container_clients.kubernetes_client import (
|
|
181
|
+
KubernetesClient,
|
|
182
|
+
)
|
|
183
|
+
|
|
159
184
|
self.client = KubernetesClient(config=self.config)
|
|
185
|
+
elif self.container_deployment == "agentrun":
|
|
186
|
+
from ...common.container_clients.agentrun_client import (
|
|
187
|
+
AgentRunClient,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
self.client = AgentRunClient(config=self.config)
|
|
160
191
|
else:
|
|
161
192
|
raise NotImplementedError("Not implemented")
|
|
162
193
|
else:
|
|
@@ -243,24 +274,28 @@ class SandboxManager:
|
|
|
243
274
|
"""
|
|
244
275
|
Init runtime pool
|
|
245
276
|
"""
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
277
|
+
for t in self.default_type:
|
|
278
|
+
queue = self.pool_queues[t]
|
|
279
|
+
while queue.size() < self.pool_size:
|
|
280
|
+
try:
|
|
281
|
+
container_name = self.create(sandbox_type=t.value)
|
|
282
|
+
container_model = self.container_mapping.get(
|
|
283
|
+
container_name,
|
|
284
|
+
)
|
|
285
|
+
if container_model:
|
|
286
|
+
# Check the pool size again to avoid race condition
|
|
287
|
+
if queue.size() < self.pool_size:
|
|
288
|
+
queue.enqueue(container_model)
|
|
289
|
+
else:
|
|
290
|
+
# The pool size has reached the limit
|
|
291
|
+
self.release(container_name)
|
|
292
|
+
break
|
|
254
293
|
else:
|
|
255
|
-
|
|
256
|
-
self.release(container_name)
|
|
294
|
+
logger.error("Failed to create container for pool")
|
|
257
295
|
break
|
|
258
|
-
|
|
259
|
-
logger.error("
|
|
296
|
+
except Exception as e:
|
|
297
|
+
logger.error(f"Error initializing runtime pool: {e}")
|
|
260
298
|
break
|
|
261
|
-
except Exception as e:
|
|
262
|
-
logger.error(f"Error initializing runtime pool: {e}")
|
|
263
|
-
break
|
|
264
299
|
|
|
265
300
|
@remote_wrapper()
|
|
266
301
|
def cleanup(self):
|
|
@@ -269,17 +304,19 @@ class SandboxManager:
|
|
|
269
304
|
)
|
|
270
305
|
|
|
271
306
|
# Clean up pool first
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
307
|
+
for queue in self.pool_queues.values():
|
|
308
|
+
try:
|
|
309
|
+
while queue.size() > 0:
|
|
310
|
+
container_json = queue.dequeue()
|
|
311
|
+
if container_json:
|
|
312
|
+
container_model = ContainerModel(**container_json)
|
|
313
|
+
logger.debug(
|
|
314
|
+
f"Destroy container"
|
|
315
|
+
f" {container_model.container_id}",
|
|
316
|
+
)
|
|
317
|
+
self.release(container_model.session_id)
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.error(f"Error cleaning up runtime pool: {e}")
|
|
283
320
|
|
|
284
321
|
# Clean up rest container
|
|
285
322
|
for key in self.container_mapping.scan(self.prefix):
|
|
@@ -297,11 +334,15 @@ class SandboxManager:
|
|
|
297
334
|
)
|
|
298
335
|
|
|
299
336
|
@remote_wrapper()
|
|
300
|
-
def create_from_pool(self, sandbox_type=None):
|
|
337
|
+
def create_from_pool(self, sandbox_type=None, meta: Optional[Dict] = None):
|
|
301
338
|
"""Try to get a container from runtime pool"""
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
339
|
+
# If not specified, use the first one
|
|
340
|
+
sandbox_type = SandboxType(sandbox_type or self.default_type[0])
|
|
341
|
+
|
|
342
|
+
if sandbox_type not in self.pool_queues:
|
|
343
|
+
return self.create(sandbox_type=sandbox_type.value, meta=meta)
|
|
344
|
+
|
|
345
|
+
queue = self.pool_queues[sandbox_type]
|
|
305
346
|
|
|
306
347
|
cnt = 0
|
|
307
348
|
try:
|
|
@@ -313,17 +354,17 @@ class SandboxManager:
|
|
|
313
354
|
cnt += 1
|
|
314
355
|
|
|
315
356
|
# Add a new one to container
|
|
316
|
-
container_name = self.create()
|
|
357
|
+
container_name = self.create(sandbox_type=sandbox_type)
|
|
317
358
|
new_container_model = self.container_mapping.get(
|
|
318
359
|
container_name,
|
|
319
360
|
)
|
|
320
361
|
|
|
321
362
|
if new_container_model:
|
|
322
|
-
|
|
363
|
+
queue.enqueue(
|
|
323
364
|
new_container_model,
|
|
324
365
|
)
|
|
325
366
|
|
|
326
|
-
container_json =
|
|
367
|
+
container_json = queue.dequeue()
|
|
327
368
|
|
|
328
369
|
if not container_json:
|
|
329
370
|
raise RuntimeError(
|
|
@@ -331,6 +372,29 @@ class SandboxManager:
|
|
|
331
372
|
)
|
|
332
373
|
|
|
333
374
|
container_model = ContainerModel(**container_json)
|
|
375
|
+
|
|
376
|
+
# Add meta field to container
|
|
377
|
+
if meta and not container_model.meta:
|
|
378
|
+
container_model.meta = meta
|
|
379
|
+
self.container_mapping.set(
|
|
380
|
+
container_model.container_name,
|
|
381
|
+
container_model.model_dump(),
|
|
382
|
+
)
|
|
383
|
+
# Update session mapping
|
|
384
|
+
if "session_ctx_id" in meta:
|
|
385
|
+
env_ids = (
|
|
386
|
+
self.session_mapping.get(
|
|
387
|
+
meta["session_ctx_id"],
|
|
388
|
+
)
|
|
389
|
+
or []
|
|
390
|
+
)
|
|
391
|
+
if container_model.container_name not in env_ids:
|
|
392
|
+
env_ids.append(container_model.container_name)
|
|
393
|
+
self.session_mapping.set(
|
|
394
|
+
meta["session_ctx_id"],
|
|
395
|
+
env_ids,
|
|
396
|
+
)
|
|
397
|
+
|
|
334
398
|
logger.debug(
|
|
335
399
|
f"Retrieved container from pool:"
|
|
336
400
|
f" {container_model.session_id}",
|
|
@@ -339,7 +403,7 @@ class SandboxManager:
|
|
|
339
403
|
if (
|
|
340
404
|
container_model.version
|
|
341
405
|
!= SandboxRegistry.get_image_by_type(
|
|
342
|
-
|
|
406
|
+
sandbox_type,
|
|
343
407
|
)
|
|
344
408
|
):
|
|
345
409
|
logger.warning(
|
|
@@ -370,37 +434,37 @@ class SandboxManager:
|
|
|
370
434
|
self.release(container_model.session_id)
|
|
371
435
|
|
|
372
436
|
except Exception as e:
|
|
373
|
-
logger.
|
|
374
|
-
|
|
375
|
-
f"new one. {e}: {traceback.format_exc()}",
|
|
437
|
+
logger.warning(
|
|
438
|
+
"Error getting container from pool, create a new one.",
|
|
376
439
|
)
|
|
440
|
+
logger.debug(f"{e}: {traceback.format_exc()}")
|
|
377
441
|
return self.create()
|
|
378
442
|
|
|
379
443
|
@remote_wrapper()
|
|
380
444
|
def create(
|
|
381
445
|
self,
|
|
382
446
|
sandbox_type=None,
|
|
383
|
-
mount_dir=None,
|
|
447
|
+
mount_dir=None, # TODO: remove to avoid leaking
|
|
384
448
|
storage_path=None,
|
|
385
449
|
environment: Optional[Dict] = None,
|
|
450
|
+
meta: Optional[Dict] = None,
|
|
386
451
|
):
|
|
387
452
|
if sandbox_type is not None:
|
|
388
453
|
target_sandbox_type = SandboxType(sandbox_type)
|
|
389
454
|
else:
|
|
390
|
-
target_sandbox_type = self.default_type
|
|
455
|
+
target_sandbox_type = self.default_type[0]
|
|
456
|
+
|
|
457
|
+
config = SandboxRegistry.get_config_by_type(target_sandbox_type)
|
|
391
458
|
|
|
392
|
-
|
|
393
|
-
if not image:
|
|
459
|
+
if not config:
|
|
394
460
|
logger.warning(
|
|
395
|
-
f"
|
|
396
|
-
f"using default",
|
|
461
|
+
f"Not found sandbox {target_sandbox_type}, " f"using default",
|
|
397
462
|
)
|
|
398
|
-
|
|
399
|
-
self.default_type,
|
|
463
|
+
config = SandboxRegistry.get_config_by_type(
|
|
464
|
+
self.default_type[0],
|
|
400
465
|
)
|
|
466
|
+
image = config.image_name
|
|
401
467
|
|
|
402
|
-
# TODO: enable for timeout for the sandbox (auto cleanup)
|
|
403
|
-
config = SandboxRegistry.get_config_by_type(target_sandbox_type)
|
|
404
468
|
environment = {
|
|
405
469
|
**(config.environment if config.environment else {}),
|
|
406
470
|
**(environment if environment else {}),
|
|
@@ -422,7 +486,7 @@ class SandboxManager:
|
|
|
422
486
|
mount_dir = os.path.join(self.default_mount_dir, session_id)
|
|
423
487
|
os.makedirs(mount_dir, exist_ok=True)
|
|
424
488
|
|
|
425
|
-
if mount_dir:
|
|
489
|
+
if mount_dir and self.container_deployment != "agentrun":
|
|
426
490
|
if not os.path.isabs(mount_dir):
|
|
427
491
|
mount_dir = os.path.abspath(mount_dir)
|
|
428
492
|
|
|
@@ -433,12 +497,16 @@ class SandboxManager:
|
|
|
433
497
|
session_id,
|
|
434
498
|
)
|
|
435
499
|
|
|
436
|
-
if
|
|
500
|
+
if (
|
|
501
|
+
mount_dir
|
|
502
|
+
and storage_path
|
|
503
|
+
and self.container_deployment != "agentrun"
|
|
504
|
+
):
|
|
437
505
|
self.storage.download_folder(storage_path, mount_dir)
|
|
438
506
|
|
|
507
|
+
# Check for an existing container with the same name
|
|
508
|
+
container_name = self._generate_container_key(session_id)
|
|
439
509
|
try:
|
|
440
|
-
# Check for an existing container with the same name
|
|
441
|
-
container_name = self._generate_container_key(session_id)
|
|
442
510
|
if self.client.inspect(container_name):
|
|
443
511
|
raise ValueError(
|
|
444
512
|
f"Container with name {container_name} already exists.",
|
|
@@ -448,7 +516,7 @@ class SandboxManager:
|
|
|
448
516
|
runtime_token = secrets.token_hex(16)
|
|
449
517
|
|
|
450
518
|
# Prepare volume bindings if a mount directory is provided
|
|
451
|
-
if mount_dir:
|
|
519
|
+
if mount_dir and self.container_deployment != "agentrun":
|
|
452
520
|
volume_bindings = {
|
|
453
521
|
mount_dir: {
|
|
454
522
|
"bind": self.workdir,
|
|
@@ -458,7 +526,16 @@ class SandboxManager:
|
|
|
458
526
|
else:
|
|
459
527
|
volume_bindings = {}
|
|
460
528
|
|
|
461
|
-
|
|
529
|
+
if self.readonly_mounts:
|
|
530
|
+
for host_path, container_path in self.readonly_mounts.items():
|
|
531
|
+
if not os.path.isabs(host_path):
|
|
532
|
+
host_path = os.path.abspath(host_path)
|
|
533
|
+
volume_bindings[host_path] = {
|
|
534
|
+
"bind": container_path,
|
|
535
|
+
"mode": "ro",
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
_id, ports, ip, *rest = self.client.create(
|
|
462
539
|
image,
|
|
463
540
|
name=container_name,
|
|
464
541
|
ports=["80/tcp"], # Nginx
|
|
@@ -470,6 +547,10 @@ class SandboxManager:
|
|
|
470
547
|
runtime_config=config.runtime_config,
|
|
471
548
|
)
|
|
472
549
|
|
|
550
|
+
http_protocol = "http"
|
|
551
|
+
if rest and rest[0] == "https":
|
|
552
|
+
http_protocol = "https"
|
|
553
|
+
|
|
473
554
|
if _id is None:
|
|
474
555
|
return None
|
|
475
556
|
|
|
@@ -487,37 +568,44 @@ class SandboxManager:
|
|
|
487
568
|
session_id=session_id,
|
|
488
569
|
container_id=_id,
|
|
489
570
|
container_name=container_name,
|
|
490
|
-
|
|
491
|
-
browser_url=f"http://{ip}:{ports[0]}/steel-api"
|
|
492
|
-
f"/{runtime_token}",
|
|
493
|
-
front_browser_ws=f"ws://{ip}:"
|
|
494
|
-
f"{ports[0]}/steel-api/"
|
|
495
|
-
f"{runtime_token}/v1/sessions/cast",
|
|
496
|
-
client_browser_ws=f"ws://{ip}:"
|
|
497
|
-
f"{ports[0]}/steel-api/{runtime_token}/&sessionId"
|
|
498
|
-
f"={BROWSER_SESSION_ID}",
|
|
499
|
-
artifacts_sio=f"http://{ip}:{ports[0]}/v1",
|
|
571
|
+
url=f"{http_protocol}://{ip}:{ports[0]}",
|
|
500
572
|
ports=[ports[0]],
|
|
501
573
|
mount_dir=str(mount_dir),
|
|
502
574
|
storage_path=storage_path,
|
|
503
575
|
runtime_token=runtime_token,
|
|
504
576
|
version=image,
|
|
577
|
+
meta=meta or {},
|
|
578
|
+
timeout=config.timeout,
|
|
505
579
|
)
|
|
580
|
+
|
|
506
581
|
# Register in mapping
|
|
507
582
|
self.container_mapping.set(
|
|
508
583
|
container_model.container_name,
|
|
509
584
|
container_model.model_dump(),
|
|
510
585
|
)
|
|
511
586
|
|
|
587
|
+
# Build mapping session_ctx_id to container_name
|
|
588
|
+
if meta and "session_ctx_id" in meta:
|
|
589
|
+
env_ids = (
|
|
590
|
+
self.session_mapping.get(
|
|
591
|
+
meta["session_ctx_id"],
|
|
592
|
+
)
|
|
593
|
+
or []
|
|
594
|
+
)
|
|
595
|
+
env_ids.append(container_model.container_name)
|
|
596
|
+
self.session_mapping.set(meta["session_ctx_id"], env_ids)
|
|
597
|
+
|
|
512
598
|
logger.debug(
|
|
513
599
|
f"Created container {container_name}"
|
|
514
600
|
f":{container_model.model_dump()}",
|
|
515
601
|
)
|
|
516
602
|
return container_name
|
|
517
603
|
except Exception as e:
|
|
518
|
-
logger.
|
|
519
|
-
f"Failed to create container: {e}
|
|
604
|
+
logger.warning(
|
|
605
|
+
f"Failed to create container: {e}",
|
|
520
606
|
)
|
|
607
|
+
logger.debug(f"{traceback.format_exc()}")
|
|
608
|
+
self.release(identity=container_name)
|
|
521
609
|
return None
|
|
522
610
|
|
|
523
611
|
@remote_wrapper()
|
|
@@ -536,6 +624,20 @@ class SandboxManager:
|
|
|
536
624
|
# remove key in mapping before we remove container
|
|
537
625
|
self.container_mapping.delete(container_json.get("container_name"))
|
|
538
626
|
|
|
627
|
+
# remove key in mapping
|
|
628
|
+
session_ctx_id = container_info.meta.get("session_ctx_id")
|
|
629
|
+
if session_ctx_id:
|
|
630
|
+
env_ids = self.session_mapping.get(session_ctx_id) or []
|
|
631
|
+
env_ids = [
|
|
632
|
+
eid
|
|
633
|
+
for eid in env_ids
|
|
634
|
+
if eid != container_info.container_name
|
|
635
|
+
]
|
|
636
|
+
if env_ids:
|
|
637
|
+
self.session_mapping.set(session_ctx_id, env_ids)
|
|
638
|
+
else:
|
|
639
|
+
self.session_mapping.delete(session_ctx_id)
|
|
640
|
+
|
|
539
641
|
self.client.stop(container_info.container_id, timeout=1)
|
|
540
642
|
self.client.remove(container_info.container_id, force=True)
|
|
541
643
|
|
|
@@ -550,10 +652,10 @@ class SandboxManager:
|
|
|
550
652
|
|
|
551
653
|
return True
|
|
552
654
|
except Exception as e:
|
|
553
|
-
logger.
|
|
554
|
-
f"Failed to destroy container: {e}
|
|
555
|
-
f"{traceback.format_exc()}",
|
|
655
|
+
logger.warning(
|
|
656
|
+
f"Failed to destroy container: {e}",
|
|
556
657
|
)
|
|
658
|
+
logger.debug(f"{traceback.format_exc()}")
|
|
557
659
|
return False
|
|
558
660
|
|
|
559
661
|
@remote_wrapper()
|
|
@@ -632,7 +734,7 @@ class SandboxManager:
|
|
|
632
734
|
self._generate_container_key(identity),
|
|
633
735
|
)
|
|
634
736
|
if container_model is None:
|
|
635
|
-
|
|
737
|
+
raise RuntimeError(f"No container found with id: {identity}.")
|
|
636
738
|
if hasattr(container_model, "model_dump_json"):
|
|
637
739
|
container_model = container_model.model_dump_json()
|
|
638
740
|
|
|
@@ -640,35 +742,26 @@ class SandboxManager:
|
|
|
640
742
|
|
|
641
743
|
def _establish_connection(self, identity):
|
|
642
744
|
container_model = ContainerModel(**self.get_info(identity))
|
|
643
|
-
# TODO: make this more robust
|
|
644
|
-
enable_browser = "browser" in container_model.version
|
|
645
745
|
|
|
646
746
|
# TODO: remake docker name
|
|
647
747
|
if (
|
|
648
748
|
"sandbox-appworld" in container_model.version
|
|
649
749
|
or "sandbox-bfcl" in container_model.version
|
|
650
750
|
):
|
|
651
|
-
parsed = urlparse(container_model.base_url)
|
|
652
|
-
base_url = urlunparse(
|
|
653
|
-
(
|
|
654
|
-
parsed.scheme,
|
|
655
|
-
parsed.netloc,
|
|
656
|
-
"",
|
|
657
|
-
"",
|
|
658
|
-
"",
|
|
659
|
-
"",
|
|
660
|
-
),
|
|
661
|
-
)
|
|
662
|
-
|
|
663
751
|
return TrainingSandboxClient(
|
|
664
|
-
base_url=
|
|
752
|
+
base_url=container_model.url,
|
|
665
753
|
).__enter__()
|
|
666
754
|
|
|
667
755
|
return SandboxHttpClient(
|
|
668
756
|
container_model,
|
|
669
|
-
enable_browser=enable_browser,
|
|
670
757
|
).__enter__()
|
|
671
758
|
|
|
759
|
+
@remote_wrapper()
|
|
760
|
+
def check_health(self, identity):
|
|
761
|
+
"""List tool"""
|
|
762
|
+
client = self._establish_connection(identity)
|
|
763
|
+
return client.check_health()
|
|
764
|
+
|
|
672
765
|
@remote_wrapper()
|
|
673
766
|
def list_tools(self, identity, tool_type=None, **kwargs):
|
|
674
767
|
"""List tool"""
|
|
@@ -691,3 +784,16 @@ class SandboxManager:
|
|
|
691
784
|
server_configs=server_configs,
|
|
692
785
|
overwrite=overwrite,
|
|
693
786
|
)
|
|
787
|
+
|
|
788
|
+
@remote_wrapper()
|
|
789
|
+
def get_session_mapping(self, session_ctx_id: str) -> list:
|
|
790
|
+
"""Get all container names bound to a session context"""
|
|
791
|
+
return self.session_mapping.get(session_ctx_id) or []
|
|
792
|
+
|
|
793
|
+
@remote_wrapper()
|
|
794
|
+
def list_session_keys(self) -> list:
|
|
795
|
+
"""Return all session_ctx_id keys currently in mapping"""
|
|
796
|
+
session_keys = []
|
|
797
|
+
for key in self.session_mapping.scan():
|
|
798
|
+
session_keys.append(key)
|
|
799
|
+
return session_keys
|