2.4.1: model picker - honest sizing, nothing hidden, upgrade-break detection - #36
Conversation
…tection
Fixes three reported UI issues plus a silent-failure gap found while
investigating them.
- No model is hidden. The dashboard dropped every fits=="no" candidate, which
made real catalog models invisible: qwen3.6-moe:35b-a3b never appeared and had
to be pulled by hand. recommend_models now returns EVERY candidate with a
fits tag + fit_reason; oversized ones render marked and confirm before pull.
- Sizing uses real numbers. MoE tags were read as their total ('...35b-a3b' as
35B, not ~3B active). Fit now prefers FastFlowLM's measured footprint_gb
(which also fixes gemma4-it:e4b: reads "4B" by name, actually 8B / 9.1 GB) and
falls back to active-param counts for MoE. flm_list forwards the catalog
metadata (footprint/installed/flm_min_version/parameter_size/context) that it
previously discarded. Verified against all 37 live FLM 0.9.45 catalog names:
zero false MoE detections.
- Provider-upgrade breakage is surfaced. FLM 0.9.45 rejects weights stamped for
0.9.43 and reports those models as not installed; nothing detected that, so
the next hotkey failed opaquely (no Ollama fallback available). New
_active_model_health reports ok / not_installed / unknown, and Config → Models
shows a warning naming the model and remedy. Listing errors stay "unknown" —
never a false alarm.
- One "Models" card. "Installed models" + "Pull a new model" merged into a
single card with Installed / Add a model / FastFlowLM runtime subheads.
- The picker is app-styled. Replaced a native <datalist> (browsers draw the
popup in their own chrome and ignore page CSS, so it could never match the
app) and a native <select size> with plain elements: theme-aware in light and
dark, keyboard navigable (↑↓/Enter/Esc), type-to-filter.
Self-caught during review: the health check compared against a "all" listing,
but Ollama's "all" is installed+suggestions, so a never-pulled model would read
as installed. It now trusts only authoritative details or re-lists with the
installed filter (SPEC B34).
Verified: ruff clean, 434 pytest (+28), app.js syntax, and live in-browser
checks — combobox/list computed styles match the app's own card/input tokens in
both light and dark, zero native selects or datalists remain.
SPEC V38/V39/V40, T28, B29-B34.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…note Records why per-round KV cache checks don't affect the prompt pipeline (all hotkey modes are single-shot/stateless; chat replays to one backend; fallback is all-or-nothing between separate servers), and corrects the stale Phase 4 blocker: the NPU-contention argument applies to a second NPU runtime, not an NPU+GPU split, which 0.9.45's cross-backend cache reuse now makes practical. Decision unchanged (speed goal already met) — only the reasoning was outdated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07a29c677b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| mem = float(hw.get("ram_gb") or 0) + float(hw.get("vram_gb") or 0) | ||
| return max(2.0, round(mem - _OS_HEADROOM_GB, 1)) |
There was a problem hiding this comment.
Exclude discrete GPU VRAM from FastFlowLM capacity
On a Ryzen AI machine that also has a discrete GPU, such as 16 GB RAM plus a 24 GB NVIDIA card, _nvidia_vram_gb() supplies that dedicated VRAM and this calculation reports 34 GB usable after headroom. FastFlowLM's NPU weights reside in system RAM and cannot use the NVIDIA VRAM, so a 24.3 GB catalog model is incorrectly marked as fitting and can then fail to load. Only add VRAM when it is known to be a UMA carve-out; otherwise base FastFlowLM footprint checks on system RAM.
Useful? React with 👍 / 👎.
| else: | ||
| fits = "no" | ||
| models.append({"name": name, "params_b": params, "fits": fits}) | ||
| return {"hardware": hw, "budget": budget, "provider": provider, "models": models} | ||
| effective = active or params |
There was a problem hiding this comment.
Account for total MoE weights when footprint is unavailable
When an older FastFlowLM listing has no footprint metadata, a 35B-total/3B-active MoE is compared to the memory-oriented model budget as only 3B. On a 16 GB machine this produces tight rather than no, despite the approximately 24 GB weight footprint, so the UI skips its oversized-model confirmation and downloads a model that cannot fit. Active parameters are useful for estimating compute speed, but the fallback capacity check must still account for total weight memory.
Useful? React with 👍 / 👎.
| provider = grammar_fix.LLM_PROVIDER | ||
| model = str(grammar_fix.FLM_MODEL or "") | ||
| health = {"provider": provider, "model": model, "installed": None, "status": "unknown", "detail": ""} |
There was a problem hiding this comment.
Check the configured model before effective fallback
When an FLM upgrade invalidates the configured model but another model remains installed, resolve_effective_llm() replaces FLM_MODEL with that installed fallback (grammar_fix.py lines 170–173). This health check therefore examines the fallback, reports ok, and never warns that the persisted configured model was invalidated—the exact upgrade-break case this change is intended to surface. Read the configured provider profile's model for this verdict rather than the already-resolved effective model.
Useful? React with 👍 / 👎.
| <h3 class="subhead">Installed</h3> | ||
| <ul class="model-list" id="models-list" role="listbox" aria-labelledby="models-title" tabindex="0"></ul> |
There was a problem hiding this comment.
Restore keyboard selection for installed models
Replacing the native <select> with a focusable <ul role="listbox"> removes the browser's keyboard selection behavior, but no key handler is added for models-list—the new Arrow/Enter handlers apply only to pull-name. A keyboard-only user can focus this list but cannot choose any installed model other than the default selection, preventing Set as active or Remove from operating on another row. Add listbox key handling and update aria-selected/selectedModel accordingly.
Useful? React with 👍 / 👎.
2.4.1 — Model picker: honest sizing, nothing hidden, upgrade-break detection
Fixes three reported UI issues, plus a silent-failure gap found while investigating them.
Fixed
fits === "no"candidate, which made real catalog models invisible —qwen3.6-moe:35b-a3bnever appeared and had to be pulled by hand.recommend_modelsnow returns every candidate with afitstag +fit_reason; oversized ones render marked and still confirm before downloading.…:35b-a3bas 35B, not ~3B active). Fit now prefers FastFlowLM's measuredfootprint_gb— which also fixesgemma4-it:e4b(reads "4B" by name, is actually 8B / 9.1 GB) — and falls back to active-param counts for MoE.flm_listforwards the catalog metadata it previously discarded (footprint/installed/flm_min_version/parameter_size/context_length, unfiltered)._active_model_healthreportsok/not_installed/unknown, and Config → Models shows a warning naming the model and the remedy. A listing failure staysunknown— never a false alarm.Changed
Installed/Add a model/FastFlowLM runtimesubheads.<datalist>— browsers draw that popup in their own chrome and ignore page CSS entirely, so it could never match the dashboard — and a native<select size>, with plain elements: theme-aware in light and dark, keyboard navigable (↑↓/Enter/Esc), type-to-filter.Self-caught during review
The health check originally compared against an
"all"listing, but Ollama's"all"isinstalled + suggestions(ffp_provider_runtime.py:80), so a never-pulled model would have read as installed — a silent false "OK". It now trusts only authoritativedetails, else re-lists with theinstalledfilter. Pinned by a regression test (SPEC B34).Verification
app.jssyntax check (validated with a deliberate-break control)..card/input tokens in both light and dark; zero nativeselect/datalistelements remain.whisper-v3:turbocorrectly returns unknown and is still listed).SPEC V38/V39/V40, T28, B29–B34.
🤖 Generated with Claude Code