codex-sdk-py 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_sdk_py-0.0.3/.github/workflows/ci.yml +82 -0
- codex_sdk_py-0.0.3/.github/workflows/release.yml +85 -0
- codex_sdk_py-0.0.3/.gitignore +107 -0
- codex_sdk_py-0.0.3/PKG-INFO +423 -0
- codex_sdk_py-0.0.3/README.md +393 -0
- codex_sdk_py-0.0.3/codex_sdk/__init__.py +135 -0
- codex_sdk_py-0.0.3/codex_sdk/codex.py +79 -0
- codex_sdk_py-0.0.3/codex_sdk/codex_options.py +53 -0
- codex_sdk_py-0.0.3/codex_sdk/events.py +107 -0
- codex_sdk_py-0.0.3/codex_sdk/exec.py +396 -0
- codex_sdk_py-0.0.3/codex_sdk/items.py +175 -0
- codex_sdk_py-0.0.3/codex_sdk/output_schema_file.py +74 -0
- codex_sdk_py-0.0.3/codex_sdk/py.typed +0 -0
- codex_sdk_py-0.0.3/codex_sdk/thread.py +256 -0
- codex_sdk_py-0.0.3/codex_sdk/thread_options.py +79 -0
- codex_sdk_py-0.0.3/codex_sdk/turn_options.py +24 -0
- codex_sdk_py-0.0.3/examples/basic_streaming.py +103 -0
- codex_sdk_py-0.0.3/examples/image_input.py +45 -0
- codex_sdk_py-0.0.3/examples/structured_output.py +38 -0
- codex_sdk_py-0.0.3/examples/structured_output_pydantic.py +55 -0
- codex_sdk_py-0.0.3/pyproject.toml +72 -0
- codex_sdk_py-0.0.3/tests/__init__.py +0 -0
- codex_sdk_py-0.0.3/tests/conftest.py +19 -0
- codex_sdk_py-0.0.3/tests/test_codex.py +45 -0
- codex_sdk_py-0.0.3/tests/test_output_schema.py +53 -0
- codex_sdk_py-0.0.3/tests/test_types.py +47 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: Lint
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install ruff mypy
|
|
25
|
+
|
|
26
|
+
- name: Run Ruff (linter)
|
|
27
|
+
run: ruff check codex_sdk/
|
|
28
|
+
|
|
29
|
+
- name: Run Ruff (formatter check)
|
|
30
|
+
run: ruff format --check codex_sdk/
|
|
31
|
+
|
|
32
|
+
- name: Run mypy (type check)
|
|
33
|
+
run: mypy codex_sdk/ --ignore-missing-imports
|
|
34
|
+
|
|
35
|
+
test:
|
|
36
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
strategy:
|
|
39
|
+
fail-fast: false
|
|
40
|
+
matrix:
|
|
41
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
|
|
46
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
47
|
+
uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: ${{ matrix.python-version }}
|
|
50
|
+
|
|
51
|
+
- name: Install dependencies
|
|
52
|
+
run: |
|
|
53
|
+
python -m pip install --upgrade pip
|
|
54
|
+
pip install -e ".[dev]"
|
|
55
|
+
|
|
56
|
+
- name: Run tests
|
|
57
|
+
run: pytest tests/ -v
|
|
58
|
+
|
|
59
|
+
build:
|
|
60
|
+
name: Build package
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- name: Set up Python
|
|
66
|
+
uses: actions/setup-python@v5
|
|
67
|
+
with:
|
|
68
|
+
python-version: "3.12"
|
|
69
|
+
|
|
70
|
+
- name: Install build tools
|
|
71
|
+
run: |
|
|
72
|
+
python -m pip install --upgrade pip
|
|
73
|
+
pip install build
|
|
74
|
+
|
|
75
|
+
- name: Build package
|
|
76
|
+
run: python -m build
|
|
77
|
+
|
|
78
|
+
- name: Upload artifact
|
|
79
|
+
uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: dist
|
|
82
|
+
path: dist/
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build package
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install build tools
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip
|
|
26
|
+
pip install build
|
|
27
|
+
|
|
28
|
+
- name: Build package
|
|
29
|
+
run: python -m build
|
|
30
|
+
|
|
31
|
+
- name: Upload artifact
|
|
32
|
+
uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
|
|
37
|
+
publish-pypi:
|
|
38
|
+
name: Publish to PyPI
|
|
39
|
+
needs: build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/project/codex-sdk-py/
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- name: Download artifact
|
|
47
|
+
uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
- name: Publish to PyPI
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
54
|
+
with:
|
|
55
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
|
56
|
+
|
|
57
|
+
github-release:
|
|
58
|
+
name: Create GitHub Release
|
|
59
|
+
needs: build
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- name: Download artifact
|
|
66
|
+
uses: actions/download-artifact@v4
|
|
67
|
+
with:
|
|
68
|
+
name: dist
|
|
69
|
+
path: dist/
|
|
70
|
+
|
|
71
|
+
- name: Get version from tag
|
|
72
|
+
id: get_version
|
|
73
|
+
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
|
74
|
+
|
|
75
|
+
- name: Create GitHub Release
|
|
76
|
+
uses: softprops/action-gh-release@v2
|
|
77
|
+
with:
|
|
78
|
+
name: v${{ steps.get_version.outputs.VERSION }}
|
|
79
|
+
draft: false
|
|
80
|
+
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
|
|
81
|
+
generate_release_notes: true
|
|
82
|
+
files: |
|
|
83
|
+
dist/*
|
|
84
|
+
env:
|
|
85
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Translations
|
|
52
|
+
*.mo
|
|
53
|
+
*.pot
|
|
54
|
+
|
|
55
|
+
# Environments
|
|
56
|
+
.env
|
|
57
|
+
.venv
|
|
58
|
+
env/
|
|
59
|
+
venv/
|
|
60
|
+
ENV/
|
|
61
|
+
env.bak/
|
|
62
|
+
venv.bak/
|
|
63
|
+
|
|
64
|
+
# Spyder project settings
|
|
65
|
+
.spyderproject
|
|
66
|
+
.spyproject
|
|
67
|
+
|
|
68
|
+
# Rope project settings
|
|
69
|
+
.ropeproject
|
|
70
|
+
|
|
71
|
+
# mkdocs documentation
|
|
72
|
+
/site
|
|
73
|
+
|
|
74
|
+
# mypy
|
|
75
|
+
.mypy_cache/
|
|
76
|
+
.dmypy.json
|
|
77
|
+
dmypy.json
|
|
78
|
+
|
|
79
|
+
# Pyre type checker
|
|
80
|
+
.pyre/
|
|
81
|
+
|
|
82
|
+
# pytype static type analyzer
|
|
83
|
+
.pytype/
|
|
84
|
+
|
|
85
|
+
# Cython debug symbols
|
|
86
|
+
cython_debug/
|
|
87
|
+
|
|
88
|
+
# Ruff
|
|
89
|
+
.ruff_cache/
|
|
90
|
+
|
|
91
|
+
# IDEs
|
|
92
|
+
.idea/
|
|
93
|
+
.vscode/
|
|
94
|
+
*.swp
|
|
95
|
+
*.swo
|
|
96
|
+
*~
|
|
97
|
+
|
|
98
|
+
# OS
|
|
99
|
+
.DS_Store
|
|
100
|
+
Thumbs.db
|
|
101
|
+
|
|
102
|
+
# Codex SDK vendor binaries (bundled separately)
|
|
103
|
+
codex_sdk/vendor/
|
|
104
|
+
|
|
105
|
+
.claude/
|
|
106
|
+
|
|
107
|
+
docs/private/
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codex-sdk-py
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Python SDK for the OpenAI Codex agent
|
|
5
|
+
Project-URL: Homepage, https://github.com/nogataka/codex-sdk-python
|
|
6
|
+
Project-URL: Documentation, https://github.com/nogataka/codex-sdk-python#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/nogataka/codex-sdk-python
|
|
8
|
+
Project-URL: Issues, https://github.com/nogataka/codex-sdk-python/issues
|
|
9
|
+
Author-email: OpenAI <support@openai.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: agent,ai,codex,openai,sdk
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.2; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Codex SDK for Python
|
|
32
|
+
|
|
33
|
+
Embed the Codex agent in your workflows and apps.
|
|
34
|
+
|
|
35
|
+
The Python SDK wraps the bundled `codex` binary. It spawns the CLI and exchanges JSONL events over stdin/stdout.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install codex-sdk-py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Requires Python 3.10+.
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio
|
|
49
|
+
from codex_sdk import Codex
|
|
50
|
+
|
|
51
|
+
async def main():
|
|
52
|
+
codex = Codex()
|
|
53
|
+
thread = codex.start_thread()
|
|
54
|
+
turn = await thread.run("Diagnose the test failure and propose a fix")
|
|
55
|
+
|
|
56
|
+
print(turn.final_response)
|
|
57
|
+
print(turn.items)
|
|
58
|
+
|
|
59
|
+
asyncio.run(main())
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Call `run()` repeatedly on the same `Thread` instance to continue that conversation.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
next_turn = await thread.run("Implement the fix")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Streaming responses
|
|
69
|
+
|
|
70
|
+
`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.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
async def stream_example():
|
|
74
|
+
codex = Codex()
|
|
75
|
+
thread = codex.start_thread()
|
|
76
|
+
|
|
77
|
+
streamed = await thread.run_streamed("Diagnose the test failure and propose a fix")
|
|
78
|
+
|
|
79
|
+
async for event in streamed.events:
|
|
80
|
+
match event.get("type"):
|
|
81
|
+
case "item.completed":
|
|
82
|
+
print("item", event.get("item"))
|
|
83
|
+
case "turn.completed":
|
|
84
|
+
print("usage", event.get("usage"))
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Structured output
|
|
88
|
+
|
|
89
|
+
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.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
schema = {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"summary": {"type": "string"},
|
|
96
|
+
"status": {"type": "string", "enum": ["ok", "action_required"]},
|
|
97
|
+
},
|
|
98
|
+
"required": ["summary", "status"],
|
|
99
|
+
"additionalProperties": False,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
turn = await thread.run("Summarize repository status", {"output_schema": schema})
|
|
103
|
+
print(turn.final_response)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
You can also create a JSON schema from a [Pydantic model](https://docs.pydantic.dev/) using `model_json_schema()`.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from pydantic import BaseModel
|
|
110
|
+
from typing import Literal
|
|
111
|
+
|
|
112
|
+
class StatusResponse(BaseModel):
|
|
113
|
+
summary: str
|
|
114
|
+
status: Literal["ok", "action_required"]
|
|
115
|
+
|
|
116
|
+
turn = await thread.run(
|
|
117
|
+
"Summarize repository status",
|
|
118
|
+
{"output_schema": StatusResponse.model_json_schema()}
|
|
119
|
+
)
|
|
120
|
+
print(turn.final_response)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Attaching images
|
|
124
|
+
|
|
125
|
+
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`.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
turn = await thread.run([
|
|
129
|
+
{"type": "text", "text": "Describe these screenshots"},
|
|
130
|
+
{"type": "local_image", "path": "./ui.png"},
|
|
131
|
+
{"type": "local_image", "path": "./diagram.jpg"},
|
|
132
|
+
])
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Resuming an existing thread
|
|
136
|
+
|
|
137
|
+
Threads are persisted in `~/.codex/sessions`. If you lose the in-memory `Thread` object, reconstruct it with `resume_thread()` and keep going.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
import os
|
|
141
|
+
|
|
142
|
+
saved_thread_id = os.environ["CODEX_THREAD_ID"]
|
|
143
|
+
thread = codex.resume_thread(saved_thread_id)
|
|
144
|
+
await thread.run("Implement the fix")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Working directory controls
|
|
148
|
+
|
|
149
|
+
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.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
thread = codex.start_thread({
|
|
153
|
+
"working_directory": "/path/to/project",
|
|
154
|
+
"skip_git_repo_check": True,
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Sandbox modes
|
|
159
|
+
|
|
160
|
+
Control how the agent interacts with your filesystem using `sandbox_mode`.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from codex_sdk import Codex, SandboxMode
|
|
164
|
+
|
|
165
|
+
thread = codex.start_thread({
|
|
166
|
+
"sandbox_mode": SandboxMode.WORKSPACE_WRITE,
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Available modes:
|
|
171
|
+
- `SandboxMode.READ_ONLY` - Agent can only read files
|
|
172
|
+
- `SandboxMode.WORKSPACE_WRITE` - Agent can read/write in the workspace
|
|
173
|
+
- `SandboxMode.DANGER_FULL_ACCESS` - Full filesystem access (use with caution)
|
|
174
|
+
|
|
175
|
+
### Approval policies
|
|
176
|
+
|
|
177
|
+
Control when the agent requires approval for actions.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from codex_sdk import Codex, ApprovalMode
|
|
181
|
+
|
|
182
|
+
thread = codex.start_thread({
|
|
183
|
+
"approval_policy": ApprovalMode.ON_FAILURE,
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Available modes:
|
|
188
|
+
- `ApprovalMode.NEVER` - Never require approval
|
|
189
|
+
- `ApprovalMode.ON_REQUEST` - Approve on explicit request
|
|
190
|
+
- `ApprovalMode.ON_FAILURE` - Approve after failures
|
|
191
|
+
- `ApprovalMode.UNTRUSTED` - Always require approval
|
|
192
|
+
|
|
193
|
+
### Cancelling a turn
|
|
194
|
+
|
|
195
|
+
Use `asyncio.Event` to cancel an ongoing turn (equivalent to `AbortSignal` in TypeScript).
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
import asyncio
|
|
199
|
+
|
|
200
|
+
async def cancellable_example():
|
|
201
|
+
codex = Codex()
|
|
202
|
+
thread = codex.start_thread()
|
|
203
|
+
|
|
204
|
+
cancel_event = asyncio.Event()
|
|
205
|
+
|
|
206
|
+
async def cancel_after_delay():
|
|
207
|
+
await asyncio.sleep(5)
|
|
208
|
+
cancel_event.set()
|
|
209
|
+
|
|
210
|
+
# Start cancellation timer
|
|
211
|
+
asyncio.create_task(cancel_after_delay())
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
turn = await thread.run(
|
|
215
|
+
"Long running task",
|
|
216
|
+
{"cancel_event": cancel_event}
|
|
217
|
+
)
|
|
218
|
+
except asyncio.CancelledError:
|
|
219
|
+
print("Turn was cancelled")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Controlling the Codex CLI environment
|
|
223
|
+
|
|
224
|
+
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.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
codex = Codex({
|
|
228
|
+
"env": {
|
|
229
|
+
"PATH": "/usr/local/bin",
|
|
230
|
+
},
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The SDK still injects its required variables (such as `OPENAI_BASE_URL` and `CODEX_API_KEY`) on top of the environment you provide.
|
|
235
|
+
|
|
236
|
+
### Passing `--config` overrides
|
|
237
|
+
|
|
238
|
+
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.
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
codex = Codex({
|
|
242
|
+
"config": {
|
|
243
|
+
"show_raw_agent_reasoning": True,
|
|
244
|
+
"sandbox_workspace_write": {"network_access": True},
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Thread options still take precedence for overlapping settings because they are emitted after these global overrides.
|
|
250
|
+
|
|
251
|
+
## API Reference
|
|
252
|
+
|
|
253
|
+
### Classes
|
|
254
|
+
|
|
255
|
+
#### `Codex`
|
|
256
|
+
Main entry point for the SDK.
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
codex = Codex(options: CodexOptions | None = None)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Methods:
|
|
263
|
+
- `start_thread(options: ThreadOptions | None = None) -> Thread` - Start a new conversation
|
|
264
|
+
- `resume_thread(thread_id: str, options: ThreadOptions | None = None) -> Thread` - Resume an existing conversation
|
|
265
|
+
|
|
266
|
+
#### `Thread`
|
|
267
|
+
Represents a conversation with the agent.
|
|
268
|
+
|
|
269
|
+
Properties:
|
|
270
|
+
- `id: str | None` - Thread ID (populated after first turn)
|
|
271
|
+
|
|
272
|
+
Methods:
|
|
273
|
+
- `async run(input: Input, turn_options: TurnOptions | None = None) -> Turn` - Execute a turn and return results
|
|
274
|
+
- `async run_streamed(input: Input, turn_options: TurnOptions | None = None) -> StreamedTurn` - Execute a turn with streaming events
|
|
275
|
+
|
|
276
|
+
#### `Turn`
|
|
277
|
+
Result of a completed turn.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
@dataclass
|
|
281
|
+
class Turn:
|
|
282
|
+
items: list[ThreadItem] # All items produced during the turn
|
|
283
|
+
final_response: str # The agent's final response text
|
|
284
|
+
usage: Usage | None # Token usage statistics
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### `StreamedTurn`
|
|
288
|
+
Result of a streamed turn.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
@dataclass
|
|
292
|
+
class StreamedTurn:
|
|
293
|
+
events: AsyncGenerator[ThreadEvent, None] # Async generator of events
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Types
|
|
297
|
+
|
|
298
|
+
#### Input Types
|
|
299
|
+
- `Input = str | list[UserInput]` - User input (string or structured)
|
|
300
|
+
- `TextUserInput` - Text input: `{"type": "text", "text": "..."}`
|
|
301
|
+
- `ImageUserInput` - Image input: `{"type": "local_image", "path": "..."}`
|
|
302
|
+
|
|
303
|
+
#### Event Types
|
|
304
|
+
- `ThreadStartedEvent` - Thread started
|
|
305
|
+
- `TurnStartedEvent` - Turn started
|
|
306
|
+
- `TurnCompletedEvent` - Turn completed (includes usage)
|
|
307
|
+
- `TurnFailedEvent` - Turn failed (includes error)
|
|
308
|
+
- `ItemStartedEvent` - Item started
|
|
309
|
+
- `ItemUpdatedEvent` - Item updated
|
|
310
|
+
- `ItemCompletedEvent` - Item completed
|
|
311
|
+
- `ThreadErrorEvent` - Unrecoverable error
|
|
312
|
+
|
|
313
|
+
#### Item Types
|
|
314
|
+
- `AgentMessageItem` - Agent's response text
|
|
315
|
+
- `ReasoningItem` - Agent's reasoning summary
|
|
316
|
+
- `CommandExecutionItem` - Shell command execution
|
|
317
|
+
- `FileChangeItem` - File modifications
|
|
318
|
+
- `McpToolCallItem` - MCP tool invocation
|
|
319
|
+
- `WebSearchItem` - Web search query
|
|
320
|
+
- `TodoListItem` - Agent's task list
|
|
321
|
+
- `ErrorItem` - Non-fatal error
|
|
322
|
+
|
|
323
|
+
### Enums
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
class SandboxMode(StrEnum):
|
|
327
|
+
READ_ONLY = "read-only"
|
|
328
|
+
WORKSPACE_WRITE = "workspace-write"
|
|
329
|
+
DANGER_FULL_ACCESS = "danger-full-access"
|
|
330
|
+
|
|
331
|
+
class ApprovalMode(StrEnum):
|
|
332
|
+
NEVER = "never"
|
|
333
|
+
ON_REQUEST = "on-request"
|
|
334
|
+
ON_FAILURE = "on-failure"
|
|
335
|
+
UNTRUSTED = "untrusted"
|
|
336
|
+
|
|
337
|
+
class ModelReasoningEffort(StrEnum):
|
|
338
|
+
MINIMAL = "minimal"
|
|
339
|
+
LOW = "low"
|
|
340
|
+
MEDIUM = "medium"
|
|
341
|
+
HIGH = "high"
|
|
342
|
+
XHIGH = "xhigh"
|
|
343
|
+
|
|
344
|
+
class WebSearchMode(StrEnum):
|
|
345
|
+
DISABLED = "disabled"
|
|
346
|
+
CACHED = "cached"
|
|
347
|
+
LIVE = "live"
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Development
|
|
351
|
+
|
|
352
|
+
### Setup
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Clone the repository
|
|
356
|
+
git clone https://github.com/openai/codex.git
|
|
357
|
+
cd codex/sdk/python
|
|
358
|
+
|
|
359
|
+
# Install development dependencies
|
|
360
|
+
pip install -e ".[dev]"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Running tests
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
pytest tests/ -v
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Linting and formatting
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Lint
|
|
373
|
+
ruff check codex_sdk/
|
|
374
|
+
|
|
375
|
+
# Format
|
|
376
|
+
ruff format codex_sdk/
|
|
377
|
+
|
|
378
|
+
# Type check
|
|
379
|
+
mypy codex_sdk/
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Release Process
|
|
383
|
+
|
|
384
|
+
Releases are automated via GitHub Actions.
|
|
385
|
+
|
|
386
|
+
### Creating a release
|
|
387
|
+
|
|
388
|
+
1. Update the version in `codex_sdk/__init__.py`:
|
|
389
|
+
```python
|
|
390
|
+
__version__ = "0.1.0"
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
2. Commit the version change:
|
|
394
|
+
```bash
|
|
395
|
+
git add codex_sdk/__init__.py
|
|
396
|
+
git commit -m "Bump version to 0.1.0"
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
3. Create and push a tag:
|
|
400
|
+
```bash
|
|
401
|
+
git tag v0.1.0
|
|
402
|
+
git push origin main --tags
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
4. GitHub Actions will automatically:
|
|
406
|
+
- Build the package
|
|
407
|
+
- Publish to PyPI
|
|
408
|
+
- Create a GitHub Release
|
|
409
|
+
|
|
410
|
+
### Required secrets
|
|
411
|
+
|
|
412
|
+
Set the following secrets in your GitHub repository:
|
|
413
|
+
|
|
414
|
+
- `PYPI_TOKEN`: Your PyPI API token for publishing packages
|
|
415
|
+
|
|
416
|
+
To create a PyPI token:
|
|
417
|
+
1. Go to https://pypi.org/manage/account/token/
|
|
418
|
+
2. Create a new token with "Upload packages" scope
|
|
419
|
+
3. Add it to your GitHub repository secrets
|
|
420
|
+
|
|
421
|
+
## License
|
|
422
|
+
|
|
423
|
+
Apache-2.0
|