Skip to content
Open
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
8 changes: 8 additions & 0 deletions constraints-3.10.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ attrs==26.1.0
# aiohttp
# e2b
# jsonschema
# opensandbox
# referencing
authlib==1.7.2
# via
Expand Down Expand Up @@ -723,6 +724,7 @@ httpx==0.28.1
# llama-index-core
# mcp
# openai
# opensandbox
httpx-sse==0.4.3
# via
# -c constraints-3.10.txt.stable.tmp
Expand Down Expand Up @@ -1032,6 +1034,10 @@ openpyxl==3.1.5
# via
# -c constraints-3.10.txt.stable.tmp
# google-adk (pyproject.toml)
opensandbox==0.1.15
# via
# -c constraints-3.10.txt.stable.tmp
# google-adk (pyproject.toml)
opentelemetry-api==1.42.1
# via
# -c constraints-3.10.txt.stable.tmp
Expand Down Expand Up @@ -1340,6 +1346,7 @@ pydantic==2.13.4
# llama-index-workflows
# mcp
# openai
# opensandbox
# pydantic-settings
# toolbox-core
pydantic-core==2.46.4
Expand Down Expand Up @@ -1431,6 +1438,7 @@ python-dateutil==2.9.0.post0
# google-cloud-bigquery
# kubernetes
# oci
# opensandbox
# pandas
python-discovery==1.4.4
# via
Expand Down
8 changes: 8 additions & 0 deletions constraints-3.11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ attrs==26.1.0
# aiohttp
# e2b
# jsonschema
# opensandbox
# referencing
authlib==1.7.2
# via
Expand Down Expand Up @@ -794,6 +795,7 @@ httpx==0.28.1
# llama-index-core
# mcp
# openai
# opensandbox
httpx-sse==0.4.3
# via
# -c constraints-3.11.txt.stable.tmp
Expand Down Expand Up @@ -1170,6 +1172,10 @@ openpyxl==3.1.5
# -c constraints-3.11.txt.stable.tmp
# google-adk (pyproject.toml)
# crewai
opensandbox==0.1.15
# via
# -c constraints-3.11.txt.stable.tmp
# google-adk (pyproject.toml)
opentelemetry-api==1.42.1
# via
# -c constraints-3.11.txt.stable.tmp
Expand Down Expand Up @@ -1542,6 +1548,7 @@ pydantic==2.12.5
# llama-index-workflows
# mcp
# openai
# opensandbox
# pydantic-settings
# toolbox-core
pydantic-core==2.41.5
Expand Down Expand Up @@ -1654,6 +1661,7 @@ python-dateutil==2.9.0.post0
# kubernetes
# lance-namespace-urllib3-client
# oci
# opensandbox
# pandas
# pendulum
# posthog
Expand Down
8 changes: 8 additions & 0 deletions constraints-3.12.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ attrs==26.1.0
# aiohttp
# e2b
# jsonschema
# opensandbox
# referencing
authlib==1.7.2
# via
Expand Down Expand Up @@ -707,6 +708,7 @@ httpx==0.28.1
# llama-index-core
# mcp
# openai
# opensandbox
httpx-sse==0.4.3
# via
# -c constraints-3.12.txt.stable.tmp
Expand Down Expand Up @@ -1020,6 +1022,10 @@ openpyxl==3.1.5
# via
# -c constraints-3.12.txt.stable.tmp
# google-adk (pyproject.toml)
opensandbox==0.1.15
# via
# -c constraints-3.12.txt.stable.tmp
# google-adk (pyproject.toml)
opentelemetry-api==1.42.1
# via
# -c constraints-3.12.txt.stable.tmp
Expand Down Expand Up @@ -1328,6 +1334,7 @@ pydantic==2.13.4
# llama-index-workflows
# mcp
# openai
# opensandbox
# pydantic-settings
# toolbox-core
pydantic-core==2.46.4
Expand Down Expand Up @@ -1419,6 +1426,7 @@ python-dateutil==2.9.0.post0
# google-cloud-bigquery
# kubernetes
# oci
# opensandbox
# pandas
python-discovery==1.4.4
# via
Expand Down
8 changes: 8 additions & 0 deletions constraints-3.13.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ attrs==26.1.0
# aiohttp
# e2b
# jsonschema
# opensandbox
# referencing
authlib==1.7.2
# via
Expand Down Expand Up @@ -699,6 +700,7 @@ httpx==0.28.1
# llama-index-core
# mcp
# openai
# opensandbox
httpx-sse==0.4.3
# via
# -c constraints-3.13.txt.stable.tmp
Expand Down Expand Up @@ -1012,6 +1014,10 @@ openpyxl==3.1.5
# via
# -c constraints-3.13.txt.stable.tmp
# google-adk (pyproject.toml)
opensandbox==0.1.15
# via
# -c constraints-3.13.txt.stable.tmp
# google-adk (pyproject.toml)
opentelemetry-api==1.42.1
# via
# -c constraints-3.13.txt.stable.tmp
Expand Down Expand Up @@ -1320,6 +1326,7 @@ pydantic==2.13.4
# llama-index-workflows
# mcp
# openai
# opensandbox
# pydantic-settings
# toolbox-core
pydantic-core==2.46.4
Expand Down Expand Up @@ -1411,6 +1418,7 @@ python-dateutil==2.9.0.post0
# google-cloud-bigquery
# kubernetes
# oci
# opensandbox
# pandas
python-discovery==1.4.4
# via
Expand Down
8 changes: 8 additions & 0 deletions constraints-3.14.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ attrs==26.1.0
# aiohttp
# e2b
# jsonschema
# opensandbox
# referencing
authlib==1.7.2
# via
Expand Down Expand Up @@ -699,6 +700,7 @@ httpx==0.28.1
# llama-index-core
# mcp
# openai
# opensandbox
httpx-sse==0.4.3
# via
# -c constraints-3.14.txt.stable.tmp
Expand Down Expand Up @@ -1012,6 +1014,10 @@ openpyxl==3.1.5
# via
# -c constraints-3.14.txt.stable.tmp
# google-adk (pyproject.toml)
opensandbox==0.1.15
# via
# -c constraints-3.14.txt.stable.tmp
# google-adk (pyproject.toml)
opentelemetry-api==1.42.1
# via
# -c constraints-3.14.txt.stable.tmp
Expand Down Expand Up @@ -1320,6 +1326,7 @@ pydantic==2.13.4
# llama-index-workflows
# mcp
# openai
# opensandbox
# pydantic-settings
# toolbox-core
pydantic-core==2.46.4
Expand Down Expand Up @@ -1411,6 +1418,7 @@ python-dateutil==2.9.0.post0
# google-cloud-bigquery
# kubernetes
# oci
# opensandbox
# pandas
python-discovery==1.4.4
# via
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# OpenSandbox Environment Sample

