Skip to content

Adding Nemotron 3 Diarization ONNX Model Support - #748

Open
themason2011 wants to merge 6 commits into
mainfrom
users/masoncorey/AddingNemotron3DiarizationModelSupport
Open

themason2011 wants to merge 6 commits into
mainfrom
users/masoncorey/AddingNemotron3DiarizationModelSupport

Conversation

@themason2011

@themason2011 themason2011 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add ONNX graph support for NVIDIA Nemotron 3 Diarization, including both an offline full-sequence model and a stateful streaming model for per-frame speaker-activity probabilities.

Changes

  • Add Nemotron3DiarizationConfig to map the Hugging Face audio, head, and streaming settings, and register nemotron3_diarization with the existing offline diarization task.
  • Implement the Sortformer-style audio path: feature stacking/subsampling, bidirectional RoPE transformer, projection, sub-pixel upsampling, and sigmoid speaker-classification head.
  • Add the diarization-streaming task. Each graph invocation processes one mel-feature chunk with configurable look-ahead and returns that chunk’s speaker probabilities plus updated fixed-capacity Arrival-Order Speaker Cache (AOSC), FIFO buffers, occupancy counts, and compression state. The cache update includes score-based top-k compression.
  • Export the model and task from their public modules and add a tiny model configuration for graph-build coverage.

Validation

  • Add streaming graph tests for package creation, input/output names, task lookup, and three consecutive ONNX Runtime calls that feed each step’s cache state into the next and check probability ranges and cache capacities.
  • The existing parameterized speech graph-build suite includes the new tiny model configuration for offline graph construction.
  • The three-step execution test uses random weights; this PR does not add a numerical comparison against Hugging Face with real model weights.

Scope / limitations

  • The graph consumes mel-spectrogram features, not raw audio. Streaming callers provide the chunk window, look-ahead length, and cache state on each invocation.
  • The streaming export assumes a fully valid input window; it does not expose Hugging Face’s padding attention_mask for a potentially padded final chunk.
  • Outputs are speaker-activity probabilities, not post-processed speaker turns or timestamps.

@themason2011
themason2011 requested review from a team and a lite review from Copilot September 28, 2026 16:48
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Performance Comparison

Comparing 6bd398de → b574814c

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0% ⚪
bert (feature-extraction) num_nodes 68 68 +0.0% ⚪
falcon model_size_bytes 364 KB 364 KB +0.0% ⚪
falcon num_nodes 66 66 +0.0% ⚪
gemma2 model_size_bytes 428 KB 428 KB +0.0% ⚪
gemma2 num_nodes 105 105 +0.0% ⚪
gpt2 model_size_bytes 324 KB 324 KB +0.0% ⚪
gpt2 num_nodes 54 54 +0.0% ⚪
llama model_size_bytes 425 KB 425 KB +0.0% ⚪
llama num_nodes 60 60 +0.0% ⚪
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0% ⚪
llama (static-cache) num_nodes 56 56 +0.0% ⚪
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0% ⚪
mamba (ssm-text-generation) num_nodes 94 94 +0.0% ⚪
phi3 model_size_bytes 421 KB 421 KB +0.0% ⚪
phi3 num_nodes 58 58 +0.0% ⚪
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0% ⚪
phi3 (static-cache) num_nodes 54 54 +0.0% ⚪
qwen2 model_size_bytes 425 KB 425 KB +0.0% ⚪
qwen2 num_nodes 60 60 +0.0% ⚪
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0% ⚪
qwen2 (static-cache) num_nodes 56 56 +0.0% ⚪
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0% ⚪
qwen3_5_moe (hybrid-text-generation) num_nodes 265 265 +0.0% ⚪
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0% ⚪
qwen3_5_text (hybrid-text-generation) num_nodes 127 127 +0.0% ⚪
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0% ⚪
qwen3_5_vl (hybrid-qwen-vl) num_nodes 450 450 +0.0% ⚪
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0% ⚪
t5 (seq2seq) num_nodes 176 176 +0.0% ⚪
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0% ⚪
whisper (speech-to-text) num_nodes 128 128 +0.0% ⚪

No performance regressions.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 6bd398de → b574814c

