Skip to content

[3/12] feat: native ASR platform — sherpa runtime, Parakeet client, model catalog & lifecycle - #12

Open
Mvkd108 wants to merge 7 commits into
feat/window-placement-servicefrom
feat/model-lifecycle-management
Open

[3/12] feat: native ASR platform — sherpa runtime, Parakeet client, model catalog & lifecycle#12
Mvkd108 wants to merge 7 commits into
feat/window-placement-servicefrom
feat/model-lifecycle-management

Conversation

@Mvkd108

@Mvkd108 Mvkd108 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Status: REVIEW ONLY — do not merge. PR 3 of 12 in a stacked series; stacked on #11. This PR shows only its own chunk. Series intro, divergence notes and full map: #10. Next: #13.

This PR (3/12): native ASR platform — sherpa runtime, Parakeet client, model catalog & lifecycle

Commits:

  • 3deca7b feat: sherpa-onnx native runtime loader and safe archive extraction — Services/NativeSherpaRuntime.cs, Services/SafeArchiveExtractor.cs, NativeRuntime/SherpaOnnxCuda/native-sherpa-cuda-runtime.json
  • 3aaac52 feat: transcription progress reporting with throttling — Services/ThrottledTranscriptionProgress.cs, Services/TranscriptionProgress.cs
  • 1bc9442 feat: Parakeet streaming ASR client and PCM normalisation — Services/NativeParakeetClient.cs, Services/StreamingPcmNormalizer.cs, Services/ParakeetTimestampSegmenter.cs
  • e987048 feat: transcription model catalog and descriptors — Services/TranscriptionModelCatalog.cs, Services/TranscriptionModels.cs
  • 5d9a017 feat: offline ASR client — Services/NativeOfflineAsrClient.cs
  • b03bafd feat: unified native transcription client — Services/NativeTranscriptionClient.cs
  • 42033f8 feat: model lifecycle management and streaming model platform — Services/TranscriptionModelLifecycleService.cs, Services/StreamingModelPlatform.cs, Services/ModelSetupArtifactCleaner.cs

16 files, +4,333.

Review focus

  • SafeArchiveExtractor — archive-slip protection, partial-download cleanup, hash/size validation of model payloads.
  • NativeSherpaRuntime — CPU vs CUDA runtime selection and load failure surfacing (see also RuntimeStatusMapper in PR 4).
  • StreamingPcmNormalizer — all inputs normalized to 16 kHz mono before ASR (this is why the "non-stereo mic" crash class cannot occur here).
  • TranscriptionModelCatalog / TranscriptionModelLifecycleService — model state machine (absent → downloading → ready → active), resume/retry behavior.
  • NativeParakeetClient vs NativeOfflineAsrClient — streaming vs batch contract split behind NativeTranscriptionClient.

Expected — do not flag: nothing calls this platform yet; wiring happens in PR 9/10. Muesli.Windows.csproj changes are package/runtime references only.

Known limits

  • First-use model download requires network; there is no offline bundle in this PR.
  • Hardware qualification (CUDA vs CPU behavior on real GPUs) is a human gate, not covered by automated tests.

Test evidence

  • Full stack at tip: build clean (--no-restore); 484/484 passing.
  • Suite lands in PR 11; classes covering this chunk: TranscriptionModelPlatformTests, ModelAndSingleInstanceTests.

Previous: #11 | Next: #13

Copilot AI lite review requested due to automatic review settings August 4, 2026 09:04
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e44e2cac-0c9a-4add-8af2-1fd5476079ee

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@socket-security

socket-security Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednuget/​org.k2fsa.sherpa.onnx@​1.13.49410090100100
Addednuget/​sharpcompress@​0.48.19410090100100

View full report

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the core “native ASR platform” foundations for the Windows app: sherpa-onnx runtime loading (CPU/CUDA), model catalog + lifecycle management (download/verify/delete), and native clients for streaming (Parakeet) and batch/offline transcription, along with progress reporting utilities.

Changes:

  • Add native sherpa runtime loader (NativeSherpaRuntime) plus safe archive extraction helper (SafeArchiveExtractor) and ship a CUDA runtime manifest/content.
  • Implement Parakeet streaming client + PCM normalization + timestamp segmentation, and add offline/native model client plus a unified transcription client wrapper.
  • Add model definitions/catalog and lifecycle services (status snapshots, download/verify/delete, progress).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
