codex-python 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-python
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: A minimal Python library scaffold for codex-python
5
5
  Project-URL: Homepage, https://github.com/gersmann/codex-python
6
6
  Project-URL: Repository, https://github.com/gersmann/codex-python
@@ -41,11 +41,11 @@ Description-Content-Type: text/markdown
41
41
 
42
42
  # codex-python
43
43
 
44
- A minimal Python library scaffold using `uv` with Python 3.13+.
44
+ Python interface to Codex, packaged as a single distribution (`codex-python`). Platform wheels include a native extension for in‑process execution. The library consolidates on native bindings (no subprocess wrapper).
45
45
 
46
46
  ## Quickstart
47
47
 
48
- - Requires Python 3.13+.
48
+ - Requires Python 3.12+.
49
49
  - Package import name: `codex`.
50
50
  - Distribution name (PyPI): `codex-python`.
51
51
 
@@ -56,42 +56,39 @@ A minimal Python library scaffold using `uv` with Python 3.13+.
56
56
 
57
57
  ## Usage
58
58
 
59
- Basic non-interactive execution via Codex CLI:
59
+ Native, non-interactive execution with Pydantic config:
60
60
 
61
61
  ```
62
- from codex import run_exec
62
+ from codex.api import run_exec, CodexClient
63
+ from codex.config import CodexConfig, ApprovalPolicy, SandboxMode
63
64
 
64
- out = run_exec("explain this repo")
65
- print(out)
66
- ```
67
-
68
- Options:
69
-
70
- - Choose model: `run_exec("...", model="gpt-4.1")`
71
- - Full auto: `run_exec("scaffold a cli", full_auto=True)`
72
- - Run in another dir: `run_exec("...", cd="/path/to/project")`
65
+ cfg = CodexConfig(
66
+ model="gpt-5",
67
+ model_provider="openai",
68
+ approval_policy=ApprovalPolicy.ON_REQUEST,
69
+ sandbox_mode=SandboxMode.WORKSPACE_WRITE,
70
+ )
73
71
 
74
- Streaming JSON events (no PyO3 required):
72
+ events = run_exec("explain this repo", config=cfg)
75
73
 
74
+ client = CodexClient(config=cfg)
75
+ for ev in client.start_conversation("add a smoke test"):
76
+ print(ev)
76
77
  ```
77
- from codex.protocol.runtime import stream_exec_events
78
-
79
- for event in stream_exec_events("explain this repo", full_auto=True):
80
- # event is a dict with shape {"id": str, "msg": {...}}
81
- print(event)
82
- ```
83
-
84
- The event payload matches the Pydantic models in `codex.protocol.types` (e.g., `EventMsg`).
85
78
 
86
- Using a client with defaults:
79
+ To stream raw dict events directly from the native layer:
87
80
 
88
81
  ```
89
- from codex import CodexClient
82
+ from codex.native import start_exec_stream
90
83
 
91
- client = CodexClient(model="gpt-4.1", full_auto=True)
92
- print(client.run("explain this repo"))
84
+ for e in start_exec_stream("explain this repo", config_overrides=cfg.to_dict()):
85
+ # each `e` is a dict representing an event envelope
86
+ print(e)
93
87
  ```
94
88
 
89
+ The event payload is typed: `Event.msg` is a union `EventMsg` from `codex.protocol.types`,
90
+ and is also exported at `codex.EventMsg` for convenience.
91
+
95
92
  ### Install uv
96
93
 
97
94
  - macOS (Homebrew): `brew install uv`
