Skip to content

Add JavaScript API (WebAssembly) for Moonshine v2 models - #3241

Merged
csukuangfj merged 2 commits into
k2-fsa:masterfrom
csukuangfj:javascript-api-moonshine-v2-wasm
Feb 28, 2026
Merged

csukuangfj merged 2 commits into
k2-fsa:masterfrom
csukuangfj:javascript-api-moonshine-v2-wasm

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Feb 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added Moonshine v2 offline recognition with merged-decoder support
    • Added FireRedASR CTC offline model support
  • Documentation

    • Added usage examples and download/run instructions for Moonshine v2 offline decoding
  • Tests

    • Added automated offline tests that download, run, and clean up Moonshine v2 and FireRedASR CTC models
  • Style

    • Made variable declarations and output formatting more robust and consistent

@dosubot dosubot Bot added the size:M This PR changes 30-99 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 sherpa-onnx library's capabilities by adding comprehensive support for Moonshine v2 models within its JavaScript/WebAssembly API. This includes necessary modifications to model configurations, the introduction of a new example for practical usage, and updates to the continuous integration pipeline to ensure stability and correctness.

Highlights

  • Moonshine v2 Model Support: Introduced JavaScript API (WebAssembly) support for Moonshine v2 models, enabling their use in Node.js environments.
  • New Offline Decoding Example: Added a new Node.js example (test-offline-moonshine-v2.js) demonstrating how to perform offline decoding with Moonshine v2 models.
  • Configuration Updates: Updated the underlying WebAssembly and Node.js C++ bindings to correctly handle the mergedDecoder path required by Moonshine v2 models.
  • CI Integration: Integrated a new test case into the Node.js npm CI script to validate the functionality of Moonshine v2 models.
Changelog
  • .github/scripts/test-nodejs-npm.sh
    • Added a new test block for downloading, extracting, running, and cleaning up a Moonshine v2 model for Node.js testing.
  • nodejs-examples/README.md
    • Added a new section documenting the test-offline-moonshine-v2.js example, including instructions for running it.
  • nodejs-examples/test-offline-moonshine-v2.js
    • Added a new JavaScript file demonstrating how to create an offline recognizer and decode a waveform using a Moonshine v2 model.
  • wasm/asr/sherpa-onnx-asr.js
    • Modified initSherpaOnnxOfflineMoonshineModelConfig to account for a new mergedDecoder path, adjusting memory allocation and pointer handling.
    • Added mergedDecoder to the default moonshine configuration within initSherpaOnnxOfflineModelConfig.
  • wasm/nodejs/sherpa-onnx-wasm-nodejs.cc
    • Updated the static_assert for SherpaOnnxOfflineMoonshineModelConfig to reflect its increased size (from 44 to 54 bytes).
    • Added merged_decoder to the PrintOfflineRecognizerConfig function for Moonshine models, allowing its value to be printed to stdout.
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

Adds Moonshine v2 mergedDecoder support to WASM bindings, a Node.js offline test and docs, and CI steps to download/extract/run Moonshine v2 (and FireRedASR CTC) offline tests; also tightens several example scripts' variable declarations.

Changes

Cohort / File(s) Summary
CI / Test script
.github/scripts/test-nodejs-npm.sh
Adds download/extract/run/cleanup steps for Moonshine v2 and downloads FireRedASR CTC model (invocation/cleanup for FireRedASR not shown here).
Documentation
nodejs-examples/README.md
Adds Moonshine v2 offline test usage sections with download/extract/run commands.
New Node.js test
nodejs-examples/test-offline-moonshine-v2.js
New script creating an offline Moonshine recognizer (encoder, mergedDecoder, tokens), feeding a WAV, decoding, logging result, and freeing resources.
Node.js example tweaks
nodejs-examples/test-offline-fire-red-asr.js, nodejs-examples/test-offline-moonshine.js, nodejs-examples/test-offline-whisper.js, nodejs-examples/test-offline-speech-enhancement-gtcrn.js
Replace implicit globals with const declarations (scoping/strictness fixes) and minor formatting edits.
WASM ASR logic
wasm/asr/sherpa-onnx-asr.js
Adds mergedDecoder handling to Moonshine config: length calc, serialization/deserialization, and default empty field.
WASM Node.js bindings
wasm/nodejs/sherpa-onnx-wasm-nodejs.cc
Updates static_assert for increased config pointer count (4→5) and prints merged_decoder field in debug output.

Sequence Diagram(s)

sequenceDiagram
    participant CI as CI Script
    participant Host as Model Host (releases)
    participant Runner as Node.js Runner
    participant WASM as sherpa-onnx WASM
    participant FS as Local FS/WAV

    CI->>Host: download Moonshine v2 model archive
    Host-->>CI: tar.gz
    CI->>FS: extract model files
    CI->>Runner: run test-offline-moonshine-v2.js
    Runner->>WASM: load model files (encoder, mergedDecoder, tokens)
    Runner->>FS: read test WAV
    Runner->>WASM: feed waveform -> decode
    WASM-->>Runner: decoded text
    Runner-->>CI: exit (log output)
    CI->>FS: cleanup extracted files
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

