Files
codex/sdk/python/README.md
T
Ahmed Ibrahim b1cbf622ad [codex] Add friendly Python SDK sandbox presets (#24772)
## Why

The Python SDK currently exposes sandbox selection differently depending
on where it is used: thread lifecycle methods accept `SandboxMode`,
while turns accept the lower-level `SandboxPolicy` shape. For the common
case of choosing an access level, that leaks app-server wire details
into otherwise straightforward SDK usage.

This makes the common path explicit and discoverable: callers choose a
named sandbox preset once, using the same keyword on threads and turns.
The preset name `workspace_write` also makes the granted capability
clear at the callsite.

## What changed

- Added a root-level `Sandbox` enum with documented presets:
  - `Sandbox.read_only`: read files without allowing writes.
- `Sandbox.workspace_write`: the normal default for projects with a
recorded trust decision; read files and write inside the workspace and
configured writable roots.
  - `Sandbox.full_access`: run without filesystem access restrictions.
- Documented that omitting `sandbox=` delegates to app-server's
configured default, while explicit turn overrides remain sticky for
subsequent turns.
- Updated sync and async thread lifecycle and turn APIs to consistently
accept `sandbox=Sandbox...`, translating to the existing app-server
thread and turn representations internally.
- Updated the public API artifact generator so regenerated SDK wrappers
retain the friendly enum shape.
- Replaced low-level policy construction in Python docs, examples, and
the walkthrough notebook with the preset API.
- Added focused coverage for root exports, method signatures,
preset-to-wire mapping, and rejection of raw string sandbox inputs.

## API impact

High-level turn calls now use `sandbox=` instead of `sandbox_policy=`:

```python
from openai_codex import Codex, Sandbox

with Codex() as codex:
    thread = codex.thread_start(sandbox=Sandbox.workspace_write)
    result = thread.run("Review the diff only.", sandbox=Sandbox.read_only)
```

`thread_start(...)` already defaults to `ApprovalMode.auto_review`, so
normal writable usage is concise:

```python
with Codex() as codex:
    thread = codex.thread_start(sandbox=Sandbox.workspace_write)
    thread.run("Update the files in this workspace.")
```

With that combination, edits inside `cwd` and configured writable roots
run within the workspace-write sandbox. Operations that require
approval, such as edits outside those roots, are routed through auto
review. When `sandbox=` is omitted, app-server resolves its configured
default. A sandbox supplied to `run(...)` or `turn(...)` applies to that
turn and subsequent turns.

## Test coverage

- `sdk/python/tests/test_public_api_signatures.py` covers the public
export and parameter names, including the default approval mode.
- `sdk/python/tests/test_public_api_runtime_behavior.py` covers preset
mappings to the existing wire types and raw string rejection.
2026-05-27 11:11:04 -07:00

133 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenAI Codex Python SDK (Experimental)
Experimental Python SDK for `codex app-server` JSON-RPC v2 over stdio, with a small default surface optimized for real scripts and apps.
The generated wire-model layer is sourced from the pinned `openai-codex-cli-bin`
runtime package and exposed as Pydantic models with snake_case Python fields
that serialize back to the app-servers camelCase wire format.
The package root exports the ergonomic client API; public app-server value and
event types live in `openai_codex.types`.
## Install
```bash
cd sdk/python
uv sync
source .venv/bin/activate
```
Published SDK builds pin an exact `openai-codex-cli-bin` runtime dependency
with the same version as the SDK. Pass `AppServerConfig(codex_bin=...)` only
when you intentionally want to run against a specific local app-server binary.
## Quickstart
```python
from openai_codex import Codex, Sandbox
with Codex() as codex:
# Call login_api_key(...) first when this app-server session is not
# already authenticated.
thread = codex.thread_start(model="gpt-5", sandbox=Sandbox.workspace_write)
result = thread.run("Say hello in one sentence.")
print(result.final_response)
print(len(result.items))
```
`thread.run(...)` and `thread.turn(...).run()` return `TurnResult`. Its
`final_response` is `None` when the turn completes without a final-answer or
phase-less assistant message item.
## Sandbox
Use the same enum when creating a thread or changing its sandbox for a turn:
```python
from openai_codex import Codex, Sandbox
with Codex() as codex:
thread = codex.thread_start(sandbox=Sandbox.workspace_write)
thread.run("Make the requested change.")
review = thread.run("Review the diff only.", sandbox=Sandbox.read_only)
```
Available presets:
- `Sandbox.read_only`: read files without allowing writes.
- `Sandbox.workspace_write`: the normal default for projects with a recorded trust decision; read files and write inside the workspace and configured writable roots.
- `Sandbox.full_access`: run without filesystem access restrictions.
When `sandbox=` is omitted, app-server uses its configured default. A sandbox
passed to `run(...)` or `turn(...)` applies to that turn and subsequent turns
on the thread.
## Login
Use the auth helper that matches your app:
```python
from openai_codex import Codex
with Codex() as codex:
codex.login_api_key("sk-...")
account = codex.account()
print(account.account)
```
Interactive ChatGPT login returns a handle. Open the provided URL or device-code
page, then wait for the matching completion event:
```python
with Codex() as codex:
login = codex.login_chatgpt()
print(login.auth_url)
completed = login.wait()
print(completed.success)
```
Use `login_chatgpt_device_code()` for device-code auth, `handle.cancel()` to
stop an in-progress interactive login, and `logout()` to clear the active
app-server account session.
## Docs map
- Golden path tutorial: `docs/getting-started.md`
- API reference (signatures + behavior): `docs/api-reference.md`
- Common decisions and pitfalls: `docs/faq.md`
- Runnable examples index: `examples/README.md`
- Jupyter walkthrough notebook: `notebooks/sdk_walkthrough.ipynb`
## Examples
Start here:
```bash
cd sdk/python
python examples/01_quickstart_constructor/sync.py
python examples/01_quickstart_constructor/async.py
```
## Runtime
Published SDK builds are pinned to an exact `openai-codex-cli-bin` package
version, and that runtime package carries the platform-specific binary for the
target wheel. The SDK package version and runtime package version must match.
## Compatibility and versioning
- Package: `openai-codex`
- Runtime package: `openai-codex-cli-bin`
- Python: `>=3.10`
- Target protocol: Codex `app-server` JSON-RPC v2
- Versioning rule: the SDK package version is the underlying Codex runtime version
## Notes
- `Codex()` is eager and performs startup + `initialize` in the constructor.
- Use context managers (`with Codex() as codex:`) to ensure shutdown.
- Plain strings are accepted anywhere a turn input is accepted; they are
shorthand for `TextInput(...)`.
- Prefer `thread.run("...")` for the common case. Use `thread.turn(...)` when
you need streaming, steering, or interrupt control.
- For transient overload, use `retry_on_overload` from the package root.