From 35f7ce61d8ae494a5954f85776e47297ca70754a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Klos-Neumann?= Date: Tue, 30 Jun 2026 18:41:22 +0200 Subject: [PATCH] fix(model-picker): handle list-of-dict `models:` in provider config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_models_payload and its downstream consumers (inventory dedup, _apply_capabilities, _apply_pricing) assume each provider row's `models` is a list of string ids — they call `m.lower()` and use the ids as dict keys. But list_authenticated_providers appended user-provider `models:` list entries verbatim, so a hand-edited config using the `{id, label}` list form (instead of the dict-keyed-by-id form Hermes writes) put dicts into the list. Result: `GET /api/model/options` 500'd with `AttributeError: 'dict' object has no attribute 'lower'`, leaving the desktop model picker empty ("Failed to list model options"). Normalize list entries to their id (`id` | `model` | `name`) so `models` stays a list[str] regardless of which config shape the user wrote. Verified: build_models_payload(load_picker_context(), …) now returns 41 providers with string model ids and no AttributeError. Co-Authored-By: Claude Opus 4.8 (1M context) --- hermes_cli/model_switch.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index 48bf031b75b3c..b3a1ae570b59e 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -1958,8 +1958,14 @@ def _has_aws_sdk_creds_for_listing(slug: str) -> bool: models_list.append(m) elif isinstance(cfg_models, list): for m in cfg_models: - if m and m not in models_list: - models_list.append(m) + # List entries are normally plain id strings, but hand-edited + # configs sometimes use {id|model|name: ..., label: ...} dicts. + # Extract the id so models_list stays a list[str]: every + # downstream consumer (inventory dedup, capabilities, pricing) + # assumes string ids and calls .lower() / uses them as keys. + mid = (m.get("id") or m.get("model") or m.get("name") or "") if isinstance(m, dict) else m + if mid and mid not in models_list: + models_list.append(mid) # Official OpenAI API rows in providers: often have base_url but no # explicit models: dict — avoid a misleading zero count in /model.