size:M

Poem

🐰 Moonshine twinkles in my paws tonight,
I stitched a decoder, tidy and light.
WASM hums, Node.js sings, models roam,
Tests hop, logs sparkle—welcome home! ✨

🚥 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 directly and accurately summarizes the main change: adding JavaScript/WebAssembly API support for Moonshine v2 models, which is reflected throughout the changeset.

✏️ 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 JavaScript API support for Moonshine v2 models, including a new test example and updates to the build/test scripts. The changes look good overall. I've provided a few suggestions to improve the new test script and example code by using constants/variables for repeated strings and fixing variable declarations. These changes will enhance maintainability and code quality.

Comment on lines +23 to +24
recognizer = createOfflineRecognizer();
stream = recognizer.createStream();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Variables recognizer and stream are being assigned without being declared with var, let, or const. This makes them global variables, which is generally considered bad practice. You should declare them with const since they are not reassigned.

Suggested change
recognizer = createOfflineRecognizer();
stream = recognizer.createStream();
const recognizer = createOfflineRecognizer();
const stream = recognizer.createStream();

Comment on lines +14 to +20
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

node ./test-offline-moonshine-v2.js

rm -rf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27

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 maintainability and reduce the chance of typos, you can store the model name in a variable and reuse it.

Suggested change
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
node ./test-offline-moonshine-v2.js
rm -rf sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27
MODEL_NAME="sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27"
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
node ./test-offline-moonshine-v2.js
rm -rf ${MODEL_NAME}

Comment thread nodejs-examples/README.md
Comment on lines +434 to +438
wget 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

node ./test-offline-moonshine-v2.js

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 make the script easier to read and maintain, consider using a variable for the model name.

Suggested change
wget 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
node ./test-offline-moonshine-v2.js
MODEL_NAME="sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27"
wget 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
node ./test-offline-moonshine-v2.js

