Duibonduil commited on
Commit
44e7e06
·
verified ·
1 Parent(s): fe0d3be

Upload 5 files

Browse files
aworld/sandbox/__init__.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Any, Optional
2
+
3
+ from aworld.sandbox.base import Sandbox
4
+ from aworld.sandbox.common import BaseSandbox
5
+ from aworld.sandbox.models import SandboxEnvType
6
+ from aworld.sandbox.implementations import LocalSandbox, KubernetesSandbox, SuperSandbox
7
+
8
+
9
+ # For backward compatibility, use LocalSandbox as the default Sandbox implementation
10
+ DefaultSandbox = LocalSandbox
11
+
12
+
13
+ # Override Sandbox class constructor to create the appropriate sandbox based on env_type
14
+ def create_sandbox(
15
+ env_type: Optional[int] = None,
16
+ sandbox_id: Optional[str] = None,
17
+ metadata: Optional[Dict[str, str]] = None,
18
+ timeout: Optional[int] = None,
19
+ mcp_servers: Optional[List[str]] = None,
20
+ mcp_config: Optional[Any] = None,
21
+ **kwargs
22
+ ) -> Sandbox:
23
+ """
24
+ Factory function to create a sandbox instance based on the environment type.
25
+
26
+ Args:
27
+ env_type: The environment type. Defaults to LOCAL if None.
28
+ sandbox_id: Unique identifier for the sandbox. If None, one will be generated.
29
+ metadata: Additional metadata for the sandbox.
30
+ timeout: Timeout for sandbox operations.
31
+ mcp_servers: List of MCP servers to use.
32
+ mcp_config: Configuration for MCP servers.
33
+ **kwargs: Additional parameters for specific sandbox types.
34
+
35
+ Returns:
36
+ Sandbox: An instance of a sandbox implementation.
37
+
38
+ Raises:
39
+ ValueError: If an invalid environment type is provided.
40
+ """
41
+ env_type = env_type or SandboxEnvType.LOCAL
42
+
43
+ if env_type == SandboxEnvType.LOCAL:
44
+ return LocalSandbox(
45
+ sandbox_id=sandbox_id,
46
+ metadata=metadata,
47
+ timeout=timeout,
48
+ mcp_servers=mcp_servers,
49
+ mcp_config=mcp_config,
50
+ **kwargs
51
+ )
52
+ elif env_type == SandboxEnvType.K8S:
53
+ return KubernetesSandbox(
54
+ sandbox_id=sandbox_id,
55
+ metadata=metadata,
56
+ timeout=timeout,
57
+ mcp_servers=mcp_servers,
58
+ mcp_config=mcp_config,
59
+ **kwargs
60
+ )
61
+ elif env_type == SandboxEnvType.SUPERCOMPUTER:
62
+ return SuperSandbox(
63
+ sandbox_id=sandbox_id,
64
+ metadata=metadata,
65
+ timeout=timeout,
66
+ mcp_servers=mcp_servers,
67
+ mcp_config=mcp_config,
68
+ **kwargs
69
+ )
70
+ else:
71
+ raise ValueError(f"Invalid environment type: {env_type}")
72
+
73
+
74
+ # Monkey patch the Sandbox class to make direct instantiation work
75
+ old_init = Sandbox.__init__
76
+
77
+ def _sandbox_init(self, *args, **kwargs):
78
+ if type(self) is Sandbox:
79
+ # This should never be called directly, as __new__ will return a different type
80
+ pass
81
+ else:
82
+ # Pass through to the original __init__ for actual implementations
83
+ old_init(self, *args, **kwargs)
84
+
85
+ # Store the original __new__ method
86
+ original_new = object.__new__
87
+
88
+ # Create a new __new__ method that intercepts Sandbox instantiation
89
+ def _sandbox_new(cls, *args, **kwargs):
90
+ if cls is Sandbox:
91
+ # If trying to instantiate Sandbox directly, use our factory instead
92
+ return create_sandbox(**kwargs)
93
+ else:
94
+ # For subclasses, use the original __new__
95
+ return original_new(cls)
96
+
97
+ # Apply the monkey patches
98
+ Sandbox.__init__ = _sandbox_init
99
+ Sandbox.__new__ = _sandbox_new
100
+
101
+
102
+ # Expose key classes and functions
103
+ __all__ = [
104
+ 'Sandbox',
105
+ 'BaseSandbox',
106
+ 'LocalSandbox',
107
+ 'KubernetesSandbox',
108
+ 'SuperSandbox',
109
+ 'DefaultSandbox',
110
+ 'SandboxEnvType',
111
+ 'create_sandbox'
112
+ ]
aworld/sandbox/base.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import abc
2
+ import asyncio
3
+ import logging
4
+ import uuid
5
+ from typing import Dict, List, Any, Optional
6
+
7
+ from aworld.sandbox.api.setup import SandboxSetup
8
+ from aworld.sandbox.models import SandboxStatus, SandboxEnvType, SandboxInfo
9
+ from aworld.sandbox.run.mcp_servers import McpServers
10
+
11
+
12
+ class Sandbox(SandboxSetup):
13
+ """
14
+ Sandbox abstract base class that defines the interface for all sandbox implementations.
15
+ A sandbox provides an isolated environment for executing code and operations.
16
+ """
17
+
18
+ default_sandbox_timeout = 3000
19
+
20
+ @property
21
+ def sandbox_id(self) -> str:
22
+ """
23
+ Returns the unique identifier of the sandbox.
24
+ """
25
+ return self._sandbox_id
26
+
27
+ @property
28
+ def status(self) -> SandboxStatus:
29
+ """
30
+ Returns the current status of the sandbox.
31
+ """
32
+ return self._status
33
+
34
+ @property
35
+ def timeout(self) -> int:
36
+ """
37
+ Returns the timeout value for sandbox operations.
38
+ """
39
+ return self._timeout
40
+
41
+ @property
42
+ def metadata(self) -> Dict[str, Any]:
43
+ """
44
+ Returns the sandbox metadata.
45
+ """
46
+ return self._metadata
47
+
48
+ @property
49
+ def env_type(self) -> SandboxEnvType:
50
+ """
51
+ Returns the environment type of the sandbox.
52
+ """
53
+ return self._env_type
54
+
55
+ @property
56
+ def mcp_config(self) -> Any:
57
+ """
58
+ Returns the MCP configuration.
59
+ """
60
+ return self._mcp_config
61
+
62
+ @property
63
+ def mcp_servers(self) -> List[str]:
64
+ """
65
+ Returns the list of MCP servers.
66
+ """
67
+ return self._mcp_servers
68
+
69
+ @property
70
+ @abc.abstractmethod
71
+ def mcpservers(self) -> McpServers:
72
+ """
73
+ Module for running MCP in the sandbox.
74
+
75
+ Returns:
76
+ McpServers: The MCP servers instance.
77
+ """
78
+ pass
79
+
80
+ def __init__(
81
+ self,
82
+ sandbox_id: Optional[str] = None,
83
+ env_type: Optional[int] = None,
84
+ metadata: Optional[Dict[str, str]] = None,
85
+ timeout: Optional[int] = None,
86
+ mcp_servers: Optional[List[str]] = None,
87
+ mcp_config: Optional[Any] = None,
88
+ ):
89
+ """
90
+ Initialize a new Sandbox instance.
91
+
92
+ Args:
93
+ sandbox_id: Unique identifier for the sandbox. If None, one will be generated.
94
+ env_type: The environment type (LOCAL, K8S, SUPERCOMPUTER).
95
+ metadata: Additional metadata for the sandbox.
96
+ timeout: Timeout for sandbox operations.
97
+ mcp_servers: List of MCP servers to use.
98
+ mcp_config: Configuration for MCP servers.
99
+ """
100
+ # Initialize basic attributes
101
+ self._sandbox_id = sandbox_id or str(uuid.uuid4())
102
+ self._status = SandboxStatus.INIT
103
+ self._timeout = timeout or self.default_sandbox_timeout
104
+ self._metadata = metadata or {}
105
+ self._env_type = env_type or SandboxEnvType.LOCAL
106
+ self._mcp_servers = mcp_servers or []
107
+ self._mcp_config = mcp_config or {}
108
+
109
+ @abc.abstractmethod
110
+ def get_info(self) -> SandboxInfo:
111
+ """
112
+ Returns information about the sandbox.
113
+
114
+ Returns:
115
+ SandboxInfo: Information about the sandbox.
116
+ """
117
+ pass
118
+
119
+ @abc.abstractmethod
120
+ async def remove(self) -> bool:
121
+ """
122
+ Remove the sandbox and clean up all resources.
123
+
124
+ Returns:
125
+ bool: True if removal was successful, False otherwise.
126
+ """
127
+ pass
128
+
129
+ @abc.abstractmethod
130
+ async def cleanup(self) -> bool:
131
+ """
132
+ Clean up the sandbox resources.
133
+
134
+ Returns:
135
+ bool: True if cleanup was successful, False otherwise.
136
+ """
137
+ pass
138
+
139
+ def __del__(self):
140
+ """
141
+ Ensure resources are cleaned up when the object is garbage collected.
142
+ """
143
+ try:
144
+ # Handle the case where an event loop already exists
145
+ try:
146
+ loop = asyncio.get_running_loop()
147
+ logging.warning("Cannot clean up sandbox in __del__ when event loop is already running")
148
+ return
149
+ except RuntimeError:
150
+ # No running event loop, create a new one
151
+ loop = asyncio.new_event_loop()
152
+ asyncio.set_event_loop(loop)
153
+ loop.run_until_complete(self.cleanup())
154
+ loop.close()
155
+ except Exception as e:
156
+ logging.warning(f"Failed to cleanup sandbox resources during garbage collection: {e}")
aworld/sandbox/common.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import logging
3
+ import abc
4
+ from typing import Dict, List, Any, Optional
5
+
6
+ from aworld.sandbox.base import Sandbox
7
+ from aworld.sandbox.models import SandboxStatus, SandboxEnvType, SandboxInfo
8
+ from aworld.sandbox.run.mcp_servers import McpServers
9
+
10
+
11
+ class BaseSandbox(Sandbox):
12
+ """
13
+ Base sandbox implementation with common functionality for all sandbox types.
14
+ This class implements common methods and provides a foundation for specific sandbox implementations.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ sandbox_id: Optional[str] = None,
20
+ env_type: Optional[int] = None,
21
+ metadata: Optional[Dict[str, str]] = None,
22
+ timeout: Optional[int] = None,
23
+ mcp_servers: Optional[List[str]] = None,
24
+ mcp_config: Optional[Any] = None,
25
+ ):
26
+ """
27
+ Initialize a new BaseSandbox instance.
28
+
29
+ Args:
30
+ sandbox_id: Unique identifier for the sandbox. If None, one will be generated.
31
+ env_type: The environment type (LOCAL, K8S, SUPERCOMPUTER).
32
+ metadata: Additional metadata for the sandbox.
33
+ timeout: Timeout for sandbox operations.
34
+ mcp_servers: List of MCP servers to use.
35
+ mcp_config: Configuration for MCP servers.
36
+ """
37
+ super().__init__(
38
+ sandbox_id=sandbox_id,
39
+ env_type=env_type,
40
+ metadata=metadata,
41
+ timeout=timeout,
42
+ mcp_servers=mcp_servers,
43
+ mcp_config=mcp_config
44
+ )
45
+ self._logger = self._setup_logger()
46
+
47
+ def _setup_logger(self):
48
+ """
49
+ Set up a logger for the sandbox instance.
50
+
51
+ Returns:
52
+ logging.Logger: Configured logger instance.
53
+ """
54
+ logger = logging.getLogger(f"sandbox.{self.__class__.__name__}.{self.sandbox_id[:8]}")
55
+ return logger
56
+
57
+ def get_info(self) -> SandboxInfo:
58
+ """
59
+ Get information about the sandbox.
60
+
61
+ Returns:
62
+ SandboxInfo: Information about the sandbox.
63
+ """
64
+ return {
65
+ "sandbox_id": self.sandbox_id,
66
+ "status": self.status,
67
+ "metadata": self.metadata,
68
+ "env_type": self.env_type
69
+ }
70
+
71
+ @property
72
+ def mcpservers(self) -> McpServers:
73
+ """
74
+ Module for running MCP servers in the sandbox.
75
+ This property provides access to the MCP servers instance.
76
+
77
+ Returns:
78
+ McpServers: The MCP servers instance.
79
+ """
80
+ if hasattr(self, '_mcpservers'):
81
+ return self._mcpservers
82
+ return None
83
+
84
+ @abc.abstractmethod
85
+ async def cleanup(self) -> bool:
86
+ """
87
+ Clean up sandbox resources.
88
+ This method must be implemented by subclasses to provide environment-specific cleanup.
89
+
90
+ Returns:
91
+ bool: True if cleanup was successful, False otherwise.
92
+ """
93
+ pass
94
+
95
+ @abc.abstractmethod
96
+ async def remove(self) -> bool:
97
+ """
98
+ Remove the sandbox.
99
+ This method must be implemented by subclasses to provide environment-specific removal.
100
+
101
+ Returns:
102
+ bool: True if removal was successful, False otherwise.
103
+ """
104
+ pass
aworld/sandbox/demo.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+
3
+ from aworld.sandbox import Sandbox, SandboxEnvType
4
+
5
+
6
+ # Define an asynchronous function to call asynchronous methods
7
+ async def run_async_tasks(sand_box):
8
+ tools = await sand_box.mcpservers.list_tools()
9
+ print(f"Tools: {tools}")
10
+ return tools
11
+
12
+ if __name__ == "__main__":
13
+ # 只使用memory服务器
14
+ mcp_servers = ["memory","amap-amap-sse"]
15
+ mcp_config = {
16
+ "mcpServers": {
17
+ "memory": {
18
+ "type": "stdio",
19
+ "command": "npx",
20
+ "args": [
21
+ "-y",
22
+ "@modelcontextprotocol/server-memory"
23
+ ]
24
+ }
25
+
26
+ }
27
+ }
28
+ sand_box = Sandbox(mcp_servers=mcp_servers, mcp_config=mcp_config,env_type=SandboxEnvType.SUPERCOMPUTER)
29
+ print(f"Sandbox ID: {sand_box.sandbox_id}")
30
+ print(f"Status: {sand_box.status}")
31
+ print(f"Timeout: {sand_box.timeout}")
32
+ print(f"Metadata: {sand_box.metadata}")
33
+ print(f"Environment Type: {sand_box.env_type}")
34
+ print(f"MCP Servers: {sand_box.mcp_servers}")
35
+ print(f"MCP Config: {sand_box.mcp_config}")
36
+
37
+ # Use asyncio to run asynchronous methods
38
+ asyncio.run(run_async_tasks(sand_box))
39
+
40
+ print(f"MCP Servers-new: {sand_box.mcp_servers}")
41
+ print(f"MCP Config-new: {sand_box.mcp_config}")
aworld/sandbox/models.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from dataclasses import dataclass
3
+ from enum import IntEnum, StrEnum
4
+
5
+ from pydantic import BaseModel
6
+ from typing_extensions import Optional, Dict,Any
7
+
8
+
9
+ class SandboxStatus(StrEnum):
10
+ """Sandbox status enumeration."""
11
+ INIT = 'Pending' # Initialization state
12
+ RUNNING = 'Running' # Running
13
+ STOPPED = 'Stopped' # Stopped
14
+ ERROR = 'Failed' # Error state
15
+ REMOVED = 'Removed' # Removed
16
+ UNKNOWN = 'Unknown' # Removed
17
+
18
+ class SandboxEnvType(IntEnum):
19
+ """Sandbox env type enumeration."""
20
+ LOCAL = 1
21
+ K8S = 2
22
+ SUPERCOMPUTER = 3
23
+
24
+
25
+
26
+ @dataclass
27
+ class SandboxCreateResponse:
28
+ sandbox_id: str = str(uuid.uuid4())
29
+ env_type: int = SandboxEnvType.LOCAL
30
+ status: Optional[str] = None
31
+ mcp_config: Optional[Any] = None
32
+
33
+ @dataclass
34
+ class SandboxK8sResponse(SandboxCreateResponse):
35
+ pod_name: Optional[str] = None
36
+ service_name: Optional[str] = None
37
+ cluster_ip: Optional[str] = None
38
+ host: Optional[str] = None
39
+
40
+
41
+ @dataclass
42
+ class SandboxLocalResponse(SandboxCreateResponse):
43
+ host: Optional[str] = None
44
+
45
+ @dataclass
46
+ class SandboxSuperResponse(SandboxCreateResponse):
47
+ host: Optional[str] = None
48
+
49
+ @dataclass
50
+ class SandboxInfo:
51
+ """Information about a sandbox."""
52
+
53
+ sandbox_id: str
54
+ """Sandbox ID."""
55
+ status: str
56
+ """sandbox status"""
57
+ metadata: Dict[str, str]
58
+ """Saved sandbox metadata."""
59
+
60
+
61
+
62
+ class EnvConfig(BaseModel):
63
+ """Data structure contained in the environment"""
64
+ name: str = "default"
65
+ version: str = "1.0.0"
66
+ dockerfile: Optional[str] = None #Dockerfile of the image required when creating the environment