Skip to content

Add Kotlin and Java API for FunASR Nano models - #3030

Merged
csukuangfj merged 4 commits into
k2-fsa:masterfrom
csukuangfj:jni-fun-asr-nano
Jan 12, 2026
Merged

csukuangfj merged 4 commits into
k2-fsa:masterfrom
csukuangfj:jni-fun-asr-nano

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Jan 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for FunASR Nano, a lightweight offline speech recognition model for converting audio files to text.
    • Included example programs demonstrating FunASR Nano usage.
  • Chores

    • Updated testing workflows and build configurations to support the new model.

✏️ 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 Jan 12, 2026
@coderabbitai

coderabbitai Bot commented Jan 12, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@csukuangfj has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a6a9d91 and facc67c.

📒 Files selected for processing (1)
  • sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java
📝 Walkthrough

Walkthrough

Adds comprehensive FunASR Nano offline recognition support across Java and Kotlin APIs, including new model configuration classes, JNI parsing logic, build infrastructure, example programs, and workflow integration to enable non-streaming decoding of WAV files using the FunASR Nano model.

Changes

Cohort / File(s) Summary
Workflow Updates
.github/workflows/run-java-test.yaml
Replaced "Omnilingual ASR CTC" test with new "FunASR Nano" test step that runs shell script and cleans up artifacts
Java API Examples & Build
java-api-examples/NonStreamingDecodeFileFunAsrNano.java, java-api-examples/run-non-streaming-decode-file-funasr-nano.sh
Added new Java example class for FunASR Nano offline decoding and orchestration script handling CMake build, JNI library compilation, model download, and Java execution
Kotlin API Examples & Integration
kotlin-api-examples/test_offline_funasr_nano.kt, kotlin-api-examples/run.sh
Added Kotlin offline recognition example with helper function for recognizer creation; integrated test into run.sh test suite
Java API Configuration Classes
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java, sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java, sherpa-onnx/java-api/Makefile
Introduced new OfflineFunAsrNanoModelConfig value-type with Builder pattern supporting encoder adaptor, LLM, embedding, tokenizer, prompts, and generation parameters; extended OfflineModelConfig with funasrNano field; added new class to Makefile
Kotlin API Configuration
sherpa-onnx/kotlin-api/OfflineRecognizer.kt
Added OfflineFunAsrNanoModelConfig data class, extended OfflineModelConfig with funasrNano field, introduced model selection case 46 for FunASR Nano with model directory and field initialization
JNI/C++ Integration
sherpa-onnx/jni/offline-recognizer.cc
Added parsing logic for FunASR Nano model configuration from Java OfflineFunAsrNanoModelConfig, extracting encoder adaptor, LLM, embedding, tokenizer, prompts, and generation parameters into native model config (appears in two locations within GetOfflineConfig)
Python Model Registry
scripts/apk/generate-vad-asr-apk-script.py
Added two new model entries: sherpa-onnx-medasr-ctc-en-int8-2025-12-25 (idx=45) and sherpa-onnx-funasr-nano-int8-2025-12-30 (idx=46) with proper directory handling and cleanup sequences

Sequence Diagram(s)

sequenceDiagram
    participant App as Java/Kotlin App
    participant API as Sherpa-onnx API
    participant JNI as JNI Layer
    participant Native as Native C++
    participant Model as FunASR Nano Model

    App->>API: Create OfflineFunAsrNanoModelConfig<br/>(with encoder, LLM, embedding paths)
    API->>API: Build OfflineModelConfig<br/>(funasrNano field set)
    API->>API: Create OfflineRecognizer
    API->>JNI: Call JNI with config
    JNI->>Native: GetOfflineConfig parses<br/>funasr_nano fields
    Native->>Native: Initialize FunASR Nano model<br/>(encoder adaptor, LLM, embedding)
    App->>API: Load WAV file & create input stream
    App->>API: Feed waveform data
    API->>JNI: Invoke recognition
    JNI->>Native: Run offline recognition
    Native->>Model: Decode with FunASR Nano
    Model-->>Native: Return result
    Native-->>JNI: Return recognized text
    JNI-->>API: Return to caller
    API-->>App: Retrieve & print result
    App->>API: Release resources
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

