2.5.1: Re-download button for installed models - #44
Conversation
Adds Config -> Models -> "Re-download..." for a model that is already installed. This is needed after a FastFlowLM upgrade invalidates local weights: the model still lists as installed but the runtime rejects it, and the ordinary Download button is a no-op because `flm pull` only fetches a model that is MISSING (its own help text says so). For FastFlowLM a forced pull is therefore remove-then-pull, which is destructive if the download then fails -- the UI confirms first, and the error states plainly that the old copy is gone and the pull must be retried. Ollama is left to its own `pull`, which already re-fetches when the remote digest changes, so nothing is deleted there. Threaded `force` through pull_start/pull_model and the daemon actions. Live-verified end to end against a bogus model name so no real weights were touched: forced=True, remove-then-pull ran, and the destructive-failure warning appeared, with both installed models intact afterwards. Also ships the two fixes already merged for this release (B51 provider output capture, B52 stale update cache) and bumps the version across _version.py, pyproject.toml, installer.iss, README.md, sign.ps1's example, and the version-sync test. SPEC: V65 (force-pull semantics), T41. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 486e1346d7
ℹ️ 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".
| const name = selectedModel; | ||
| if (!name) { | ||
| setStatus("config-status", "Pick an installed model first.", false); |
There was a problem hiding this comment.
Allow invalidated active models to be re-downloaded
When an FLM upgrade invalidates the active model, _active_model_health explicitly handles it as installed: false, so models_installed omits that model and renderInstalledModels resets selectedModel to another installed model or an empty string. Because this new handler can only force-pull selectedModel, the alert's instruction to select the invalidated model below cannot be followed, and the exact upgrade-recovery scenario this button targets remains unavailable. The invalidated active model needs to remain selectable or be passed directly from the health result.
Useful? React with 👍 / 👎.
| removed = subprocess.run( | ||
| [cli, "remove", model], | ||
| capture_output=True, text=True, encoding="utf-8", errors="replace", | ||
| creationflags=no_window, check=False, | ||
| ) |
There was a problem hiding this comment.
Stop when the forced removal fails
If flm remove fails because the model is locked, inaccessible, or cannot be deleted, its nonzero returncode is ignored and the pull proceeds. Since the implementation itself notes that pulling a still-present FLM model is a successful no-op, this path can finish with state="done" and report a fresh download even though the stale copy was never removed or replaced. Only a confirmed not-installed result should be tolerated; other removal failures should terminate the forced pull with the captured diagnostic.
Useful? React with 👍 / 👎.
Cuts 2.5.1. Everything here came out of a live debugging session on a real machine, not from a backlog.
Added — Re-download… for an installed model
Config → Models gains a Re-download… button.
The need is concrete: a FastFlowLM upgrade invalidates weights you already pulled. The model still lists as installed, but the runtime rejects it — and the ordinary Download button does nothing, because
flm pullonly fetches a model that is missing (its own help text:pull <model_tag> - Download model files if not present). There was no way to force fresh weights from the UI.So a forced pull is remove-then-pull for FastFlowLM, which makes it destructive if the download then fails:
Ollama is deliberately left to its own
pull, which already re-fetches when the remote digest changes — nothing is deleted there.forceis threaded throughpull_start/pull_modeland both daemon actions.Live-verified end to end against a bogus model name so no real weights were touched:
forced=True, remove-then-pull ran, the destructive-failure warning appeared, and both real models were intact afterwards.Also in this release (already merged)
server.log_to_filedefaults to false, and on that path the server's output went nowhere, so a refused start surfaced only asexited early (exit 1). Output is now always captured and its tail appended to the error.Version bump
_version.py,pyproject.toml,installer/installer.iss,README.md,installer/sign.ps1example, and the hardcoded set intests/test_version_sync.py. CHANGELOG + README notes added.SPEC
Test plan
ruff check .clean; full suite green — 550 tests, +5 new for force-pullforcedin status, destructive-failure wording, non-forced failure not claiming removal, Ollama not removing first, FLM removing before pullingtest_version_syncpasses on 2.5.1iscccompile via branch dispatch — PR CI never runs Inno Setup, which is how a Pascal error reached a tag twice during 2.5.0; dispatched on this branch before merge🤖 Generated with Claude Code