Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,56 @@ asyncio.run(main())
- Using LocalSandboxManager: Uniformly orchestrate lifecycle/cleanup of multiple sandboxes on local machine; suitable for service-oriented, multi-task parallel scenarios
- Using HttpSandboxManager: Manage sandboxes uniformly through remote HTTP service; suitable for cross-machine/distributed or stronger isolation deployments

### 0) Manager Factory: SandboxManagerFactory (Automatic Local/HTTP selection)

When to use:
- You want a single entry point that chooses Local or HTTP manager automatically.
- You prefer central registration and discovery of available manager types.

Key points:
- If manager_type is provided, it is used directly.
- If base_url is provided (in config or kwargs), HTTP manager is created.
- Otherwise, Local manager is created by default.

Example: implicit selection by base_url
```python
import asyncio
from ms_enclave.sandbox.manager import SandboxManagerFactory

async def main():
async with SandboxManagerFactory.create_manager(base_url='http://127.0.0.1:8000') as m:
# Use exactly like HttpSandboxManager
# e.g., create a DOCKER sandbox and execute a tool
# ... your code ...
pass

asyncio.run(main())
```

Example: explicit selection + custom config
```python
import asyncio
from ms_enclave.sandbox.manager import SandboxManagerFactory
from ms_enclave.sandbox.model import SandboxManagerConfig, SandboxManagerType

async def main():
cfg = SandboxManagerConfig(cleanup_interval=600)
async with SandboxManagerFactory.create_manager(
manager_type=SandboxManagerType.LOCAL, config=cfg
) as m:
# Use exactly like LocalSandboxManager
# ... your code ...
pass

asyncio.run(main())
```

Discover registered manager types:
```python
from ms_enclave.sandbox.manager import SandboxManagerFactory
print(SandboxManagerFactory.get_registered_types())
```

### 1) Direct Sandbox Creation: SandboxFactory (Lightweight, Temporary)

Use Cases:
Expand Down Expand Up @@ -189,6 +239,66 @@ async def main():
asyncio.run(main())
```

### 4) Pooled Sandboxes: Pre-warmed workers (Sandbox Pool)

Why:
- Amortize container startup by keeping a fixed-size pool of ready sandboxes.
- Each execution borrows a sandbox and returns it; requests queue FIFO when all are busy.

Local pool example:

```python
import asyncio
from ms_enclave.sandbox.manager import LocalSandboxManager
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
async with LocalSandboxManager() as m:
cfg = DockerSandboxConfig(
image='python:3.11-slim',
tools_config={'python_executor': {}}
)
# Create a pool of 2 pre-warmed sandboxes
await m.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)

# Execute multiple tasks; sandboxes are reused and queued FIFO when busy
tasks = [
m.execute_tool_in_pool('python_executor', {'code': f'print("task {i}")', 'timeout': 30})
for i in range(5)
]
results = await asyncio.gather(*tasks)
print([r.output.strip() for r in results])

# Pool stats
stats = await m.get_stats()
print('pool_size =', stats['pool_size'])

asyncio.run(main())
```

HTTP pool example:

```python
import asyncio
from ms_enclave.sandbox.manager import HttpSandboxManager
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
async with HttpSandboxManager(base_url='http://127.0.0.1:8000') as m:
cfg = DockerSandboxConfig(image='python:3.11-slim', tools_config={'python_executor': {}})
await m.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)

r = await m.execute_tool_in_pool('python_executor', {'code': 'print("hello from pool")', 'timeout': 30})
print(r.output)

asyncio.run(main())
```

Notes:
- Waiting timeout: `await m.execute_tool_in_pool(..., timeout=1.0)` raises `TimeoutError` if no sandbox is available in time.
- FIFO behavior: pool borrows/returns in FIFO order under load.
- Errors: even if a tool execution fails, the sandbox is returned to the pool.

---

## Sandbox Types & Tool Support
Expand Down Expand Up @@ -233,6 +343,10 @@ DockerNotebookConfig(tools_config={'notebook_executor': {}})
- `network_enabled`: Whether to enable network (Notebook sandbox requires True)
- `remove_on_exit`: Whether to delete container on exit (default True)

Manager Config (SandboxManagerConfig):
- `base_url`: If set, HttpSandboxManager is selected automatically
- `cleanup_interval`: Background cleanup interval in seconds (local manager)

**Example of Installing Additional Dependencies in Sandbox**
```python
async with SandboxFactory.create_sandbox(SandboxType.DOCKER, config) as sandbox:
Expand Down
114 changes: 114 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,56 @@ asyncio.run(main())
- 使用 LocalSandboxManager:在本机统一编排多个沙箱的生命周期/清理;适合服务化、多任务并行场景
- 使用 HttpSandboxManager:通过远程 HTTP 服务统一管理沙箱;适合跨机/分布式或隔离更强的部署

### 0) 管理器工厂:SandboxManagerFactory(自动选择本地/HTTP)

适用场景:
- 希望用一个入口根据参数自动选择本地或 HTTP 管理器
- 需要查询已注册的管理器类型,或统一构造逻辑

要点:
- 显式传入 manager_type 时,按类型创建
- 当提供 base_url(在 config 或 kwargs)时,创建 HTTP 管理器
- 两者都未提供时,默认创建本地管理器

