Skip to content

Simplify workspace mounting with SANDBOX_VOLUMES #8242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9367e3d
Simplify workspace mounting with RUNTIME_MOUNT environment variable
openhands-agent May 3, 2025
7bdb4e8
Add support for multiple mount paths with RUNTIME_MOUNT and CUSTOM_VO…
openhands-agent May 3, 2025
eb2f4a7
Fix mypy type error in Docker runtime
openhands-agent May 3, 2025
c4a347b
Remove RUNTIME_MOUNT and update tests to use CUSTOM_VOLUMES
openhands-agent May 3, 2025
c77fa8e
Fix linting issues in test_docker_runtime.py
openhands-agent May 3, 2025
25ef7d8
Update tests and documentation to use CUSTOM_VOLUMES instead of depre…
openhands-agent May 4, 2025
f4506b8
🤖 Auto-fix Python linting issues
openhands-agent May 5, 2025
06bc45b
Refactor Docker runtime volume processing and update tests to call ac…
openhands-agent May 5, 2025
652e9b9
🤖 Auto-fix Python linting issues
openhands-agent May 5, 2025
1afb038
Merge branch 'main' into openhands/multiple-mount-paths
enyst May 5, 2025
70ca48c
Move custom_volumes to SandboxConfig and rename to volumes, update al…
openhands-agent May 5, 2025
7aae2e0
Merge branch 'main' into openhands/multiple-mount-paths
xingyaoww May 5, 2025
4757ef5
docs: improve documentation about /workspace path and read-only data …
openhands-agent May 5, 2025
2458616
Update tests/unit/test_config.py
xingyaoww May 5, 2025
bca19f4
Merge branch 'main' into openhands/multiple-mount-paths
xingyaoww May 5, 2025
da5ee15
🤖 Auto-fix Python linting issues
openhands-agent May 5, 2025
b8fcae4
Merge branch 'main' into openhands/multiple-mount-paths
xingyaoww May 5, 2025
1777ca0
Mark workspace_* parameters as deprecated in configuration-options.md
openhands-agent May 6, 2025
636a69e
Update translations to mark workspace_* parameters as deprecated
openhands-agent May 6, 2025
76633c8
Update all translation files for configuration-options.md
openhands-agent May 6, 2025
6f3db69
remove update_translations.py
xingyaoww May 6, 2025
79c0e86
update a huge chunk of documentations results from docs/translation_u…
xingyaoww May 6, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,62 @@ nikolaik の `SANDBOX_RUNTIME_CONTAINER_IMAGE` は、ランタイムサーバー

## ファイルシステムへの接続
ここでの便利な機能の1つは、ローカルファイルシステムに接続する機能です。ファイルシステムをランタイムにマウントするには:

### SANDBOX_VOLUMES の使用

ローカルファイルシステムをマウントする最も簡単な方法は、`SANDBOX_VOLUMES` 環境変数を使用することです:

```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e SANDBOX_VOLUMES=/path/to/your/code:/workspace:rw \
# ...
```

`SANDBOX_VOLUMES` の形式は:`ホストパス:コンテナパス[:モード]`

- `ホストパス`:マウントしたいホストマシン上のパス
- `コンテナパス`:ホストパスがマウントされるコンテナ内のパス
- エージェントに変更させたいファイルには `/workspace` を使用してください。エージェントはデフォルトで `/workspace` で作業します。
- 読み取り専用の参照資料や大きなデータセットには、別のパス(例:`/data`)を使用してください
- `モード`:オプションのマウントモード、`rw`(読み書き可能、デフォルト)または `ro`(読み取り専用)

カンマ(`,`)で区切ることで、複数のマウントを指定することもできます:

```bash
export SANDBOX_VOLUMES=/path1:/workspace/path1,/path2:/workspace/path2:ro
```

例:

```bash
# Linux と Mac の例 - 書き込み可能なワークスペース
export SANDBOX_VOLUMES=$HOME/OpenHands:/workspace:rw

# Windows の WSL の例 - 書き込み可能なワークスペース
export SANDBOX_VOLUMES=/mnt/c/dev/OpenHands:/workspace:rw

# 読み取り専用参照コードの例
export SANDBOX_VOLUMES=/path/to/reference/code:/data:ro

# 複数マウントの例 - 書き込み可能なワークスペースと読み取り専用データ
export SANDBOX_VOLUMES=$HOME/projects:/workspace:rw,/path/to/large/dataset:/data:ro
```

