218 lines
7.1 KiB
Python
218 lines
7.1 KiB
Python
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
|
|
preset_effort: str | None = None
|
|
|
|
|
|
_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.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.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.5-pro",
|
|
upstream_id="gpt-5.5",
|
|
aliases=("gpt5.5-pro", "gpt-5.5-pro-latest"),
|
|
allowed_efforts=frozenset(("none", "low", "medium", "high", "xhigh")),
|
|
variant_efforts=(),
|
|
preset_effort="xhigh",
|
|
),
|
|
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_PUBLIC_ID = {spec.public_id: spec for spec in _MODEL_SPECS}
|
|
_ALIASES = {}
|
|
for _spec in _MODEL_SPECS:
|
|
_ALIASES[_spec.public_id] = _spec.public_id
|
|
for _alias in _spec.aliases:
|
|
_ALIASES[_alias] = _spec.public_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)
|
|
public_id = _ALIASES.get(base)
|
|
if not public_id:
|
|
return None
|
|
return _SPECS_BY_PUBLIC_ID.get(public_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:
|
|
spec = model_spec_for_name(model)
|
|
effort = spec.preset_effort if spec is not None else None
|
|
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
|