mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
eb1cc3824c
## Why The initial public `openai-codex` beta should read and install like a normal published Python package before a release tag is created. This follows merged PR #24828, which establishes the independent SDK beta release plumbing and exact runtime dependency. ## What changed - Rewrote `sdk/python/README.md` as a compact PyPI-facing beta package page: published installation, one quickstart, short login examples, built-in help, and links to deeper guides. - Updated the getting-started guide, API reference, FAQ, and examples index to present the published beta consistently without repeating onboarding in the package landing page or reference page. - Made `pip install openai-codex` the primary install path while beta releases are the only published SDK releases, with `--pre` documented for opting into prereleases after a stable release exists. - Added curated `help()` / `pydoc` docstrings across the public API and generated public convenience methods through `scripts/update_sdk_artifacts.py`. - Declared the repository `Apache-2.0` license expression and Documentation URL in package metadata, without introducing a duplicated SDK-local license file. - Kept the source distribution focused on installable package material (`src/openai_codex`, `README.md`, and `pyproject.toml`); the repository docs and runnable examples remain linked from the PyPI README. - Built release artifacts in an Alpine container on the Ubuntu runner, matching Python SDK CI and allowing type generation to install the published `musllinux` runtime wheel. - Added `twine check --strict` to the release workflow so malformed PyPI metadata or rendered README content fails before publishing. - Added focused SDK assertions for beta metadata, the exact runtime pin, source distribution contents, and the built-in Python documentation surface. ## Validation - Ran `uv run --frozen --extra dev ruff check scripts/update_sdk_artifacts.py src/openai_codex tests/test_public_api_signatures.py tests/test_artifact_workflow_and_binaries.py` before the final README-only reductions and review-fix follow-ups. - Built `openai_codex-0.1.0b1-py3-none-any.whl` and `openai_codex-0.1.0b1.tar.gz` before the final README-only reductions and review-fix follow-ups. - Ran `python -m twine check --strict` on both built artifacts before the final README-only reductions and review-fix follow-ups. - Verified artifact metadata reports `Apache-2.0` without a duplicated SDK-local license file. - Verified `inspect.getdoc(...)` resolves documentation for the package, `Codex`, `CodexConfig`, and key generated thread methods. - Rebased the documentation/readiness change onto merged PR #24828 without changing the intended SDK or workflow file contents. - Final verification is delegated to online CI for this PR.
122 lines
3.6 KiB
Python
122 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
class CodexError(Exception):
|
|
"""Base exception for SDK errors."""
|
|
|
|
|
|
class JsonRpcError(CodexError):
|
|
"""Raw JSON-RPC error wrapper from the server."""
|
|
|
|
def __init__(self, code: int, message: str, data: Any = None):
|
|
super().__init__(f"JSON-RPC error {code}: {message}")
|
|
self.code = code
|
|
self.message = message
|
|
self.data = data
|
|
|
|
|
|
class TransportClosedError(CodexError):
|
|
"""Raised when the Codex transport closes unexpectedly."""
|
|
|
|
|
|
class CodexRpcError(JsonRpcError):
|
|
"""Base typed error for JSON-RPC failures."""
|
|
|
|
|
|
class ParseError(CodexRpcError):
|
|
"""Raised when a request or response cannot be parsed."""
|
|
|
|
|
|
class InvalidRequestError(CodexRpcError):
|
|
"""Raised when the runtime rejects the request shape."""
|
|
|
|
|
|
class MethodNotFoundError(CodexRpcError):
|
|
"""Raised when the requested operation is unavailable."""
|
|
|
|
|
|
class InvalidParamsError(CodexRpcError):
|
|
"""Raised when an operation receives invalid parameters."""
|
|
|
|
|
|
class InternalRpcError(CodexRpcError):
|
|
"""Raised when the runtime reports an internal RPC failure."""
|
|
|
|
|
|
class ServerBusyError(CodexRpcError):
|
|
"""Server is overloaded / unavailable and caller should retry."""
|
|
|
|
|
|
class RetryLimitExceededError(ServerBusyError):
|
|
"""Server exhausted internal retry budget for a retryable operation."""
|
|
|
|
|
|
def _contains_retry_limit_text(message: str) -> bool:
|
|
lowered = message.lower()
|
|
return "retry limit" in lowered or "too many failed attempts" in lowered
|
|
|
|
|
|
def _is_server_overloaded(data: Any) -> bool:
|
|
if data is None:
|
|
return False
|
|
|
|
if isinstance(data, str):
|
|
return data.lower() == "server_overloaded"
|
|
|
|
if isinstance(data, dict):
|
|
direct = data.get("codex_error_info") or data.get("codexErrorInfo") or data.get("errorInfo")
|
|
if isinstance(direct, str) and direct.lower() == "server_overloaded":
|
|
return True
|
|
if isinstance(direct, dict):
|
|
for value in direct.values():
|
|
if isinstance(value, str) and value.lower() == "server_overloaded":
|
|
return True
|
|
for value in data.values():
|
|
if _is_server_overloaded(value):
|
|
return True
|
|
|
|
if isinstance(data, list):
|
|
return any(_is_server_overloaded(value) for value in data)
|
|
|
|
return False
|
|
|
|
|
|
def map_jsonrpc_error(code: int, message: str, data: Any = None) -> JsonRpcError:
|
|
"""Map a raw JSON-RPC error into a richer SDK exception class."""
|
|
|
|
if code == -32700:
|
|
return ParseError(code, message, data)
|
|
if code == -32600:
|
|
return InvalidRequestError(code, message, data)
|
|
if code == -32601:
|
|
return MethodNotFoundError(code, message, data)
|
|
if code == -32602:
|
|
return InvalidParamsError(code, message, data)
|
|
if code == -32603:
|
|
return InternalRpcError(code, message, data)
|
|
|
|
if -32099 <= code <= -32000:
|
|
if _is_server_overloaded(data):
|
|
if _contains_retry_limit_text(message):
|
|
return RetryLimitExceededError(code, message, data)
|
|
return ServerBusyError(code, message, data)
|
|
if _contains_retry_limit_text(message):
|
|
return RetryLimitExceededError(code, message, data)
|
|
return CodexRpcError(code, message, data)
|
|
|
|
return JsonRpcError(code, message, data)
|
|
|
|
|
|
def is_retryable_error(exc: BaseException) -> bool:
|
|
"""True if the exception is a transient overload-style error."""
|
|
|
|
if isinstance(exc, ServerBusyError):
|
|
return True
|
|
|
|
if isinstance(exc, JsonRpcError):
|
|
return _is_server_overloaded(exc.data)
|
|
|
|
return False
|