uttrflow / uttrflow/uttrflow-swift
Typing can start a multi-gigabyte suggestion model download with no progress shown, when the model cache is incomplete after an idle release
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
The suggestion model is released when idle, and it is loaded again on demand. `IdleReleasingModel.isReady` is asked on a suggestion turn. If the model is not loaded, the method calls `reload()` (`Sources/UttrflowPredict/IdleRelease.swift:89-96`), which runs `model.prepare(onProgress: { _ in })`, discarding progress (`:140-152`).
`prepare` finds the weights through `LocalModel.weightsDirectory` (`Sources/UttrflowLocalModel/CachedSnapshot.swift:171-186`). When `CachedSnapshot.complete` does not accept the cache, the method falls through to `resolve(configuration: ModelConfiguration(id: identifier), from: downloader(), …)`, which downloads the repository from the Hugging Face hub. `complete` rejects a cache in these cases:
- files were removed;
- the weights are under nine tenths of the recorded size (`minimumWeightBytes`, `:167`);
- a partial first download was left behind.
So after someone clears `~/.cache/huggingface`, a cleaning utility removes it, or the first download was interrupted, the next keystrokes in a text field start a download of several GB in the background. The first download from Settings shows progress and size (#124). This one, started by typing, shows nothing, and a failure is silently swallowed (`settle`). Every later turn retries.
`Scripts/offline_audit.sh` does not look at `UttrflowPredict` or `UttrflowLocalModel`, so nothing flags this path (see the offline audit issue).
## Why it matters
A metered or tethered connection can lose gigabytes without any visible cause. On an 8 GB / 256 GB Mac, a silent download can also fill the disk. The app's promise is that nothing touches the network unless the person started a download.
## How to reproduce
1. Turn AI suggestions on and let the model download.
2. Wait for the idle release (or trigger memory pressure).
3. Delete one weights file from the model's folder in the Hugging Face cache.
4. Type in any text field.
5. Watch the network use (Activity Monitor → Network, for Uttrflow): the download restarts, and nothing in the app says so.
## Acceptance criteria
- A reload from `isReady` never downloads. It loads from a complete cache, or it fails, marks the feature as needing its model again, and shows that in Settings and the menu bar (the same state #546 asks to surface).
- Only the explicit download action in Settings may pass a real downloader.
- A test in `Tests/UttrflowPredictTests` (or `UttrflowLocalModelTests`) with an incomplete cache and a downloader that records calls asserts the typing path never calls it.
Contributor guide
Research direction
Start with IdleReleasingModel.isReady and reload() in Sources/UttrflowPredict/IdleRelease.swift:89-96,140-152, then trace cache validation in Sources/UttrflowLocalModel/CachedSnapshot.swift:167,171-186. Add or update the test in Tests/UttrflowPredictTests or UttrflowLocalModelTests using an incomplete cache and a downloader spy. Done means the typing reload path never invokes the downloader and the failed-model state is surfaced as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, swift
- Domain
- ai, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100