示例:通过 base_url 隐式选择 HTTP 管理器
```python
import asyncio
from ms_enclave.sandbox.manager import SandboxManagerFactory

async def main():
async with SandboxManagerFactory.create_manager(base_url='http://127.0.0.1:8000') as m:
# 与 HttpSandboxManager 用法一致
# 例如:创建 DOCKER 沙箱并执行工具
# ... 你的代码 ...
pass

asyncio.run(main())
```

示例:显式选择 + 自定义配置
```python
import asyncio
from ms_enclave.sandbox.manager import SandboxManagerFactory
from ms_enclave.sandbox.model import SandboxManagerConfig, SandboxManagerType

async def main():
cfg = SandboxManagerConfig(cleanup_interval=600)
async with SandboxManagerFactory.create_manager(
manager_type=SandboxManagerType.LOCAL, config=cfg
) as m:
# 与 LocalSandboxManager 用法一致
# ... 你的代码 ...
pass

asyncio.run(main())
```

查看已注册类型:
```python
from ms_enclave.sandbox.manager import SandboxManagerFactory
print(SandboxManagerFactory.get_registered_types())
```

### 1) 直接创建沙箱:SandboxFactory(轻量、临时)

适用场景:
Expand Down Expand Up @@ -191,6 +241,66 @@ async def main():
asyncio.run(main())
```

### 4) 沙箱池:预热复用的工作进程(Sandbox Pool)

为何使用:
- 通过预热固定数量的沙箱,摊销容器启动开销,提高吞吐。
- 每次执行从池中借出沙箱并在完成后归还;当全部忙碌时按 FIFO 排队。

本地管理示例:

```python
import asyncio
from ms_enclave.sandbox.manager import LocalSandboxManager
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
async with LocalSandboxManager() as m:
cfg = DockerSandboxConfig(
image='python:3.11-slim',
tools_config={'python_executor': {}}
)
# 预热 2 个沙箱
await m.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)

# 多次执行;忙时按 FIFO 排队,执行完成后归还至池中
tasks = [
m.execute_tool_in_pool('python_executor', {'code': f'print("task {i}")', 'timeout': 30})
for i in range(5)
]
results = await asyncio.gather(*tasks)
print([r.output.strip() for r in results])

# 查看统计
stats = await m.get_stats()
print('pool_size =', stats['pool_size'])

asyncio.run(main())
```

HTTP 管理示例:

```python
import asyncio
from ms_enclave.sandbox.manager import HttpSandboxManager
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
async with HttpSandboxManager(base_url='http://127.0.0.1:8000') as m:
cfg = DockerSandboxConfig(image='python:3.11-slim', tools_config={'python_executor': {}})
await m.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)

r = await m.execute_tool_in_pool('python_executor', {'code': 'print("hello from pool")', 'timeout': 30})
print(r.output)

asyncio.run(main())
```

说明:
- 等待超时:`await m.execute_tool_in_pool(..., timeout=1.0)` 若在超时时间内无可用沙箱将抛出 `TimeoutError`。
- FIFO 行为:在并发负载下,借还顺序遵循 FIFO。
- 错误处理:即使执行失败,沙箱也会归还至池中。

---

## 沙箱类型与工具支持
Expand Down Expand Up @@ -235,6 +345,10 @@ DockerNotebookConfig(tools_config={'notebook_executor': {}})
- `network_enabled`: 是否启用网络(Notebook 沙箱需 True)
- `remove_on_exit`: 退出后是否删除容器(默认 True)

管理器配置(SandboxManagerConfig):
- `base_url`:若设置则自动选择 HttpSandboxManager
- `cleanup_interval`:本地管理器的后台清理间隔(秒)

**Sandbox中安装额外依赖示例**
```python
async with SandboxFactory.create_sandbox(SandboxType.DOCKER, config) as sandbox:
Expand Down
6 changes: 3 additions & 3 deletions ms_enclave/sandbox/manager/local_manager.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Sandbox environment manager."""

import asyncio
from collections import Counter, deque
from collections import Counter
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, Union

from ms_enclave.sandbox.model.constants import DEFAULT_POOL_EXECUTION_TIMEOUT
from ms_enclave.utils import get_logger

from ..boxes import Sandbox, SandboxFactory
Expand Down Expand Up @@ -372,7 +373,6 @@ async def execute_tool_in_pool(
Args:
tool_name: Tool name to execute
parameters: Tool parameters
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The timeout parameter was removed from the docstring, but it is still part of the function signature and is used within the method. To maintain documentation accuracy and prevent confusion, the timeout parameter should be documented in the docstring.

Suggested change
parameters: Tool parameters
parameters: Tool parameters
timeout: Optional timeout for waiting for available sandbox

Comment on lines 373 to 375
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout parameter was removed from the docstring but it still exists in the function signature at line 364. The docstring should document this parameter. It represents the maximum time to wait for an available sandbox from the pool before raising a TimeoutError.

Copilot uses AI. Check for mistakes.
timeout: Optional timeout for waiting for available sandbox

Returns:
Tool execution result
Expand All @@ -387,7 +387,7 @@ async def execute_tool_in_pool(
if not self._pool_condition:
raise RuntimeError('Sandbox manager not started')

timeout = timeout or self.config.timeout
timeout = timeout or self.config.timeout or DEFAULT_POOL_EXECUTION_TIMEOUT

async with self._pool_condition:
# Wait for an available sandbox
Expand Down
1 change: 1 addition & 0 deletions ms_enclave/sandbox/model/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFAULT_POOL_EXECUTION_TIMEOUT = 3600 # 1 hour