Files
codex/sdk/python/docs/faq.md
Ahmed Ibrahim 4c89772314 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.
2026-05-16 19:49:28 -07:00

3.3 KiB

FAQ

Thread vs turn

  • A Thread is conversation state.
  • A Turn is one model execution inside that thread.
  • Multi-turn chat means multiple turns on the same Thread.

run() vs stream()

  • TurnHandle.run() / AsyncTurnHandle.run() is the easiest path. It consumes events until completion and returns the public app-server Turn model from openai_codex.types.
  • TurnHandle.stream() / AsyncTurnHandle.stream() yields raw notifications (Notification) so you can react event-by-event.

Choose run() for most apps. Choose stream() for progress UIs, custom timeout logic, or custom parsing.

Sync vs async clients

  • Codex is the sync public API.
  • AsyncCodex is an async replica of the same public API shape.
  • Prefer async with AsyncCodex() for async code. It is the standard path for explicit startup/shutdown, and AsyncCodex initializes lazily on context entry or first awaited API use.

If your app is not already async, stay with Codex.

How do I log in?

  • login_api_key(...) authenticates immediately with an API key.
  • login_chatgpt() starts browser login and returns a handle with auth_url.
  • login_chatgpt_device_code() starts device-code login and returns a handle with verification_url and user_code.
  • Interactive handles expose wait() for the matching account/login/completed notification and cancel() to stop that attempt.
  • account() reads the current account state, and logout() clears it.

Public kwargs are snake_case

Public API keyword names are snake_case. The SDK still maps them to wire camelCase under the hood.

If you are migrating older code, update these names:

  • approvalPolicy -> approval_policy
  • baseInstructions -> base_instructions
  • developerInstructions -> developer_instructions
  • modelProvider -> model_provider
  • modelProviders -> model_providers
  • sortKey -> sort_key
  • sourceKinds -> source_kinds
  • outputSchema -> output_schema
  • sandboxPolicy -> sandbox_policy

Why only thread_start(...) and thread_resume(...)?

The public API keeps only explicit lifecycle calls:

  • thread_start(...) to create new threads
  • thread_resume(thread_id, ...) to continue existing threads

This avoids duplicate ways to do the same operation and keeps behavior explicit.

Why does constructor fail?

Codex() is eager: it starts transport and calls initialize in __init__.

Common causes:

  • published runtime package (openai-codex-cli-bin) is not installed
  • local codex_bin override points to a missing file
  • incompatible/old app-server

Why does a turn "hang"?

A turn is complete only when turn/completed arrives for that turn ID.

  • run() waits for this automatically.
  • With stream(), keep consuming notifications until completion.

How do I retry safely?

Use retry_on_overload(...) for transient overload failures (ServerBusyError).

Do not blindly retry all errors. For InvalidParamsError or MethodNotFoundError, fix inputs/version compatibility instead.

Common pitfalls

  • Starting a new thread for every prompt when you wanted continuity.
  • Forgetting to close() (or not using context managers).
  • Assuming run() returns extra SDK-only fields instead of the public Turn model.
  • Mixing SDK input classes with raw dicts incorrectly.