Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0b7467e
docs: standardize SDK guides format and merge reasoning documentation
xingyaoww Oct 21, 2025
41be81b
remove agent-server api
xingyaoww Oct 21, 2025
c91e368
done with llm and condenser
xingyaoww Oct 21, 2025
ae0881e
sync(openapi): agent-sdk/main cab92fc
all-hands-bot Oct 21, 2025
bed1ef2
rename tab to sdk
xingyaoww Oct 21, 2025
35f7b0b
Merge branch 'main' into xw/sdk
xingyaoww Oct 21, 2025
a215e48
Merge commit '35f7b0b67170ee67cec4620cdd4095004827e00f' into xw/sdk
xingyaoww Oct 21, 2025
6fc72c7
merge convo cost
xingyaoww Oct 21, 2025
c26f0a4
merge confirmation mode
xingyaoww Oct 21, 2025
1e64616
docs: sync code blocks from agent-sdk examples
github-actions[bot] Oct 21, 2025
8461f56
link to llm registry
xingyaoww Oct 21, 2025
fc560a9
rename and some improvements
xingyaoww Oct 21, 2025
0375219
fix image input
xingyaoww Oct 21, 2025
66ab264
fix pause and resume
xingyaoww Oct 21, 2025
307870c
improve secrets
xingyaoww Oct 21, 2025
0a26347
done with interactive terminal
xingyaoww Oct 21, 2025
0c2d58d
audited browser-use doc
xingyaoww Oct 21, 2025
a98faaa
add stuck detector
xingyaoww Oct 21, 2025
0602d16
improve custom planning agent
xingyaoww Oct 21, 2025
d503f1c
rename
xingyaoww Oct 21, 2025
7d8635f
fix broken link
xingyaoww Oct 21, 2025
a651e92
remove stuff not ready yet
xingyaoww Oct 21, 2025
f37b3e1
fix typo
xingyaoww Oct 22, 2025
45f3c0b
docs: sync code blocks from agent-sdk examples
github-actions[bot] Oct 22, 2025
8a71612
Merge branch 'main' into xw/sdk
xingyaoww Oct 22, 2025
ee1eccc
Merge commit 'b419a23b49be3ee1b701e018d026643fc9c98e90' into xw/sdk
xingyaoww Oct 22, 2025
6450938
make llm routing expandable
xingyaoww Oct 22, 2025
d8890e1
Revert "remove stuff not ready yet"
xingyaoww Oct 22, 2025
45ab46e
Merge commit '114842042edeead65560d6622d6af5f307126f85' into xw/sdk
xingyaoww Oct 22, 2025
e4dd99f
rename to agent-sdk
xingyaoww Oct 22, 2025
f66f005
initial rewrite
xingyaoww Oct 22, 2025
5a28398
rename
xingyaoww Oct 22, 2025
6424cbc
improve agent-server overview
xingyaoww Oct 22, 2025
64f58d5
audited local server
xingyaoww Oct 22, 2025
51f83bc
improve remote agent server doc
xingyaoww Oct 22, 2025
d3b2687
remove
xingyaoww Oct 22, 2025
f61e250
tweak
xingyaoww Oct 22, 2025
029ded2
simplify docker sandbox documentation
xingyaoww Oct 22, 2025
db6de52
tweaks
xingyaoww Oct 22, 2025
383a3e6
improve docs
xingyaoww Oct 22, 2025
83c8bad
remove arch docs
xingyaoww Oct 22, 2025
dc53f31
allow sync code block to work with abitrary file
xingyaoww Oct 22, 2025
7616ccf
fix
xingyaoww Oct 22, 2025
6f8775e
remove gh workflows
xingyaoww Oct 22, 2025
048d9d1
docs(sdk): update API sandbox env vars in bash block; rely on sync fo…
enyst Oct 23, 2025
cf8db21
Update sdk/guides/agent-server/api-sandbox.mdx
enyst Oct 23, 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
4 changes: 2 additions & 2 deletions .github/scripts/sync_code_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def extract_code_blocks(content: str) -> list[tuple[str, str, int, int]]:
<code content>
```
"""
# Captures examples/...*.py after the first line, then the body up to ```
pattern = r'```python[^\n]*\s+(examples/[^\s]+\.py)\n(.*?)```'
# Captures ...*.py after the first line, then the body up to ```
pattern = r'```python[^\n]*\s+([^\s]+\.py)\n(.*?)```'
matches: list[tuple[str, str, int, int]] = []
for match in re.finditer(pattern, content, re.DOTALL):
file_ref = match.group(1)
Expand Down
7 changes: 6 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,15 @@
{
"group": "Remote Agent Server",
"pages": [
"sdk/guides/agent-server/overview",
"sdk/guides/agent-server/local-server",
"sdk/guides/agent-server/docker-sandbox",
"sdk/guides/agent-server/api-sandbox",
{
"group": "API Reference",
"openapi": {
"source": "/openapi/agent-sdk.json"
"source": "/openapi/agent-sdk.json",
"directory": "sdk/guides/agent-server/api-reference"
}
}
]
Expand Down
190 changes: 190 additions & 0 deletions sdk/guides/agent-server/api-sandbox.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
title: API-based Sandbox
description: Connect to hosted API-based agent server for fully managed infrastructure.
---

The API-sandboxed agent server demonstrates how to use `APIRemoteWorkspace` to connect to a [OpenHands runtime API service](https://runtime.all-hands.dev/). This eliminates the need to manage your own infrastructure, providing automatic scaling, monitoring, and secure sandboxed execution.

## Basic Example

<Note>
This example is available on GitHub: [examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py](https://github.com/OpenHands/agent-sdk/blob/main/examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py)
</Note>

This example shows how to connect to a hosted runtime API for fully managed agent execution:

```python icon="python" expandable examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py
"""Example: APIRemoteWorkspace with Dynamic Build.

This example demonstrates building an agent-server image on-the-fly from the SDK
codebase and launching it in a remote sandboxed environment via Runtime API.

Usage:
uv run examples/24_remote_convo_with_api_sandboxed_server.py

Requirements:
- LITELLM_API_KEY: API key for LLM access
- RUNTIME_API_KEY: API key for runtime API access
"""

import os
import time

from pydantic import SecretStr

from openhands.sdk import (
LLM,
Conversation,
RemoteConversation,
get_logger,
)
from openhands.tools.preset.default import get_default_agent
from openhands.workspace import APIRemoteWorkspace


logger = get_logger(__name__)


api_key = os.getenv("LITELLM_API_KEY")
assert api_key, "LITELLM_API_KEY required"

llm = LLM(
usage_id="agent",
model="litellm_proxy/anthropic/claude-sonnet-4-5-20250929",
base_url="https://llm-proxy.eval.all-hands.dev",
api_key=SecretStr(api_key),
)

runtime_api_key = os.getenv("RUNTIME_API_KEY")
if not runtime_api_key:
logger.error("RUNTIME_API_KEY required")
exit(1)


with APIRemoteWorkspace(
runtime_api_url="https://runtime.eval.all-hands.dev",
runtime_api_key=runtime_api_key,
server_image="ghcr.io/openhands/agent-server:main-python",
) as workspace:
agent = get_default_agent(llm=llm, cli_mode=True)
received_events: list = []
last_event_time = {"ts": time.time()}

def event_callback(event) -> None:
received_events.append(event)
last_event_time["ts"] = time.time()

result = workspace.execute_command(
"echo 'Hello from sandboxed environment!' && pwd"
)
logger.info(f"Command completed: {result.exit_code}, {result.stdout}")

conversation = Conversation(
agent=agent, workspace=workspace, callbacks=[event_callback], visualize=True
)
assert isinstance(conversation, RemoteConversation)

try:
conversation.send_message(
"Read the current repo and write 3 facts about the project into FACTS.txt."
)
conversation.run()

while time.time() - last_event_time["ts"] < 2.0:
time.sleep(0.1)

conversation.send_message("Great! Now delete that file.")
conversation.run()
finally:
conversation.close()
```

```bash Running the Example
export LLM_API_KEY="your-api-key"
# If using the OpenHands LLM proxy, set its base URL:
export LLM_BASE_URL="https://llm-proxy.eval.all-hands.dev"
export RUNTIME_API_KEY="your-runtime-api-key"
# Set the runtime API URL for the remote sandbox
export RUNTIME_API_URL="https://runtime.eval.all-hands.dev"
cd agent-sdk
uv run python examples/02_remote_agent_server/04_convo_with_api_sandboxed_server.py
```

## Key Concepts

### APIRemoteWorkspace

The `APIRemoteWorkspace` connects to a hosted runtime API service:

```python highlight={48-52}
with APIRemoteWorkspace(
runtime_api_url="https://runtime.eval.all-hands.dev",
runtime_api_key=runtime_api_key,
server_image="ghcr.io/openhands/agent-server:main-python",
) as workspace:
```

This workspace type:
- Connects to a remote runtime API service
- Automatically provisions sandboxed environments
- Manages container lifecycle through the API
- Handles all infrastructure concerns

### Runtime API Authentication

The example requires a runtime API key for authentication:

```python highlight={42-45}
runtime_api_key = os.getenv("RUNTIME_API_KEY")
if not runtime_api_key:
logger.error("RUNTIME_API_KEY required")
exit(1)
```

This key authenticates your requests to the hosted runtime service.

### Pre-built Image Selection

You can specify which pre-built agent server image to use:

```python highlight={51}
APIRemoteWorkspace(
runtime_api_url="https://runtime.eval.all-hands.dev",
runtime_api_key=runtime_api_key,
server_image="ghcr.io/openhands/agent-server:main-python",
)
```

The runtime API will pull and run the specified image in a sandboxed environment.

### Workspace Testing

Just like with DockerWorkspace, you can test the workspace before running the agent:

```python highlight={61-64}
result = workspace.execute_command(
"echo 'Hello from sandboxed environment!' && pwd"
)
logger.info(f"Command completed: {result.exit_code}, {result.stdout}")
```

This verifies connectivity to the remote runtime and ensures the environment is ready.

### Automatic RemoteConversation

The conversation uses WebSocket communication with the remote server:

```python highlight={66-68}
conversation = Conversation(
agent=agent, workspace=workspace, callbacks=[event_callback], visualize=True
)
assert isinstance(conversation, RemoteConversation)
```

All agent execution happens on the remote runtime infrastructure.

## Next Steps

- **[Docker Sandboxed Server](/sdk/guides/agent-server/docker-sandboxed-server)**
- **[Local Agent Server](/sdk/guides/agent-server/local-agent-server)**
- **[Agent Server Package Architecture](/sdk/arch/agent-server-package)**
Loading