Skip to content

Add Go API for Google MedAsr model - #2952

Merged
csukuangfj merged 2 commits into
k2-fsa:masterfrom
csukuangfj:go-api-medasr
Dec 29, 2025
Merged

csukuangfj merged 2 commits into
k2-fsa:masterfrom
csukuangfj:go-api-medasr

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Dec 29, 2025 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added MedASR CTC model configuration support for offline speech-to-text decoding
    • Added a non-streaming MedASR CTC decoding example that processes WAV files and outputs decoded text
    • Added Windows DLL distribution for non-streaming MedASR decoding
  • Tests

    • Added automated test steps/workflow to build, run, and validate non-streaming MedASR decoding artifacts
  • Chores

    • Added project/module/run helpers and ignore entries to support examples and CI execution

✏️ Tip: You can customize this high-level summary in your review settings.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Dec 29, 2025
@coderabbitai

coderabbitai Bot commented Dec 29, 2025 •

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a non-streaming MedASR CTC Go example and internal wrapper, updates Go bindings to support MedASR model config, and integrates build/run steps for this example into the test-go CI workflow (including Windows DLL distribution and cleanup).

Changes

Cohort / File(s) Summary
Non-streaming MedASR decoding example
go-api-examples/non-streaming-medasr-ctc-decode-files/go.mod, go-api-examples/non-streaming-medasr-ctc-decode-files/main.go, go-api-examples/non-streaming-medasr-ctc-decode-files/run.sh
New Go module and example binary that reads 16-bit PCM WAV, converts samples to float32, runs OfflineRecognizer decoding with MedASR CTC model, and prints lowercase text. Run script downloads model tokens, builds, and runs the example.
Internal build wrapper
scripts/go/_internal/non-streaming-medasr-ctc-decode-files/.gitignore, scripts/go/_internal/non-streaming-medasr-ctc-decode-files/go.mod, scripts/go/_internal/non-streaming-medasr-ctc-decode-files/main.go, scripts/go/_internal/non-streaming-medasr-ctc-decode-files/run.sh
Adds internal wrapper module and scripts that reference the example implementation (go.mod with local replace, thin main/run wrappers, and .gitignore).
Go bindings enhancement
scripts/go/sherpa_onnx.go
Adds OfflineMedAsrCtcModelConfig and extends OfflineModelConfig to include MedAsr; populates C offline recognizer config with MedASR model path and frees the C string during cleanup.
CI/CD workflow integration
.github/workflows/test-go.yaml
Adds Windows DLL distribution of MedASR artifacts and new test steps to build and run the non-streaming MedASR example (inspect files, go mod tidy, build, run.sh, and remove sherpa-onnx-medasr-* artifacts).

Sequence Diagram(s)

sequenceDiagram
    rect rgb(240,248,255)
    participant User as Developer/CI
    participant GoApp as Go example (main)
    participant GoBindings as scripts/go/sherpa_onnx.go
    participant CLib as SherpaONNX C library
    participant ModelFiles as MedASR model & tokens
    Note over User,GoApp: Run build & execute (local or CI)
    end
    User->>GoApp: execute binary / run.sh
    GoApp->>ModelFiles: ensure tokens and model present (download/unpack)
    GoApp->>GoBindings: create OfflineRecognizerConfig (includes MedAsr model path)
    GoBindings->>CLib: marshal config -> C struct (set model_config.medasr.model)
    GoApp->>CLib: create stream, send waveform samples
    CLib-->>GoApp: decoding events/results
    GoApp->>User: print recognized text
    Note right of CLib: cleanup frees C strings (medasr.model) 
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
I hopped through code and found a new tune,
WAVs turned to text beneath the moon.
Bindings stitched tight and CI gives a cheer,
MedASR decodes — hop, build, run — we’re here!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 accurately summarizes the main change: adding Go API support for the Google MedASR model, which is reflected across all modified and new files including the Go module, main implementation, test workflows, and API configuration.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9dbeee and 12d6b99.

📒 Files selected for processing (1)
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/go.mod
✅ Files skipped from review due to trivial changes (1)
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/go.mod

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

Copy link
Copy Markdown

Summary of Changes

Hello @csukuangfj, 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 significantly enhances the sherpa-onnx-go library by adding comprehensive support for the Google MedAsr model. It provides the essential Go API structures and functions required to configure and utilize MedAsr models for offline speech recognition tasks, alongside a practical, self-contained example that streamlines the process of model acquisition and demonstration.

Highlights

  • New Go API for MedAsr: Introduced a new OfflineMedAsrCtcModelConfig struct and integrated it into the OfflineModelConfig to support Google MedAsr models within the sherpa-onnx-go library.
  • MedAsr Example Added: A complete Go example (non-streaming-medasr-ctc-decode-files) has been added, demonstrating how to perform non-streaming automatic speech recognition using the Google MedAsr model.
  • Automated Model Download: The new example includes a run.sh script that automatically downloads the necessary MedAsr model from a release URL, simplifying setup for users.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/test-go.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. ↩

