Skip to content

Commit 3ef8794

Browse files
danielmillerpclaude
andcommitted
feat(examples): add async + temporal local-sandbox OpenAI Agents SDK tutorials
Companion to the sync 050 example, covering all three execution modes: - 10_async/00_base/120: async (non-Temporal) FastACP - 10_async/10_temporal/120: Temporal — OpenAIAgentsPlugin + SandboxClientProvider("local", UnixLocalSandboxClient()) + temporal_sandbox_client("local"); uses NoopSnapshotSpec to skip the per-turn workspace snapshot and relies on the streaming model to emit the assistant message (no double-post). Each mirrors its family's sibling (110_pydantic_ai / 060_open_ai_agents_sdk) and has integration tests exercising the sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3475de1 commit 3ef8794

21 files changed

Lines changed: 1672 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
build/
8+
develop-eggs/
9+
dist/
10+
downloads/
11+
eggs/
12+
.eggs/
13+
lib/
14+
lib64/
15+
parts/
16+
sdist/
17+
var/
18+
wheels/
19+
*.egg-info/
20+
.installed.cfg
21+
*.egg
22+
23+
# Environments
24+
.env**
25+
.venv
26+
env/
27+
venv/
28+
ENV/
29+
env.bak/
30+
venv.bak/
31+
32+
# IDE
33+
.idea/
34+
.vscode/
35+
*.swp
36+
*.swo
37+
38+
# Git
39+
.git
40+
.gitignore
41+
42+
# Misc
43+
.DS_Store
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# syntax=docker/dockerfile:1.3
2+
FROM python:3.12-slim
3+
COPY --from=ghcr.io/astral-sh/uv:0.6.4 /uv /uvx /bin/
4+
5+
# Install system dependencies
6+
RUN apt-get update && apt-get install -y \
7+
htop \
8+
vim \
9+
curl \
10+
tar \
11+
python3-dev \
12+
postgresql-client \
13+
build-essential \
14+
libpq-dev \
15+
gcc \
16+
cmake \
17+
netcat-openbsd \
18+
&& apt-get clean \
19+
&& rm -rf /var/lib/apt/lists/*
20+
21+
RUN uv pip install --system --upgrade pip setuptools wheel
22+
23+
ENV UV_HTTP_TIMEOUT=1000
24+
25+
# Copy pyproject.toml and README.md to install dependencies
26+
COPY 10_async/00_base/120_openai_agents_local_sandbox/pyproject.toml /app/120_openai_agents_local_sandbox/pyproject.toml
27+
COPY 10_async/00_base/120_openai_agents_local_sandbox/README.md /app/120_openai_agents_local_sandbox/README.md
28+
29+
WORKDIR /app/120_openai_agents_local_sandbox
30+
31+
# Copy the project code
32+
COPY 10_async/00_base/120_openai_agents_local_sandbox/project /app/120_openai_agents_local_sandbox/project
33+
34+
# Copy the test files
35+
COPY 10_async/00_base/120_openai_agents_local_sandbox/tests /app/120_openai_agents_local_sandbox/tests
36+
37+
# Copy shared test utilities
38+
COPY test_utils /app/test_utils
39+
40+
# Install the required Python packages with dev dependencies
41+
RUN uv pip install --system .[dev] pytest-asyncio httpx
42+
43+
# Set environment variables
44+
ENV PYTHONPATH=/app
45+
46+
# Set test environment variables
47+
ENV AGENT_NAME=ab120-openai-agents-local-sandbox
48+
49+
# Run the agent using uvicorn
50+
CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"]
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Tutorial 120: Async OpenAI Agents SDK with a Local Sandbox
2+
3+
This tutorial demonstrates how to build an **async (non-Temporal)** agent on AgentEx
4+
using the [OpenAI Agents SDK](https://developers.openai.com/api/docs/guides/agents)
5+
and its **sandbox** runtime, running with the **local** (`unix_local`) backend.
6+
7+
The agent is a "local sandbox assistant": it answers questions by actually running
8+
real shell commands (e.g. `python3 --version`, `ls /tmp`, `python3 -c "..."`)
9+
instead of guessing.
10+
11+
This mirrors the Pydantic AI async tutorial (`110_pydantic_ai`): same async ACP
12+
model (`acp_type: async`, `temporal.enabled: false`), same per-task `adk.state`
13+
multi-turn memory pattern. The difference is the runtime — here we use the OpenAI
14+
Agents SDK `SandboxAgent` with the local sandbox backend.
15+
16+
## Key Concepts
17+
18+
### Async ACP (base)
19+
The async ACP model is event-driven: `on_task_create` initializes per-task state,
20+
and `on_task_event_send` handles each user message. Conversation history is
21+
persisted across turns via `adk.state`.
22+
23+
### OpenAI Agents SDK Sandbox
24+
The OpenAI Agents SDK ships `agents.sandbox`, which lets you give an agent
25+
**capabilities** (instead of hand-written tools) that the runtime turns into real
26+
tools backed by a sandbox:
27+
28+
- **`SandboxAgent`**: an `Agent` that is granted sandbox capabilities.
29+
- **Capabilities** (`from agents.sandbox.capabilities import Shell, Filesystem, Memory`):
30+
each capability expands into a set of real tools. This tutorial uses `Shell`, which
31+
lets the model run real shell commands.
32+
- **`SandboxRunConfig`** + a sandbox **client**: tells the runtime *where* the tools
33+
actually execute.
34+
35+
### The LOCAL sandbox (`UnixLocalSandboxClient`)
36+
This tutorial uses the local backend
37+
(`from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient, UnixLocalSandboxClientOptions`),
38+
`backend_id="unix_local"`. The local sandbox runs shell commands **ON THE HOST**
39+
the agent's own container/process. There is **no Docker, no Temporal, and no remote
40+
sandbox infrastructure** involved.
41+
42+
The sandbox is wired up through the SDK's `RunConfig`:
43+
44+
```python
45+
from agents import Runner, set_tracing_disabled
46+
from agents.run_config import RunConfig
47+
from agents.sandbox import SandboxAgent, SandboxRunConfig
48+
from agents.sandbox.capabilities import Shell
49+
from agents.sandbox.sandboxes.unix_local import (
50+
UnixLocalSandboxClient,
51+
UnixLocalSandboxClientOptions,
52+
)
53+
54+
set_tracing_disabled(True) # avoid api.openai.com tracing 401 behind a gateway
55+
56+
agent = SandboxAgent(
57+
name="Local Sandbox Assistant",
58+
instructions="...use the shell tools to actually run commands...",
59+
capabilities=[Shell()],
60+
)
61+
run_config = RunConfig(
62+
sandbox=SandboxRunConfig(
63+
client=UnixLocalSandboxClient(),
64+
options=UnixLocalSandboxClientOptions(),
65+
)
66+
)
67+
result = await Runner.run(agent, input=input_list, run_config=run_config)
68+
print(result.final_output)
69+
```
70+
71+
`Runner.run` drives the full tool-call loop internally: the model issues shell
72+
commands, the local sandbox runs them on the host, the output is fed back, and the
73+
loop continues until the model produces a final answer. Because the loop is
74+
self-contained, the async handler runs the agent and persists a single final
75+
`TextContent` rather than streaming tokens.
76+
77+
## Files
78+
79+
| File | Description |
80+
|------|-------------|
81+
| `project/acp.py` | Async ACP server + handlers (`adk.state` multi-turn, runs the sandbox agent) |
82+
| `project/agent.py` | `SandboxAgent` + `RunConfig(sandbox=...)` wiring + `run_agent` |
83+
| `project/tools.py` | Sandbox capability factory (`Shell`) |
84+
| `tests/test_agent.py` | Integration tests (polling pattern) |
85+
| `manifest.yaml` | Agent configuration |
86+
87+
## Running Locally
88+
89+
```bash
90+
# From this directory
91+
agentex agents run
92+
```
93+
94+
Set `OPENAI_API_KEY` (or `LITELLM_API_KEY` if you're behind the Scale LiteLLM
95+
gateway) in your environment or in a `.env` file in `project/` so the agent can call
96+
the model.
97+
98+
## Running Tests
99+
100+
```bash
101+
pytest tests/test_agent.py -v
102+
```
103+
104+
## Notes
105+
106+
- **No infra required.** Because this uses the `unix_local` backend, the shell tools
107+
run directly in the agent's process — no Docker daemon, no Temporal, no remote
108+
sandbox. Swap the client for a remote/containerized backend to isolate execution.
109+
- **Tracing.** `set_tracing_disabled(True)` turns off the OpenAI Agents SDK's native
110+
tracer (which would otherwise try to ship traces to `api.openai.com`). The manifest
111+
also sets `OPENAI_AGENTS_DISABLE_TRACING=1`. AgentEx/SGP tracing still runs via the
112+
tracing manager configured in `acp.py` when SGP credentials are present.
113+
- **Capabilities are the tools.** To let the agent do more, add capabilities in
114+
`project/tools.py` (e.g. `Filesystem()`, `Memory()`).
115+
116+
## Further Reading
117+
118+
- OpenAI Agents SDK guide: https://developers.openai.com/api/docs/guides/agents
119+
- The Temporal variant of this tutorial: `10_async/10_temporal/120_openai_agents_local_sandbox`
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
build:
2+
context:
3+
root: ../../../
4+
include_paths:
5+
- 10_async/00_base/120_openai_agents_local_sandbox
6+
- test_utils
7+
dockerfile: 10_async/00_base/120_openai_agents_local_sandbox/Dockerfile
8+
dockerignore: 10_async/00_base/120_openai_agents_local_sandbox/.dockerignore
9+
10+
local_development:
11+
agent:
12+
port: 8000
13+
host_address: host.docker.internal
14+
paths:
15+
acp: project/acp.py
16+
17+
agent:
18+
acp_type: async
19+
name: ab120-openai-agents-local-sandbox
20+
description: An async OpenAI Agents SDK agent using a local (unix_local) sandbox
21+
22+
temporal:
23+
enabled: false
24+
25+
credentials:
26+
- env_var_name: OPENAI_API_KEY
27+
secret_name: openai-api-key
28+
secret_key: api-key
29+
- env_var_name: REDIS_URL
30+
secret_name: redis-url-secret
31+
secret_key: url
32+
- env_var_name: SGP_API_KEY
33+
secret_name: sgp-api-key
34+
secret_key: api-key
35+
- env_var_name: SGP_ACCOUNT_ID
36+
secret_name: sgp-account-id
37+
secret_key: account-id
38+
- env_var_name: SGP_CLIENT_BASE_URL
39+
secret_name: sgp-client-base-url
40+
secret_key: url
41+
42+
env:
43+
OPENAI_AGENTS_DISABLE_TRACING: "1"
44+
45+
deployment:
46+
image:
47+
repository: ""
48+
tag: "latest"
49+
50+
global:
51+
agent:
52+
name: "ab120-openai-agents-local-sandbox"
53+
description: "An async OpenAI Agents SDK agent using a local (unix_local) sandbox"
54+
replicaCount: 1
55+
resources:
56+
requests:
57+
cpu: "500m"
58+
memory: "1Gi"
59+
limits:
60+
cpu: "1000m"
61+
memory: "2Gi"

examples/tutorials/10_async/00_base/120_openai_agents_local_sandbox/project/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)