9 Commits

  • [codex] Accept string input for Python turns (#23162)
    ## Summary
    - Allow thread.turn and turn.steer, including async variants, to accept
    RunInput so plain strings work alongside typed input objects.
    - Export RunInput and update the SDK artifact generator so regenerated
    turn methods keep the same signature and normalization.
    - Update docs, examples, notebook cells, and tests to use string
    shorthand for text-only turns while keeping typed inputs for multimodal
    input.
    
    ## Validation
    - uv run --extra dev ruff format .
    - uv run --extra dev ruff check --output-format=github .
    - python3 -m py_compile sdk/python/src/openai_codex/__init__.py
    sdk/python/src/openai_codex/api.py
    sdk/python/src/openai_codex/_inputs.py
    sdk/python/scripts/update_sdk_artifacts.py
    sdk/python/tests/test_public_api_signatures.py
    sdk/python/tests/test_app_server_streaming.py
    sdk/python/tests/test_app_server_turn_controls.py
    sdk/python/tests/test_real_app_server_integration.py
    - python3 -c "import json;
    json.load(open('sdk/python/notebooks/sdk_walkthrough.ipynb'))"
    - sdk/python/.venv/bin/python -c "import inspect, openai_codex; from
    openai_codex import Thread, AsyncThread, TurnHandle, AsyncTurnHandle,
    RunInput; funcs=[Thread.run, Thread.turn, AsyncThread.run,
    AsyncThread.turn, TurnHandle.steer, AsyncTurnHandle.steer]; assert
    all(inspect.signature(fn).parameters['input'].annotation == 'RunInput'
    for fn in funcs); assert RunInput is openai_codex.RunInput"
  • [codex] Return TurnResult from Python turn handles (#23151)
    ## Why
    
    `TurnHandle.run()` returned the raw app-server `Turn`, whose live
    start/completed payloads do not include loaded `items`, so users saw
    empty `items` after starting a turn. That made the handle-based path
    behave differently from `Thread.run(...)`, and pushed examples toward
    persisted-thread reads plus helper extraction.
    
    This PR makes the run APIs standalone: starting a turn and running it
    returns collected turn data directly, or fails visibly when required
    stream events are missing.
    
    ## What Changed
    
    - Replaces the public `RunResult` export with `TurnResult`.
    - Adds turn metadata to `TurnResult`: `id`, `status`, `error`,
    `started_at`, `completed_at`, and `duration_ms`, alongside
    `final_response`, `items`, and `usage`.
    - Changes `TurnHandle.run()` and `AsyncTurnHandle.run()` to consume
    stream events with the same collector used by `Thread.run(...)`.
    - Exports `TurnError` from `openai_codex.types` for the new result
    shape.
    - Updates tests, examples, docs, and the walkthrough notebook to use
    `result.final_response` and `result.items` directly.
    - Removes persisted-thread helper paths and placeholder/skipped control
    flows from the public examples and notebook.
    
    ## Verification
    
    - `python3 -m py_compile ...` over changed SDK, example, and test Python
    files.
    - `python3 -c "import json;
    json.load(open('sdk/python/notebooks/sdk_walkthrough.ipynb'))"`
    - `git diff --check`
    - `PYTHONPATH=sdk/python/src python3 -c ...` import/signature smoke for
    `TurnResult`, `TurnHandle.run`, and `AsyncTurnHandle.run`.
  • sdk/python: add first-class login support (#23093)
    ## Why
    
    The Python SDK can already create threads and run turns, but
    authentication still has to be arranged outside the SDK. App-server
    already exposes account login, account inspection, logout, and
    `account/login/completed` notifications, so SDK users currently have to
    work around a missing public client layer for a core setup step.
    
    This change makes authentication a normal SDK workflow while preserving
    the backend flow shape: API-key login completes immediately, and
    interactive ChatGPT flows return live handles that complete later
    through app-server notifications.
    
    ## What changed
    
    - Added public sync and async auth methods on `Codex` / `AsyncCodex`:
      - `login_api_key(...)`
      - `login_chatgpt()`
      - `login_chatgpt_device_code()`
      - `account(...)`
      - `logout()`
    - Added public browser-login and device-code handle types with
    attempt-local `wait()` and `cancel()` helpers. Cancellation stays on the
    handle instead of a root-level SDK method.
    - Extended the Python app-server client and notification router so login
    completion events are routed by `login_id` without consuming unrelated
    global notifications.
    - Kept login request/handle logic in a focused internal `_login.py`
    module so `api.py` remains the public facade instead of absorbing more
    auth plumbing.
    - Exported the new handle types plus curated account/login response
    types from the SDK surfaces.
    - Updated SDK docs, added sync/async login walkthrough examples, and
    added a notebook login walkthrough cell.
    
    ## Verification
    
    Added SDK coverage for:
    
    - API-key login, account readback, and logout through the app-server
    harness in both sync and async clients.
    - Browser login cancellation plus `handle.wait()` completion through the
    real app-server boundary used by the Python SDK harness.
    - Waiter routing that stays scoped across replaced interactive login
    attempts, plus async handle cancellation coverage.
    - Login notification demuxing, replay of early completion events, and
    async client delegation.
    - Public export/signature assertions.
    - Real integration-suite smoke coverage for the new examples and
    notebook login cell.
  • [codex] Refine Python SDK user-facing docs (#22941)
    ## Summary
    - Remove maintainer and release-process wording from the Python SDK
    README and docs.
    - Rewrite SDK-facing comments/docstrings so they read as standalone
    product documentation.
    - Add a real app-server integration smoke that follows the public
    quickstart-style `Codex() -> thread_start() -> run()` path.
    
    ## Integration coverage
    - Add `test_real_quickstart_style_flow_smoke` in the real app-server
    integration suite.
    
    ## Validation
    - Local tests were not run per repo guidance. CI should validate this
    branch once the PR is online.
  • [8/8] Add Python SDK Ruff formatting (#22021)
    ## Why
    
    The Python SDK needs the same tight formatter/lint loop as the rest of
    the repo: a safe Ruff autofix pass, Ruff formatting, editor save
    behavior, and CI checks that catch drift. Without that loop, SDK changes
    can land with formatting or import ordering that differs from what
    reviewers and CI expect.
    
    ## What
    
    - Add Ruff configuration to `sdk/python/pyproject.toml`, excluding
    generated protocol code and notebooks from the normal lint/format pass.
    - Update `just fmt` so it still formats Rust and also runs Python SDK
    Ruff autofix and formatting.
    - Add Python SDK CI steps for `ruff check` and `ruff format --check`
    before pytest.
    - Recommend the Ruff VS Code extension and enable Python
    format/fix/organize-on-save so Cmd+S uses the same tooling.
    - Apply the resulting Ruff formatting to SDK Python files, examples, and
    the checked-in generated `v2_all.py` output emitted by the pinned
    generator.
    - Add a guard test for the `just fmt` recipe so it keeps working from
    both Rust and Python SDK working directories.
    
    ## Stack
    
    1. #21891 `[1/8]` Pin Python SDK runtime dependency
    2. #21893 `[2/8]` Generate Python SDK types from pinned runtime
    3. #21895 `[3/8]` Run Python SDK tests in CI
    4. #21896 `[4/8]` Define Python SDK public API surface
    5. #21905 `[5/8]` Rename Python SDK package to `openai-codex`
    6. #21910 `[6/8]` Add high-level Python SDK approval mode
    7. #22014 `[7/8]` Add Python SDK app-server integration harness
    8. This PR `[8/8]` Add Python SDK Ruff formatting
    
    ## Verification
    
    - Added `test_root_fmt_recipe_formats_rust_and_python_sdk` for the
    shared format recipe.
    - Ran `just fmt` after the recipe update.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [5/8] Rename Python SDK package to openai-codex (#21905)
    ## Why
    
    The SDK should publish under the reserved public distribution name
    `openai-codex`, and its import module should match that name in the
    Python style. Since package names can contain hyphens but import modules
    cannot, the public import path becomes `openai_codex`.
    
    Keeping the rename separate from the public API surface change makes the
    naming change easy to review and avoids mixing it with API curation.
    
    ## What
    
    - Rename the SDK distribution from `openai-codex-app-server-sdk` to
    `openai-codex`.
    - Rename the import package from `codex_app_server` to `openai_codex`.
    - Keep the runtime wheel as the separate `openai-codex-cli-bin`
    dependency.
    - Update docs, examples, notebooks, artifact scripts, lockfile metadata,
    and tests for the new distribution/module names.
    
    ## Stack
    
    1. #21891 `[1/8]` Pin Python SDK runtime dependency
    2. #21893 `[2/8]` Generate Python SDK types from pinned runtime
    3. #21895 `[3/8]` Run Python SDK tests in CI
    4. #21896 `[4/8]` Define Python SDK public API surface
    5. This PR `[5/8]` Rename Python SDK package to `openai-codex`
    6. #21910 `[6/8]` Add high-level Python SDK approval mode
    7. #22014 `[7/8]` Add Python SDK app-server integration harness
    8. #22021 `[8/8]` Add Python SDK Ruff formatting
    
    ## Verification
    
    - Updated package metadata and public API tests to assert the
    distribution and import names.
    
    Co-authored-by: Codex <noreply@openai.com>
  • Fix stale quickstart integration assertion (#15677)
    TL;DR: update the quickstart integration assertion to match the current
    example output.
    
    - replace the stale `Status:` expectation for
    `01_quickstart_constructor` with `Server:`, `Items:`, and `Text:`
    - keep the existing guard against `Server: unknown`
  • Add Python SDK thread.run convenience methods (#15088)
    ## TL;DR
    Add `thread.run(...)` / `async thread.run(...)` convenience methods to
    the Python SDK for the common case.
    
    - add `RunInput = Input | str` and `RunResult` with `final_response`,
    collected `items`, and optional `usage`
    - keep `thread.turn(...)` strict and lower-level for streaming,
    steering, interrupting, and raw generated `Turn` access
    - update Python SDK docs, quickstart examples, and tests for the sync
    and async convenience flows
    
    ## Validation
    - `python3 -m pytest sdk/python/tests/test_public_api_signatures.py
    sdk/python/tests/test_public_api_runtime_behavior.py`
    - `python3 -m pytest
    sdk/python/tests/test_real_app_server_integration.py -k
    'thread_run_convenience or async_thread_run_convenience'` (skipped in
    this environment)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add Python SDK public API and examples (#14446)
    ## TL;DR
    WIP esp the examples
    
    Thin the Python SDK public surface so the wrapper layer returns
    canonical app-server generated models directly.
    
    - keeps `Codex` / `AsyncCodex` / `Thread` / `Turn` and input helpers,
    but removes alias-only type layers and custom result models
    - `metadata` now returns `InitializeResponse` and `run()` returns the
    generated app-server `Turn`
    - updates docs, examples, notebook, and tests to use canonical generated
    types and regenerates `v2_all.py` against current schema
    - keeps the pinned runtime-package integration flow and real integration
    coverage
    
      ## Validation
      - `PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests`
    - `GH_TOKEN="$(gh auth token)" RUN_REAL_CODEX_TESTS=1
    PYTHONPATH=sdk/python/src python3 -m pytest sdk/python/tests -rs`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>