From a83f669bcf2b0f33909ec5a969692a8ee6737149 Mon Sep 17 00:00:00 2001 From: Teknium Date: Tue, 28 Apr 2026 01:47:31 -0700 Subject: [PATCH] fix(models): auto-derive xAI model list from models.dev cache (#16699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the static list refresh: replace the hardcoded xAI entries with _xai_curated_models(), mirroring the _codex_curated_models() pattern from PR #7844. The helper reads $HERMES_HOME/models_dev_cache.json at import time (no network call) and falls back to a small static list when the cache is missing or malformed. Why: _PROVIDER_MODELS["xai"] has drifted once already (issue #16699) and will drift again next time xAI renames a model. Hermes already maintains the models.dev cache and uses it for context-length lookups; pointing _PROVIDER_MODELS at the same source means the /model picker self-heals on the next cache refresh instead of requiring a PR. Behavior: - With cache populated (normal user): shows every current xAI model ID, picks up renames automatically on next refresh. - Without cache (fresh install, offline): falls back to a static snapshot of the 9 current flagship IDs. - Malformed cache / unexpected shape: same static fallback, no crash. Import time verified <20ms — disk read only, no HTTP. Addresses the structural piece of #16699 ("consider a single _provider_models(provider) resolver") for xAI. Other per-provider lists can adopt the same pattern as drift is observed. --- hermes_cli/models.py | 57 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 3afc894d596..21fa3b74f35 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -106,6 +106,51 @@ def _codex_curated_models() -> list[str]: return _add_forward_compat_models(list(DEFAULT_CODEX_MODELS)) +# Static fallback for xAI when the models.dev disk cache is empty (fresh +# install, offline first run, etc.). Mirrors the xAI-direct model IDs from +# $HERMES_HOME/models_dev_cache.json as of 2026-04-28. Whenever xAI renames +# or retires a model, the disk cache picks it up on the next refresh and the +# fallback here only matters until that refresh lands. +_XAI_STATIC_FALLBACK: list[str] = [ + "grok-4.20-0309-reasoning", + "grok-4.20-0309-non-reasoning", + "grok-4.20-multi-agent-0309", + "grok-4-1-fast", + "grok-4-1-fast-non-reasoning", + "grok-4-fast", + "grok-4-fast-non-reasoning", + "grok-4", + "grok-code-fast-1", +] + + +def _xai_curated_models() -> list[str]: + """Derive the xAI-direct curated list from models.dev disk cache. + + Reads $HERMES_HOME/models_dev_cache.json directly (no network) so this + runs at import time without blocking. Falls back to ``_XAI_STATIC_FALLBACK`` + when the cache is empty or unreadable. Hermes refreshes the cache from + https://models.dev/api.json on normal use, so this list self-heals as + xAI renames models. + + Mirrors ``_codex_curated_models()``'s role for openai-codex. + """ + try: + from agent.models_dev import _load_disk_cache + data = _load_disk_cache() + xai = data.get("xai") if isinstance(data, dict) else None + models = xai.get("models") if isinstance(xai, dict) else None + if isinstance(models, dict) and models: + ids = [mid for mid in models.keys() if isinstance(mid, str)] + if ids: + return sorted(ids) + except Exception: + # Any failure (missing file, malformed JSON, import error) + # falls through to the static list. + pass + return list(_XAI_STATIC_FALLBACK) + + _PROVIDER_MODELS: dict[str, list[str]] = { "nous": [ "moonshotai/kimi-k2.6", @@ -193,17 +238,7 @@ _PROVIDER_MODELS: dict[str, list[str]] = { "glm-4.5", "glm-4.5-flash", ], - "xai": [ - "grok-4.20-0309-reasoning", - "grok-4.20-0309-non-reasoning", - "grok-4.20-multi-agent-0309", - "grok-4-1-fast", - "grok-4-1-fast-non-reasoning", - "grok-4-fast", - "grok-4-fast-non-reasoning", - "grok-4", - "grok-code-fast-1", - ], + "xai": _xai_curated_models(), "nvidia": [ # NVIDIA flagship reasoning models "nvidia/nemotron-3-super-120b-a12b",