@@ -148,8 +145,50 @@ make gen-protocol
148
145
  ```
149
146
 
150
147
  This will:
151
- - run `codex-proj/codex-rs/protocol-ts` to emit TypeScript types under `.generated/ts/`
152
- - convert them to Python `TypedDict`/`Literal` aliases at `codex/protocol/types.py`
148
+ - emit TypeScript types under `.generated/ts/`
149
+ - convert them to Python Pydantic models + Literal unions at `codex/protocol/types.py`
150
+
151
+ ### Native bindings (PyO3)
152
+
153
+ - Install path
154
+
155
+ `pip install codex-python` installs a platform wheel that includes the native extension on supported platforms (Python 3.12/3.13; CI also attempts 3.14).
156
+
157
+ - Build locally (requires Rust + maturin):
158
+
159
+ ```
160
+ make dev-native
161
+ ```
162
+
163
+ - Notes:
164
+ - The native path embeds Codex directly; no subprocess.
165
+ - A helper `codex.native.preview_config(...)` returns a compact snapshot of the effective configuration (selected fields) for tests and introspection.
166
+
167
+ ### Configuration (Pydantic)
168
+
169
+ Use the `CodexConfig` Pydantic model to pass overrides which mirror the Rust `ConfigOverrides`:
170
+
171
+ ```
172
+ from codex.config import CodexConfig, ApprovalPolicy, SandboxMode
173
+ from codex.api import run_exec, CodexClient
174
+
175
+ cfg = CodexConfig(
176
+ model="gpt-5",
177
+ model_provider="openai",
178
+ approval_policy=ApprovalPolicy.ON_REQUEST,
179
+ sandbox_mode=SandboxMode.WORKSPACE_WRITE,
180
+ cwd="/path/to/project",
181
+ include_apply_patch_tool=True,
182
+ )
183
+
184
+ events = run_exec("Explain this project", config=cfg)
185
+
186
+ client = CodexClient(config=cfg)
187
+ for ev in client.start_conversation("Add a test for feature X"):
188
+ print(ev)
189
+ ```
190
+
191
+ `CodexConfig.to_dict()` emits only set (non‑None) fields and serializes enums to their kebab‑case strings to match the native Rust types.
153
192
 
154
193
  ## Project Layout
155
194
 
@@ -168,4 +207,4 @@ Version is managed via `codex/__init__.py` and exposed as `__version__`. The bui
168
207
 
169
208
  ## Python Compatibility
170
209
 
171
- - Requires Python `>=3.13`.
210
+ - Supported: Python 3.12, 3.13. CI attempts 3.14 wheels when available.
@@ -0,0 +1,11 @@
1
+ codex/__init__.py,sha256=xlyhxrsoviZDinInmCg1mHhybg5gFtxuuPKe4-WKp3w,606
2
+ codex/api.py,sha256=FhRSIAu5dpwTB4sO2qjuVjH_akFoDAG-2wyAyRSGCvw,2892
3
+ codex/config.py,sha256=388mrVtzyA6t8qigS8JxFSFPJbT3A-KMDMP-YQo6rUs,2869
4
+ codex/event.py,sha256=HPS6ySBa7Pwn4gRjQKj2yUE5_kiad4WFqwmA7aEdoiI,387
5
+ codex/native.py,sha256=ukaOK5U96oaJaY80sFSUSFNhRv1Wefix_8M-BCUat7A,2063
6
+ codex/protocol/types.py,sha256=YxnQBq5sgV_JC7E0q5Hkp8EIRcpvSTnBm1Uhv4Lc4Zs,38558
7
+ codex/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
8
+ codex_python-0.2.0.dist-info/METADATA,sha256=hLsX65ljGft-oPgvimWlDACHJAytrB1yjSxFW97fESw,6599
9
+ codex_python-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ codex_python-0.2.0.dist-info/licenses/LICENSE,sha256=ZhGahTKhsCbPWNmZ7ugZ14LVewMo4Gh1OeIOlQabyrI,1066
11
+ codex_python-0.2.0.dist-info/RECORD,,
codex/protocol/runtime.py DELETED
@@ -1,80 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import subprocess
6
- from collections.abc import Iterator
7
- from typing import Any
8
-
9
- from pydantic import BaseModel
10
-
11
-
12
- class Event(BaseModel):
13
- """Protocol event envelope emitted by `codex exec --json`.
14
-
15
- Note: `msg` is kept as a raw mapping to preserve all fields from
16
- intersection types. If you need strong typing, try validating
17
- against `codex.protocol.types.EventMsg` manually.
18
- """
19
-
20
- id: str
21
- msg: dict[str, Any]
22
-
23
-
24
- def stream_exec_events(
25
- prompt: str,
26
- *,
27
- executable: str = "codex",
28
- model: str | None = None,
29
- oss: bool = False,
30
- full_auto: bool = False,
31
- cd: str | None = None,
32
- skip_git_repo_check: bool = False,
33
- env: dict[str, str] | None = None,
34
- ) -> Iterator[Event]:
35
- """Spawn `codex exec --json` and yield Event objects from NDJSON stdout.
36
-
37
- Non-event lines (config summary, prompt echo) are ignored.
38
- """
39
- cmd: list[str] = [executable]
40
- if cd:
41
- cmd += ["--cd", cd]
42
- if model:
43
- cmd += ["-m", model]
44
- if oss:
45
- cmd.append("--oss")
46
- if full_auto:
47
- cmd.append("--full-auto")
48
- if skip_git_repo_check:
49
- cmd.append("--skip-git-repo-check")
50
- cmd += ["exec", "--json", prompt]
51
-
52
- with subprocess.Popen(
53
- cmd,
54
- stdout=subprocess.PIPE,
55
- stderr=subprocess.PIPE,
56
- text=True,
57
- env={**os.environ, **(env or {})},
58
- ) as proc:
59
- assert proc.stdout is not None
60
- for line in proc.stdout:
61
- line = line.strip()
62
- if not line:
63
- continue
64
- try:
65
- obj = json.loads(line)
66
- except json.JSONDecodeError:
67
- continue
68
-
69
- # Filter out non-event helper lines
70
- if not isinstance(obj, dict):
71
- continue
72
- if "id" in obj and "msg" in obj:
73
- # Attempt to validate into our Pydantic Event model
74
- yield Event.model_validate(obj)
75
-
76
- # Drain stderr for diagnostics if the process failed
77
- ret = proc.wait()
78
- if ret != 0 and proc.stderr is not None:
79
- err = proc.stderr.read()
80
- raise RuntimeError(f"codex exec failed with {ret}: {err}")
@@ -1,9 +0,0 @@
1
- codex/__init__.py,sha256=H12NLxWqZAOhHbkZO0rnMRhkdo_9y5DUVTmxyX2ncI0,514
2
- codex/api.py,sha256=gabhwkBQpdu7QdA_K75m8TPpusC9a4LteyhiZN0cI1Y,5173
3
- codex/protocol/runtime.py,sha256=lfnlkMYDg303Ug8awiu2FKwmbz3PGWnhTpFng-CsIe4,2229
4
- codex/protocol/types.py,sha256=852vSY0cdepXcu9DOJakzhsZBYpa0xF9aqBBj9GI_hE,39108
5
- codex/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
- codex_python-0.1.2.dist-info/METADATA,sha256=swB6w-sW3820mh45KzPfoxlYBGIvAzWjN8N3efzySZg,5083
7
- codex_python-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- codex_python-0.1.2.dist-info/licenses/LICENSE,sha256=ZhGahTKhsCbPWNmZ7ugZ14LVewMo4Gh1OeIOlQabyrI,1066
9
- codex_python-0.1.2.dist-info/RECORD,,