@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 Google MedASR model to the Go API. This includes changes to the Go bindings to incorporate the new model configuration and a new example demonstrating its usage. The changes to the Go bindings are correct and follow the existing conventions. The new example code has a few issues, mainly related to error handling and performance, which should be addressed. I've provided specific comments and suggestions for these improvements.

}

func readWave(filename string) (samples []float32, sampleRate int) {
file, _ := os.Open(filename)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The error returned by os.Open() is ignored. If the file does not exist or cannot be opened, this will lead to a panic later in the code. You should handle this error to make the program more robust.

Suggested change
file, _ := os.Open(filename)
file, err := os.Open(filename)
if err != nil {
log.Fatalf("Failed to open %s: %v", filename, err)
}

Comment on lines +69 to +72
n, err := reader.Read(buf)
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Returned %v bytes\n", reader.Size, n)
}

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

The error returned by reader.Read() is not checked. An error during reading could lead to processing incomplete or corrupt data. It's important to handle this error. Also, log.Fatalf automatically adds a newline, so the \n at the end of the format string is redundant.

Suggested change
n, err := reader.Read(buf)
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Returned %v bytes\n", reader.Size, n)
}
n, err := reader.Read(buf)
if err != nil {
log.Fatalf("Failed to read wave data: %v", err)
}
if n != int(reader.Size) {
log.Fatalf("Failed to read %v bytes. Got %v bytes", reader.Size, n)
}

Comment on lines +19 to +22
config.ModelConfig.MedAsr.Model = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/model.int8.onnx"
config.ModelConfig.Tokens = "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/tokens.txt"

waveFilename := "./sherpa-onnx-medasr-ctc-en-int8-2025-12-25/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 file paths for the model, tokens, and wave file are hardcoded. While this is an example, making them configurable via command-line flags would make the example more flexible and easier to use with different files. Consider using the flag package for this.

Comment on lines +49 to +52
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format")
}

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

When logging a fatal error, it's helpful to include the underlying error message for better debugging.

Suggested change
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format")
}
format, err := reader.Format()
if err != nil {
log.Fatalf("Failed to read wave format: %v", err)
}

Comment on lines +84 to +94
for i := 0; i != numSamples; i++ {
s := inSamples[i*2 : (i+1)*2]

var s16 int16
buf := bytes.NewReader(s)
err := binary.Read(buf, binary.LittleEndian, &s16)
if err != nil {
log.Fatal("Failed to parse 16-bit sample")
}
outSamples[i] = float32(s16) / 32768
}

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 current implementation of samplesInt16ToFloat creates a new bytes.Reader for every sample inside the loop, which is inefficient. You can achieve better performance by directly reading from the byte slice using binary.LittleEndian.Uint16.

for i := 0; i < numSamples; i++ {
		s16 := int16(binary.LittleEndian.Uint16(inSamples[i*2:]))
		outSamples[i] = float32(s16) / 32768.0
	}

@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: 2

🧹 Nitpick comments (1)
scripts/go/_internal/non-streaming-medasr-ctc-decode-files/.gitignore (1)

1-1: Consider adding Windows executable pattern for cross-platform support.

The .gitignore entry currently ignores only the Unix binary name. For cross-platform projects, add a pattern to also ignore Windows executables (.exe extension).

🔎 Proposed improvement
-non-streaming-medasr-ctc-decode-files
+non-streaming-medasr-ctc-decode-files
+*.exe

Alternatively, use a glob pattern to match both:

-non-streaming-medasr-ctc-decode-files
+non-streaming-medasr-ctc-decode-files*
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13b8b84 and e9dbeee.

📒 Files selected for processing (9)
  • .github/workflows/test-go.yaml
  • go-api-examples/non-streaming-medasr-ctc-decode-files/go.mod
  • go-api-examples/non-streaming-medasr-ctc-decode-files/main.go
  • go-api-examples/non-streaming-medasr-ctc-decode-files/run.sh
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/.gitignore
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/go.mod
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/main.go
  • scripts/go/_internal/non-streaming-medasr-ctc-decode-files/run.sh
  • scripts/go/sherpa_onnx.go
🧰 Additional context used
🧬 Code graph analysis (2)
scripts/go/sherpa_onnx.go (1)
sherpa-onnx/c-api/cxx-api.h (1)
  • OfflineMedAsrCtcModelConfig (275-277)
go-api-examples/non-streaming-medasr-ctc-decode-files/main.go (1)
scripts/go/sherpa_onnx.go (5)
  • OfflineRecognizerConfig (513-529)
  • NewOfflineRecognizer (830-842)
  • DeleteOfflineRecognizer (823-826)
  • NewOfflineStream (860-864)
  • DeleteOfflineStream (853-856)
🔇 Additional comments (15)
scripts/go/_internal/non-streaming-medasr-ctc-decode-files/main.go (1)

1-1: LGTM - Consistent reference pattern.

This file follows the project's pattern of referencing the actual implementation from the internal test directory.

scripts/go/_internal/non-streaming-medasr-ctc-decode-files/go.mod (1)

1-15: LGTM - Complete module configuration.