> **注意:** 複数のマウントを使用する場合、最初のマウントが主要なワークスペースと見なされ、単一のワークスペースを想定するツールとの後方互換性のために使用されます。

> **重要:** エージェントはデフォルトで `/workspace` で作業します。ローカルディレクトリ内のファイルをエージェントに変更させたい場合は、そのディレクトリを `/workspace` にマウントする必要があります。エージェントにアクセスさせたいが変更させたくない読み取り専用データがある場合は、別のパス(例:`/data`)にマウントし、エージェントにそこを見るよう明示的に指示してください。

### WORKSPACE_* 変数の使用(非推奨)

> **注意:** この方法は非推奨であり、将来のバージョンで削除される予定です。代わりに `SANDBOX_VOLUMES` を使用してください。

1. `WORKSPACE_BASE` を設定します:

```bash
export WORKSPACE_BASE=/path/to/your/code

# Linux と Mac の例
# export WORKSPACE_BASE=$HOME/OpenHands
# $WORKSPACE_BASE を /home/<username>/OpenHands に設定します
#
# Windows の WSL の例
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# $WORKSPACE_BASE を C:\dev\OpenHands に設定します
```

2. 以下のオプションを `docker run` コマンドに追加します:

```bash
Expand Down
7 changes: 4 additions & 3 deletions docs/modules/usage/how-to/cli-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ To run OpenHands in CLI mode with Docker:

1. Set the following environmental variables in your terminal:

- `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
- `SANDBOX_VOLUMES` to specify the directory you want OpenHands to access (Ex: `export SANDBOX_VOLUMES=$(pwd)/workspace:/workspace:rw`).
- The agent works in `/workspace` by default, so mount your project directory there if you want the agent to modify files.
- For read-only data, use a different mount path (Ex: `export SANDBOX_VOLUMES=$(pwd)/workspace:/workspace:rw,/path/to/large/dataset:/data:ro`).
- `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
- `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).

Expand All @@ -37,10 +39,9 @@ docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.36-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
-e LLM_API_KEY=$LLM_API_KEY \
-e LLM_MODEL=$LLM_MODEL \
-v $WORKSPACE_BASE:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.openhands-state:/.openhands-state \
--add-host host.docker.internal:host-gateway \
Expand Down
7 changes: 4 additions & 3 deletions docs/modules/usage/how-to/headless-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ To run OpenHands in Headless mode with Docker:

1. Set the following environmental variables in your terminal:

- `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
- `SANDBOX_VOLUMES` to specify the directory you want OpenHands to access (Ex: `export SANDBOX_VOLUMES=$(pwd)/workspace:/workspace:rw`).
- The agent works in `/workspace` by default, so mount your project directory there if you want the agent to modify files.
- For read-only data, use a different mount path (Ex: `export SANDBOX_VOLUMES=$(pwd)/workspace:/workspace:rw,/path/to/large/dataset:/data:ro`).
- `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
- `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).

Expand All @@ -34,11 +36,10 @@ docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.36-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e SANDBOX_VOLUMES=$SANDBOX_VOLUMES \
-e LLM_API_KEY=$LLM_API_KEY \
-e LLM_MODEL=$LLM_MODEL \
-e LOG_ALL_EVENTS=true \
-v $WORKSPACE_BASE:/opt/workspace_base \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.openhands-state:/.openhands-state \
--add-host host.docker.internal:host-gateway \
Expand Down
59 changes: 51 additions & 8 deletions docs/modules/usage/runtimes/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,62 @@ You can also [build your own runtime image](../how-to/custom-sandbox-guide).

## Connecting to Your filesystem
A useful feature is the ability to connect to your local filesystem. To mount your filesystem into the runtime:

### Using SANDBOX_VOLUMES

The simplest way to mount your local filesystem is to use the `SANDBOX_VOLUMES` environment variable:

```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e SANDBOX_VOLUMES=/path/to/your/code:/workspace:rw \
# ...
```

The `SANDBOX_VOLUMES` format is: `host_path:container_path[:mode]`

- `host_path`: The path on your host machine that you want to mount
- `container_path`: The path inside the container where the host path will be mounted
- Use `/workspace` for files you want the agent to modify. The agent works in `/workspace` by default.
- Use a different path (e.g., `/data`) for read-only reference materials or large datasets
- `mode`: Optional mount mode, either `rw` (read-write, default) or `ro` (read-only)

You can also specify multiple mounts by separating them with commas (`,`):

```bash
export SANDBOX_VOLUMES=/path1:/workspace/path1,/path2:/workspace/path2:ro
```

Examples:

```bash
# Linux and Mac Example - Writable workspace
export SANDBOX_VOLUMES=$HOME/OpenHands:/workspace:rw

