Skip to content

Add Rust API for Moonshine v2 - #3247

Merged
csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:rust-api-moonshine-v2
Feb 28, 2026
Merged

csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:rust-api-moonshine-v2

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Feb 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Moonshine v2 offline automatic speech recognition model support with flexible encoder/decoder configuration and parallel processing capabilities
    • New example code demonstrating Moonshine v2 implementation including audio input handling, result decoding, and detailed performance analysis
  • Chores

    • Version updated to 0.1.9 with coordinated dependency upgrades

@csukuangfj
csukuangfj requested a review from Copilot February 28, 2026 07:31
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Feb 28, 2026
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates the latest Moonshine v2 model architecture into the sherpa-onnx Rust API, enhancing its capabilities for offline speech recognition. The changes include extending the model configuration to support Moonshine v2's merged decoder, providing a new example to showcase its usage, and updating related scripts and crate versions. This allows Rust developers to leverage the improved performance and features of Moonshine v2 models.

Highlights

  • Moonshine v2 API Support: Added comprehensive support for Moonshine v2 models within the sherpa-onnx Rust API, including necessary configuration fields for the new model architecture.
  • New Example and Runner Script: Introduced a new Rust example (moonshine_v2.rs) demonstrating offline speech recognition with Moonshine v2, accompanied by a shell script (run-moonshine-v2.sh) to facilitate easy execution and model download.
  • Crate Version Updates: Incremented the versions of rust-api-examples, sherpa-onnx, and sherpa-onnx-sys crates to 0.1.9 to reflect the new API additions and ensure compatibility.
Changelog
  • .github/scripts/test-rust.sh
    • Added a new line to execute the run-moonshine-v2.sh script, integrating the new example into the test suite.
  • rust-api-examples/Cargo.lock
    • Updated the version of the rust-api-examples package from 0.1.8 to 0.1.9.
    • Updated the version of the sherpa-onnx package from 0.1.8 to 0.1.9 and its associated checksum.
    • Updated the version of the sherpa-onnx-sys package from 0.1.8 to 0.1.9 and its associated checksum.
  • rust-api-examples/Cargo.toml
    • Updated the package version for rust-api-examples from 0.1.8 to 0.1.9.
    • Updated the sherpa-onnx dependency version from 0.1.8 to 0.1.9.
  • rust-api-examples/examples/moonshine_v2.rs
    • Added a new Rust example file to demonstrate offline speech recognition using Moonshine v2 models, including argument parsing, recognizer configuration, and performance measurement.
  • rust-api-examples/run-moonshine-v2.sh
    • Added a new shell script to download the necessary Moonshine v2 models and execute the moonshine_v2 Rust example.
  • sherpa-onnx/rust/sherpa-onnx-sys/Cargo.toml
    • Updated the package version for sherpa-onnx-sys from 0.1.8 to 0.1.9.
  • sherpa-onnx/rust/sherpa-onnx-sys/src/offline_asr.rs
    • Added a merged_decoder field to the OfflineMoonshineModelConfig struct to support Moonshine v2 model configurations.
  • sherpa-onnx/rust/sherpa-onnx/Cargo.toml
    • Updated the package version for sherpa-onnx from 0.1.8 to 0.1.9.
    • Updated the sherpa-onnx-sys dependency version from 0.1.8 to 0.1.9.
  • sherpa-onnx/rust/sherpa-onnx/src/offline_asr.rs
    • Added a merged_decoder field to the OfflineMoonshineModelConfig struct.
    • Included documentation comments to clarify the model requirements for Moonshine v1 and Moonshine v2.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@coderabbitai

coderabbitai Bot commented Feb 28, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds Moonshine v2 offline ASR model support to the Rust bindings by introducing a merged_decoder field to the OfflineMoonshineModelConfig FFI structure, providing a complete executable example with integration into the test pipeline, and bumping package versions across related Cargo.toml files.

Changes

