[codex] Rename Python SDK AppServerConfig to CodexConfig (#24800)

## Why

`AppServerConfig` is exported as part of the ergonomic Python SDK
surface and passed to `Codex(...)` and `AsyncCodex(...)`. That name
exposes the underlying app-server transport at the same layer where
users are configuring the Codex client. `CodexConfig` makes the common
callsite read naturally and names the object it configures.

## What changed

- Renamed the public configuration dataclass from `AppServerConfig` to
`CodexConfig`.
- Updated `Codex`, `AsyncCodex`, and the transport clients to accept
`CodexConfig`.
- Updated binary-resolution messages, package exports, docs, examples,
and related coverage to use the new public name.

## API impact

```python
from openai_codex import Codex, CodexConfig

with Codex(config=CodexConfig(codex_bin="/path/to/codex")) as codex:
    ...
```

Callers should now import and construct `CodexConfig`; `AppServerConfig`
is no longer part of the Python SDK surface.

## Validation

- `uv run --frozen --extra dev ruff check src/openai_codex scripts
examples tests`
- Tests are deferred to online CI for this PR.
This commit is contained in:
Ahmed Ibrahim
2026-05-27 16:10:15 -07:00
committed by GitHub
Unverified
parent 090144e0ec
commit 0db49a7e6a
22 changed files with 127 additions and 126 deletions
+16 -16
View File
@@ -12,7 +12,7 @@ from pydantic import BaseModel
from ._message_router import MessageRouter
from ._version import __version__ as SDK_VERSION
from .errors import AppServerError, TransportClosedError
from .errors import CodexError, TransportClosedError
from .generated.notification_registry import NOTIFICATION_MODELS
from .generated.v2_all import (
AccountLoginCompletedNotification,
@@ -94,7 +94,7 @@ def _installed_codex_path() -> Path:
except ImportError as exc:
raise FileNotFoundError(
"Unable to locate the pinned Codex runtime. Install the published SDK build "
f"with its {RUNTIME_PKG_NAME} dependency, or set AppServerConfig.codex_bin "
f"with its {RUNTIME_PKG_NAME} dependency, or set CodexConfig.codex_bin "
"explicitly."
) from exc
@@ -153,12 +153,12 @@ def _default_codex_bin_resolver_ops() -> CodexBinResolverOps:
)
def resolve_codex_bin(config: "AppServerConfig", ops: CodexBinResolverOps) -> Path:
def resolve_codex_bin(config: "CodexConfig", ops: CodexBinResolverOps) -> Path:
if config.codex_bin is not None:
codex_bin = Path(config.codex_bin)
if not ops.path_exists(codex_bin):
raise FileNotFoundError(
f"Codex binary not found at {codex_bin}. Set AppServerConfig.codex_bin "
f"Codex binary not found at {codex_bin}. Set CodexConfig.codex_bin "
"to a valid binary path."
)
return codex_bin
@@ -166,12 +166,12 @@ def resolve_codex_bin(config: "AppServerConfig", ops: CodexBinResolverOps) -> Pa
return ops.installed_codex_path()
def _resolve_codex_bin(config: "AppServerConfig") -> Path:
def _resolve_codex_bin(config: "CodexConfig") -> Path:
return resolve_codex_bin(config, _default_codex_bin_resolver_ops())
@dataclass(slots=True)
class AppServerConfig:
class CodexConfig:
codex_bin: str | None = None
launch_args_override: tuple[str, ...] | None = None
config_overrides: tuple[str, ...] = ()
@@ -183,15 +183,15 @@ class AppServerConfig:
experimental_api: bool = True
class AppServerClient:
class CodexClient:
"""Synchronous typed JSON-RPC client for `codex app-server` over stdio."""
def __init__(
self,
config: AppServerConfig | None = None,
config: CodexConfig | None = None,
approval_handler: ApprovalHandler | None = None,
) -> None:
self.config = config or AppServerConfig()
self.config = config or CodexConfig()
self._approval_handler = approval_handler or self._default_approval_handler
self._proc: subprocess.Popen[str] | None = None
self._lock = threading.Lock()
@@ -200,7 +200,7 @@ class AppServerClient:
self._stderr_thread: threading.Thread | None = None
self._reader_thread: threading.Thread | None = None
def __enter__(self) -> "AppServerClient":
def __enter__(self) -> "CodexClient":
self.start()
return self
@@ -289,7 +289,7 @@ class AppServerClient:
) -> ModelT:
result = self._request_raw(method, params)
if not isinstance(result, dict):
raise AppServerError(f"{method} response must be a JSON object")
raise CodexError(f"{method} response must be a JSON object")
return response_model.model_validate(result)
def _request_raw(self, method: str, params: JsonObject | None = None) -> JsonValue:
@@ -659,28 +659,28 @@ class AppServerClient:
def _write_message(self, payload: JsonObject) -> None:
if self._proc is None or self._proc.stdin is None:
raise TransportClosedError("app-server is not running")
raise TransportClosedError("Codex process is not running")
with self._lock:
self._proc.stdin.write(json.dumps(payload) + "\n")
self._proc.stdin.flush()
def _read_message(self) -> dict[str, JsonValue]:
if self._proc is None or self._proc.stdout is None:
raise TransportClosedError("app-server is not running")
raise TransportClosedError("Codex process is not running")
line = self._proc.stdout.readline()
if not line:
raise TransportClosedError(
f"app-server closed stdout. stderr_tail={self._stderr_tail()[:2000]}"
f"Codex process closed stdout. stderr_tail={self._stderr_tail()[:2000]}"
)
try:
message = json.loads(line)
except json.JSONDecodeError as exc:
raise AppServerError(f"Invalid JSON-RPC line: {line!r}") from exc
raise CodexError(f"Invalid JSON-RPC line: {line!r}") from exc
if not isinstance(message, dict):
raise AppServerError(f"Invalid JSON-RPC payload: {message!r}")
raise CodexError(f"Invalid JSON-RPC payload: {message!r}")
return message