Skip to content

modelscope/ms-enclave

Repository files navigation



中文   |   English  

PyPI version PyPI - Downloads

📖 中文文档   |   📖 English Documentation

⭐ If you like this project, please click the "Star" button in the upper right corner to support us. Your support is our motivation to move forward!

Introduction

ms-enclave is a modular and stable sandbox runtime environment that provides a secure isolated execution environment for applications. It implements strong isolation through Docker containers, equipped with local/HTTP managers and an extensible tool system, helping you execute code safely and efficiently in a controlled environment.

  • 🔒 Secure Isolation: Complete isolation and resource limits based on Docker
  • 🧩 Modular: Both sandboxes and tools are extensible (registered factory)
  • ⚡ Stable Performance: Clean implementation, fast startup, with lifecycle management
  • 🌐 Remote Management: Built-in FastAPI service, supports HTTP management
  • 🔧 Tool System: Standardized tools enabled by sandbox type (OpenAI-style schema)

System Requirements

  • Python >= 3.10
  • Operating System: Linux, macOS, or Windows with Docker support
  • Docker daemon available on local machine (Notebook sandbox requires port 8888 open)

Installation

Install from PyPI

pip install ms-enclave
# If Docker support is needed, install extra dependencies
pip install 'ms-enclave[docker]'

Install from Source

git clone https://github.com/modelscope/ms-enclave.git
cd ms-enclave
pip install -e .
# If Docker support is needed, install extra dependencies
pip install -e '.[docker]'

Quick Start: Minimal Viable Example (SandboxFactory)

Tools need to be explicitly enabled in the configured tools_config, otherwise they won't be registered.

import asyncio
from ms_enclave.sandbox.boxes import SandboxFactory
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
    config = DockerSandboxConfig(
        image='python:3.11-slim',
        memory_limit='512m',
        tools_config={
            'python_executor': {},
            'file_operation': {},
            'shell_executor': {}
        }
    )

    async with SandboxFactory.create_sandbox(SandboxType.DOCKER, config) as sandbox:
        # 1) Write file
        await sandbox.execute_tool('file_operation', {
            'operation': 'write', 'file_path': '/sandbox/hello.txt', 'content': 'hi from enclave'
        })
        # 2) Execute Python code
        result = await sandbox.execute_tool('python_executor', {
            'code': "print('Hello from sandbox!')\nprint(open('/sandbox/hello.txt').read())"
        })
        print(result.output)

asyncio.run(main())

Agent Model Tool Calling (OpenAI Tools)

Expose sandbox tools to the Agent in OpenAI Tools format, allowing the model to trigger tools and execute them securely in the sandbox.

import asyncio, os, json
from openai import OpenAI
from ms_enclave.sandbox.manager import SandboxManagerFactory
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def demo():
    client = OpenAI(
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
        api_key=os.getenv("DASHSCOPE_API_KEY")
    )
    async with SandboxManagerFactory.create_manager() as m:
        cfg = DockerSandboxConfig(image="python:3.11-slim", tools_config={"python_executor": {}, "shell_executor": {}})
        sid = await m.create_sandbox(SandboxType.DOCKER, cfg)

        tools = list((await m.get_sandbox_tools(sid)).values())
        messages = [{"role": "user", "content": "Print 'hello' in Python, then list /sandbox via shell."}]

        rsp = client.chat.completions.create(model="qwen-plus", messages=messages, tools=tools, tool_choice="auto")
        msg = rsp.choices[0].message
        messages.append(msg.model_dump())

        if getattr(msg, "tool_calls", None):
            for tc in msg.tool_calls:
                name = tc.function.name
                args = json.loads(tc.function.arguments or "{}")
                result = await m.execute_tool(sid, name, args)
                messages.append({"role": "tool", "content": result.model_dump_json(), "tool_call_id": tc.id, "name": name})
            final = client.chat.completions.create(model="qwen-plus", messages=messages)
            print(final.choices[0].message.content or "")
        else:
            print(msg.content or "")

asyncio.run(demo())

Notes:

  • Use get_sandbox_tools(sandbox_id) to retrieve tool schemas (OpenAI-compatible)
  • Pass tools=... to the model, handle returned tool_calls and execute them in the sandbox
  • Call the model again to generate the final answer

Typical Usage Patterns & Examples

  • Direct use of SandboxFactory: Create/destroy sandboxes within a single process, most lightweight; suitable for scripts or one-time tasks
  • 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

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

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:

from ms_enclave.sandbox.manager import SandboxManagerFactory
print(SandboxManagerFactory.get_registered_types())

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

Use Cases:

  • Temporarily run a piece of code in scripts or microservices
  • Fine-grained control over sandbox lifecycle (cleanup on context exit)

Example (Docker sandbox + Python execution):

import asyncio
from ms_enclave.sandbox.boxes import SandboxFactory
from ms_enclave.sandbox.model import DockerSandboxConfig, SandboxType

async def main():
    cfg = DockerSandboxConfig(
        tools_config={'python_executor': {}}
    )
    async with SandboxFactory.create_sandbox(SandboxType.DOCKER, cfg) as sb:
        r = await sb.execute_tool('python_executor', {
            'code': 'import platform; print(platform.python_version())'
        })
        print(r.output)

asyncio.run(main())

2) Local Unified Orchestration: LocalSandboxManager (Multiple Sandboxes, Lifecycle Management)

Use Cases:

  • Need to create/manage multiple sandboxes within the same process (create, query, stop, periodic cleanup)
  • Want unified status view, statistics, and health checks

Example:

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

