Skip to content

fix(dash): slot swap popover reads live /api/models#351

Merged
thinmintdev merged 3 commits into
mainfrom
fix/slot-swap-popover-live-models
May 27, 2026
Merged

fix(dash): slot swap popover reads live /api/models#351
thinmintdev merged 3 commits into
mainfrom
fix/slot-swap-popover-live-models

Conversation

@thinmintdev
Copy link
Copy Markdown
Contributor

Summary

  • InlineSwapPopover + CreateSlotModal now consume useModels() instead of HAL0_DATA.models (the static seed in ui/src/dash/data.jsx carrying fictional ids like qwen3.6-27b-mtp, qwen3-coder-30b).
  • New normalizeApiModel() maps /api/models shape → legacy seed shape so the existing filter + render code keeps working.
  • useHardware() replaces HAL0_DATA.host.ram.free for the "fits" check.

Why

Clicking a popover entry was sending a fictional id to POST /api/slots/{name}/swap. The slot orchestrator (correctly) bounced it against the real registry: code=model.not_found … "model 'qwen3.6-27b-mtp' is not in the registry". The dropdown was lying, the error message was honest.

Comment at slot-modals.jsx:54 acknowledged the gap but the wire-up never shipped.

Closes #345.

Test plan

  • Manual: POST /api/slots/hermes-agent/swap with a real registered id (qwen3.6-27b-q5kxl) succeeds → slot idle, lemonade backend
  • Bundle inspection: built dashboard JS no longer references HAL0_DATA.models.filter inside the swap popover; reads useModels().data instead
  • CI: typecheck + lint + tests green
  • Browser: hard-refresh dashboard, open inline swap on any slot, confirm only real models show

🤖 Generated with Claude Code

thinmintdev and others added 3 commits May 27, 2026 13:08
The InlineSwapPopover and CreateSlotModal were both reading from
HAL0_DATA.models (the dashboard's static seed in ui/src/dash/data.jsx),
which carries fictional ids like `qwen3.6-27b-mtp` and `qwen3-coder-30b`.
Clicking one tunneled into POST /api/slots/{name}/swap and the slot
orchestrator (correctly) bounced the bad id against the real registry:

  code=model.not_found message="model 'qwen3.6-27b-mtp' is not in
    the registry (slot 'hermes-agent' not touched)"

The comment at slot-modals.jsx:54 acknowledged the gap — "Model
catalogue still lives in HAL0_DATA — replaced when the models hook
ships (parallel teammate)" — but the wire-up never landed.

Changes
- Add normalizeApiModel() that maps the /api/models shape
  (capabilities, backends, size_bytes, name, hf_repo) onto the legacy
  HAL0_DATA shape (type, device, size, longName, repo) that the JSX
  expected. Done in JSX rather than at the API layer so models.jsx
  (Models view) still gets the unchanged response.
- CreateSlotModal + InlineSwapPopover now consume useModels() and
  useHardware() instead of HAL0_DATA.{models,host}.
- InlineSwapPopover: move `if (!open) return null` AFTER the hook calls
  to keep rules-of-hooks legal (useQuery's caching makes the cost ~zero
  when closed).
- CreateSlotModal device filter switched from single-string match
  (m.device === "rocm") to backend-list membership (m.backends.includes
  ("rocm")) since /api/models advertises multiple backends per row.

Verified live on LXC 105: qwen3.6-27b-q5kxl swaps cleanly into
hermes-agent (idle, lemonade backend) — was the spike repro for #345.

Closes #345.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The popover now reads from useModels(); without a fixture route the
apiMock catch-all returned {} → useModels()=[] → 0 .swap-pop-items →
slots-wireup-v3.spec.ts:135 toBeVisible() timed out.

Adds 4 representative MOCK_DATA.models rows (chat, coding, embed,
rerank) keyed by capabilities/backends so the JSX normalizer derives
matching slot.type values, and a /api/models route on
installDefaultMocks that fulfils them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the Vite dev server's /api proxy target is down (or in CI before
the page.route handlers attach), src/api/mock.ts falls back to
HAL0_DATA.models — which use the legacy seed shape (labels + type +
device + size as strings) rather than the registry shape (capabilities
+ backends + size_bytes). The first version of normalizeApiModel only
read capabilities/backends/size_bytes, so every row got type='' and
the popover filter rejected all 14 entries → empty .swap-pop list.

Tolerate both shapes: prefer existing m.type/m.device/m.size if the
row already carries them, else derive from capabilities/backends/
size_bytes. Same for longName + repo.

Verified slots-wireup-v3 'Swap model — inline popover' passes locally
(1.3s, was failing at 5s timeout in CI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thinmintdev thinmintdev merged commit 996c881 into main May 27, 2026
4 checks passed
@thinmintdev thinmintdev deleted the fix/slot-swap-popover-live-models branch May 27, 2026 17:29
thinmintdev added a commit that referenced this pull request May 28, 2026
…rough + gut installer auth section (#390)

- docs/operate/lemonade.md (new, .md canonical): operator reference for
  the v0.2 Lemonade runtime — what it is, where state lives, the /v1/*
  proxy + dispatcher fallthrough (PRs #248/#277), slot ↔ Lemonade
  model mapping (PRs #281/#282), max_loaded_models = 8 LRU cap (PR
  #283), per-type LRU eviction per ADR-0008 (supersedes nuclear-evict
  ADR-0007), OFFLINE-on-eviction (PR #276), and the three known v0.3
  caveats (Vulkan KV gauge missing, whisper RUNPATH workaround, GPU
  cleanup unload hang).

- docs/dashboard/v3.md (new, .md canonical, new docs/dashboard/ dir):
  page-by-page tour of the v3 React dashboard shipped in
  v0.3.0-alpha.1 (PR #235). Covers the shell + Mock-badge convention,
  /dashboard (system overview after #356), /chat (real surface per
  #309/#314/#315/#351), /slots (sidebar mirror per #357 + #344 UX
  sweep), /models (#313/#319/#353), /mcp (#304/#300), /agents (Peers
  per #299), /memory (graph #297, throughput #308), Settings (no Auth
  tab post-ADR-0012), and the footer journal (Epic #322 — PRs
  #321/#328/#329/#330/#332). Mock-fallback issues linked via the
  dashboard-v3 label, not enumerated.

- installer/README.md: gut ~95 lines of stale auth prose (Caddy,
  Bearer-token mint/use/revoke, first-run OTP claim wizard,
  HAL0_AUTH_ENABLED/HAL0_AUTH_DISABLED, password recovery, basic_auth
  upgrade path, the TLS recipe). Replace with one paragraph pointing
  at docs/operate/auth.mdx for the reverse-proxy recipe and
  docs/agents/identity.md for the X-hal0-Agent identity model. Auth
  was removed in v0.3.0-alpha.1 per ADR-0012; the README hadn't
  caught up.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

dash: Slot swap popover + Create-slot modal were emitting fictional model_ids from HAL0_DATA mock

1 participant