Phase 9 of the rebrand cascade. Sweep covers everything the Phase 8
frontend pass deliberately skipped: docs/, root markdown, scripts,
Cargo.toml descriptions, code comments that survived earlier
word-boundary sed, plus a handful of identifiers caught on the final
verify pass.
transcription-app changes:
- README.md, HANDOVER.md, KNOWN-ISSUES.md, run.sh — magnotia/Magnotia
-> lumotia/Lumotia.
- docs/ — sweep across all subdirs except docs/handovers/ (preserved
as immutable audit trail). Includes architecture-map references
to magnotia_core::*, magnotia_storage::*, etc. now pointing at
lumotia_*; dev-setup.md tracing output examples (lumotia_startup
target); brief/ + superpowers/ + issues/ + whisper-ecosystem/ +
audit/.
- Cargo.toml descriptions on 9 crates (core, audio, cloud-providers,
hotkey, llm, mcp, plus referenced others).
- crates/core/src/{error,hardware,recommendation,paths}.rs +
crates/audio/src/wav.rs + crates/llm/src/model_manager.rs +
crates/cloud-providers/src/keystore.rs + crates/mcp/src/lib.rs —
doc comments and a model-manager user-agent string.
- Caught on final pass: BroadcastChannel("magnotia_task_sync") -> ...
("lumotia_task_sync"); magnotia_locale i18n localStorage key
renamed + migration shim added; CSS keyframe names
magnotiaPulse / magnotiaBar / magnotiaFade renamed in the design-
system kit; magnotia_viewer_item / magnotia_viewer_mode handoff
keys renamed in HistoryPage + viewer/+page.svelte; src/assets/
wordmark.svg text.
- src-tauri/src/lib.rs comment cleanup ("magnotia era" was sed'd
to "lumotia era" earlier — restored).
Preserved (intentional):
- crates/core/src/paths.rs — keeps "magnotia" / "Magnotia" / ".magnotia"
legacy detection strings in legacy_and_target_paths() so the
migration shim can still find user data from the magnotia era.
- src/lib/stores/{page,focusTimer}.svelte.ts + src/lib/i18n/index.ts
— migration call sites reference the legacy magnotia keys
deliberately.
- docs/handovers/ — historical audit trail.
cargo build --workspace passes. npm run check: 0 errors / 0 warnings
(3958 files). cargo test --workspace: 339 pass / 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
9.9 KiB
Markdown
114 lines
9.9 KiB
Markdown
---
|
||
name: Model registry and runtime
|
||
type: architecture-map-page
|
||
slice: 02-tauri-runtime
|
||
last_verified: 2026/05/09
|
||
---
|
||
|
||
# `commands::models`
|
||
|
||
> **Where you are:** [Architecture map](../../README.md) → [Tauri runtime](../README.md) → [Commands](README.md) → Models
|
||
|
||
**Plain English summary.** Owns the Whisper and Parakeet model lifecycle: download with progress events, presence checks, list, load (with optional sequential-GPU coordination against the LLM), and the runtime-capabilities snapshot that Settings consumes (accelerators list, active compute device, CPU features, parallel-mode availability). Also owns the silent warm-up pass that pre-allocates the Whisper context window so the user's first transcription is fast. Emits runtime warnings on startup when the CPU lacks AVX2 / FMA or the Vulkan loader is absent.
|
||
|
||
## At a glance
|
||
|
||
- Path: `src-tauri/src/commands/models.rs`.
|
||
- LOC: 691.
|
||
- Tauri commands exposed (13 total):
|
||
- Whisper: `download_model(size)`, `check_model(size)`, `list_models()`, `load_model(size, concurrent)`, `prewarm_default_model_cmd()`, `check_engine()`, `get_runtime_capabilities()`.
|
||
- Parakeet: `download_parakeet_model(name)`, `check_parakeet_model(name)`, `list_parakeet_models()`, `load_parakeet_model(name, concurrent)`, `check_parakeet_engine()`.
|
||
- Public Rust helpers (used by other command modules): `default_model_id_for_engine`, `ensure_model_loaded`, `prewarm_default_model`, `load_model_from_disk`, `detect_active_compute_device`, `emit_runtime_warnings`.
|
||
- Events emitted: `model-download-progress` (whisper), `parakeet-download-progress`, `runtime-warning` (tagged enum: `Avx2Missing`, `VulkanLoaderMissing`, future `CudaFallback`).
|
||
- Depends on: `lumotia_transcription::{model_manager, load_whisper, load_parakeet, LocalEngine, Transcriber}`, `lumotia_core::{model_registry, hardware, constants, types}`.
|
||
- Called from frontend at: Settings → Models page (download, load, status), the dictation page boot path (`prewarm_default_model_cmd`), Settings → About (runtime capabilities).
|
||
|
||
## What's in here
|
||
|
||
### Model id mapping
|
||
|
||
- `whisper_model_id(size)` (`src-tauri/src/commands/models.rs:18`) — maps legacy size strings (`"tiny"`, `"base"`, `"small"`, `"distil-small"`, `"medium"`, `"distil-large-v3"`) to canonical `ModelId`s used by `model_registry`.
|
||
- `parakeet_model_id(name)` (`src-tauri/src/commands/models.rs:32`) — maps `"ctc-int8"` to `parakeet-ctc-0.6b-int8`.
|
||
|
||
### `load_model_from_disk` (`src-tauri/src/commands/models.rs:77`)
|
||
|
||
Looks up the registry entry, picks the engine path:
|
||
|
||
- `Engine::Whisper` (gated on `feature = "whisper"`) calls `load_whisper(&model_file)`. Without the feature, returns a clear error explaining the feature is off.
|
||
- `Engine::Parakeet` calls `load_parakeet(&dir)`.
|
||
- `Engine::Moonshine` returns "not yet supported".
|
||
|
||
Returns a `Box<dyn Transcriber + Send>`.
|
||
|
||
### `default_model_id_for_engine` (`src-tauri/src/commands/models.rs:111`)
|
||
|
||
Returns `parakeet-ctc-0.6b-int8` for `"parakeet"`, `whisper-base-en` for everything else. Used by `commands::transcription`, `commands::live`, and `prewarm_default_model`.
|
||
|
||
### `ensure_model_loaded` (`src-tauri/src/commands/models.rs:118`)
|
||
|
||
The shared model-load gateway used by both whisper and parakeet command paths. Validates the engine matches the model, checks the model is downloaded, and short-circuits if the engine already has the right model loaded. Implements the Phase A.1 sequential-GPU guard (brief item #28): when `concurrent == Some(false)` and the LLM is loaded, unloads the LLM before loading the transcription engine. The inverse guard lives in `commands::llm::load_llm_model`. Loads run inside `spawn_blocking`.
|
||
|
||
### `prewarm_default_model` (`src-tauri/src/commands/models.rs:180`)
|
||
|
||
Loads the default Whisper model (if downloaded and not already loaded) plus runs a 1-second silence pass through `transcribe_sync`. The silence run pre-allocates the Whisper context window and warms GPU shader caches so the first real user transcription completes in ≤ 1.5× steady-state latency rather than the 4–5 s cold-start path documented in `ufal/whisper_streaming` issues #96 and #135. All errors are logged, never panic.
|
||
|
||
### `prewarm_default_model_cmd` (`src-tauri/src/commands/models.rs:225`)
|
||
|
||
Tauri command wrapper. `ensure_main_window`. Triggers the warm-up. The frontend invokes this after the dictation page mounts, so warm-up runs in parallel with the first paint.
|
||
|
||
### Runtime-capabilities reporting
|
||
|
||
- `RuntimeCapabilities` (`src-tauri/src/commands/models.rs:237`) — frontend-facing struct. Contains `accelerators: Vec<String>`, `engines: Vec<EngineRuntimeCapabilities>`, `active_compute_device`, `cpu_features`, `parallel_mode_available`.
|
||
- `ActiveComputeDevice` (`:259`) — `kind`, `label`, optional `reason` (set when we fell back from a richer backend).
|
||
- `CpuFeaturesInfo` (`:270`) — avx2 / avx512f / fma / sse4_2 / neon plus a `has_ggml_baseline` shortcut.
|
||
- `EngineRuntimeCapabilities` and `ModelRuntimeCapabilities` carry per-engine and per-model status (downloaded, loaded, language support, default model id).
|
||
- `compose_accelerators` (`:337`) — pure helper combining whisper-feature flag + Vulkan-loader-availability + target family. Always starts with `"cpu"`, appends `"metal"` (macOS) or `"vulkan"` (other) only when whisper is compiled in AND the loader resolves. Replaces the old hard-coded `["cpu", "vulkan"]` (RB-07).
|
||
- `detect_active_compute_device` (`:369`) — returns the `ActiveComputeDevice` snapshot. macOS gets `"metal"` (via MoltenVK) or a CPU-fallback message; everything else gets `"vulkan"` or a CPU-fallback message naming the missing loader package.
|
||
- `emit_runtime_warnings` (`:428`) — called from `lib.rs::run` setup. Emits `runtime-warning` events for the `Avx2Missing` and `VulkanLoaderMissing` cases. The frontend renders a one-line banner.
|
||
- `get_runtime_capabilities` (`:454`) — assembles and returns the `RuntimeCapabilities` snapshot. Called by Settings.
|
||
|
||
### Whisper commands (`src-tauri/src/commands/models.rs:515`)
|
||
|
||
- `download_model(size) -> Result<String, String>` — main-window only. Calls `model_manager::download` with a closure that emits `model-download-progress`.
|
||
- `check_model(size) -> Result<bool, String>` — `model_manager::is_downloaded`.
|
||
- `list_models() -> Result<Vec<String>, String>` — returns human-readable size names of currently-downloaded whisper models.
|
||
- `load_model(size, concurrent) -> Result<String, String>` — main-window only. Calls `ensure_model_loaded`.
|
||
- `check_engine(state) -> Result<bool, String>` — returns whether the whisper engine has any model loaded.
|
||
|
||
### Parakeet commands (`src-tauri/src/commands/models.rs:576`)
|
||
|
||
Same shape as whisper, prefixed `parakeet_` and emitting `parakeet-download-progress`.
|
||
|
||
### Tests (`src-tauri/src/commands/models.rs:631`)
|
||
|
||
Five `compose_accelerators` permutations cover the RB-07 regression. Confirm:
|
||
- Whisper disabled + loader present → `["cpu"]`.
|
||
- Whisper enabled + loader missing → `["cpu"]`.
|
||
- Whisper enabled + loader present + macOS → `["cpu", "metal"]`.
|
||
- Whisper enabled + loader present + Linux/Windows → `["cpu", "vulkan"]`.
|
||
- `"cpu"` is always first (frontend index-0 contract).
|
||
|
||
## Data flow
|
||
|
||
- **Download:** frontend invokes `download_model(size)` → `model_manager::download` streams bytes → progress events fire on each chunk → frontend renders progress bar.
|
||
- **Load:** frontend invokes `load_model(size, concurrent)` → `ensure_model_loaded` validates and unloads LLM if `concurrent=false` → `load_model_from_disk` runs in `spawn_blocking` → `LocalEngine.load(model, model_id)` stashes the model on the engine.
|
||
- **Transcribe:** every transcription path calls `ensure_model_loaded` first. Idempotent when the right model is already loaded.
|
||
- **Runtime warnings:** fired once at startup via `lib.rs::run` setup → frontend listens for `runtime-warning` and shows a banner in Settings.
|
||
|
||
## Watch-outs
|
||
|
||
- **The `concurrent` flag is a tri-state.** `None` and `Some(true)` keep the legacy parallel-residency behaviour. Only `Some(false)` triggers the unload-the-other-engine guard. Live transcription explicitly passes `None`. If you ever ship a 4 GB-VRAM Settings preset that flips `concurrent=false` by default, also test the live transcription path.
|
||
- **`parallel_mode_available` is hard-wired to `false` until Phase A.4 lands a real GPU VRAM probe** (`src-tauri/src/commands/models.rs:509`). Today's frontend reads this and disables the toggle. Flag for follow-up.
|
||
- **`prewarm_default_model` only runs whisper.** Parakeet has no warm-up. The Parakeet model is also smaller and loads faster, so the cold-start gap is less obvious. If you swap Lumotia's default to Parakeet, add an equivalent warm-up.
|
||
- **Vulkan loader detection is per-platform** (`lumotia_core::hardware::vulkan_loader_available`). The CPU-fallback `reason` strings are user-visible; keep them actionable (the Linux one names `libvulkan1`, the macOS one names the Vulkan SDK runtime).
|
||
- **Whisper feature gating.** `--no-default-features` builds compile but `load_model_from_disk` returns a runtime error when the user requests a Whisper model. `list_models` will still show whisper models that happened to be downloaded already; consider hiding them when the feature is off if a no-whisper build ever ships to users.
|
||
- **Download progress events fire from a closure inside `model_manager::download`.** That closure is called from inside an async `spawn_blocking`-equivalent. The `let _ = app_clone.emit(...)` pattern silently drops emit errors; if a window has been closed mid-download the progress events stop landing but the download itself completes.
|
||
|
||
## See also
|
||
|
||
- [Transcription](transcription.md) — the consumer of `ensure_model_loaded`.
|
||
- [Live transcription](live.md) — also calls `ensure_model_loaded` (with `concurrent=None`).
|
||
- [LLM](llm.md) — the inverse sequential-GPU guard lives in `commands::llm::load_llm_model`.
|
||
- [App lifecycle](../app-lifecycle.md) — `emit_runtime_warnings` is called from setup.
|
||
- [Cargo and features](../cargo-and-features.md) — `feature = "whisper"` is what gates `load_model_from_disk`.
|