agentscope-runtime 0.1.4__py3-none-any.whl → 0.1.5__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.
Files changed (48) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent/agent.py +56 -12
  2. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +2 -1
  3. agentscope_runtime/engine/agents/agno_agent.py +11 -5
  4. agentscope_runtime/engine/agents/autogen_agent.py +10 -4
  5. agentscope_runtime/engine/agents/utils.py +53 -0
  6. agentscope_runtime/engine/services/mem0_memory_service.py +124 -0
  7. agentscope_runtime/engine/services/memory_service.py +2 -1
  8. agentscope_runtime/engine/services/redis_session_history_service.py +4 -3
  9. agentscope_runtime/engine/services/sandbox_service.py +6 -16
  10. agentscope_runtime/engine/services/session_history_service.py +4 -3
  11. agentscope_runtime/engine/services/tablestore_memory_service.py +304 -0
  12. agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
  13. agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
  14. agentscope_runtime/engine/services/utils/__init__.py +0 -0
  15. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
  16. agentscope_runtime/sandbox/box/base/base_sandbox.py +2 -2
  17. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +2 -2
  18. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +2 -2
  19. agentscope_runtime/sandbox/box/training_box/training_box.py +4 -12
  20. agentscope_runtime/sandbox/build.py +37 -17
  21. agentscope_runtime/sandbox/client/http_client.py +42 -10
  22. agentscope_runtime/sandbox/client/training_client.py +0 -1
  23. agentscope_runtime/sandbox/constant.py +26 -0
  24. agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
  25. agentscope_runtime/sandbox/custom/example.py +2 -2
  26. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +4 -2
  27. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
  28. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  29. agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1096 -0
  30. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +25 -201
  31. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +1 -3
  32. agentscope_runtime/sandbox/manager/sandbox_manager.py +40 -13
  33. agentscope_runtime/sandbox/manager/server/app.py +27 -0
  34. agentscope_runtime/sandbox/manager/server/config.py +30 -2
  35. agentscope_runtime/sandbox/model/container.py +1 -1
  36. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  37. agentscope_runtime/sandbox/utils.py +97 -0
  38. agentscope_runtime/version.py +1 -1
  39. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/METADATA +52 -56
  40. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/RECORD +44 -39
  41. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  42. agentscope_runtime/engine/llms/__init__.py +0 -3
  43. agentscope_runtime/engine/llms/base_llm.py +0 -60
  44. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  45. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/WHEEL +0 -0
  46. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/entry_points.txt +0 -0
  47. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/licenses/LICENSE +0 -0
  48. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,17 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import traceback
3
3
  import logging
4
- import platform
5
4
  import socket
6
- import subprocess
7
5
 
8
6
  import docker
9
7
 
10
8
  from .base_client import BaseClient
11
- from ..collections import RedisSetCollection, InMemorySetCollection
9
+ from ..collections import (
10
+ RedisSetCollection,
11
+ InMemorySetCollection,
12
+ RedisMapping,
13
+ InMemoryMapping,
14
+ )
12
15
 
13
16
 
14
17
  logger = logging.getLogger(__name__)
@@ -34,138 +37,6 @@ def is_port_available(port):
34
37
  return False
35
38
 
36
39
 
