Commit Graph

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.
  • [6/8] Add high-level Python SDK approval mode (#21910)
    ## Why
    
    The high-level SDK should expose the approval behavior it actually
    supports instead of leaking generated app-server routing fields. New
    work should have two clear choices: default auto review, or explicitly
    deny escalated permission requests. Existing threads and subsequent
    turns should preserve their current approval behavior unless the caller
    passes an override.
    
    ## What
    
    - Add the public `ApprovalMode` enum with `auto_review` and `deny_all`.
    - Default new thread creation to `ApprovalMode.auto_review`.
    - Preserve existing approval settings by default for resume, fork, run,
    and turn helpers.
    - Remove raw `approval_policy` / `approvals_reviewer` kwargs from
    high-level SDK wrappers.
    - Update generated wrapper output, docs, examples, notebooks, and tests
    for the high-level approval mode API.
    
    ## 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. This PR `[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
    
    - Added approval-mode mapping/default tests for new threads, existing
    threads, forks, resumes, and subsequent turns.
    
    ---------
    
    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>
  • [4/8] Define Python SDK public API surface (#21896)
    ## Why
    
    The SDK package root should be the ergonomic public client API, not a
    dump of every generated app-server schema type. Generated models still
    need a supported import path, but callers should be able to tell which
    names are high-level SDK entrypoints and which names are protocol value
    models.
    
    ## What
    
    - Define a curated root `__all__` for clients, handles, input helpers,
    retry helpers, config, and public errors.
    - Add a `types` module as the supported home for generated app-server
    response, event, enum, and helper models.
    - Update docs and examples to import protocol/value models from the type
    module.
    - Add tests that lock root exports, type-module exports, star-import
    behavior, and example import hygiene.
    
    ## 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. This PR `[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. #22021 `[8/8]` Add Python SDK Ruff formatting
    
    ## Verification
    
    - Added public API signature tests for root exports, `types` exports,
    and example imports.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Route Python SDK turn notifications by ID (#21778)
    ## Why
    
    The Python SDK previously protected the stdio transport with a single
    active turn-consumer guard. That avoided competing reads from stdout,
    but it also meant one `Codex`/`AsyncCodex` client could not stream
    multiple active turns at the same time. Notifications could also arrive
    before the caller received a `TurnHandle` and registered for streaming,
    so the SDK needed an explicit routing layer instead of letting
    individual API calls read directly from the shared transport.
    
    ## What Changed
    
    - Added a private `MessageRouter` that owns per-request response queues,
    per-turn notification queues, pending turn-notification replay, and
    global notification delivery behind a single stdout reader thread.
    - Generated typed notification routing metadata so turn IDs come from
    known payload shapes instead of router-side attribute guessing, with
    explicit fallback handling for unknown notification payloads.
    - Updated sync and async turn streaming so `TurnHandle.stream()`/`run()`
    and `stream_text()` consume only notifications for their own turn ID,
    while `AsyncAppServerClient` no longer serializes all transport calls
    behind one async lock.
    - Cleared pending turn-notification buffers when unregistered turns
    complete so never-consumed turn handles do not leave stale queues
    behind.
    - Removed the internal stream-until helper now that turn completion
    waiting can register directly with routed turn notifications.
    - Updated Python SDK docs and focused tests for concurrent transport
    calls, interleaved turn routing, buffered early notifications, unknown
    notification routing, async delegation, and routed turn completion
    behavior.
    
    ## Validation
    
    - `uv run --extra dev ruff format scripts/update_sdk_artifacts.py
    src/codex_app_server/_message_router.py src/codex_app_server/client.py
    src/codex_app_server/generated/notification_registry.py
    tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `uv run --extra dev ruff check scripts/update_sdk_artifacts.py
    src/codex_app_server/_message_router.py src/codex_app_server/client.py
    src/codex_app_server/generated/notification_registry.py
    tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `uv run --extra dev pytest tests/test_client_rpc_methods.py
    tests/test_public_api_runtime_behavior.py
    tests/test_async_client_behavior.py`
    - `git diff --check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • 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>