agentscope-runtime 0.1.1__py3-none-any.whl → 0.1.3__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/agent.py +105 -50
- agentscope_runtime/engine/agents/agentscope_agent/hooks.py +16 -3
- agentscope_runtime/engine/helpers/helper.py +33 -0
- agentscope_runtime/engine/runner.py +33 -1
- agentscope_runtime/engine/schemas/agent_schemas.py +208 -13
- agentscope_runtime/engine/services/context_manager.py +34 -1
- agentscope_runtime/engine/services/rag_service.py +195 -0
- agentscope_runtime/engine/services/reme_personal_memory_service.py +106 -0
- agentscope_runtime/engine/services/reme_task_memory_service.py +11 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +25 -0
- agentscope_runtime/sandbox/box/sandbox.py +60 -7
- agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -2
- agentscope_runtime/sandbox/box/training_box/env_service.py +1 -1
- agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_dataprocess.py +216 -0
- agentscope_runtime/sandbox/box/training_box/environments/bfcl/bfcl_env.py +380 -0
- agentscope_runtime/sandbox/box/training_box/environments/bfcl/env_handler.py +934 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +139 -9
- agentscope_runtime/sandbox/client/http_client.py +1 -1
- agentscope_runtime/sandbox/enums.py +2 -0
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +19 -9
- agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +61 -6
- agentscope_runtime/sandbox/manager/sandbox_manager.py +95 -35
- agentscope_runtime/sandbox/manager/server/app.py +128 -17
- agentscope_runtime/sandbox/model/__init__.py +1 -5
- agentscope_runtime/sandbox/model/manager_config.py +2 -13
- agentscope_runtime/sandbox/tools/mcp_tool.py +1 -1
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/METADATA +59 -3
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/RECORD +33 -27
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.1.dist-info → agentscope_runtime-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,7 @@ with specific configuration and tool calling methods.
|
|
|
7
7
|
"""
|
|
8
8
|
import platform
|
|
9
9
|
from typing import Dict, Optional
|
|
10
|
+
import os
|
|
10
11
|
|
|
11
12
|
from ...registry import SandboxRegistry
|
|
12
13
|
from ...enums import SandboxType
|
|
@@ -21,14 +22,6 @@ def get_image_tag() -> str:
|
|
|
21
22
|
return IMAGE_TAG
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
@SandboxRegistry.register(
|
|
25
|
-
f"agentscope/runtime-sandbox-appworld:{get_image_tag()}",
|
|
26
|
-
sandbox_type=SandboxType.APPWORLD,
|
|
27
|
-
runtime_config={"shm_size": "5.06gb"},
|
|
28
|
-
security_level="medium",
|
|
29
|
-
timeout=30,
|
|
30
|
-
description="appworld Sandbox",
|
|
31
|
-
)
|
|
32
25
|
class TrainingSandbox(Sandbox):
|
|
33
26
|
"""
|
|
34
27
|
Training Sandbox class for managing and executing training-related tasks.
|
|
@@ -43,6 +36,7 @@ class TrainingSandbox(Sandbox):
|
|
|
43
36
|
timeout: int = 3000,
|
|
44
37
|
base_url: Optional[str] = None,
|
|
45
38
|
bearer_token: Optional[str] = None,
|
|
39
|
+
box_type: SandboxType = SandboxType.APPWORLD,
|
|
46
40
|
):
|
|
47
41
|
"""
|
|
48
42
|
Initialize the Training Sandbox.
|
|
@@ -58,7 +52,7 @@ class TrainingSandbox(Sandbox):
|
|
|
58
52
|
timeout,
|
|
59
53
|
base_url,
|
|
60
54
|
bearer_token,
|
|
61
|
-
|
|
55
|
+
box_type,
|
|
62
56
|
)
|
|
63
57
|
|
|
64
58
|
def create_instance(
|
|
@@ -217,3 +211,139 @@ class TrainingSandbox(Sandbox):
|
|
|
217
211
|
"instance_id": instance_id,
|
|
218
212
|
},
|
|
219
213
|
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@SandboxRegistry.register(
|
|
217
|
+
f"agentscope/runtime-sandbox-appworld:{get_image_tag()}",
|
|
218
|
+
sandbox_type=SandboxType.APPWORLD,
|
|
219
|
+
runtime_config={"shm_size": "5.06gb"},
|
|
220
|
+
security_level="medium",
|
|
221
|
+
timeout=30,
|
|
222
|
+
description="appworld Sandbox",
|
|
223
|
+
)
|
|
224
|
+
class APPWorldSandbox(TrainingSandbox):
|
|
225
|
+
"""
|
|
226
|
+
Training Sandbox class for managing and executing training-related tasks.
|
|
227
|
+
|
|
228
|
+
This class provides methods to create, manage, and interact with
|
|
229
|
+
training environment instances using specialized tool calls.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
def __init__(
|
|
233
|
+
self,
|
|
234
|
+
sandbox_id: Optional[str] = None,
|
|
235
|
+
timeout: int = 3000,
|
|
236
|
+
base_url: Optional[str] = None,
|
|
237
|
+
bearer_token: Optional[str] = None,
|
|
238
|
+
):
|
|
239
|
+
"""
|
|
240
|
+
Initialize the Training Sandbox.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
sandbox_id (Optional[str]): Unique identifier for the sandbox.
|
|
244
|
+
timeout (int): Maximum time allowed for sandbox operations.
|
|
245
|
+
base_url (Optional[str]): Base URL for sandbox API.
|
|
246
|
+
bearer_token (Optional[str]): Authentication token for API access.
|
|
247
|
+
"""
|
|
248
|
+
super().__init__(
|
|
249
|
+
sandbox_id,
|
|
250
|
+
timeout,
|
|
251
|
+
base_url,
|
|
252
|
+
bearer_token,
|
|
253
|
+
SandboxType.APPWORLD,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
DATASET_SUB_TYPE = os.environ.get("DATASET_SUB_TYPE", "multi_turn")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@SandboxRegistry.register(
|
|
261
|
+
f"agentscope/runtime-sandbox-bfcl:{get_image_tag()}",
|
|
262
|
+
sandbox_type=SandboxType.BFCL,
|
|
263
|
+
runtime_config={"shm_size": "8.06gb"},
|
|
264
|
+
security_level="medium",
|
|
265
|
+
environment={
|
|
266
|
+
"OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY", ""),
|
|
267
|
+
"BFCL_DATA_PATH": f"/agentscope_runtime/training_box/bfcl/multi_turn/"
|
|
268
|
+
f"{DATASET_SUB_TYPE}_processed.jsonl",
|
|
269
|
+
"BFCL_SPLID_ID_PATH": f"/agentscope_runtime/training_box/"
|
|
270
|
+
f"bfcl/multi_turn/"
|
|
271
|
+
f"{DATASET_SUB_TYPE}_split_ids.json",
|
|
272
|
+
},
|
|
273
|
+
# ["all","all_scoring","multi_turn","single_turn",
|
|
274
|
+
# "live","non_live","non_python","python"]
|
|
275
|
+
timeout=30,
|
|
276
|
+
description="bfcl Sandbox",
|
|
277
|
+
)
|
|
278
|
+
class BFCLSandbox(TrainingSandbox):
|
|
279
|
+
"""
|
|
280
|
+
Training Sandbox class for managing and executing training-related tasks.
|
|
281
|
+
|
|
282
|
+
This class provides methods to create, manage, and interact with
|
|
283
|
+
training environment instances using specialized tool calls.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(
|
|
287
|
+
self,
|
|
288
|
+
sandbox_id: Optional[str] = None,
|
|
289
|
+
timeout: int = 3000,
|
|
290
|
+
base_url: Optional[str] = None,
|
|
291
|
+
bearer_token: Optional[str] = None,
|
|
292
|
+
):
|
|
293
|
+
"""
|
|
294
|
+
Initialize the Training Sandbox.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
sandbox_id (Optional[str]): Unique identifier for the sandbox.
|
|
298
|
+
timeout (int): Maximum time allowed for sandbox operations.
|
|
299
|
+
base_url (Optional[str]): Base URL for sandbox API.
|
|
300
|
+
bearer_token (Optional[str]): Authentication token for API access.
|
|
301
|
+
"""
|
|
302
|
+
super().__init__(
|
|
303
|
+
sandbox_id,
|
|
304
|
+
timeout,
|
|
305
|
+
base_url,
|
|
306
|
+
bearer_token,
|
|
307
|
+
SandboxType.BFCL,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@SandboxRegistry.register(
|
|
312
|
+
f"agentscope/runtime-sandbox-webshop:{get_image_tag()}",
|
|
313
|
+
sandbox_type=SandboxType.WEBSHOP,
|
|
314
|
+
runtime_config={"shm_size": "5.06gb"},
|
|
315
|
+
security_level="medium",
|
|
316
|
+
timeout=30,
|
|
317
|
+
description="webshop Sandbox",
|
|
318
|
+
)
|
|
319
|
+
class WebShopSandbox(TrainingSandbox):
|
|
320
|
+
"""
|
|
321
|
+
Training Sandbox class for managing and executing training-related tasks.
|
|
322
|
+
|
|
323
|
+
This class provides methods to create, manage, and interact with
|
|
324
|
+
training environment instances using specialized tool calls.
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
def __init__(
|
|
328
|
+
self,
|
|
329
|
+
sandbox_id: Optional[str] = None,
|
|
330
|
+
timeout: int = 3000,
|
|
331
|
+
base_url: Optional[str] = None,
|
|
332
|
+
bearer_token: Optional[str] = None,
|
|
333
|
+
):
|
|
334
|
+
"""
|
|
335
|
+
Initialize the Training Sandbox.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
sandbox_id (Optional[str]): Unique identifier for the sandbox.
|
|
339
|
+
timeout (int): Maximum time allowed for sandbox operations.
|
|
340
|
+
base_url (Optional[str]): Base URL for sandbox API.
|
|
341
|
+
bearer_token (Optional[str]): Authentication token for API access.
|
|
342
|
+
"""
|
|
343
|
+
super().__init__(
|
|
344
|
+
sandbox_id,
|
|
345
|
+
timeout,
|
|
346
|
+
base_url,
|
|
347
|
+
bearer_token,
|
|
348
|
+
SandboxType.BFCL,
|
|
349
|
+
)
|
|
@@ -195,7 +195,7 @@ class SandboxHttpClient:
|
|
|
195
195
|
mcp_tools = response.json()
|
|
196
196
|
mcp_tools["generic"] = self.generic_tools
|
|
197
197
|
if tool_type:
|
|
198
|
-
return {tool_type: mcp_tools.get(tool_type,
|
|
198
|
+
return {tool_type: mcp_tools.get(tool_type, {})}
|
|
199
199
|
return mcp_tools
|
|
200
200
|
except requests.exceptions.RequestException as e:
|
|
201
201
|
logging.error(f"An error occurred: {e}")
|
|
@@ -217,9 +217,12 @@ class DockerClient(BaseClient):
|
|
|
217
217
|
acr_registry = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
|
|
218
218
|
acr_image = f"{acr_registry}/{image}"
|
|
219
219
|
|
|
220
|
-
logger.
|
|
220
|
+
logger.info(
|
|
221
|
+
f"Attempting to pull from ACR: {acr_image}, it might take "
|
|
222
|
+
f"several minutes.",
|
|
223
|
+
)
|
|
221
224
|
self.client.images.pull(acr_image)
|
|
222
|
-
logger.
|
|
225
|
+
logger.info(f"Successfully pulled image from ACR: {acr_image}")
|
|
223
226
|
|
|
224
227
|
# Retag the image
|
|
225
228
|
acr_img_obj = self.client.images.get(acr_image)
|
|
@@ -234,7 +237,9 @@ class DockerClient(BaseClient):
|
|
|
234
237
|
logger.debug(f"Failed to remove original tag: {e}")
|
|
235
238
|
return True
|
|
236
239
|
except Exception as e:
|
|
237
|
-
logger.error(
|
|
240
|
+
logger.error(
|
|
241
|
+
f"Failed to pull from ACR: {e}, {traceback.format_exc()}",
|
|
242
|
+
)
|
|
238
243
|
return False
|
|
239
244
|
|
|
240
245
|
def create(
|
|
@@ -263,11 +268,15 @@ class DockerClient(BaseClient):
|
|
|
263
268
|
self.client.images.get(image)
|
|
264
269
|
logger.debug(f"Image '{image}' found locally.")
|
|
265
270
|
except docker.errors.ImageNotFound:
|
|
266
|
-
logger.
|
|
271
|
+
logger.info(
|
|
267
272
|
f"Image '{image}' not found locally. "
|
|
268
273
|
f"Attempting to pull it...",
|
|
269
274
|
)
|
|
270
275
|
try:
|
|
276
|
+
logger.info(
|
|
277
|
+
f"Attempting to pull: {image}, "
|
|
278
|
+
f"it might take several minutes.",
|
|
279
|
+
)
|
|
271
280
|
self.client.images.pull(image)
|
|
272
281
|
logger.debug(
|
|
273
282
|
f"Image '{image}' successfully pulled from default "
|
|
@@ -278,7 +287,8 @@ class DockerClient(BaseClient):
|
|
|
278
287
|
logger.warning(
|
|
279
288
|
f"Failed to pull from default registry: {e}",
|
|
280
289
|
)
|
|
281
|
-
logger.
|
|
290
|
+
logger.warning("Trying to pull from ACR fallback...")
|
|
291
|
+
|
|
282
292
|
pull_success = self._try_pull_from_acr(image)
|
|
283
293
|
|
|
284
294
|
if not pull_success:
|
|
@@ -286,11 +296,11 @@ class DockerClient(BaseClient):
|
|
|
286
296
|
f"Failed to pull image '{image}' from both "
|
|
287
297
|
f"default and ACR",
|
|
288
298
|
)
|
|
289
|
-
return
|
|
299
|
+
return None, None, None
|
|
290
300
|
|
|
291
301
|
except docker.errors.APIError as e:
|
|
292
302
|
logger.error(f"Error occurred while checking the image: {e}")
|
|
293
|
-
return
|
|
303
|
+
return None, None, None
|
|
294
304
|
|
|
295
305
|
# Create and run the container
|
|
296
306
|
container = self.client.containers.run(
|
|
@@ -304,10 +314,10 @@ class DockerClient(BaseClient):
|
|
|
304
314
|
)
|
|
305
315
|
container.reload()
|
|
306
316
|
_id = container.id
|
|
307
|
-
return _id, list(port_mapping.values())
|
|
317
|
+
return _id, list(port_mapping.values()), "localhost"
|
|
308
318
|
except Exception as e:
|
|
309
319
|
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
310
|
-
return None, None
|
|
320
|
+
return None, None, None
|
|
311
321
|
|
|
312
322
|
def start(self, container_id):
|
|
313
323
|
"""Start a Docker container."""
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# pylint: disable=too-many-branches
|
|
3
|
+
import os
|
|
3
4
|
import time
|
|
4
5
|
import hashlib
|
|
5
6
|
import traceback
|
|
@@ -89,7 +90,9 @@ class KubernetesClient(BaseClient):
|
|
|
89
90
|
runtime_config = {}
|
|
90
91
|
|
|
91
92
|
container_name = name or "main-container"
|
|
93
|
+
|
|
92
94
|
# Container specification
|
|
95
|
+
# TODO: use image from docker registry first
|
|
93
96
|
container = client.V1Container(
|
|
94
97
|
name=container_name,
|
|
95
98
|
image=f"agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
|
|
@@ -244,6 +247,7 @@ class KubernetesClient(BaseClient):
|
|
|
244
247
|
)
|
|
245
248
|
|
|
246
249
|
exposed_ports = []
|
|
250
|
+
pod_node_ip = "localhost"
|
|
247
251
|
# Auto-create services for exposed ports (like Docker's port
|
|
248
252
|
# mapping)
|
|
249
253
|
if ports:
|
|
@@ -259,19 +263,22 @@ class KubernetesClient(BaseClient):
|
|
|
259
263
|
parsed_ports,
|
|
260
264
|
)
|
|
261
265
|
if service_created:
|
|
262
|
-
|
|
266
|
+
(
|
|
267
|
+
exposed_ports,
|
|
268
|
+
pod_node_ip,
|
|
269
|
+
) = self._get_service_node_ports(name)
|
|
263
270
|
logger.debug(
|
|
264
271
|
f"Pod '{name}' created with exposed ports: {exposed_ports}",
|
|
265
272
|
)
|
|
266
273
|
|
|
267
274
|
if not self.wait_for_pod_ready(name, timeout=60):
|
|
268
275
|
logger.error(f"Pod '{name}' failed to become ready")
|
|
269
|
-
return None, None
|
|
276
|
+
return None, None, None
|
|
270
277
|
|
|
271
|
-
return name, exposed_ports
|
|
278
|
+
return name, exposed_ports, pod_node_ip
|
|
272
279
|
except Exception as e:
|
|
273
280
|
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
274
|
-
return None, None
|
|
281
|
+
return None, None, None
|
|
275
282
|
|
|
276
283
|
def start(self, container_id):
|
|
277
284
|
"""
|
|
@@ -510,7 +517,10 @@ class KubernetesClient(BaseClient):
|
|
|
510
517
|
service = client.V1Service(
|
|
511
518
|
api_version="v1",
|
|
512
519
|
kind="Service",
|
|
513
|
-
metadata=client.V1ObjectMeta(
|
|
520
|
+
metadata=client.V1ObjectMeta(
|
|
521
|
+
name=service_name,
|
|
522
|
+
namespace=self.namespace,
|
|
523
|
+
),
|
|
514
524
|
spec=service_spec,
|
|
515
525
|
)
|
|
516
526
|
|
|
@@ -540,11 +550,56 @@ class KubernetesClient(BaseClient):
|
|
|
540
550
|
)
|
|
541
551
|
|
|
542
552
|
node_ports = []
|
|
553
|
+
pod_node_ip = self._get_pod_node_ip(pod_name)
|
|
554
|
+
|
|
543
555
|
for port in service_info.spec.ports:
|
|
544
556
|
if port.node_port:
|
|
545
557
|
node_ports.append(port.node_port)
|
|
546
558
|
|
|
547
|
-
return node_ports
|
|
559
|
+
return node_ports, pod_node_ip
|
|
548
560
|
except Exception as e:
|
|
549
561
|
logger.error(f"Failed to get node port: {e}")
|
|
550
562
|
return None
|
|
563
|
+
|
|
564
|
+
def _get_pod_node_ip(self, pod_name):
|
|
565
|
+
"""Get the IP of the node where the pod is running"""
|
|
566
|
+
|
|
567
|
+
# Check if we are running in Colima, where pod runs in VM
|
|
568
|
+
docker_host = os.getenv("DOCKER_HOST", "")
|
|
569
|
+
if "colima" in docker_host.lower():
|
|
570
|
+
return "localhost"
|
|
571
|
+
|
|
572
|
+
try:
|
|
573
|
+
pod = self.v1.read_namespaced_pod(
|
|
574
|
+
name=pod_name,
|
|
575
|
+
namespace=self.namespace,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
node_name = pod.spec.node_name
|
|
579
|
+
if not node_name:
|
|
580
|
+
logger.warning(
|
|
581
|
+
f"Pod {pod_name} is not scheduled to any node yet",
|
|
582
|
+
)
|
|
583
|
+
return None
|
|
584
|
+
|
|
585
|
+
node = self.v1.read_node(name=node_name)
|
|
586
|
+
|
|
587
|
+
external_ip = None
|
|
588
|
+
internal_ip = None
|
|
589
|
+
|
|
590
|
+
for address in node.status.addresses:
|
|
591
|
+
if address.type == "ExternalIP":
|
|
592
|
+
external_ip = address.address
|
|
593
|
+
elif address.type == "InternalIP":
|
|
594
|
+
internal_ip = address.address
|
|
595
|
+
|
|
596
|
+
result_ip = external_ip or internal_ip
|
|
597
|
+
logger.debug(
|
|
598
|
+
f"Using IP: {result_ip} (external: {external_ip}, internal:"
|
|
599
|
+
f" {internal_ip})",
|
|
600
|
+
)
|
|
601
|
+
return result_ip
|
|
602
|
+
|
|
603
|
+
except Exception as e:
|
|
604
|
+
logger.error(f"Failed to get pod node IP: {e}")
|
|
605
|
+
return None
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
# pylint: disable=redefined-outer-name, protected-access, too-many-branches
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import json
|
|
5
6
|
import secrets
|
|
6
7
|
import inspect
|
|
7
8
|
import traceback
|
|
8
9
|
|
|
9
10
|
from functools import wraps
|
|
10
11
|
from typing import Optional, Dict
|
|
12
|
+
from urllib.parse import urlparse, urlunparse
|
|
11
13
|
|
|
12
14
|
import shortuuid
|
|
13
15
|
import requests
|
|
@@ -15,7 +17,6 @@ import requests
|
|
|
15
17
|
from ..model import (
|
|
16
18
|
ContainerModel,
|
|
17
19
|
SandboxManagerEnvConfig,
|
|
18
|
-
DEFAULT_LOCAL_MANAGER_CONFIG,
|
|
19
20
|
)
|
|
20
21
|
from ..enums import SandboxType
|
|
21
22
|
from ..registry import SandboxRegistry
|
|
@@ -81,7 +82,7 @@ def remote_wrapper(
|
|
|
81
82
|
class SandboxManager:
|
|
82
83
|
def __init__(
|
|
83
84
|
self,
|
|
84
|
-
config: SandboxManagerEnvConfig =
|
|
85
|
+
config: Optional[SandboxManagerEnvConfig] = None,
|
|
85
86
|
base_url=None,
|
|
86
87
|
bearer_token=None,
|
|
87
88
|
default_type: SandboxType | str = SandboxType.BASE,
|
|
@@ -96,19 +97,28 @@ class SandboxManager:
|
|
|
96
97
|
self.http_session.headers.update(
|
|
97
98
|
{"Authorization": f"Bearer {bearer_token}"},
|
|
98
99
|
)
|
|
100
|
+
# Remote mode, return directly
|
|
101
|
+
return
|
|
99
102
|
else:
|
|
100
103
|
self.http_session = None
|
|
101
104
|
self.base_url = None
|
|
102
105
|
|
|
106
|
+
if not config:
|
|
107
|
+
config = SandboxManagerEnvConfig(
|
|
108
|
+
file_system="local",
|
|
109
|
+
redis_enabled=False,
|
|
110
|
+
container_deployment="docker",
|
|
111
|
+
pool_size=0,
|
|
112
|
+
default_mount_dir="sessions_mount_dir",
|
|
113
|
+
)
|
|
114
|
+
|
|
103
115
|
self.default_type = SandboxType(default_type)
|
|
104
116
|
self.workdir = "/workspace"
|
|
105
117
|
|
|
106
118
|
self.config = config
|
|
107
119
|
self.pool_size = self.config.pool_size
|
|
108
120
|
self.prefix = self.config.container_prefix_key
|
|
109
|
-
self.default_mount_dir =
|
|
110
|
-
self.config.default_mount_dir or "sessions_mount_dir"
|
|
111
|
-
)
|
|
121
|
+
self.default_mount_dir = self.config.default_mount_dir
|
|
112
122
|
self.storage_folder = (
|
|
113
123
|
self.config.storage_folder or self.default_mount_dir
|
|
114
124
|
)
|
|
@@ -197,8 +207,35 @@ class SandboxManager:
|
|
|
197
207
|
try:
|
|
198
208
|
response.raise_for_status()
|
|
199
209
|
except requests.exceptions.HTTPError as e:
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
error_components = [
|
|
211
|
+
f"HTTP {response.status_code} Error: {str(e)}",
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
server_response = response.json()
|
|
216
|
+
if "detail" in server_response:
|
|
217
|
+
error_components.append(
|
|
218
|
+
f"Server Detail: {server_response['detail']}",
|
|
219
|
+
)
|
|
220
|
+
elif "error" in server_response:
|
|
221
|
+
error_components.append(
|
|
222
|
+
f"Server Error: {server_response['error']}",
|
|
223
|
+
)
|
|
224
|
+
else:
|
|
225
|
+
error_components.append(
|
|
226
|
+
f"Server Response: {server_response}",
|
|
227
|
+
)
|
|
228
|
+
except (ValueError, json.JSONDecodeError):
|
|
229
|
+
if response.text:
|
|
230
|
+
error_components.append(
|
|
231
|
+
f"Server Response: {response.text}",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
error = " | ".join(error_components)
|
|
235
|
+
|
|
236
|
+
logger.error(f"Error making request: {error}")
|
|
237
|
+
|
|
238
|
+
return {"data": f"Error: {error}"}
|
|
202
239
|
|
|
203
240
|
return response.json()
|
|
204
241
|
|
|
@@ -380,20 +417,24 @@ class SandboxManager:
|
|
|
380
417
|
short_uuid = shortuuid.ShortUUID(alphabet=alphabet).uuid()
|
|
381
418
|
session_id = str(short_uuid)
|
|
382
419
|
|
|
383
|
-
if mount_dir
|
|
384
|
-
|
|
385
|
-
|
|
420
|
+
if not mount_dir:
|
|
421
|
+
if self.default_mount_dir:
|
|
422
|
+
mount_dir = os.path.join(self.default_mount_dir, session_id)
|
|
423
|
+
os.makedirs(mount_dir, exist_ok=True)
|
|
386
424
|
|
|
387
|
-
if
|
|
388
|
-
|
|
425
|
+
if mount_dir:
|
|
426
|
+
if not os.path.isabs(mount_dir):
|
|
427
|
+
mount_dir = os.path.abspath(mount_dir)
|
|
389
428
|
|
|
390
429
|
if storage_path is None:
|
|
391
|
-
|
|
392
|
-
self.
|
|
393
|
-
|
|
394
|
-
|
|
430
|
+
if self.storage_folder:
|
|
431
|
+
storage_path = self.storage.path_join(
|
|
432
|
+
self.storage_folder,
|
|
433
|
+
session_id,
|
|
434
|
+
)
|
|
395
435
|
|
|
396
|
-
|
|
436
|
+
if mount_dir and storage_path:
|
|
437
|
+
self.storage.download_folder(storage_path, mount_dir)
|
|
397
438
|
|
|
398
439
|
try:
|
|
399
440
|
# Check for an existing container with the same name
|
|
@@ -407,14 +448,17 @@ class SandboxManager:
|
|
|
407
448
|
runtime_token = secrets.token_hex(16)
|
|
408
449
|
|
|
409
450
|
# Prepare volume bindings if a mount directory is provided
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
451
|
+
if mount_dir:
|
|
452
|
+
volume_bindings = {
|
|
453
|
+
mount_dir: {
|
|
454
|
+
"bind": self.workdir,
|
|
455
|
+
"mode": "rw",
|
|
456
|
+
},
|
|
457
|
+
}
|
|
458
|
+
else:
|
|
459
|
+
volume_bindings = {}
|
|
416
460
|
|
|
417
|
-
_id, ports = self.client.create(
|
|
461
|
+
_id, ports, ip = self.client.create(
|
|
418
462
|
image,
|
|
419
463
|
name=container_name,
|
|
420
464
|
ports=["80/tcp"], # Nginx
|
|
@@ -443,16 +487,16 @@ class SandboxManager:
|
|
|
443
487
|
session_id=session_id,
|
|
444
488
|
container_id=_id,
|
|
445
489
|
container_name=container_name,
|
|
446
|
-
base_url=f"http://
|
|
447
|
-
browser_url=f"http://
|
|
490
|
+
base_url=f"http://{ip}:{ports[0]}/fastapi",
|
|
491
|
+
browser_url=f"http://{ip}:{ports[0]}/steel-api"
|
|
448
492
|
f"/{runtime_token}",
|
|
449
|
-
front_browser_ws=f"ws://
|
|
493
|
+
front_browser_ws=f"ws://{ip}:"
|
|
450
494
|
f"{ports[0]}/steel-api/"
|
|
451
495
|
f"{runtime_token}/v1/sessions/cast",
|
|
452
|
-
client_browser_ws=f"ws://
|
|
496
|
+
client_browser_ws=f"ws://{ip}:"
|
|
453
497
|
f"{ports[0]}/steel-api/{runtime_token}/&sessionId"
|
|
454
498
|
f"={BROWSER_SESSION_ID}",
|
|
455
|
-
artifacts_sio=f"http://
|
|
499
|
+
artifacts_sio=f"http://{ip}:{ports[0]}/v1",
|
|
456
500
|
ports=[ports[0]],
|
|
457
501
|
mount_dir=str(mount_dir),
|
|
458
502
|
storage_path=storage_path,
|
|
@@ -498,10 +542,11 @@ class SandboxManager:
|
|
|
498
542
|
logger.debug(f"Container for {identity} destroyed.")
|
|
499
543
|
|
|
500
544
|
# Upload to storage
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
545
|
+
if container_info.mount_dir and container_info.storage_path:
|
|
546
|
+
self.storage.upload_folder(
|
|
547
|
+
container_info.mount_dir,
|
|
548
|
+
container_info.storage_path,
|
|
549
|
+
)
|
|
505
550
|
|
|
506
551
|
return True
|
|
507
552
|
except Exception as e:
|
|
@@ -599,9 +644,24 @@ class SandboxManager:
|
|
|
599
644
|
enable_browser = "browser" in container_model.version
|
|
600
645
|
|
|
601
646
|
# TODO: remake docker name
|
|
602
|
-
if
|
|
647
|
+
if (
|
|
648
|
+
"sandbox-appworld" in container_model.version
|
|
649
|
+
or "sandbox-bfcl" in container_model.version
|
|
650
|
+
):
|
|
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
|
+
|
|
603
663
|
return TrainingSandboxClient(
|
|
604
|
-
base_url=
|
|
664
|
+
base_url=base_url,
|
|
605
665
|
).__enter__()
|
|
606
666
|
|
|
607
667
|
return SandboxHttpClient(
|