37
- def sweep_port(port):
38
- """Sweep all processes found listening on a given port.
39
-
40
- Args:
41
- port (int): The port number.
42
-
43
- Returns:
44
- bool: True if successful, False if failed.
45
- """
46
- try:
47
- system = platform.system().lower()
48
- if system == "windows":
49
- return _sweep_port_windows(port)
50
- else:
51
- return _sweep_port_unix(port)
52
- except Exception as e:
53
- logger.error(
54
- f"An error occurred while killing processes on port {port}: {e}",
55
- )
56
- return False
57
-
58
-
59
- def _sweep_port_unix(port):
60
- """
61
- Sweep all processes found listening on a given port.
62
-
63
- Args:
64
- port (int): The port number.
65
-
66
- Returns:
67
- int: Number of processes swept (terminated).
68
- """
69
- try:
70
- # Use lsof to find the processes using the port
71
- # TODO: support windows
72
- result = subprocess.run(
73
- ["lsof", "-i", f":{port}"],
74
- capture_output=True,
75
- text=True,
76
- check=True,
77
- )
78
-
79
- # Parse the output
80
- lines = result.stdout.strip().split("\n")
81
- if len(lines) <= 1:
82
- # No process is using the port
83
- return True
84
-
85
- # Iterate over each line (excluding the header) and kill each process
86
- killed_count = 0
87
- for line in lines[1:]:
88
- parts = line.split()
89
- if len(parts) > 1:
90
- pid = parts[1]
91
-
92
- # Kill the process using the PID
93
- subprocess.run(["kill", "-9", pid], check=False)
94
- killed_count += 1
95
-
96
- if not is_port_available(port):
97
- logger.warning(
98
- f"Port {port} is still in use after killing processes.",
99
- )
100
-
101
- return True
102
-
103
- except Exception as e:
104
- logger.error(
105
- f"An error occurred while killing processes on port {port}: {e}",
106
- )
107
- return False
108
-
109
-
110
- def _sweep_port_windows(port):
111
- """
112
- Windows implementation using netstat and taskkill
113
- """
114
- try:
115
- # Use netstat to find the processes using the port
116
- result = subprocess.run(
117
- ["netstat", "-ano"],
118
- capture_output=True,
119
- text=True,
120
- check=True,
121
- )
122
-
123
- # Parse the output to find processes using the specific port
124
- lines = result.stdout.strip().split("\n")
125
- pids_to_kill = set()
126
-
127
- for line in lines:
128
- if f":{port}" in line and "LISTENING" in line:
129
- parts = line.split()
130
- if len(parts) >= 5:
131
- pid = parts[-1] # PID is usually the last column
132
- if pid.isdigit(): # Ensure it's a valid PID
133
- pids_to_kill.add(pid)
134
-
135
- if not pids_to_kill:
136
- return True
137
-
138
- # Kill the processes
139
- killed_count = 0
140
- for pid in pids_to_kill:
141
- try:
142
- result = subprocess.run(
143
- ["taskkill", "/PID", pid, "/F"],
144
- capture_output=True,
145
- text=True,
146
- check=False,
147
- )
148
- if result.returncode == 0:
149
- killed_count += 1
150
- except Exception as e:
151
- logger.debug(f"Failed to kill process {pid}: {e}")
152
- continue
153
-
154
- if not is_port_available(port):
155
- logger.warning(
156
- f"Port {port} is still in use after killing processes.",
157
- )
158
-
159
- return True
160
-
161
- except subprocess.CalledProcessError as e:
162
- logger.error(f"netstat command failed: {e}")
163
- return False
164
- except Exception as e:
165
- logger.error(f"Error in Windows port sweep: {e}")
166
- return False
167
-
168
-
169
40
  class DockerClient(BaseClient):
170
41
  def __init__(self, config=None):
171
42
  self.config = config
@@ -193,8 +64,13 @@ class DockerClient(BaseClient):
193
64
  redis_client,
194
65
  set_name=self.config.redis_port_key,
195
66
  )
67
+ self.ports_cache = RedisMapping(
68
+ redis_client,
69
+ prefix=self.config.redis_port_key,
70
+ )
196
71
  else:
197
72
  self.port_set = InMemorySetCollection()
73
+ self.ports_cache = InMemoryMapping()
198
74
 
199
75
  try:
200
76
  self.client = docker.from_env()
@@ -208,40 +84,6 @@ class DockerClient(BaseClient):
208
84
  "export DOCKER_HOST=unix://$HOME/.colima/docker.sock",
209
85
  ) from e
210
86
 
