agentscope-runtime 0.1.5b1__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.5b1.dist-info → agentscope_runtime-0.1.6.dist-info}/METADATA +168 -77
  53. {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.1.6.dist-info}/RECORD +59 -78
  54. {agentscope_runtime-0.1.5b1.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 -143
  63. agentscope_runtime/engine/deployers/kubernetes_deployer.py +0 -265
  64. agentscope_runtime/engine/deployers/modelstudio_deployer.py +0 -626
  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.5b1.dist-info → agentscope_runtime-0.1.6.dist-info}/WHEEL +0 -0
  89. {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.1.6.dist-info}/licenses/LICENSE +0 -0
  90. {agentscope_runtime-0.1.5b1.dist-info → agentscope_runtime-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ import logging
2
3
 
3
4
  from agentscope_runtime.engine import Runner
4
5
  from agentscope_runtime.engine.schemas.agent_schemas import (
@@ -13,22 +14,28 @@ from agentscope_runtime.engine.services.environment_manager import (
13
14
  create_environment_manager,
14
15
  )
15
16
 
17
+ logger = logging.getLogger(__name__)
18
+
16
19
 
17
20
  async def simple_call_agent(query, runner, user_id=None, session_id=None):
18
- request = AgentRequest(
19
- input=[
20
- {
21
- "role": "user",
22
- "content": [
23
- {
24
- "type": "text",
25
- "text": query,
26
- },
27
- ],
28
- },
29
- ],
30
- session_id=session_id,
31
- )
21
+ if isinstance(query, str):
22
+ request = AgentRequest(
23
+ input=[
24
+ {
25
+ "role": "user",
26
+ "content": [
27
+ {
28
+ "type": "text",
29
+ "text": query,
30
+ },
31
+ ],
32
+ },
33
+ ],
34
+ session_id=session_id,
35
+ )
36
+ else:
37
+ request = query
38
+
32
39
  all_result = ""
33
40
  async for message in runner.stream_query(
34
41
  user_id=user_id,
@@ -58,6 +65,23 @@ async def simple_call_agent_direct(agent, query):
58
65
 
59
66
 
60
67
  async def simple_call_agent_tool(agent, query):
68
+ if isinstance(query, str):
69
+ request = AgentRequest(
70
+ input=[
71
+ {
72
+ "role": "user",
73
+ "content": [
74
+ {
75
+ "type": "text",
76
+ "text": query,
77
+ },
78
+ ],
79
+ },
80
+ ],
81
+ )
82
+ else:
83
+ request = query
84
+
61
85
  all_result = ""
62
86
  async with create_context_manager() as context_manager:
63
87
  async with create_environment_manager() as environment_manager:
@@ -67,20 +91,6 @@ async def simple_call_agent_tool(agent, query):
67
91
  environment_manager=environment_manager,
68
92
  )
69
93
 
70
- request = AgentRequest(
71
- input=[
72
- {
73
- "role": "user",
74
- "content": [
75
- {
76
- "type": "text",
77
- "text": query,
78
- },
79
- ],
80
- },
81
- ],
82
- )
83
-
84
94
  async for message in runner.stream_query(
85
95
  request=request,
86
96
  ):
@@ -94,12 +104,7 @@ async def simple_call_agent_tool(agent, query):
94
104
 
95
105
 
96
106
  async def simple_call_agent_tool_auto_lifecycle(agent, query):
97
- all_result = ""
98
- async with Runner(
99
- agent=agent,
100
- context_manager=create_context_manager(),
101
- environment_manager=create_environment_manager(),
102
- ) as runner:
107
+ if isinstance(query, str):
103
108
  request = AgentRequest(
104
109
  input=[
105
110
  {
@@ -113,7 +118,15 @@ async def simple_call_agent_tool_auto_lifecycle(agent, query):
113
118
  },
114
119
  ],
115
120
  )
121
+ else:
122
+ request = query
116
123
 
124
+ all_result = ""
125
+ async with Runner(
126
+ agent=agent,
127
+ context_manager=create_context_manager(),
128
+ environment_manager=create_environment_manager(),
129
+ ) as runner:
117
130
  async for message in runner.stream_query(
118
131
  request=request,
119
132
  ):
@@ -127,13 +140,7 @@ async def simple_call_agent_tool_auto_lifecycle(agent, query):
127
140
 
128
141
 
129
142
  async def simple_call_agent_tool_wo_env(agent, query):
130
- all_result = ""
131
- async with create_context_manager() as context_manager:
132
- runner = Runner(
133
- agent=agent,
134
- context_manager=context_manager,
135
- )
136
-
143
+ if isinstance(query, str):
137
144
  request = AgentRequest(
138
145
  input=[
139
146
  {
@@ -147,6 +154,15 @@ async def simple_call_agent_tool_wo_env(agent, query):
147
154
  },
148
155
  ],
149
156
  )
157
+ else:
158
+ request = query
159
+
160
+ all_result = ""
161
+ async with create_context_manager() as context_manager:
162
+ runner = Runner(
163
+ agent=agent,
164
+ context_manager=context_manager,
165
+ )
150
166
 
151
167
  async for message in runner.stream_query(
152
168
  request=request,
@@ -157,4 +173,7 @@ async def simple_call_agent_tool_wo_env(agent, query):
157
173
  and RunStatus.Completed == message.status
158
174
  ):
159
175
  all_result = message.content[0].text
176
+
177
+ logger.debug(message.model_dump())
178
+
160
179
  return all_result
@@ -1,26 +1,23 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import uuid
3
+ from typing import Optional, List, AsyncGenerator, Any
3
4
  from contextlib import AsyncExitStack
4
- from typing import Optional, List, AsyncGenerator, Any, Union, Dict
5
5
 
6
6
  from openai.types.chat import ChatCompletion
7
7
 
8
- from agentscope_runtime.engine.deployers.utils.service_utils import (
9
- ServicesConfig,
10
- )
8
+ from .deployers.adapter.protocol_adapter import ProtocolAdapter
11
9
  from .agents import Agent
10
+ from .schemas.context import Context
12
11
  from .deployers import (
13
12
  DeployManager,
14
13
  LocalDeployManager,
15
14
  )
16
- from .deployers.adapter.protocol_adapter import ProtocolAdapter
17
15
  from .schemas.agent_schemas import (
18
16
  Event,
19
17
  AgentRequest,
20
18
  RunStatus,
21
19
  AgentResponse,
22
20
  )
23
- from .schemas.context import Context
24
21
  from .services.context_manager import ContextManager
25
22
  from .services.environment_manager import EnvironmentManager
26
23
  from .tracing import TraceType
@@ -80,57 +77,37 @@ class Runner:
80
77
  endpoint_path: str = "/process",
81
78
  stream: bool = True,
82
79
  protocol_adapters: Optional[list[ProtocolAdapter]] = None,
83
- requirements: Optional[Union[str, List[str]]] = None,
84
- extra_packages: Optional[List[str]] = None,
85
- base_image: str = "python:3.9-slim",
86
- environment: Optional[Dict[str, str]] = None,
87
- runtime_config: Optional[Dict] = None,
88
- services_config: Optional[Union[ServicesConfig, dict]] = None,
89
- **kwargs,
90
80
  ):
91
81
  """
92
82
  Deploys the agent as a service.
93
83
 
94
84
  Args:
85
+ protocol_adapters: protocol adapters
95
86
  deploy_manager: Deployment manager to handle service deployment
96
87
  endpoint_path: API endpoint path for the processing function
97
88
  stream: If start a streaming service
98
- protocol_adapters: protocol adapters
99
- requirements: PyPI dependencies
100
- extra_packages: User code directory/file path
101
- base_image: Docker base image (for containerized deployment)
102
- environment: Environment variables dict
103
- runtime_config: Runtime configuration dict
104
- services_config: Services configuration dict
105
- **kwargs: Additional arguments passed to deployment manager
106
89
  Returns:
107
90
  URL of the deployed service
108
91
 
109
92
  Raises:
110
93
  RuntimeError: If deployment fails
111
94
  """
95
+ if stream:
96
+ deploy_func = self.stream_query
97
+ else:
98
+ deploy_func = self.query
112
99
  deploy_result = await deploy_manager.deploy(
113
- runner=self,
100
+ deploy_func,
114
101
  endpoint_path=endpoint_path,
115
- stream=stream,
116
102
  protocol_adapters=protocol_adapters,
117
- requirements=requirements,
118
- extra_packages=extra_packages,
119
- base_image=base_image,
120
- environment=environment,
121
- runtime_config=runtime_config,
122
- services_config=services_config,
123
- **kwargs,
124
103
  )
125
-
126
- # TODO: add redis or other persistant method
127
104
  self._deploy_managers[deploy_manager.deploy_id] = deploy_result
128
105
  return deploy_result
129
106
 
130
107
  @trace(TraceType.AGENT_STEP)
131
108
  async def stream_query( # pylint:disable=unused-argument
132
109
  self,
133
- request: Union[AgentRequest, dict],
110
+ request: AgentRequest,
134
111
  user_id: Optional[str] = None,
135
112
  tools: Optional[List] = None,
136
113
  **kwargs: Any,
@@ -138,9 +115,6 @@ class Runner:
138
115
  """
139
116
  Streams the agent.
140
117
  """
141
- if isinstance(request, dict):
142
- request = AgentRequest(**request)
143
-
144
118
  response = AgentResponse()
145
119
  yield response
146
120
 
@@ -221,6 +195,7 @@ class Runner:
221
195
  """
222
196
  Streams the agent.
223
197
  """
198
+ # TODO: fix this @zhicheng
224
199
  return self._agent.query(message, session_id)
225
200
 
226
201
  # TODO: should be sync method?
@@ -27,7 +27,6 @@ class MessageType:
27
27
  MCP_APPROVAL_REQUEST = "mcp_approval_request"
28
28
  MCP_TOOL_CALL = "mcp_call"
29
29
  MCP_APPROVAL_RESPONSE = "mcp_approval_response"
30
- REASONING = "reasoning"
31
30
  HEARTBEAT = "heartbeat"
32
31
  ERROR = "error"
33
32
 
@@ -46,8 +45,6 @@ class ContentType:
46
45
  DATA = "data"
47
46
  IMAGE = "image"
48
47
  AUDIO = "audio"
49
- FILE = "file"
50
- REFUSAL = "refusal"
51
48
 
52
49
 
53
50
  class Role:
@@ -69,8 +66,6 @@ class RunStatus:
69
66
  Failed = "failed"
70
67
  Rejected = "rejected"
71
68
  Unknown = "unknown"
72
- Queued = "queued"
73
- Incomplete = "incomplete"
74
69
 
75
70
 
76
71
  class FunctionParameters(BaseModel):
@@ -287,63 +282,6 @@ class DataContent(Content):
287
282
  """The data content."""
288
283
 
289
284
 
290
- class AudioContent(Content):
291
- type: Literal[ContentType.AUDIO] = ContentType.AUDIO
292
- """The type of the content part."""
293
-
294
- data: Optional[str] = None
295
- """The audio data details."""
296
-
297
- format: Optional[str] = None
298
- """
299
- The format of the audio data.
300
- """
301
-
302
-
303
- class FileContent(Content):
304
- type: Literal[ContentType.FILE] = ContentType.FILE
305
- """The type of the content part."""
306
-
307
- file_url: Optional[str] = None
308
- """The file URL details."""
309
-
310
- file_id: Optional[str] = None
311
- """The file ID details."""
312
-
313
- filename: Optional[str] = None
314
- """The file name details."""
315
-
316
- file_data: Optional[str] = None
317
- """The file data details."""
318
-
319
-
320
- class RefusalContent(Content):
321
- type: Literal[ContentType.REFUSAL] = ContentType.REFUSAL
322
- """The type of the content part."""
323
-
324
- refusal: Optional[str] = None
325
- """The refusal content."""
326
-
327
-
328
- class ToolCall(BaseModel):
329
- arguments: str
330
- """A JSON string of the arguments to pass to the function."""
331
-
332
- call_id: str
333
- """The unique ID of the function tool call generated by the model."""
334
-
335
- name: str
336
- """The name of the function to run."""
337
-
338
-
339
- class ToolCallOutput(BaseModel):
340
- call_id: str
341
- """The unique ID of the function tool call generated by the model."""
342
-
343
- output: str
344
- """A JSON string of the output of the function tool call."""
345
-
346
-
347
285
  AgentRole: TypeAlias = Literal[
348
286
  Role.ASSISTANT,
349
287
  Role.SYSTEM,
@@ -351,15 +289,9 @@ AgentRole: TypeAlias = Literal[
351
289
  Role.TOOL,
352
290
  ]
353
291
 
292
+
354
293
  AgentContent = Annotated[
355
- Union[
356
- TextContent,
357
- ImageContent,
358
- DataContent,
359
- AudioContent,
360
- FileContent,
361
- RefusalContent,
362
- ],
294
+ Union[TextContent, ImageContent, DataContent],
363
295
  Field(discriminator="type"),
364
296
  ]
365
297
 
@@ -2,27 +2,17 @@
2
2
  # pylint: disable=too-many-branches
3
3
  from typing import List
4
4
 
5
- try:
6
- from ...sandbox.enums import SandboxType
7
- from ...sandbox.manager import SandboxManager
8
- from ...sandbox.registry import SandboxRegistry
9
- from ...sandbox.tools.mcp_tool import MCPTool
10
- from ...sandbox.tools.sandbox_tool import SandboxTool
11
- from ...sandbox.tools.function_tool import FunctionTool
12
- except ImportError:
13
- pass
14
-
5
+ from ...sandbox.enums import SandboxType
6
+ from ...sandbox.manager import SandboxManager
7
+ from ...sandbox.registry import SandboxRegistry
8
+ from ...sandbox.tools.mcp_tool import MCPTool
9
+ from ...sandbox.tools.sandbox_tool import SandboxTool
10
+ from ...sandbox.tools.function_tool import FunctionTool
15
11
  from ...engine.services.base import ServiceWithLifecycleManager
16
12
 
17
13
 
18
14
  class SandboxService(ServiceWithLifecycleManager):
19
15
  def __init__(self, base_url=None, bearer_token=None):
20
- if SandboxManager is None:
21
- raise ImportError(
22
- "SandboxManager is not available. "
23
- "Please install agentscope-runtime[sandbox]",
24
- )
25
-
26
16
  self.manager_api = SandboxManager(
27
17
  base_url=base_url,
28
18
  bearer_token=bearer_token,
@@ -30,18 +20,20 @@ class SandboxService(ServiceWithLifecycleManager):
30
20
 
31
21
  self.base_url = base_url
32
22
  self.bearer_token = bearer_token
33
- self.sandbox_type_set = set(item.value for item in SandboxType)
34
-
35
- self.session_mapping = {}
36
23
 
37
24
  async def start(self) -> None:
38
25
  pass
39
26
 
40
27
  async def stop(self) -> None:
41
28
  # Release all environments
42
- for _, env_ids in self.session_mapping.items():
43
- for env_id in env_ids:
44
- self.manager_api.release(env_id)
29
+ session_keys = self.manager_api.list_session_keys()
30
+
31
+ if session_keys:
32
+ for session_ctx_id in session_keys:
33
+ env_ids = self.manager_api.get_session_mapping(session_ctx_id)
34
+ if env_ids:
35
+ for env_id in env_ids:
36
+ self.manager_api.release(env_id)
45
37
 
46
38
  if self.base_url is None:
47
39
  # Embedded mode
@@ -57,61 +49,52 @@ class SandboxService(ServiceWithLifecycleManager):
57
49
  env_types=None,
58
50
  tools=None,
59
51
  ) -> List:
60
- for tool in tools:
61
- if not isinstance(tool, (SandboxTool, FunctionTool, MCPTool)):
62
- raise ValueError(
63
- "tools must be instances of SandboxTool, FunctionTool, "
64
- "or MCPTool",
65
- )
66
-
67
52
  # Create a composite key
68
- composite_key = self._create_composite_key(session_id, user_id)
53
+ session_ctx_id = self._create_session_ctx_id(session_id, user_id)
69
54
 
70
- # Check if the composite_key already has an environment
71
- if composite_key in self.session_mapping:
55
+ env_ids = self.manager_api.get_session_mapping(session_ctx_id)
56
+
57
+ # Check if the session_ctx_id already has an environment
58
+ if env_ids:
72
59
  # Connect to existing environment
73
- return self._connect_existing_environment(composite_key)
60
+ return self._connect_existing_environment(env_ids)
74
61
  else:
75
62
  # Create a new environment
76
63
  return self._create_new_environment(
77
- composite_key,
64
+ session_ctx_id,
78
65
  env_types,
79
66
  tools,
80
67
  )
81
68
 
82
69
  def _create_new_environment(
83
70
  self,
84
- composite_key,
71
+ session_ctx_id: str,
85
72
  env_types=None,
86
73
  tools=None,
87
74
  ):
75
+ if tools:
76
+ for tool in tools:
77
+ if not isinstance(tool, (SandboxTool, FunctionTool, MCPTool)):
78
+ raise ValueError(
79
+ "tools must be instances of SandboxTool, "
80
+ "FunctionTool, or MCPTool",
81
+ )
82
+
88
83
  if env_types is None:
89
84
  assert (
90
85
  tools is not None
91
86
  ), "tools must be specified when env_types is not set"
92
87
 
93
- server_configs = None
94
88
  if tools:
95
- server_config_list = []
96
89
  tool_env_types = set()
97
90
  for tool in tools:
98
91
  tool_env_types.add(tool.sandbox_type)
99
- if isinstance(tool, MCPTool):
100
- server_config_list.append(tool.server_configs)
101
-
102
92
  if env_types is None:
103
93
  env_types = []
104
94
 
105
- if server_config_list:
106
- server_configs = {"mcpServers": {}}
107
- for server_config in server_config_list:
108
- server_configs["mcpServers"].update(
109
- server_config["mcpServers"],
110
- )
111
-
112
95
  env_types = set(env_types) | tool_env_types
113
96
 
114
- wbs = []
97
+ sandboxes = []
115
98
  for env_type in env_types:
116
99
  if env_type is None:
117
100
  continue
@@ -120,6 +103,7 @@ class SandboxService(ServiceWithLifecycleManager):
120
103
 
121
104
  box_id = self.manager_api.create_from_pool(
122
105
  sandbox_type=box_type.value,
106
+ meta={"session_ctx_id": session_ctx_id},
123
107
  )
124
108
  box_cls = SandboxRegistry.get_classes_by_type(box_type)
125
109
 
@@ -135,22 +119,30 @@ class SandboxService(ServiceWithLifecycleManager):
135
119
  # Embedded mode
136
120
  box.manager_api = self.manager_api
137
121
 
138
- if box_type == SandboxType.BASE:
139
- if server_configs:
140
- box.add_mcp_servers(server_configs, overwrite=False)
141
-
142
- # Store mapping from composite_key to env_id
143
- if composite_key not in self.session_mapping:
144
- self.session_mapping[composite_key] = []
145
-
146
- self.session_mapping[composite_key].append(box_id)
147
-
148
- wbs.append(box)
149
- return wbs
150
-
151
- def _connect_existing_environment(self, composite_key):
152
- env_ids = self.session_mapping.get(composite_key)
153
-
122
+ # Add MCP to the sandbox
123
+ server_config_list = []
124
+ if tools:
125
+ for tool in tools:
126
+ if isinstance(tool, MCPTool) and SandboxType(
127
+ tool.sandbox_type,
128
+ ) == SandboxType(box_type):
129
+ server_config_list.append(tool.server_configs)
130
+ if server_config_list:
131
+ server_configs = {"mcpServers": {}}
132
+ for server_config in server_config_list:
133
+ if (
134
+ server_config is not None
135
+ and "mcpServers" in server_config
136
+ ):
137
+ server_configs["mcpServers"].update(
138
+ server_config["mcpServers"],
139
+ )
140
+ box.add_mcp_servers(server_configs, overwrite=False)
141
+
142
+ sandboxes.append(box)
143
+ return sandboxes
144
+
145
+ def _connect_existing_environment(self, env_ids: List[str]):
154
146
  boxes = []
155
147
  for env_id in env_ids:
156
148
  info = self.manager_api.get_info(env_id)
@@ -183,16 +175,16 @@ class SandboxService(ServiceWithLifecycleManager):
183
175
  return boxes
184
176
 
185
177
  def release(self, session_id, user_id):
186
- composite_key = self._create_composite_key(session_id, user_id)
178
+ session_ctx_id = self._create_session_ctx_id(session_id, user_id)
187
179
 
188
- # Retrieve and remove env_id using composite_key
189
- env_ids = self.session_mapping.pop(composite_key, [])
180
+ env_ids = self.manager_api.get_session_mapping(session_ctx_id)
190
181
 
191
- for env_id in env_ids:
192
- self.manager_api.release(env_id)
182
+ if env_ids:
183
+ for env_id in env_ids:
184
+ self.manager_api.release(env_id)
193
185
 
194
186
  return True
195
187
 
196
- def _create_composite_key(self, session_id, user_id):
188
+ def _create_session_ctx_id(self, session_id, user_id):
197
189
  # Create a composite key from session_id and user_id
198
190
  return f"{session_id}_{user_id}"