Cohort / File(s) Summary
Test Pipeline Integration
.github/scripts/test-rust.sh
Adds invocation of ./run-moonshine-v2.sh to the test script execution sequence.
Version Updates
rust-api-examples/Cargo.toml, sherpa-onnx/rust/sherpa-onnx-sys/Cargo.toml, sherpa-onnx/rust/sherpa-onnx/Cargo.toml
Bumps version from 0.1.8 to 0.1.9 across crates and updates sherpa-onnx dependency versions accordingly.
Moonshine v2 FFI Support
sherpa-onnx/rust/sherpa-onnx-sys/src/offline_asr.rs, sherpa-onnx/rust/sherpa-onnx/src/offline_asr.rs
Adds merged_decoder field to OfflineMoonshineModelConfig struct in both C FFI bindings and Rust wrapper, with proper conversion mapping in to_sys.
Moonshine v2 Example
rust-api-examples/examples/moonshine_v2.rs, rust-api-examples/run-moonshine-v2.sh
Adds new Rust example demonstrating Moonshine v2 offline ASR with command-line argument parsing, model loading, and performance metrics; includes bash script for automated setup and execution with model downloading.

Sequence Diagram

sequenceDiagram
    participant User as User/Script
    participant Example as moonshine_v2.rs
    participant Wrapper as Rust Wrapper<br/>(sherpa-onnx)
    participant FFI as FFI Layer<br/>(sherpa-onnx-sys)
    participant Backend as C Backend

    User->>Example: invoke with arguments<br/>(wav, encoder, decoder, merged_decoder, tokens, provider, threads)
    Example->>Example: parse arguments &<br/>load WAV file
    Example->>Wrapper: build OfflineRecognizerConfig<br/>with Moonshine v2 settings
    Wrapper->>FFI: to_sys() conversion<br/>maps merged_decoder field
    FFI->>Backend: create OfflineRecognizer
    Backend-->>FFI: recognizer instance
    FFI-->>Wrapper: return recognizer
    Wrapper-->>Example: OfflineRecognizer created
    Example->>Wrapper: create stream &<br/>feed waveform data
    Wrapper->>FFI: invoke C recognition API
    FFI->>Backend: decode audio stream
    Backend-->>FFI: recognition result
    FFI-->>Wrapper: return decoded text
    Wrapper-->>Example: result & timing metrics
    Example->>User: output text & RTF metrics
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size:M

Poem

🐰 Hops with glee through merged_decoder calls,
A Moonshine v2 example that enthralls!
FFI fields now shine so bright,
Rust bindings working just right. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Rust API for Moonshine v2' directly summarizes the main change: adding support for Moonshine v2 in the Rust API with new example code, scripts, and configuration updates.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request adds support for Moonshine v2 models to the Rust API. The changes include a new example, updates to the FFI bindings and safe wrappers, and a test script. The implementation is solid. I've provided one suggestion in the new example file to improve performance by avoiding unnecessary string allocations.

Comment on lines +53 to +57
recognizer_config.model_config.moonshine.encoder = Some(args.encoder.clone());
recognizer_config.model_config.moonshine.merged_decoder = Some(args.decoder.clone());

recognizer_config.model_config.tokens = Some(args.tokens.clone());
recognizer_config.model_config.provider = Some(args.provider.clone());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To improve performance and adhere to Rust's ownership principles, you can avoid cloning these String values. Since the args struct is not used after this configuration block, you can move the values directly into the recognizer_config. This prevents unnecessary memory allocations.

Suggested change
recognizer_config.model_config.moonshine.encoder = Some(args.encoder.clone());
recognizer_config.model_config.moonshine.merged_decoder = Some(args.decoder.clone());
recognizer_config.model_config.tokens = Some(args.tokens.clone());
recognizer_config.model_config.provider = Some(args.provider.clone());
recognizer_config.model_config.moonshine.encoder = Some(args.encoder);
recognizer_config.model_config.moonshine.merged_decoder = Some(args.decoder);
recognizer_config.model_config.tokens = Some(args.tokens);
recognizer_config.model_config.provider = Some(args.provider);

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

Adds Moonshine v2 support to the Rust bindings by exposing the merged decoder model in the Moonshine offline config, plus a runnable Rust example and CI script coverage.

Changes:

  • Extend Moonshine offline model config in both sherpa-onnx and sherpa-onnx-sys to include merged_decoder.
  • Bump Rust crate versions to 0.1.9 and update the examples crate to use the new version.
  • Add a Moonshine v2 Rust example + download/run script, and run it in the Rust CI script.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sherpa-onnx/rust/sherpa-onnx/src/offline_asr.rs Adds merged_decoder to the safe Rust Moonshine config and passes it through to FFI.
