-
Notifications
You must be signed in to change notification settings - Fork 8
feat(examples): OpenAI Agents SDK local-sandbox tutorials (sync + async + temporal) #377
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
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
3475de1
feat(examples): add 050_openai_agents_local_sandbox tutorial
danielmillerp 3ef8794
feat(examples): add async + temporal local-sandbox OpenAI Agents SDK …
danielmillerp e928801
feat(cli): add sync-openai-agents-local-sandbox init template + fix 0…
danielmillerp b709d87
fix(examples): address review — max_turns ceiling, LITELLM_API_KEY gu…
danielmillerp e3e28fd
Merge remote-tracking branch 'origin/next' into dm/oai-local-sandbox-…
danielmillerp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
examples/tutorials/00_sync/050_openai_agents_local_sandbox/.dockerignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # Python | ||
| __pycache__/ | ||
| *.py[cod] | ||
| *$py.class | ||
| *.so | ||
| .Python | ||
| build/ | ||
| develop-eggs/ | ||
| dist/ | ||
| downloads/ | ||
| eggs/ | ||
| .eggs/ | ||
| lib/ | ||
| lib64/ | ||
| parts/ | ||
| sdist/ | ||
| var/ | ||
| wheels/ | ||
| *.egg-info/ | ||
| .installed.cfg | ||
| *.egg | ||
|
|
||
| # Environments | ||
| .env** | ||
| .venv | ||
| env/ | ||
| venv/ | ||
| ENV/ | ||
| env.bak/ | ||
| venv.bak/ | ||
|
|
||
| # IDE | ||
| .idea/ | ||
| .vscode/ | ||
| *.swp | ||
| *.swo | ||
|
|
||
| # Git | ||
| .git | ||
| .gitignore | ||
|
|
||
| # Misc | ||
| .DS_Store |
50 changes: 50 additions & 0 deletions
50
examples/tutorials/00_sync/050_openai_agents_local_sandbox/Dockerfile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # syntax=docker/dockerfile:1.3 | ||
| FROM python:3.12-slim | ||
| COPY --from=ghcr.io/astral-sh/uv:0.6.4 /uv /uvx /bin/ | ||
|
|
||
| # Install system dependencies | ||
| RUN apt-get update && apt-get install -y \ | ||
| htop \ | ||
| vim \ | ||
| curl \ | ||
| tar \ | ||
| python3-dev \ | ||
| postgresql-client \ | ||
| build-essential \ | ||
| libpq-dev \ | ||
| gcc \ | ||
| cmake \ | ||
| netcat-openbsd \ | ||
| && apt-get clean \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| RUN uv pip install --system --upgrade pip setuptools wheel | ||
|
|
||
| ENV UV_HTTP_TIMEOUT=1000 | ||
|
|
||
| # Copy pyproject.toml and README.md to install dependencies | ||
| COPY 00_sync/050_openai_agents_local_sandbox/pyproject.toml /app/050_openai_agents_local_sandbox/pyproject.toml | ||
| COPY 00_sync/050_openai_agents_local_sandbox/README.md /app/050_openai_agents_local_sandbox/README.md | ||
|
|
||
| WORKDIR /app/050_openai_agents_local_sandbox | ||
|
|
||
| # Copy the project code | ||
| COPY 00_sync/050_openai_agents_local_sandbox/project /app/050_openai_agents_local_sandbox/project | ||
|
|
||
| # Copy the test files | ||
| COPY 00_sync/050_openai_agents_local_sandbox/tests /app/050_openai_agents_local_sandbox/tests | ||
|
|
||
| # Copy shared test utilities | ||
| COPY test_utils /app/test_utils | ||
|
|
||
| # Install the required Python packages with dev dependencies | ||
| RUN uv pip install --system .[dev] | ||
|
|
||
| # Set environment variables | ||
| ENV PYTHONPATH=/app | ||
|
|
||
| # Set test environment variables | ||
| ENV AGENT_NAME=s050-openai-agents-local-sandbox | ||
|
|
||
| # Run the agent using uvicorn | ||
| CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"] |
113 changes: 113 additions & 0 deletions
113
examples/tutorials/00_sync/050_openai_agents_local_sandbox/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # Tutorial 050: Sync OpenAI Agents SDK with a Local Sandbox | ||
|
|
||
| This tutorial demonstrates how to build a **synchronous** agent on AgentEx using the | ||
| [OpenAI Agents SDK](https://developers.openai.com/api/docs/guides/agents) and its | ||
| **sandbox** runtime, running with the **local** (`unix_local`) backend. | ||
|
|
||
| The agent is a "local sandbox assistant": it answers questions by actually running | ||
| real shell commands (e.g. `python3 --version`, `ls /tmp`, `python3 -c "..."`) | ||
| instead of guessing. | ||
|
|
||
| ## Key Concepts | ||
|
|
||
| ### Sync ACP | ||
| The sync ACP model uses HTTP request/response for communication. The | ||
| `@acp.on_message_send` handler receives a message, runs the agent, and returns the | ||
| agent's final answer as a `TextContent`. | ||
|
|
||
| ### OpenAI Agents SDK Sandbox | ||
| The OpenAI Agents SDK ships `agents.sandbox`, which lets you give an agent | ||
| **capabilities** (instead of hand-written tools) that the runtime turns into real | ||
| tools backed by a sandbox: | ||
|
|
||
| - **`SandboxAgent`**: an `Agent` that is granted sandbox capabilities. | ||
| - **Capabilities** (`from agents.sandbox.capabilities import Shell, Filesystem, Memory`): | ||
| each capability expands into a set of real tools. This tutorial uses `Shell`, which | ||
| lets the model run real shell commands. | ||
| - **`SandboxRunConfig`** + a sandbox **client**: tells the runtime *where* the tools | ||
| actually execute. | ||
|
|
||
| ### The LOCAL sandbox (`UnixLocalSandboxClient`) | ||
| This tutorial uses the local backend | ||
| (`from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient, UnixLocalSandboxClientOptions`), | ||
| `backend_id="unix_local"`. The local sandbox runs shell commands **ON THE HOST** — | ||
| the agent's own container/process. There is **no Docker, no Temporal, and no remote | ||
| sandbox infrastructure** involved. This makes it the simplest way to give an agent a | ||
| real shell. | ||
|
|
||
| The sandbox is wired up through the SDK's `RunConfig`: | ||
|
|
||
| ```python | ||
| from agents import Runner, set_tracing_disabled | ||
| from agents.run_config import RunConfig | ||
| from agents.sandbox import SandboxAgent, SandboxRunConfig | ||
| from agents.sandbox.capabilities import Shell | ||
| from agents.sandbox.sandboxes.unix_local import ( | ||
| UnixLocalSandboxClient, | ||
| UnixLocalSandboxClientOptions, | ||
| ) | ||
|
|
||
| set_tracing_disabled(True) # avoid api.openai.com tracing 401 behind a gateway | ||
|
|
||
| agent = SandboxAgent( | ||
| name="Local Sandbox Assistant", | ||
| instructions="...use the shell tools to actually run commands...", | ||
| capabilities=[Shell()], | ||
| ) | ||
| run_config = RunConfig( | ||
| sandbox=SandboxRunConfig( | ||
| client=UnixLocalSandboxClient(), | ||
| options=UnixLocalSandboxClientOptions(), | ||
| ) | ||
| ) | ||
| result = await Runner.run(agent, input="what's the python version?", run_config=run_config) | ||
| print(result.final_output) | ||
| ``` | ||
|
|
||
| `Runner.run` drives the full tool-call loop internally: the model issues shell | ||
| commands, the local sandbox runs them on the host, the output is fed back, and the | ||
| loop continues until the model produces a final answer. | ||
|
|
||
| ## Files | ||
|
|
||
| | File | Description | | ||
| |------|-------------| | ||
| | `project/acp.py` | ACP server and message handler (runs the sandbox agent) | | ||
| | `project/agent.py` | `SandboxAgent` + `RunConfig(sandbox=...)` wiring + `run_agent` | | ||
| | `project/tools.py` | Sandbox capability factory (`Shell`) | | ||
| | `tests/test_agent.py` | Integration tests | | ||
| | `manifest.yaml` | Agent configuration | | ||
|
|
||
| ## Running Locally | ||
|
|
||
| ```bash | ||
| # From this directory | ||
| agentex agents run | ||
| ``` | ||
|
|
||
| Set `OPENAI_API_KEY` (or `LITELLM_API_KEY` if you're behind the Scale LiteLLM | ||
| gateway) in your environment or in a `.env` file in `project/` so the agent can call | ||
| the model. | ||
|
|
||
| ## Running Tests | ||
|
|
||
| ```bash | ||
| pytest tests/test_agent.py -v | ||
| ``` | ||
|
|
||
| ## Notes | ||
|
|
||
| - **No infra required.** Because this uses the `unix_local` backend, the shell tools | ||
| run directly in the agent's process — no Docker daemon, no Temporal, no remote | ||
| sandbox. Swap the client for a remote/containerized backend to isolate execution. | ||
| - **Tracing.** `set_tracing_disabled(True)` turns off the OpenAI Agents SDK's native | ||
| tracer (which would otherwise try to ship traces to `api.openai.com`). The manifest | ||
| also sets `OPENAI_AGENTS_DISABLE_TRACING=1`. AgentEx/SGP tracing still runs via the | ||
| tracing manager configured in `acp.py` when SGP credentials are present. | ||
| - **Capabilities are the tools.** To let the agent do more, add capabilities in | ||
| `project/tools.py` (e.g. `Filesystem()`, `Memory()`). | ||
|
|
||
| ## Further Reading | ||
|
|
||
| - OpenAI Agents SDK guide: https://developers.openai.com/api/docs/guides/agents | ||
| - The next evolution of the Agents SDK: https://openai.com/index/the-next-evolution-of-the-agents-sdk/ |
61 changes: 61 additions & 0 deletions
61
examples/tutorials/00_sync/050_openai_agents_local_sandbox/manifest.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| build: | ||
| context: | ||
| root: ../../ | ||
| include_paths: | ||
| - 00_sync/050_openai_agents_local_sandbox | ||
| - test_utils | ||
| dockerfile: 00_sync/050_openai_agents_local_sandbox/Dockerfile | ||
| dockerignore: 00_sync/050_openai_agents_local_sandbox/.dockerignore | ||
|
|
||
| local_development: | ||
| agent: | ||
| port: 8000 | ||
| host_address: host.docker.internal | ||
| paths: | ||
| acp: project/acp.py | ||
|
|
||
| agent: | ||
| acp_type: sync | ||
| name: s050-openai-agents-local-sandbox | ||
| description: A sync OpenAI Agents SDK agent using a local (unix_local) sandbox | ||
|
|
||
| temporal: | ||
| enabled: false | ||
|
|
||
| credentials: | ||
| - env_var_name: OPENAI_API_KEY | ||
| secret_name: openai-api-key | ||
| secret_key: api-key | ||
| - env_var_name: REDIS_URL | ||
| secret_name: redis-url-secret | ||
| secret_key: url | ||
| - env_var_name: SGP_API_KEY | ||
| secret_name: sgp-api-key | ||
| secret_key: api-key | ||
| - env_var_name: SGP_ACCOUNT_ID | ||
| secret_name: sgp-account-id | ||
| secret_key: account-id | ||
| - env_var_name: SGP_CLIENT_BASE_URL | ||
| secret_name: sgp-client-base-url | ||
| secret_key: url | ||
|
|
||
| env: | ||
| OPENAI_AGENTS_DISABLE_TRACING: "1" | ||
|
|
||
| deployment: | ||
| image: | ||
| repository: "" | ||
| tag: "latest" | ||
|
|
||
| global: | ||
| agent: | ||
| name: "s050-openai-agents-local-sandbox" | ||
| description: "A sync OpenAI Agents SDK agent using a local (unix_local) sandbox" | ||
| replicaCount: 1 | ||
| resources: | ||
| requests: | ||
| cpu: "500m" | ||
| memory: "1Gi" | ||
| limits: | ||
| cpu: "1000m" | ||
| memory: "2Gi" |
Empty file.
77 changes: 77 additions & 0 deletions
77
examples/tutorials/00_sync/050_openai_agents_local_sandbox/project/acp.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| """ACP (Agent Communication Protocol) handler for Agentex. | ||
|
|
||
| This is the API layer — it owns the agent lifecycle and runs the OpenAI Agents | ||
| SDK *sandbox* agent for each incoming message, returning the agent's final | ||
| answer to the Agentex frontend. | ||
|
|
||
| The agent uses the LOCAL sandbox backend (``UnixLocalSandboxClient``), which runs | ||
| shell commands on the host (this process/container). The OpenAI Agents SDK runs | ||
| its tool-call loop internally via ``Runner.run`` and returns the final output, so | ||
| this sync handler returns a single ``TextContent`` rather than streaming tokens. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
|
|
||
| from dotenv import load_dotenv | ||
|
|
||
| load_dotenv() | ||
|
|
||
| from agentex.lib import adk | ||
| from project.agent import run_agent | ||
| from agentex.lib.types.acp import SendMessageParams | ||
| from agentex.lib.types.tracing import SGPTracingProcessorConfig | ||
| from agentex.lib.utils.logging import make_logger | ||
| from agentex.types.text_content import TextContent | ||
| from agentex.lib.sdk.fastacp.fastacp import FastACP | ||
| from agentex.types.task_message_content import TaskMessageContent | ||
| from agentex.lib.core.tracing.tracing_processor_manager import ( | ||
| add_tracing_processor_config, | ||
| ) | ||
|
|
||
| logger = make_logger(__name__) | ||
|
|
||
| # LiteLLM proxy auth: copy LITELLM_API_KEY to OPENAI_API_KEY for OpenAI client | ||
| # compatibility, so the same example works behind the Scale LiteLLM gateway. | ||
| _litellm_key = os.environ.get("LITELLM_API_KEY") | ||
| if _litellm_key and not os.environ.get("OPENAI_API_KEY"): | ||
| os.environ["OPENAI_API_KEY"] = _litellm_key | ||
|
|
||
| SGP_API_KEY = os.environ.get("SGP_API_KEY", "") | ||
| SGP_ACCOUNT_ID = os.environ.get("SGP_ACCOUNT_ID", "") | ||
| SGP_CLIENT_BASE_URL = os.environ.get("SGP_CLIENT_BASE_URL", "") | ||
|
|
||
| if SGP_API_KEY and SGP_ACCOUNT_ID: | ||
| add_tracing_processor_config( | ||
| SGPTracingProcessorConfig( | ||
| sgp_api_key=SGP_API_KEY, | ||
| sgp_account_id=SGP_ACCOUNT_ID, | ||
| sgp_base_url=SGP_CLIENT_BASE_URL, | ||
| ) | ||
| ) | ||
|
|
||
| acp = FastACP.create(acp_type="sync") | ||
|
|
||
|
|
||
| @acp.on_message_send | ||
| async def handle_message_send( | ||
| params: SendMessageParams, | ||
| ) -> TaskMessageContent: | ||
| """Handle incoming messages by running the local-sandbox agent.""" | ||
| task_id = params.task.id | ||
| user_message = params.content.content | ||
| logger.info(f"Processing message for task {task_id}") | ||
|
|
||
| async with adk.tracing.span( | ||
| trace_id=task_id, | ||
| task_id=task_id, | ||
| name="message", | ||
| input={"message": user_message}, | ||
| data={"__span_type__": "AGENT_WORKFLOW"}, | ||
| ) as turn_span: | ||
| final_output = await run_agent(user_message) | ||
| if turn_span: | ||
| turn_span.output = {"final_output": final_output} | ||
|
|
||
| return TextContent(author="agent", content=final_output) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.