mirror of
https://github.com/openai/codex.git
synced 2026-04-25 07:05:38 +00:00
## Summary Foundation PR only (base for PR #3). This PR contains the SDK runtime foundation and generated artifacts: - pinned runtime binary in `sdk/python/bin/` (`codex` or `codex.exe` by platform) - single maintenance script: `sdk/python/scripts/update_sdk_artifacts.py` - generated protocol/types artifacts under: - `sdk/python/src/codex_app_server/generated/protocol_types.py` - `sdk/python/src/codex_app_server/generated/schema_types.py` - `sdk/python/src/codex_app_server/generated/v2_all/*` - generation-contract test wiring (`tests/test_contract_generation.py`) ## Release asset behavior `update_sdk_artifacts.py` now: - selects latest release by channel (`--channel stable|alpha`) - resolves the correct asset for current OS/arch - extracts platform binary (`codex` on macOS/Linux, `codex.exe` on Windows) - keeps runtime on single pinned binary source in `sdk/python/bin/` ## Scope boundary - ✅ PR #2 = binary + generation pipeline + generated types foundation - ❌ PR #2 does **not** include examples/integration logic polish (that is PR #3) ## Validation - Ran: `python scripts/update_sdk_artifacts.py --channel stable` - Regenerated and committed resulting generated artifacts - Local tests pass on branch
126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
class AppServerError(Exception):
|
|
"""Base exception for SDK errors."""
|
|
|
|
|
|
class JsonRpcError(AppServerError):
|
|
"""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(AppServerError):
|
|
"""Raised when the app-server transport closes unexpectedly."""
|
|
|
|
|
|
class AppServerRpcError(JsonRpcError):
|
|
"""Base typed error for JSON-RPC failures."""
|
|
|
|
|
|
class ParseError(AppServerRpcError):
|
|
pass
|
|
|
|
|
|
class InvalidRequestError(AppServerRpcError):
|
|
pass
|
|
|
|
|
|
class MethodNotFoundError(AppServerRpcError):
|
|
pass
|
|
|
|
|
|
class InvalidParamsError(AppServerRpcError):
|
|
pass
|
|
|
|
|
|
class InternalRpcError(AppServerRpcError):
|
|
pass
|
|
|
|
|
|
class ServerBusyError(AppServerRpcError):
|
|
"""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 AppServerRpcError(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
|