sherpa-onnx/rust/sherpa-onnx/Cargo.toml Bumps sherpa-onnx to 0.1.9 and updates sherpa-onnx-sys dependency version.
sherpa-onnx/rust/sherpa-onnx-sys/src/offline_asr.rs Adds merged_decoder to the FFI struct to match the C API.
sherpa-onnx/rust/sherpa-onnx-sys/Cargo.toml Bumps sherpa-onnx-sys to 0.1.9.
rust-api-examples/run-moonshine-v2.sh New script to download Moonshine v2 models and run the example.
rust-api-examples/examples/moonshine_v2.rs New offline Moonshine v2 Rust example using merged_decoder.
rust-api-examples/Cargo.toml Bumps examples crate and updates sherpa-onnx dependency to 0.1.9.
rust-api-examples/Cargo.lock Updates locked versions/checksums for 0.1.9.
.github/scripts/test-rust.sh Runs the new Moonshine v2 script in Rust CI.
Comments suppressed due to low confidence (1)

rust-api-examples/run-moonshine-v2.sh:9

  • For consistency with other run-*.sh scripts (and to keep CI logs smaller), consider using curl -SsL (or similar) here; curl -SL will emit progress output and, combined with set -x and tar xvf, can make logs very noisy.
if [ ! -f ./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/encoder_model.ort ]; then
  curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
  tar xvf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
  rm sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2

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

Comment on lines 1 to +3
[package]
name = "sherpa-onnx"
version = "0.1.8"
version = "0.1.9"

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

OfflineMoonshineModelConfig gained a new public field (merged_decoder), which is a breaking change for downstream crates that construct the struct via a literal or destructure it in patterns. Consider bumping the crate version with a breaking-change increment (e.g., 0.2.0) or otherwise providing a backwards-compatible migration path before publishing.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to 10
sherpa-onnx = "0.1.9"
# sherpa-onnx = { path = "../sherpa-onnx/rust/sherpa-onnx" }

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

rust-api-examples depends on sherpa-onnx from crates.io (not the in-repo path dependency). That means CI for this repo won’t exercise the code changes in sherpa-onnx/rust/…, and it also assumes 0.1.9 is already published. If the goal is to test the PR’s code, consider switching this to a path dependency in CI (or using a [patch.crates-io] override) and only using the registry version for end-user examples/releases.

Suggested change
sherpa-onnx = "0.1.9"
# sherpa-onnx = { path = "../sherpa-onnx/rust/sherpa-onnx" }
# sherpa-onnx = "0.1.9"
sherpa-onnx = { path = "../sherpa-onnx/rust/sherpa-onnx" }

Copilot uses AI. Check for mistakes.
[package]
name = "sherpa-onnx-sys"
version = "0.1.8"
version = "0.1.9"

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

This release adds a new public field to the OfflineMoonshineModelConfig FFI struct, which is a breaking API change for downstream users of sherpa-onnx-sys. Consider using a breaking-change version bump (or documenting the semver policy for 0.x) before publishing.

