openai-codex 0.1.0b1__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.
- openai_codex-0.1.0b1/PKG-INFO +117 -0
- openai_codex-0.1.0b1/README.md +89 -0
- openai_codex-0.1.0b1/examples/README.md +91 -0
- openai_codex-0.1.0b1/pyproject.toml +92 -0
- openai_codex-0.1.0b1/src/openai_codex/__init__.py +93 -0
- openai_codex-0.1.0b1/src/openai_codex/_approval_mode.py +51 -0
- openai_codex-0.1.0b1/src/openai_codex/_initialize_metadata.py +54 -0
- openai_codex-0.1.0b1/src/openai_codex/_inputs.py +73 -0
- openai_codex-0.1.0b1/src/openai_codex/_login.py +172 -0
- openai_codex-0.1.0b1/src/openai_codex/_message_router.py +222 -0
- openai_codex-0.1.0b1/src/openai_codex/_run.py +135 -0
- openai_codex-0.1.0b1/src/openai_codex/_sandbox.py +78 -0
- openai_codex-0.1.0b1/src/openai_codex/_version.py +36 -0
- openai_codex-0.1.0b1/src/openai_codex/api.py +807 -0
- openai_codex-0.1.0b1/src/openai_codex/async_client.py +285 -0
- openai_codex-0.1.0b1/src/openai_codex/client.py +694 -0
- openai_codex-0.1.0b1/src/openai_codex/errors.py +121 -0
- openai_codex-0.1.0b1/src/openai_codex/generated/__init__.py +1 -0
- openai_codex-0.1.0b1/src/openai_codex/generated/notification_registry.py +177 -0
- openai_codex-0.1.0b1/src/openai_codex/generated/v2_all.py +8287 -0
- openai_codex-0.1.0b1/src/openai_codex/models.py +99 -0
- openai_codex-0.1.0b1/src/openai_codex/py.typed +0 -0
- openai_codex-0.1.0b1/src/openai_codex/retry.py +41 -0
- openai_codex-0.1.0b1/src/openai_codex/types.py +81 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openai-codex
|
|
3
|
+
Version: 0.1.0b1
|
|
4
|
+
Summary: Python SDK for Codex
|
|
5
|
+
Project-URL: Homepage, https://github.com/openai/codex
|
|
6
|
+
Project-URL: Repository, https://github.com/openai/codex
|
|
7
|
+
Project-URL: Issues, https://github.com/openai/codex/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/openai/codex/tree/main/sdk/python/docs
|
|
9
|
+
Author: OpenAI
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: agents,ai,codex,llm,sdk
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: openai-codex-cli-bin==0.132.0
|
|
22
|
+
Requires-Dist: pydantic>=2.12
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: datamodel-code-generator==0.31.2; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.15.8; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# OpenAI Codex Python SDK (Beta)
|
|
30
|
+
|
|
31
|
+
Build Python applications that start Codex threads, run turns, stream progress,
|
|
32
|
+
and control workspace access.
|
|
33
|
+
|
|
34
|
+
> [!NOTE]
|
|
35
|
+
> `openai-codex` is in beta. Public APIs may change before `1.0`.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
Install the SDK:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install openai-codex
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For reproducible environments, install this release exactly:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install openai-codex==0.1.0b1
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The SDK requires Python `>=3.10` and installs its compatible Codex runtime
|
|
52
|
+
dependency automatically. While beta releases are the only published SDK
|
|
53
|
+
releases, the normal install command selects the latest beta. After a stable
|
|
54
|
+
release exists, use `pip install --pre openai-codex` to explicitly select a
|
|
55
|
+
newer prerelease.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
The SDK reuses your existing Codex authentication when one is already
|
|
60
|
+
available:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from openai_codex import Codex
|
|
64
|
+
|
|
65
|
+
with Codex() as codex:
|
|
66
|
+
thread = codex.thread_start()
|
|
67
|
+
result = thread.run("Explain this repository in three bullets.")
|
|
68
|
+
print(result.final_response)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`thread.run(...)` returns a `TurnResult` containing the final response,
|
|
72
|
+
collected items, and token usage.
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
Existing Codex authentication is reused automatically. To start ChatGPT
|
|
77
|
+
browser login explicitly:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from openai_codex import Codex
|
|
81
|
+
|
|
82
|
+
with Codex() as codex:
|
|
83
|
+
login = codex.login_chatgpt()
|
|
84
|
+
print(login.auth_url)
|
|
85
|
+
print(login.wait().success)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For device-code login:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
with Codex() as codex:
|
|
92
|
+
login = codex.login_chatgpt_device_code()
|
|
93
|
+
print(login.verification_url, login.user_code)
|
|
94
|
+
login.wait()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For API-key login:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
with Codex() as codex:
|
|
101
|
+
codex.login_api_key("sk-...")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Built-In Help
|
|
105
|
+
|
|
106
|
+
Use Python's standard `help(openai_codex)`, `help(Codex)`, or
|
|
107
|
+
`python -m pydoc openai_codex` documentation tools.
|
|
108
|
+
|
|
109
|
+
## Documentation
|
|
110
|
+
|
|
111
|
+
- [Getting started](https://github.com/openai/codex/blob/main/sdk/python/docs/getting-started.md)
|
|
112
|
+
- [API reference](https://github.com/openai/codex/blob/main/sdk/python/docs/api-reference.md)
|
|
113
|
+
- [FAQ](https://github.com/openai/codex/blob/main/sdk/python/docs/faq.md)
|
|
114
|
+
- [Examples](https://github.com/openai/codex/blob/main/sdk/python/examples/README.md)
|
|
115
|
+
|
|
116
|
+
The package is licensed under the
|
|
117
|
+
[repository Apache License 2.0](https://github.com/openai/codex/blob/main/LICENSE).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# OpenAI Codex Python SDK (Beta)
|
|
2
|
+
|
|
3
|
+
Build Python applications that start Codex threads, run turns, stream progress,
|
|
4
|
+
and control workspace access.
|
|
5
|
+
|
|
6
|
+
> [!NOTE]
|
|
7
|
+
> `openai-codex` is in beta. Public APIs may change before `1.0`.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Install the SDK:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install openai-codex
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For reproducible environments, install this release exactly:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install openai-codex==0.1.0b1
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The SDK requires Python `>=3.10` and installs its compatible Codex runtime
|
|
24
|
+
dependency automatically. While beta releases are the only published SDK
|
|
25
|
+
releases, the normal install command selects the latest beta. After a stable
|
|
26
|
+
release exists, use `pip install --pre openai-codex` to explicitly select a
|
|
27
|
+
newer prerelease.
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
The SDK reuses your existing Codex authentication when one is already
|
|
32
|
+
available:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from openai_codex import Codex
|
|
36
|
+
|
|
37
|
+
with Codex() as codex:
|
|
38
|
+
thread = codex.thread_start()
|
|
39
|
+
result = thread.run("Explain this repository in three bullets.")
|
|
40
|
+
print(result.final_response)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`thread.run(...)` returns a `TurnResult` containing the final response,
|
|
44
|
+
collected items, and token usage.
|
|
45
|
+
|
|
46
|
+
## Authentication
|
|
47
|
+
|
|
48
|
+
Existing Codex authentication is reused automatically. To start ChatGPT
|
|
49
|
+
browser login explicitly:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from openai_codex import Codex
|
|
53
|
+
|
|
54
|
+
with Codex() as codex:
|
|
55
|
+
login = codex.login_chatgpt()
|
|
56
|
+
print(login.auth_url)
|
|
57
|
+
print(login.wait().success)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For device-code login:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
with Codex() as codex:
|
|
64
|
+
login = codex.login_chatgpt_device_code()
|
|
65
|
+
print(login.verification_url, login.user_code)
|
|
66
|
+
login.wait()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For API-key login:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
with Codex() as codex:
|
|
73
|
+
codex.login_api_key("sk-...")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Built-In Help
|
|
77
|
+
|
|
78
|
+
Use Python's standard `help(openai_codex)`, `help(Codex)`, or
|
|
79
|
+
`python -m pydoc openai_codex` documentation tools.
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
- [Getting started](https://github.com/openai/codex/blob/main/sdk/python/docs/getting-started.md)
|
|
84
|
+
- [API reference](https://github.com/openai/codex/blob/main/sdk/python/docs/api-reference.md)
|
|
85
|
+
- [FAQ](https://github.com/openai/codex/blob/main/sdk/python/docs/faq.md)
|
|
86
|
+
- [Examples](https://github.com/openai/codex/blob/main/sdk/python/examples/README.md)
|
|
87
|
+
|
|
88
|
+
The package is licensed under the
|
|
89
|
+
[repository Apache License 2.0](https://github.com/openai/codex/blob/main/LICENSE).
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Python SDK Examples
|
|
2
|
+
|
|
3
|
+
Each example folder contains runnable versions:
|
|
4
|
+
|
|
5
|
+
- `sync.py` (public sync surface: `Codex`)
|
|
6
|
+
- `async.py` (public async surface: `AsyncCodex`)
|
|
7
|
+
|
|
8
|
+
All examples intentionally use only public SDK exports from `openai_codex`
|
|
9
|
+
and `openai_codex.types`.
|
|
10
|
+
|
|
11
|
+
Examples use plain strings for text-only turns and typed input objects for
|
|
12
|
+
multimodal or structured input lists.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Python `>=3.10`
|
|
17
|
+
- Install the SDK for the same Python interpreter you will use to run examples
|
|
18
|
+
|
|
19
|
+
Install the published beta:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
python -m pip install openai-codex
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The SDK installs its pinned `openai-codex-cli-bin` runtime dependency.
|
|
26
|
+
The pinned runtime version comes from the SDK package dependency.
|
|
27
|
+
|
|
28
|
+
## Run From A Checkout
|
|
29
|
+
|
|
30
|
+
Contributors using these checked-in scripts should install development
|
|
31
|
+
dependencies from `sdk/python`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv sync --extra dev
|
|
35
|
+
source .venv/bin/activate
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The examples bootstrap local SDK imports from `sdk/python/src`. If the pinned
|
|
39
|
+
runtime is not already installed, the bootstrap installs the matching runtime
|
|
40
|
+
package for the active interpreter and cleans up temporary files afterward.
|
|
41
|
+
|
|
42
|
+
## Run examples
|
|
43
|
+
|
|
44
|
+
From `sdk/python`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python examples/<example-folder>/sync.py
|
|
48
|
+
python examples/<example-folder>/async.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The checked-in examples use the local SDK source tree automatically.
|
|
52
|
+
|
|
53
|
+
## Recommended first run
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python examples/01_quickstart_constructor/sync.py
|
|
57
|
+
python examples/01_quickstart_constructor/async.py
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Index
|
|
61
|
+
|
|
62
|
+
- `01_quickstart_constructor/`
|
|
63
|
+
- first run / sanity check
|
|
64
|
+
- `02_turn_run/`
|
|
65
|
+
- inspect full turn output fields
|
|
66
|
+
- `03_turn_stream_events/`
|
|
67
|
+
- stream a turn with a small curated event view
|
|
68
|
+
- `04_models_and_metadata/`
|
|
69
|
+
- discover visible models for the connected runtime
|
|
70
|
+
- `05_existing_thread/`
|
|
71
|
+
- resume a real existing thread (created in-script)
|
|
72
|
+
- `06_thread_lifecycle_and_controls/`
|
|
73
|
+
- thread lifecycle + control calls
|
|
74
|
+
- `07_image_and_text/`
|
|
75
|
+
- remote image URL + text multimodal turn
|
|
76
|
+
- `08_local_image_and_text/`
|
|
77
|
+
- local image + text multimodal turn using a generated temporary sample image
|
|
78
|
+
- `09_async_parity/`
|
|
79
|
+
- parity-style sync flow (see async parity in other examples)
|
|
80
|
+
- `10_error_handling_and_retry/`
|
|
81
|
+
- overload retry pattern + typed error handling structure
|
|
82
|
+
- `11_cli_mini_app/`
|
|
83
|
+
- interactive chat loop
|
|
84
|
+
- `12_turn_params_kitchen_sink/`
|
|
85
|
+
- structured output with a curated advanced `turn(...)` configuration
|
|
86
|
+
- `13_model_select_and_turn_params/`
|
|
87
|
+
- list models, pick highest model + highest supported reasoning effort, run turns, print message and usage
|
|
88
|
+
- `14_turn_controls/`
|
|
89
|
+
- separate `steer()` and `interrupt()` demos with concise summaries
|
|
90
|
+
- `15_login_and_account/`
|
|
91
|
+
- browser-login handle lifecycle, cancellation, and account inspection
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openai-codex"
|
|
7
|
+
version = "0.1.0b1"
|
|
8
|
+
description = "Python SDK for Codex"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [{ name = "OpenAI" }]
|
|
13
|
+
keywords = ["codex", "sdk", "llm", "ai", "agents"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
dependencies = ["pydantic>=2.12", "openai-codex-cli-bin==0.132.0"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/openai/codex"
|
|
28
|
+
Repository = "https://github.com/openai/codex"
|
|
29
|
+
Issues = "https://github.com/openai/codex/issues"
|
|
30
|
+
Documentation = "https://github.com/openai/codex/tree/main/sdk/python/docs"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = ["pytest>=8.0", "datamodel-code-generator==0.31.2", "ruff>=0.15.8"]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build]
|
|
36
|
+
exclude = [
|
|
37
|
+
".venv/**",
|
|
38
|
+
".venv2/**",
|
|
39
|
+
".pytest_cache/**",
|
|
40
|
+
"dist/**",
|
|
41
|
+
"build/**",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/openai_codex"]
|
|
46
|
+
include = [
|
|
47
|
+
"src/openai_codex/py.typed",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.sdist]
|
|
51
|
+
include = [
|
|
52
|
+
"src/openai_codex/**",
|
|
53
|
+
"README.md",
|
|
54
|
+
"pyproject.toml",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
addopts = "-q"
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
|
|
61
|
+
[tool.ruff]
|
|
62
|
+
target-version = "py310"
|
|
63
|
+
required-version = ">=0.15.8"
|
|
64
|
+
line-length = 100
|
|
65
|
+
extend-exclude = [
|
|
66
|
+
"notebooks/**",
|
|
67
|
+
"src/openai_codex/generated/**",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = ["E", "F", "I", "B", "C4"]
|
|
72
|
+
ignore = ["E501"]
|
|
73
|
+
preview = true
|
|
74
|
+
extend-safe-fixes = ["ALL"]
|
|
75
|
+
unfixable = ["F841"]
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint.per-file-ignores]
|
|
78
|
+
"examples/**/*.py" = ["E402"]
|
|
79
|
+
"tests/test_real_app_server_integration.py" = ["E402"]
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.isort]
|
|
82
|
+
combine-as-imports = true
|
|
83
|
+
|
|
84
|
+
[tool.uv]
|
|
85
|
+
exclude-newer = "7 days"
|
|
86
|
+
exclude-newer-package = { openai-codex-cli-bin = "2026-05-20T21:00:00Z" }
|
|
87
|
+
index-strategy = "first-index"
|
|
88
|
+
|
|
89
|
+
[tool.uv.pip]
|
|
90
|
+
exclude-newer = "7 days"
|
|
91
|
+
exclude-newer-package = { openai-codex-cli-bin = "2026-05-20T21:00:00Z" }
|
|
92
|
+
index-strategy = "first-index"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Python SDK for running Codex workflows.
|
|
2
|
+
|
|
3
|
+
Start with :class:`Codex` for synchronous applications or
|
|
4
|
+
:class:`AsyncCodex` for async applications. Most programs create a thread and
|
|
5
|
+
run a turn::
|
|
6
|
+
|
|
7
|
+
from openai_codex import Codex, Sandbox
|
|
8
|
+
|
|
9
|
+
with Codex() as codex:
|
|
10
|
+
thread = codex.thread_start(sandbox=Sandbox.workspace_write)
|
|
11
|
+
result = thread.run("Describe this project.")
|
|
12
|
+
print(result.final_response)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from ._version import __version__
|
|
16
|
+
from .api import (
|
|
17
|
+
ApprovalMode,
|
|
18
|
+
AsyncChatgptLoginHandle,
|
|
19
|
+
AsyncCodex,
|
|
20
|
+
AsyncDeviceCodeLoginHandle,
|
|
21
|
+
AsyncThread,
|
|
22
|
+
AsyncTurnHandle,
|
|
23
|
+
ChatgptLoginHandle,
|
|
24
|
+
Codex,
|
|
25
|
+
DeviceCodeLoginHandle,
|
|
26
|
+
ImageInput,
|
|
27
|
+
Input,
|
|
28
|
+
InputItem,
|
|
29
|
+
LocalImageInput,
|
|
30
|
+
MentionInput,
|
|
31
|
+
RunInput,
|
|
32
|
+
Sandbox,
|
|
33
|
+
SkillInput,
|
|
34
|
+
TextInput,
|
|
35
|
+
Thread,
|
|
36
|
+
TurnHandle,
|
|
37
|
+
TurnResult,
|
|
38
|
+
)
|
|
39
|
+
from .client import CodexConfig
|
|
40
|
+
from .errors import (
|
|
41
|
+
CodexError,
|
|
42
|
+
CodexRpcError,
|
|
43
|
+
InternalRpcError,
|
|
44
|
+
InvalidParamsError,
|
|
45
|
+
InvalidRequestError,
|
|
46
|
+
JsonRpcError,
|
|
47
|
+
MethodNotFoundError,
|
|
48
|
+
ParseError,
|
|
49
|
+
RetryLimitExceededError,
|
|
50
|
+
ServerBusyError,
|
|
51
|
+
TransportClosedError,
|
|
52
|
+
is_retryable_error,
|
|
53
|
+
)
|
|
54
|
+
from .retry import retry_on_overload
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"__version__",
|
|
58
|
+
"CodexConfig",
|
|
59
|
+
"Codex",
|
|
60
|
+
"AsyncCodex",
|
|
61
|
+
"ApprovalMode",
|
|
62
|
+
"Sandbox",
|
|
63
|
+
"ChatgptLoginHandle",
|
|
64
|
+
"DeviceCodeLoginHandle",
|
|
65
|
+
"AsyncChatgptLoginHandle",
|
|
66
|
+
"AsyncDeviceCodeLoginHandle",
|
|
67
|
+
"Thread",
|
|
68
|
+
"AsyncThread",
|
|
69
|
+
"TurnHandle",
|
|
70
|
+
"AsyncTurnHandle",
|
|
71
|
+
"TurnResult",
|
|
72
|
+
"Input",
|
|
73
|
+
"InputItem",
|
|
74
|
+
"RunInput",
|
|
75
|
+
"TextInput",
|
|
76
|
+
"ImageInput",
|
|
77
|
+
"LocalImageInput",
|
|
78
|
+
"SkillInput",
|
|
79
|
+
"MentionInput",
|
|
80
|
+
"retry_on_overload",
|
|
81
|
+
"CodexError",
|
|
82
|
+
"TransportClosedError",
|
|
83
|
+
"JsonRpcError",
|
|
84
|
+
"CodexRpcError",
|
|
85
|
+
"ParseError",
|
|
86
|
+
"InvalidRequestError",
|
|
87
|
+
"MethodNotFoundError",
|
|
88
|
+
"InvalidParamsError",
|
|
89
|
+
"InternalRpcError",
|
|
90
|
+
"ServerBusyError",
|
|
91
|
+
"RetryLimitExceededError",
|
|
92
|
+
"is_retryable_error",
|
|
93
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import NoReturn
|
|
5
|
+
|
|
6
|
+
from .generated.v2_all import (
|
|
7
|
+
ApprovalsReviewer,
|
|
8
|
+
AskForApproval,
|
|
9
|
+
AskForApprovalValue,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApprovalMode(str, Enum):
|
|
14
|
+
"""High-level approval behavior for escalated permission requests."""
|
|
15
|
+
|
|
16
|
+
deny_all = "deny_all"
|
|
17
|
+
auto_review = "auto_review"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _approval_mode_settings(
|
|
21
|
+
approval_mode: ApprovalMode,
|
|
22
|
+
) -> tuple[AskForApproval, ApprovalsReviewer | None]:
|
|
23
|
+
"""Map the public approval mode to generated app-server start params."""
|
|
24
|
+
if not isinstance(approval_mode, ApprovalMode):
|
|
25
|
+
supported = ", ".join(mode.value for mode in ApprovalMode)
|
|
26
|
+
raise ValueError(f"approval_mode must be one of: {supported}")
|
|
27
|
+
|
|
28
|
+
match approval_mode:
|
|
29
|
+
case ApprovalMode.auto_review:
|
|
30
|
+
return (
|
|
31
|
+
AskForApproval(root=AskForApprovalValue.on_request),
|
|
32
|
+
ApprovalsReviewer.auto_review,
|
|
33
|
+
)
|
|
34
|
+
case ApprovalMode.deny_all:
|
|
35
|
+
return AskForApproval(root=AskForApprovalValue.never), None
|
|
36
|
+
case _:
|
|
37
|
+
return _assert_never_approval_mode(approval_mode)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _assert_never_approval_mode(approval_mode: NoReturn) -> NoReturn:
|
|
41
|
+
"""Make approval mode mapping exhaustive for static type checkers."""
|
|
42
|
+
raise AssertionError(f"Unhandled approval mode: {approval_mode!r}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _approval_mode_override_settings(
|
|
46
|
+
approval_mode: ApprovalMode | None,
|
|
47
|
+
) -> tuple[AskForApproval | None, ApprovalsReviewer | None]:
|
|
48
|
+
"""Map an optional public approval mode to app-server override params."""
|
|
49
|
+
if approval_mode is None:
|
|
50
|
+
return None, None
|
|
51
|
+
return _approval_mode_settings(approval_mode)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .models import InitializeResponse, ServerInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _split_user_agent(user_agent: str) -> tuple[str | None, str | None]:
|
|
7
|
+
raw = user_agent.strip()
|
|
8
|
+
if not raw:
|
|
9
|
+
return None, None
|
|
10
|
+
if "/" in raw:
|
|
11
|
+
name, version = raw.split("/", 1)
|
|
12
|
+
return (name or None), (version or None)
|
|
13
|
+
parts = raw.split(maxsplit=1)
|
|
14
|
+
if len(parts) == 2:
|
|
15
|
+
return parts[0], parts[1]
|
|
16
|
+
return raw, None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_initialize_metadata(payload: InitializeResponse) -> InitializeResponse:
|
|
20
|
+
user_agent = (payload.userAgent or "").strip()
|
|
21
|
+
server = payload.serverInfo
|
|
22
|
+
|
|
23
|
+
server_name: str | None = None
|
|
24
|
+
server_version: str | None = None
|
|
25
|
+
|
|
26
|
+
if server is not None:
|
|
27
|
+
server_name = (server.name or "").strip() or None
|
|
28
|
+
server_version = (server.version or "").strip() or None
|
|
29
|
+
|
|
30
|
+
if (server_name is None or server_version is None) and user_agent:
|
|
31
|
+
parsed_name, parsed_version = _split_user_agent(user_agent)
|
|
32
|
+
if server_name is None:
|
|
33
|
+
server_name = parsed_name
|
|
34
|
+
if server_version is None:
|
|
35
|
+
server_version = parsed_version
|
|
36
|
+
|
|
37
|
+
normalized_server_name = (server_name or "").strip()
|
|
38
|
+
normalized_server_version = (server_version or "").strip()
|
|
39
|
+
if not user_agent or not normalized_server_name or not normalized_server_version:
|
|
40
|
+
raise RuntimeError(
|
|
41
|
+
"initialize response missing required metadata "
|
|
42
|
+
f"(user_agent={user_agent!r}, server_name={normalized_server_name!r}, server_version={normalized_server_version!r})"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if server is None:
|
|
46
|
+
payload.serverInfo = ServerInfo(
|
|
47
|
+
name=normalized_server_name,
|
|
48
|
+
version=normalized_server_version,
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
server.name = normalized_server_name
|
|
52
|
+
server.version = normalized_server_version
|
|
53
|
+
|
|
54
|
+
return payload
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from .models import JsonObject
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(slots=True)
|
|
9
|
+
class TextInput:
|
|
10
|
+
"""Text supplied to a turn or steering request."""
|
|
11
|
+
|
|
12
|
+
text: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True)
|
|
16
|
+
class ImageInput:
|
|
17
|
+
"""Remote image URL supplied as turn input."""
|
|
18
|
+
|
|
19
|
+
url: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(slots=True)
|
|
23
|
+
class LocalImageInput:
|
|
24
|
+
"""Local image path supplied as turn input."""
|
|
25
|
+
|
|
26
|
+
path: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(slots=True)
|
|
30
|
+
class SkillInput:
|
|
31
|
+
"""Named skill reference supplied as turn input."""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
path: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(slots=True)
|
|
38
|
+
class MentionInput:
|
|
39
|
+
"""Named resource mention supplied as turn input."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
path: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
InputItem = TextInput | ImageInput | LocalImageInput | SkillInput | MentionInput
|
|
46
|
+
Input = list[InputItem] | InputItem
|
|
47
|
+
RunInput = Input | str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _to_wire_item(item: InputItem) -> JsonObject:
|
|
51
|
+
if isinstance(item, TextInput):
|
|
52
|
+
return {"type": "text", "text": item.text}
|
|
53
|
+
if isinstance(item, ImageInput):
|
|
54
|
+
return {"type": "image", "url": item.url}
|
|
55
|
+
if isinstance(item, LocalImageInput):
|
|
56
|
+
return {"type": "localImage", "path": item.path}
|
|
57
|
+
if isinstance(item, SkillInput):
|
|
58
|
+
return {"type": "skill", "name": item.name, "path": item.path}
|
|
59
|
+
if isinstance(item, MentionInput):
|
|
60
|
+
return {"type": "mention", "name": item.name, "path": item.path}
|
|
61
|
+
raise TypeError(f"unsupported input item: {type(item)!r}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _to_wire_input(input: Input) -> list[JsonObject]:
|
|
65
|
+
if isinstance(input, list):
|
|
66
|
+
return [_to_wire_item(i) for i in input]
|
|
67
|
+
return [_to_wire_item(input)]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _normalize_run_input(input: RunInput) -> Input:
|
|
71
|
+
if isinstance(input, str):
|
|
72
|
+
return TextInput(input)
|
|
73
|
+
return input
|