windows-native/Muesli.Windows/Services/TranscriptionProgress.cs Defines transcription stage + progress payload used by long-running imports/transcription.
windows-native/Muesli.Windows/Services/ThrottledTranscriptionProgress.cs Throttles progress callbacks to avoid WPF dispatcher starvation during decoding.
windows-native/Muesli.Windows/Services/TranscriptionModels.cs Adds shared DTOs for transcription/model operations and download progress display.
windows-native/Muesli.Windows/Services/TranscriptionModelCatalog.cs Adds pinned model catalog and cache utilities for native offline models.
windows-native/Muesli.Windows/Services/TranscriptionModelLifecycleService.cs Adds per-model lifecycle operations (prepare/verify/delete/cancel/retry) and snapshots for UI.
windows-native/Muesli.Windows/Services/NativeTranscriptionClient.cs Provides a role-scoped “single selected model” wrapper over Parakeet vs offline clients.
windows-native/Muesli.Windows/Services/NativeParakeetClient.cs Implements Parakeet (sherpa-onnx) client with download/verify, provider selection, and file transcription.
windows-native/Muesli.Windows/Services/NativeOfflineAsrClient.cs Implements offline/batch ASR models via sherpa-onnx with pinned-file verification and chunked decoding.
windows-native/Muesli.Windows/Services/StreamingPcmNormalizer.cs Normalizes live PCM input to 16kHz mono float samples for streaming ASR input.
windows-native/Muesli.Windows/Services/ParakeetTimestampSegmenter.cs Produces timestamped transcript segments using token timestamps with fallbacks.
windows-native/Muesli.Windows/Services/SafeArchiveExtractor.cs Adds archive-slip protection for model extraction into cache directories.
windows-native/Muesli.Windows/Services/NativeSherpaRuntime.cs Adds CPU vs CUDA sherpa runtime discovery/loading and diagnostic surfacing.
windows-native/Muesli.Windows/Services/StreamingModelPlatform.cs Adds streaming model catalog + installer + lifecycle service for live meeting models (incl. VAD).
windows-native/Muesli.Windows/Services/ModelSetupArtifactCleaner.cs Adds cleanup for partial/staging artifacts from interrupted downloads/extractions.
windows-native/Muesli.Windows/NativeRuntime/SherpaOnnxCuda/native-sherpa-cuda-runtime.json Adds CUDA runtime manifest describing required runtime + NVIDIA dependency DLLs.
windows-native/Muesli.Windows/Muesli.Windows.csproj Adds sherpa-onnx + SharpCompress package refs and ships CUDA runtime content to output/publish.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

(name.EndsWith(".partial", StringComparison.OrdinalIgnoreCase) ||
name.EndsWith(".download", StringComparison.OrdinalIgnoreCase)))
{
CapturedAudio.TryDelete(file);
Comment on lines +191 to +194
var genericBytes = Directory.Exists(ModelCacheDirectory)
? Directory.EnumerateFiles(ModelCacheDirectory, "*", SearchOption.AllDirectories)
.Sum(path => new FileInfo(path).Length)
: 0;
Comment on lines +204 to +206
public long DiskSizeBytes() => Directory.Exists(_model.ModelPath)
? Directory.EnumerateFiles(_model.ModelPath, "*", SearchOption.AllDirectories).Sum(path => new FileInfo(path).Length)
: 0;
Comment on lines +37 to +40
using var input = entry.OpenEntryStream();
using var output = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None);
input.CopyTo(output);
output.Flush(flushToDisk: true);
Comment on lines +336 to +340
lock (state.Gate)
{
state.Status = null;
state.Detail = "Cancelled; safe partial artifacts will be cleaned on retry.";
}
Mvkd108 and others added 3 commits August 18, 2026 19:48
Adds NativeSherpaRuntime to resolve and load the bundled sherpa-onnx
native libraries (CPU and CUDA), SafeArchiveExtractor for path-traversal
safe model unpacking, and ModelSetupArtifactCleaner to reclaim partial
downloads. Wires the sherpa-onnx and SharpCompress package references and
ships the CUDA runtime manifest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Introduces a common TranscriptionProgress contract plus a throttling
wrapper so long transcriptions can report progress without flooding the
UI dispatcher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the sherpa-onnx Parakeet client for streaming recognition, a
timestamp segmenter that turns token timings into readable segments, and
a PCM normaliser that converts capture buffers to the model's expected
sample rate and format.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Mvkd108
Mvkd108 force-pushed the feat/model-lifecycle-management branch from 42033f8 to e097c53 Compare August 18, 2026 14:19
@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown

Claude finished @Mvkd108's task in 9m 14s —— View job


Code Review — PR #12 (3/12: native ASR platform)

  • Gather context (PR diff vs origin/feat/window-placement-service, CLAUDE.md, changed files)
  • Review focus areas called out in PR description (SafeArchiveExtractor, NativeSherpaRuntime, StreamingPcmNormalizer, model catalog/lifecycle, Parakeet vs offline client split)
  • Cross-check prior Copilot review comments against current code
  • Post findings

This is review-only per the PR description (nothing wired up yet), so feedback below is about correctness/robustness of this chunk in isolation.

Already fixed since the earlier Copilot review

  • ModelSetupArtifactCleaner.Cleanup no longer calls the nonexistent CapturedAudio.TryDelete(...); it now uses a private best-effort TryDelete(string) (Services/ModelSetupArtifactCleaner.cs:48). Confirmed this compiles fine as written.

