fix(models): stale cross-provider model no longer shows as unavailable in picker (closes #829)
* fix(models): stale cross-provider model no longer shows as unavailable in picker Two bugs allowed an openai/gpt-5.4-mini stale session model to appear as '(unavailable)' under a custom provider group for users who never configured OpenAI (#829). Backend (api/routes.py): _resolve_compatible_session_model() had a blanket early-return for active_provider in {custom, openrouter} that skipped all normalization regardless of whether any catalog group could route the model's prefix. A custom_providers-only user with a stale openai/... session model was never corrected. Fixed: only skip normalization when the model prefix is actually routable (matches a catalog group provider_id, or an openrouter group is present that can route any provider/model). Frontend (static/ui.js): renderSession() injected a bare <option> (not in any <optgroup>) for models not found in the dropdown. renderModelDropdown() rendered bare options without emitting a group heading, so they visually inherited the last rendered provider heading — making the stale model appear to belong to the custom provider group. Fixed: silently reset to the first available model and fire a PATCH to persist the correction instead of injecting a misleading (unavailable) option. 5 new tests in test_provider_mismatch.py cover: - stale openai model cleared when custom_providers-only + no default_model - stale openai model cleared when custom_providers-only + default_model set - openrouter model preserved when openrouter group present - custom/ namespace always preserved - ui.js no longer injects model_unavailable option * fix(ui): declare modelSel locally in syncTopbar reset path; fix test assertion - Use const modelSel=$('modelSelect') instead of undeclared sel in the stale-model reset branch of syncTopbar() (caught in Opus review) - Fix test assertion: or → and for model_unavailable key absence check --------- Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
This commit is contained in:
@@ -208,7 +208,7 @@ def _resolve_compatible_session_model(model_id: str | None) -> tuple[str, bool]:
|
||||
return default_model, bool(default_model)
|
||||
|
||||
active_provider = _normalize_provider_id(catalog.get("active_provider"))
|
||||
if not active_provider or active_provider in {"custom", "openrouter"}:
|
||||
if not active_provider:
|
||||
return model, False
|
||||
|
||||
slash = model.find("/")
|
||||
@@ -223,6 +223,32 @@ def _resolve_compatible_session_model(model_id: str | None) -> tuple[str, bool]:
|
||||
return model, False
|
||||
|
||||
model_provider = _normalize_provider_id(model[:slash])
|
||||
|
||||
# For custom/openrouter active providers: only skip normalization when the
|
||||
# model's namespace prefix is actually routable by a group in the catalog.
|
||||
# A user who only has custom_providers configured (active_provider="custom")
|
||||
# with a stale session model like "openai/gpt-5.4-mini" would otherwise
|
||||
# never get cleaned up, causing "(unavailable)" to appear in the picker.
|
||||
if active_provider in {"custom", "openrouter"}:
|
||||
# These namespaces are always routable as-is — preserve them.
|
||||
if model_provider in {"", "custom", "openrouter"}:
|
||||
return model, False
|
||||
# Check if any catalog group can actually route this model's prefix.
|
||||
groups = catalog.get("groups") or []
|
||||
routable_provider_ids = {
|
||||
_normalize_provider_id(g.get("provider_id") or "") for g in groups
|
||||
}
|
||||
# openrouter group can route any provider/model namespace
|
||||
has_openrouter_group = any(
|
||||
(g.get("provider_id") or "") == "openrouter" for g in groups
|
||||
)
|
||||
if model_provider in routable_provider_ids or has_openrouter_group:
|
||||
return model, False
|
||||
# Model prefix is not routable — stale cross-provider reference, clear it.
|
||||
if default_model:
|
||||
return default_model, True
|
||||
return model, False
|
||||
|
||||
# Skip normalization for models on custom/openrouter namespaces — these are
|
||||
# user-controlled and should never be silently replaced.
|
||||
if model_provider and model_provider not in {"", "custom", "openrouter"} and model_provider != active_provider and default_model:
|
||||
|
||||
Reference in New Issue
Block a user