Skip to content

fix(ui-v3): normalize /api/slots — defaults for missing .metrics / .spark / .model (deferred fix from v3 cutover handoff)#249

Merged
thinmintdev merged 1 commit into
mainfrom
fix/v3-slots-metrics-normalizer
May 23, 2026
Merged

fix(ui-v3): normalize /api/slots — defaults for missing .metrics / .spark / .model (deferred fix from v3 cutover handoff)#249
thinmintdev merged 1 commit into
mainfrom
fix/v3-slots-metrics-normalizer

Conversation

@thinmintdev
Copy link
Copy Markdown
Contributor

Summary

After PR #235 landed v3 on main, the slots/hardware/dashboard pages black-screened on real backend payloads because v3 components dereference slot.metrics.toks, slot.metrics.kv, slot.spark, etc. directly, and the backend can omit those fields for offline / not-yet-loaded slots.

This was the deferred fix called out in the v3 cutover handoff:

Two known React crash points still exist (deferred):

  • slot.metrics.toks and friends crash when /api/slots returns slots without a metrics field (real backend behavior). Proper fix: add a normalizer in ui/src/api/hooks/useSlots.ts that defaults metrics: { toks: 0, ttft: 0, kv: 0, ... } when absent.

Change

useSlots.ts gains a normalizeSlot() helper applied in both fetchSlotsUnion and useSlotDetail:

  • metrics defaults to a full SlotMetrics shape (toks 0, ttft null, kv null, …)
  • spark defaults to [] (guards <Spark data={slot.spark} /> rendering)
  • model falls back model → model_id → model_default → ''

One change covers all 20+ slot.metrics.* access sites in slots.jsx / dashboard.jsx / slot-modals.jsx.

Why hot-patch first, PR after

The LXC at hal0.thinmint.dev was hot-patched (same byte content as this commit) and rebuilt before this PR went up, so the dashboard is already rendering. This PR brings main in sync.

Known follow-up not in scope

Test plan

🤖 Generated with Claude Code

…ots/hardware/dashboard

Backend /api/slots can omit `metrics` (and `spark`, sometimes `model`)
for offline / not-yet-loaded slots. v3 components dereference fields
like `slot.metrics.toks`, `slot.metrics.kv`, `slot.spark` directly, so
the slots/hardware/dashboard routes black-screened on real backend
payloads.

Add a `normalizeSlot()` helper in useSlots that:
  - defaults `metrics` to a full SlotMetrics shape (toks 0, ttft null, …)
  - defaults `spark` to []
  - falls back model → model_id → model_default → '' if model is absent

Applied in both `fetchSlotsUnion` and `useSlotDetail`. One change covers
all 20+ `slot.metrics.*` access sites in slots.jsx / dashboard.jsx /
slot-modals.jsx.

