| from __future__ import annotations |
|
|
| from dataclasses import dataclass |
| from typing import Iterable |
|
|
|
|
| ALL_REASONING_EFFORTS = ("none", "minimal", "low", "medium", "high", "xhigh") |
| DEFAULT_REASONING_EFFORTS = frozenset(ALL_REASONING_EFFORTS) |
|
|
|
|
| @dataclass(frozen=True) |
| class ModelSpec: |
| public_id: str |
| upstream_id: str |
| aliases: tuple[str, ...] |
| allowed_efforts: frozenset[str] |
| variant_efforts: tuple[str, ...] |
| uses_codex_instructions: bool = False |
|
|
|
|
| _MODEL_SPECS = ( |
| ModelSpec( |
| public_id="gpt-5", |
| upstream_id="gpt-5", |
| aliases=("gpt5", "gpt-5-latest"), |
| allowed_efforts=DEFAULT_REASONING_EFFORTS, |
| variant_efforts=("high", "medium", "low", "minimal"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.1", |
| upstream_id="gpt-5.1", |
| aliases=(), |
| allowed_efforts=frozenset(("low", "medium", "high")), |
| variant_efforts=("high", "medium", "low"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.2", |
| upstream_id="gpt-5.2", |
| aliases=("gpt5.2", "gpt-5.2-latest"), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.4", |
| upstream_id="gpt-5.4", |
| aliases=("gpt5.4", "gpt-5.4-latest"), |
| allowed_efforts=frozenset(("none", "low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low", "none"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.5", |
| upstream_id="gpt-5.5", |
| aliases=("gpt5.5", "gpt-5.5-latest"), |
| allowed_efforts=frozenset(("none", "low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low", "none"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.4-mini", |
| upstream_id="gpt-5.4-mini", |
| aliases=("gpt5.4-mini", "gpt-5.4-mini-latest"), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| ), |
| ModelSpec( |
| public_id="gpt-5.3-codex", |
| upstream_id="gpt-5.3-codex", |
| aliases=("gpt5.3-codex", "gpt-5.3-codex-latest"), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5.3-codex-spark", |
| upstream_id="gpt-5.3-codex-spark", |
| aliases=("gpt5.3-codex-spark", "gpt-5.3-codex-spark-latest"), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5-codex", |
| upstream_id="gpt-5-codex", |
| aliases=("gpt5-codex", "gpt-5-codex-latest"), |
| allowed_efforts=DEFAULT_REASONING_EFFORTS, |
| variant_efforts=("high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5.2-codex", |
| upstream_id="gpt-5.2-codex", |
| aliases=("gpt5.2-codex", "gpt-5.2-codex-latest"), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5.1-codex", |
| upstream_id="gpt-5.1-codex", |
| aliases=(), |
| allowed_efforts=frozenset(("low", "medium", "high")), |
| variant_efforts=("high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5.1-codex-max", |
| upstream_id="gpt-5.1-codex-max", |
| aliases=(), |
| allowed_efforts=frozenset(("low", "medium", "high", "xhigh")), |
| variant_efforts=("xhigh", "high", "medium", "low"), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="gpt-5.1-codex-mini", |
| upstream_id="gpt-5.1-codex-mini", |
| aliases=(), |
| allowed_efforts=frozenset(("low", "medium", "high")), |
| variant_efforts=(), |
| uses_codex_instructions=True, |
| ), |
| ModelSpec( |
| public_id="codex-mini", |
| upstream_id="codex-mini-latest", |
| aliases=("codex", "codex-mini-latest"), |
| allowed_efforts=DEFAULT_REASONING_EFFORTS, |
| variant_efforts=(), |
| uses_codex_instructions=True, |
| ), |
| ) |
|
|
| _SPECS_BY_UPSTREAM = {spec.upstream_id: spec for spec in _MODEL_SPECS} |
| _ALIASES = {} |
| for _spec in _MODEL_SPECS: |
| _ALIASES[_spec.public_id] = _spec.upstream_id |
| for _alias in _spec.aliases: |
| _ALIASES[_alias] = _spec.upstream_id |
|
|
|
|
| def _strip_model_name(model: str | None) -> tuple[str, str | None]: |
| if not isinstance(model, str): |
| return "", None |
| value = model.strip().lower() |
| if not value: |
| return "", None |
| if ":" in value: |
| base, maybe_effort = value.rsplit(":", 1) |
| if maybe_effort in DEFAULT_REASONING_EFFORTS: |
| return base, maybe_effort |
| for separator in ("-", "_"): |
| for effort in ALL_REASONING_EFFORTS: |
| suffix = f"{separator}{effort}" |
| if value.endswith(suffix): |
| return value[: -len(suffix)], effort |
| return value, None |
|
|
|
|
| def model_spec_for_name(model: str | None) -> ModelSpec | None: |
| base, _ = _strip_model_name(model) |
| upstream_id = _ALIASES.get(base) |
| if not upstream_id: |
| return None |
| return _SPECS_BY_UPSTREAM.get(upstream_id) |
|
|
|
|
| def normalize_model_name(model: str | None, debug_model: str | None = None) -> str: |
| if isinstance(debug_model, str) and debug_model.strip(): |
| return debug_model.strip() |
| spec = model_spec_for_name(model) |
| if spec is not None: |
| return spec.upstream_id |
| base, _ = _strip_model_name(model) |
| return base or "gpt-5.4" |
|
|
|
|
| def uses_codex_instructions(model: str | None) -> bool: |
| spec = model_spec_for_name(model) |
| if spec is not None: |
| return spec.uses_codex_instructions |
| return "codex" in ((model or "").strip().lower()) |
|
|
|
|
| def allowed_efforts_for_model(model: str | None) -> frozenset[str]: |
| spec = model_spec_for_name(model) |
| if spec is not None: |
| return spec.allowed_efforts |
| return DEFAULT_REASONING_EFFORTS |
|
|
|
|
| def extract_reasoning_from_model_name(model: str | None) -> dict[str, str] | None: |
| _, effort = _strip_model_name(model) |
| if not effort: |
| return None |
| return {"effort": effort} |
|
|
|
|
| def list_public_models(expose_reasoning_models: bool = False) -> list[str]: |
| model_ids: list[str] = [] |
| for spec in _MODEL_SPECS: |
| model_ids.append(spec.public_id) |
| if expose_reasoning_models: |
| model_ids.extend(f"{spec.public_id}-{effort}" for effort in spec.variant_efforts) |
| return model_ids |
|
|
|
|
| def iter_public_models() -> Iterable[ModelSpec]: |
| return _MODEL_SPECS |
|
|