Suggested change
version = "0.1.9"
version = "0.2.0"

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rust-api-examples/examples/moonshine_v2.rs`:
- Around line 41-42: Validate the command-line field num_threads (the struct
field named num_threads with #[arg(...)]) before it is forwarded to the
recognizer creation call where it's used later (the use at the site that
forwards num_threads around line 59); reject or handle values <= 0 instead of
passing them through. Add a check right after parsing CLI args (or immediately
before the recognizer is constructed) that returns a clear error/exit or clamps
to a safe minimum if num_threads <= 0, and convert the validated positive value
to the expected unsigned type (usize) before passing it into the recognizer
creation/initialization call.
- Around line 103-105: The example currently only prints an error when decoding
fails (the else branch that calls eprintln!("Failed to get recognition
result")), which allows the program to exit with code 0; update that failure
branch to terminate with a non-zero exit status (for example call
std::process::exit(1) or return an Err from main) so CI sees the failure—locate
the else block around the recognition result handling in moonshine_v2.rs and
replace the simple eprintln! with an error log plus a non-zero exit/Err return.

In `@rust-api-examples/run-moonshine-v2.sh`:
- Around line 7-9: Add a SHA-256 integrity check for the downloaded artifact
before extraction: after the curl download command (the line that fetches
sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2) compute the file's
SHA-256 (using sha256sum or shasum -a 256) and compare it against a pinned
expected checksum constant; if the checksums do not match, print an error and
exit non‑zero so the subsequent tar xvf step is never run, otherwise proceed to
tar and then remove the archive. Ensure the check is fail-fast (exit on
mismatch) and references the exact filename used in the curl/tar commands so it
cannot be bypassed.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13ab997 and 8c76472.

⛔ Files ignored due to path filters (1)
  • rust-api-examples/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • .github/scripts/test-rust.sh
  • rust-api-examples/Cargo.toml
  • rust-api-examples/examples/moonshine_v2.rs
  • rust-api-examples/run-moonshine-v2.sh
  • sherpa-onnx/rust/sherpa-onnx-sys/Cargo.toml
  • sherpa-onnx/rust/sherpa-onnx-sys/src/offline_asr.rs
  • sherpa-onnx/rust/sherpa-onnx/Cargo.toml
  • sherpa-onnx/rust/sherpa-onnx/src/offline_asr.rs

Comment on lines +41 to +42
#[arg(long, default_value_t = 2)]
num_threads: i32,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Validate num_threads before using it.

Line 41-Line 42 accepts any i32, and Line 59 forwards it directly. Reject <= 0 to prevent invalid recognizer settings.

🔧 Proposed fix
 fn main() {
     let args = Args::parse();
+    if args.num_threads <= 0 {
+        eprintln!("--num-threads must be > 0");
+        std::process::exit(2);
+    }

     let wave = Wave::read(&args.wav).expect("Failed to read WAV file");

Also applies to: 59-59

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust-api-examples/examples/moonshine_v2.rs` around lines 41 - 42, Validate
the command-line field num_threads (the struct field named num_threads with
#[arg(...)]) before it is forwarded to the recognizer creation call where it's
used later (the use at the site that forwards num_threads around line 59);
reject or handle values <= 0 instead of passing them through. Add a check right
after parsing CLI args (or immediately before the recognizer is constructed)
that returns a clear error/exit or clamps to a safe minimum if num_threads <= 0,
and convert the validated positive value to the expected unsigned type (usize)
before passing it into the recognizer creation/initialization call.

Comment on lines +103 to +105
} else {
eprintln!("Failed to get recognition result");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Return a non-zero exit code when decoding fails.

Line 103-Line 105 only prints an error, so this example can still exit successfully and mask failures in CI.

🔧 Proposed fix
-    } else {
-        eprintln!("Failed to get recognition result");
-    }
+    } else {
+        eprintln!("Failed to get recognition result");
+        std::process::exit(1);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else {
eprintln!("Failed to get recognition result");
}
} else {
eprintln!("Failed to get recognition result");
std::process::exit(1);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust-api-examples/examples/moonshine_v2.rs` around lines 103 - 105, The
example currently only prints an error when decoding fails (the else branch that
calls eprintln!("Failed to get recognition result")), which allows the program
to exit with code 0; update that failure branch to terminate with a non-zero
exit status (for example call std::process::exit(1) or return an Err from main)
so CI sees the failure—locate the else block around the recognition result
handling in moonshine_v2.rs and replace the simple eprintln! with an error log
plus a non-zero exit/Err return.

Comment on lines +7 to +9
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
tar xvf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
rm sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Verify artifact integrity before extraction.

Line 7-Line 9 downloads and untars a remote archive without checksum verification. Please add a pinned SHA-256 check before tar xvf.

🔧 Proposed hardening sketch
-  curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
-  tar xvf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
+  curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
+  echo "<expected_sha256>  sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2" | sha256sum -c -
+  tar xvf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust-api-examples/run-moonshine-v2.sh` around lines 7 - 9, Add a SHA-256
integrity check for the downloaded artifact before extraction: after the curl
download command (the line that fetches
sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27.tar.bz2) compute the file's
SHA-256 (using sha256sum or shasum -a 256) and compare it against a pinned
expected checksum constant; if the checksums do not match, print an error and
exit non‑zero so the subsequent tar xvf step is never run, otherwise proceed to
tar and then remove the archive. Ensure the check is fail-fast (exit on
mismatch) and references the exact filename used in the curl/tar commands so it
cannot be bypassed.

@csukuangfj
csukuangfj merged commit a579b72 into k2-fsa:master Feb 28, 2026
5 checks passed
@csukuangfj
csukuangfj deleted the rust-api-moonshine-v2 branch February 28, 2026 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants