Skip to content

Commit 13770a0

Browse files
authored
Merge pull request #7 from modelscope/add/pool
[Feature] Add sandbox pools
2 parents d42dc85 + d823ac6 commit 13770a0

File tree

13 files changed

+1234
-267
lines changed

13 files changed

+1234
-267
lines changed

examples/sandbox_usage_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async def direct_sandbox_example():
1313

1414
# Create Docker sandbox configuration
1515
config = DockerSandboxConfig(
16-
image='python-sandbox',
16+
image='python:3.11-slim',
1717
timeout=30,
1818
memory_limit='512m',
1919
cpu_limit=1.0,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""Sandbox manager implementations."""
22

3-
from .base import SandboxManager
3+
from .base import SandboxManager, SandboxManagerFactory
44
from .http_manager import HttpSandboxManager
55
from .local_manager import LocalSandboxManager
66

77
__all__ = [
88
'SandboxManager',
9+
'SandboxManagerFactory',
910
'LocalSandboxManager',
1011
'HttpSandboxManager',
1112
]

ms_enclave/sandbox/manager/base.py

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
"""Base sandbox manager interface."""
22

3+
import asyncio
34
from abc import ABC, abstractmethod
4-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
5-
6-
from ..model import SandboxConfig, SandboxInfo, SandboxStatus, SandboxType, ToolResult
5+
from collections import deque
6+
from typing import TYPE_CHECKING, Any, Deque, Dict, List, Optional, Union
7+
8+
from ..model import (
9+
SandboxConfig,
10+
SandboxInfo,
11+
SandboxManagerConfig,
12+
SandboxManagerType,
13+
SandboxStatus,
14+
SandboxType,
15+
ToolResult,
16+
)
717

818
if TYPE_CHECKING:
919
from ..boxes import Sandbox
@@ -12,10 +22,18 @@
1222
class SandboxManager(ABC):
1323
"""Abstract base class for sandbox managers."""
1424

15-
def __init__(self):
16-
"""Initialize the sandbox manager."""
25+
def __init__(self, config: Optional[SandboxManagerConfig] = None, **kwargs):
26+
"""Initialize the sandbox manager.
27+
28+
Args:
29+
config: Sandbox manager configuration
30+
"""
31+
self.config = config or SandboxManagerConfig()
1732
self._running = False
1833
self._sandboxes: Dict[str, 'Sandbox'] = {}
34+
self._sandbox_pool: Deque[str] = deque()
35+
self._pool_lock = asyncio.Lock()
36+
self._pool_initialized = False
1937

2038
@abstractmethod
2139
async def start(self) -> None:
@@ -144,6 +162,48 @@ async def cleanup_all_sandboxes(self) -> None:
144162
"""Clean up all sandboxes."""
145163
pass
146164

165+
@abstractmethod
166+
async def initialize_pool(
167+
self,
168+
pool_size: Optional[int] = None,
169+
sandbox_type: Optional[SandboxType] = None,
170+
config: Optional[Union[SandboxConfig, Dict]] = None
171+
) -> List[str]:
172+
"""Initialize sandbox pool.
173+
174+
Args:
175+
pool_size: Number of sandboxes in pool (uses config if not provided)
176+
sandbox_type: Type of sandbox to create
177+
config: Sandbox configuration (uses config.sandbox_config if not provided)
178+
179+
Returns:
180+
List of created sandbox IDs
181+
"""
182+
pass
183+
184+
@abstractmethod
185+
async def execute_tool_in_pool(
186+
self, tool_name: str, parameters: Dict[str, Any], timeout: Optional[float] = None
187+
) -> ToolResult:
188+
"""Execute tool using an available sandbox from the pool.
189+
190+
Uses FIFO queue to get an idle sandbox, marks it as busy during execution,
191+
then returns it to the pool as idle.
192+
193+
Args:
194+
tool_name: Tool name to execute
195+
parameters: Tool parameters
196+
timeout: Optional timeout for waiting for available sandbox
197+
198+
Returns:
199+
Tool execution result
200+
201+
Raises:
202+
ValueError: If pool is empty or no sandbox available
203+
TimeoutError: If timeout waiting for available sandbox
204+
"""
205+
pass
206+
147207
# Context manager support
148208
async def __aenter__(self):
149209
"""Async context manager entry."""
@@ -153,3 +213,81 @@ async def __aenter__(self):
153213
async def __aexit__(self, exc_type, exc_val, exc_tb):
154214
"""Async context manager exit."""
155215
await self.stop()
216+
217+
218+
class SandboxManagerFactory:
219+
"""Factory for creating sandbox managers."""
220+
221+
_registry: Dict[SandboxManagerType, type] = {}
222+
223+
@classmethod
224+
def register(cls, manager_type: SandboxManagerType, manager_class: type) -> None:
225+
"""Register a sandbox manager class.
226+
227+
Args:
228+
manager_type: Manager type to register
229+
manager_class: Manager class to register
230+
"""
231+
cls._registry[manager_type] = manager_class
232+
233+
@classmethod
234+
def create_manager(
235+
cls,
236+
manager_type: Optional[SandboxManagerType] = None,
237+
config: Optional[SandboxManagerConfig] = None,
238+
**kwargs
239+
) -> SandboxManager:
240+
"""Create a sandbox manager instance.
241+
242+
Args:
243+
manager_type: Type of manager to create
244+
config: Manager configuration
245+
**kwargs: Additional manager-specific parameters
246+
247+
Returns:
248+
Sandbox manager instance
249+
250+
Raises:
251+
ValueError: If manager type is not registered
252+
"""
253+
if not manager_type:
254+
api_url = config.base_url if config else kwargs.get('base_url')
255+
if api_url:
256+
manager_type = SandboxManagerType.HTTP
257+
else:
258+
manager_type = SandboxManagerType.LOCAL
259+
260+
if manager_type not in cls._registry:
261+
raise ValueError(
262+
f"Sandbox manager type '{manager_type}' not registered. "
263+
f'Available types: {list(cls._registry.keys())}'
264+
)
265+
266+
manager_class = cls._registry[manager_type]
267+
return manager_class(config=config, **kwargs)
268+
269+
@classmethod
270+
def get_registered_types(cls) -> List[SandboxManagerType]:
271+
"""Get list of registered manager types.
272+
273+
Returns:
274+
List of registered manager types
275+
"""
276+
return list(cls._registry.keys())
277+
278+
279+
def register_manager(manager_type: SandboxManagerType):
280+
"""Decorator to register a sandbox manager class.
281+
282+
Args:
283+
manager_type: Manager type to register
284+
285+
Returns:
286+
Decorator function
287+
"""
288+
289+
def decorator(manager_class: type) -> type:
290+
SandboxManagerFactory.register(manager_type, manager_class)
291+
return manager_class
292+
293+
return decorator

0 commit comments

Comments
 (0)