agent: lumotia-rebrand — docs, scripts, root config, residuals
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>
This commit is contained in:
@@ -9,19 +9,19 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → Audio + Transcription
|
||||
|
||||
**Plain English summary.** Two Rust workspace crates form the backbone of Magnotia's speech-to-text path. `magnotia-audio` captures microphone input via `cpal`, decodes audio files via `symphonia`, resamples to 16 kHz mono via `rubato`, and writes WAV files via `hound`. `magnotia-transcription` loads Whisper or Parakeet models, runs inference, and provides streaming primitives (VAD chunking, LocalAgreement-n commit policy, commit-bounded buffer trim) so live captures stay responsive.
|
||||
**Plain English summary.** Two Rust workspace crates form the backbone of Lumotia's speech-to-text path. `lumotia-audio` captures microphone input via `cpal`, decodes audio files via `symphonia`, resamples to 16 kHz mono via `rubato`, and writes WAV files via `hound`. `lumotia-transcription` loads Whisper or Parakeet models, runs inference, and provides streaming primitives (VAD chunking, LocalAgreement-n commit policy, commit-bounded buffer trim) so live captures stay responsive.
|
||||
|
||||
## At a glance
|
||||
|
||||
| Crate | LOC (`src/`) | Purpose |
|
||||
|---|---|---|
|
||||
| `magnotia-audio` | 1,533 | Capture, VAD stub, resample, decode, WAV I/O |
|
||||
| `magnotia-transcription` | 2,266 (incl. tests + build) | Engines, model manager, streaming primitives |
|
||||
| `lumotia-audio` | 1,533 | Capture, VAD stub, resample, decode, WAV I/O |
|
||||
| `lumotia-transcription` | 2,266 (incl. tests + build) | Engines, model manager, streaming primitives |
|
||||
|
||||
Public crate surface (re-exports from `lib.rs`):
|
||||
|
||||
```rust
|
||||
// magnotia-audio
|
||||
// lumotia-audio
|
||||
pub use capture::{AudioChunk, CaptureRuntimeError, DeviceInfo, MicrophoneCapture};
|
||||
pub use concurrency::decode_and_resample;
|
||||
pub use decode::{decode_audio_file, decode_audio_file_limited, probe_audio_duration_secs};
|
||||
@@ -30,7 +30,7 @@ pub use streaming_resample::StreamingResampler;
|
||||
pub use vad::SpeechDetector;
|
||||
pub use wav::{read_wav, write_wav, WavWriter};
|
||||
|
||||
// magnotia-transcription
|
||||
// lumotia-transcription
|
||||
pub use concurrency::run_inference;
|
||||
#[cfg(feature = "whisper")]
|
||||
pub use local_engine::load_whisper;
|
||||
@@ -57,7 +57,7 @@ External deps that matter:
|
||||
- `tracing 0.1` — backend boundary observability.
|
||||
- `voice_activity_detector` / `silero-vad-rust` — **deferred** (ort 2.0.0-rc.10 vs 2.0.0-rc.12 conflict; see `audio-vad.md`).
|
||||
|
||||
Cargo feature matrix (`magnotia-transcription`):
|
||||
Cargo feature matrix (`lumotia-transcription`):
|
||||
|
||||
| Feature | Default | Gates |
|
||||
|---|---|---|
|
||||
@@ -65,7 +65,7 @@ Cargo feature matrix (`magnotia-transcription`):
|
||||
| `whisper-vulkan` | yes | `whisper-rs/vulkan` (Vulkan GPU offload) |
|
||||
| Parakeet (`transcribe-rs`) | always on | unconditional dep, no feature flag |
|
||||
|
||||
`magnotia-audio` has no Cargo features.
|
||||
`lumotia-audio` has no Cargo features.
|
||||
|
||||
## Map of this slice
|
||||
|
||||
@@ -79,7 +79,7 @@ Cargo feature matrix (`magnotia-transcription`):
|
||||
- [`transcription-whisper.md`](transcription-whisper.md) — `WhisperRsBackend`, `WhisperContext`, params, `initial_prompt`, GPU offload.
|
||||
- [`transcription-parakeet.md`](transcription-parakeet.md) — `SpeechModelAdapter` + `ParakeetWordGranularity`.
|
||||
- [`transcription-streaming.md`](transcription-streaming.md) — `VadChunker` trait, `RmsVadChunker`, `LocalAgreement`, `trim_buffer_to_commit_point`.
|
||||
- [`transcription-model-manager.md`](transcription-model-manager.md) — download flow, SHA verify, `.magnotia-verified` manifest, Range resume.
|
||||
- [`transcription-model-manager.md`](transcription-model-manager.md) — download flow, SHA verify, `.lumotia-verified` manifest, Range resume.
|
||||
- [`transcription-concurrency.md`](transcription-concurrency.md) — `run_inference` async wrapper around `spawn_blocking`.
|
||||
- [`cargo-features.md`](cargo-features.md) — feature matrix, build commands, rationale.
|
||||
- [`build-tokenizers-guard.md`](build-tokenizers-guard.md) — `build.rs` Windows-MSVC-CRT guard against `tokenizers`.
|
||||
@@ -90,13 +90,13 @@ Cargo feature matrix (`magnotia-transcription`):
|
||||
- **Slice 2 (Tauri runtime)** owns `src-tauri/src/commands/{audio,transcription,live,models}.rs`. Those wrappers call into this slice via the `pub use` exports above. The live command in particular drives `MicrophoneCapture` + `StreamingResampler` + `RmsVadChunker` + `LocalAgreement` + `WavWriter` together. This crate publishes the primitives; slice 2 publishes the orchestrator.
|
||||
- **Slice 4 (LLM + AI formatting)** runs after this slice produces a `Transcript`. It consumes the segment text via the storage layer, never directly. No type traffic flows back into this slice from formatting.
|
||||
- **Slice 5 (core / storage / hotkey / build)** provides the shared types this slice depends on:
|
||||
- `magnotia_core::error::{MagnotiaError, Result}` — error envelope.
|
||||
- `magnotia_core::types::{AudioSamples, Segment, Transcript, TranscriptionOptions, EngineName, ModelId, DownloadProgress, Megabytes}`.
|
||||
- `magnotia_core::constants::{WHISPER_SAMPLE_RATE, VAD_SPEECH_THRESHOLD}`.
|
||||
- `magnotia_core::hardware::vulkan_loader_available` — runtime Vulkan probe used by `WhisperRsBackend`.
|
||||
- `magnotia_core::tuning::{inference_thread_count, Workload}` — power-aware thread count picker.
|
||||
- `magnotia_core::paths::app_paths` — `models_dir()` / `speech_model_dir()` resolution.
|
||||
- `magnotia_core::model_registry::{find_model, all_models, ModelEntry, ModelFile}` — declarative model catalogue.
|
||||
- `lumotia_core::error::{MagnotiaError, Result}` — error envelope.
|
||||
- `lumotia_core::types::{AudioSamples, Segment, Transcript, TranscriptionOptions, EngineName, ModelId, DownloadProgress, Megabytes}`.
|
||||
- `lumotia_core::constants::{WHISPER_SAMPLE_RATE, VAD_SPEECH_THRESHOLD}`.
|
||||
- `lumotia_core::hardware::vulkan_loader_available` — runtime Vulkan probe used by `WhisperRsBackend`.
|
||||
- `lumotia_core::tuning::{inference_thread_count, Workload}` — power-aware thread count picker.
|
||||
- `lumotia_core::paths::app_paths` — `models_dir()` / `speech_model_dir()` resolution.
|
||||
- `lumotia_core::model_registry::{find_model, all_models, ModelEntry, ModelFile}` — declarative model catalogue.
|
||||
- Storage (slice 5) writes the produced `Transcript` to disk; this slice does not call into it directly.
|
||||
|
||||
## Open questions / debt
|
||||
@@ -116,7 +116,7 @@ Cargo feature matrix (`magnotia-transcription`):
|
||||
- `docs/whisper-ecosystem/brief.md` — top-level Whisper-ecosystem audit (the items #6, #8, #13, #19, #21, #24, #25, #26 referenced throughout this slice).
|
||||
- `docs/whisper-ecosystem/workstream-A.md` — VAD / streaming roadmap (the source of `RmsVadChunker` thresholds).
|
||||
- `docs/whisper-ecosystem/workstream-B.md` — UI commit/tentative contract that drives `LocalAgreement`.
|
||||
- `docs/whisper-ecosystem/magnotia-context.md` — context summary for the workstreams.
|
||||
- `docs/whisper-ecosystem/lumotia-context.md` — context summary for the workstreams.
|
||||
- `docs/code-review-2026-04-22.md` — audit pass that flagged decode.rs RB-09 and `read_wav` filter_map bug (both fixed in tree).
|
||||
- `docs/issues/decoder-partial-audio-on-error.md` — the RB-09 ticket.
|
||||
- `docs/issues/native-capture-worker-join.md`, `c1-live-session-race.md`, `run-live-session-monolith.md` — slice 2's live-session debt; they reference primitives in this slice.
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-audio`
|
||||
- Crate: `lumotia-audio`
|
||||
- Path: `crates/audio/src/capture.rs`
|
||||
- LOC: 583
|
||||
- External deps: `cpal 0.17`, `serde 1` (DeviceInfo wire type)
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-audio`
|
||||
- Crate: `lumotia-audio`
|
||||
- Paths: `crates/audio/src/decode.rs` (283 LOC), `crates/audio/src/concurrency.rs` (19 LOC).
|
||||
- External deps: `symphonia 0.5` with features `mp3, aac, flac, pcm, vorbis, wav, ogg, isomp4`.
|
||||
- Internal callers (best effort, slice 2 reconciles): the import-file Tauri command and the file-mode transcription command call `decode_and_resample`. Standalone `decode_audio_file` is exposed for tests and any path that wants the native rate.
|
||||
@@ -82,5 +82,5 @@ Path
|
||||
|
||||
- [Audio resampling](audio-resampling.md) — `resample_to_16khz` is the second half of `decode_and_resample`.
|
||||
- [Audio WAV I/O](audio-wav-io.md) — `read_wav` is the WAV-only fast path that uses `hound` instead of symphonia.
|
||||
- `magnotia_core::types::AudioSamples` (slice 5) — the value type returned.
|
||||
- `lumotia_core::types::AudioSamples` (slice 5) — the value type returned.
|
||||
- Tests at `decode.rs:175` (RB-09 regression among others).
|
||||
|
||||
@@ -17,7 +17,7 @@ last_verified: 2026/05/09
|
||||
- Path: `static/pcm-processor.js`
|
||||
- LOC: 44
|
||||
- External deps: none (Web Audio API).
|
||||
- Internal callers (best effort, slice 1 reconciles): the Svelte frontend instantiates it with `audioContext.audioWorklet.addModule("/pcm-processor.js")` and listens for `port.message` events. The native cpal path in `magnotia-audio` is the production path; this exists for parity in dev/web contexts.
|
||||
- Internal callers (best effort, slice 1 reconciles): the Svelte frontend instantiates it with `audioContext.audioWorklet.addModule("/pcm-processor.js")` and listens for `port.message` events. The native cpal path in `lumotia-audio` is the production path; this exists for parity in dev/web contexts.
|
||||
|
||||
Public surface: registers the AudioWorklet processor name `pcm-processor`.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-audio`
|
||||
- Crate: `lumotia-audio`
|
||||
- Paths: `crates/audio/src/resample.rs` (100 LOC), `crates/audio/src/streaming_resample.rs` (211 LOC).
|
||||
- External deps: `rubato 0.15` (sinc interpolation).
|
||||
- Internal callers (best effort, slice 2 reconciles): `concurrency::decode_and_resample` (file path used by `audio.rs` and `transcription.rs` Tauri commands), `live.rs` Tauri command (live path).
|
||||
@@ -103,4 +103,4 @@ cpal AudioChunk (native rate, possibly stereo, downmixed by caller)
|
||||
|
||||
- [Audio capture pipeline](audio-capture-pipeline.md) — produces the `AudioChunk`s that feed `StreamingResampler`.
|
||||
- [Audio file decoding](audio-file-decoding.md) — `decode_and_resample` chains decode + `resample_to_16khz`.
|
||||
- `magnotia_core::constants::WHISPER_SAMPLE_RATE` (slice 5) — single source of truth for the target rate.
|
||||
- `lumotia_core::constants::WHISPER_SAMPLE_RATE` (slice 5) — single source of truth for the target rate.
|
||||
|
||||
@@ -9,14 +9,14 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → [Audio + Transcription](README.md) → Audio VAD
|
||||
|
||||
**Plain English summary.** `magnotia-audio` exposes a `SpeechDetector` type, but right now its `is_speech()` returns `true` for every input. This is a deliberate stub: both candidate Silero VAD bindings pin an older `ort` version than `transcribe-rs` (Parakeet) requires, so adding either today would force a workspace-wide downgrade. Live capture uses the energy-based `RmsVadChunker` from the transcription crate as the working fallback until the `ort` ecosystem aligns.
|
||||
**Plain English summary.** `lumotia-audio` exposes a `SpeechDetector` type, but right now its `is_speech()` returns `true` for every input. This is a deliberate stub: both candidate Silero VAD bindings pin an older `ort` version than `transcribe-rs` (Parakeet) requires, so adding either today would force a workspace-wide downgrade. Live capture uses the energy-based `RmsVadChunker` from the transcription crate as the working fallback until the `ort` ecosystem aligns.
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-audio`
|
||||
- Crate: `lumotia-audio`
|
||||
- Path: `crates/audio/src/vad.rs`
|
||||
- LOC: 35
|
||||
- External deps: none (pulls `VAD_SPEECH_THRESHOLD` from `magnotia_core::constants`).
|
||||
- External deps: none (pulls `VAD_SPEECH_THRESHOLD` from `lumotia_core::constants`).
|
||||
- Internal callers (best effort): unused in production today. The threshold is read but never compared, because `is_speech` always returns `true`.
|
||||
|
||||
Public surface:
|
||||
@@ -51,5 +51,5 @@ The threshold comes from `VAD_SPEECH_THRESHOLD` (slice 5) and is currently dead
|
||||
## See also
|
||||
|
||||
- [Transcription streaming](transcription-streaming.md) — `RmsVadChunker`, the actual gating today.
|
||||
- `magnotia_core::constants::VAD_SPEECH_THRESHOLD` (slice 5) — wired but unused.
|
||||
- `Cargo.toml` of `magnotia-audio` — the commented-out `silero-vad-rust = "6"` line documents the deferred dep.
|
||||
- `lumotia_core::constants::VAD_SPEECH_THRESHOLD` (slice 5) — wired but unused.
|
||||
- `Cargo.toml` of `lumotia-audio` — the commented-out `silero-vad-rust = "6"` line documents the deferred dep.
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-audio`
|
||||
- Crate: `lumotia-audio`
|
||||
- Path: `crates/audio/src/wav.rs`
|
||||
- LOC: 287
|
||||
- External deps: `hound 3.5`.
|
||||
|
||||
@@ -9,11 +9,11 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → [Audio + Transcription](README.md) → Build script
|
||||
|
||||
**Plain English summary.** `magnotia-transcription/build.rs` reads `Cargo.lock` and refuses to compile on Windows if the `tokenizers` crate ever lands in the workspace dependency graph. Linking `whisper-rs-sys` and `tokenizers` together has been a repeated MSVC C-runtime conflict (Whispering v7.11.0 shipped a broken Windows build over exactly this). On non-Windows the check is a `cargo:warning`, not a fatal error, so the failure surfaces at CI build time rather than waiting for a Windows ship.
|
||||
**Plain English summary.** `lumotia-transcription/build.rs` reads `Cargo.lock` and refuses to compile on Windows if the `tokenizers` crate ever lands in the workspace dependency graph. Linking `whisper-rs-sys` and `tokenizers` together has been a repeated MSVC C-runtime conflict (Whispering v7.11.0 shipped a broken Windows build over exactly this). On non-Windows the check is a `cargo:warning`, not a fatal error, so the failure surfaces at CI build time rather than waiting for a Windows ship.
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Path: `crates/transcription/build.rs`
|
||||
- LOC: 73
|
||||
- External deps: stdlib only (`std::env`, `std::fs`).
|
||||
@@ -52,13 +52,13 @@ fn main() {
|
||||
|
||||
if target_os == "windows" {
|
||||
panic!(
|
||||
"magnotia-transcription: the `tokenizers` crate appears in Cargo.lock and this is a \
|
||||
"lumotia-transcription: the `tokenizers` crate appears in Cargo.lock and this is a \
|
||||
Windows build. ..."
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
"cargo:warning=magnotia-transcription: `tokenizers` crate is in the dependency graph. \
|
||||
"cargo:warning=lumotia-transcription: `tokenizers` crate is in the dependency graph. \
|
||||
This build is non-Windows so the link will succeed, but Windows builds will panic ..."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → [Audio + Transcription](README.md) → Cargo features
|
||||
|
||||
**Plain English summary.** `magnotia-audio` has no Cargo features. `magnotia-transcription` has two: `whisper` (default on, gates `whisper-rs`, `whisper_rs_backend.rs`, and `load_whisper`) and `whisper-vulkan` (default on, additive Vulkan GPU offload). Parakeet is unconditional. The defaults give a desktop GPU build; non-Vulkan or cloud-only configurations turn features off explicitly.
|
||||
**Plain English summary.** `lumotia-audio` has no Cargo features. `lumotia-transcription` has two: `whisper` (default on, gates `whisper-rs`, `whisper_rs_backend.rs`, and `load_whisper`) and `whisper-vulkan` (default on, additive Vulkan GPU offload). Parakeet is unconditional. The defaults give a desktop GPU build; non-Vulkan or cloud-only configurations turn features off explicitly.
|
||||
|
||||
## At a glance
|
||||
|
||||
@@ -24,26 +24,26 @@ last_verified: 2026/05/09
|
||||
| `whisper` | yes | `whisper-rs 0.16` (optional dep) | `whisper_rs_backend` module, `load_whisper` re-export, the `set_initial_prompt` path |
|
||||
| `whisper-vulkan` | yes | `whisper-rs?/vulkan` | Compile-time GPU offload. Runtime gated additionally on `vulkan_loader_available()` |
|
||||
|
||||
Other deps are unconditional (always present): `transcribe-rs 0.3` (Parakeet), `magnotia-core`, `tokio`, `reqwest`, `futures-util`, `sha2`, `thiserror`, `tracing`. Dev-only: `tempfile`, `num_cpus` (used by `thread_sweep.rs` to label physical vs logical core counts).
|
||||
Other deps are unconditional (always present): `transcribe-rs 0.3` (Parakeet), `lumotia-core`, `tokio`, `reqwest`, `futures-util`, `sha2`, `thiserror`, `tracing`. Dev-only: `tempfile`, `num_cpus` (used by `thread_sweep.rs` to label physical vs logical core counts).
|
||||
|
||||
## Build commands
|
||||
|
||||
Default (Whisper + Vulkan):
|
||||
|
||||
```sh
|
||||
cargo build -p magnotia-transcription
|
||||
cargo build -p lumotia-transcription
|
||||
```
|
||||
|
||||
Whisper without Vulkan (CPU-only desktop, Android without GPU drivers):
|
||||
|
||||
```sh
|
||||
cargo build -p magnotia-transcription --no-default-features --features whisper
|
||||
cargo build -p lumotia-transcription --no-default-features --features whisper
|
||||
```
|
||||
|
||||
No Whisper at all (Parakeet only — useful for shrinking the binary and dropping `whisper-rs-sys`):
|
||||
|
||||
```sh
|
||||
cargo build -p magnotia-transcription --no-default-features
|
||||
cargo build -p lumotia-transcription --no-default-features
|
||||
```
|
||||
|
||||
The `--no-default-features` form drops the `whisper_rs_backend` module entirely, so any sibling code holding a `WhisperRsBackend` will fail to compile. `LocalEngine`, `SpeechModelAdapter`, `load_parakeet`, and the streaming primitives stay available.
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription` (mostly) and `magnotia-audio` (inline only).
|
||||
- Crate: `lumotia-transcription` (mostly) and `lumotia-audio` (inline only).
|
||||
- Paths:
|
||||
- `crates/audio/src/{capture,decode,wav,resample,streaming_resample}.rs` — inline `#[cfg(test)]` modules.
|
||||
- `crates/transcription/src/{model_manager,local_engine,transcriber,streaming/*}.rs` — inline `#[cfg(test)]` modules.
|
||||
@@ -24,7 +24,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## Inline unit tests (selected highlights)
|
||||
|
||||
### `magnotia-audio`
|
||||
### `lumotia-audio`
|
||||
|
||||
- `capture.rs:570` — `monitor_pattern_detection` for the PulseAudio / PipeWire heuristics.
|
||||
- `decode.rs:262` — `mid_stream_io_error_propagates_instead_of_returning_partial_audio`. RB-09 regression: a flaky `MediaSource` (`FlakyCursor`, `decode.rs:203`) injects an I/O error after the probe header and asserts the function errors instead of returning `Ok(partial)`.
|
||||
@@ -33,7 +33,7 @@ last_verified: 2026/05/09
|
||||
- `streaming_resample.rs:182` — `streaming_48k_to_16k_preserves_duration`. Pushes 1 s of 48 kHz audio in 700-sample chunks (forcing the residual-buffer path) and asserts the output is within 50 ms of 1 s at 16 kHz.
|
||||
- `resample.rs:84` — `resample_preserves_approximate_duration`.
|
||||
|
||||
### `magnotia-transcription`
|
||||
### `lumotia-transcription`
|
||||
|
||||
- `streaming/rms_vad.rs:371`–`:734` — comprehensive state-machine coverage: silence, hysteresis, onset gating, max-chunk continuity (`max_chunk_split_preserves_audio_contiguity`), padded-final-frame regression (`flush_preserves_hit_max_chunk_from_padded_final_frame`, `flush_preserves_end_of_utterance_chunk_from_padded_final_frame` — both 2026-04-22 CRITICAL C2 fixes), idempotent flush.
|
||||
- `streaming/commit_policy.rs:212`–`:402` — LocalAgreement-n correctness: text-only equality, non-shrinkage invariant, defensive shorter-pass handling (`shorter_pass_after_commit_does_not_panic`, regression for the `latest[committed_count..]` panic), n=3 behaviour.
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Path: `crates/transcription/src/concurrency.rs`
|
||||
- LOC: 19
|
||||
- External deps: `tokio` (rt feature for `spawn_blocking`).
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Paths: `crates/transcription/src/transcriber.rs` (62 LOC), `crates/transcription/src/local_engine.rs` (226 LOC).
|
||||
- External deps: `transcribe-rs 0.3` (onnx feature). `whisper-rs 0.16` is feature-gated and lives in the next page.
|
||||
- Internal callers (best effort, slice 2 reconciles): `src-tauri/src/commands/models.rs` calls `load_whisper` / `load_parakeet` and `LocalEngine::load`. `src-tauri/src/commands/transcription.rs` (and `live.rs`) call `LocalEngine::transcribe_sync` indirectly via `concurrency::run_inference`.
|
||||
@@ -104,4 +104,4 @@ options: TranscriptionOptions (language, initial_prompt)
|
||||
- [Transcription parakeet](transcription-parakeet.md) — the transcribe-rs Parakeet wrapper.
|
||||
- [Transcription concurrency](transcription-concurrency.md) — `run_inference` async wrapper.
|
||||
- [Transcription streaming](transcription-streaming.md) — VAD chunker + LocalAgreement stack that drives live capture.
|
||||
- `magnotia_core::types::{Segment, Transcript, TranscriptionOptions, EngineName, ModelId}` (slice 5).
|
||||
- `lumotia_core::types::{Segment, Transcript, TranscriptionOptions, EngineName, ModelId}` (slice 5).
|
||||
|
||||
@@ -9,19 +9,19 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → [Audio + Transcription](README.md) → Model manager
|
||||
|
||||
**Plain English summary.** Speech models live on disk under a per-platform path. `model_manager` resolves those paths, checks "is this model fully downloaded", lists what is downloaded, and downloads missing files with HTTP Range resume + per-chunk SHA256 verification. A single in-process `HashSet` reservation prevents concurrent downloads of the same model. After a successful download a `.magnotia-verified` manifest is written so a subsequent process restart can short-circuit re-hashing.
|
||||
**Plain English summary.** Speech models live on disk under a per-platform path. `model_manager` resolves those paths, checks "is this model fully downloaded", lists what is downloaded, and downloads missing files with HTTP Range resume + per-chunk SHA256 verification. A single in-process `HashSet` reservation prevents concurrent downloads of the same model. After a successful download a `.lumotia-verified` manifest is written so a subsequent process restart can short-circuit re-hashing.
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Path: `crates/transcription/src/model_manager.rs`
|
||||
- LOC: 617 (production code + extensive in-tree download server tests)
|
||||
- External deps: `reqwest 0.12` (rustls-tls, stream), `futures-util 0.3` (StreamExt), `sha2 0.10`. Path resolution comes from `magnotia_core::paths::app_paths`. Catalogue from `magnotia_core::model_registry`.
|
||||
- External deps: `reqwest 0.12` (rustls-tls, stream), `futures-util 0.3` (StreamExt), `sha2 0.10`. Path resolution comes from `lumotia_core::paths::app_paths`. Catalogue from `lumotia_core::model_registry`.
|
||||
- Internal callers (best effort, slice 2 reconciles): the `models` Tauri command consumes all five public functions. The frontend Settings → Models view drives `download` with a progress callback.
|
||||
|
||||
Public surface:
|
||||
|
||||
- `pub fn models_dir() -> PathBuf` — `crates/transcription/src/model_manager.rs:42`. Resolves to `%LOCALAPPDATA%/magnotia/models` on Windows, `~/.magnotia/models` on Unix (delegates to `magnotia_core::paths::app_paths`).
|
||||
- `pub fn models_dir() -> PathBuf` — `crates/transcription/src/model_manager.rs:42`. Resolves to `%LOCALAPPDATA%/lumotia/models` on Windows, `~/.lumotia/models` on Unix (delegates to `lumotia_core::paths::app_paths`).
|
||||
- `pub fn model_dir(id: &ModelId) -> PathBuf` — `crates/transcription/src/model_manager.rs:47`.
|
||||
- `pub fn is_downloaded(id: &ModelId) -> bool` — `crates/transcription/src/model_manager.rs:52`.
|
||||
- `pub fn list_downloaded() -> Vec<ModelId>` — `crates/transcription/src/model_manager.rs:63`.
|
||||
@@ -38,7 +38,7 @@ Private helpers:
|
||||
|
||||
### Path resolution
|
||||
|
||||
`models_dir` and `model_dir` defer entirely to `magnotia_core::paths::app_paths()`. Anything platform-specific lives in slice 5; this crate just consumes the resolution.
|
||||
`models_dir` and `model_dir` defer entirely to `lumotia_core::paths::app_paths()`. Anything platform-specific lives in slice 5; this crate just consumes the resolution.
|
||||
|
||||
### Download reservation
|
||||
|
||||
@@ -49,13 +49,13 @@ Private helpers:
|
||||
`model_manager.rs:52`. Two-step:
|
||||
|
||||
1. Every file declared in the registry entry must exist at `model_dir(id).join(filename)`.
|
||||
2. The verified-manifest at `model_dir.join(".magnotia-verified")` must record the same `filename\tsha256\tsize` triple per file.
|
||||
2. The verified-manifest at `model_dir.join(".lumotia-verified")` must record the same `filename\tsha256\tsize` triple per file.
|
||||
|
||||
A missing or stale manifest causes `is_downloaded` to return `false` even if the bytes are correct. The next `download` call will revalidate and rewrite the manifest. A truncated or tampered file fails the existence + size check, so a subsequent `download` redownloads it.
|
||||
|
||||
### `list_downloaded`
|
||||
|
||||
`model_manager.rs:63`. Filters the static `magnotia_core::model_registry::all_models()` catalogue by `is_downloaded`.
|
||||
`model_manager.rs:63`. Filters the static `lumotia_core::model_registry::all_models()` catalogue by `is_downloaded`.
|
||||
|
||||
### `download` (the orchestrator)
|
||||
|
||||
@@ -114,7 +114,7 @@ ModelId
|
||||
|
||||
## Watch-outs
|
||||
|
||||
- **Reservation is per-process.** Two Magnotia instances (dev + release, two cargo runs) racing on the same model dir can collide. The `.part → dest` atomic rename is the only thing standing between them and a torn file.
|
||||
- **Reservation is per-process.** Two Lumotia instances (dev + release, two cargo runs) racing on the same model dir can collide. The `.part → dest` atomic rename is the only thing standing between them and a torn file.
|
||||
- **`DownloadProgress` is emitted per percent step, not per byte.** A 10 MB file at 4G download speeds emits ~100 events; a 5 GB file emits the same 100 events. UI must not assume tight cadence.
|
||||
- **Manifest schema is `version\t1` + `filename\tsha256\tsize` lines.** Bumping the version is a manual operation; a stale manifest after a registry update will simply trigger a redownload (waste, not corruption).
|
||||
- **Cross-process file locking is not used.** A user opening Settings → Models in two windows of the same desktop session will trip the in-process reservation, but two physical machines syncing the same network home would not be detected.
|
||||
@@ -127,6 +127,6 @@ ModelId
|
||||
|
||||
- [Transcription engines overview](transcription-engines-overview.md) — `load_whisper` / `load_parakeet` are called against `model_dir(id)` outputs.
|
||||
- [Tests and fixtures](tests-and-fixtures.md) — in-tree TcpListener-backed servers that exercise the resume + sha-mismatch + 5xx paths.
|
||||
- `magnotia_core::model_registry::{find_model, all_models, ModelEntry, ModelFile}` (slice 5) — the source of URLs and hashes.
|
||||
- `magnotia_core::paths::app_paths` (slice 5) — platform path resolution.
|
||||
- `magnotia_core::types::DownloadProgress` (slice 5) — the progress event shape.
|
||||
- `lumotia_core::model_registry::{find_model, all_models, ModelEntry, ModelFile}` (slice 5) — the source of URLs and hashes.
|
||||
- `lumotia_core::paths::app_paths` (slice 5) — platform path resolution.
|
||||
- `lumotia_core::types::DownloadProgress` (slice 5) — the progress event shape.
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Path: `crates/transcription/src/local_engine.rs:167` (the `ParakeetWordGranularity` shim) and `:197` (`load_parakeet`).
|
||||
- LOC (Parakeet-specific code): ~40.
|
||||
- External deps: `transcribe-rs 0.3` with `default-features = false, features = ["onnx"]`.
|
||||
@@ -41,7 +41,7 @@ fn transcribe_raw(&mut self, samples, options) -> ...TranscriptionResult... {
|
||||
}
|
||||
```
|
||||
|
||||
Why this exists: `transcribe-rs` 0.3's blanket `SpeechModel` impl for `ParakeetModel::transcribe_raw` ignores `TranscribeOptions` and uses `TimestampGranularity::Token` (per-subword), which surfaces in Magnotia as `T Est Ing . One , Two , Three` output. The concrete-type method `ParakeetModel::transcribe_with` accepts `ParakeetParams` with an explicit granularity. The shim exposes that to the trait object.
|
||||
Why this exists: `transcribe-rs` 0.3's blanket `SpeechModel` impl for `ParakeetModel::transcribe_raw` ignores `TranscribeOptions` and uses `TimestampGranularity::Token` (per-subword), which surfaces in Lumotia as `T Est Ing . One , Two , Three` output. The concrete-type method `ParakeetModel::transcribe_with` accepts `ParakeetParams` with an explicit granularity. The shim exposes that to the trait object.
|
||||
|
||||
### `load_parakeet`
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ last_verified: 2026/05/09
|
||||
|
||||
## At a glance
|
||||
|
||||
- Crate: `magnotia-transcription`
|
||||
- Crate: `lumotia-transcription`
|
||||
- Paths:
|
||||
- `crates/transcription/src/streaming/mod.rs` (84 LOC) — trait + types + re-exports.
|
||||
- `crates/transcription/src/streaming/rms_vad.rs` (736 LOC) — `RmsVadChunker`.
|
||||
|
||||
@@ -9,11 +9,11 @@ last_verified: 2026/05/09
|
||||
|
||||
> **Where you are:** [Architecture map](../README.md) → [Audio + Transcription](README.md) → 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.
|
||||
**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 Lumotia'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`
|
||||
- Crate: `lumotia-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.
|
||||
@@ -46,7 +46,7 @@ Public surface:
|
||||
3. `FullParams::new(SamplingStrategy::Greedy { best_of: 1 })`.
|
||||
4. Optional `params.set_language(Some(lang))` if `options.language` is set and non-empty.
|
||||
5. Optional `params.set_initial_prompt(prompt)` if `options.initial_prompt` is set and non-empty.
|
||||
6. `params.set_n_threads(inference_thread_count(Workload::Whisper, gpu_offloaded) as i32)` where `gpu_offloaded = cfg!(feature = "whisper-vulkan") && vulkan_loader_available()`. Power-aware (`magnotia_core::tuning::inference_thread_count` reads battery vs AC; helper returns a smaller count on battery to preserve runtime).
|
||||
6. `params.set_n_threads(inference_thread_count(Workload::Whisper, gpu_offloaded) as i32)` where `gpu_offloaded = cfg!(feature = "whisper-vulkan") && vulkan_loader_available()`. Power-aware (`lumotia_core::tuning::inference_thread_count` reads battery vs AC; helper returns a smaller count on battery to preserve runtime).
|
||||
7. `set_print_special(false)`, `set_print_progress(false)`, `set_print_realtime(false)` — keep stdout silent.
|
||||
8. `state.full(params, samples)` — runs inference.
|
||||
9. Loops `state.full_n_segments()` calling `state.get_segment(i)`, pulling `seg.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).
|
||||
@@ -75,7 +75,7 @@ options: TranscriptionOptions { language, initial_prompt }
|
||||
## Watch-outs
|
||||
|
||||
- **`whisper-rs-sys` is the C++ link target.** Brief item #6 documents the historical Whispering v7.11.0 failure linking `whisper-rs-sys` + `tokenizers` together on Windows MSVC. The `build.rs` guard panics if `tokenizers` ever lands in the workspace lockfile on a Windows target. See [build-tokenizers-guard.md](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 5 `magnotia_core::hardware`) confirms `libvulkan.so` actually resolves. If the feature is on but the loader is missing, the helper falls back to CPU thread count.
|
||||
- **Vulkan is detected at runtime, not just compile time.** `cfg!(feature = "whisper-vulkan")` is the static check; `vulkan_loader_available()` (slice 5 `lumotia_core::hardware`) confirms `libvulkan.so` actually 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.full` warm-up is lower than `WhisperContext` load 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: 1` is the simplest correct path. Tuning later is a single function.
|
||||
- **Timestamp units.** whisper-rs returns centiseconds (10 ms). Multiplying by `0.01` gives seconds. A prior version that used milliseconds or token offsets would slice timing wrong; tests should pin this.
|
||||
@@ -88,4 +88,4 @@ options: TranscriptionOptions { language, initial_prompt }
|
||||
- [Cargo features](cargo-features.md) — `whisper` and `whisper-vulkan` flag matrix.
|
||||
- [Build tokenizers guard](build-tokenizers-guard.md) — the Windows MSVC-CRT defence linked to this backend.
|
||||
- [Tests and fixtures](tests-and-fixtures.md) — `whisper_rs_smoke.rs` exercises the load + transcribe + initial-prompt path; `jfk_bench.rs` measures cold/warm RTF; `thread_sweep.rs` validates thread-count scaling.
|
||||
- `magnotia_core::hardware::vulkan_loader_available`, `magnotia_core::tuning::{inference_thread_count, Workload}` (slice 5).
|
||||
- `lumotia_core::hardware::vulkan_loader_available`, `lumotia_core::tuning::{inference_thread_count, Workload}` (slice 5).
|
||||
|
||||
Reference in New Issue
Block a user