# WSL on Windows Example - Writable workspace
export SANDBOX_VOLUMES=/mnt/c/dev/OpenHands:/workspace:rw

# Read-only reference code example
export SANDBOX_VOLUMES=/path/to/reference/code:/data:ro

# Multiple mounts example - Writable workspace with read-only reference data
export SANDBOX_VOLUMES=$HOME/projects:/workspace:rw,/path/to/large/dataset:/data:ro
```

> **Note:** When using multiple mounts, the first mount is considered the primary workspace and will be used for backward compatibility with tools that expect a single workspace.

> **Important:** The agent will work in `/workspace` by default. If you want the agent to modify files in your local directory, you should mount that directory to `/workspace`. If you have read-only data that you want the agent to access but not modify, mount it to a different path (like `/data`) and explicitly instruct the agent to look there.

### Using WORKSPACE_* variables (Deprecated)

> **Note:** This method is deprecated and will be removed in a future version. Please use `SANDBOX_VOLUMES` instead.

1. Set `WORKSPACE_BASE`:

```bash
export WORKSPACE_BASE=/path/to/your/code

# Linux and Mac Example
# export WORKSPACE_BASE=$HOME/OpenHands
# Will set $WORKSPACE_BASE to /home/<username>/OpenHands
#
# WSL on Windows Example
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# Will set $WORKSPACE_BASE to C:\dev\OpenHands
```

2. Add the following options to the `docker run` command:

```bash
Expand Down
20 changes: 15 additions & 5 deletions docs/modules/usage/runtimes/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,26 @@ Via environment variables:
export RUNTIME=local

# Optional but recommended
export WORKSPACE_BASE=/path/to/your/workspace
# The agent works in /workspace by default, so mount your project directory there
export SANDBOX_VOLUMES=/path/to/your/workspace:/workspace:rw
# For read-only data, use a different mount path
# export SANDBOX_VOLUMES=/path/to/your/workspace:/workspace:rw,/path/to/large/dataset:/data:ro
```

Via `config.toml`:

```toml
[core]
runtime = "local"
workspace_base = "/path/to/your/workspace"

