Skip to content

feat: Model Settings — whisper selection, download/delete, dedicated page - #21

Merged
davidbzyk merged 8 commits into
mainfrom
feat/model-settings
Apr 3, 2026
Merged

davidbzyk merged 8 commits into
mainfrom
feat/model-settings

Conversation

@davidbzyk

@davidbzyk davidbzyk commented Apr 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add dedicated Models page (/models) accessible from sidebar under Settings
  • Users can download, delete, and select ML models individually
  • Whisper model selection: choose between Medium and Large v3 Turbo (recommended) for transcription
  • Smart button in Settings Voice section: shows "Setup Models" when required models are missing, "+ Create Voice" when ready
  • Create Voice wizard updated to only require models for the selected TTS mode
  • Model cache invalidation after download/delete for instant UI refresh

Changes across all 3 layers

Python sidecar: MODEL_REGISTRY with categories, DELETE /models/{name} endpoint, configurable transcribe() with model hot-swap + memory cleanup, exact repo_id matching

Rust backend: delete_model command, whisper_model config field, model name passthrough in pipeline, ModelInfo with category/recommended fields

Svelte frontend: Models page with ModelCard component, sidebar nav item, shared getRequiredModelNames() utility, $derived.by for reactive model status

…el management

Add a dedicated Models tab to Settings where users can browse, download,
delete, and select ML models. Users on M1 Macs can now choose smaller
Whisper variants (small, medium) instead of the default large-v3-turbo.

- Add whisper_model config field (Rust + TS) with backward-compatible default
- Expand sidecar MODEL_REGISTRY with 3 whisper variants, categories, and
  recommended flags
- Make transcribe() accept configurable model_name with hot-swap support
- Add DELETE /models/{name} endpoint using HF cache deletion API
- Add delete_model Tauri command and whisper model passthrough in pipeline
- Add Models tab UI with grouped display, download/delete/select controls
- Replace "+ Create Voice" with smart button detecting model setup state
- Update Create Voice wizard to only require mode-specific models
- Add tests across all layers (94 frontend, 127 Rust, 77 Python)
The sidecar returns these fields but Rust's ModelInfo was silently
dropping them during deserialization, causing the frontend to render
empty model lists (filter by category found no matches).
Move model management from a Settings tab to its own /models route
with a sidebar nav item nested under Settings. This prepares for
the upcoming expansion to 8+ TTS and STS model variants that would
overwhelm a tab.

- Create src/routes/(app)/models/+page.svelte with full model UI
- Add "Models" as indented sidebar item under Settings
- Remove Models tab and model UI code from settings page
- Smart button in Voice section now navigates to /models
The _model_cache had a 30s TTL that kept stale False results after
a model was downloaded, causing /models/status to still report
"Not downloaded" until the cache expired.
The test was flaky due to parallel test threads sharing the same
temp directory. Fixed by using thread-unique entry names, creating
all entries before backdating mtime, setting both atime+mtime, and
adding a verification step that the filesystem honored the backdate.
Critical: fix $derived.by reactivity (BUG-1), health endpoint hardcoded
model (BUG-3), silent model name fallback (BUG-4). High: add memory
cleanup on model hot-swap/delete (BUG-5/17), use shared HttpClient
(BUG-6), remove duplicate check_models_downloaded (BUG-7), don't cache
transient errors (BUG-8), log swallowed errors (BUG-9), split delete
try/except (BUG-10). Medium: extract getRequiredModelNames utility
(BUG-11), extract ModelCard component (BUG-12), async transcription
(BUG-13), single scan_cache_dir call (BUG-14), selectWhisperModel
error handling (BUG-15), fix stale fallback after delete (BUG-16),
WHISPER_MODELS sync test (BUG-18). Low: add logger.error calls
(BUG-19), SSE parsing tests (BUG-20), dir_size tests (BUG-21),
DELETE endpoint tests (BUG-22). Pre-existing: stream SSE via
bytes_stream instead of buffering (BUG-2).
@coderabbitai

coderabbitai Bot commented Apr 3, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces comprehensive model management features across the full stack: adds a model registry with legacy aliases in the Python sidecar, implements model selection for transcription and model deletion endpoints, updates the Rust backend to support whisper model configuration and deletion, and adds a new Models UI page with model card component for browsing, selecting, and managing downloaded TTS/transcription/voice-conversion models.

Changes