211
- def _try_pull_from_acr(self, image):
212
- """
213
- Attempt to pull the image from the Alibaba Cloud Container Registry
214
- (ACR) and retag it.
215
- """
216
- try:
217
- acr_registry = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
218
- acr_image = f"{acr_registry}/{image}"
219
-
220
- logger.info(
221
- f"Attempting to pull from ACR: {acr_image}, it might take "
222
- f"several minutes.",
223
- )
224
- self.client.images.pull(acr_image)
225
- logger.info(f"Successfully pulled image from ACR: {acr_image}")
226
-
227
- # Retag the image
228
- acr_img_obj = self.client.images.get(acr_image)
229
- acr_img_obj.tag(image)
230
- logger.debug(f"Successfully tagged image as: {image}")
231
-
232
- # Optionally remove the original tag to save space
233
- try:
234
- self.client.images.remove(acr_image)
235
- logger.debug(f"Removed original tag: {acr_image}")
236
- except Exception as e:
237
- logger.debug(f"Failed to remove original tag: {e}")
238
- return True
239
- except Exception as e:
240
- logger.error(
241
- f"Failed to pull from ACR: {e}, {traceback.format_exc()}",
242
- )
243
- return False
244
-
245
87
  def create(
246
88
  self,
247
89
  image,
@@ -279,22 +121,11 @@ class DockerClient(BaseClient):
279
121
  )
280
122
  self.client.images.pull(image)
281
123
  logger.debug(
282
- f"Image '{image}' successfully pulled from default "
283
- f"registry.",
284
- )
285
- pull_success = True
286
- except docker.errors.APIError as e:
287
- logger.warning(
288
- f"Failed to pull from default registry: {e}",
124
+ f"Image '{image}' successfully pulled.",
289
125
  )
290
- logger.warning("Trying to pull from ACR fallback...")
291
-
292
- pull_success = self._try_pull_from_acr(image)
293
-
294
- if not pull_success:
126
+ except Exception as e:
295
127
  logger.error(
296
- f"Failed to pull image '{image}' from both "
297
- f"default and ACR",
128
+ f"Failed to pull image '{image}': {str(e)}",
298
129
  )
299
130
  return None, None, None
300
131
 
@@ -314,6 +145,9 @@ class DockerClient(BaseClient):
314
145
  )
315
146
  container.reload()
316
147
  _id = container.id
148
+
149
+ self.ports_cache.set(_id, list(port_mapping.values()))
150
+
317
151
  return _id, list(port_mapping.values()), "localhost"
318
152
  except Exception as e:
319
153
  logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
@@ -326,16 +160,6 @@ class DockerClient(BaseClient):
326
160
  container_id,
327
161
  )
328
162
 
329
- # Check whether the ports are occupied by other processes
330
- port_mapping = container.attrs["NetworkSettings"]["Ports"]
331
- for _, mappings in port_mapping.items():
332
- if mappings is not None:
333
- for mapping in mappings:
334
- host_port = int(mapping["HostPort"])
335
- if is_port_available(host_port):
336
- continue
337
- sweep_port(host_port["HostPort"])
338
-
339
163
  container.start()
340
164
  return True
341
165
  except Exception as e:
@@ -360,16 +184,16 @@ class DockerClient(BaseClient):
360
184
  container = self.client.containers.get(
361
185
  container_id,
362
186
  )
363
- # Remove ports
364
- port_mapping = container.attrs["NetworkSettings"]["Ports"]
187
+ ports = self.ports_cache.get(container_id)
188
+ self.ports_cache.delete(container_id)
189
+
190
+ # Remove container
365
191
  container.remove(force=force)
366
192
 
367
- # Iterate over each port and its mappings
368
- for _, mappings in port_mapping.items():
369
- if mappings is not None:
370
- for mapping in mappings:
371
- host_port = int(mapping["HostPort"])
372
- self.port_set.remove(host_port)
193
+ # Remove ports
194
+ if ports:
195
+ for host_port in ports:
196
+ self.port_set.remove(host_port)
373
197
 
374
198
  return True
375
199
  except Exception as e:
@@ -92,11 +92,9 @@ class KubernetesClient(BaseClient):
92
92
  container_name = name or "main-container"
93
93
 
94
94
  # Container specification