async def main():
    async with LocalSandboxManager() as manager:
        cfg = DockerSandboxConfig(tools_config={'shell_executor': {}})
        sandbox_id = await manager.create_sandbox(SandboxType.DOCKER, cfg)

        # Execute command
        res = await manager.execute_tool(sandbox_id, 'shell_executor', {'command': 'echo hello'})
        print(res.output.strip())  # hello

        # View list
        infos = await manager.list_sandboxes()
        print([i.id for i in infos])

        # Stop and delete
        await manager.stop_sandbox(sandbox_id)
        await manager.delete_sandbox(sandbox_id)

asyncio.run(main())

3) Remote Unified Management: HttpSandboxManager (Cross-machine/Isolated Deployment)

Use Cases:

  • Run sandbox service on a separate host/container, invoke remotely via HTTP
  • Multiple applications share a secure controlled sandbox cluster

Start the service first (choose one):

# Method A: Command line
ms-enclave server --host 0.0.0.0 --port 8000

# Method B: Python startup
python -c "from ms_enclave.sandbox import create_server; create_server().run(host='0.0.0.0', port=8000)"

Client example:

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(tools_config={'python_executor': {}})
        sid = await m.create_sandbox(SandboxType.DOCKER, cfg)
        r = await m.execute_tool(sid, 'python_executor', {'code': 'print("Hello remote")'})
        print(r.output)
        await m.delete_sandbox(sid)

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:

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:

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

Current built-in sandbox types:

  • DOCKER (General container execution)

    • Supported tools:
      • python_executor (Execute Python code)
      • shell_executor (Execute Shell commands)
      • file_operation (Read/write/delete/list files)
      • multi_code_executor (Multi-language code execution, supports python, cpp, csharp, go, java, nodejs, ts, rust, php, bash, pytest, jest, go_test, lua, r, perl, d_ut, ruby, scala, julia, kotlin_script, verilog, lean, swift, racket) Requires specifying image volcengine/sandbox-fusion:server-20250609
    • Features: Configurable memory/CPU limits, volume mounts, network toggle, privileged mode, port mapping
  • DOCKER_NOTEBOOK (Jupyter Kernel Gateway environment)

    • Supported tools:
      • notebook_executor (Execute code through Jupyter kernel, supports saving code context)
    • Note: This type only loads notebook_executor; other DOCKER-specific tools won't be enabled in this sandbox
    • Dependencies: Requires port 8888 exposed, network enabled

Tool loading rules:

  • Tools are only initialized and available when explicitly declared in tools_config
  • Tools validate required_sandbox_type, automatically ignored if mismatched

Example:

DockerSandboxConfig(tools_config={'python_executor': {}, 'shell_executor': {}, 'file_operation': {}})
DockerNotebookConfig(tools_config={'notebook_executor': {}})

Common Configuration Options

  • image: Docker image name (e.g., python:3.11-slim or jupyter-kernel-gateway)
  • memory_limit: Memory limit (e.g., 512m/1g)
  • cpu_limit: CPU limit (float, >0)
  • volumes: Volume mounts, formatted as {host_path: {"bind": "/container/path", "mode": "rw"}}
  • ports: Port mapping, formatted as { "8888/tcp": ("127.0.0.1", 8888) }
  • 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

async with SandboxFactory.create_sandbox(SandboxType.DOCKER, config) as sandbox:
    # 1) Write a file
    requirements_file = '/sandbox/requirements.txt'
    await sandbox.execute_tool('file_operation', {
        'operation': 'write', 'file_path': f'{requirements_file}', 'content': 'numpy\npandas\nmodelscope\n'
    })
    # 2) Execute Python code
    result = await sandbox.execute_tool('python_executor', {
        'code': f"print('Hello from sandbox!')\nprint(open(f'{requirements_file}').read())"
    })
    print(result.output)

    # 3) Execute CLI
    result_cli = await sandbox.execute_command(f'pip install -r {requirements_file}')
    print(result_cli.stdout, flush=True)

Example of Reading/Writing Host Files in Sandbox

async with LocalSandboxManager() as manager:
    # Create sandbox
    config = DockerSandboxConfig(
        # image='python-sandbox',
        image='python:3.11-slim',
        tools_config={'python_executor': {}, 'file_operation': {}},
        volumes={'~/Code/ms-enclave/output': {'bind': '/sandbox/data', 'mode': 'rw'}}
    )
    sandbox_id = await manager.create_sandbox(SandboxType.DOCKER, config)

    # Write file
    result = await manager.execute_tool(
        sandbox_id, 'file_operation', {'operation': 'write', 'file_path': '/sandbox/data/hello.txt', 'content': 'Hello, Sandbox!'}
    )
    print(result.model_dump())

Error Handling & Debugging

result = await sandbox.execute_tool('python_executor', {'code': 'print(1/0)'})
if result.error:
    print('Error message:', result.error)
else:
    print('Output:', result.output)

Development & Testing

# Clone repository
git clone https://github.com/modelscope/ms-enclave.git
cd ms-enclave

# Create virtual environment
conda create -n ms-enclave python=3.10 -y
conda activate ms-enclave

# Install dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run examples (provided in repository)
python examples/sandbox_usage_examples.py
python examples/local_manager_example.py
python examples/server_manager_example.py

Contributing

We welcome contributions! Please check CONTRIBUTING.md for details.

Contribution Steps

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Develop and add tests
  4. Run tests locally: pytest
  5. Commit changes: git commit -m 'Add amazing feature'
  6. Push branch: git push origin feature/amazing-feature
  7. Submit Pull Request

License

This project is licensed under the Apache 2.0 License. See LICENSE for details.

About

A modular and stable agent sandbox runtime environment.

Resources

License

Security policy

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •