Skip to content

Add Pascal API for Moonshine v2 - #3248

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

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

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Feb 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Introduced Moonshine v2 offline speech recognition example with automated dependency management and compilation.
    • Added merged decoder variant support to Moonshine model configuration.
  • Tests

    • Enhanced CI/CD pipeline to validate the new Moonshine v2 example.

@csukuangfj
csukuangfj requested a review from Copilot February 28, 2026 07:35
@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 expands the Pascal API within sherpa-onnx to incorporate support for the Moonshine v2 automatic speech recognition model. It introduces a new example demonstrating how to use this model for non-streaming ASR in Pascal, complete with a build and run script, and updates the underlying API structures to accommodate the new model's configuration.

Highlights

  • Pascal API Extension: Introduced Pascal API support for the Moonshine v2 automatic speech recognition model.
  • New Example: Added a new Pascal example (moonshine_v2.pas) demonstrating non-streaming ASR with Moonshine v2.
  • Build and Run Script: Provided a shell script (run-moonshine-v2.sh) to facilitate building and executing the new Pascal example.
  • API Configuration Update: Updated the core Pascal API definitions to include a MergedDecoder field for Moonshine model configurations.
Changelog
  • pascal-api-examples/non-streaming-asr/.gitignore
    • Added moonshine_v2 to the list of ignored files.
  • pascal-api-examples/non-streaming-asr/moonshine_v2.pas
    • Added a new Pascal example file for non-streaming ASR using Moonshine v2.
  • pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh
    • Added a new shell script to build and execute the Moonshine v2 Pascal example.
  • sherpa-onnx/pascal-api/sherpa_onnx.pas
    • Added the MergedDecoder field to the TSherpaOnnxOfflineMoonshineModelConfig record.
    • Added the MergedDecoder field to the SherpaOnnxOfflineMoonshineModelConfig C-API struct.
    • Updated the TSherpaOnnxOfflineMoonshineModelConfig.ToString function to include the new MergedDecoder field.
    • Updated the ConvertOfflineRecognizerConfig function to handle the new MergedDecoder field.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/pascal.yaml
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 extends Sherpa ONNX's Pascal binding to support Moonshine v2 ONNX models. Changes include adding a MergedDecoder field to the offline Moonshine model configuration, creating a complete Pascal example program for non-streaming Moonshine v2 decoding, adding an automated build and execution script, and updating the CI workflow to test the new example.

Changes

Cohort / File(s) Summary
CI Workflow
.github/workflows/pascal.yaml
Added execution of run-moonshine-v2.sh script in the Non-Streaming ASR test sequence before the existing Moonshine v1 test step.
Gitignore
pascal-api-examples/non-streaming-asr/.gitignore
Added moonshine_v2 entry to exclude compiled binary from version control.
Pascal API Binding
sherpa-onnx/pascal-api/sherpa_onnx.pas
Extended TSherpaOnnxOfflineMoonshineModelConfig with a new MergedDecoder field (AnsiString). Updated corresponding C binding structure and serialization logic in ConvertOfflineRecognizerConfig and ToString methods to handle the new field.
Pascal Example & Build Script
pascal-api-examples/non-streaming-asr/moonshine_v2.pas, pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh
Added standalone Pascal program demonstrating non-streaming Moonshine v2 decoding with configurable encoder/decoder and tokens models. Added Bash script that orchestrates environment setup, builds Sherpa ONNX C API libraries if needed, downloads required models, compiles the Pascal example with FPC, and executes the binary.

Possibly related PRs

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Moonshine v2 hops into Pascal's embrace,
With merged decoders in all the right place,
The bunny's compiler now speaks the new song,
While scripts and examples hop merrily along,
One more language bound, the ONNX shines bright! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Pascal API for Moonshine v2' accurately reflects the main changes in the pull request, which include adding a new Moonshine v2 Pascal example, supporting script, and API extensions to handle the merged decoder configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 the Moonshine v2 model to the Pascal API, including a new example program and a run script. The changes to the sherpa_onnx.pas file correctly add the MergedDecoder field for the new model. My feedback focuses on improving the maintainability of the new example script and Pascal program by reducing hardcoded strings.

Comment on lines +36 to +46
Initialize(Config);

Config.ModelConfig.Moonshine.Encoder := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/encoder_model.ort';
Config.ModelConfig.Moonshine.MergedDecoder := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/decoder_model_merged.ort';

