---
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`.
