Skip to content

Commit 5379e72

Browse files
committed
docs: add PLAN.md outlining subprocess JSON path and PyO3 plan
1 parent 9614233 commit 5379e72

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

PLAN.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Plan: Python Interface to Codex
2+
3+
## Goal
4+
Provide a clean Python interface to Codex suitable for apps and automation, with a quick path today (no native build) and a deeper path later (native bindings) while tracking the upstream protocol.
5+
6+
## Phase 1 — Subprocess + JSON (Implemented)
7+
- Approach
8+
- Invoke the Codex CLI (`codex exec`) with JSON output and parse NDJSON lines.
9+
- Validate event payloads using generated Pydantic models derived from the upstream protocol.
10+
- Components
11+
- `codex.api.run_exec(prompt, *, json=False, model=None, full_auto=False, cd=None, ...) -> str`
12+
- Adds `--json` when `json=True`.
13+
- `codex.protocol.runtime.stream_exec_events(prompt, *, model=None, full_auto=False, cd=None, ...) -> Iterator[Event]`
14+
- Spawns `codex exec --json` and yields validated `Event` envelopes (`id: str`, `msg: EventMsg`).
15+
- `codex.protocol.types` — Pydantic models (generated) for protocol request/response/event types.
16+
- Usage
17+
- One‑shot: `from codex import run_exec`
18+
- Streaming: `from codex.protocol.runtime import stream_exec_events`
19+
- Pros
20+
- No native build or wheel distribution required.
21+
- Works anywhere the CLI works; simple integration surface.
22+
- Cons
23+
- Separate process; IPC via stdout/stderr.
24+
- Less direct control than embedding codex directly.
25+
26+
## Phase 2 — Native PyO3 Bindings (Optional, Future)
27+
- Motivation
28+
- Single process; lower latency; richer control (interrupt/shutdown, approvals, patch streaming) exposed as Python calls.
29+
- Scope
30+
- Create a Rust crate (e.g., `crates/codex_py`) using `pyo3`.
31+
- Depend on upstream `codex-rs` crates via path deps under `codex-proj/codex-rs`.
32+
- Expose minimal API initially:
33+
- `start_session(config) -> Session`
34+
- `Session.send_user_message(text, images=...) -> Iterator[Event]`
35+
- `Session.interrupt()` / `Session.shutdown()`
36+
- Build & Publish
37+
- Use `maturin` (or `hatch-maturin`) to build wheels.
38+
- GH Actions matrix to build manylinux + macOS + Windows wheels on tags (`v*`).
39+
- Risks
40+
- Cross‑platform wheel complexity.
41+
- API surface alignment with evolving upstream.
42+
43+
## Protocol Models — Generation Flow
44+
- Source of truth: `codex-proj/codex-rs/protocol-ts` (Rust → TypeScript via `ts-rs`).
45+
- Generation steps (Make target):
46+
- `make gen-protocol`
47+
- Runs the TS generator to `.generated/ts/`.
48+
- Converts TS → Python Pydantic models at `codex/protocol/types.py`.
49+
- Notes
50+
- Generated code forbids extra fields (`model_config = ConfigDict(extra='forbid')`).
51+
- Optional TS fields (`| null`) map to Optional fields with default `None`.
52+
53+
## CI & Release
54+
- CI runs `ruff`, `mypy`, and `pytest` on pushes/PRs.
55+
- PyPI publish via Trusted Publishing (OIDC) on tags matching `v*`.
56+
57+
## Open Questions / Next Steps
58+
- Should we emit discriminated unions in Pydantic (e.g., `Field(discriminator="method")`) for simpler parsing of request/notification unions?
59+
- Add higher‑level convenience API: e.g., `CodexSession` in Python wrapping `stream_exec_events` with callbacks.
60+
- Windows and shell escaping: ensure prompts and cwd are robust on Windows.
61+
- Back‑pressure & large outputs: expose an async-friendly variant in the future (if needed).
62+
63+
## Acceptance Criteria
64+
- Phase 1 usable APIs documented and tested:
65+
- `run_exec(..., json=True)` returns JSON mode output.
66+
- `stream_exec_events(...)` yields validated `Event` instances for typical flows.
67+
- Protocol generator (`make gen-protocol`) succeeds and passes lint/mypy.
68+
- Publishing workflow remains functional after additions.

0 commit comments

Comments
 (0)