Five-slice navigable map of the entire codebase under
docs/architecture-map/. Each slice is a self-contained
breadcrumbed sub-tree:
01-frontend (16) Svelte/SvelteKit UI
02-tauri-runtime (26) src-tauri commands + lifecycle
03-audio-transcription (16) audio + transcription crates
04-llm-formatting-mcp (19) llm, ai-formatting, mcp, cloud
05-core-storage-hotkey-build core, storage, hotkey, workspace,
(26) CI, dev glue
Plus master README.md and data-flow-end-to-end.md tracing
audio bytes from microphone to FTS5 search to MCP read.
Generated by 5 parallel subagents on 2026/05/09 against
HEAD 3c47000. Each page has YAML frontmatter, file:line code
refs, sibling cross-links, plain-English summaries.
Aggregated debt surfaced (full lists in master README):
RB-08 macOS power assertion, schema head drift v14 vs v15,
VAD blocked on ort version conflict, streaming primitives
not wired into live.rs, no prompt versioning, MCP has no
auth, cloud-providers in-memory keystore, SettingsPage
2 484 LOC, commands/live.rs 1 737 LOC, dual theme system,
brand rename to Lumenote pending across the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.9 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| Whisper backend (whisper-rs) | architecture-map-page | 03-audio-transcription | 2026/05/09 |
Whisper backend (whisper-rs)
Where you are: Architecture map → Audio + Transcription → Whisper backend
Plain English summary. WhisperRsBackend owns a WhisperContext from whisper-rs 0.16 and runs Whisper directly (not via transcribe-rs). It is the only backend that pipes Magnotia's initial_prompt into the model. Each call to transcribe_sync builds a fresh WhisperState, applies language and prompt parameters, picks a thread count via the power-aware helper, and converts segment timestamps from centiseconds to seconds before returning Vec<Segment>. Vulkan GPU offload is additive behind a Cargo feature.
At a glance
- Crate:
magnotia-transcription - Path:
crates/transcription/src/whisper_rs_backend.rs - LOC: 128
- Cargo feature gate:
whisper(default on). Module is excluded entirely when the feature is off. - External deps:
whisper-rs 0.16(with optionalvulkansub-feature viawhisper-vulkan),thiserror 2,tracing 0.1. - Internal callers (best effort, slice 2 reconciles):
local_engine::load_whisper(crates/transcription/src/local_engine.rs:208) constructs it; production load path runs throughsrc-tauri/src/commands/models.rs.
Public surface:
pub struct WhisperRsBackend—crates/transcription/src/whisper_rs_backend.rs:30. Field:ctx: WhisperContext.pub fn WhisperRsBackend::load(model_path: &Path) -> std::result::Result<Self, WhisperBackendError>—crates/transcription/src/whisper_rs_backend.rs:35.pub enum WhisperBackendError { Load(String), State(String), Transcribe(String) }—crates/transcription/src/whisper_rs_backend.rs:21.thiserror-derived; convertible toMagnotiaError::TranscriptionFailedviato_string().impl Transcriber for WhisperRsBackend—crates/transcription/src/whisper_rs_backend.rs:42.
What's in here
load
whisper_rs_backend.rs:35. WhisperContext::new_with_params(model_path, WhisperContextParameters::default()). Errors map to WhisperBackendError::Load. The context is the heavy object (mmap'd GGML tensors, GPU buffers if Vulkan is active). It is held for the life of the backend.
capabilities
whisper_rs_backend.rs:43. Returns sample_rate = WHISPER_SAMPLE_RATE (slice 5 constant), channels = 1, supports_initial_prompt = true. The truth flag is what tells the Settings UI to surface the prompt field.
transcribe_sync
whisper_rs_backend.rs:56. The actual inference path:
tracing::info!boundary log capturinglanguageandhas_initial_prompt— used by the diagnostic bundle to confirm the prompt actually reached the backend.self.ctx.create_state()— freshWhisperStateper call. The header notes "state can be reused, but fresh-per-call is simpler and matches the transcribe-rs call style we are replacing".FullParams::new(SamplingStrategy::Greedy { best_of: 1 }).- Optional
params.set_language(Some(lang))ifoptions.languageis set and non-empty. - Optional
params.set_initial_prompt(prompt)ifoptions.initial_promptis set and non-empty. params.set_n_threads(inference_thread_count(Workload::Whisper, gpu_offloaded) as i32)wheregpu_offloaded = cfg!(feature = "whisper-vulkan") && vulkan_loader_available(). Power-aware (magnotia_core::tuning::inference_thread_countreads battery vs AC; helper returns a smaller count on battery to preserve runtime).set_print_special(false),set_print_progress(false),set_print_realtime(false)— keep stdout silent.state.full(params, samples)— runs inference.- Loops
state.full_n_segments()callingstate.get_segment(i), pullingseg.to_str(), and converting timestamps:start = seg.start_timestamp() as f64 * 0.01,end = seg.end_timestamp() as f64 * 0.01(whisper-rs reports centiseconds). - Returns
Vec<Segment>.
Errors all flow through MagnotiaError::TranscriptionFailed(WhisperBackendError::*.to_string()).
Data flow
samples (f32, 16 kHz mono)
options: TranscriptionOptions { language, initial_prompt }
└─ tracing::info(boundary)
└─ WhisperState (fresh per call)
└─ FullParams (greedy, best_of=1)
├─ set_language(options.language)
├─ set_initial_prompt(options.initial_prompt)
├─ set_n_threads(inference_thread_count(Whisper, gpu_offloaded))
└─ silence stdout flags
└─ state.full(params, samples)
└─ for i in 0..state.full_n_segments():
Segment { start = t0 * 0.01, end = t1 * 0.01, text = seg.to_str() }
→ Vec<Segment>
Watch-outs
whisper-rs-sysis the C++ link target. Brief item #6 documents the historical Whispering v7.11.0 failure linkingwhisper-rs-sys+tokenizerstogether on Windows MSVC. Thebuild.rsguard panics iftokenizersever lands in the workspace lockfile on a Windows target. See build-tokenizers-guard.md.- Vulkan is detected at runtime, not just compile time.
cfg!(feature = "whisper-vulkan")is the static check;vulkan_loader_available()(slice 5magnotia_core::hardware) confirmslibvulkan.soactually resolves. If the feature is on but the loader is missing, the helper falls back to CPU thread count. - Fresh state per call has a cost.
state.fullwarm-up is lower thanWhisperContextload but non-zero. Brief comment hints reuse is possible. Worth measuring once the live-streaming path is dogfooded. - No diarisation, no temperature fallback, no token suppression flags. Greedy with
best_of: 1is the simplest correct path. Tuning later is a single function. - Timestamp units. whisper-rs returns centiseconds (10 ms). Multiplying by
0.01gives seconds. A prior version that used milliseconds or token offsets would slice timing wrong; tests should pin this. initial_promptis the differentiator fromtranscribe-rs. The whole point of writing this backend (vs going throughSpeechModelAdapter) is that the adapter cannot pipe the prompt. Anyone tempted to "simplify" by routing Whisper through the adapter would silently drop the user-vocabulary feature.- Tracing field name typo immune.
language = ?options.languageuses Debug formatting. IfLanguageever stops implementingDebug, this breaks at compile time, not at runtime.
See also
- Transcription engines overview — the
Transcribertrait this implements. - Cargo features —
whisperandwhisper-vulkanflag matrix. - Build tokenizers guard — the Windows MSVC-CRT defence linked to this backend.
- Tests and fixtures —
whisper_rs_smoke.rsexercises the load + transcribe + initial-prompt path;jfk_bench.rsmeasures cold/warm RTF;thread_sweep.rsvalidates thread-count scaling. magnotia_core::hardware::vulkan_loader_available,magnotia_core::tuning::{inference_thread_count, Workload}(slice 5).