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.
Files changed (90) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent.py +447 -0
  2. agentscope_runtime/engine/agents/agno_agent.py +19 -18
  3. agentscope_runtime/engine/agents/autogen_agent.py +13 -8
  4. agentscope_runtime/engine/agents/utils.py +53 -0
  5. agentscope_runtime/engine/deployers/__init__.py +0 -13
  6. agentscope_runtime/engine/deployers/local_deployer.py +501 -356
  7. agentscope_runtime/engine/helpers/helper.py +60 -41
  8. agentscope_runtime/engine/runner.py +11 -36
  9. agentscope_runtime/engine/schemas/agent_schemas.py +2 -70
  10. agentscope_runtime/engine/services/sandbox_service.py +62 -70
  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/tablestore_service_utils.py +352 -0
  15. agentscope_runtime/sandbox/__init__.py +2 -0
  16. agentscope_runtime/sandbox/box/base/__init__.py +4 -0
  17. agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -3
  18. agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
  19. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +8 -13
  20. agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
  21. agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
  22. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +8 -6
  23. agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
  24. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +80 -0
  25. agentscope_runtime/sandbox/box/sandbox.py +5 -2
  26. agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
  27. agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
  28. agentscope_runtime/sandbox/box/training_box/training_box.py +10 -15
  29. agentscope_runtime/sandbox/build.py +143 -58
  30. agentscope_runtime/sandbox/client/http_client.py +43 -49
  31. agentscope_runtime/sandbox/client/training_client.py +0 -1
  32. agentscope_runtime/sandbox/constant.py +24 -1
  33. agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
  34. agentscope_runtime/sandbox/custom/example.py +2 -2
  35. agentscope_runtime/sandbox/enums.py +1 -0
  36. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +11 -6
  37. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
  38. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  39. agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1098 -0
  40. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +33 -205
  41. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +8 -555
  42. agentscope_runtime/sandbox/manager/sandbox_manager.py +187 -88
  43. agentscope_runtime/sandbox/manager/server/app.py +82 -14
  44. agentscope_runtime/sandbox/manager/server/config.py +50 -3
  45. agentscope_runtime/sandbox/model/container.py +6 -23
  46. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  47. agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
  48. agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
  49. agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
  50. agentscope_runtime/sandbox/utils.py +124 -0
  51. agentscope_runtime/version.py +1 -1
  52. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/METADATA +168 -77
  53. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/RECORD +59 -78
  54. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/entry_points.txt +0 -1
  55. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
  56. agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
  57. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
  58. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  59. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +0 -2886
  60. agentscope_runtime/engine/deployers/adapter/responses/response_api_agent_adapter.py +0 -51
  61. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +0 -314
  62. agentscope_runtime/engine/deployers/cli_fc_deploy.py +0 -184
  63. agentscope_runtime/engine/deployers/kubernetes_deployer.py +0 -265
  64. agentscope_runtime/engine/deployers/modelstudio_deployer.py +0 -677
  65. agentscope_runtime/engine/deployers/utils/deployment_modes.py +0 -14
  66. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +0 -8
  67. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +0 -429
  68. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +0 -240
  69. agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +0 -297
  70. agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -932
  71. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -9
  72. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +0 -504
  73. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +0 -157
  74. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +0 -268
  75. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
  76. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
  77. agentscope_runtime/engine/deployers/utils/wheel_packager.py +0 -389
  78. agentscope_runtime/engine/helpers/agent_api_builder.py +0 -651
  79. agentscope_runtime/engine/llms/__init__.py +0 -3
  80. agentscope_runtime/engine/llms/base_llm.py +0 -60
  81. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  82. agentscope_runtime/engine/schemas/embedding.py +0 -37
  83. agentscope_runtime/engine/schemas/modelstudio_llm.py +0 -310
  84. agentscope_runtime/engine/schemas/oai_llm.py +0 -538
  85. agentscope_runtime/engine/schemas/realtime.py +0 -254
  86. /agentscope_runtime/engine/{deployers/adapter/responses → services/utils}/__init__.py +0 -0
  87. /agentscope_runtime/{engine/deployers/utils → sandbox/box/gui/box}/__init__.py +0 -0
  88. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/WHEEL +0 -0
  89. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/licenses/LICENSE +0 -0
  90. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/top_level.txt +0 -0
