fix(proxy): give an older Codex a translated entry for models past its version

Codex's own entries name the oldest release that can read them in minimal_client_version. A release older than that can fail to decode the entry, such as a reasoning effort it has no name for, which fails the whole catalog, so it now gets the translated entry, whose levels are trimmed to that release
This commit is contained in:
ryan-crabbe-berri 2026-11-05 17:54:17 -08:00
parent 95264b4e6d
commit fa8558d8ca
2 changed files with 62 additions and 1 deletions

View file

@ -32,6 +32,7 @@ from pathlib import Path
from types import MappingProxyType
from typing import TYPE_CHECKING, Final, Literal, TypeAlias
from packaging.version import Version
from pydantic import ConfigDict, TypeAdapter, ValidationError
from litellm._logging import verbose_proxy_logger
@ -109,6 +110,7 @@ class CodexStockModel(LiteLLMBaseModel):
supported_reasoning_levels: tuple[CodexReasoningLevel, ...] = ()
context_window: int | None = None
max_context_window: int | None = None
minimal_client_version: str | tuple[int, ...] | None = None
class CodexStockCatalog(LiteLLMBaseModel):
@ -278,6 +280,19 @@ def _stock_for(row: CodexCatalogRow, stock: Mapping[str, CodexStockModel]) -> Co
return next((stock[name] for name in names if name in stock), None)
def _readable_by(stock_model: CodexStockModel, client_version: str | None) -> bool:
"""Whether Codex's own server would hand this entry to the requesting release: one at or past the entry's
`minimal_client_version`. An older release can fail to decode it, and one entry it cannot decode (a reasoning
effort it has no name for) fails the whole catalog, so it gets a translated entry. No version keeps the entry."""
minimum: Final = stock_model.minimal_client_version
if minimum is None or client_version is None:
return True
try:
return Version(client_version) >= Version(minimum if isinstance(minimum, str) else ".".join(map(str, minimum)))
except ValueError:
return False
def _tier_fields(stock_model: CodexStockModel, tiers: tuple[CodexServiceTier, ...]) -> Mapping[str, object]:
"""The tier fields of an entry offering `tiers`. Codex turns `/fast` on off a speed tier as much as off a
service tier, so a speed tier stays only while a service tier billed the same way is still offered, and the
@ -370,7 +385,8 @@ def _entry(
alias_defaults: CodexAliasDefaults,
client_version: str | None,
) -> CodexCatalogEntry:
stock_model: Final = _stock_for(row, stock)
matched: Final = _stock_for(row, stock)
stock_model: Final = matched if matched is not None and _readable_by(matched, client_version) else None
stock_tiers: Final = MappingProxyType(
{tier.id: tier for tier in (stock_model.service_tiers if stock_model is not None else ())}
)

View file

@ -180,6 +180,51 @@ def test_a_model_codex_knows_keeps_its_own_reasoning_levels(levels):
assert (entry["default_reasoning_level"], entry["supported_reasoning_levels"]) == ("medium", levels)
@pytest.mark.parametrize("minimum", ["0.153.0", [0, 153, 0]], ids=["string", "parts"])
@pytest.mark.parametrize(
("client_version", "display_name", "levels"),
[
("0.153.0", "GPT-6.1 Terra", ["low", "max"]),
("0.166.0", "GPT-6.1 Terra", ["low", "max"]),
(None, "GPT-6.1 Terra", ["low", "max"]),
("0.152.9", "gpt-6.1-terra", ["low", "max"]),
("0.137.0", "gpt-6.1-terra", ["low"]),
("not-a-version", "gpt-6.1-terra", ["low"]),
],
)
def test_a_codex_older_than_an_entrys_minimal_version_gets_an_entry_it_can_decode(
minimum, client_version, display_name, levels
):
"""Codex 0.137 rejects the whole catalog on an effort it has no name for, such as the `max` Codex's own
gpt-6.1-terra entry offers, so a release older than the entry's `minimal_client_version` gets the translated
entry, whose levels are trimmed to that release."""
stock = CodexStockModel.model_validate(
{
"slug": "gpt-6.1-terra",
"display_name": "GPT-6.1 Terra",
"priority": 3,
"visibility": "list",
"minimal_client_version": minimum,
"default_reasoning_level": "low",
"supported_reasoning_levels": [{"effort": "low"}, {"effort": "max"}],
}
)
(entry,) = json.loads(
codex_models_response_json(
(_row("gpt-6.1-terra", supported_reasoning_efforts=("low", "max")),),
stock={"gpt-6.1-terra": stock},
instructions=_PROMPT,
client_version=client_version,
).json
)["models"]
assert (entry["display_name"], [level["effort"] for level in entry["supported_reasoning_levels"]]) == (
display_name,
levels,
)
def test_known_model_keeps_codex_stock_entry_listed_under_the_served_id():
(entry,) = _models(_row("gpt-daybreak"))