agentscope-runtime 0.1.0__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 (131) hide show
  1. agentscope_runtime/__init__.py +4 -0
  2. agentscope_runtime/engine/__init__.py +9 -0
  3. agentscope_runtime/engine/agents/__init__.py +2 -0
  4. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +6 -0
  5. agentscope_runtime/engine/agents/agentscope_agent/agent.py +342 -0
  6. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +156 -0
  7. agentscope_runtime/engine/agents/agno_agent.py +220 -0
  8. agentscope_runtime/engine/agents/base_agent.py +29 -0
  9. agentscope_runtime/engine/agents/langgraph_agent.py +59 -0
  10. agentscope_runtime/engine/agents/llm_agent.py +51 -0
  11. agentscope_runtime/engine/deployers/__init__.py +3 -0
  12. agentscope_runtime/engine/deployers/adapter/__init__.py +0 -0
  13. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +2 -0
  14. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +425 -0
  15. agentscope_runtime/engine/deployers/adapter/a2a/a2a_agent_adapter.py +69 -0
  16. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +60 -0
  17. agentscope_runtime/engine/deployers/adapter/protocol_adapter.py +24 -0
  18. agentscope_runtime/engine/deployers/base.py +17 -0
  19. agentscope_runtime/engine/deployers/local_deployer.py +586 -0
  20. agentscope_runtime/engine/helpers/helper.py +127 -0
  21. agentscope_runtime/engine/llms/__init__.py +3 -0
  22. agentscope_runtime/engine/llms/base_llm.py +60 -0
  23. agentscope_runtime/engine/llms/qwen_llm.py +47 -0
  24. agentscope_runtime/engine/misc/__init__.py +0 -0
  25. agentscope_runtime/engine/runner.py +186 -0
  26. agentscope_runtime/engine/schemas/__init__.py +0 -0
  27. agentscope_runtime/engine/schemas/agent_schemas.py +551 -0
  28. agentscope_runtime/engine/schemas/context.py +54 -0
  29. agentscope_runtime/engine/services/__init__.py +9 -0
  30. agentscope_runtime/engine/services/base.py +77 -0
  31. agentscope_runtime/engine/services/context_manager.py +129 -0
  32. agentscope_runtime/engine/services/environment_manager.py +50 -0
  33. agentscope_runtime/engine/services/manager.py +174 -0
  34. agentscope_runtime/engine/services/memory_service.py +270 -0
  35. agentscope_runtime/engine/services/sandbox_service.py +198 -0
  36. agentscope_runtime/engine/services/session_history_service.py +256 -0
  37. agentscope_runtime/engine/tracing/__init__.py +40 -0
  38. agentscope_runtime/engine/tracing/base.py +309 -0
  39. agentscope_runtime/engine/tracing/local_logging_handler.py +356 -0
  40. agentscope_runtime/engine/tracing/tracing_metric.py +69 -0
  41. agentscope_runtime/engine/tracing/wrapper.py +321 -0
  42. agentscope_runtime/sandbox/__init__.py +14 -0
  43. agentscope_runtime/sandbox/box/__init__.py +0 -0
  44. agentscope_runtime/sandbox/box/base/__init__.py +0 -0
  45. agentscope_runtime/sandbox/box/base/base_sandbox.py +37 -0
  46. agentscope_runtime/sandbox/box/base/box/__init__.py +0 -0
  47. agentscope_runtime/sandbox/box/browser/__init__.py +0 -0
  48. agentscope_runtime/sandbox/box/browser/box/__init__.py +0 -0
  49. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +176 -0
  50. agentscope_runtime/sandbox/box/dummy/__init__.py +0 -0
  51. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +26 -0
  52. agentscope_runtime/sandbox/box/filesystem/__init__.py +0 -0
  53. agentscope_runtime/sandbox/box/filesystem/box/__init__.py +0 -0
  54. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +87 -0
  55. agentscope_runtime/sandbox/box/sandbox.py +115 -0
  56. agentscope_runtime/sandbox/box/shared/__init__.py +0 -0
  57. agentscope_runtime/sandbox/box/shared/app.py +44 -0
  58. agentscope_runtime/sandbox/box/shared/dependencies/__init__.py +5 -0
  59. agentscope_runtime/sandbox/box/shared/dependencies/deps.py +22 -0
  60. agentscope_runtime/sandbox/box/shared/routers/__init__.py +12 -0
  61. agentscope_runtime/sandbox/box/shared/routers/generic.py +173 -0
  62. agentscope_runtime/sandbox/box/shared/routers/mcp.py +207 -0
  63. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +153 -0
  64. agentscope_runtime/sandbox/box/shared/routers/runtime_watcher.py +187 -0
  65. agentscope_runtime/sandbox/box/shared/routers/workspace.py +325 -0
  66. agentscope_runtime/sandbox/box/training_box/__init__.py +0 -0
  67. agentscope_runtime/sandbox/box/training_box/base.py +120 -0
  68. agentscope_runtime/sandbox/box/training_box/env_service.py +752 -0
  69. agentscope_runtime/sandbox/box/training_box/environments/__init__.py +0 -0
  70. agentscope_runtime/sandbox/box/training_box/environments/appworld/appworld_env.py +987 -0
  71. agentscope_runtime/sandbox/box/training_box/registry.py +54 -0
  72. agentscope_runtime/sandbox/box/training_box/src/trajectory.py +278 -0
  73. agentscope_runtime/sandbox/box/training_box/training_box.py +219 -0
  74. agentscope_runtime/sandbox/build.py +213 -0
  75. agentscope_runtime/sandbox/client/__init__.py +5 -0
  76. agentscope_runtime/sandbox/client/http_client.py +527 -0
  77. agentscope_runtime/sandbox/client/training_client.py +265 -0
  78. agentscope_runtime/sandbox/constant.py +5 -0
  79. agentscope_runtime/sandbox/custom/__init__.py +16 -0
  80. agentscope_runtime/sandbox/custom/custom_sandbox.py +40 -0
  81. agentscope_runtime/sandbox/custom/example.py +37 -0
  82. agentscope_runtime/sandbox/enums.py +68 -0
  83. agentscope_runtime/sandbox/manager/__init__.py +4 -0
  84. agentscope_runtime/sandbox/manager/collections/__init__.py +22 -0
  85. agentscope_runtime/sandbox/manager/collections/base_mapping.py +20 -0
  86. agentscope_runtime/sandbox/manager/collections/base_queue.py +25 -0
  87. agentscope_runtime/sandbox/manager/collections/base_set.py +25 -0
  88. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +22 -0
  89. agentscope_runtime/sandbox/manager/collections/in_memory_queue.py +28 -0
  90. agentscope_runtime/sandbox/manager/collections/in_memory_set.py +27 -0
  91. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +26 -0
  92. agentscope_runtime/sandbox/manager/collections/redis_queue.py +27 -0
  93. agentscope_runtime/sandbox/manager/collections/redis_set.py +23 -0
  94. agentscope_runtime/sandbox/manager/container_clients/__init__.py +8 -0
  95. agentscope_runtime/sandbox/manager/container_clients/base_client.py +39 -0
  96. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +170 -0
  97. agentscope_runtime/sandbox/manager/sandbox_manager.py +694 -0
  98. agentscope_runtime/sandbox/manager/server/__init__.py +0 -0
  99. agentscope_runtime/sandbox/manager/server/app.py +194 -0
  100. agentscope_runtime/sandbox/manager/server/config.py +68 -0
  101. agentscope_runtime/sandbox/manager/server/models.py +17 -0
  102. agentscope_runtime/sandbox/manager/storage/__init__.py +10 -0
  103. agentscope_runtime/sandbox/manager/storage/data_storage.py +16 -0
  104. agentscope_runtime/sandbox/manager/storage/local_storage.py +44 -0
  105. agentscope_runtime/sandbox/manager/storage/oss_storage.py +89 -0
  106. agentscope_runtime/sandbox/manager/utils.py +78 -0
  107. agentscope_runtime/sandbox/mcp_server.py +192 -0
  108. agentscope_runtime/sandbox/model/__init__.py +12 -0
  109. agentscope_runtime/sandbox/model/api.py +16 -0
  110. agentscope_runtime/sandbox/model/container.py +72 -0
  111. agentscope_runtime/sandbox/model/manager_config.py +158 -0
  112. agentscope_runtime/sandbox/registry.py +129 -0
  113. agentscope_runtime/sandbox/tools/__init__.py +12 -0
  114. agentscope_runtime/sandbox/tools/base/__init__.py +8 -0
  115. agentscope_runtime/sandbox/tools/base/tool.py +52 -0
  116. agentscope_runtime/sandbox/tools/browser/__init__.py +57 -0
  117. agentscope_runtime/sandbox/tools/browser/tool.py +597 -0
  118. agentscope_runtime/sandbox/tools/filesystem/__init__.py +32 -0
  119. agentscope_runtime/sandbox/tools/filesystem/tool.py +319 -0
  120. agentscope_runtime/sandbox/tools/function_tool.py +321 -0
  121. agentscope_runtime/sandbox/tools/mcp_tool.py +191 -0
  122. agentscope_runtime/sandbox/tools/sandbox_tool.py +104 -0
  123. agentscope_runtime/sandbox/tools/tool.py +123 -0
  124. agentscope_runtime/sandbox/tools/utils.py +68 -0
  125. agentscope_runtime/version.py +2 -0
  126. agentscope_runtime-0.1.0.dist-info/METADATA +327 -0
  127. agentscope_runtime-0.1.0.dist-info/RECORD +131 -0
  128. agentscope_runtime-0.1.0.dist-info/WHEEL +5 -0
  129. agentscope_runtime-0.1.0.dist-info/entry_points.txt +4 -0
  130. agentscope_runtime-0.1.0.dist-info/licenses/LICENSE +202 -0
  131. agentscope_runtime-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,192 @@
