--- 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: `magnotia_transcription::{model_manager, load_whisper, load_parakeet, LocalEngine, Transcriber}`, `magnotia_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`. ### `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`, `engines: Vec`, `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` — main-window only. Calls `model_manager::download` with a closure that emits `model-download-progress`. - `check_model(size) -> Result` — `model_manager::is_downloaded`. - `list_models() -> Result, String>` — returns human-readable size names of currently-downloaded whisper models. - `load_model(size, concurrent) -> Result` — main-window only. Calls `ensure_model_loaded`. - `check_engine(state) -> Result` — 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 Magnotia's default to Parakeet, add an equivalent warm-up. - **Vulkan loader detection is per-platform** (`magnotia_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`.