[sandbox]
# The agent works in /workspace by default, so mount your project directory there
volumes = "/path/to/your/workspace:/workspace:rw"
# For read-only data, use a different mount path
# volumes = "/path/to/your/workspace:/workspace:rw,/path/to/large/dataset:/data:ro"
```

If `WORKSPACE_BASE` is not set, the runtime will create a temporary directory for the agent to work in.
If `SANDBOX_VOLUMES` is not set, the runtime will create a temporary directory for the agent to work in.

## Example Usage

Expand All @@ -48,8 +56,10 @@ Here's an example of how to start OpenHands with the Local Runtime in Headless M
# Set the runtime type to local
export RUNTIME=local

# Optionally set a workspace directory
export WORKSPACE_BASE=/path/to/your/project
# Set a workspace directory (the agent works in /workspace by default)
export SANDBOX_VOLUMES=/path/to/your/project:/workspace:rw
# For read-only data that you don't want the agent to modify, use a different path
# export SANDBOX_VOLUMES=/path/to/your/project:/workspace:rw,/path/to/reference/data:/data:ro

# Start OpenHands
poetry run python -m openhands.core.main -t "write a bash script that prints hi"
Expand Down
1 change: 1 addition & 0 deletions openhands/agenthub/codeact_agent/codeact_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

if TYPE_CHECKING:
from litellm import ChatCompletionToolParam

from openhands.events.action import Action
from openhands.llm.llm import ModelResponse

Expand Down
2 changes: 1 addition & 1 deletion openhands/agenthub/readonly_agent/readonly_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"""

import os

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from litellm import ChatCompletionToolParam

from openhands.events.action import Action
from openhands.llm.llm import ModelResponse

Expand Down
10 changes: 6 additions & 4 deletions openhands/core/config/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ class AppConfig(BaseModel):
save_trajectory_path: str | None = Field(default=None)
save_screenshots_in_trajectory: bool = Field(default=False)
replay_trajectory_path: str | None = Field(default=None)
workspace_base: str | None = Field(default=None)
workspace_mount_path: str | None = Field(default=None)
workspace_mount_path_in_sandbox: str = Field(default='/workspace')
workspace_mount_rewrite: str | None = Field(default=None)

# Deprecated parameters - will be removed in a future version
workspace_base: str | None = Field(default=None, deprecated=True)
workspace_mount_path: str | None = Field(default=None, deprecated=True)
workspace_mount_path_in_sandbox: str = Field(default='/workspace', deprecated=True)
workspace_mount_rewrite: str | None = Field(default=None, deprecated=True)
cache_dir: str = Field(default='/tmp/cache')
run_as_openhands: bool = Field(default=True)
max_iterations: int = Field(default=OH_MAX_ITERATIONS)
Expand Down
4 changes: 4 additions & 0 deletions openhands/core/config/sandbox_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ class SandboxConfig(BaseModel):
selected_repo: str | None = Field(default=None)
trusted_dirs: list[str] = Field(default_factory=list)
vscode_port: int | None = Field(default=None)
volumes: str | None = Field(
default=None,
description="Volume mounts in the format 'host_path:container_path[:mode]', e.g. '/my/host/dir:/workspace:rw'. Multiple mounts can be specified using commas, e.g. '/path1:/workspace/path1,/path2:/workspace/path2:ro'",
)

model_config = {'extra': 'forbid'}

Expand Down
49 changes: 45 additions & 4 deletions openhands/core/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,51 @@ def get_or_create_jwt_secret(file_store: FileStore) -> str:

def finalize_config(cfg: AppConfig) -> None:
"""More tweaks to the config after it's been loaded."""
if cfg.workspace_base is not None:
cfg.workspace_base = os.path.abspath(cfg.workspace_base)
if cfg.workspace_mount_path is None:
cfg.workspace_mount_path = cfg.workspace_base
# Handle the sandbox.volumes parameter
if cfg.sandbox.volumes is not None:
# Split by commas to handle multiple mounts
mounts = cfg.sandbox.volumes.split(',')

# Use the first volume for backward compatibility
if mounts:
primary_mount = mounts[0]
parts = primary_mount.split(':')
if len(parts) < 2 or len(parts) > 3:
raise ValueError(
f'Invalid sandbox.volumes format: {primary_mount}. '
f"Expected format: 'host_path:container_path[:mode]', e.g. '/my/host/dir:/workspace:rw'"
)

host_path = os.path.abspath(parts[0])
container_path = parts[1]

# Set the workspace_mount_path and workspace_mount_path_in_sandbox for backward compatibility
cfg.workspace_mount_path = host_path
cfg.workspace_mount_path_in_sandbox = container_path

# Also set workspace_base for backward compatibility
cfg.workspace_base = host_path

# Validate all mounts
for mount in mounts:
parts = mount.split(':')
if len(parts) < 2 or len(parts) > 3:
raise ValueError(
f'Invalid mount format in sandbox.volumes: {mount}. '
f"Expected format: 'host_path:container_path[:mode]', e.g. '/my/host/dir:/workspace:rw'"
)

# Handle the deprecated workspace_* parameters
elif cfg.workspace_base is not None or cfg.workspace_mount_path is not None:
logger.openhands_logger.warning(
'DEPRECATED: The WORKSPACE_BASE and WORKSPACE_MOUNT_PATH environment variables are deprecated. '
"Please use RUNTIME_MOUNT instead, e.g. 'RUNTIME_MOUNT=/my/host/dir:/workspace:rw'"
)

if cfg.workspace_base is not None:
cfg.workspace_base = os.path.abspath(cfg.workspace_base)
if cfg.workspace_mount_path is None:
cfg.workspace_mount_path = cfg.workspace_base

if cfg.workspace_mount_rewrite:
base = cfg.workspace_base or os.getcwd()
Expand Down
1 change: 1 addition & 0 deletions openhands/events/observation/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import traceback
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Self

from pydantic import BaseModel
Expand Down
Loading
Loading