1
+ # -*- coding: utf-8 -*-
2
+ import argparse
3
+ import inspect
4
+ import logging
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+ from .box.base.base_sandbox import BaseSandbox
8
+ from .box.browser.browser_sandbox import BrowserSandbox
9
+ from .box.filesystem.filesystem_sandbox import FilesystemSandbox
10
+ from .enums import SandboxType
11
+
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ mcp = FastMCP("AgentRuntime Sandbox MCP Server")
16
+
17
+
18
+ def json_type_to_python_type(json_type: str):
19
+ """Convert JSON schema type to Python type annotation."""
20
+ type_mapping = {
21
+ "string": str,
22
+ "integer": int,
23
+ "number": float,
24
+ "boolean": bool,
25
+ "array": list,
26
+ "object": dict,
27
+ }
28
+ return type_mapping.get(json_type, str)
29
+
30
+
31
+ def extract_content_from_mcp_response(response):
32
+ """Extract actual content from MCP protocol response."""
33
+ if isinstance(response, dict):
34
+ if "content" in response:
35
+ content = response["content"]
36
+ if isinstance(content, list) and len(content) > 0:
37
+ first_item = content[0]
38
+ if isinstance(first_item, dict) and "text" in first_item:
39
+ return first_item["text"]
40
+ elif isinstance(first_item, dict) and "data" in first_item:
41
+ return first_item["data"]
42
+ return content
43
+ elif "result" in response:
44
+ return response["result"]
45
+ elif "data" in response:
46
+ return response["data"]
47
+ return response
48
+
49
+
50
+ def create_dynamic_function(schema, method_name, box):
51
+ """Safely create a function with dynamic signature using inspect API."""
52
+ func_name = schema["function"]["name"]
53
+ func_description = schema["function"]["description"]
54
+ parameters = schema["function"]["parameters"]
55
+ properties = parameters.get("properties", {})
56
+ required = parameters.get("required", [])
57
+
58
+ # Create parameter list using inspect.Parameter
59
+ params = []
60
+ for param_name, param_info in properties.items():
61
+ param_type = json_type_to_python_type(param_info.get("type", "string"))
62
+ if param_name in required:
63
+ param = inspect.Parameter(
64
+ param_name,
65
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
66
+ annotation=param_type,
67
+ )
68
+ else:
69
+ param = inspect.Parameter(
70
+ param_name,
71
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
72
+ default=None,
73
+ annotation=param_type,
74
+ )
75
+ params.append(param)
76
+
77
+ # Create new signature
78
+ new_signature = inspect.Signature(params)
79
+
80
+ # Create wrapper function
81
+ def wrapper(*args, **kwargs):
82
+ """Dynamically generated wrapper function."""
83
+ # Bind arguments to signature
84
+ try:
85
+ bound = new_signature.bind(*args, **kwargs)
86
+ bound.apply_defaults()
87
+ except TypeError as e:
88
+ raise TypeError(f"Invalid arguments for {func_name}: {e}") from e
89
+
90
+ # Filter out None values for optional parameters
91
+ filtered_kwargs = {
92
+ k: v for k, v in bound.arguments.items() if v is not None
93
+ }
94
+
95
+ # Call the actual method and extract content from MCP response
96
+ mcp_response = getattr(box, method_name)(**filtered_kwargs)
97
+ # Extract the actual content, not the MCP protocol wrapper
98
+ actual_result = extract_content_from_mcp_response(mcp_response)
99
+
100
+ return actual_result
101
+
102
+ # Set function metadata
103
+ wrapper.__signature__ = new_signature
104
+ wrapper.__name__ = func_name
105
+ wrapper.__doc__ = func_description
106
+ wrapper.__qualname__ = func_name
107
+ return wrapper
108
+
109
+
110
+ def register_tools(box):
111
+ """Register all tools with the MCP server using FastMCP decorators."""
112
+ try:
113
+ tools_json = box.list_tools()
114
+
115
+ for server_name, tool_dict in tools_json.items():
116
+ logger.info(f"Registering tools from server: {server_name}")
117
+
118
+ for tool_name, tool_info in tool_dict.items():
119
+ json_schema = tool_info["json_schema"]
120
+
121
+ # Create dynamic function with proper signature
122
+ dynamic_func = create_dynamic_function(
123
+ json_schema,
124
+ tool_name,
125
+ box,
126
+ )
127
+
128
+ # Apply MCP decorator
129
+ _ = mcp.tool(
130
+ name=json_schema["function"]["name"],
131
+ description=json_schema["function"]["description"],
132
+ )(dynamic_func)
133
+
134
+ logger.info(
135
+ f"Registered tool: {json_schema['function']['name']}",
136
+ )
137
+
138
+ except Exception as e:
139
+ logger.error(f"Error registering tools: {e}")
140
+ raise
141
+
142
+
143
+ def main():
144
+ parser = argparse.ArgumentParser(
145
+ description="Run the AgentRuntime MCP Server.",
146
+ )
147
+ parser.add_argument(
148
+ "--type",
149
+ required=False,
150
+ default="base",
151
+ choices=["base", "browser", "filesystem"],
152
+ help="Type of sandbox to run",
153
+ )
154
+ parser.add_argument(
155
+ "--base_url",
156
+ required=False,
157
+ default=None,
158
+ help="Base URL for the server",
159
+ )
160
+ parser.add_argument(
161
+ "--bearer_token",
162
+ required=False,
163
+ default=None,
164
+ help="Bearer token for authentication",
165
+ )
166
+
167
+ args = parser.parse_args()
168
+
169
+ logger.info(
170
+ f"Running {args.type} sandbox with base URL `{args.base_url}` and "
171
+ f"bearer token `{args.bearer_token}`",
172
+ )
173
+
174
+ sandbox_type = SandboxType(args.type)
175
+
176
+ if sandbox_type == SandboxType.BASE:
177
+ sandbox_cls = BaseSandbox
178
+ elif sandbox_type == SandboxType.BROWSER:
179
+ sandbox_cls = BrowserSandbox
180
+ elif sandbox_type == SandboxType.FILESYSTEM:
181
+ sandbox_cls = FilesystemSandbox
182
+
183
+ with sandbox_cls(
184
+ base_url=args.base_url,
185
+ bearer_token=args.bearer_token,
186
+ ) as box:
187
+ register_tools(box)
188
+ mcp.run()
189
+
190
+
191
+ if __name__ == "__main__":
192
+ main()
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ from .container import ContainerModel
3
+ from .manager_config import (
4
+ SandboxManagerEnvConfig,
5
+ DEFAULT_LOCAL_MANAGER_CONFIG,
6
+ )
7
+
8
+ __all__ = [
9
+ "ContainerModel",
10
+ "SandboxManagerEnvConfig",
11
+ "DEFAULT_LOCAL_MANAGER_CONFIG",
12
+ ]
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class RuntimeDiffResult(BaseModel):
8
+ diff: Optional[str] = Field(
9
+ None,
10
+ description="The modifications in the filesystem since the "
11
+ "last check.",
12
+ )
13
+ url: Optional[str] = Field(
14
+ None,
15
+ description="The current position where the browser is located.",
16
+ )
@@ -0,0 +1,72 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import List
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ # TODO: support k8s version
8
+ class ContainerModel(BaseModel):
9
+ session_id: str = Field(
10
+ ...,
11
+ description="Unique identifier for the session",
12
+ )
13
+
14
+ container_id: str = Field(
15
+ ...,
16
+ description="Unique identifier for the container instance",
17
+ )
18
+
19
+ container_name: str = Field(
20
+ ...,
21
+ description="Human-readable name for the container",
22
+ )
23
+
24
+ base_url: str = Field(
25
+ ...,
26
+ description="Base URL for accessing the container",
27
+ )
28
+
29
+ browser_url: str = Field(
30
+ ...,
31
+ description="URL for browser interface within the container",
32
+ )
33
+
34
+ front_browser_ws: str = Field(
35
+ ...,
36
+ description="WebSocket URL for the browser used by frontend",
37
+ )
38
+
39
+ client_browser_ws: str = Field(
40
+ ...,
41
+ description="WebSocket URL for the browser used by runtime client",
42
+ )
43
+
44
+ artifacts_sio: str = Field(
45
+ ...,
46
+ description="Socketio URL for the artifacts used by frontend",
47
+ )
48
+ ports: List[int] = Field(
49
+ ...,
50
+ description="List of occupied port numbers",
51
+ )
52
+
53
+ mount_dir: str | None = Field(
54
+ None,
55
+ description="The mount directory of workspace.",
56
+ )
57
+
58
+ storage_path: str | None = Field(
59
+ None,
60
+ description="The oss_path of workspace.",
61
+ )
62
+
63
+ runtime_token: str | None = Field(
64
+ None,
65
+ description="Runtime token used for authentication or secure "
66
+ "communication",
67
+ )
68
+
69
+ version: str | None = Field(
70
+ None,
71
+ description="Image version of the container",
72
+ )
@@ -0,0 +1,158 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=no-self-argument
3
+ import os
4
+ from typing import Optional, Literal, Tuple
5
+ from pydantic import BaseModel, Field, model_validator
6
+
7
+
8
+ class SandboxManagerEnvConfig(BaseModel):
9
+ container_prefix_key: str = Field(
10
+ "runtime_sandbox_container_",
11
+ description="Prefix for keys related to Container models.",
12
+ )
13
+
14
+ file_system: Literal["local", "oss"] = Field(
15
+ ...,
16
+ description="Type of file system to use: 'local' or 'oss'.",
17
+ )
18
+ storage_folder: Optional[str] = Field(
19
+ "",
20
+ description="Folder path in storage.",
21
+ )
22
+ redis_enabled: bool = Field(
23
+ ...,
24
+ description="Indicates if Redis is enabled.",
25
+ )
26
+ container_deployment: Literal["docker", "cloud"] = Field(
27
+ ...,
28
+ description="container_deployment: 'docker'.",
29
+ )
30
+
31
+ default_mount_dir: Optional[str] = Field(
32
+ None,
33
+ description="Path for local file system storage.",
34
+ )
35
+
36
+ port_range: Tuple[int, int] = Field(
37
+ (49152, 59152),
38
+ description="Range of ports to be used by the manager.",
39
+ )
40
+
41
+ pool_size: int = Field(
42
+ 0,
43
+ description="Number of containers to be kept in the pool.",
44
+ )
45
+
46
+ # OSS settings
47
+ oss_endpoint: Optional[str] = Field(
48
+ "http://oss-cn-hangzhou.aliyuncs.com",
49
+ description="OSS endpoint URL. Required if file_system is 'oss'.",
50
+ )
51
+ oss_access_key_id: Optional[str] = Field(
52
+ None,
53
+ description="Access key ID for OSS. Required if file_system is 'oss'.",
54
+ )
55
+ oss_access_key_secret: Optional[str] = Field(
56
+ None,
57
+ description="Access key secret for OSS. Required if file_system is "
58
+ "'oss'.",
59
+ )
60
+ oss_bucket_name: Optional[str] = Field(
61
+ None,
62
+ description="Bucket name in OSS. Required if file_system is 'oss'.",
63
+ )
64
+
65
+ # Redis settings
66
+ redis_server: Optional[str] = Field(
67
+ "localhost",
68
+ description="Redis server address. Required if Redis is enabled.",
69
+ )
70
+ redis_port: Optional[int] = Field(
71
+ 6379,
72
+ description="Port for connecting to Redis. Required if Redis is "
73
+ "enabled.",
74
+ )
75
+ redis_db: Optional[int] = Field(
76
+ 0,
77
+ description="Database index to use in Redis. Required if Redis is "
78
+ "enabled.",
79
+ )
80
+ redis_user: Optional[str] = Field(
81
+ None,
82
+ description="Username for Redis authentication.",
83
+ )
84
+ redis_password: Optional[str] = Field(
85
+ None,
86
+ description="Password for Redis authentication.",
87
+ )
88
+
89
+ redis_port_key: str = Field(
90
+ "_runtime_sandbox_container_occupied_ports",
91
+ description="Prefix for Redis keys related to occupied ports.",
92
+ )
93
+ redis_container_pool_key: str = Field(
94
+ "_runtime_sandbox_container_container_pool",
95
+ description="Prefix for Redis keys related to container pool.",
96
+ )
97
+
98
+ @model_validator(mode="after")
99
+ def check_settings(cls, self):
100
+ if not self.default_mount_dir:
101
+ raise ValueError("default_mount_dir must be set")
102
+
103
+ os.makedirs(self.default_mount_dir, exist_ok=True)
104
+
105
+ if self.file_system == "oss":
106
+ required_oss_fields = [
107
+ self.oss_endpoint,
108
+ self.oss_access_key_id,
109
+ self.oss_access_key_secret,
110
+ self.oss_bucket_name,
111
+ ]
112
+ for field_name, field_value in zip(
113
+ [
114
+ "oss_endpoint",
115
+ "oss_access_key_id",
116
+ "oss_access_key_secret",
117
+ "oss_bucket_name",
118
+ ],
119
+ required_oss_fields,
120
+ ):
121
+ if not field_value:
122
+ raise ValueError(
123
+ f"{field_name} must be set when file_system is 'oss'",
124
+ )
125
+
126
+ if self.redis_enabled:
127
+ required_redis_fields = [
128
+ self.redis_server,
129
+ self.redis_port,
130
+ self.redis_db,
131
+ self.redis_port_key,
132
+ self.redis_container_pool_key,
133
+ ]
134
+ for field_name, field_value in zip(
135
+ [
136
+ "redis_server",
137
+ "redis_port",
138
+ "redis_db",
139
+ "redis_port_key",
140
+ "redis_container_pool_key",
141
+ ],
142
+ required_redis_fields,
143
+ ):
144
+ if field_value is None:
145
+ raise ValueError(
146
+ f"{field_name} must be set when redis is enabled",
147
+ )
148
+
149
+ return self
150
+
151
+
152
+ DEFAULT_LOCAL_MANAGER_CONFIG = SandboxManagerEnvConfig(
153
+ file_system="local",
154
+ redis_enabled=False,
155
+ container_deployment="docker",
156
+ pool_size=0,
157
+ default_mount_dir="sessions_mount_dir",
158
+ )
@@ -0,0 +1,129 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Dict, Type, Optional
3
+ from dataclasses import dataclass
4
+
5
+ from .enums import SandboxType
6
+
7
+
8
+ @dataclass
9
+ class SandboxConfig:
10
+ """sandbox configuration information"""
11
+
12
+ image_name: str
13
+ sandbox_type: SandboxType | str
14
+ resource_limits: Optional[Dict] = None
15
+ security_level: str = "medium"
16
+ timeout: int = 60 # Default timeout of 5 minutes
17
+ description: str = ""
18
+ environment: Optional[Dict] = None
19
+ runtime_config: Optional[Dict] = None
20
+
21
+ def __post_init__(self):
22
+ if self.runtime_config is None:
23
+ self.runtime_config = {}
24
+ if "memory" in self.resource_limits:
25
+ self.runtime_config["mem_limit"] = self.resource_limits["memory"]
26
+ if "cpu" in self.resource_limits:
27
+ self.runtime_config["nano_cpus"] = int(
28
+ float(self.resource_limits["cpu"]) * 1e9,
29
+ )
30
+
31
+
32
+ class SandboxRegistry:
33
+ """Docker image registry for sandboxes"""
34
+
35
+ _registry: Dict[Type, SandboxConfig] = {}
36
+ _type_registry: Dict[SandboxType, Type] = {}
37
+
38
+ @classmethod
39
+ def register(
40
+ cls,
41
+ image_name: str,
42
+ sandbox_type: SandboxType | str,
43
+ resource_limits: Dict = None,
44
+ security_level: str = "medium", # Not used for now
45
+ timeout: int = 300,
46
+ description: str = "",
47
+ environment: Dict = None,
48
+ runtime_config: Optional[Dict] = None,
49
+ ):
50
+ """
51
+ Decorator to register sandbox classes
52
+
53
+ Args:
54
+ image_name: Docker image name
55
+ sandbox_type: Sandbox type
56
+ resource_limits: Resource limit configuration
57
+ security_level: Security level (low/medium/high)
58
+ timeout: Timeout in seconds
59
+ description: Description
60
+ environment: Environment variables
61
+ runtime_config: runtime configurations
62
+ """
63
+
64
+ def decorator(target_class: Type) -> Type:
65
+ if isinstance(sandbox_type, str) and sandbox_type not in [
66
+ x.value for x in SandboxType
67
+ ]:
68
+ SandboxType.add_member(
69
+ sandbox_type.upper(),
70
+ )
71
+
72
+ _sandbox_type = SandboxType(sandbox_type)
73
+
74
+ config = SandboxConfig(
75
+ image_name=image_name,
76
+ sandbox_type=_sandbox_type,
77
+ resource_limits=resource_limits or {},
78
+ security_level=security_level,
79
+ timeout=timeout,
80
+ description=description,
81
+ environment=environment,
82
+ runtime_config=runtime_config,
83
+ )
84
+
85
+ cls._registry[target_class] = config
86
+ cls._type_registry[_sandbox_type] = target_class
87
+
88
+ return target_class
89
+
90
+ return decorator
91
+
92
+ @classmethod
93
+ def get_config(cls, target_class: Type) -> Optional[SandboxConfig]:
94
+ """Get the sandbox configuration for a class"""
95
+ return cls._registry.get(target_class)
96
+
97
+ @classmethod
98
+ def get_image(cls, target_class: Type) -> Optional[str]:
99
+ """Get the Docker image name for a class"""
100
+ config = cls.get_config(target_class)
101
+ return config.image_name if config else None
102
+
103
+ @classmethod
104
+ def get_classes_by_type(cls, sandbox_type: SandboxType | str):
105
+ """Get all related classes by sandbox type"""
106
+ sandbox_type = SandboxType(sandbox_type)
107
+ return cls._type_registry.get(sandbox_type)
108
+
109
+ @classmethod
110
+ def list_all_sandboxes(cls) -> Dict[Type, SandboxConfig]:
111
+ """List all registered sandboxes"""
112
+ return cls._registry.copy()
113
+
114
+ @classmethod
115
+ def get_config_by_type(
116
+ cls,
117
+ sandbox_type: SandboxType | str,
118
+ ):
119
+ """Get all configurations by sandbox type"""
120
+ sandbox_type = SandboxType(sandbox_type)
121
+ cls_ = cls.get_classes_by_type(sandbox_type)
122
+ return cls.get_config(cls_)
123
+
124
+ @classmethod
125
+ def get_image_by_type(cls, sandbox_type: SandboxType | str):
126
+ """Get all Docker image names by sandbox type"""
127
+ sandbox_type = SandboxType(sandbox_type)
128
+ cls_ = cls.get_classes_by_type(sandbox_type)
129
+ return cls.get_image(cls_)
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ from .sandbox_tool import SandboxTool
3
+ from .mcp_tool import MCPTool
4
+ from .function_tool import FunctionTool, function_tool, create_function_tool
5
+
6
+ __all__ = [
7
+ "SandboxTool",
8
+ "MCPTool",
9
+ "FunctionTool",
10
+ "function_tool",
11
+ "create_function_tool",
12
+ ]
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ from .tool import run_shell_command, run_ipython_cell
3
+
4
+
5
+ __all__ = [
6
+ "run_shell_command",
7
+ "run_ipython_cell",
8
+ ]
@@ -0,0 +1,52 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Dict
3
+
4
+ from ..sandbox_tool import SandboxTool
5
+
6
+
7
+ class RunIPythonCellTool(SandboxTool):
8
+ """Tool for running IPython cells."""
9
+
10
+ _name: str = "run_ipython_cell"
11
+ _sandbox_type: str = "base"
12
+ _tool_type: str = "generic"
13
+ _schema: Dict = {
14
+ "name": "run_ipython_cell",
15
+ "description": "Run an IPython cell.",
16
+ "parameters": {
17
+ "type": "object",
18
+ "properties": {
19
+ "code": {
20
+ "type": "string",
21
+ "description": "IPython code to execute",
22
+ },
23
+ },
24
+ "required": ["code"],
25
+ },
26
+ }
27
+
28
+
29
+ class RunShellCommandTool(SandboxTool):
30
+ """Tool for running shell commands."""
31
+
32
+ _name: str = "run_shell_command"
33
+ _sandbox_type: str = "base"
34
+ _tool_type: str = "generic"
35
+ _schema: Dict = {
36
+ "name": "run_shell_command",
37
+ "description": "Run a shell command.",
38
+ "parameters": {
39
+ "type": "object",
40
+ "properties": {
41
+ "command": {
42
+ "type": "string",
43
+ "description": "Shell command to execute",
44
+ },
45
+ },
46
+ "required": ["command"],
47
+ },
48
+ }
49
+
50
+
51
+ run_ipython_cell = RunIPythonCellTool()
52
+ run_shell_command = RunShellCommandTool()