Findings

  1. TranscriptionModelLifecycleService.cs:~338 — cancellation hides the Retry action it promises.
    In StartOperationAsync's catch (OperationCanceledException) handler, state.Status is reset to null and state.Detail is set to "Cancelled; safe partial artifacts will be cleaned on retry.", but state.Retry is left populated. Snapshot() computes CanRetry = Retry != null && activeStatus is Failed or DeletionFailed — since activeStatus is now null, CanRetry is false even though calling RetryAsync would actually work. The UI text explicitly promises a retry path that the same snapshot hides. Either mark cancellation as its own status (e.g. add Cancelled to TranscriptionModelStatus and allow retry from it) or don't null out Status on cancellation.

  2. TranscriptionModelCatalog.cs:189-195 (CacheSizeBytes) and StreamingModelPlatform.cs:~204 (StreamingModelInstaller.DiskSizeBytes) — TOCTOU race summing file sizes.
    Both do Directory.EnumerateFiles(...).Sum(path => new FileInfo(path).Length) with no existence/exception guard. If a file is deleted or moved mid-enumeration (plausible here since DeleteAsync/PrepareAsync mutate the same directories these are used to display size for, e.g. via Snapshot() polling during an active operation), this throws and breaks whatever UI is reading cache/model size. Note NativeTranscriptionModelOperations.DiskSizeBytes() in TranscriptionModelLifecycleService.cs already does this correctly (.Select(path => new FileInfo(path)).Where(f => f.Exists).Sum(f => f.Length)) — the other two call sites should match that pattern (also raised by the earlier Copilot review for CacheSizeBytes, but the StreamingModelPlatform instance of the same bug wasn't addressed).

  3. SafeArchiveExtractor.cs:40ExtractSafely accepts a CancellationToken but the actual copy ignores it (still open from the earlier Copilot pass). input.CopyTo(output) doesn't check the token, so a large entry blocks cancellation until that single file finishes copying. Worth switching to await input.CopyToAsync(output, cancellationToken) (sync-over-async is fine since the method is otherwise synchronous), especially since this extraction is used for multi-hundred-MB to multi-GB archives per the catalog.

  4. StreamingModelPlatform.cs / TranscriptionModelLifecycleService.csModelChanged event is raised off the UI thread and before operation-state cleanup runs.
    Both services' operation-runner methods invoke ModelChanged?.Invoke(this, id) synchronously (e.g. once before the try that owns state.Cancellation, and again in progress callbacks/finally), after await ...ConfigureAwait(false) continuations, i.e. on a thread-pool thread rather than the original SynchronizationContext. Nothing wires this event up in this PR (per "Expected — do not flag"), but once MainWindow subscribes directly (PR 9/10), any WPF control update in the handler will throw InvalidOperationException for cross-thread access, and if a handler throws synchronously before the try/finally runs, state.Cancellation never gets cleared, permanently wedging that model with "operation already running." Worth flagging now since both services already exist and the pattern will need Dispatcher.Invoke/try-wrapping at the subscriber or here before they're wired up.

  5. StreamingModelPlatform.cs and TranscriptionModelLifecycleService.cs duplicate an ~80-line concurrency state machine (OperationState, cancel/retry/progress plumbing) almost verbatim. They've already diverged in behavior (see Models page: "Parakeet unavailable" toast does not explain why #1 above — the streaming service's cancellation/retry handling differs from the transcription one). Given the model-count is about to grow (7 offline models + 1 streaming model today, more likely later), consider extracting a shared generic lifecycle base to avoid this class of divergence bug recurring.

  6. StreamingModelPlatform.csStreamingModelInstaller.SetupGate is a static SemaphoreSlim shared across all model instances, serializing prepare/verify/delete globally rather than per-model. Currently invisible since StreamingModelCatalog.Models has exactly one entry, but StreamingModelLifecycleService is otherwise built for multiple streaming models (keyed per-id via _states). Adding a second streaming model later will cause unrelated models to block on each other's downloads. TranscriptionModelLifecycleService's per-model serialization (via _states) doesn't have this issue — worth aligning.

None of the above are blockers for "review only, do not merge" — flagging for the maintainers to address before PR 9/10 wires this platform up and before StreamingModelCatalog.Models grows past one entry.

Branch: feat/model-lifecycle-management

Mvkd108 and others added 4 commits August 18, 2026 19:52
Describes the supported ASR models, their download sources, on-disk
layout and hardware requirements in one place so download, qualification
and UI all agree on what is installable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the batch (non-streaming) recognition path used for file imports and
post-meeting finalisation, sharing model resolution and progress
reporting with the streaming client.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fronts the streaming and offline clients behind a single entry point that
selects the right backend from the catalog and the user's configured
model.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Handles install, verification, repair and removal of ASR models, and
resolves which streaming platform (CPU or CUDA) a given model can run on
for the current machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Mvkd108
Mvkd108 force-pushed the feat/model-lifecycle-management branch from e097c53 to a121059 Compare August 18, 2026 14:22
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.

2 participants