mirror of
https://github.com/openai/codex.git
synced 2026-05-10 08:11:19 +03:00
Compare commits
5 Commits
codex/pyth
...
codex/pyth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9306e60848 | ||
|
|
8d7a5c27c1 | ||
|
|
692c08faf9 | ||
|
|
8b8e868140 | ||
|
|
2654cc299e |
33
.github/workflows/sdk.yml
vendored
33
.github/workflows/sdk.yml
vendored
@@ -6,6 +6,39 @@ on:
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
python-sdk:
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
labels: codex-linux-x64
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Test Python SDK
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Run inside Alpine so dependency resolution exercises the pinned
|
||||
# runtime wheel on the same Linux wheel family that CI installs.
|
||||
docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-e HOME=/tmp/codex-python-sdk-home \
|
||||
-e UV_LINK_MODE=copy \
|
||||
-v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
|
||||
-w "${GITHUB_WORKSPACE}/sdk/python" \
|
||||
python:3.12-alpine \
|
||||
sh -euxc '
|
||||
python -m venv /tmp/uv
|
||||
/tmp/uv/bin/python -m pip install uv==0.11.3
|
||||
/tmp/uv/bin/uv sync --extra dev --frozen
|
||||
/tmp/uv/bin/uv run --extra dev pytest
|
||||
'
|
||||
|
||||
sdks:
|
||||
runs-on:
|
||||
group: codex-runners
|
||||
|
||||
@@ -5,6 +5,8 @@ Experimental Python SDK for `codex app-server` JSON-RPC v2 over stdio, with a sm
|
||||
The generated wire-model layer is sourced from the pinned `openai-codex-cli-bin`
|
||||
runtime package and exposed as Pydantic models with snake_case Python fields
|
||||
that serialize back to the app-server’s camelCase wire format.
|
||||
The package root exports the ergonomic client API; public app-server value and
|
||||
event types live in `codex_app_server.types`.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -110,4 +112,4 @@ This supports the CI release flow:
|
||||
- Use context managers (`with Codex() as codex:`) to ensure shutdown.
|
||||
- Prefer `thread.run("...")` for the common case. Use `thread.turn(...)` when
|
||||
you need streaming, steering, or interrupt control.
|
||||
- For transient overload, use `codex_app_server.retry.retry_on_overload`.
|
||||
- For transient overload, use `retry_on_overload` from the package root.
|
||||
|
||||
@@ -15,7 +15,6 @@ from codex_app_server import (
|
||||
AsyncThread,
|
||||
TurnHandle,
|
||||
AsyncTurnHandle,
|
||||
InitializeResponse,
|
||||
Input,
|
||||
InputItem,
|
||||
TextInput,
|
||||
@@ -23,14 +22,18 @@ from codex_app_server import (
|
||||
LocalImageInput,
|
||||
SkillInput,
|
||||
MentionInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
InitializeResponse,
|
||||
ThreadItem,
|
||||
ThreadTokenUsage,
|
||||
TurnStatus,
|
||||
)
|
||||
from codex_app_server.generated.v2_all import ThreadItem, ThreadTokenUsage
|
||||
```
|
||||
|
||||
- Version: `codex_app_server.__version__`
|
||||
- Requires Python >= 3.10
|
||||
- Canonical generated app-server models live in `codex_app_server.generated.v2_all`
|
||||
- Public app-server value and event types live in `codex_app_server.types`
|
||||
|
||||
## Codex (sync)
|
||||
|
||||
@@ -124,7 +127,7 @@ object with:
|
||||
phase-less assistant message item.
|
||||
|
||||
Use `turn(...)` when you need low-level turn control (`stream()`, `steer()`,
|
||||
`interrupt()`) or the canonical generated `Turn` from `TurnHandle.run()`.
|
||||
`interrupt()`) or the public `Turn` model from `TurnHandle.run()`.
|
||||
|
||||
## TurnHandle / AsyncTurnHandle
|
||||
|
||||
@@ -133,7 +136,7 @@ Use `turn(...)` when you need low-level turn control (`stream()`, `steer()`,
|
||||
- `steer(input: Input) -> TurnSteerResponse`
|
||||
- `interrupt() -> TurnInterruptResponse`
|
||||
- `stream() -> Iterator[Notification]`
|
||||
- `run() -> codex_app_server.generated.v2_all.Turn`
|
||||
- `run() -> codex_app_server.types.Turn`
|
||||
|
||||
Behavior notes:
|
||||
|
||||
@@ -145,7 +148,7 @@ Behavior notes:
|
||||
- `steer(input: Input) -> Awaitable[TurnSteerResponse]`
|
||||
- `interrupt() -> Awaitable[TurnInterruptResponse]`
|
||||
- `stream() -> AsyncIterator[Notification]`
|
||||
- `run() -> Awaitable[codex_app_server.generated.v2_all.Turn]`
|
||||
- `run() -> Awaitable[codex_app_server.types.Turn]`
|
||||
|
||||
Behavior notes:
|
||||
|
||||
@@ -165,16 +168,15 @@ InputItem = TextInput | ImageInput | LocalImageInput | SkillInput | MentionInput
|
||||
Input = list[InputItem] | InputItem
|
||||
```
|
||||
|
||||
## Generated Models
|
||||
## Public Types
|
||||
|
||||
The SDK wrappers return and accept canonical generated app-server models wherever possible:
|
||||
The SDK wrappers return and accept public app-server models wherever possible:
|
||||
|
||||
```python
|
||||
from codex_app_server.generated.v2_all import (
|
||||
from codex_app_server.types import (
|
||||
AskForApproval,
|
||||
ThreadReadResponse,
|
||||
Turn,
|
||||
TurnStartParams,
|
||||
TurnStatus,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## `run()` vs `stream()`
|
||||
|
||||
- `TurnHandle.run()` / `AsyncTurnHandle.run()` is the easiest path. It consumes events until completion and returns the canonical generated app-server `Turn` model.
|
||||
- `TurnHandle.run()` / `AsyncTurnHandle.run()` is the easiest path. It consumes events until completion and returns the public app-server `Turn` model from `codex_app_server.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.
|
||||
@@ -99,5 +99,5 @@ Do not blindly retry all errors. For `InvalidParamsError` or `MethodNotFoundErro
|
||||
|
||||
- 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 generated `Turn` model.
|
||||
- Assuming `run()` returns extra SDK-only fields instead of the public `Turn` model.
|
||||
- Mixing SDK input classes with raw dicts incorrectly.
|
||||
|
||||
@@ -95,12 +95,13 @@ with Codex() as codex:
|
||||
print(result.final_response)
|
||||
```
|
||||
|
||||
## 6) Generated models
|
||||
## 6) Public app-server types
|
||||
|
||||
The convenience wrappers live at the package root, but the canonical app-server models live under:
|
||||
The convenience wrappers live at the package root. Public app-server value and
|
||||
event types live under:
|
||||
|
||||
```python
|
||||
from codex_app_server.generated.v2_all import Turn, TurnStatus, ThreadReadResponse
|
||||
from codex_app_server.types import ThreadReadResponse, Turn, TurnStatus
|
||||
```
|
||||
|
||||
## 7) Next stops
|
||||
|
||||
@@ -24,9 +24,9 @@ from codex_app_server import (
|
||||
JsonRpcError,
|
||||
ServerBusyError,
|
||||
TextInput,
|
||||
TurnStatus,
|
||||
is_retryable_error,
|
||||
)
|
||||
from codex_app_server.types import TurnStatus
|
||||
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ from codex_app_server import (
|
||||
JsonRpcError,
|
||||
ServerBusyError,
|
||||
TextInput,
|
||||
TurnStatus,
|
||||
retry_on_overload,
|
||||
)
|
||||
from codex_app_server.types import TurnStatus
|
||||
|
||||
with Codex(config=runtime_config()) as codex:
|
||||
thread = codex.thread_start(model="gpt-5.4", config={"model_reasoning_effort": "high"})
|
||||
|
||||
@@ -14,6 +14,8 @@ import asyncio
|
||||
from codex_app_server import (
|
||||
AsyncCodex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
ThreadTokenUsageUpdatedNotification,
|
||||
TurnCompletedNotification,
|
||||
)
|
||||
|
||||
@@ -12,6 +12,8 @@ ensure_local_sdk_src()
|
||||
from codex_app_server import (
|
||||
Codex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
ThreadTokenUsageUpdatedNotification,
|
||||
TurnCompletedNotification,
|
||||
)
|
||||
|
||||
@@ -18,11 +18,13 @@ ensure_local_sdk_src()
|
||||
import asyncio
|
||||
|
||||
from codex_app_server import (
|
||||
AskForApproval,
|
||||
AsyncCodex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningSummary,
|
||||
TextInput,
|
||||
)
|
||||
|
||||
OUTPUT_SCHEMA = {
|
||||
|
||||
@@ -16,11 +16,13 @@ from _bootstrap import (
|
||||
ensure_local_sdk_src()
|
||||
|
||||
from codex_app_server import (
|
||||
AskForApproval,
|
||||
Codex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningSummary,
|
||||
TextInput,
|
||||
)
|
||||
|
||||
OUTPUT_SCHEMA = {
|
||||
|
||||
@@ -12,13 +12,15 @@ ensure_local_sdk_src()
|
||||
import asyncio
|
||||
|
||||
from codex_app_server import (
|
||||
AskForApproval,
|
||||
AsyncCodex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
SandboxPolicy,
|
||||
TextInput,
|
||||
)
|
||||
|
||||
REASONING_RANK = {
|
||||
|
||||
@@ -10,13 +10,15 @@ from _bootstrap import assistant_text_from_turn, ensure_local_sdk_src, find_turn
|
||||
ensure_local_sdk_src()
|
||||
|
||||
from codex_app_server import (
|
||||
AskForApproval,
|
||||
Codex,
|
||||
TextInput,
|
||||
)
|
||||
from codex_app_server.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
SandboxPolicy,
|
||||
TextInput,
|
||||
)
|
||||
|
||||
REASONING_RANK = {
|
||||
|
||||
@@ -5,7 +5,8 @@ Each example folder contains runnable versions:
|
||||
- `sync.py` (public sync surface: `Codex`)
|
||||
- `async.py` (public async surface: `AsyncCodex`)
|
||||
|
||||
All examples intentionally use only public SDK exports from `codex_app_server`.
|
||||
All examples intentionally use only public SDK exports from `codex_app_server`
|
||||
and `codex_app_server.types`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from .async_client import AsyncAppServerClient
|
||||
from .client import AppServerClient, AppServerConfig
|
||||
from .client import AppServerConfig
|
||||
from .errors import (
|
||||
AppServerError,
|
||||
AppServerRpcError,
|
||||
@@ -14,29 +13,6 @@ from .errors import (
|
||||
TransportClosedError,
|
||||
is_retryable_error,
|
||||
)
|
||||
from .generated.v2_all import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
PlanType,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
SandboxMode,
|
||||
SandboxPolicy,
|
||||
ThreadItem,
|
||||
ThreadForkParams,
|
||||
ThreadListParams,
|
||||
ThreadResumeParams,
|
||||
ThreadSortKey,
|
||||
ThreadSource,
|
||||
ThreadSourceKind,
|
||||
ThreadStartParams,
|
||||
ThreadTokenUsageUpdatedNotification,
|
||||
TurnCompletedNotification,
|
||||
TurnStartParams,
|
||||
TurnStatus,
|
||||
TurnSteerParams,
|
||||
)
|
||||
from .models import InitializeResponse
|
||||
from .api import (
|
||||
AsyncCodex,
|
||||
AsyncThread,
|
||||
@@ -58,8 +34,6 @@ from ._version import __version__
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
"AppServerClient",
|
||||
"AsyncAppServerClient",
|
||||
"AppServerConfig",
|
||||
"Codex",
|
||||
"AsyncCodex",
|
||||
@@ -67,7 +41,6 @@ __all__ = [
|
||||
"AsyncThread",
|
||||
"TurnHandle",
|
||||
"AsyncTurnHandle",
|
||||
"InitializeResponse",
|
||||
"RunResult",
|
||||
"Input",
|
||||
"InputItem",
|
||||
@@ -76,26 +49,6 @@ __all__ = [
|
||||
"LocalImageInput",
|
||||
"SkillInput",
|
||||
"MentionInput",
|
||||
"ThreadItem",
|
||||
"ThreadTokenUsageUpdatedNotification",
|
||||
"TurnCompletedNotification",
|
||||
"AskForApproval",
|
||||
"Personality",
|
||||
"PlanType",
|
||||
"ReasoningEffort",
|
||||
"ReasoningSummary",
|
||||
"SandboxMode",
|
||||
"SandboxPolicy",
|
||||
"ThreadStartParams",
|
||||
"ThreadResumeParams",
|
||||
"ThreadListParams",
|
||||
"ThreadSortKey",
|
||||
"ThreadSource",
|
||||
"ThreadSourceKind",
|
||||
"ThreadForkParams",
|
||||
"TurnStatus",
|
||||
"TurnStartParams",
|
||||
"TurnSteerParams",
|
||||
"retry_on_overload",
|
||||
"AppServerError",
|
||||
"TransportClosedError",
|
||||
|
||||
69
sdk/python/src/codex_app_server/types.py
Normal file
69
sdk/python/src/codex_app_server/types.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""Public generated app-server model exports for type annotations and matching."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .generated.v2_all import (
|
||||
ApprovalsReviewer,
|
||||
AskForApproval,
|
||||
ModelListResponse,
|
||||
Personality,
|
||||
PlanType,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
SandboxMode,
|
||||
SandboxPolicy,
|
||||
SortDirection,
|
||||
ThreadArchiveResponse,
|
||||
ThreadCompactStartResponse,
|
||||
ThreadItem,
|
||||
ThreadListCwdFilter,
|
||||
ThreadListResponse,
|
||||
ThreadReadResponse,
|
||||
ThreadSetNameResponse,
|
||||
ThreadSortKey,
|
||||
ThreadSource,
|
||||
ThreadSourceKind,
|
||||
ThreadStartSource,
|
||||
ThreadTokenUsage,
|
||||
ThreadTokenUsageUpdatedNotification,
|
||||
Turn,
|
||||
TurnCompletedNotification,
|
||||
TurnInterruptResponse,
|
||||
TurnStatus,
|
||||
TurnSteerResponse,
|
||||
)
|
||||
from .models import InitializeResponse, JsonObject, Notification
|
||||
|
||||
__all__ = [
|
||||
"ApprovalsReviewer",
|
||||
"AskForApproval",
|
||||
"InitializeResponse",
|
||||
"JsonObject",
|
||||
"ModelListResponse",
|
||||
"Notification",
|
||||
"Personality",
|
||||
"PlanType",
|
||||
"ReasoningEffort",
|
||||
"ReasoningSummary",
|
||||
"SandboxMode",
|
||||
"SandboxPolicy",
|
||||
"SortDirection",
|
||||
"ThreadArchiveResponse",
|
||||
"ThreadCompactStartResponse",
|
||||
"ThreadItem",
|
||||
"ThreadListCwdFilter",
|
||||
"ThreadListResponse",
|
||||
"ThreadReadResponse",
|
||||
"ThreadSetNameResponse",
|
||||
"ThreadSortKey",
|
||||
"ThreadSource",
|
||||
"ThreadSourceKind",
|
||||
"ThreadStartSource",
|
||||
"ThreadTokenUsage",
|
||||
"ThreadTokenUsageUpdatedNotification",
|
||||
"Turn",
|
||||
"TurnCompletedNotification",
|
||||
"TurnInterruptResponse",
|
||||
"TurnStatus",
|
||||
"TurnSteerResponse",
|
||||
]
|
||||
@@ -7,12 +7,86 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import codex_app_server
|
||||
from codex_app_server import AppServerConfig, RunResult
|
||||
from codex_app_server.models import InitializeResponse
|
||||
from codex_app_server.api import AsyncCodex, AsyncThread, Codex, Thread
|
||||
import codex_app_server.types as public_types
|
||||
from codex_app_server import (
|
||||
AppServerConfig,
|
||||
AsyncCodex,
|
||||
AsyncThread,
|
||||
Codex,
|
||||
RunResult,
|
||||
Thread,
|
||||
)
|
||||
from codex_app_server.types import InitializeResponse
|
||||
|
||||
EXPECTED_ROOT_EXPORTS = [
|
||||
"__version__",
|
||||
"AppServerConfig",
|
||||
"Codex",
|
||||
"AsyncCodex",
|
||||
"Thread",
|
||||
"AsyncThread",
|
||||
"TurnHandle",
|
||||
"AsyncTurnHandle",
|
||||
"RunResult",
|
||||
"Input",
|
||||
"InputItem",
|
||||
"TextInput",
|
||||
"ImageInput",
|
||||
"LocalImageInput",
|
||||
"SkillInput",
|
||||
"MentionInput",
|
||||
"retry_on_overload",
|
||||
"AppServerError",
|
||||
"TransportClosedError",
|
||||
"JsonRpcError",
|
||||
"AppServerRpcError",
|
||||
"ParseError",
|
||||
"InvalidRequestError",
|
||||
"MethodNotFoundError",
|
||||
"InvalidParamsError",
|
||||
"InternalRpcError",
|
||||
"ServerBusyError",
|
||||
"RetryLimitExceededError",
|
||||
"is_retryable_error",
|
||||
]
|
||||
|
||||
EXPECTED_TYPES_EXPORTS = [
|
||||
"ApprovalsReviewer",
|
||||
"AskForApproval",
|
||||
"InitializeResponse",
|
||||
"JsonObject",
|
||||
"ModelListResponse",
|
||||
"Notification",
|
||||
"Personality",
|
||||
"PlanType",
|
||||
"ReasoningEffort",
|
||||
"ReasoningSummary",
|
||||
"SandboxMode",
|
||||
"SandboxPolicy",
|
||||
"SortDirection",
|
||||
"ThreadArchiveResponse",
|
||||
"ThreadCompactStartResponse",
|
||||
"ThreadItem",
|
||||
"ThreadListCwdFilter",
|
||||
"ThreadListResponse",
|
||||
"ThreadReadResponse",
|
||||
"ThreadSetNameResponse",
|
||||
"ThreadSortKey",
|
||||
"ThreadSource",
|
||||
"ThreadSourceKind",
|
||||
"ThreadStartSource",
|
||||
"ThreadTokenUsage",
|
||||
"ThreadTokenUsageUpdatedNotification",
|
||||
"Turn",
|
||||
"TurnCompletedNotification",
|
||||
"TurnInterruptResponse",
|
||||
"TurnStatus",
|
||||
"TurnSteerResponse",
|
||||
]
|
||||
|
||||
|
||||
def _keyword_only_names(fn: object) -> list[str]:
|
||||
"""Return only user-facing keyword-only parameter names for a public method."""
|
||||
signature = inspect.signature(fn)
|
||||
return [
|
||||
param.name
|
||||
@@ -22,6 +96,7 @@ def _keyword_only_names(fn: object) -> list[str]:
|
||||
|
||||
|
||||
def _assert_no_any_annotations(fn: object) -> None:
|
||||
"""Reject loose annotations on public wrapper methods."""
|
||||
signature = inspect.signature(fn)
|
||||
for param in signature.parameters.values():
|
||||
if param.annotation is Any:
|
||||
@@ -33,14 +108,17 @@ def _assert_no_any_annotations(fn: object) -> None:
|
||||
|
||||
|
||||
def test_root_exports_app_server_config() -> None:
|
||||
"""The root package should expose the process configuration object."""
|
||||
assert AppServerConfig.__name__ == "AppServerConfig"
|
||||
|
||||
|
||||
def test_root_exports_run_result() -> None:
|
||||
"""The root package should expose the common-case run result wrapper."""
|
||||
assert RunResult.__name__ == "RunResult"
|
||||
|
||||
|
||||
def test_package_and_default_client_versions_follow_project_version() -> None:
|
||||
"""The importable package version should stay aligned with pyproject metadata."""
|
||||
pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
||||
pyproject = tomllib.loads(pyproject_path.read_text())
|
||||
|
||||
@@ -49,10 +127,85 @@ def test_package_and_default_client_versions_follow_project_version() -> None:
|
||||
|
||||
|
||||
def test_package_includes_py_typed_marker() -> None:
|
||||
"""The wheel should advertise that inline type information is available."""
|
||||
marker = resources.files("codex_app_server").joinpath("py.typed")
|
||||
assert marker.is_file()
|
||||
|
||||
|
||||
def test_package_root_exports_only_public_api() -> None:
|
||||
"""The package root should expose the supported SDK surface, not internals."""
|
||||
assert codex_app_server.__all__ == EXPECTED_ROOT_EXPORTS
|
||||
assert {
|
||||
name: hasattr(codex_app_server, name) for name in EXPECTED_ROOT_EXPORTS
|
||||
} == {name: True for name in EXPECTED_ROOT_EXPORTS}
|
||||
assert {
|
||||
"AppServerClient": hasattr(codex_app_server, "AppServerClient"),
|
||||
"AsyncAppServerClient": hasattr(codex_app_server, "AsyncAppServerClient"),
|
||||
"InitializeResponse": hasattr(codex_app_server, "InitializeResponse"),
|
||||
"ThreadStartParams": hasattr(codex_app_server, "ThreadStartParams"),
|
||||
"TurnStartParams": hasattr(codex_app_server, "TurnStartParams"),
|
||||
"TurnCompletedNotification": hasattr(
|
||||
codex_app_server, "TurnCompletedNotification"
|
||||
),
|
||||
"TurnStatus": hasattr(codex_app_server, "TurnStatus"),
|
||||
} == {
|
||||
"AppServerClient": False,
|
||||
"AsyncAppServerClient": False,
|
||||
"InitializeResponse": False,
|
||||
"ThreadStartParams": False,
|
||||
"TurnStartParams": False,
|
||||
"TurnCompletedNotification": False,
|
||||
"TurnStatus": False,
|
||||
}
|
||||
|
||||
|
||||
def test_package_star_import_matches_public_api() -> None:
|
||||
"""Star imports should follow the same explicit public API list."""
|
||||
namespace: dict[str, object] = {}
|
||||
exec("from codex_app_server import *", namespace)
|
||||
|
||||
exported = set(namespace) - {"__builtins__"}
|
||||
assert exported == set(EXPECTED_ROOT_EXPORTS)
|
||||
|
||||
|
||||
def test_types_module_exports_curated_public_types() -> None:
|
||||
"""The public type module should be the supported place for app-server models."""
|
||||
assert public_types.__all__ == EXPECTED_TYPES_EXPORTS
|
||||
assert {name: hasattr(public_types, name) for name in EXPECTED_TYPES_EXPORTS} == {
|
||||
name: True for name in EXPECTED_TYPES_EXPORTS
|
||||
}
|
||||
|
||||
|
||||
def test_types_star_import_matches_public_types() -> None:
|
||||
"""Star imports from the type module should match its explicit export list."""
|
||||
namespace: dict[str, object] = {}
|
||||
exec("from codex_app_server.types import *", namespace)
|
||||
|
||||
exported = set(namespace) - {"__builtins__"}
|
||||
assert exported == set(EXPECTED_TYPES_EXPORTS)
|
||||
|
||||
|
||||
def test_examples_use_public_import_surfaces() -> None:
|
||||
"""Examples should teach users the public root and type-module imports only."""
|
||||
examples_root = Path(__file__).resolve().parents[1] / "examples"
|
||||
private_import_markers = [
|
||||
"codex_app_server.api",
|
||||
"codex_app_server.client",
|
||||
"codex_app_server.generated",
|
||||
"codex_app_server.models",
|
||||
"codex_app_server.retry",
|
||||
]
|
||||
|
||||
offenders = {
|
||||
str(path.relative_to(examples_root)): marker
|
||||
for path in examples_root.rglob("*.py")
|
||||
for marker in private_import_markers
|
||||
if marker in path.read_text()
|
||||
}
|
||||
|
||||
assert offenders == {}
|
||||
|
||||
|
||||
def test_generated_public_signatures_are_snake_case_and_typed() -> None:
|
||||
"""Generated convenience methods should expose typed Pythonic keyword names."""
|
||||
expected = {
|
||||
@@ -228,6 +381,7 @@ def test_generated_public_signatures_are_snake_case_and_typed() -> None:
|
||||
|
||||
|
||||
def test_lifecycle_methods_are_codex_scoped() -> None:
|
||||
"""Lifecycle operations should hang off the client rather than thread objects."""
|
||||
assert hasattr(Codex, "thread_resume")
|
||||
assert hasattr(Codex, "thread_fork")
|
||||
assert hasattr(Codex, "thread_archive")
|
||||
@@ -258,6 +412,7 @@ def test_lifecycle_methods_are_codex_scoped() -> None:
|
||||
|
||||
|
||||
def test_initialize_metadata_parses_user_agent_shape() -> None:
|
||||
"""Initialize metadata should accept the legacy user-agent-only payload shape."""
|
||||
payload = InitializeResponse.model_validate({"userAgent": "codex-cli/1.2.3"})
|
||||
parsed = Codex._validate_initialize(payload)
|
||||
assert parsed is payload
|
||||
@@ -268,6 +423,7 @@ def test_initialize_metadata_parses_user_agent_shape() -> None:
|
||||
|
||||
|
||||
def test_initialize_metadata_requires_non_empty_information() -> None:
|
||||
"""Initialize metadata should fail when the runtime gives no identity signal."""
|
||||
try:
|
||||
Codex._validate_initialize(InitializeResponse.model_validate({}))
|
||||
except RuntimeError as exc:
|
||||
|
||||
Reference in New Issue
Block a user