Files
codex/sdk/python/src/openai_codex/_runtime_requirements.py
Ahmed Ibrahim 8afccec87a Expose Python SDK history selection and per-turn options (#44084)
## Why

Python callers need control over response history loading and a way to override the service tier for one turn. These options also need runtime compatibility checks to prevent older CLIs from silently ignoring them.

## What changed

- Add `include_turns` to sync and async thread resume/fork methods. Omission preserves server defaults; `False` skips response history loading without changing model context.
- Add `turn_service_tier` and `source` to sync and async `run()` and `turn()`, and generate both methods together to keep their options aligned.
- Require CLI `0.151.0` or newer when sending the new options, with lazy schema checks for unversioned local builds.
- Pin the bundled runtime dependency to `0.153.4` and reject unsupported runtime versions during SDK packaging.

## Testing

Add coverage for option forwarding, history flag omission and inversion, runtime version checks, cached schema probing, and packaging compatibility. Extend app-server and installed SDK smoke tests to exercise the new options.

GitOrigin-RevId: 4bcc9cff687b0651e67852e7c080df7fadac6d76
2026-09-09 06:40:56 +00:00

65 lines
2.4 KiB
Python

"""Runtime version and checkout-schema requirements for newer SDK options."""
import json
import re
import subprocess
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from tempfile import TemporaryDirectory
from packaging.version import InvalidVersion, Version
MINIMUM_RUNTIME_VERSION = "0.151.0"
def require_runtime_version(version: str | None) -> None:
"""Reject unknown or unsupported versions, including prereleases at the minimum."""
# CLI alpha hotfixes use 0.154.0-alpha.1.2; PEP 440 spells that a1.post2.
normalized = re.sub(r"-alpha\.(\d+)\.(\d+)$", r"a\1.post\2", version or "")
try:
if Version(normalized) >= Version(MINIMUM_RUNTIME_VERSION):
return
except InvalidVersion:
pass
raise ValueError(
f"Codex CLI {MINIMUM_RUNTIME_VERSION} or newer is required; "
f"reported version is {version or 'unknown'!r}"
)
@dataclass
class CheckoutCapabilities:
"""Lazily inspect the same executable and configuration as a running checkout."""
command: tuple[str, ...]
cwd: str | None
env: dict[str, str]
@cached_property
def fields(self) -> dict[str, frozenset[str]]:
try:
with TemporaryDirectory(prefix="codex-sdk-schema-") as directory:
subprocess.run(
[*self.command, "generate-json-schema", "--experimental", "--out", directory],
cwd=self.cwd,
env=self.env,
capture_output=True,
check=True,
timeout=30,
)
result = {}
for method, name in (
("turn/start", "TurnStartParams"),
("thread/resume", "ThreadResumeParams"),
("thread/fork", "ThreadForkParams"),
):
schema = json.loads((Path(directory) / "v2" / f"{name}.json").read_text())
properties = schema.get("properties") if isinstance(schema, dict) else None
if not isinstance(properties, dict):
raise ValueError(f"Missing properties in {name} schema")
result[method] = frozenset(properties)
return result
except (OSError, subprocess.SubprocessError, ValueError) as exc:
raise ValueError("Could not inspect the unversioned CLI's experimental schema") from exc