fix(dash): slot swap popover reads live /api/models#351
Merged
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
InlineSwapPopover+CreateSlotModalnow consumeuseModels()instead ofHAL0_DATA.models(the static seed inui/src/dash/data.jsxcarrying fictional ids likeqwen3.6-27b-mtp,qwen3-coder-30b).normalizeApiModel()maps/api/modelsshape → legacy seed shape so the existing filter + render code keeps working.useHardware()replacesHAL0_DATA.host.ram.freefor 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
POST /api/slots/hermes-agent/swapwith a real registered id (qwen3.6-27b-q5kxl) succeeds → slot idle, lemonade backendHAL0_DATA.models.filterinside the swap popover; readsuseModels().datainstead🤖 Generated with Claude Code