let modelConfig = {
moonshine: {
encoder:
'./sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27/encoder_model.ort',

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 './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27' is repeated in this file (lines 9, 11, 13, 27). It would be better to define it as a constant at the top of the file to improve maintainability and avoid potential typos. For example: const modelDir = './sherpa-onnx-moonshine-tiny-en-quantized-2026-02-27'; and then use it like ${modelDir}/encoder_model.ort.

@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 (1)
nodejs-examples/test-offline-moonshine-v2.js (1)

23-24: Add const declarations to avoid implicit globals.

The recognizer and stream variables are assigned without const or let, creating implicit global variables. This works but is not best practice.

Suggested fix
-recognizer = createOfflineRecognizer();
-stream = recognizer.createStream();
+const recognizer = createOfflineRecognizer();
+const stream = recognizer.createStream();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodejs-examples/test-offline-moonshine-v2.js` around lines 23 - 24, The
variables recognizer and stream are being assigned without declarations,
creating implicit globals; update the code where createOfflineRecognizer() and
recognizer.createStream() are called to declare them with const (e.g., const
recognizer = createOfflineRecognizer(); const stream =
recognizer.createStream();) so they become local constants and avoid polluting
the global scope.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@nodejs-examples/test-offline-moonshine-v2.js`:
- Around line 23-24: The variables recognizer and stream are being assigned
without declarations, creating implicit globals; update the code where
createOfflineRecognizer() and recognizer.createStream() are called to declare
them with const (e.g., const recognizer = createOfflineRecognizer(); const
stream = recognizer.createStream();) so they become local constants and avoid
polluting the global scope.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8519d61 and 9990674.

📒 Files selected for processing (5)
  • .github/scripts/test-nodejs-npm.sh
  • nodejs-examples/README.md
  • nodejs-examples/test-offline-moonshine-v2.js
  • wasm/asr/sherpa-onnx-asr.js
  • wasm/nodejs/sherpa-onnx-wasm-nodejs.cc

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 WebAssembly/JavaScript surface support for Moonshine v2 offline decoding by wiring the new merged_decoder field through the WASM config plumbing and providing a Node.js example + CI exercise.

Changes:

  • Extend Moonshine offline model config in WASM Node.js glue and JS initializer to include mergedDecoder/merged_decoder.
  • Add a Node.js example for Moonshine v2 merged-decoder usage and document how to run it.
  • Update the Node.js npm CI script to download a Moonshine v2 model and run the new example.

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
wasm/nodejs/sherpa-onnx-wasm-nodejs.cc Updates struct size assertion and prints merged_decoder in offline recognizer config dump.
wasm/asr/sherpa-onnx-asr.js Adds mergedDecoder allocation/packing into the WASM Moonshine config and default config field.
nodejs-examples/test-offline-moonshine-v2.js New Node.js example showing Moonshine v2 decoding using encoder + mergedDecoder.
nodejs-examples/README.md Documents how to download the Moonshine v2 model and run the new example.
.github/scripts/test-nodejs-npm.sh CI coverage: downloads Moonshine v2 model, runs the new example, and cleans up.

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

Comment on lines +23 to +24
recognizer = createOfflineRecognizer();
stream = recognizer.createStream();

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.

recognizer and stream are assigned without const/let, which creates implicit globals in non-strict mode (and will throw in strict mode). Declare them (e.g., const recognizer = ..., const stream = ...) to avoid polluting the global scope and to keep the example robust across runtimes/tooling.

Suggested change
recognizer = createOfflineRecognizer();
stream = recognizer.createStream();
const recognizer = createOfflineRecognizer();
const stream = recognizer.createStream();

Copilot uses AI. Check for mistakes.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Feb 28, 2026

@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 (2)
nodejs-examples/test-online-paraformer-microphone-mic.js (2)

2-4: Minor: Line-wrapped require statement looks unusual.

While functionally correct, the line-wrapped require('mic') with the trailing comment spanning multiple lines is unconventional. Consider keeping the require on a single line with the comment on a separate line for better readability.

📝 Suggested format
-const mic = require(
-    'mic');  // It uses `mic` for better compatibility, do check its
-             // [npm](https://www.npmjs.com/package/mic) before running it.
+// It uses `mic` for better compatibility, do check its
+// npm page (https://www.npmjs.com/package/mic) before running it.
+const mic = require('mic');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodejs-examples/test-online-paraformer-microphone-mic.js` around lines 2 - 4,
The require call for the mic module is line-wrapped and the long trailing
comment reduces readability; change the statement that assigns the mic variable
so it uses a single-line require (mic = require('mic')) and move the explanatory
npm comment to its own preceding line so the require remains concise and the
comment stays clear; update the occurrence that sets const mic to use this
single-line form and separate the comment above it.

80-91: Consider using English comments for consistency.

The file mixes English and Chinese comments (e.g., 显示历史句子, 显示当前正在识别的内容). For consistency with the rest of the codebase and to aid contributors who may not read Chinese, consider translating these to English.

📝 Suggested translation
-  // 显示历史句子
+  // Display past sentences
   if (currentSession.sentences.length > 0) {
     console.log('Recognized Sentences:');
     currentSession.sentences.forEach((sentence, index) => {
       console.log(`[${sentence.timestamp}] ${index + 1}. ${sentence.text}`);
     });
     console.log('------------------------');
   }

-  // 显示当前正在识别的内容
+  // Display currently recognizing content
   if (currentSession.currentText) {
     console.log('Recognizing:', currentSession.currentText);
   }

Also at line 120:

-  debug: false,  // 关闭调试输出
+  debug: false,  // Disable debug output

And at line 164:

-    // 检查是否需要开始新会话
+    // Check if we need to start a new session
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodejs-examples/test-online-paraformer-microphone-mic.js` around lines 80 -
91, Replace Chinese inline comments with English equivalents for consistency:
change the comment above the currentSession.sentences block (originally
"显示历史句子") to an English comment like "Display recognized sentences" and change
the comment before currentSession.currentText (originally "显示当前正在识别的内容") to
"Display currently recognizing text"; also scan this file for other Chinese
comments (e.g., other comments near currentSession usage) and translate them to
English so all comments are consistently in English.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@nodejs-examples/test-online-paraformer-microphone-mic.js`:
- Around line 2-4: The require call for the mic module is line-wrapped and the
long trailing comment reduces readability; change the statement that assigns the
mic variable so it uses a single-line require (mic = require('mic')) and move
the explanatory npm comment to its own preceding line so the require remains
concise and the comment stays clear; update the occurrence that sets const mic
to use this single-line form and separate the comment above it.
- Around line 80-91: Replace Chinese inline comments with English equivalents
for consistency: change the comment above the currentSession.sentences block
(originally "显示历史句子") to an English comment like "Display recognized sentences"
and change the comment before currentSession.currentText (originally
"显示当前正在识别的内容") to "Display currently recognizing text"; also scan this file for
other Chinese comments (e.g., other comments near currentSession usage) and
translate them to English so all comments are consistently in English.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9990674 and a1659f9.

📒 Files selected for processing (6)
  • nodejs-examples/test-offline-fire-red-asr.js
  • nodejs-examples/test-offline-moonshine-v2.js
  • nodejs-examples/test-offline-moonshine.js
  • nodejs-examples/test-offline-speech-enhancement-gtcrn.js
  • nodejs-examples/test-offline-whisper.js
  • nodejs-examples/test-online-paraformer-microphone-mic.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • nodejs-examples/test-offline-moonshine-v2.js

@csukuangfj
csukuangfj merged commit d9e3fcf into k2-fsa:master Feb 28, 2026
1 check passed
@csukuangfj
csukuangfj deleted the javascript-api-moonshine-v2-wasm branch February 28, 2026 04:21
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