Config.ModelConfig.Tokens := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/tokens.txt';
Config.ModelConfig.Provider := 'cpu';
Config.ModelConfig.NumThreads := 1;
Config.ModelConfig.Debug := False;

WaveFilename := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/test_wavs/0.wav';

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

The model directory path is repeated multiple times. To improve maintainability, you can define it as a local variable. This makes it easier to update the path in one place if needed.

  var
    ModelDir: AnsiString;
  Initialize(Config);

  ModelDir := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27';

  Config.ModelConfig.Moonshine.Encoder := ModelDir + '/encoder_model.ort';
  Config.ModelConfig.Moonshine.MergedDecoder := ModelDir + '/decoder_model_merged.ort';

  Config.ModelConfig.Tokens := ModelDir + '/tokens.txt';
  Config.ModelConfig.Provider := 'cpu';
  Config.ModelConfig.NumThreads := 1;
  Config.ModelConfig.Debug := False;

  WaveFilename := ModelDir + '/test_wavs/0.wav';

Comment on lines +27 to +31
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
fi

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

The model name sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27 is hardcoded multiple times. To improve maintainability, it's better to define it as a variable. This makes it easier to update the model version in the future.

Suggested change
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
fi
model_name="sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27"
if [ ! -f ./${model_name}/encoder_model.ort ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/${model_name}.tar.bz2
tar xvf ${model_name}.tar.bz2
rm ${model_name}.tar.bz2
fi

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

🧹 Nitpick comments (3)
pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh (2)

22-24: Minor: ls shows build directory, not install directory.

The ls -lh lib command lists the build output directory, but the library check at line 10 looks for files in ../../build/install/lib. For consistency, consider listing the install directory instead:

🔧 Proposed fix
   cmake --build . --target install --config Release
-  ls -lh lib
+  ls -lh install/lib
   popd
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh` around lines 22 -
24, The script currently runs the build (cmake --build . --target install
--config Release) but then executes ls -lh lib which lists the build output
directory; change that ls invocation to list the actual install directory used
by the earlier check (use the ../../build/install/lib path referenced earlier)
so the directory inspected matches the install check—update the ls command
(replace ls -lh lib) to ls -lh ../../build/install/lib (or equivalent install
dir variable) to keep outputs consistent.

5-6: Quote variable in path to handle spaces.

The $SCRIPT_DIR variable should be quoted when used in the cd command to properly handle paths containing spaces.

🔧 Proposed fix
 SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-SHERPA_ONNX_DIR=$(cd $SCRIPT_DIR/../.. && pwd)
+SHERPA_ONNX_DIR=$(cd "$SCRIPT_DIR/../.." && pwd)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh` around lines 5 -
6, The SHERPA_ONNX_DIR assignment uses cd $SCRIPT_DIR/../.. which will break on
paths with spaces; update the cd invocation to quote the SCRIPT_DIR variable
(use "$SCRIPT_DIR" when composing the path) so the shell treats the directory
with spaces as a single argument and SHERPA_ONNX_DIR is computed correctly;
reference: variables SCRIPT_DIR and SHERPA_ONNX_DIR and the use of BASH_SOURCE
in the SCRIPT_DIR assignment.
pascal-api-examples/non-streaming-asr/moonshine_v2.pas (1)

46-48: Consider adding error handling for wave file loading.

If SherpaOnnxReadWave fails to load the file (file not found, invalid format), Wave.Samples will be empty and Wave.SampleRate will be 0, causing a division by zero at line 62 when calculating Duration.

While this example script is straightforward and the run script ensures the file exists, adding a simple check would make the example more robust for users adapting it:

💡 Proposed defensive check
   WaveFilename := './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/test_wavs/0.wav';

   Wave := SherpaOnnxReadWave(WaveFilename);
+
+  if (Wave.Samples = nil) or (Wave.SampleRate = 0) then
+  begin
+    WriteLn('Failed to read wave file: ', WaveFilename);
+    Exit;
+  end;

   Recognizer := TSherpaOnnxOfflineRecognizer.Create(Config);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pascal-api-examples/non-streaming-asr/moonshine_v2.pas` around lines 46 - 48,
Check the result of SherpaOnnxReadWave for a valid waveform before using
Wave.Samples/ Wave.SampleRate: after calling SherpaOnnxReadWave(WaveFilename)
validate that (Wave.SampleRate > 0) and (Length(Wave.Samples) > 0), and if the
check fails emit a clear message including WaveFilename (e.g., Writeln or
process logger) and abort (Halt(1) or raise an exception) so the subsequent
Duration calculation cannot divide by zero; update references around
WaveFilename, SherpaOnnxReadWave, Wave.Samples and Wave.SampleRate accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pascal-api-examples/non-streaming-asr/moonshine_v2.pas`:
- Around line 46-48: Check the result of SherpaOnnxReadWave for a valid waveform
before using Wave.Samples/ Wave.SampleRate: after calling
SherpaOnnxReadWave(WaveFilename) validate that (Wave.SampleRate > 0) and
(Length(Wave.Samples) > 0), and if the check fails emit a clear message
including WaveFilename (e.g., Writeln or process logger) and abort (Halt(1) or
raise an exception) so the subsequent Duration calculation cannot divide by
zero; update references around WaveFilename, SherpaOnnxReadWave, Wave.Samples
and Wave.SampleRate accordingly.

In `@pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh`:
- Around line 22-24: The script currently runs the build (cmake --build .
--target install --config Release) but then executes ls -lh lib which lists the
build output directory; change that ls invocation to list the actual install
directory used by the earlier check (use the ../../build/install/lib path
referenced earlier) so the directory inspected matches the install check—update
the ls command (replace ls -lh lib) to ls -lh ../../build/install/lib (or
equivalent install dir variable) to keep outputs consistent.
- Around line 5-6: The SHERPA_ONNX_DIR assignment uses cd $SCRIPT_DIR/../..
which will break on paths with spaces; update the cd invocation to quote the
SCRIPT_DIR variable (use "$SCRIPT_DIR" when composing the path) so the shell
treats the directory with spaces as a single argument and SHERPA_ONNX_DIR is
computed correctly; reference: variables SCRIPT_DIR and SHERPA_ONNX_DIR and the
use of BASH_SOURCE in the SCRIPT_DIR assignment.

ℹ️ 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 93692f3.

📒 Files selected for processing (5)
  • .github/workflows/pascal.yaml
  • pascal-api-examples/non-streaming-asr/.gitignore
  • pascal-api-examples/non-streaming-asr/moonshine_v2.pas
  • pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh
  • sherpa-onnx/pascal-api/sherpa_onnx.pas

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

This PR adds Pascal API support for Moonshine v2 speech recognition models, which use a different architecture than Moonshine v1 (a single MergedDecoder instead of separate UncachedDecoder/CachedDecoder + Preprocessor). It extends the existing Moonshine model configuration in the Pascal bindings and adds a full working example.

Changes:

  • Adds MergedDecoder field to the TSherpaOnnxOfflineMoonshineModelConfig Pascal record (and corresponding C-interop struct), matching the field already defined in the C header.
  • Introduces a new example program moonshine_v2.pas and build/run script run-moonshine-v2.sh demonstrating usage with the v2 model.
  • Registers the new example in the CI workflow and .gitignore.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
sherpa-onnx/pascal-api/sherpa_onnx.pas Adds MergedDecoder field to both the high-level Pascal record and C-interop struct; updates ToString and ConvertOfflineRecognizerConfig accordingly
pascal-api-examples/non-streaming-asr/moonshine_v2.pas New example program using Moonshine v2 with encoder + merged decoder
pascal-api-examples/non-streaming-asr/run-moonshine-v2.sh New build and run script for the moonshine v2 example
pascal-api-examples/non-streaming-asr/.gitignore Adds moonshine_v2 binary to the ignore list
.github/workflows/pascal.yaml Adds CI step to run the new moonshine v2 example

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

rm -rf sherpa-onnx-*
echo "---"

./run-moonshine-v2.sh

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.

The ./run-moonshine-v2.sh step is missing the rm -rf sherpa-onnx-* and echo "---" lines that every other step in the workflow uses. While the moonshine-v2 directory (sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27) will eventually be cleaned up by the rm -rf sherpa-onnx-* that follows run-moonshine.sh, the inconsistency could leave artifacts during the run-moonshine.sh run. The pattern used throughout the workflow is that each script is immediately followed by its own cleanup and separator. This should be corrected to match the established pattern.

Suggested change
./run-moonshine-v2.sh
./run-moonshine-v2.sh
rm -rf sherpa-onnx-*
echo "---"

Copilot uses AI. Check for mistakes.
@csukuangfj
csukuangfj merged commit c93a2a1 into k2-fsa:master Feb 28, 2026
5 checks passed
@csukuangfj
csukuangfj deleted the pascal-api-moonshine branch February 28, 2026 07:52
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