Model Sub-model Changes Status
bert (feature-extraction) model 0 ⚪
falcon model 0 ⚪
gemma2 model 0 ⚪
gemma4 (gemma4) decoder 0 ⚪
gemma4 (gemma4) embedding 0 ⚪
gemma4 (gemma4) vision_encoder 0 ⚪
gemma4_text model 0 ⚪
gpt2 model 0 ⚪
llama model 0 ⚪
llama (static-cache) model 0 ⚪
mamba (ssm-text-generation) model 0 ⚪
phi3 model 0 ⚪
phi3 (static-cache) model 0 ⚪
qwen model 0 ⚪
qwen (static-cache) model 0 ⚪
qwen2 model 0 ⚪
qwen2 (static-cache) model 0 ⚪
qwen2_moe model 0 ⚪
qwen2_moe (static-cache) model 0 ⚪
qwen3 model 0 ⚪
qwen3 (static-cache) model 0 ⚪
qwen3_5_moe (hybrid-text-generation) model 0 ⚪
qwen3_5_text (hybrid-text-generation) model 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) decoder 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) embedding 0 ⚪
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0 ⚪
qwen3_moe model 0 ⚪
qwen3_moe (static-cache) model 0 ⚪
qwen3_next (hybrid-text-generation) model 0 ⚪
t5 (seq2seq) decoder 0 ⚪
t5 (seq2seq) encoder 0 ⚪
whisper (speech-to-text) decoder 0 ⚪
whisper (speech-to-text) encoder 0 ⚪

No architecture changes detected. ✅


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Critical and moderate issues remain in model compatibility, streaming behavior, configuration extraction, test coverage, and registry coverage.

Review effort: Lite
Findings: 4 High severity · 1 Medium severity

Open (5)
What changed in this PR

Adds Nemotron 3 Diarization ONNX support, including configuration, model registration, offline/stateful streaming graphs, cache handling, and tests.

Changes:

  • Adds Nemotron model architecture and configuration extraction.
  • Adds streaming task wiring with cache/FIFO handling.
  • Adds registry/exports and graph execution tests.
File Summary / findings
tests/​build_graph/​speech_test.py Adds graph and runtime tests; needs HF numerical parity coverage for normal, final-chunk, and compressed-cache steps.
tests/​_test_configs.py Adds a tiny Nemotron test configuration.
src/​mobius/​tasks/​_diarization_streaming.py Adds streaming graph I/O; fixed dimensions and lookahead handling do not support shorter final chunks or processor streaming modes.
src/​mobius/​tasks/​__init__.py Exports the streaming task.
src/​mobius/​models/​nemotron3_diarization.py Implements the model and cache logic; contains MHA/KV shape incompatibility and dtype-invalid padding.
src/​mobius/​models/​__init__.py Exports the Nemotron model.
src/​mobius/​_registry.py Registers the model; missing coverage YAML or documented skip will fail the coverage suite.
src/​mobius/​_configs/​_base.py Adds configuration extraction; nested configs must be normalized before field access.
src/​mobius/​_configs/​__init__.py Exports the configuration.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mobius/_registry.py
Comment thread src/mobius/models/nemotron3_diarization.py
Comment thread src/mobius/models/nemotron3_diarization.py Outdated
Comment thread src/mobius/tasks/_diarization_streaming.py Outdated
Comment thread tests/build_graph/speech_test.py Outdated
@titaiwangms
titaiwangms self-requested a review September 28, 2026 17:59
themason2011 and others added 3 commits September 28, 2026 18:22
Nemotron3DiarizationModel's diarization/diarization-streaming task
strings are not part of the generic testdata/cases/ + generate_golden.py
YAML pipeline (same limitation as the NeMo-based sortformer model), so
tests/model_coverage_test.py's L4/L5 check was failing.

Add:
- scripts/generate_nemotron3_diarization_golden.py: regenerates
  testdata/golden/speech/nemotron3_diarization.npz from the real
  HuggingFace Nemotron3DiarizationForAudioFrameClassification reference
  (offline single-pass output, plus a 3-chunk streaming session that
  exercises FIFO overflow and AOSC top-k compression).