This is the deferred fix called out in the v3 cutover handoff. The
LXC at hal0.thinmint.dev was already hot-patched before this commit so
the dashboard renders again; this PR brings main in sync.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thinmintdev thinmintdev merged commit b620fb4 into main May 23, 2026
3 of 4 checks passed
@thinmintdev thinmintdev deleted the fix/v3-slots-metrics-normalizer branch May 23, 2026 19:59
thinmintdev added a commit that referenced this pull request May 23, 2026
…hardware actually render (#253)

Two follow-up regressions surfaced after the v3 cutover (#235 + #249):

1. **/slots looked blank/black**
   Backend /api/slots returns sparse slots without `type`, `device`, or
   `group` for built-in slots (primary, embed, stt, …). The v3 SlotsView
   groups via `slots.filter(s => s.group === "chat")` etc., so without
   inference the primary slot fell out of every section and only the
   page header rendered. Looked like a black screen on the dark theme.

   Fix: extend the useSlots `normalizeSlot()` helper with `inferSlotShape()`
   — derives type/group/device from BUILTIN_SLOTS naming conventions
   (primary/coder/agent → llm/chat, embed/rerank → embed, stt/whisper →
   voice, tts/kokoro/vibe → voice, img/sd → img) plus provider/backend
   fallbacks. Sparse backend slots now land in the correct group.

2. **/hardware crashed with TypeError**
   Backend /api/hardware returns a flat shape (cpu_model, ram_mb,
   gpu_name, npu.{present}, …). HardwareView dereferences nested fields
   (H.ram.total, H.npu.columns) directly — `H = hwQuery.data || mock`
   means the live data overrode the mock and crashed.

   Fix: add `normalizeHardware()` in useHardware that maps flat keys
   into the nested shape components expect, with neutral defaults for
   fields the backend doesn't surface (columns/ctx/hostname/uptime).

Both fixes confirmed end-to-end on the hal0 LXC: /slots now renders
the primary slot card in a Chat section, /hardware renders all five
cards (Host/CPU/GPU/NPU/Memory) without errors.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
thinmintdev added a commit that referenced this pull request May 23, 2026
…ice D) (#267)

Final slice of the Phase 1 auth removal. Closes the architectural
loop opened by ADR-0001, surfaces the breaking change to operators,
bumps the version, and patches the one test that #256 missed.

ADR
- New: docs/internal/adr/0012-remove-auth-and-caddy.md — accepts the
  removal direction, references the four implementing PRs
  (#254, #255, #256, #266), records what was deleted (~6,000 LOC) and
  what stays (uninstall cleanup, MCPAuthMiddleware as identity stash,
  bcrypt/PyJWT/argon2 deps untouched), and frames the v0.3 stream-4
  follow-up work (MCPIdentityMiddleware rename, Hermes header swap).
  Note: numbered 0012 because 0011 was taken by agent-identity-cards
  via PR #239 while the auth-removal work was in flight — the original
  briefing's "ADR-0011" suggestion was stale by the time it landed.
- ADR-0001 frontmatter flipped to `Status: Superseded by ADR-0012
  (2026-05-23)` with a paragraph explaining why "FastAPI owns auth"
  ended up being transitional.

Docs
- docs/operate/auth.mdx — full rewrite. The old page documented Caddy
  + basic_auth + the LAN-vs-WAN wizard flow; the new page tells
  operators hal0 ships open on a trusted LAN and gives three concrete
  upstream-proxy patterns (Traefik, nginx, Cloudflare Tunnel) with
  copy-pasteable configs. OpenWebUI's own `WEBUI_AUTH_TRUSTED_EMAIL_HEADER`
  override is documented for the upstream-proxy SSO case.
- README.md
  - drop "password setup → bundle pick" from the first-run feature line
  - rewrite the "Auth posture" section: references ADR-0012, drops
    the FIRST_RUN_LOCK / OTP / wizard-password-step prose, points at
    docs/operate/auth.mdx for upstream-proxy patterns
- CHANGELOG.md
  - new top-level [v0.3.0-alpha.1] — 2026-05-23 entry
  - Breaking: auth gone, Caddy gone, `--no-tls` gone, HAL0_AUTH_*
    env vars are no-ops, Bearer tokens minted under v0.2.x stop working
  - New / improved: v3 dashboard on main + normalizers (#235/#249/#253),
    /v1/* proxy (#248/#212), footer nullability (#252/#221), Settings
    default tab → Secrets
  - Removed code: per-file accounting of the ~6,000 deletions
  - Upgrade notes: lose-your-password warning, drop --no-tls flag

Tests
- tests/openwebui/test_env_writer.py
  - delete `test_auth_disabled_keeps_webui_auth_false` (covered by the
    rewritten test below)
  - delete `test_auth_enabled_flips_webui_auth_true` (the one test that
    failed CI on #256 — was asserting the now-removed
    HAL0_AUTH_ENABLED→WEBUI_AUTH=True branching)
  - delete `test_auth_falsy_values_keep_defaults` (parametrized
    sibling, same removed branching)
  - delete `test_overrides_still_win_under_auth` (same)
  - add `test_webui_auth_is_always_false_by_default` (defensive: the
    env vars are gone, WEBUI_AUTH stays False regardless)
  - add `test_trusted_email_header_via_explicit_override` (documents
    the new opt-in path through the `overrides` parameter)

Version
- pyproject.toml: 0.2.0-alpha.3 → 0.3.0-alpha.1
  (single source of truth per memory hal0_version_two_locations —
  src/hal0/__init__.py reads via importlib.metadata)

Sequence (Phase 1 complete)
- Slice B (#255, merged)
- Slice A (#256, merged with admin-override since this test failure
  was the only blocker; resolved here)
- Slice C (#266, in CI)
- Slice D (this PR)

Follow-up (per the user's briefing, not in this PR)
- Update Hermes bootstrap plan + ADR-0011 (agent identity cards) to
  drop bearer references and switch to X-hal0-Agent identity header
- Edit issues #240 / #243 / #246 to drop bearer-token acceptance
  criteria
- Rename MCPAuthMiddleware → MCPIdentityMiddleware in mcp_mount.py
- Update auto-memory feedback-caddy-reduction-divergence to note the
  direction landed

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.

1 participant