Python SDK for the OpenAI Codex agent
Project description
Codex SDK for Python
Embed the Codex agent in your workflows and apps.
The Python SDK wraps the bundled codex binary. It spawns the CLI and exchanges JSONL events over stdin/stdout.
TypeScript版との互換性
このPython SDKは TypeScript版 Codex SDK を1対1でポーティングしており、全ての機能・データ構造・動作が同一です。
- 全8種のイベント型(ThreadStartedEvent, TurnCompletedEvent など)
- 全8種のアイテム型(AgentMessageItem, CommandExecutionItem など)
- 全4種のEnum(ApprovalMode, SandboxMode, ModelReasoningEffort, WebSearchMode)
- CLI引数の構築ロジック、環境変数の処理、TOML設定シリアライズ
詳細な比較は docs/typescript-python-comparison.md を参照してください。
Installation
pip install codex-sdk-py
Requires Python 3.10+.
Quickstart
import asyncio
from codex_sdk import Codex
async def main():
codex = Codex()
thread = codex.start_thread()
turn = await thread.run("Diagnose the test failure and propose a fix")
print(turn.final_response)
print(turn.items)
asyncio.run(main())
Call run() repeatedly on the same Thread instance to continue that conversation.
next_turn = await thread.run("Implement the fix")
Streaming responses
run() buffers events until the turn finishes. To react to intermediate progress—tool calls, streaming responses, and file change notifications—use run_streamed() instead, which returns an async generator of structured events.
async def stream_example():
codex = Codex()
thread = codex.start_thread()
streamed = await thread.run_streamed("Diagnose the test failure and propose a fix")
async for event in streamed.events:
match event.get("type"):
case "item.completed":
print("item", event.get("item"))
case "turn.completed":
print("usage", event.get("usage"))
Structured output
The Codex agent can produce a JSON response that conforms to a specified schema. The schema can be provided for each turn as a plain JSON object.
schema = {
"type": "object",
"properties": {
"summary": {"type": "string"},
"status": {"type": "string", "enum": ["ok", "action_required"]},
},
"required": ["summary", "status"],
"additionalProperties": False,
}
turn = await thread.run("Summarize repository status", {"output_schema": schema})
print(turn.final_response)
You can also create a JSON schema from a Pydantic model using model_json_schema().
from pydantic import BaseModel
from typing import Literal
class StatusResponse(BaseModel):
summary: str
status: Literal["ok", "action_required"]
turn = await thread.run(
"Summarize repository status",
{"output_schema": StatusResponse.model_json_schema()}
)
print(turn.final_response)
Attaching images
Provide structured input entries when you need to include images alongside text. Text entries are concatenated into the final prompt while image entries are passed to the Codex CLI via --image.
turn = await thread.run([
{"type": "text", "text": "Describe these screenshots"},
{"type": "local_image", "path": "./ui.png"},
{"type": "local_image", "path": "./diagram.jpg"},
])
Resuming an existing thread
Threads are persisted in ~/.codex/sessions. If you lose the in-memory Thread object, reconstruct it with resume_thread() and keep going.
import os
saved_thread_id = os.environ["CODEX_THREAD_ID"]
thread = codex.resume_thread(saved_thread_id)
await thread.run("Implement the fix")
Working directory controls
Codex runs in the current working directory by default. To avoid unrecoverable errors, Codex requires the working directory to be a Git repository. You can skip the Git repository check by passing the skip_git_repo_check option when creating a thread.
thread = codex.start_thread({
"working_directory": "/path/to/project",
"skip_git_repo_check": True,
})
Sandbox modes
Control how the agent interacts with your filesystem using sandbox_mode.
from codex_sdk import Codex, SandboxMode
thread = codex.start_thread({
"sandbox_mode": SandboxMode.WORKSPACE_WRITE,
})
Available modes:
SandboxMode.READ_ONLY- Agent can only read filesSandboxMode.WORKSPACE_WRITE- Agent can read/write in the workspaceSandboxMode.DANGER_FULL_ACCESS- Full filesystem access (use with caution)
Approval policies
Control when the agent requires approval for actions.
from codex_sdk import Codex, ApprovalMode
thread = codex.start_thread({
"approval_policy": ApprovalMode.ON_FAILURE,
})
Available modes:
ApprovalMode.NEVER- Never require approvalApprovalMode.ON_REQUEST- Approve on explicit requestApprovalMode.ON_FAILURE- Approve after failuresApprovalMode.UNTRUSTED- Always require approval
Cancelling a turn
Use asyncio.Event to cancel an ongoing turn (equivalent to AbortSignal in TypeScript).
import asyncio
async def cancellable_example():
codex = Codex()
thread = codex.start_thread()
cancel_event = asyncio.Event()
async def cancel_after_delay():
await asyncio.sleep(5)
cancel_event.set()
# Start cancellation timer
asyncio.create_task(cancel_after_delay())
try:
turn = await thread.run(
"Long running task",
{"cancel_event": cancel_event}
)
except asyncio.CancelledError:
print("Turn was cancelled")
Controlling the Codex CLI environment
By default, the Codex CLI inherits the Python process environment. Provide the optional env parameter when instantiating the Codex client to fully control which variables the CLI receives—useful for sandboxed hosts.
codex = Codex({
"env": {
"PATH": "/usr/local/bin",
},
})
The SDK still injects its required variables (such as OPENAI_BASE_URL and CODEX_API_KEY) on top of the environment you provide.
Passing --config overrides
Use the config option to provide additional Codex CLI configuration overrides. The SDK accepts a dict, flattens it into dotted paths, and serializes values as TOML literals before passing them as repeated --config key=value flags.
codex = Codex({
"config": {
"show_raw_agent_reasoning": True,
"sandbox_workspace_write": {"network_access": True},
},
})
Thread options still take precedence for overlapping settings because they are emitted after these global overrides.
API Reference
Classes
Codex
Main entry point for the SDK.
codex = Codex(options: CodexOptions | None = None)
Methods:
start_thread(options: ThreadOptions | None = None) -> Thread- Start a new conversationresume_thread(thread_id: str, options: ThreadOptions | None = None) -> Thread- Resume an existing conversation
Thread
Represents a conversation with the agent.
Properties:
id: str | None- Thread ID (populated after first turn)
Methods:
async run(input: Input, turn_options: TurnOptions | None = None) -> Turn- Execute a turn and return resultsasync run_streamed(input: Input, turn_options: TurnOptions | None = None) -> StreamedTurn- Execute a turn with streaming events
Turn
Result of a completed turn.
@dataclass
class Turn:
items: list[ThreadItem] # All items produced during the turn
final_response: str # The agent's final response text
usage: Usage | None # Token usage statistics
StreamedTurn
Result of a streamed turn.
@dataclass
class StreamedTurn:
events: AsyncGenerator[ThreadEvent, None] # Async generator of events
Types
Input Types
Input = str | list[UserInput]- User input (string or structured)TextUserInput- Text input:{"type": "text", "text": "..."}ImageUserInput- Image input:{"type": "local_image", "path": "..."}
Event Types
ThreadStartedEvent- Thread startedTurnStartedEvent- Turn startedTurnCompletedEvent- Turn completed (includes usage)TurnFailedEvent- Turn failed (includes error)ItemStartedEvent- Item startedItemUpdatedEvent- Item updatedItemCompletedEvent- Item completedThreadErrorEvent- Unrecoverable error
Item Types
AgentMessageItem- Agent's response textReasoningItem- Agent's reasoning summaryCommandExecutionItem- Shell command executionFileChangeItem- File modificationsMcpToolCallItem- MCP tool invocationWebSearchItem- Web search queryTodoListItem- Agent's task listErrorItem- Non-fatal error
Enums
class SandboxMode(StrEnum):
READ_ONLY = "read-only"
WORKSPACE_WRITE = "workspace-write"
DANGER_FULL_ACCESS = "danger-full-access"
class ApprovalMode(StrEnum):
NEVER = "never"
ON_REQUEST = "on-request"
ON_FAILURE = "on-failure"
UNTRUSTED = "untrusted"
class ModelReasoningEffort(StrEnum):
MINIMAL = "minimal"
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
XHIGH = "xhigh"
class WebSearchMode(StrEnum):
DISABLED = "disabled"
CACHED = "cached"
LIVE = "live"
Development
Setup
# Clone the repository
git clone https://github.com/nogataka/codex-sdk-py.git
cd codex-sdk-py
# Install development dependencies
pip install -e ".[dev]"
Running tests
pytest tests/ -v
Linting and formatting
# Lint
ruff check codex_sdk/
# Format
ruff format codex_sdk/
# Type check
mypy codex_sdk/
Release Process
Releases are automated via GitHub Actions.
Creating a release
-
Update the version in
codex_sdk/__init__.py:__version__ = "0.1.0"
-
Commit the version change:
git add codex_sdk/__init__.py git commit -m "Bump version to 0.1.0"
-
Create and push a tag:
git tag v0.1.0 git push origin main --tags
-
GitHub Actions will automatically:
- Build the package
- Publish to PyPI
- Create a GitHub Release
Required secrets
Set the following secrets in your GitHub repository:
PYPI_TOKEN: Your PyPI API token for publishing packages
To create a PyPI token:
- Go to https://pypi.org/manage/account/token/
- Create a new token with "Upload packages" scope
- Add it to your GitHub repository secrets
License
Apache-2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codex_sdk_py-0.0.7.tar.gz.
File metadata
- Download URL: codex_sdk_py-0.0.7.tar.gz
- Upload date:
- Size: 20.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c4c65bcbf95c46b8f5d387c03d86a8f7b5b0d5d109c7e84dd58ca7a0f30d523
|
|
| MD5 |
f399baecfefd6931bfe5b52816c203c2
|
|
| BLAKE2b-256 |
509e05879da148689f3de30b5bfdf1648b599d894bb934a66fe5faa473cf75ee
|
File details
Details for the file codex_sdk_py-0.0.7-py3-none-any.whl.
File metadata
- Download URL: codex_sdk_py-0.0.7-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b1903e803dba271e52da175091c1e793b636c074a71923bf1264479f33c1eee
|
|
| MD5 |
df1e081e32076db1517c6e276170af3c
|
|
| BLAKE2b-256 |
60c6e37b04ffc0fc73ef0bcdb8a9760645f56b5bfe4d04f5624615a485d962c1
|