- tests/nemotron3_diarization_integration_test.py: L4 offline parity and
  L5 multi-chunk streaming-session parity (speaker probabilities and full
  cache state) against that golden, building the real ONNX graphs via
  mobius.build().
- _COVERAGE_SKIP entry in tests/model_coverage_test.py pointing at the new
  integration test, mirroring the existing sortformer entry.

Signed-off-by: themason2011 <masoncorey@microsoft.com>
Previously, nvidia/Nemotron-3-Diarization and sortformer had bespoke,
one-off integration test scripts and golden generators that duplicated
the shared L4/L5 golden-test framework (testdata/cases/,
scripts/generate_golden.py, tests/e2e_golden_test.py,
mobius._testing.golden/parity). This meant diarization models were not
subject to the same coverage checks, schema validation, or discovery
machinery as every other architecture, and required a
model_coverage_test.py _COVERAGE_SKIP escape hatch to pass CI.

This change extends the existing framework to natively support
diarization's per-frame speaker-probability output, instead of
building a separate one-off harness:

- Add "diarization" / "diarization-streaming" task types and a "nemo"
  reference_loader to testdata/cases/schema.json.
- Add save_diarization_golden()/load_diarization_golden() to
  mobius._testing.golden, storing full-precision probability arrays in
  a companion .npz (mirroring the existing drafter-inputs pattern) with
  lightweight metadata in the standard golden .json.
- Add compare_diarization_golden() to mobius._testing.parity: an
  elementwise allclose gate over continuous frame probabilities, with
  dominant-speaker argmax agreement and active-speaker-set Jaccard as
  diagnostics, returning the same ParityReport shape used everywhere
  else in the framework.
- Add _generate_diarization_offline()/_generate_diarization_streaming()
  generators to scripts/generate_golden.py, dispatching to either
  AutoModelForAudioFrameClassification (HF) or a NeMo .nemo checkpoint
  depending on case.model_type.
- Wire diarization/diarization-streaming dispatch into
  tests/e2e_golden_test.py: a nemo-loader branch in
  _build_model_package(), new helpers for offline and streaming
  diarization runs, an early-exit branch in
  TestL4CheckpointVerified, and a new TestL5DiarizationSession class
  for full cache-threaded streaming verification.
- Add 3 new YAML cases under testdata/cases/diarization/ covering
  nemotron3 offline (L4), nemotron3 streaming (L5), and sortformer
  offline (L4+L5, migrated from NeMo).
- Migrate existing committed golden data for both models into the new
  storage format; nemotron3's HF-sourced mel (channel-last) is
  pre-transposed to the channel-first layout the ONNX graph expects,
  matching NeMo's native convention used by sortformer.
- Remove the now-redundant bespoke test/generator scripts and their
  ad-hoc golden .npz files.
- Remove the nemotron3_diarization _COVERAGE_SKIP entry entirely (now
  covered generically). Narrow the sortformer _COVERAGE_SKIP entry to
  only its pre-existing, unrelated L1-L3 gap (no test_model_id/tiny
  config, matching fastconformer_rnnt), since its L4/L5 coverage is now
  handled by this framework too.

