Stage 314: PR #1827 — sync Codex provider card models with picker by @Michaelyklam

Note: PR #1827 was branched before v0.51.19 shipped #1812, which
introduced an initial (pure live-fetch) Codex provider card hook in
api/providers.py at the same line range. The contributor's PR was
filed AFTER #1812 shipped but their diff didn't yet account for it.
Stage 314 absorbs the contributor's intent (visible Codex cache
merge for gpt-5.3-codex-spark visibility) by replacing the v0.51.19
hook with the richer merged version directly in stage. Production
code change ≡ what the contributor's PR would have produced if
rebased onto current master. Test file + pr-media adopted verbatim.
Marker commit so the stage log makes the absorption visible.
This commit is contained in:
hermes-agent
2026-05-07 17:58:52 +00:00
parent eb88d5390e
commit 0ed63968b6
4 changed files with 105 additions and 16 deletions

View File

@@ -20,6 +20,9 @@ from api.config import (
_PROVIDER_DISPLAY,
_PROVIDER_MODELS,
_get_label_for_model,
_models_from_live_provider_ids,
_read_live_provider_model_ids,
_read_visible_codex_cache_model_ids,
_save_yaml_config_file,
get_config,
invalidate_models_cache,
@@ -692,23 +695,24 @@ def get_providers() -> dict[str, Any]:
models = list(_PROVIDER_MODELS.get(pid, []))
models_total = len(models)
# Codex account catalogs are account-specific and can drift faster than
# WebUI's static fallback table (#1807). Prefer the live agent resolver
# for the providers card too so stale static-only model IDs are not
# presented as available when discovery succeeds.
# OpenAI Codex account catalogs drift independently from WebUI releases.
# The model picker already prefers hermes_cli + Codex local cache for
# this provider (the agent's `provider_model_ids("openai-codex")` filters
# IDs with `supported_in_api: false`, but Codex CLI still surfaces some
# of those — notably `gpt-5.3-codex-spark` from #1680 — in its picker).
# Merge both sources here so the providers card matches the picker
# exactly. Static entries remain the offline fallback when live
# discovery and the local Codex cache are both unavailable. (#1807
# follow-up to v0.51.19 #1812.)
if pid == "openai-codex":
try:
from hermes_cli.models import provider_model_ids as _provider_model_ids
live_ids = [mid for mid in (_provider_model_ids("openai-codex") or []) if mid]
if live_ids:
models = [
{"id": mid, "label": _get_label_for_model(mid, [])}
for mid in live_ids
]
models_total = len(models)
except Exception:
logger.debug("Failed to load OpenAI Codex models from hermes_cli")
live_ids = _read_live_provider_model_ids("openai-codex")
for mid in _read_visible_codex_cache_model_ids():
if mid not in live_ids:
live_ids.append(mid)
live_models = _models_from_live_provider_ids(pid, live_ids)
if live_models:
models = live_models
models_total = len(models)
# Nous Portal: prefer the live catalog so the providers card matches
# the dropdown picker (#1538). Same fallback shape as the static-only
# case below — when hermes_cli is unavailable or its lookup raises,

View File

@@ -0,0 +1,35 @@
{
"id": "openai-codex",
"display_name": "OpenAI Codex",
"has_key": true,
"configurable": false,
"is_oauth": true,
"key_source": "oauth",
"models": [
{
"id": "gpt-5.5",
"label": "GPT 5.5"
},
{
"id": "gpt-5.4",
"label": "GPT 5.4"
},
{
"id": "gpt-5.4-mini",
"label": "GPT 5.4 Mini"
},
{
"id": "gpt-5.3-codex",
"label": "GPT 5.3 Codex"
},
{
"id": "gpt-5.2",
"label": "GPT 5.2"
},
{
"id": "gpt-5.3-codex-spark",
"label": "GPT 5.3 Codex Spark"
}
],
"models_total": 6
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -161,6 +161,56 @@ class TestGetProviders:
config.cfg.update(old_cfg)
config._cfg_mtime = old_mtime
def test_openai_codex_provider_card_prefers_live_catalog(self, monkeypatch, tmp_path):
"""OpenAI Codex provider cards should not advertise stale static fallback models.
/api/models already uses hermes_cli/Codex cache discovery for Codex. The
provider card should share that source order so rejected stale entries
such as gpt-5.5-mini are not presented as currently available when the
live account catalog excludes them (#1807).
"""
_install_fake_hermes_cli(monkeypatch)
monkeypatch.setattr(profiles, "get_active_hermes_home", lambda: tmp_path)
fake_models = sys.modules["hermes_cli.models"]
fake_models.provider_model_ids = lambda pid: (
["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex", "gpt-5.2"]
if pid == "openai-codex"
else []
)
codex_home = tmp_path / "empty-codex-home"
codex_home.mkdir()
monkeypatch.setenv("CODEX_HOME", str(codex_home))
old_cfg = dict(config.cfg)
old_mtime = config._cfg_mtime
config.cfg.clear()
config.cfg["model"] = {"provider": "openai-codex", "default": "gpt-5.5"}
config.cfg["providers"] = {}
try:
config._cfg_mtime = config.Path(config._get_config_path()).stat().st_mtime
except Exception:
config._cfg_mtime = 0.0
from api.providers import get_providers
try:
result = get_providers()
codex = next(p for p in result["providers"] if p["id"] == "openai-codex")
model_ids = [m["id"] for m in codex["models"]]
assert model_ids == [
"gpt-5.5",
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.3-codex",
"gpt-5.2",
]
assert "gpt-5.5-mini" not in model_ids
assert codex["models_total"] == len(model_ids)
finally:
config.cfg.clear()
config.cfg.update(old_cfg)
config._cfg_mtime = old_mtime
class TestSetProviderKey:
"""Unit tests for set_provider_key() function."""