Skip to content

Commit df6baf4

Browse files
xuanyang15copybara-github
authored andcommitted
feat(integrations): Add DaytonaEnvironment for remote sandbox workspaces
Co-authored-by: Xuan Yang <xygoogle@google.com> PiperOrigin-RevId: 941353505
1 parent 3466586 commit df6baf4

10 files changed

Lines changed: 700 additions & 12 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Daytona Environment Sample
2+
3+
## Overview
4+
5+
A small data analysis agent that uses the `DaytonaEnvironment` with the
6+
`EnvironmentToolset` to download public datasets and analyze them inside a
7+
[Daytona](https://daytona.io) remote sandbox.
8+
9+
Instead of running on the local machine, all commands and file operations
10+
execute in an isolated remote sandbox with internet access. Asked a question,
11+
the agent downloads a public dataset (a GCS-hosted world population /
12+
demographics dataset by default), installs `pandas` on demand, writes a short
13+
analysis script, runs it, and reports the result — all without touching the
14+
user's machine. This makes the sandbox a natural fit for running
15+
model-generated code safely and keeping the host clean.
16+
17+
## Prerequisites
18+
19+
1. Install the `daytona` extra:
20+
21+
```bash
22+
pip install google-adk[daytona]
23+
```
24+
25+
1. Set your Daytona configuration. Get a server and API key by following the
26+
Daytona installation guide (e.g. self-hosted or via Daytona Cloud).
27+
28+
If you are using Daytona Cloud, you only need to set:
29+
30+
```bash
31+
export DAYTONA_API_KEY="your-api-key"
32+
```
33+
34+
If you are using a self-hosted Daytona server, also set:
35+
36+
```bash
37+
export DAYTONA_API_URL="your-api-url"
38+
```
39+
40+
## Sample Inputs
41+
42+
- `Download the world demographics dataset and tell me which country has the largest population.`
43+
44+
The agent downloads the dataset, installs `pandas`, filters to country-level
45+
rows, and finds the maximum. Expected: China (`CN`), ≈ 1.44 billion, just
46+
ahead of India (`IN`) at ≈ 1.38 billion.
47+
48+
- `For the United States, what is the urban vs rural population split?`
49+
50+
A follow-up to the previous turn. Because the sandbox persists across the
51+
session, the agent reuses the already-downloaded CSV and the installed
52+
`pandas` — it only writes and runs a new script. Expected for `US`: urban
53+
≈ 270.7 million vs rural ≈ 57.6 million (out of ≈ 331 million total).
54+
55+
- `Using https://storage.googleapis.com/cloud-samples-data/bigquery/us-states/us-states.csv, how many US states are listed?`
56+
57+
Demonstrates pointing the agent at your own dataset URL instead of the
58+
default.
59+
60+
## Graph
61+
62+
```mermaid
63+
graph TD
64+
User -->|question| Agent[data_analysis_agent]
65+
Agent -->|EnvironmentToolset| Sandbox[DaytonaEnvironment sandbox]
66+
Sandbox -->|download / install / run| Agent
67+
Agent -->|answer| User
68+
```
69+
70+
## How To
71+
72+
The agent is a standalone `Agent` (no workflow graph) wired to a single
73+
`EnvironmentToolset` whose `environment` is a `DaytonaEnvironment`:
74+
75+
```python
76+
from google.adk.integrations.daytona import DaytonaEnvironment
77+
from google.adk.tools.environment import EnvironmentToolset
78+
79+
EnvironmentToolset(
80+
environment=DaytonaEnvironment(timeout=300),
81+
)
82+
```
83+
84+
- `timeout` bounds the sandbox lifetime in seconds.
85+
- By default, it will spin up a sandbox from the built-in default Python snapshot.
86+
If you want to use a custom Docker image instead, you can pass it to the
87+
`image` parameter (e.g. `image="python:3.12"`).
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""A data analysis agent that runs Python in a Daytona remote sandbox."""
16+
17+
from google.adk import Agent
18+
from google.adk.integrations.daytona import DaytonaEnvironment
19+
from google.adk.tools.environment import EnvironmentToolset
20+
21+
root_agent = Agent(
22+
name="data_analysis_agent",
23+
description=(
24+
"A data analysis agent that downloads public datasets and analyzes"
25+
" them inside a Daytona remote sandbox."
26+
),
27+
instruction="""\
28+
You are a data analysis assistant. You work inside an isolated Daytona remote
29+
sandbox that has internet access, where you can safely download data and run
30+
Python, so you never touch the user's machine.
31+
32+
To analyze a dataset:
33+
1. Download it from the internet into the working directory, e.g. with
34+
`curl -O <url>` or `wget <url>`.
35+
2. Install whatever you need on demand, e.g. `pip install pandas`.
36+
3. Write a short Python script that loads the data and computes the answer.
37+
4. Run the script and report the result, showing the numbers you found.
38+
39+
Prefer writing a script and executing it over guessing. If a command fails,
40+
read the error, fix the script, and try again.
41+
""",
42+
tools=[EnvironmentToolset(environment=DaytonaEnvironment())],
43+
)

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ optional-dependencies.agent-identity = [
6565
]
6666
optional-dependencies.all = [
6767
"anyio>=4.9,<5",
68+
"daytona>=0.191",
6869
"e2b>=2,<3",
6970
"google-api-python-client>=2.157,<3",
7071
"google-cloud-aiplatform[agent-engines]>=1.148.1,<2",
@@ -101,6 +102,9 @@ optional-dependencies.benchmark = [
101102
optional-dependencies.community = [
102103
"google-adk-community",
103104
]
105+
optional-dependencies.daytona = [
106+
"daytona>=0.191", # For DaytonaEnvironment remote sandbox.
107+
]
104108
optional-dependencies.db = [
105109
"sqlalchemy>=2,<3",
106110
"sqlalchemy-spanner>=1.14",
@@ -199,6 +203,7 @@ optional-dependencies.test = [
199203
"anyio>=4.9,<5",
200204
"beautifulsoup4>=3.2.2",
201205
"crewai[tools]; python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
206+
"daytona>=0.191",
202207
"docker>=7", # For ContainerCodeExecutor tests
203208
"e2b>=2,<3",
204209
"gepa>=0.1",

src/google/adk/features/_feature_registry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class FeatureName(str, Enum):
3737
COMPUTER_USE = "COMPUTER_USE"
3838
DATA_AGENT_TOOL_CONFIG = "DATA_AGENT_TOOL_CONFIG"
3939
DATA_AGENT_TOOLSET = "DATA_AGENT_TOOLSET"
40+
DAYTONA_ENVIRONMENT = "DAYTONA_ENVIRONMENT"
41+
E2B_ENVIRONMENT = "E2B_ENVIRONMENT"
4042
ENVIRONMENT_SIMULATION = "ENVIRONMENT_SIMULATION"
4143
GCS_ADMIN_TOOLSET = "GCS_ADMIN_TOOLSET"
4244
GCS_TOOL_SETTINGS = "GCS_TOOL_SETTINGS"
@@ -128,6 +130,12 @@ class FeatureConfig:
128130
FeatureName.DATA_AGENT_TOOLSET: FeatureConfig(
129131
FeatureStage.EXPERIMENTAL, default_on=True
130132
),
133+
FeatureName.DAYTONA_ENVIRONMENT: FeatureConfig(
134+
FeatureStage.EXPERIMENTAL, default_on=True
135+
),
136+
FeatureName.E2B_ENVIRONMENT: FeatureConfig(
137+
FeatureStage.EXPERIMENTAL, default_on=True
138+
),
131139
FeatureName.ENVIRONMENT_SIMULATION: FeatureConfig(
132140
FeatureStage.EXPERIMENTAL, default_on=True
133141
),
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Daytona sandbox integration.
16+
17+
This module provides a BaseEnvironment implementation backed by a Daytona
18+
remote sandbox, offering a persistent remote workspace for file CRUD and
19+
shell execution.
20+
21+
Requires the ``daytona`` extra: ``pip install google-adk[daytona]``.
22+
23+
Example:
24+
```python
25+
from google.adk.integrations.daytona import DaytonaEnvironment
26+
27+
env = DaytonaEnvironment()
28+
await env.initialize()
29+
result = await env.execute("pip install requests")
30+
await env.close()
31+
```
32+
"""
33+
34+
from ._daytona_environment import DaytonaEnvironment
35+
36+
__all__ = [
37+
"DaytonaEnvironment",
38+
]

0 commit comments

Comments
 (0)