Cohort / File(s) Summary
Dependencies
src-tauri/Cargo.toml
Added futures-util crate dependency for async utilities.
Sidecar Model Registry & Management
src-tauri/sidecar/server.py
Introduced MODEL_REGISTRY constant with transcription/TTS/voice-conversion model metadata and _LEGACY_MODEL_ALIASES mapping. Refactored model download caching from keyword-based to repo_id-based matching. Extended /transcribe and /transcribe-path endpoints with optional model_name parameter. Updated /health and /models/status to iterate registry entries and return per-model category/recommended fields. Added DELETE /models/{model_name} endpoint with legacy alias resolution, cache deletion, and in-memory state cleanup.
Sidecar Model Tests
src-tauri/sidecar/test_server_integration.py, src-tauri/sidecar/test_tts.py
Added MODEL_REGISTRY and _LEGACY_MODEL_ALIASES validation tests, model deletion endpoint coverage, and legacy alias resolution. Updated /models/status assertion from 3 to 4 models. Added TestWhisperModels to validate WHISPER_MODELS sync with registry and TestTranscribeModelName to verify model_name parameter with mocked model loading.
Sidecar Transcription Module
src-tauri/sidecar/tts.py
Added _whisper_model_name global tracking and WHISPER_MODELS repo-ID mapping. Extended transcribe(...) signature with model_name parameter (default: whisper-large-v3-turbo), model resolution via mapping, and cache reload/unload with gc.collect() and MLX metal cache clearing on model switch.
Rust Configuration
src-tauri/src/config.rs
Added whisper_model: String field to AppConfig with serde default "whisper-large-v3-turbo" and corresponding unit test coverage for serialization/deserialization.
Rust Model Management
src-tauri/src/models.rs
Removed check_models_downloaded command. Added delete_model Tauri command calling DELETE /models/{model} via sidecar. Refactored download_model to use shared HttpClient and send_with_timeout, switched SSE handling from line-based text parsing to incremental byte-stream buffering with new parse_sse_line helper. Added dir_size utility and extended unit test coverage for parse_sse_line and dir_size.
Rust Local TTS & Pipeline
src-tauri/src/local_tts.rs, src-tauri/src/lib.rs, src-tauri/src/pipeline.rs
Changed send_with_timeout visibility to pub(crate). Extended ModelInfo with optional category and recommended fields (serde-defaulted). Updated speech_to_speech signature to accept whisper_model: Option<&str>, constructing multipart form with model_name field when provided. Updated command handler registration to add delete_model and remove check_models_downloaded. Updated process_recording to pass whisper_model from config to TTS function.
Rust Tests
src-tauri/src/commands/recording.rs
Refactored test artifact naming to include thread/process ID (test-cleanup-{pid}-{tid}) for parallel-test isolation, added pre-test cleanup and backdated timestamp assertion using filetime::set_file_times.
Frontend Model UI Component
src/lib/ModelCard.svelte
New component rendering individual model rows with conditional download/delete/selection buttons based on state (downloaded, downloadingModel, confirmingDelete), supporting two-step delete confirmation and optional model selection radio button.
Frontend Model Utilities & State
src/lib/models.ts, src/lib/state.svelte.ts, src/lib/voicebox.ts
Added getRequiredModelNames(config) utility returning array of required models (whisper + category-specific TTS/voice-conversion). Extended AppConfig type and state defaults to include whisper_model. Extended VoiceboxModelStatus interface with category and recommended fields. Added VoiceboxClient.deleteModel(modelName) method calling new Tauri command.
Frontend Navigation & Pages
src/lib/Sidebar.svelte, src/routes/(app)/models/+page.svelte
Added "Models" nav entry with indent styling. New Models page with category-grouped model lists, per-model download/delete/select actions, model status loading/error handling, and config fallback selection on deletion of active transcription model.
Frontend Create Voice & Settings Pages
src/routes/(app)/create-voice/+page.svelte, src/routes/(app)/settings/+page.svelte
Updated wizard prerequisites to require only models returned by getRequiredModelNames() (not all available models). Updated settings to link to /models when required models are not ready. Added model-status loading with provider-awareness.
Frontend Tests
src/lib/state.svelte.test.ts, src/lib/voicebox.test.ts
Added whisper_model default assertion in config tests. Extended getModelStatus test to verify category and recommended fields. Added deleteModel test group verifying command invocation and error propagation.

Sequence Diagram(s)

sequenceDiagram
    participant User as User<br/>(Frontend)
    participant UI as Models Page
    participant Tauri as Tauri Runtime
    participant Rust as Rust Backend
    participant Sidecar as Python Sidecar
    participant HF as HuggingFace Cache
    participant Memory as In-Memory<br/>Model State

    User->>UI: Click delete model button
    UI->>UI: Set confirmingDelete state
    User->>UI: Confirm deletion
    UI->>Tauri: delete_model(model_name)
    activate Tauri
    Tauri->>Rust: Call delete_model command
    activate Rust
    Rust->>Sidecar: DELETE /models/{model_name}
    activate Sidecar
    Sidecar->>Sidecar: Resolve legacy alias
    Sidecar->>HF: scan_cache_dir() for repo_id
    HF-->>Sidecar: Cache entries
    Sidecar->>HF: Delete matching revisions
    Sidecar->>Memory: Clear in-memory model globals<br/>(per category)
    Sidecar->>Sidecar: Invalidate _model_cache[repo_id]
    Sidecar-->>Rust: Return JSON success
    deactivate Sidecar
    Rust-->>Tauri: Return response
    deactivate Rust
    Tauri-->>UI: Success callback
    deactivate Tauri
    UI->>Tauri: getModelStatus() [refresh]
    UI-->>User: Update model list, clear delete state
    alt Deleted model was active whisper model
        UI->>UI: Select fallback downloaded model
        UI->>Tauri: Update config.whisper_model
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 hop hop — A registry of models, organized with care,
Legacy names mapped true, deletion handled fair!
Whisper picks its voice, TTS finds its way,
From sidecar to UI, all models at play! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a dedicated Models page with whisper selection, download/delete functionality.
Docstring Coverage ✅ Passed Docstring coverage is 81.54% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The PR description clearly describes the changeset, detailing the addition of a Models page, model selection functionality, and cross-layer implementation changes across Python, Rust, and Svelte.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/model-settings
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/model-settings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@davidbzyk
davidbzyk merged commit 6fbc5ed into main Apr 3, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant