Skip to content

fix(dashboard): resolve model-switch spinner + persist benchmark on switchboard hosts - #289

Merged
azilber merged 1 commit into
mainfrom
fix/switchboard-activation-ready-and-benchmark-persist
Aug 11, 2026
Merged

fix(dashboard): resolve model-switch spinner + persist benchmark on switchboard hosts#289
azilber merged 1 commit into
mainfrom
fix/switchboard-activation-ready-and-benchmark-persist

Conversation

@azilber

@azilber azilber commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Problem

On switchboard-mode hosts (e.g. CPU installs after PR #288), the Models dashboard misbehaves:

  1. "Working" spinner never resolves on model switch. The host agent records activation completion in data/model-state.json (record_verified_route) and never writes the legacy data/model-activation-receipt.json. The dashboard-api's activationReadyModel signal was gated solely on that receipt, so it stayed null forever and the UI (activationMatches in useModels.js) spun to its 610s timeout on every switch — even though the switch had actually succeeded (llama-server + model-state.json confirm it).
  2. Benchmark result vanished after a few seconds. The UI discarded the benchmark response and re-derived Speed server-side on each poll, so the freshly measured tok/s only survived until the next refresh.

Fix

  • dashboard-api (routers/models.py): add _read_model_state() + _switchboard_activation_complete() and OR it into the activationReadyModel gate. The legacy receipt path is preserved, so non-switchboard installs are unaffected. Reuses _model_name_tokens().
  • dashboard UI (useModels.js, Models.jsx): capture the benchmark tokensPerSecond into a client-side benchmarkResults map so the value survives the next /api/models poll; getSpeedDisplay(model, benchmarkOverride) prefers it, threaded through the rows and the current-model panel. Poll acceleration also covers a running benchmark.

Not changed (working as designed)

  • "Agent blocked" on Qwen 2.5 0.5B — catalog flags it not_agent_viable.
  • Hermes "model doesn't update" — llama.cpp ignores the requested model name and serves whatever GGUF is loaded.

Tests

  • Backend: 6 new switchboard-completion cases (positive + negative) in test_models.py; full test_models.py (114), test_model_state.py/test_config.py (99) pass.
  • Frontend: new useModels test that benchmarked tok/s survives a later poll; ESLint clean; useModels (35) + Models (36) tests pass; vite build succeeds.

Verified live

Rebuilt/recreated ods-dashboard-api + ods-dashboard; with no receipt on disk, GET /api/models now returns activationReadyModel: "qwen3.5-9b-q4" (was null), sourced from the switchboard state.

Two pre-existing large files (models.py, tests/test_models.py, already >2000 lines) carry a # FILE-SIZE-OK marker; splitting is a separate follow-up.

🤖 Generated with Claude Code

…witchboard hosts

On switchboard-mode hosts the host agent records model-activation completion
in data/model-state.json (record_verified_route) and never writes the legacy
data/model-activation-receipt.json. The dashboard-api's activationReadyModel
signal was gated solely on that receipt, so it stayed null forever and the
Models page "Working" spinner ran to its 610s timeout on every switch even
though the switch had actually succeeded.

- dashboard-api: add _read_model_state() + _switchboard_activation_complete()
  and OR it into the activationReadyModel gate (legacy receipt path preserved,
  so non-switchboard installs are unaffected). Reuses _model_name_tokens().
- dashboard UI: capture the benchmark tokensPerSecond into a client-side
  benchmarkResults map so the freshly measured value survives the next
  /api/models poll (which re-derives Speed server-side); getSpeedDisplay now
  prefers that override, threaded through the model rows and the current-model
  panel. Poll acceleration also covers a running benchmark.

Adds backend unit tests for switchboard completion (positive + negative cases)
and a UI test that the benchmarked tok/s survives a subsequent poll.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@azilber
azilber merged commit 6a3a4cf into main Aug 11, 2026
20 checks passed
@azilber
azilber deleted the fix/switchboard-activation-ready-and-benchmark-persist branch August 11, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant