diff --git a/tests/test_server_available_models.py b/tests/test_server_available_models.py index 65f6fc0e..958adca3 100644 --- a/tests/test_server_available_models.py +++ b/tests/test_server_available_models.py @@ -5,8 +5,10 @@ composer + the channel gateway. The dashboard's Options panel renders each one as a "Default — alias (model)" placeholder, so the resolution chain has to stay honest: -* ``default_alias`` ← ``model.default_alias``, falling back to the - registry default; blanked when it points at a disabled/removed alias. +* ``default_alias`` ← ``model.default_alias`` when it names an enabled + alias, otherwise ``registry.default`` (mirrors session_factory's + ``_effective_default_alias`` — the model a new workstream actually launches + on). Only blanked when even ``registry.default`` is unresolvable. * ``channel_default_alias`` ← ``channels.default_model_alias``. * ``judge_default_alias`` ← ``judge.model``, but *only* when it names an enabled alias. An unset / whitespace / unknown / disabled value stays @@ -151,10 +153,30 @@ def test_model_default_falls_back_to_registry_default() -> None: assert body["judge_default_alias"] == "" -def test_model_default_blanked_when_pointing_at_unknown_alias() -> None: +def test_model_default_foreign_alias_falls_back_to_registry_default() -> None: + """``model.default_alias`` naming an alias absent from THIS server's + registry (e.g. a console-only alias leaking through a shared ConfigStore) + falls back to ``registry.default`` — the model creation actually uses — + rather than being blanked. Blanking made the dashboard show a bare + "Default model" placeholder even though a workstream would launch on a + concrete model.""" body = _get_models( _make_client( aliases={"primary": "vendor/primary"}, + registry_default="primary", + settings={"model.default_alias": "ghost"}, + ) + ) + assert body["default_alias"] == "primary" + + +def test_model_default_blanks_only_when_registry_default_also_unresolvable() -> None: + """The defensive blank still applies when neither the configured alias + nor ``registry.default`` resolves to an enabled alias.""" + body = _get_models( + _make_client( + aliases={"primary": "vendor/primary"}, + registry_default="", settings={"model.default_alias": "ghost"}, ) ) diff --git a/turnstone/server.py b/turnstone/server.py index 24234e6b..a5693301 100644 --- a/turnstone/server.py +++ b/turnstone/server.py @@ -1170,10 +1170,17 @@ async def list_available_models(request: Request) -> JSONResponse: default_alias = cs.get("model.default_alias") or "" channel_default_alias = cs.get("channels.default_model_alias") or "" judge_default_alias = (cs.get("judge.model") or "").strip() - if not default_alias: - default_alias = registry.default - # Clear defaults that point to unknown/disabled aliases. + # Mirror session_factory's ``_effective_default_alias`` (and + # ``_effective_routing``): a ``model.default_alias`` that's unset — or + # names an alias absent from THIS server's registry — falls back to + # ``registry.default``, which is the model a new workstream actually + # launches on. Reporting "" here made the dashboard show a bare + # "Default model" placeholder even though creation has a concrete default + # (e.g. when the shared ConfigStore points at a console-only alias). enabled_aliases = set(registry.list_aliases()) + if default_alias not in enabled_aliases: + default_alias = registry.default + # Defensive: only blank if even registry.default is unknown/disabled. if default_alias and default_alias not in enabled_aliases: default_alias = "" if channel_default_alias and channel_default_alias not in enabled_aliases: