mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-15 00:12:26 -06:00
fix(console-ui): align coordinator placeholder fallback with session_factory
Two Copilot-review followups on /v1/api/models default resolution. - console/server.py: coordinator_default_alias now mirrors the full fallback chain in console/session_factory.py:109-110 — explicit coordinator.model_alias → model.default_alias → registry.default. The registry tier was missing, so the home composer placeholder went blank whenever an operator never set model.default_alias in the admin UI even though new coordinator sessions still launch on registry.default (loaded from config.toml [model].default by load_model_registry). Two new tests cover the registry-default branch and the disabled-alias guard. - console/static/app.js: _resolveModelLabel returns "" (not the bare alias) when the alias isn't found in the dropdown's model list, so callers can rely on the documented "fall back to neutral placeholder" contract. Matches the existing doc comment.
This commit is contained in:
@@ -67,6 +67,7 @@ def _make_client(
|
||||
storage: SQLiteBackend,
|
||||
*,
|
||||
settings: dict[str, str] | None = None,
|
||||
registry_default: str | None = None,
|
||||
) -> TestClient:
|
||||
app = Starlette(
|
||||
routes=[Route("/v1/api/models", list_available_models)],
|
||||
@@ -74,6 +75,12 @@ def _make_client(
|
||||
)
|
||||
app.state.auth_storage = storage
|
||||
app.state.config_store = _FakeConfigStore(dict(settings or {}))
|
||||
if registry_default is not None:
|
||||
# Mimic the live console's ``coord_registry`` shape — the handler
|
||||
# only reads ``.default``, so a SimpleNamespace is enough.
|
||||
from types import SimpleNamespace
|
||||
|
||||
app.state.coord_registry = SimpleNamespace(default=registry_default)
|
||||
client = TestClient(app)
|
||||
client.headers.update({"X-Test-User": "admin", "X-Test-Perms": ""})
|
||||
return client
|
||||
@@ -165,6 +172,34 @@ def test_coordinator_set_to_unknown_alias_falls_back_to_default(
|
||||
assert body["coordinator_default_alias"] == "primary"
|
||||
|
||||
|
||||
def test_coordinator_falls_back_to_registry_default_when_config_store_empty(
|
||||
storage: SQLiteBackend,
|
||||
) -> None:
|
||||
"""Match ``console/session_factory.py:109-110``: when both
|
||||
``coordinator.model_alias`` and ``model.default_alias`` are unset, new
|
||||
coordinator sessions run on ``registry.default`` (loaded from
|
||||
config.toml ``[model].default``). The placeholder must report the
|
||||
same alias rather than going blank — otherwise the home composer
|
||||
advertises "Default model" while sessions actually launch on a
|
||||
concrete alias."""
|
||||
_seed_model(storage, definition_id="m1", alias="primary")
|
||||
body = _get_models(_make_client(storage, registry_default="primary"))
|
||||
assert body["default_alias"] == ""
|
||||
assert body["coordinator_default_alias"] == "primary"
|
||||
assert body["judge_default_alias"] == "primary"
|
||||
|
||||
|
||||
def test_coordinator_skips_registry_default_when_alias_disabled(
|
||||
storage: SQLiteBackend,
|
||||
) -> None:
|
||||
"""Registry default points at an alias that's been disabled in the DB
|
||||
— the placeholder stays blank rather than advertising a model that
|
||||
workstream creation would refuse to use."""
|
||||
_seed_model(storage, definition_id="m1", alias="legacy", enabled=False)
|
||||
body = _get_models(_make_client(storage, registry_default="legacy"))
|
||||
assert body["coordinator_default_alias"] == ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Judge resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1684,15 +1684,25 @@ async def list_available_models(request: Request) -> JSONResponse:
|
||||
default_alias = ""
|
||||
if channel_default_alias and channel_default_alias not in enabled_aliases:
|
||||
channel_default_alias = ""
|
||||
# Coordinator falls back to model.default_alias when its setting is
|
||||
# empty or points at a disabled/removed alias (matches
|
||||
# console/session_factory.py). Judge falls back to the resolved
|
||||
# coordinator alias when judge.model is empty *or* not a registered
|
||||
# alias — judge.model is alias-only (matches IntentJudge.__init__),
|
||||
# so an unknown value is operator misconfiguration that the judge
|
||||
# itself silently inherits the session model on.
|
||||
# Coordinator fallback chain mirrors console/session_factory.py:109-110:
|
||||
# explicit ``coordinator.model_alias`` → ``model.default_alias``
|
||||
# (admin-managed) → ``registry.default`` (the config.toml default that
|
||||
# session_factory falls through to via ``registry.default`` when both
|
||||
# ConfigStore keys are unset). Without the registry tier the
|
||||
# placeholder lies whenever an operator never set ``model.default_alias``
|
||||
# in the admin UI — new coordinator sessions will still run on
|
||||
# ``registry.default``. Judge falls back to the resolved coordinator
|
||||
# alias when ``judge.model`` is empty *or* not a registered alias —
|
||||
# judge.model is alias-only (matches IntentJudge.__init__), so an
|
||||
# unknown value is operator misconfiguration that the judge itself
|
||||
# silently inherits the session model on.
|
||||
if not coordinator_default_alias or coordinator_default_alias not in enabled_aliases:
|
||||
coordinator_default_alias = default_alias
|
||||
if not coordinator_default_alias:
|
||||
coord_registry = getattr(request.app.state, "coord_registry", None)
|
||||
registry_default = getattr(coord_registry, "default", "") if coord_registry else ""
|
||||
if registry_default and registry_default in enabled_aliases:
|
||||
coordinator_default_alias = registry_default
|
||||
if not judge_default_alias or judge_default_alias not in enabled_aliases:
|
||||
judge_default_alias = coordinator_default_alias
|
||||
return JSONResponse(
|
||||
|
||||
@@ -1813,7 +1813,7 @@ function _resolveModelLabel(alias, models) {
|
||||
return m.alias === m.model ? m.alias : m.alias + " (" + m.model + ")";
|
||||
}
|
||||
}
|
||||
return alias;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Populate Model + Judge Model dropdowns from /v1/api/models — same
|
||||
|
||||
Reference in New Issue
Block a user