size:L

Poem

🐰 Hops with glee through code so fine,
FunASR Nano now will shine!
Java, Kotlin, JNI threads,
Offline speech where magic treads,
From WAV to words, a rabbit's delight! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% 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 clearly summarizes the main change: adding Kotlin and Java API support for FunASR Nano models, which is reflected across multiple files including new Java/Kotlin examples, configuration classes, and build scripts.

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


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 expands the project's capabilities by integrating the FunASR Nano automatic speech recognition models into its Java and Kotlin APIs. It provides developers with new, dedicated configuration options and clear examples for performing non-streaming audio decoding. The changes ensure seamless model loading and execution across these platforms, enhancing the project's support for advanced speech processing tasks.

Highlights

  • New Java API for FunASR Nano: Introduced OfflineFunAsrNanoModelConfig.java and an example NonStreamingDecodeFileFunAsrNano.java to enable non-streaming speech recognition using FunASR Nano models in Java.
  • New Kotlin API for FunASR Nano: Added OfflineFunAsrNanoModelConfig data class and an example test_offline_funasr_nano.kt to support FunASR Nano models in Kotlin.
  • Integrated Model Configuration: The OfflineModelConfig in both Java and Kotlin APIs has been extended to include the new FunASR Nano model configuration, streamlining its usage.
  • JNI Layer Update: The C++ JNI binding (offline-recognizer.cc) was updated to correctly parse and utilize the FunASR Nano model parameters passed from the Java layer.
  • Example Scripts and Model Management: New shell scripts (run-non-streaming-decode-file-funasr-nano.sh and updates to kotlin-api-examples/run.sh) were added to automate the building of necessary components, downloading of the FunASR Nano model, and execution of the new examples.
  • APK Script Integration: The generate-vad-asr-apk-script.py script was updated to include the FunASR Nano model, facilitating its packaging in Android applications.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/run-java-test.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 FunASR Nano models to the Java and Kotlin APIs. The changes are well-structured and follow existing patterns. I've provided some suggestions to improve the example code for robustness and flexibility, mainly concerning resource management with try-finally blocks to prevent leaks, and parameterizing hardcoded file paths. I've also pointed out some areas in the Kotlin example where more idiomatic and safer code can be used. The accompanying shell scripts have also been reviewed with suggestions for better maintainability.

Comment on lines +46 to +57
OfflineRecognizer recognizer = new OfflineRecognizer(config);
OfflineStream stream = recognizer.createStream();
stream.acceptWaveform(reader.getSamples(), reader.getSampleRate());

recognizer.decode(stream);

String text = recognizer.getResult(stream).getText();

System.out.printf("filename:%s\nresult:%s\n", waveFilename, text);

stream.release();
recognizer.release();

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

There's a potential resource leak here. If an exception occurs after recognizer and stream are created but before release() is called, these resources will not be cleaned up. It's safer to use a try-finally block to ensure release() is always called.

    OfflineRecognizer recognizer = null;
    OfflineStream stream = null;
    try {
      recognizer = new OfflineRecognizer(config);
      stream = recognizer.createStream();
      stream.acceptWaveform(reader.getSamples(), reader.getSampleRate());

      recognizer.decode(stream);

      String text = recognizer.getResult(stream).getText();

      System.out.printf("filename:%s\nresult:%s\n", waveFilename, text);
    } finally {
      if (stream != null) {
        stream.release();
      }
      if (recognizer != null) {
        recognizer.release();
      }
    }

Comment on lines +13 to +21
var stream = recognizer.createStream()
stream.acceptWaveform(samples, sampleRate=sampleRate)
recognizer.decode(stream)

var result = recognizer.getResult(stream)
println(result)

stream.release()
recognizer.release()

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

