feat(desktop): warn when main-model switch leaves auxiliary tasks pinned to another provider#40286
Conversation
…ned to another provider Switching the main model never touches auxiliary slot pins (they're independent, sticky per-task overrides). A user who switches main away from a now-unpaid provider keeps paying 402s on every background aux call until they manually reset those pins — silently, with no UI signal. - /api/model/set scope:'main' now returns stale_aux: slots still pinned to a provider different from the new main (additive field). - Desktop Model Settings shows a switch-time notice after Apply AND a persistent banner when any loaded aux slot mismatches the main provider, both wired to the existing 'Reset all to main' action. - Never auto-clears pins — a dedicated cheaper aux model is a legitimate config; surface-and-offer instead of nuking. - Fixes a stale pre-existing assertion in the panel test (main model now renders via selectors, not a standalone label).
🔎 Lint report:
|
alpindiay
left a comment
There was a problem hiding this comment.
LGTM. Excellent UX improvement that prevents the silent credit-burn path — when a user switches their main model provider but auxiliary tasks remain pinned to the old provider. Two detection layers:
Backend (web_server.py)
set_model_assignment with scope: "main" now returns stale_aux listing any auxiliary slots pinned to a provider different from the new main. Auto slots are correctly excluded.
Frontend (model-settings.tsx)
- Switch-time warning (
switchStaleAux): shown immediately after applying a new main model - Persistent warning (
persistentStaleAux): computed on load from current config, catches "forgot months ago" cases - Shared
StaleAuxWarningcomponent with task names, provider info, and one-click "Reset all to main" button - Banner uses amber styling — noticeable but not alarming
New StaleAuxAssignment type defined in both hermes.ts (desktop) and api.ts (web). Tests cover both the backend stale_aux response and the React component rendering. No security concerns.
Minor suggestion (non-blocking): the StaleAuxWarning prop applying could be named disabled or resetting for clarity, but it's clear from context.
…ned to another provider (NousResearch#40286) Switching the main model never touches auxiliary slot pins (they're independent, sticky per-task overrides). A user who switches main away from a now-unpaid provider keeps paying 402s on every background aux call until they manually reset those pins — silently, with no UI signal. - /api/model/set scope:'main' now returns stale_aux: slots still pinned to a provider different from the new main (additive field). - Desktop Model Settings shows a switch-time notice after Apply AND a persistent banner when any loaded aux slot mismatches the main provider, both wired to the existing 'Reset all to main' action. - Never auto-clears pins — a dedicated cheaper aux model is a legitimate config; surface-and-offer instead of nuking. - Fixes a stale pre-existing assertion in the panel test (main model now renders via selectors, not a standalone label).
Summary
Switching the main model in the desktop app now warns when auxiliary tasks are still pinned to a different provider, so users don't silently burn credits on a provider they thought they left.
Root cause: a main-model switch (
/api/model/setscope:main) writesconfig.yamlmodel.default/model.provider, which the auxiliary client reads as its fallback. But per-task aux pins (auxiliary.<task>.provider) are independent and win over the main model — and the switch never touches them. A user who switches main away from a now-unpaid provider keeps paying 402s on every background aux call (compression, curator, skill-review, title gen) with zero UI signal. This is the credit-burn path from the Discord report (gemini main + aux still pinned to nous/claude on a $0 balance).Changes
hermes_cli/web_server.py:/api/model/setscope:mainnow returnsstale_aux— aux slots pinned to a provider different from the new main (additive field; existing callers ignore it). Never auto-clears pins.apps/desktop/src/app/settings/model-settings.tsx: switch-time notice after Apply and a persistent banner when any loaded aux slot mismatches the main provider; both wired to the existing "Reset all to main" action.apps/desktop/src/types/hermes.ts+web/src/lib/api.ts(shared/api/model/setendpoint).Desktop == CLI engine confirmed: both write the same
config.yaml, so the main-model switch already propagates to aux routing. The gap was purely the silent sticky aux pins — surfaced here, not auto-nuked (a dedicated cheaper aux model is a legitimate config).Validation
tests/hermes_cli/test_web_server.py -k "stale_aux or set_model_main"tsc -bmodel-settings.test.tsx(jsdom)Infographic