95
- # TODO: use image from docker registry first
96
95
  container = client.V1Container(
97
96
  name=container_name,
98
- image=f"agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
99
- f"/{image}",
97
+ image=image,
100
98
  image_pull_policy=runtime_config.get(
101
99
  "image_pull_policy",
102
100
  "IfNotPresent",
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
- # pylint: disable=redefined-outer-name, protected-access, too-many-branches
2
+ # pylint: disable=redefined-outer-name, protected-access
3
+ # pylint: disable=too-many-branches, too-many-statements
3
4
  import logging
4
5
  import os
5
6
  import json
@@ -14,6 +15,8 @@ from urllib.parse import urlparse, urlunparse
14
15
  import shortuuid
15
16
  import requests
16
17
 
18
+ from .container_clients.docker_client import DockerClient
19
+ from .container_clients.kubernetes_client import KubernetesClient
17
20
  from ..model import (
18
21
  ContainerModel,
19
22
  SandboxManagerEnvConfig,
@@ -32,7 +35,6 @@ from ..manager.storage import (
32
35
  LocalStorage,
33
36
  OSSStorage,
34
37
  )
35
- from ..manager.container_clients import DockerClient, KubernetesClient
36
38
  from ..constant import BROWSER_SESSION_ID
37
39
 
38
40
  logging.basicConfig(level=logging.INFO)
@@ -119,6 +121,7 @@ class SandboxManager:
119
121
  self.pool_size = self.config.pool_size
120
122
  self.prefix = self.config.container_prefix_key
121
123
  self.default_mount_dir = self.config.default_mount_dir
124
+ self.readonly_mounts = self.config.readonly_mounts
122
125
  self.storage_folder = (
123
126
  self.config.storage_folder or self.default_mount_dir
124
127
  )
@@ -157,6 +160,10 @@ class SandboxManager:
157
160
  self.client = DockerClient(config=self.config)
158
161
  elif self.container_deployment == "k8s":
159
162
  self.client = KubernetesClient(config=self.config)
163
+ elif self.container_deployment == "agentrun":
164
+ from .container_clients.agentrun_client import AgentRunClient
165
+
166
+ self.client = AgentRunClient(config=self.config)
160
167
  else:
161
168
  raise NotImplementedError("Not implemented")
162
169
  else:
@@ -380,7 +387,7 @@ class SandboxManager:
380
387
  def create(
381
388
  self,
382
389
  sandbox_type=None,
383
- mount_dir=None,
390
+ mount_dir=None, # TODO: remove to avoid leaking
384
391
  storage_path=None,
385
392
  environment: Optional[Dict] = None,
386
393
  ):
@@ -422,7 +429,7 @@ class SandboxManager:
422
429
  mount_dir = os.path.join(self.default_mount_dir, session_id)
423
430
  os.makedirs(mount_dir, exist_ok=True)
424
431
 
425
- if mount_dir:
432
+ if mount_dir and self.container_deployment != "agentrun":
426
433
  if not os.path.isabs(mount_dir):
427
434
  mount_dir = os.path.abspath(mount_dir)
428
435
 
@@ -433,7 +440,11 @@ class SandboxManager:
433
440
  session_id,
434
441
  )
435
442
 
436
- if mount_dir and storage_path:
443
+ if (
444
+ mount_dir
445
+ and storage_path
446
+ and self.container_deployment != "agentrun"
447
+ ):
437
448
  self.storage.download_folder(storage_path, mount_dir)
438
449
 
439
450
  try:
@@ -448,7 +459,7 @@ class SandboxManager:
448
459
  runtime_token = secrets.token_hex(16)
449
460
 
450
461
  # Prepare volume bindings if a mount directory is provided
451
- if mount_dir:
462
+ if mount_dir and self.container_deployment != "agentrun":
452
463
  volume_bindings = {
453
464
  mount_dir: {
454
465
  "bind": self.workdir,
@@ -458,7 +469,16 @@ class SandboxManager:
458
469
  else:
459
470
  volume_bindings = {}
460
471
 
461
- _id, ports, ip = self.client.create(
472
+ if self.readonly_mounts:
473
+ for host_path, container_path in self.readonly_mounts.items():
474
+ if not os.path.isabs(host_path):
475
+ host_path = os.path.abspath(host_path)
476
+ volume_bindings[host_path] = {
477
+ "bind": container_path,
478
+ "mode": "ro",
479
+ }
480
+
481
+ _id, ports, ip, *rest = self.client.create(
462
482
  image,
463
483
  name=container_name,
464
484
  ports=["80/tcp"], # Nginx
@@ -470,6 +490,12 @@ class SandboxManager:
470
490
  runtime_config=config.runtime_config,
471
491
  )
472
492
 
493
+ http_protocol = "http"
494
+ ws_protocol = "ws"
495
+ if rest and rest[0] == "https":
496
+ http_protocol = "https"
497
+ ws_protocol = "wss"
498
+
473
499
  if _id is None:
474
500
  return None
475
501
 
@@ -487,22 +513,23 @@ class SandboxManager:
487
513
  session_id=session_id,
488
514
  container_id=_id,
489
515
  container_name=container_name,
490
- base_url=f"http://{ip}:{ports[0]}/fastapi",
491
- browser_url=f"http://{ip}:{ports[0]}/steel-api"
516
+ base_url=f"{http_protocol}://{ip}:{ports[0]}/fastapi",
517
+ browser_url=f"{http_protocol}://{ip}:{ports[0]}/steel-api"
492
518
  f"/{runtime_token}",
493
- front_browser_ws=f"ws://{ip}:"
519
+ front_browser_ws=f"{ws_protocol}://{ip}:"
494
520
  f"{ports[0]}/steel-api/"
495
521
  f"{runtime_token}/v1/sessions/cast",
496
- client_browser_ws=f"ws://{ip}:"
522
+ client_browser_ws=f"{ws_protocol}://{ip}:"
497
523
  f"{ports[0]}/steel-api/{runtime_token}/&sessionId"
498
524
  f"={BROWSER_SESSION_ID}",
499
- artifacts_sio=f"http://{ip}:{ports[0]}/v1",
525
+ artifacts_sio=f"{http_protocol}://{ip}:{ports[0]}/v1",
500
526
  ports=[ports[0]],
501
527
  mount_dir=str(mount_dir),
502
528
  storage_path=storage_path,
503
529
  runtime_token=runtime_token,
504
530
  version=image,
505
531
  )
532
+
506
533
  # Register in mapping
507
534
  self.container_mapping.set(
508
535
  container_model.container_name,
@@ -632,7 +659,7 @@ class SandboxManager:
632
659
  self._generate_container_key(identity),
633
660
  )
634
661
  if container_model is None:
635
- return None
662
+ raise RuntimeError(f"No container found with id: {identity}.")
636
663
  if hasattr(container_model, "model_dump_json"):
637
664
  container_model = container_model.model_dump_json()
638
665
 
@@ -20,6 +20,7 @@ from ...manager.server.models import (
20
20
  )
21
21
  from ...manager.sandbox_manager import SandboxManager
22
22
  from ...model.manager_config import SandboxManagerEnvConfig
23
+ from ...utils import dynamic_import
23
24
  from ....version import __version__
24
25
 
25
26
  # Configure logging
@@ -61,6 +62,7 @@ def get_config() -> SandboxManagerEnvConfig:
61
62
  redis_enabled=settings.REDIS_ENABLED,
62
63
  container_deployment=settings.CONTAINER_DEPLOYMENT,
63
64
  default_mount_dir=settings.DEFAULT_MOUNT_DIR,
65
+ readonly_mounts=settings.READONLY_MOUNTS,
64
66
  storage_folder=settings.STORAGE_FOLDER,
65
67
  port_range=settings.PORT_RANGE,
66
68
  pool_size=settings.POOL_SIZE,
@@ -77,6 +79,18 @@ def get_config() -> SandboxManagerEnvConfig:
77
79
  redis_container_pool_key=settings.REDIS_CONTAINER_POOL_KEY,
78
80
  k8s_namespace=settings.K8S_NAMESPACE,
79
81
  kubeconfig_path=settings.KUBECONFIG_PATH,
82
+ agent_run_access_key_id=settings.AGENT_RUN_ACCESS_KEY_ID,
83
+ agent_run_access_key_secret=settings.AGENT_RUN_ACCESS_KEY_SECRET,
84
+ agent_run_account_id=settings.AGENT_RUN_ACCOUNT_ID,
85
+ agent_run_region_id=settings.AGENT_RUN_REGION_ID,
86
+ agent_run_cpu=settings.AGENT_RUN_CPU,
87
+ agent_run_memory=settings.AGENT_RUN_MEMORY,
88
+ agent_run_vpc_id=settings.AGENT_RUN_VPC_ID,
89
+ agent_run_vswitch_ids=settings.AGENT_RUN_VSWITCH_IDS,
90
+ agent_run_security_group_id=settings.AGENT_RUN_SECURITY_GROUP_ID,
91
+ agent_run_prefix=settings.AGENT_RUN_PREFIX,
92
+ agentrun_log_project=settings.AGENT_RUN_LOG_PROJECT,
93
+ agentrun_log_store=settings.AGENT_RUN_LOG_STORE,
80
94
  )
81
95
  return _config
82
96
 
@@ -311,8 +325,21 @@ def main():
311
325
  default="INFO",
312
326
  help="Set the logging level (default: INFO)",
313
327
  )
328
+
329
+ parser.add_argument(
330
+ "--extension",
331
+ action="append",
332
+ help="Path to a Python file or module name to load as an extension",
333
+ )
334
+
314
335
  args = parser.parse_args()
315
336
 
337
+ if args.extension:
338
+ for ext in args.extension:
339
+ logger.info(f"Loading extension: {ext}")
340
+ mod = dynamic_import(ext)
341
+ logger.info(f"Extension loaded: {mod.__name__}")
342
+
316
343
  # Setup logging based on command line argument
317
344
  setup_logging(args.log_level)
318
345
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import os
3
- from typing import Optional, Tuple, Literal
3
+ from typing import Optional, Tuple, Literal, Dict
4
4
  from pydantic_settings import BaseSettings
5
5
  from pydantic import field_validator, ConfigDict
6
6
  from dotenv import load_dotenv
@@ -21,8 +21,18 @@ class Settings(BaseSettings):
21
21
  POOL_SIZE: int = 1
22
22
  AUTO_CLEANUP: bool = True
23
23
  CONTAINER_PREFIX_KEY: str = "runtime_sandbox_container_"
24
- CONTAINER_DEPLOYMENT: Literal["docker", "cloud", "k8s"] = "docker"
24
+ CONTAINER_DEPLOYMENT: Literal[
25
+ "docker",
26
+ "cloud",
27
+ "k8s",
28
+ "agentrun",
29
+ ] = "docker"
25
30
  DEFAULT_MOUNT_DIR: str = "sessions_mount_dir"
31
+ # Read-only mounts (host_path -> container_path)
32
+ # Example in .env:
33
+ # READONLY_MOUNTS={"\/opt\/shared": "\/mnt\/shared", "\/etc\/timezone":
34
+ # "\/etc\/timezone"}
35
+ READONLY_MOUNTS: Optional[Dict[str, str]] = None
26
36
  STORAGE_FOLDER: str = "runtime_sandbox_storage"
27
37
  PORT_RANGE: Tuple[int, int] = (49152, 59152)
28
38
 
@@ -47,6 +57,24 @@ class Settings(BaseSettings):
47
57
  K8S_NAMESPACE: str = "default"
48
58
  KUBECONFIG_PATH: Optional[str] = None
49
59
 
60
+ # AgentRun settings
61
+ AGENT_RUN_ACCOUNT_ID: Optional[str] = None
62
+ AGENT_RUN_ACCESS_KEY_ID: Optional[str] = None
63
+ AGENT_RUN_ACCESS_KEY_SECRET: Optional[str] = None
64
+ AGENT_RUN_REGION_ID: str = "cn-hangzhou"
65
+
66
+ AGENT_RUN_CPU: float = 2.0
67
+ AGENT_RUN_MEMORY: int = 2048
68
+
69
+ AGENT_RUN_VPC_ID: Optional[str] = None
70
+ AGENT_RUN_VSWITCH_IDS: Optional[list[str]] = None
71
+ AGENT_RUN_SECURITY_GROUP_ID: Optional[str] = None
72
+
73
+ AGENT_RUN_PREFIX: str = "agentscope-sandbox"
74
+
75
+ AGENT_RUN_LOG_PROJECT: Optional[str] = None
76
+ AGENT_RUN_LOG_STORE: Optional[str] = None
77
+
50
78
  model_config = ConfigDict(
51
79
  case_sensitive=True,
52
80
  extra="allow",
@@ -44,7 +44,7 @@ class ContainerModel(BaseModel):
44
44
  ...,
45
45
  description="Socketio URL for the artifacts used by frontend",
46
46
  )
47
- ports: List[int] = Field(
47
+ ports: List[int | str] = Field(
48
48
  ...,
49
49
  description="List of occupied port numbers",
50
50
  )