Verified: tests/yaml_schema_test.py, tests/model_coverage_test.py, and
tests/e2e_golden_test.py -k diarization all pass; full baseline suite
unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: themason2011 <masoncorey@microsoft.com>
Fixes 3 of the 5 outstanding review-bot comments on this PR (the other
two -- missing L2/L4 coverage, and insufficient streaming test coverage
-- were already resolved by the prior 'Integrate diarization L4/L5
testing' commit).

- tests/_test_configs.py: set num_key_value_heads=TINY_HEADS on the
  nemotron3_diarization tiny config. HuggingFace's checkpoint is always
  MHA (audio_config sets num_key_value_heads == num_attention_heads);
  the shared tiny-config default is GQA (TINY_KV_HEADS < TINY_HEADS),
  which built a V-projection shape no real checkpoint could populate.

- src/mobius/models/nemotron3_diarization.py: cast every FLOAT32
  literal in the AOSC scoring/compression path (_get_frame_scores's
  threshold/complement/log-half/neg-inf/positivity constants, the
  tail-boost Where branches, the score-cache Pad fill value, and the
  top-k -inf sentinel comparison) to match the surrounding tensor's
  config.dtype via CastLike. ONNX elementwise ops require matching
  operand dtypes, so these bare FLOAT32 constants made fp16/bf16
  streaming exports invalid.

- src/mobius/tasks/_diarization_streaming.py: make the streaming
  input_features time axis symbolic instead of a fixed
  chunk_length+chunk_right_context window. forward_streaming already
  derives every frame count from the input's actual shape, so only the
  declared graph input shape was blocking HuggingFace's supported
  shorter final chunk.

- tests/build_graph/speech_test.py: exercise a shorter final chunk
  (no lookahead) in the streaming execution test to prove the symbolic
  time axis actually accepts variable-length windows, and add a
  docstring note pointing to tests/e2e_golden_test.py::
  TestL5DiarizationSession for real-weights numeric parity (including
  the lookahead trim and cache-compression transitions), since this
  test intentionally stays a fast random-weight structural smoke test.

Verified: tests/build_graph/speech_test.py -k Nemotron3Diarization,
tests/model_coverage_test.py, and
tests/e2e_golden_test.py -k nemotron3_diarization -m integration
(against the real nvidia/Nemotron-3-Diarization checkpoint) all pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: themason2011 <masoncorey@microsoft.com>
@titaiwangms

Copy link
Copy Markdown
Contributor

Full review of head 90435051 (read-only; no changes made). The config/model/task/registry split follows Mobius conventions, the offline path reuses DiarizationTask, and the new streaming task has an explicit cache-state I/O contract. The earlier Copilot comments about missing YAML, MHA tiny-config dimensions, dtype constants, fixed-length final chunks, and missing real-weight goldens have been addressed in subsequent commits; the findings below concern the current head.

Findings

  1. Major — L4/L5 can pass with incorrect diarization probabilities. In src/mobius/_testing/parity.py:424-438, a failed elementwise allclose becomes AMBIGUOUS whenever the dominant speaker per frame is unchanged. Both diarization consumers in tests/e2e_golden_test.py:2243,2308 assert only result != FAIL, accepting that result. For example, golden [0.9, 0.6] versus ONNX [0.9, 0.1] passes the argmax check even though the second speaker changes from active to inactive and the numeric error far exceeds the stated tolerance. This also weakens the migrated Sortformer L4 check. Please require PASS/allclose for continuous-probability parity and add a regression test with a secondary speaker crossing the 0.5 threshold.

  2. Major — the default offline graph silently changes semantics for long recordings. src/mobius/models/nemotron3_diarization.py:22-26,582-598 describes a single full-sequence forward with no chunking or speaker cache, while the Hugging Face offline forward chunks a whole recording. The PR itself limits its parity claim to one chunk (chunk_length * subsampling_factor, approximately 2,720 mel frames at the checkpoint defaults); the committed offline golden deliberately stays below this limit. Inputs beyond it still execute but no longer follow the reference inference path. Please decide on and enforce the public contract: reject/clearly constrain long offline inputs and route them to streaming, or implement the matching chunked offline path.

Smaller follow-ups

  • src/mobius/tasks/_diarization.py:20-24 says speaker_probs has time / subsampling_factor frames, but Nemotron upsamples and returns approximately one output per mel frame. Make the shared task's output-frame rule model-specific. Also correct the streaming task's output-length formula for windows whose raw-frame count is not divisible by the subsampling factor (src/mobius/tasks/_diarization_streaming.py:49-55).
  • The migrated testdata/golden/diarization/sortformer.json retains input_frames/num_spks provenance, while the new scripts/generate_golden.py emits num_frames/num_speakers; regenerate it or document that it was carried over. The Nemotron golden records a development Transformers version but no immutable Transformers source revision, making the reference implementation harder to reproduce.

Verification boundary: This was a static review, not an independent real-weight run. L1, L3, and lint passed on this head, but the L4 and L5 CI jobs each hit their one-hour timeout and were cancelled; that is not evidence that the model-specific goldens failed or passed. A targeted completed L4/L5 run is still needed. Short, non-subsampling-aligned final chunks and reduced-precision cache compression have not been independently compared with the HF reference here; these are open verification questions, not additional confirmed defects.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Fixes the 6 findings confirmed with the user from titaiwangms's latest
review pass:

- src/mobius/_testing/parity.py, parity_test.py,
  tests/e2e_golden_test.py: remove compare_diarization_golden()'s
  AMBIGUOUS downgrade. Dominant-speaker argmax agreement is not a valid
  substitute for full elementwise tolerance on a multi-label sigmoid
  output -- a secondary (non-dominant) speaker's probability can cross
  the 0.5 activation threshold (a real diarization error) while the
  argmax stays unchanged. Every allclose failure is now a hard FAIL;
  dominant-speaker match and active-speaker-set Jaccard are reported as
  diagnostics only. Updated L4/L5 assertions to require
  ParityResult.PASS.

- src/mobius/tasks/_diarization.py: made DiarizationTask's docstring
  model-agnostic -- "frames = time / subsampling_factor" is true for
  Sortformer but wrong for Nemotron3 (which upsamples back to
  per-mel-frame resolution, frames == time).

- src/mobius/models/nemotron3_diarization.py (forward_streaming): fixed
  a real pad-leak bug -- the raw-frame trim bound included the
  lookahead window's frame count instead of just the current chunk's,
  so a shorter final chunk without lookahead retained trailing padding
  in its output. Added a regression test in
  tests/build_graph/speech_test.py.

- testdata/golden/diarization/sortformer*: regenerated via real NeMo
  inference so field names match the current schema.

- scripts/generate_golden.py: pinned an exact transformers commit
  (c8b81b63232be35ab1774dd3cabbf499d8b9808f) for reproducible
  Nemotron3Diarization golden generation (the model isn't in any
  released transformers version yet); added
  _best_effort_package_commit() to record the resolved git commit in
  golden provenance for git-installed dependencies (transformers,
  nemo_toolkit). Regenerated the Nemotron3 golden JSON/NPZ files
  against the pinned commit.

- src/mobius/models/nemotron3_diarization.py (forward, the offline/
  non-streaming graph -- the largest finding): replaced the single
  flat non-causal pass with an ONNX Loop-based chunked implementation
  matching HuggingFace's real offline forward, which embeds the whole
  input once then loops in fixed chunk_length-sized chunks (with
  chunk_right_context lookahead), reusing the AOSC + FIFO cache
  mechanism with offline-specific cache sizes (config.fifo_length,
  config.speaker_cache_update_period -- distinct from streaming_config's
  sizes). Extracted a shared _run_chunk_and_update_cache() helper out
  of forward_streaming so both callers share the same encode/classify/
  cache-update logic parameterized by cache size. The loop accumulates
  logits into a fixed-size buffer (zero-padded per-iteration regions
  added in, rather than growing via Concat) to avoid an optimizer
  silently folding Concat(compile-time-empty, x) -> x and discarding
  all but the last iteration. Added _prerealize_parameters() to
  register every backbone/classifier parameter as a root-graph
  initializer with its final qualified name before the Loop body is
  built, and pushed distinguishing module scopes around each Loop/If
  subgraph -- both fixes for an ONNX SSA violation caused by nested
  subgraphs' independent per-graph node counters coincidentally
  generating identical auto-generated names.

- src/mobius/_configs/_base.py: added offline_fifo_length (default 40)
  and offline_speaker_cache_update_period (default 300) to
  Nemotron3DiarizationConfig, extracted from HuggingFace's top-level
  config.fifo_length/config.speaker_cache_update_period.

- src/mobius/_passes/_fold_transpose.py: fixed
  FoldTransposedInitializerPass removing a Transpose node via
  model.graph.remove(...) instead of node.graph.remove(...) -- this
  crashed whenever the node lived inside a subgraph (e.g. the new
  offline diarization Loop body), since ir.Graph.all_nodes() recurses
  into subgraphs but a node only belongs to its own (sub)graph. A
  general-purpose optimizer bug, not diarization-specific.

- tests/build_graph/speech_test.py: added
  TestBuildGraphNemotron3DiarizationOffline (package_builds, Loop
  presence, single/multi-chunk shape and cache-compression coverage)
  since no offline-forward ORT-execution tests existed before.

- testdata/cases/diarization/nemotron3_diarization_multichunk.yaml
  (new) + golden data: a 6000-mel-frame recording (750 encoder embeds,
  well above chunk_length=340) that forces HuggingFace's real offline
  forward to internally re-chunk into 3 iterations and exercise cache
  compression -- verifying full numeric parity of the new Loop-based
  offline graph against real HuggingFace chunked inference, not just
  structural ORT execution.

Verified: tests/build_graph/speech_test.py (214 passed),
tests/e2e_golden_test.py -k "diarization or sortformer" -m integration
(25 passed, against real checkpoints), and the broader
tests/build_graph + src/ suite (no new regressions; two pre-existing
gemma4/gemma3n failures confirmed unrelated via git stash).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: themason2011 <masoncorey@microsoft.com>
@themason2011

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough re-review — addressed all findings in b574814. Summary of changes and verification for each:

1. Major — L4/L5 can pass with incorrect diarization probabilities.
Removed compare_diarization_golden()'s AMBIGUOUS downgrade entirely (src/mobius/_testing/parity.py). Any elementwise allclose failure is now a hard FAIL; dominant-speaker argmax and active-speaker-set Jaccard are diagnostics only, never a substitute for the tolerance gate. Both L4/L5 consumers in tests/e2e_golden_test.py now assert result == ParityResult.PASS explicitly. Added a regression test reproducing your exact example (src/mobius/_testing/parity_test.py::test_secondary_speaker_threshold_crossing_fails, golden [0.9, 0.6] vs ONNX [0.9, 0.1]) confirming it now fails.

2. Major — the default offline graph silently changes semantics for long recordings.
Rewrote the offline forward() (src/mobius/models/nemotron3_diarization.py) as a chunked ONNX Loop that matches HuggingFace's real offline forward: the whole input is embedded once, then processed in chunk_length-sized windows (with chunk_right_context lookahead), reusing the AOSC + FIFO speaker-cache mechanism across chunks with offline-specific cache sizes (config.fifo_length / config.speaker_cache_update_period, exposed as offline_fifo_length / offline_speaker_cache_update_period on Nemotron3DiarizationConfig). This is no longer a "stay under the limit" contract — arbitrary-length recordings now follow the real chunked reference path. Verified with new golden data for a 6000-mel-frame recording (750 encoder embeds, well above chunk_length=340) that forces 3 real chunking iterations and triggers cache compression in HuggingFace's own reference implementation (testdata/cases/diarization/nemotron3_diarization_multichunk.yaml); tests/e2e_golden_test.py::TestL4CheckpointVerified::test_prefill_argmax_matches_golden[diarization/nemotron3_diarization_multichunk] passes against it. Also added tests/build_graph/speech_test.py::TestBuildGraphNemotron3DiarizationOffline (Loop presence, single/multi-chunk shapes, cache-compression trigger) since no offline ORT-execution tests existed before.

Smaller follow-ups:

  • DiarizationTask's docstring (src/mobius/tasks/_diarization.py) is now model-agnostic — no longer asserts frames = time / subsampling_factor universally; points to the specific model class for its exact formula. (The streaming task's output-length formula for non-subsampling-aligned windows was already corrected in a prior commit — confirmed still accurate.)
  • Regenerated testdata/golden/diarization/sortformer.json via real NeMo inference so its field names match the current schema (no more carried-over input_frames/num_spks).
  • The Nemotron golden now records an immutable transformers_commit (resolved via a new _best_effort_package_commit() helper in scripts/generate_golden.py) alongside the dev version string, for reproducibility.

Verification for this pass: tests/build_graph/speech_test.py (214 passed), tests/e2e_golden_test.py -k "diarization or sortformer" -m integration (25 passed, against the real nvidia/Nemotron-3-Diarization and Sortformer checkpoints — a completed, non-timed-out run), and the broader tests/build_graph + src/ suite (no new regressions). Along the way also fixed an unrelated general-purpose optimizer bug found while building the offline Loop graph: FoldTransposedInitializerPass was removing nodes via the wrong graph object, crashing for any Transpose living inside a subgraph.

Full details in the commit message: b574814.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants