mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[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:
committed by
GitHub
Unverified
parent
090144e0ec
commit
0db49a7e6a
@@ -10,7 +10,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from openai_codex import AppServerConfig
|
||||
from openai_codex import CodexConfig
|
||||
|
||||
Json = dict[str, Any]
|
||||
|
||||
@@ -225,9 +225,9 @@ class AppServerHarness:
|
||||
shutil.rmtree(self.codex_home, ignore_errors=True)
|
||||
shutil.rmtree(self.workspace, ignore_errors=True)
|
||||
|
||||
def app_server_config(self) -> AppServerConfig:
|
||||
def app_server_config(self) -> CodexConfig:
|
||||
"""Build SDK config for an isolated pinned-runtime app-server process."""
|
||||
return AppServerConfig(
|
||||
return CodexConfig(
|
||||
cwd=str(self.workspace),
|
||||
env={
|
||||
"CODEX_HOME": str(self.codex_home),
|
||||
|
||||
@@ -5,14 +5,14 @@ import json
|
||||
|
||||
from app_server_harness import AppServerHarness
|
||||
|
||||
from openai_codex import AppServerConfig, Codex
|
||||
from openai_codex import Codex, CodexConfig
|
||||
from openai_codex.generated.v2_all import (
|
||||
ChatgptAuthTokensLoginAccountParams,
|
||||
LoginAccountParams,
|
||||
)
|
||||
|
||||
|
||||
def _app_server_config(harness: AppServerHarness) -> AppServerConfig:
|
||||
def _app_server_config(harness: AppServerHarness) -> CodexConfig:
|
||||
"""Build an isolated login config without inheriting ambient API-key auth."""
|
||||
config = harness.app_server_config()
|
||||
config.env = {**(config.env or {}), "OPENAI_API_KEY": ""}
|
||||
|
||||
@@ -114,7 +114,7 @@ def test_async_stream_routes_text_deltas_and_completion(tmp_path) -> None:
|
||||
|
||||
|
||||
def test_low_level_sync_stream_text_uses_real_turn_routing(tmp_path) -> None:
|
||||
"""AppServerClient.stream_text should stream through a real app-server turn."""
|
||||
"""CodexClient.stream_text should stream through a real app-server turn."""
|
||||
with AppServerHarness(tmp_path) as harness:
|
||||
harness.responses.enqueue_sse(
|
||||
streaming_response("low-sync-stream", "msg-low-sync-stream", ["fir", "st"])
|
||||
|
||||
@@ -669,7 +669,7 @@ def test_default_runtime_is_resolved_from_installed_runtime_package(
|
||||
path_exists=lambda path: path == fake_binary,
|
||||
)
|
||||
|
||||
config = client_module.AppServerConfig()
|
||||
config = client_module.CodexConfig()
|
||||
assert config.codex_bin is None
|
||||
assert client_module.resolve_codex_bin(config, ops) == fake_binary
|
||||
|
||||
@@ -717,7 +717,7 @@ def test_explicit_codex_bin_override_takes_priority(tmp_path: Path) -> None:
|
||||
path_exists=lambda path: path == explicit_binary,
|
||||
)
|
||||
|
||||
config = client_module.AppServerConfig(codex_bin=str(explicit_binary))
|
||||
config = client_module.CodexConfig(codex_bin=str(explicit_binary))
|
||||
assert client_module.resolve_codex_bin(config, ops) == explicit_binary
|
||||
|
||||
|
||||
@@ -732,7 +732,7 @@ def test_missing_runtime_package_requires_explicit_codex_bin() -> None:
|
||||
)
|
||||
|
||||
with pytest.raises(FileNotFoundError, match="missing packaged runtime"):
|
||||
client_module.resolve_codex_bin(client_module.AppServerConfig(), ops)
|
||||
client_module.resolve_codex_bin(client_module.CodexConfig(), ops)
|
||||
|
||||
|
||||
def test_broken_runtime_package_does_not_fall_back() -> None:
|
||||
@@ -746,6 +746,6 @@ def test_broken_runtime_package_does_not_fall_back() -> None:
|
||||
)
|
||||
|
||||
with pytest.raises(FileNotFoundError) as exc_info:
|
||||
client_module.resolve_codex_bin(client_module.AppServerConfig(), ops)
|
||||
client_module.resolve_codex_bin(client_module.CodexConfig(), ops)
|
||||
|
||||
assert str(exc_info.value) == ("missing packaged binary")
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
from openai_codex.async_client import AsyncAppServerClient
|
||||
from openai_codex.async_client import AsyncCodexClient
|
||||
from openai_codex.generated.v2_all import (
|
||||
TurnCompletedNotification,
|
||||
)
|
||||
@@ -15,7 +15,7 @@ def test_async_client_allows_concurrent_transport_calls() -> None:
|
||||
|
||||
async def scenario() -> int:
|
||||
"""Run two blocking sync calls and report peak overlap."""
|
||||
client = AsyncAppServerClient()
|
||||
client = AsyncCodexClient()
|
||||
active = 0
|
||||
max_active = 0
|
||||
|
||||
@@ -40,7 +40,7 @@ def test_async_client_turn_notification_methods_delegate_to_sync_client() -> Non
|
||||
|
||||
async def scenario() -> tuple[list[tuple[str, str]], Notification, str]:
|
||||
"""Record the sync-client calls made by async turn notification wrappers."""
|
||||
client = AsyncAppServerClient()
|
||||
client = AsyncCodexClient()
|
||||
event = Notification(
|
||||
method="unknown/direct",
|
||||
payload=UnknownNotification(params={"turnId": "turn-1"}),
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from openai_codex.client import AppServerClient, _params_dict
|
||||
from openai_codex.client import CodexClient, _params_dict
|
||||
from openai_codex.generated.notification_registry import notification_turn_id
|
||||
from openai_codex.generated.v2_all import (
|
||||
AgentMessageDeltaNotification,
|
||||
@@ -63,7 +63,7 @@ def test_thread_resume_response_accepts_auto_review_reviewer() -> None:
|
||||
|
||||
|
||||
def test_notifications_are_typed_with_canonical_v2_methods() -> None:
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
event = client._coerce_notification(
|
||||
"thread/tokenUsage/updated",
|
||||
{
|
||||
@@ -94,7 +94,7 @@ def test_notifications_are_typed_with_canonical_v2_methods() -> None:
|
||||
|
||||
|
||||
def test_unknown_notifications_fall_back_to_unknown_payloads() -> None:
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
event = client._coerce_notification(
|
||||
"unknown/notification",
|
||||
{
|
||||
@@ -110,7 +110,7 @@ def test_unknown_notifications_fall_back_to_unknown_payloads() -> None:
|
||||
|
||||
|
||||
def test_invalid_notification_payload_falls_back_to_unknown() -> None:
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
event = client._coerce_notification("thread/tokenUsage/updated", {"threadId": "missing"})
|
||||
|
||||
assert event.method == "thread/tokenUsage/updated"
|
||||
@@ -144,7 +144,7 @@ def test_generated_notification_turn_id_handles_known_payload_shapes() -> None:
|
||||
|
||||
def test_turn_notification_router_demuxes_registered_turns() -> None:
|
||||
"""The router should deliver out-of-order turn events to the matching queues."""
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
client.register_turn_notifications("turn-1")
|
||||
client.register_turn_notifications("turn-2")
|
||||
|
||||
@@ -187,7 +187,7 @@ def test_turn_notification_router_demuxes_registered_turns() -> None:
|
||||
|
||||
def test_client_reader_routes_interleaved_turn_notifications_by_turn_id() -> None:
|
||||
"""Reader-loop routing should preserve order within each interleaved turn stream."""
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
client.register_turn_notifications("turn-1")
|
||||
client.register_turn_notifications("turn-2")
|
||||
|
||||
@@ -266,7 +266,7 @@ def test_client_reader_routes_interleaved_turn_notifications_by_turn_id() -> Non
|
||||
|
||||
def test_turn_notification_router_buffers_events_before_registration() -> None:
|
||||
"""Early turn events should be replayed once their TurnHandle registers."""
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
client._router.route_notification(
|
||||
client._coerce_notification(
|
||||
"item/agentMessage/delta",
|
||||
@@ -291,7 +291,7 @@ def test_turn_notification_router_buffers_events_before_registration() -> None:
|
||||
|
||||
def test_turn_notification_router_clears_unregistered_turn_when_completed() -> None:
|
||||
"""A completed unregistered turn should not leave a pending queue behind."""
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
client._router.route_notification(
|
||||
client._coerce_notification(
|
||||
"item/agentMessage/delta",
|
||||
@@ -318,7 +318,7 @@ def test_turn_notification_router_clears_unregistered_turn_when_completed() -> N
|
||||
|
||||
def test_turn_notification_router_routes_unknown_turn_notifications() -> None:
|
||||
"""Unknown notifications should still route when their raw params carry a turn id."""
|
||||
client = AppServerClient()
|
||||
client = CodexClient()
|
||||
client.register_turn_notifications("turn-1")
|
||||
client.register_turn_notifications("turn-2")
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ def test_codex_init_failure_closes_client(monkeypatch: pytest.MonkeyPatch) -> No
|
||||
self._closed = True
|
||||
closed.append(True)
|
||||
|
||||
monkeypatch.setattr(public_api_module, "AppServerClient", FakeClient)
|
||||
monkeypatch.setattr(public_api_module, "CodexClient", FakeClient)
|
||||
|
||||
with pytest.raises(RuntimeError, match="missing required metadata"):
|
||||
Codex()
|
||||
|
||||
@@ -11,11 +11,11 @@ import openai_codex
|
||||
import openai_codex.types as public_types
|
||||
from openai_codex import (
|
||||
ApprovalMode,
|
||||
AppServerConfig,
|
||||
AsyncCodex,
|
||||
AsyncThread,
|
||||
AsyncTurnHandle,
|
||||
Codex,
|
||||
CodexConfig,
|
||||
Sandbox,
|
||||
Thread,
|
||||
TurnHandle,
|
||||
@@ -26,7 +26,7 @@ from openai_codex.types import InitializeResponse
|
||||
|
||||
EXPECTED_ROOT_EXPORTS = [
|
||||
"__version__",
|
||||
"AppServerConfig",
|
||||
"CodexConfig",
|
||||
"Codex",
|
||||
"AsyncCodex",
|
||||
"ApprovalMode",
|
||||
@@ -49,10 +49,10 @@ EXPECTED_ROOT_EXPORTS = [
|
||||
"SkillInput",
|
||||
"MentionInput",
|
||||
"retry_on_overload",
|
||||
"AppServerError",
|
||||
"CodexError",
|
||||
"TransportClosedError",
|
||||
"JsonRpcError",
|
||||
"AppServerRpcError",
|
||||
"CodexRpcError",
|
||||
"ParseError",
|
||||
"InvalidRequestError",
|
||||
"MethodNotFoundError",
|
||||
@@ -129,9 +129,9 @@ def _assert_no_any_annotations(fn: object) -> None:
|
||||
raise AssertionError(f"{fn} has public return annotation typed as Any")
|
||||
|
||||
|
||||
def test_root_exports_app_server_config() -> None:
|
||||
def test_root_exports_codex_config() -> None:
|
||||
"""The root package should expose the process configuration object."""
|
||||
assert AppServerConfig.__name__ == "AppServerConfig"
|
||||
assert CodexConfig.__name__ == "CodexConfig"
|
||||
|
||||
|
||||
def test_root_exports_turn_result() -> None:
|
||||
@@ -208,7 +208,7 @@ def test_package_and_default_client_versions_follow_project_version() -> None:
|
||||
pyproject = tomllib.loads(pyproject_path.read_text())
|
||||
|
||||
assert openai_codex.__version__ == pyproject["project"]["version"]
|
||||
assert AppServerConfig().client_version == openai_codex.__version__
|
||||
assert CodexConfig().client_version == openai_codex.__version__
|
||||
|
||||
|
||||
def test_package_includes_py_typed_marker() -> None:
|
||||
@@ -224,16 +224,16 @@ def test_package_root_exports_only_public_api() -> None:
|
||||
EXPECTED_ROOT_EXPORTS, True
|
||||
)
|
||||
assert {
|
||||
"AppServerClient": hasattr(openai_codex, "AppServerClient"),
|
||||
"AsyncAppServerClient": hasattr(openai_codex, "AsyncAppServerClient"),
|
||||
"CodexClient": hasattr(openai_codex, "CodexClient"),
|
||||
"AsyncCodexClient": hasattr(openai_codex, "AsyncCodexClient"),
|
||||
"InitializeResponse": hasattr(openai_codex, "InitializeResponse"),
|
||||
"ThreadStartParams": hasattr(openai_codex, "ThreadStartParams"),
|
||||
"TurnStartParams": hasattr(openai_codex, "TurnStartParams"),
|
||||
"TurnCompletedNotification": hasattr(openai_codex, "TurnCompletedNotification"),
|
||||
"TurnStatus": hasattr(openai_codex, "TurnStatus"),
|
||||
} == {
|
||||
"AppServerClient": False,
|
||||
"AsyncAppServerClient": False,
|
||||
"CodexClient": False,
|
||||
"AsyncCodexClient": False,
|
||||
"InitializeResponse": False,
|
||||
"ThreadStartParams": False,
|
||||
"TurnStartParams": False,
|
||||
@@ -252,7 +252,7 @@ def test_package_star_import_matches_public_api() -> None:
|
||||
|
||||
|
||||
def test_types_module_exports_curated_public_types() -> None:
|
||||
"""The public type module should be the supported place for app-server models."""
|
||||
"""The public type module should expose Codex protocol models."""
|
||||
assert public_types.__all__ == EXPECTED_TYPES_EXPORTS
|
||||
assert {name: hasattr(public_types, name) for name in EXPECTED_TYPES_EXPORTS} == dict.fromkeys(
|
||||
EXPECTED_TYPES_EXPORTS, True
|
||||
|
||||
Reference in New Issue
Block a user