The module manifest properly declares all dependencies with the correct replace directive for local development.

scripts/go/sherpa_onnx.go (4)

426-428: LGTM - Consistent struct definition.

The OfflineMedAsrCtcModelConfig struct follows the established pattern for CTC model configurations and matches the C++ API structure.


492-492: LGTM - Proper integration into model config.

The MedAsr field is correctly integrated into OfflineModelConfig alongside other model types.


597-597: LGTM - Correct C string allocation.

The C string is properly allocated and will be freed by freeCOfflineRecognizerConfig.


751-754: LGTM - Proper memory cleanup.

The MedASR model string is correctly freed with appropriate nil check, following the established pattern.

go-api-examples/non-streaming-medasr-ctc-decode-files/main.go (3)

14-42: LGTM - Clear example implementation.

The main function demonstrates proper usage of the MedASR model with appropriate resource cleanup using defer statements.


66-72: LGTM - Proper wave data reading.

The code correctly initializes the reader size and validates the number of bytes read.


80-97: LGTM - Correct PCM to float conversion.

The function properly converts 16-bit PCM samples to normalized float32 values with appropriate endianness handling.

scripts/go/_internal/non-streaming-medasr-ctc-decode-files/run.sh (1)

1-1: LGTM - Consistent reference pattern.

This file follows the project's pattern of referencing the actual implementation script from the internal test directory.

go-api-examples/non-streaming-medasr-ctc-decode-files/run.sh (3)

1-5: LGTM - Proper shell script setup.

The script correctly enables error handling and sets CGO_ENABLED for C bindings.


7-11: LGTM - Efficient model download.

The script efficiently checks for existing files before downloading and properly cleans up the archive.


13-15: LGTM - Standard Go build workflow.

The build and execution sequence is correct.

.github/workflows/test-go.yaml (2)

105-105: LGTM - Consistent Windows DLL distribution.

The DLL copy follows the established pattern for other test directories.


145-157: LGTM - Well-structured test step.

The MedASR test step follows the established pattern for other model tests with appropriate cleanup.

Comment on lines +1 to +3
module non-streaming-medasr-ctc-decode-files

go 1.17

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

Add missing dependencies to the module manifest.

The main.go file imports github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx and github.com/youpy/go-wav, but this go.mod does not declare them. Users attempting to build this example directly will encounter unresolved dependency errors.

🔎 Add the missing dependencies
 module non-streaming-medasr-ctc-decode-files
 
 go 1.17
+
+replace github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx => ../../scripts/go
+
+require (
+	github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx v0.0.0-00010101000000-000000000000
+	github.com/youpy/go-wav v0.3.2
+)
📝 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
module non-streaming-medasr-ctc-decode-files
go 1.17
module non-streaming-medasr-ctc-decode-files
go 1.17
replace github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx => ../../scripts/go
require (
github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx v0.0.0-00010101000000-000000000000
github.com/youpy/go-wav v0.3.2
)
🤖 Prompt for AI Agents
In go-api-examples/non-streaming-medasr-ctc-decode-files/go.mod lines 1-3, the
module manifest is missing required dependencies used in main.go; add require
directives for github.com/k2-fsa/sherpa-onnx-go/sherpa_onnx and
github.com/youpy/go-wav with appropriate semantic versions (or run the module
add command to resolve and populate go.mod and go.sum) so the example builds
without unresolved dependency errors.

Comment on lines +44 to +46
func readWave(filename string) (samples []float32, sampleRate int) {
file, _ := os.Open(filename)
defer file.Close()

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 | 🔴 Critical

Fix unhandled file open error.

The error from os.Open is ignored, which will cause a nil pointer dereference panic when defer file.Close() is executed if the file cannot be opened.

🔎 Handle the error properly
 func readWave(filename string) (samples []float32, sampleRate int) {
-	file, _ := os.Open(filename)
+	file, err := os.Open(filename)
+	if err != nil {
+		log.Fatalf("Failed to open file: %v", err)
+	}
 	defer file.Close()
📝 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
func readWave(filename string) (samples []float32, sampleRate int) {
file, _ := os.Open(filename)
defer file.Close()
func readWave(filename string) (samples []float32, sampleRate int) {
file, err := os.Open(filename)
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
defer file.Close()
🤖 Prompt for AI Agents
In go-api-examples/non-streaming-medasr-ctc-decode-files/main.go around lines
44-46, the call to os.Open ignores the error and defers file.Close()
immediately, which can panic if the open fails; change the code to capture the
error (file, err := os.Open(filename)), check if err != nil and handle it
(return nil, 0 or propagate/log the error as appropriate for this package), and
only call defer file.Close() after confirming file is non-nil; ensure callers
can distinguish the failure by returning zero values or returning the error up
the call chain.

@csukuangfj
csukuangfj merged commit d2b8492 into k2-fsa:master Dec 29, 2025
1 check passed
@csukuangfj
csukuangfj deleted the go-api-medasr branch December 29, 2025 04:48
@coderabbitai coderabbitai Bot mentioned this pull request Mar 25, 2026
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.

1 participant