Skip to content

Refactor ONNX export script for nemotron-3.5-asr-streaming-0.6b - #3734

Merged
csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:export-nemotron-3.5
Jul 8, 2026
Merged

csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:export-nemotron-3.5

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator

Remove a lot of unncessary code.

Summary by CodeRabbit

  • Bug Fixes

    • Improved ONNX export reliability and compatibility, including better handling of model metadata, prompt settings, and feature dimensions.
    • Simplified token generation during export to better match the model’s vocabulary.
  • Tests

    • Added workflow checks that validate exported ONNX models across multiple chunk sizes and both quantized and non-quantized variants.
    • Enhanced the export job to verify required tooling is available and to use authenticated access during runtime.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 8, 2026
@csukuangfj
csukuangfj merged commit 6a20463 into k2-fsa:master Jul 8, 2026
1 check was pending
@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1efeecd7-1e1e-4812-bfd8-7b9fe4941131

📥 Commits

Reviewing files that changed from the base of the PR and between 4392c45 and f71d85f.

📒 Files selected for processing (2)
  • .github/workflows/export-nemotron-3.5-asr-streaming-0.6b.yaml
  • scripts/nemo/nemotron-3.5-asr-streaming-0.6b/export_onnx.py

📝 Walkthrough

Walkthrough

This PR simplifies the Nemotron-3.5 ASR export script by replacing SentencePiece-based token extraction with vocabulary-based extraction, removing several ONNX graph validation assertions, adjusting the export opset version and feature dimension derivation, and adds CI steps to install sherpa-onnx and validate exported ONNX models across multiple chunk sizes.

Changes

Export script and CI test updates

Layer / File(s) Summary
Simplify token/prompt extraction and metadata
scripts/nemo/nemotron-3.5-asr-streaming-0.6b/export_onnx.py
save_tokens now writes tokens from asr_model.joint.vocabulary instead of SentencePiece lookup; prompt-dictionary helper functions removed; main() derives prompt_dictionary/auto_prompt_id directly from config; meta_data normalizes prompt_dictionary via _normalize_prompt_dictionary; unused Optional import dropped.
Remove export validation and adjust parameters
scripts/nemo/nemotron-3.5-asr-streaming-0.6b/export_onnx.py
Removes assert_forward_for_export_signature call, deletes assert_encoder_graph helper and its post-export call sites, derives dummy audio_signal feature dimension from asr_model.cfg.preprocessor.features, and lowers ONNX opset_version from 17 to 13.
CI installation and ONNX model testing
.github/workflows/export-nemotron-3.5-asr-streaming-0.6b.yaml
Installs sherpa-onnx-bin earlier and prints its version, passes HF_TOKEN to the export run step, and adds a "Test onnx models" step that installs sherpa-onnx, downloads a test WAV, and runs sherpa-onnx across chunk sizes 80/160/560/1120 for int8 and float32 model variants.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • k2-fsa/sherpa-onnx#3671: Both PRs modify scripts/nemo/nemotron-3.5-asr-streaming-0.6b/export_onnx.py, touching prompt/token handling, PromptedStreamingEncoder, and prompt_dictionary/metadata logic.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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 simplifies the ONNX export script for Nemotron ASR streaming by removing several validation checks, refactoring token saving to extract vocabulary directly from the model's joint vocabulary, dynamically retrieving the feature dimension from the model configuration, and downgrading the ONNX opset version from 17 to 13. Feedback highlights an incorrect return type annotation and potential UnboundLocalError in the save_tokens function, as well as a hardcoded feature dimension remaining in the metadata dictionary.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 85 to +89
def save_tokens(asr_model, filename: str = "tokens.txt") -> int:
sp = _find_sentencepiece_processor(getattr(asr_model, "tokenizer", None))
if sp is None:
raise RuntimeError("Could not find the SentencePiece tokenizer in the model")

vocab_size = sp.get_piece_size()
with open(filename, "w", encoding="utf-8") as f:
for i in range(vocab_size):
f.write(f"{sp.id_to_piece(i)} {i}\n")
f.write(f"<blk> {vocab_size}\n")

print(f"Saved {filename}")
return vocab_size


def assert_forward_for_export_signature(encoder):
if not hasattr(encoder, "forward_for_export"):
raise RuntimeError("Expected encoder.forward_for_export for ONNX export")

signature = inspect.signature(encoder.forward_for_export)
missing = [
name for name in FORWARD_FOR_EXPORT_ARGS if name not in signature.parameters
]
if missing:
raise RuntimeError(
"encoder.forward_for_export is missing expected argument(s): "
f"{missing}. Signature: {signature}"
)
for i, s in enumerate(asr_model.joint.vocabulary):
f.write(f"{s} {i}\n")
f.write(f"<blk> {i+1}\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.

medium

The save_tokens function has a return type annotation of -> int, but it does not return any value (implicitly returning None). Additionally, referencing the loop variable i outside the loop to write the <blk> token can raise an UnboundLocalError if the vocabulary is empty, and is generally considered a code smell in Python.

Consider updating the return type annotation to -> None and using len(vocab) instead of i+1 to determine the vocabulary size safely.

Suggested change
def save_tokens(asr_model, filename: str = "tokens.txt") -> int:
sp = _find_sentencepiece_processor(getattr(asr_model, "tokenizer", None))
if sp is None:
raise RuntimeError("Could not find the SentencePiece tokenizer in the model")
vocab_size = sp.get_piece_size()
with open(filename, "w", encoding="utf-8") as f:
for i in range(vocab_size):
f.write(f"{sp.id_to_piece(i)} {i}\n")
f.write(f"<blk> {vocab_size}\n")
print(f"Saved {filename}")
return vocab_size
def assert_forward_for_export_signature(encoder):
if not hasattr(encoder, "forward_for_export"):
raise RuntimeError("Expected encoder.forward_for_export for ONNX export")
signature = inspect.signature(encoder.forward_for_export)
missing = [
name for name in FORWARD_FOR_EXPORT_ARGS if name not in signature.parameters
]
if missing:
raise RuntimeError(
"encoder.forward_for_export is missing expected argument(s): "
f"{missing}. Signature: {signature}"
)
for i, s in enumerate(asr_model.joint.vocabulary):
f.write(f"{s} {i}\n")
f.write(f"<blk> {i+1}\n")
def save_tokens(asr_model, filename: str = "tokens.txt") -> None:
vocab = asr_model.joint.vocabulary
with open(filename, "w", encoding="utf-8") as f:
for i, s in enumerate(vocab):
f.write(f"{s} {i}\n")
f.write(f"<blk> {len(vocab)}\n")

Comment on lines +185 to +187
feat_dim = asr_model.cfg.preprocessor.features

audio_signal = torch.zeros(1, feat_dim, window_size, dtype=dtype, device=device)

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

While feat_dim is now dynamically retrieved from asr_model.cfg.preprocessor.features to initialize the dummy audio_signal tensor, the metadata dictionary on line 359 still hardcodes "feat_dim": 128. To ensure consistency and avoid potential issues if the model configuration changes, the metadata should also use asr_model.cfg.preprocessor.features instead of a hardcoded value.

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