## Overview

This sample uses `OpenSandboxEnvironment` with `EnvironmentToolset` so an ADK
agent can execute commands and edit files in an isolated, persistent remote
workspace. The default environment creates a `python:3.11` sandbox, keeps its
five-minute lifetime active while the agent uses it, and destroys it when the
toolset closes.

OpenSandbox can run locally on Docker or as a self-hosted remote deployment.
See the [OpenSandbox documentation](https://open-sandbox.ai) for server setup.

## Prerequisites

1. Install the OpenSandbox extra:

```bash
pip install google-adk[opensandbox]
```

1. Start an OpenSandbox server. A local server uses `localhost:8080` by
default. For a remote deployment, configure its domain and API key:

```bash
export OPEN_SANDBOX_DOMAIN="https://sandbox.example.com"
export OPEN_SANDBOX_API_KEY="your-api-key"
```

1. Configure the model credentials required by your ADK setup.

## Sample Inputs

- `Write a Python script that prints the first 20 Fibonacci numbers, run it, and report the output.`
- `Create a binary file containing bytes 0 through 255, then verify its size and SHA-256 digest.`
- `Create a small CSV of five products and write a Python script that reports the most expensive one.`

## Graph

```mermaid
graph TD
Agent[opensandbox_coding_agent] -->|calls| Toolset[EnvironmentToolset]
Toolset --> Execute[Execute]
Toolset --> ReadFile[ReadFile]
Toolset --> WriteFile[WriteFile]
Toolset --> EditFile[EditFile]
Toolset -->|runs in| Environment[OpenSandboxEnvironment]
```

## How To

The agent is connected to a normal `EnvironmentToolset`:

```python
from google.adk.integrations.opensandbox import OpenSandboxEnvironment
from google.adk.tools.environment import EnvironmentToolset

toolset = EnvironmentToolset(
environment=OpenSandboxEnvironment(
image="python:3.11",
timeout=300,
)
)
```

The environment maps ADK's lifecycle, command, and byte-oriented file APIs to
the OpenSandbox async SDK. Relative paths resolve below `/workspace`.

To reuse an existing sandbox, pass `sandbox_id`. The environment treats an
attached sandbox as caller-owned, so closing the toolset releases the SDK client
without destroying the remote sandbox:

```python
environment = OpenSandboxEnvironment(sandbox_id="existing-sandbox-id")
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from . import agent
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""An agent that runs generated code in an OpenSandbox workspace."""

from __future__ import annotations

from google.adk import Agent
from google.adk.integrations.opensandbox import OpenSandboxEnvironment
from google.adk.tools.environment import EnvironmentToolset

root_agent = Agent(
name="opensandbox_coding_agent",
description=(
"A coding agent that executes commands and edits files in an isolated "
"OpenSandbox workspace."
),
instruction="""\
You are a coding assistant. Work only through the environment tools, which run
inside an isolated OpenSandbox workspace rather than on the user's machine.

For each request:
1. Inspect relevant files before changing them.
2. Write the smallest script or data file needed to solve the request.
3. Execute the script and use its actual output in your answer.
4. If a command fails, read stderr, fix the problem, and retry.
""",
tools=[
EnvironmentToolset(
environment=OpenSandboxEnvironment(
image="python:3.11",
timeout=300,
)
)
],
)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ optional-dependencies.all = [
"oci>=2.126",
"openai>=2.20,<3",
"openpyxl>=3.1.5,<4",
"opensandbox>=0.1.15,<0.2",
"opentelemetry-exporter-gcp-logging>=1.9.0a0,<=1.12.0a0",
"opentelemetry-exporter-gcp-monitoring>=1.9.0a0,<2",
"opentelemetry-exporter-gcp-trace>=1.9,<2",
Expand Down Expand Up @@ -250,6 +251,9 @@ optional-dependencies.mcp = [
optional-dependencies.oci = [
"oci>=2.126", # OCI Generative AI native SDK (OCIGenAILlm)
]
optional-dependencies.opensandbox = [
"opensandbox>=0.1.15,<0.2", # For OpenSandboxEnvironment remote sandbox.
]
optional-dependencies.otel-gcp = [
"opentelemetry-instrumentation-google-genai>=0.7b1,<1",
"opentelemetry-instrumentation-grpc>=0.43b0,<1",
Expand Down Expand Up @@ -301,6 +305,7 @@ optional-dependencies.test = [
"nltk!=3.10.1", # Transitive via rouge-score and llama-index-core; 3.10.1's import hook breaks any venv living inside the working directory (reverted upstream in nltk/nltk#3732).
"openai>=2.20,<3",
"openpyxl>=3.1.5,<4",
"opensandbox>=0.1.15,<0.2",
"opentelemetry-exporter-gcp-logging>=1.9.0a0,<=1.12.0a0",
"opentelemetry-exporter-gcp-monitoring>=1.9.0a0,<2",
"opentelemetry-exporter-gcp-trace>=1.9,<2",
Expand Down
4 changes: 4 additions & 0 deletions src/google/adk/features/_feature_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class FeatureName(str, Enum):
DYNAMIC_INSTRUCTION_ROUTING = "DYNAMIC_INSTRUCTION_ROUTING"
DAYTONA_ENVIRONMENT = "DAYTONA_ENVIRONMENT"
E2B_ENVIRONMENT = "E2B_ENVIRONMENT"
OPENSANDBOX_ENVIRONMENT = "OPENSANDBOX_ENVIRONMENT"
ENVIRONMENT_SIMULATION = "ENVIRONMENT_SIMULATION"
EVENTARC_TOOL_CONFIG = "EVENTARC_TOOL_CONFIG"
EVENTARC_TOOLSET = "EVENTARC_TOOLSET"
Expand Down Expand Up @@ -143,6 +144,9 @@ class FeatureConfig:
FeatureName.E2B_ENVIRONMENT: FeatureConfig(
FeatureStage.EXPERIMENTAL, default_on=True
),
FeatureName.OPENSANDBOX_ENVIRONMENT: FeatureConfig(
FeatureStage.EXPERIMENTAL, default_on=True
),
FeatureName.ENVIRONMENT_SIMULATION: FeatureConfig(
FeatureStage.EXPERIMENTAL, default_on=True
),
Expand Down
Loading