@@ -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
@@ -8,12 +9,13 @@ import inspect
8
9
  import traceback
9
10
 
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
14
  import shortuuid
15
15
  import requests
16
16
 
17
+ from .container_clients.docker_client import DockerClient
18
+ from .container_clients.kubernetes_client import KubernetesClient
17
19
  from ..model import (
18
20
  ContainerModel,
19
21
  SandboxManagerEnvConfig,
@@ -32,8 +34,6 @@ from ..manager.storage import (
32
34
  LocalStorage,
33
35
  OSSStorage,
34
36
  )
35
- from ..manager.container_clients import DockerClient, KubernetesClient
36
- from ..constant import BROWSER_SESSION_ID
37
37
 
38
38
  logging.basicConfig(level=logging.INFO)
39
39
  logger = logging.getLogger(__name__)
@@ -85,7 +85,11 @@ class SandboxManager:
85
85
  config: Optional[SandboxManagerEnvConfig] = None,
86
86
  base_url=None,
87
87
  bearer_token=None,
88
- default_type: SandboxType | str = SandboxType.BASE,
88
+ default_type: Union[
89
+ SandboxType,
90
+ str,
91
+ List[Union[SandboxType, str]],
92
+ ] = SandboxType.BASE,
89
93
  ):
90
94
  if base_url:
91
95
  # Initialize HTTP session for remote mode with bearer token
@@ -112,17 +116,24 @@ class SandboxManager:
112
116
  default_mount_dir="sessions_mount_dir",
113
117
  )
114
118
 
115
- self.default_type = SandboxType(default_type)
119
+ # Support multi sandbox pool
120
+ if isinstance(default_type, (SandboxType, str)):
121
+ self.default_type = [SandboxType(default_type)]
122
+ else:
123
+ self.default_type = [SandboxType(x) for x in list(default_type)]
124
+
116
125
  self.workdir = "/workspace"
117
126
 
118
127
  self.config = config
119
128
  self.pool_size = self.config.pool_size
120
129
  self.prefix = self.config.container_prefix_key
121
130
  self.default_mount_dir = self.config.default_mount_dir
131
+ self.readonly_mounts = self.config.readonly_mounts
122
132
  self.storage_folder = (
123
133
  self.config.storage_folder or self.default_mount_dir
124
134
  )
125
135
 
136
+ self.pool_queues = {}
126
137
  if self.config.redis_enabled:
127
138
  import redis
128
139
 
@@ -142,13 +153,22 @@ class SandboxManager:
142
153
  ) from e
143
154
 
144
155
  self.container_mapping = RedisMapping(redis_client)