There's a potential resource leak if an exception occurs. Using a try-finally block ensures stream and recognizer are always released. Also, stream and result are not reassigned and can be declared with val.

  val stream = recognizer.createStream()
  try {
      stream.acceptWaveform(samples, sampleRate=sampleRate)
      recognizer.decode(stream)

      val result = recognizer.getResult(stream)
      println(result)
  } finally {
      stream.release()
      recognizer.release()
  }

Comment on lines +13 to +20
String encoderAdaptor = "./sherpa-onnx-funasr-nano-int8-2025-12-30/encoder_adaptor.int8.onnx";
String llm = "./sherpa-onnx-funasr-nano-int8-2025-12-30/llm.int8.onnx";
String embedding = "./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx";
String tokenizer = "./sherpa-onnx-funasr-nano-int8-2025-12-30/Qwen3-0.6B";

String tokens = "";

String waveFilename = "./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.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 and the wave file are hardcoded. This makes the example less flexible. Consider passing these paths as command-line arguments.

Suggested change
String encoderAdaptor = "./sherpa-onnx-funasr-nano-int8-2025-12-30/encoder_adaptor.int8.onnx";
String llm = "./sherpa-onnx-funasr-nano-int8-2025-12-30/llm.int8.onnx";
String embedding = "./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx";
String tokenizer = "./sherpa-onnx-funasr-nano-int8-2025-12-30/Qwen3-0.6B";
String tokens = "";
String waveFilename = "./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.wav";
if (args.length != 5) {
System.err.println("Usage: java NonStreamingDecodeFileFunAsrNano <encoder_adaptor.onnx> <llm.onnx> <embedding.onnx> <tokenizer_dir> <wave_file>");
System.exit(1);
}
String encoderAdaptor = args[0];
String llm = args[1];
String embedding = args[2];
String tokenizer = args[3];
String tokens = "";
String waveFilename = args[4];

Comment on lines +22 to +37
if [ ! -f ../sherpa-onnx/java-api/build/sherpa-onnx.jar ]; then
pushd ../sherpa-onnx/java-api
make
popd
fi

if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
rm sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
fi

java \
-Djava.library.path=$PWD/../build/lib \
-cp ../sherpa-onnx/java-api/build/sherpa-onnx.jar \
NonStreamingDecodeFileFunAsrNano.java

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

