Skip to content
Open
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ limitations under the License.

✨ **Simplicity**: the logic for agents fits in ~1,000 lines of code (see [agents.py](https://github.com/huggingface/smolagents/blob/main/src/smolagents/agents.py)). We kept abstractions to their minimal shape above raw code!

🧑‍💻 **First-class support for Code Agents**. Our [`CodeAgent`](https://huggingface.co/docs/smolagents/reference/agents#smolagents.CodeAgent) writes its actions in code (as opposed to "agents being used to write code"). To make it secure, we support executing in sandboxed environments via [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/), [Modal](https://modal.com/), or Docker.
🧑‍💻 **First-class support for Code Agents**. Our [`CodeAgent`](https://huggingface.co/docs/smolagents/reference/agents#smolagents.CodeAgent) writes its actions in code (as opposed to "agents being used to write code"). To make it secure, we support executing in sandboxed environments via [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/), [Modal](https://modal.com/), [Tenki](https://tenki.cloud), or Docker.

🤗 **Hub integrations**: you can [share/pull tools or agents to/from the Hub](https://huggingface.co/docs/smolagents/reference/tools#smolagents.Tool.from_hub) for instant sharing of the most efficient agents!

Expand Down Expand Up @@ -239,7 +239,7 @@ for request in requests_to_search:
Writing actions as code snippets is demonstrated to work better than the current industry practice of letting the LLM output a dictionary of the tools it wants to call: [uses 30% fewer steps](https://huggingface.co/papers/2402.01030) (thus 30% fewer LLM calls) and [reaches higher performance on difficult benchmarks](https://huggingface.co/papers/2411.01747). Head to [our high-level intro to agents](https://huggingface.co/docs/smolagents/conceptual_guides/intro_agents) to learn more on that.

Since code execution can be a serious security concern (arbitrary code execution!), **you should run agent code in a sandbox**. We support several options:
- [E2B](https://e2b.dev/), [Blaxel](https://blaxel.ai), [Modal](https://modal.com/) — managed cloud sandboxes, simplest to set up
- [E2B](https://e2b.dev/), [Blaxel](https://blaxel.ai), [Modal](https://modal.com/), [Tenki](https://tenki.cloud) — managed cloud sandboxes, simplest to set up
- [Docker](https://www.docker.com/) — self-hosted container isolation

The built-in `LocalPythonExecutor` is **not a security sandbox**. It applies some restrictions but can be bypassed and must not be used as a security boundary.
Expand Down
2 changes: 1 addition & 1 deletion docs/source/en/guided_tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ This could also be authorized by using `numpy.*`, which will allow `numpy` as we

The execution will stop at any code trying to perform an illegal operation or if there is a regular Python error with the code generated by the agent.

You can also use [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/docs#what-is-e2-b), or Docker instead of a local Python interpreter. For Blaxel, first [set the `BL_API_KEY` and `BL_WORKSPACE` environment variables](https://app.blaxel.ai/profile/security) and then pass `executor_type="blaxel"` upon agent initialization. For E2B, first [set the `E2B_API_KEY` environment variable](https://e2b.dev/dashboard?tab=keys) and then pass `executor_type="e2b"`. For Docker, pass `executor_type="docker"`.
You can also use [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/docs#what-is-e2-b), [Tenki](https://tenki.cloud), or Docker instead of a local Python interpreter. For Blaxel, first [set the `BL_API_KEY` and `BL_WORKSPACE` environment variables](https://app.blaxel.ai/profile/security) and then pass `executor_type="blaxel"` upon agent initialization. For E2B, first [set the `E2B_API_KEY` environment variable](https://e2b.dev/dashboard?tab=keys) and then pass `executor_type="e2b"`. For Tenki, first set the `TENKI_API_KEY` environment variable (plus `TENKI_PROJECT_ID` if your account has more than one project) and then pass `executor_type="tenki"`. For Docker, pass `executor_type="docker"`.


> [!TIP]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Key features of `smolagents` include:

✨ **Simplicity**: The logic for agents fits in ~thousand lines of code. We kept abstractions to their minimal shape above raw code!

🧑‍💻 **First-class support for Code Agents**: [`CodeAgent`](reference/agents#smolagents.CodeAgent) writes its actions in code (as opposed to "agents being used to write code") to invoke tools or perform computations, enabling natural composability (function nesting, loops, conditionals). To make it secure, we support [executing in sandboxed environment](tutorials/secure_code_execution) via [Modal](https://modal.com/), [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/), or Docker.
🧑‍💻 **First-class support for Code Agents**: [`CodeAgent`](reference/agents#smolagents.CodeAgent) writes its actions in code (as opposed to "agents being used to write code") to invoke tools or perform computations, enabling natural composability (function nesting, loops, conditionals). To make it secure, we support [executing in sandboxed environment](tutorials/secure_code_execution) via [Modal](https://modal.com/), [Blaxel](https://blaxel.ai), [E2B](https://e2b.dev/), [Tenki](https://tenki.cloud), or Docker.

📡 **Common Tool-Calling Agent Support**: In addition to CodeAgents, [`ToolCallingAgent`](reference/agents#smolagents.ToolCallingAgent) supports usual JSON/text-based tool-calling for scenarios where that paradigm is preferred.

Expand Down
8 changes: 8 additions & 0 deletions docs/source/en/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ Extras for executing code remotely:
```bash
pip install "smolagents[e2b]"
```
- **tenki**: Enable Tenki support for remote execution.
```bash
pip install "smolagents[tenki]"
```
- **docker**: Add support for executing code in Docker containers.
```bash
pip install "smolagents[docker]"
Expand All @@ -202,6 +206,10 @@ Extras for executing code remotely:
```bash
uv pip install "smolagents[e2b]"
```
- **tenki**: Enable Tenki support for remote execution.
```bash
uv pip install "smolagents[tenki]"
```
- **docker**: Add support for executing code in Docker containers.
```bash
uv pip install "smolagents[docker]"
Expand Down
4 changes: 4 additions & 0 deletions docs/source/en/reference/python_executors.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ available executor implementations.

[[autodoc]] smolagents.remote_executors.ModalExecutor

### TenkiExecutor

[[autodoc]] smolagents.remote_executors.TenkiExecutor

### DockerExecutor

[[autodoc]] smolagents.remote_executors.DockerExecutor
39 changes: 37 additions & 2 deletions docs/source/en/tutorials/secure_code_execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ When working with AI agents that execute code, security is paramount. There are

![Sandbox approaches comparison](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/smolagents/sandboxed_execution.png)

1. **Running individual code snippets in a sandbox**: This approach (left side of diagram) only executes the agent-generated Python code snippets in a sandbox while keeping the rest of the agentic system in your local environment. It's simpler to set up using `executor_type="blaxel"`, `executor_type="e2b"`, `executor_type="modal"`, or
1. **Running individual code snippets in a sandbox**: This approach (left side of diagram) only executes the agent-generated Python code snippets in a sandbox while keeping the rest of the agentic system in your local environment. It's simpler to set up using `executor_type="blaxel"`, `executor_type="e2b"`, `executor_type="modal"`, `executor_type="tenki"`, or
`executor_type="docker"`, but it doesn't support multi-agents and still requires passing state data between your environment and the sandbox.

2. **Running the entire agentic system in a sandbox**: This approach (right side of diagram) runs the entire agentic system, including the agent, model, and tools, within a sandbox environment. This provides better isolation but requires more manual setup and may require passing sensitive credentials (like API keys) to the sandbox environment.
Expand Down Expand Up @@ -279,6 +279,41 @@ with CodeAgent(model=InferenceClientModel(), tools=[], executor_type="modal") as

The agent state and generated code from the `InferenceClientModel` are sent to a Modal sandbox, which can securely execute code inside them.

### Tenki setup

#### Installation

1. Create a Tenki account at [tenki.cloud](https://tenki.cloud)
2. Install the required packages:
```bash
pip install 'smolagents[tenki]'
```
3. Set your API key as an environment variable:
```bash
export TENKI_API_KEY="your-api-key"
```
If your account has more than one project, also set `TENKI_PROJECT_ID` (or pass `executor_kwargs={"create_kwargs": {"project_id": ...}}`);
with a single project it is detected automatically.

#### Running your agent in Tenki: quick start

We provide a simple way to use a Tenki Sandbox: simply add `executor_type="tenki"` to the agent initialization, as follows:

```py
from smolagents import InferenceClientModel, CodeAgent

with CodeAgent(model=InferenceClientModel(), tools=[], executor_type="tenki") as agent:
agent.run("What is the 42th Fibonacci number?")
```

> [!TIP]
> Using the agent as a context manager (with the `with` statement) ensures that the Tenki sandbox is cleaned up immediately after the agent completes its task.
> Alternatively, you can manually call the agent's `cleanup()` method.

Each agent run executes in a disposable Linux microVM with kernel-level isolation, booted from a pre-warmed pool.
You can customize the sandbox via `executor_kwargs`, e.g. `executor_kwargs={"create_kwargs": {"cpu_cores": 4, "memory_mb": 8192}}`;
the sandbox image only needs to provide `python3` (`pip` and the Jupyter Kernel Gateway are installed on startup if missing).

### Docker setup

#### Installation
Expand Down Expand Up @@ -434,7 +469,7 @@ finally:

### Best practices for sandboxes

These key practices apply to Blaxel, E2B, and Docker sandboxes:
These key practices apply to Blaxel, E2B, Tenki, and Docker sandboxes:

- Resource management
- Set memory and CPU limits
Expand Down
5 changes: 5 additions & 0 deletions examples/sandboxed_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@
with CodeAgent(tools=[WebSearchTool()], model=model, executor_type="modal") as agent:
output = agent.run("How many seconds would it take for a leopard at full speed to run through Pont des Arts?")
print("Modal executor result:", output)

# Tenki executor example
with CodeAgent(tools=[WebSearchTool()], model=model, executor_type="tenki") as agent:
output = agent.run("How many seconds would it take for a leopard at full speed to run through Pont des Arts?")
print("Tenki executor result:", output)
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ modal = [
"modal>=1.1.3",
"websocket-client",
]
tenki = [
"tenki-sandbox>=0.4.0",
"websocket-client",
]
openai = [
"openai>=1.58.1"
]
Expand Down Expand Up @@ -90,7 +94,7 @@ vllm = [
"torch"
]
all = [
"smolagents[audio,blaxel,docker,e2b,gradio,litellm,mcp,mlx-lm,modal,openai,telemetry,toolkit,transformers,vision,bedrock]",
"smolagents[audio,blaxel,docker,e2b,gradio,litellm,mcp,mlx-lm,modal,openai,telemetry,tenki,toolkit,transformers,vision,bedrock]",
]
quality = [
"ruff>=0.9.0",
Expand Down
9 changes: 5 additions & 4 deletions src/smolagents/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
Monitor,
TokenUsage,
)
from .remote_executors import BlaxelExecutor, DockerExecutor, E2BExecutor, ModalExecutor
from .remote_executors import BlaxelExecutor, DockerExecutor, E2BExecutor, ModalExecutor, TenkiExecutor
from .tools import BaseTool, Tool, validate_tool_arguments
from .utils import (
AgentError,
Expand Down Expand Up @@ -1513,7 +1513,7 @@ class CodeAgent(MultiStepAgent):
additional_authorized_imports (`list[str]`, *optional*): Additional authorized imports for the agent.
planning_interval (`int`, *optional*): Interval at which the agent will run a planning step.
executor ([`PythonExecutor`], *optional*): Custom Python code executor. If not provided, a default executor will be created based on `executor_type`.
executor_type (`Literal["local", "blaxel", "e2b", "modal", "docker"]`, default `"local"`): Type of code executor.
executor_type (`Literal["local", "blaxel", "e2b", "modal", "docker", "tenki"]`, default `"local"`): Type of code executor.
executor_kwargs (`dict`, *optional*): Additional arguments to pass to initialize the executor.
max_print_outputs_length (`int`, *optional*): Maximum length of the print outputs.
stream_outputs (`bool`, *optional*, default `False`): Whether to stream outputs during execution.
Expand All @@ -1532,7 +1532,7 @@ def __init__(
additional_authorized_imports: list[str] | None = None,
planning_interval: int | None = None,
executor: PythonExecutor = None,
executor_type: Literal["local", "blaxel", "e2b", "modal", "docker"] = "local",
executor_type: Literal["local", "blaxel", "e2b", "modal", "docker", "tenki"] = "local",
executor_kwargs: dict[str, Any] | None = None,
max_print_outputs_length: int | None = None,
stream_outputs: bool = False,
Expand Down Expand Up @@ -1596,7 +1596,7 @@ def cleanup(self):
self.python_executor.cleanup()

def create_python_executor(self) -> PythonExecutor:
if self.executor_type not in {"local", "blaxel", "e2b", "modal", "docker"}:
if self.executor_type not in {"local", "blaxel", "e2b", "modal", "docker", "tenki"}:
raise ValueError(f"Unsupported executor type: {self.executor_type}")

if self.executor_type == "local":
Expand All @@ -1612,6 +1612,7 @@ def create_python_executor(self) -> PythonExecutor:
"e2b": E2BExecutor,
"docker": DockerExecutor,
"modal": ModalExecutor,
"tenki": TenkiExecutor,
}
return remote_executors[self.executor_type](
self.additional_authorized_imports, self.logger, **self.executor_kwargs
Expand Down
Loading