145
- self.pool_queue = RedisQueue(
156
+ self.session_mapping = RedisMapping(
146
157
  redis_client,
147
- self.config.redis_container_pool_key,
158
+ prefix="session_mapping",
148
159
  )
160
+
161
+ # Init multi sand box pool
162
+ for t in self.default_type:
163
+ queue_key = f"{self.config.redis_container_pool_key}:{t.value}"
164
+ self.pool_queues[t] = RedisQueue(redis_client, queue_key)
149
165
  else:
150
166
  self.container_mapping = InMemoryMapping()
151
- self.pool_queue = InMemoryQueue()
167
+ self.session_mapping = InMemoryMapping()
168
+
169
+ # Init multi sand box pool
170
+ for t in self.default_type:
171
+ self.pool_queues[t] = InMemoryQueue()
152
172
 
153
173
  self.container_deployment = self.config.container_deployment
154
174
 
@@ -157,6 +177,10 @@ class SandboxManager:
157
177
  self.client = DockerClient(config=self.config)
158
178
  elif self.container_deployment == "k8s":
159
179
  self.client = KubernetesClient(config=self.config)
180
+ elif self.container_deployment == "agentrun":
181
+ from .container_clients.agentrun_client import AgentRunClient
182
+
183
+ self.client = AgentRunClient(config=self.config)
160
184
  else:
161
185
  raise NotImplementedError("Not implemented")
162
186
  else:
@@ -243,24 +267,28 @@ class SandboxManager:
243
267
  """
244
268
  Init runtime pool
245
269
  """
246
- while self.pool_queue.size() < self.pool_size:
247
- try:
248
- container_name = self.create()
249
- container_model = self.container_mapping.get(container_name)
250
- if container_model:
251
- # Check the pool size again to avoid race condition
252
- if self.pool_queue.size() < self.pool_size:
253
- self.pool_queue.enqueue(container_model)
270
+ for t in self.default_type:
271
+ queue = self.pool_queues[t]
272
+ while queue.size() < self.pool_size:
273
+ try:
274
+ container_name = self.create(sandbox_type=t.value)
275
+ container_model = self.container_mapping.get(
276
+ container_name,
277
+ )
278
+ if container_model:
279
+ # Check the pool size again to avoid race condition
280
+ if queue.size() < self.pool_size:
281
+ queue.enqueue(container_model)
282
+ else:
283
+ # The pool size has reached the limit
284
+ self.release(container_name)
285
+ break
254
286
  else:
255
- # The pool size has reached the limit
256
- self.release(container_name)
287
+ logger.error("Failed to create container for pool")
257
288
  break
258
- else:
259
- logger.error("Failed to create container for pool")
289
+ except Exception as e:
290
+ logger.error(f"Error initializing runtime pool: {e}")
260
291
  break
261
- except Exception as e:
262
- logger.error(f"Error initializing runtime pool: {e}")
263
- break
264
292
 
265
293
  @remote_wrapper()
266
294
  def cleanup(self):
@@ -269,17 +297,19 @@ class SandboxManager:
269
297
  )
270
298
 
271
299
  # Clean up pool first
272
- try:
273
- while self.pool_queue.size() > 0:
274
- container_json = self.pool_queue.dequeue()
275
- if container_json:
276
- container_model = ContainerModel(**container_json)
277
- logger.debug(
278
- f"Destroy container {container_model.container_id}",
279
- )
280
- self.release(container_model.session_id)
281
- except Exception as e:
282
- logger.error(f"Error cleaning up runtime pool: {e}")
300
+ for queue in self.pool_queues.values():
301
+ try:
302
+ while queue.size() > 0:
303
+ container_json = queue.dequeue()
304
+ if container_json:
305
+ container_model = ContainerModel(**container_json)
306
+ logger.debug(
307
+ f"Destroy container"
308
+ f" {container_model.container_id}",
309
+ )
310
+ self.release(container_model.session_id)
311
+ except Exception as e:
312
+ logger.error(f"Error cleaning up runtime pool: {e}")
283
313
 
284
314
  # Clean up rest container
285
315
  for key in self.container_mapping.scan(self.prefix):
@@ -297,11 +327,15 @@ class SandboxManager:
297
327
  )
298
328
 
299
329
  @remote_wrapper()
300
- def create_from_pool(self, sandbox_type=None):
330
+ def create_from_pool(self, sandbox_type=None, meta: Optional[Dict] = None):
301
331
  """Try to get a container from runtime pool"""
302
- sandbox_type = SandboxType(sandbox_type)
303
- if sandbox_type != self.default_type:
304
- return self.create(sandbox_type=sandbox_type.value)
332
+ # If not specified, use the first one
333
+ sandbox_type = SandboxType(sandbox_type or self.default_type[0])
334
+
335
+ if sandbox_type not in self.pool_queues:
336
+ return self.create(sandbox_type=sandbox_type.value, meta=meta)
337
+
338
+ queue = self.pool_queues[sandbox_type]
305
339
 