This script can be improved for better readability and maintainability:

  1. Use a variable for the model directory to avoid repetition.
  2. Use [[ instead of [ for consistency with the rest of the script.
  3. If the Java example is updated to accept command-line arguments, this script should be updated to pass them.
MODEL_DIR=sherpa-onnx-funasr-nano-int8-2025-12-30
if [[ ! -f ../sherpa-onnx/java-api/build/sherpa-onnx.jar ]]; then
  pushd ../sherpa-onnx/java-api
  make
  popd
fi

if [[ ! -f ./$MODEL_DIR/embedding.int8.onnx ]]; then
  curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/$MODEL_DIR.tar.bz2
  tar xvf $MODEL_DIR.tar.bz2
  rm $MODEL_DIR.tar.bz2
fi

java \
  -Djava.library.path=$PWD/../build/lib \
  -cp ../sherpa-onnx/java-api/build/sherpa-onnx.jar \
  NonStreamingDecodeFileFunAsrNano.java \
  ./$MODEL_DIR/encoder_adaptor.int8.onnx \
  ./$MODEL_DIR/llm.int8.onnx \
  ./$MODEL_DIR/embedding.int8.onnx \
  ./$MODEL_DIR/Qwen3-0.6B \
  ./$MODEL_DIR/test_wavs/lyrics.wav

Comment on lines +541 to +544
if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
rm sherpa-onnx-funasr-nano-int8-2025-12-30.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.

medium

The model directory name sherpa-onnx-funasr-nano-int8-2025-12-30 is used multiple times. It's good practice to store it in a variable to improve readability and make it easier to update in the future.

Suggested change
if [ ! -f ./sherpa-onnx-funasr-nano-int8-2025-12-30/embedding.int8.onnx ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
tar xvf sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
rm sherpa-onnx-funasr-nano-int8-2025-12-30.tar.bz2
local model_dir=sherpa-onnx-funasr-nano-int8-2025-12-30
if [ ! -f ./$model_dir/embedding.int8.onnx ]; then
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/$model_dir.tar.bz2
tar xvf $model_dir.tar.bz2
rm $model_dir.tar.bz2

faked-asset-manager.kt

ls -lh $out_filename
java -Djava.library.path=../build/lib -jar $out_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.

medium

If test_offline_funasr_nano.kt is updated to accept the wave file path as a command-line argument, this line should be updated to pass it.

Suggested change
java -Djava.library.path=../build/lib -jar $out_filename
java -Djava.library.path=../build/lib -jar $out_filename ./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.wav

Comment on lines +3 to +5
fun main() {
val recognizer = createOfflineRecognizer()
val waveFilename = "./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.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 main function can be updated to accept the wave file path from command-line arguments. This makes the example more flexible and reusable.

Suggested change
fun main() {
val recognizer = createOfflineRecognizer()
val waveFilename = "./sherpa-onnx-funasr-nano-int8-2025-12-30/test_wavs/lyrics.wav"
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("Usage: please provide a wave file")
return
}
val recognizer = createOfflineRecognizer()
val waveFilename = args[0]

Comment on lines +10 to +11
val samples: FloatArray = objArray[0] as FloatArray
val sampleRate: Int = objArray[1] as Int

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 unchecked cast as can throw a ClassCastException if the types returned from WaveReader.readWaveFromFile are not what's expected. For more robust code, consider using a safe cast as? with an elvis operator ?: to throw a more informative exception, or using require to validate the types before casting.

Suggested change
val samples: FloatArray = objArray[0] as FloatArray
val sampleRate: Int = objArray[1] as Int
require(objArray.size == 2) { "Expected 2 elements from readWaveFromFile" }
val samples = objArray[0] as? FloatArray ?: error("Element 0 is not a FloatArray")
val sampleRate = objArray[1] as? Int ?: error("Element 1 is not an Int")


fun createOfflineRecognizer(): OfflineRecognizer {
val config = OfflineRecognizerConfig(
modelConfig = getOfflineModelConfig(type = 46)!!,

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

Using the non-null assertion operator !! is generally discouraged as it can lead to NullPointerExceptions at runtime. It's safer to handle the potential null case explicitly, for example by using the elvis operator ?: to throw a more descriptive exception.

      modelConfig = getOfflineModelConfig(type = 46) ?: error("Failed to get model config for type 46"),

@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

🤖 Fix all issues with AI agents
In @java-api-examples/run-non-streaming-decode-file-funasr-nano.sh:
- Around line 34-37: The script incorrectly passes the source file name
NonStreamingDecodeFileFunAsrNano.java to the java launcher (source-file mode)
while the project targets Java 1.8; instead, compile the example with javac
first and then invoke the class by its simple name. Update the run command to
(1) run javac on NonStreamingDecodeFileFunAsrNano.java (ensuring the classpath
includes ../sherpa-onnx/java-api/build/sherpa-onnx.jar), and (2) invoke java
with -Djava.library.path pointing to ../build/lib and -cp including both the jar
and the current directory, calling NonStreamingDecodeFileFunAsrNano (no .java).
Apply the same fix to all scripts in java-api-examples/.

In
@sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java:
- Around line 32-66: The class OfflineFunAsrNanoModelConfig is missing a getter
for the tokenizer field: add a public String getTokenizer() method that returns
the tokenizer field so JNI/config consumers can read it; locate the tokenizer
field and its existing setter setTokenizer(...) and implement the matching
getter in the same style as getEncoderAdaptor()/getLLM() to maintain
consistency.
🧹 Nitpick comments (1)
kotlin-api-examples/test_offline_funasr_nano.kt (1)

25-31: Consider safer null handling for test robustness.

Using !! on getOfflineModelConfig(type = 46) will throw a NullPointerException if the type is not found. For test code, this is acceptable as it will clearly fail, but you could use requireNotNull() for a more descriptive error message.

♻️ Optional: Use requireNotNull for clearer error messages
 fun createOfflineRecognizer(): OfflineRecognizer {
   val config = OfflineRecognizerConfig(
-      modelConfig = getOfflineModelConfig(type = 46)!!,
+      modelConfig = requireNotNull(getOfflineModelConfig(type = 46)) { "Model type 46 (FunASR Nano) not found" },
   )

   return OfflineRecognizer(config = config)
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5437407 and a6a9d91.

📒 Files selected for processing (11)
  • .github/workflows/run-java-test.yaml
  • java-api-examples/NonStreamingDecodeFileFunAsrNano.java
  • java-api-examples/run-non-streaming-decode-file-funasr-nano.sh
  • kotlin-api-examples/run.sh
  • kotlin-api-examples/test_offline_funasr_nano.kt
  • scripts/apk/generate-vad-asr-apk-script.py
  • sherpa-onnx/java-api/Makefile
  • sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java
  • sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java
  • sherpa-onnx/jni/offline-recognizer.cc
  • sherpa-onnx/kotlin-api/OfflineRecognizer.kt
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-06T04:18:47.981Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:18:47.981Z
Learning: In sherpa-onnx Java API, the native library names in Core.java (WIN_NATIVE_LIBRARY_NAME = "sherpa-onnx-jni.dll", UNIX_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.so", MACOS_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.dylib") are copied directly from the compiled binary filenames and should not be changed to match other libraries' naming conventions.

Applied to files:

  • sherpa-onnx/java-api/Makefile
  • java-api-examples/run-non-streaming-decode-file-funasr-nano.sh
  • sherpa-onnx/jni/offline-recognizer.cc
📚 Learning: 2025-08-06T04:23:50.237Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:23:50.237Z
Learning: The sherpa-onnx JNI library files are stored in Hugging Face repository at https://huggingface.co/csukuangfj/sherpa-onnx-libs under versioned directories like jni/1.12.7/, and the actual Windows JNI library filename is "sherpa-onnx-jni.dll" as defined in Core.java constants.

Applied to files:

  • sherpa-onnx/java-api/Makefile
  • java-api-examples/run-non-streaming-decode-file-funasr-nano.sh
  • sherpa-onnx/jni/offline-recognizer.cc
🧬 Code graph analysis (6)
sherpa-onnx/kotlin-api/OfflineRecognizer.kt (4)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java (1)
  • OfflineFunAsrNanoModelConfig (3-134)
sherpa-onnx/c-api/cxx-api.h (1)
  • OfflineModelConfig (299-324)
scripts/go/sherpa_onnx.go (1)
  • OfflineModelConfig (491-524)
scripts/dotnet/OfflineModelConfig.cs (1)
  • OfflineModelConfig (11-35)
java-api-examples/NonStreamingDecodeFileFunAsrNano.java (2)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java (1)
  • OfflineFunAsrNanoModelConfig (3-134)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java (1)
  • OfflineModelConfig (5-281)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java (1)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java (1)
  • Builder (143-280)
sherpa-onnx/jni/offline-recognizer.cc (2)
sherpa-onnx/kotlin-api/OfflineRecognizer.kt (1)
  • encoderAdaptor (53-64)
android/SherpaOnnxSimulateStreamingAsr/app/src/main/java/com/k2fsa/sherpa/onnx/OfflineRecognizer.kt (1)
  • encoderAdaptor (53-64)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java (1)
sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java (1)
  • OfflineFunAsrNanoModelConfig (3-134)
scripts/apk/generate-vad-asr-apk-script.py (6)
scripts/apk/generate-asr-2pass-apk-script.py (1)
  • Model (27-43)
scripts/apk/generate-qnn-vad-asr-apk-script.py (1)
  • Model (28-47)
scripts/wasm/generate-vad-asr.py (1)
  • Model (27-32)
scripts/apk/generate-asr-apk-script.py (1)
  • Model (27-44)
scripts/lazarus/generate-subtitles.py (1)
  • Model (28-32)
scripts/hap/generate-vad-asr-hap-script.py (1)
  • Model (28-48)
🔇 Additional comments (12)
scripts/apk/generate-vad-asr-apk-script.py (1)

780-795: The new FunASR Nano model entry (idx=46) is correctly structured and properly mapped.

The model definition follows the established pattern with sequential idx values (44 → 45 → 46), and the cmd block with pushd/rm/ls/popd sequence matches other entries. Cross-file verification confirms idx=46 is properly mapped to OfflineFunAsrNanoModelConfig in the Kotlin API (OfflineRecognizer.kt line 786) and test files, with model paths consistent across all implementations.

sherpa-onnx/java-api/Makefile (1)

43-43: LGTM!

The new OfflineFunAsrNanoModelConfig.java is correctly added to the compilation list, following the established pattern for model configuration files.

.github/workflows/run-java-test.yaml (1)

111-116: LGTM!

The new FunASR Nano test step follows the established workflow pattern, including proper cleanup of model files after the test.

sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineModelConfig.java (1)

18-18: LGTM!

The funasrNano field, getter, and builder methods are correctly implemented following the established pattern used by other model configurations in this class.

Also applies to: 42-42, 103-106, 156-156, 211-214

kotlin-api-examples/run.sh (2)

540-560: LGTM!

The testOfflineFunAsrNano function correctly follows the established pattern for model testing:

  • Downloads model archive if not present
  • Compiles the Kotlin test with all required dependencies
  • Runs the test jar with the JNI library path

586-586: LGTM!

The test function is appropriately placed in the test sequence after testVersion.

sherpa-onnx/jni/offline-recognizer.cc (1)

270-309: LGTM!

The JNI parsing for FunASR Nano configuration is correctly implemented, following the established pattern used by other model configurations. All 10 fields are properly read using the appropriate macros (SHERPA_ONNX_JNI_READ_STRING, SHERPA_ONNX_JNI_READ_INT, SHERPA_ONNX_JNI_READ_FLOAT), and the C++ struct fields match exactly.

java-api-examples/NonStreamingDecodeFileFunAsrNano.java (1)

1-58: LGTM!

The example correctly demonstrates FunASR Nano usage following the existing patterns in the codebase. The resource handling is appropriate with stream.release() and recognizer.release() at the end.

sherpa-onnx/kotlin-api/OfflineRecognizer.kt (3)

53-64: LGTM!

The OfflineFunAsrNanoModelConfig data class is well-defined with sensible defaults that match the Java counterpart. The field names and default values are consistent across both APIs.


114-114: LGTM!

The funasrNano field is properly added to OfflineModelConfig with a default instance, consistent with the pattern used for other model configurations.


786-797: LGTM!

The case 46 implementation correctly configures the FunASR Nano model with appropriate paths. Using an empty tokens string is intentional since this model uses a tokenizer directory path instead.

sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/OfflineFunAsrNanoModelConfig.java (1)

68-133: LGTM!

The Builder class follows the established pattern with appropriate default values that are consistent with the Kotlin API counterpart. The fluent setter pattern is correctly implemented.

Comment on lines +34 to +37
java \
-Djava.library.path=$PWD/../build/lib \
-cp ../sherpa-onnx/java-api/build/sherpa-onnx.jar \
NonStreamingDecodeFileFunAsrNano.java

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's look at the actual script to see the full context
cat -n java-api-examples/run-non-streaming-decode-file-funasr-nano.sh

Repository: k2-fsa/sherpa-onnx

Length of output: 1339


🏁 Script executed:

#!/bin/bash
# Check other similar scripts to understand the pattern
find java-api-examples -name "*.sh" -type f | head -10

Repository: k2-fsa/sherpa-onnx

Length of output: 651


🏁 Script executed:

#!/bin/bash
# Search for Java execution patterns in all shell scripts
rg '^\s*java\s' java-api-examples/ -A 2 -B 2 | head -60

Repository: k2-fsa/sherpa-onnx

Length of output: 3841


🏁 Script executed:

#!/bin/bash
# Check for Java version requirements in common config files
find . -maxdepth 2 -type f \( -name "README*" -o -name "pom.xml" -o -name "build.gradle" -o -name ".github" \) | head -20

Repository: k2-fsa/sherpa-onnx

Length of output: 556


🏁 Script executed:

#!/bin/bash
# Check if there's a Makefile in java-api-examples that compiles Java files
cat java-api-examples/Makefile 2>/dev/null || echo "No Makefile found"

Repository: k2-fsa/sherpa-onnx

Length of output: 79


🏁 Script executed:

#!/bin/bash
# Check the sherpa-onnx java-api Makefile to see how Java is built
cat ../sherpa-onnx/java-api/Makefile 2>/dev/null | head -50

Repository: k2-fsa/sherpa-onnx

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Look for Java version configuration in gradle or maven files
find . -type f \( -name "*.gradle" -o -name "pom.xml" \) -exec grep -l "source\|target\|release\|java.version" {} \;

Repository: k2-fsa/sherpa-onnx

Length of output: 590


🏁 Script executed:

#!/bin/bash
# Check if the Java source files actually exist or are compiled
ls -la java-api-examples/*.java 2>/dev/null | head -20

Repository: k2-fsa/sherpa-onnx

Length of output: 2178


🏁 Script executed:

#!/bin/bash
# Check the pom.xml to see Java version requirements
cat ./pom.xml

Repository: k2-fsa/sherpa-onnx

Length of output: 857


🏁 Script executed:

#!/bin/bash
# Check the sherpa-onnx java-api pom.xml
cat ./sherpa-onnx/java-api/pom.xml

Repository: k2-fsa/sherpa-onnx

Length of output: 3286


🏁 Script executed:

#!/bin/bash
# Look at one of the Java example files to understand its structure
head -30 java-api-examples/NonStreamingDecodeFileFunAsrNano.java

Repository: k2-fsa/sherpa-onnx

Length of output: 1243


🏁 Script executed:

#!/bin/bash
# Check the java-api-examples README for instructions
cat java-api-examples/README.md

Repository: k2-fsa/sherpa-onnx

Length of output: 2922


Remove .java extension or compile files before execution.

The script passes NonStreamingDecodeFileFunAsrNano.java directly to the Java command (source-file mode), which requires Java 11+. However, the project is configured for Java 1.8 (see sherpa-onnx/java-api/pom.xml), creating an incompatibility. Either compile the Java files with javac first and pass the class name, or remove the .java extension from the command. This issue affects all scripts in java-api-examples/.

🤖 Prompt for AI Agents
In @java-api-examples/run-non-streaming-decode-file-funasr-nano.sh around lines
34 - 37, The script incorrectly passes the source file name
NonStreamingDecodeFileFunAsrNano.java to the java launcher (source-file mode)
while the project targets Java 1.8; instead, compile the example with javac
first and then invoke the class by its simple name. Update the run command to
(1) run javac on NonStreamingDecodeFileFunAsrNano.java (ensuring the classpath
includes ../sherpa-onnx/java-api/build/sherpa-onnx.jar), and (2) invoke java
with -Djava.library.path pointing to ../build/lib and -cp including both the jar
and the current directory, calling NonStreamingDecodeFileFunAsrNano (no .java).
Apply the same fix to all scripts in java-api-examples/.

@csukuangfj
csukuangfj merged commit e4c046a into k2-fsa:master Jan 12, 2026
1 check was pending
@csukuangfj
csukuangfj deleted the jni-fun-asr-nano branch January 12, 2026 10:22
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