306
340
  cnt = 0
307
341
  try:
@@ -313,17 +347,17 @@ class SandboxManager:
313
347
  cnt += 1
314
348
 
315
349
  # Add a new one to container
316
- container_name = self.create()
350
+ container_name = self.create(sandbox_type=sandbox_type)
317
351
  new_container_model = self.container_mapping.get(
318
352
  container_name,
319
353
  )
320
354
 
321
355
  if new_container_model:
322
- self.pool_queue.enqueue(
356
+ queue.enqueue(
323
357
  new_container_model,
324
358
  )
325
359
 
326
- container_json = self.pool_queue.dequeue()
360
+ container_json = queue.dequeue()
327
361
 
328
362
  if not container_json:
329
363
  raise RuntimeError(
@@ -331,6 +365,29 @@ class SandboxManager:
331
365
  )
332
366
 
333
367
  container_model = ContainerModel(**container_json)
368
+
369
+ # Add meta field to container
370
+ if meta and not container_model.meta:
371
+ container_model.meta = meta
372
+ self.container_mapping.set(
373
+ container_model.container_name,
374
+ container_model.model_dump(),
375
+ )
376
+ # Update session mapping
377
+ if "session_ctx_id" in meta:
378
+ env_ids = (
379
+ self.session_mapping.get(
380
+ meta["session_ctx_id"],
381
+ )
382
+ or []
383
+ )
384
+ if container_model.container_name not in env_ids:
385
+ env_ids.append(container_model.container_name)
386
+ self.session_mapping.set(
387
+ meta["session_ctx_id"],
388
+ env_ids,
389
+ )
390
+
334
391
  logger.debug(
335
392
  f"Retrieved container from pool:"
336
393
  f" {container_model.session_id}",
@@ -339,7 +396,7 @@ class SandboxManager:
339
396
  if (
340
397
  container_model.version
341
398
  != SandboxRegistry.get_image_by_type(
342
- self.default_type,
399
+ sandbox_type,
343
400
  )
344
401
  ):
345
402
  logger.warning(
@@ -370,24 +427,25 @@ class SandboxManager:
370
427
  self.release(container_model.session_id)
371
428
 
372
429
  except Exception as e:
373
- logger.error(
374
- f"Error getting container from pool, create a "
375
- f"new one. {e}: {traceback.format_exc()}",
430
+ logger.warning(
431
+ "Error getting container from pool, create a new one.",
376
432
  )
433
+ logger.debug(f"{e}: {traceback.format_exc()}")
377
434
  return self.create()
378
435
 
379
436
  @remote_wrapper()
380
437
  def create(
381
438
  self,
382
439
  sandbox_type=None,
383
- mount_dir=None,
440
+ mount_dir=None, # TODO: remove to avoid leaking
384
441
  storage_path=None,
385
442
  environment: Optional[Dict] = None,
443
+ meta: Optional[Dict] = None,
386
444
  ):
387
445
  if sandbox_type is not None:
388
446
  target_sandbox_type = SandboxType(sandbox_type)
389
447
  else:
390
- target_sandbox_type = self.default_type
448
+ target_sandbox_type = self.default_type[0]
391
449
 
392
450
  image = SandboxRegistry.get_image_by_type(target_sandbox_type)
393
451
  if not image:
@@ -396,7 +454,7 @@ class SandboxManager:
396
454
  f"using default",
397
455
  )
398
456
  image = SandboxRegistry.get_image_by_type(
399
- self.default_type,
457
+ self.default_type[0],
400
458
  )
401
459
 
402
460
  # TODO: enable for timeout for the sandbox (auto cleanup)
@@ -422,7 +480,7 @@ class SandboxManager:
422
480
  mount_dir = os.path.join(self.default_mount_dir, session_id)
423
481
  os.makedirs(mount_dir, exist_ok=True)
424
482
 
425
- if mount_dir:
483
+ if mount_dir and self.container_deployment != "agentrun":
426
484
  if not os.path.isabs(mount_dir):
427
485
  mount_dir = os.path.abspath(mount_dir)
428
486
 
@@ -433,12 +491,16 @@ class SandboxManager:
433
491
  session_id,
434
492
  )
435
493
 
436
- if mount_dir and storage_path:
494
+ if (
495
+ mount_dir
496
+ and storage_path
497
+ and self.container_deployment != "agentrun"
498
+ ):
437
499
  self.storage.download_folder(storage_path, mount_dir)
438
500
 
501
+ # Check for an existing container with the same name
502
+ container_name = self._generate_container_key(session_id)
439
503
  try:
440
- # Check for an existing container with the same name
441
- container_name = self._generate_container_key(session_id)
442
504
  if self.client.inspect(container_name):
443
505
  raise ValueError(
444
506
  f"Container with name {container_name} already exists.",
@@ -448,7 +510,7 @@ class SandboxManager:
448
510
  runtime_token = secrets.token_hex(16)
449
511
 
450
512
  # Prepare volume bindings if a mount directory is provided
451
- if mount_dir:
513
+ if mount_dir and self.container_deployment != "agentrun":
452
514
  volume_bindings = {
453
515
  mount_dir: {
454
516
  "bind": self.workdir,
@@ -458,7 +520,16 @@ class SandboxManager:
458
520
  else:
459
521
  volume_bindings = {}
460
522
 
461
- _id, ports, ip = self.client.create(
523
+ if self.readonly_mounts:
524
+ for host_path, container_path in self.readonly_mounts.items():
525
+ if not os.path.isabs(host_path):
526
+ host_path = os.path.abspath(host_path)
527
+ volume_bindings[host_path] = {
528
+ "bind": container_path,
529
+ "mode": "ro",
530
+ }
531
+
532
+ _id, ports, ip, *rest = self.client.create(
462
533
  image,
463
534
  name=container_name,
464
535
  ports=["80/tcp"], # Nginx
@@ -470,6 +541,10 @@ class SandboxManager:
470
541
  runtime_config=config.runtime_config,
471
542
  )
472
543
 
544
+ http_protocol = "http"
545
+ if rest and rest[0] == "https":
546
+ http_protocol = "https"
547
+
473
548
  if _id is None:
474
549
  return None
475
550
 
@@ -487,37 +562,43 @@ class SandboxManager:
487
562
  session_id=session_id,
488
563
  container_id=_id,
489
564
  container_name=container_name,
490
- base_url=f"http://{ip}:{ports[0]}/fastapi",
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",
565
+ url=f"{http_protocol}://{ip}:{ports[0]}",
500
566
  ports=[ports[0]],
501
567
  mount_dir=str(mount_dir),
502
568
  storage_path=storage_path,
503
569
  runtime_token=runtime_token,
504
570
  version=image,
571
+ meta=meta or {},
505
572
  )
573
+
506
574
  # Register in mapping
507
575
  self.container_mapping.set(
508
576
  container_model.container_name,
509
577
  container_model.model_dump(),
510
578
  )
511
579
 
580
+ # Build mapping session_ctx_id to container_name
581
+ if meta and "session_ctx_id" in meta:
582
+ env_ids = (
583
+ self.session_mapping.get(
584
+ meta["session_ctx_id"],
585
+ )
586
+ or []
587
+ )
588
+ env_ids.append(container_model.container_name)
589
+ self.session_mapping.set(meta["session_ctx_id"], env_ids)
590
+
512
591
  logger.debug(
513
592
  f"Created container {container_name}"
514
593
  f":{container_model.model_dump()}",
515
594
  )
516
595
  return container_name
517
596
  except Exception as e:
518
- logger.error(
519
- f"Failed to create container: {e}: {traceback.format_exc()}",
597
+ logger.warning(
598
+ f"Failed to create container: {e}",
520
599
  )
600
+ logger.debug(f"{traceback.format_exc()}")
601
+ self.release(identity=container_name)
521
602
  return None
522
603
 
523
604
  @remote_wrapper()
@@ -536,6 +617,20 @@ class SandboxManager:
536
617
  # remove key in mapping before we remove container
537
618
  self.container_mapping.delete(container_json.get("container_name"))
538
619
 
620
+ # remove key in mapping
621
+ session_ctx_id = container_info.meta.get("session_ctx_id")
622
+ if session_ctx_id:
623
+ env_ids = self.session_mapping.get(session_ctx_id) or []
624
+ env_ids = [
625
+ eid
626
+ for eid in env_ids
627
+ if eid != container_info.container_name
628
+ ]
629
+ if env_ids:
630
+ self.session_mapping.set(session_ctx_id, env_ids)
631
+ else:
632
+ self.session_mapping.delete(session_ctx_id)
633
+
539
634
  self.client.stop(container_info.container_id, timeout=1)
540
635
  self.client.remove(container_info.container_id, force=True)
541
636
 
@@ -550,10 +645,10 @@ class SandboxManager:
550
645
 
551
646
  return True
552
647
  except Exception as e:
553
- logger.error(
554
- f"Failed to destroy container: {e}: "
555
- f"{traceback.format_exc()}",
648
+ logger.warning(
649
+ f"Failed to destroy container: {e}",
556
650
  )
651
+ logger.debug(f"{traceback.format_exc()}")
557
652
  return False
558
653
 
559
654
  @remote_wrapper()
@@ -632,7 +727,7 @@ class SandboxManager:
632
727
  self._generate_container_key(identity),
633
728
  )
634
729
  if container_model is None:
635
- return None
730
+ raise RuntimeError(f"No container found with id: {identity}.")
636
731
  if hasattr(container_model, "model_dump_json"):
637
732
  container_model = container_model.model_dump_json()
638
733
 
@@ -640,35 +735,26 @@ class SandboxManager:
640
735
 
641
736
  def _establish_connection(self, identity):
642
737
  container_model = ContainerModel(**self.get_info(identity))
643
- # TODO: make this more robust
644
- enable_browser = "browser" in container_model.version
645
738
 
646
739
  # TODO: remake docker name
647
740
  if (
648
741
  "sandbox-appworld" in container_model.version
649
742
  or "sandbox-bfcl" in container_model.version
650
743
  ):
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
744
  return TrainingSandboxClient(
664
- base_url=base_url,
745
+ base_url=container_model.url,
665
746
  ).__enter__()
666
747
 
667
748
  return SandboxHttpClient(
668
749
  container_model,
669
- enable_browser=enable_browser,
670
750
  ).__enter__()
671
751
 
752
+ @remote_wrapper()
753
+ def check_health(self, identity):
754
+ """List tool"""
755
+ client = self._establish_connection(identity)
756
+ return client.check_health()
757
+
672
758
  @remote_wrapper()
673
759
  def list_tools(self, identity, tool_type=None, **kwargs):
674
760
  """List tool"""
@@ -691,3 +777,16 @@ class SandboxManager:
691
777
  server_configs=server_configs,
692
778
  overwrite=overwrite,
693
779
  )
780
+
781
+ @remote_wrapper()
782
+ def get_session_mapping(self, session_ctx_id: str) -> list:
783
+ """Get all container names bound to a session context"""
784
+ return self.session_mapping.get(session_ctx_id) or []
785
+
786
+ @remote_wrapper()
787
+ def list_session_keys(self) -> list:
788
+ """Return all session_ctx_id keys currently in mapping"""
789
+ session_keys = []
790
+ for key in self.session_mapping.scan():
791
+ session_keys.append(key)
792
+ return session_keys