Skip to content

Audio: add NeMo FastConformer ASR adapter - #2254

Merged
mohammadaaftabv merged 20 commits into
NVIDIA-NeMo:mainfrom
mohammadaaftabv:aaftabv/audio-nemo-fastconformer-adapter
Aug 11, 2026
Merged

Audio: add NeMo FastConformer ASR adapter#2254
mohammadaaftabv merged 20 commits into
NVIDIA-NeMo:mainfrom
mohammadaaftabv:aaftabv/audio-nemo-fastconformer-adapter

Conversation

@mohammadaaftabv

@mohammadaaftabv mohammadaaftabv commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a NeMo ASR adapter for the shared ASRStage / ASRAdapter contract and migrates Curator's in-repository NeMo ASR consumers to that adapter-based path.

The resulting ownership split is:

  • ASRStage owns file decoding, mono conversion, resampling, language filtering, batching, skip handling, output assembly, resources, and lifecycle routing.
  • NeMoASRAdapter owns NeMo checkpoint download, worker-local model load/unload, optional FastConformer local-attention conversion, optional RNNT CUDA-graph decoder configuration, batched transcription, and NeMo output normalization.

Changes

  • Add NeMoASRAdapter behind the shared ASR adapter protocol.
  • Keep model-specific options in adapter_kwargs rather than forcing them onto every ASR adapter.
  • Migrate the FLEURS benchmark, YAML, README, and notebook to ASRStage + NeMoASRAdapter.
  • Add a minimal shared-runner FastConformer tutorial using README.md + pipeline.yaml.
  • Update the shared YAML runner so pipeline is the default Hydra config name.
  • Keep Qwen-Omni model revision adapter-owned while preserving its existing pipeline behavior.
  • Move TorchCodec into the audio-common dependency path and select its CPU wheel for audio installs while retaining the Torch 2.11-compatible version override already present on main.
  • Update focused unit, configuration, lazy-import, stage, tagging, and real-GPU smoke coverage.

Intentional migration

This PR removes the legacy nemo_curator.stages.audio.inference.asr.asr_nemo.InferenceAsrNemoStage module and migrates all in-repository consumers to the shared stage/adapter path. It does not add a compatibility re-export; downstream users of the legacy import must move to:

ASRStage(
    adapter_target="nemo_curator.models.asr.nemo_asr.NeMoASRAdapter",
    model_id="nvidia/stt_en_fastconformer_ctc_large",
)

Review feedback addressed

  • Removed redundant adapter type/value checks.
  • Moved torch and OmegaConf imports to module scope while retaining the optional NeMo lazy import.
  • Consolidated the GPU smoke test into tests/models/asr/test_nemo_asr.py.
  • Made --config-name pipeline optional for shared-runner tutorials and added CLI coverage.
  • Removed the exact direct TorchCodec pin and documented the ABI-compatible shared override.

Validation after rebasing onto current main

  • 126 focused CPU tests passed; the marked real-GPU smoke test was deselected in this CPU test command.
  • Ruff lint passed.
  • CI-equivalent pre-commit checks passed.
  • uv lock --check passed with uv 0.12.2.
  • git diff --check passed.

@copy-pr-bot

copy-pr-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@mohammadaaftabv
mohammadaaftabv force-pushed the aaftabv/audio-nemo-fastconformer-adapter branch from 6e97d31 to 42b7cb6 Compare July 28, 2026 10:14
@mohammadaaftabv
mohammadaaftabv force-pushed the aaftabv/audio-nemo-fastconformer-adapter branch from c8e6c58 to a54f655 Compare August 4, 2026 08:02
@mohammadaaftabv
mohammadaaftabv marked this pull request as ready for review August 4, 2026 18:36
@mohammadaaftabv
mohammadaaftabv requested review from a team as code owners August 4, 2026 18:36
@mohammadaaftabv
mohammadaaftabv requested review from ayushdg and removed request for a team August 4, 2026 18:36
@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR introduces a NeMo FastConformer adapter and migrates in-repository NeMo ASR usage to the shared ASRStage lifecycle.

  • Adds NeMoASRAdapter with checkpoint staging, worker-local model lifecycle, FastConformer local-attention conversion, RNNT decoder configuration, and normalized batched results.
  • Migrates benchmarks, tutorials, configuration, and tests from InferenceAsrNemoStage to ASRStage.
  • Makes pipeline.yaml the default Hydra configuration and updates shared audio dependency resolution.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported legacy-import concern is addressed by the explicitly intentional breaking migration and accompanying guidance.

Important Files Changed

Filename Overview
nemo_curator/models/asr/nemo_asr.py Adds the NeMo ASR adapter, including model download/load lifecycle, optional decoder configuration, batched transcription, and output normalization.
nemo_curator/stages/audio/inference/asr/stage.py Updates the shared ASR stage to construct adapters with adapter-owned options and route NeMo inference through the common stage lifecycle.
nemo_curator/stages/audio/inference/asr/asr_nemo.py Removes the legacy NeMo-specific stage as part of the explicitly documented adapter migration.
nemo_curator/config/run.py Makes pipeline.yaml the default Hydra configuration while retaining explicit config-name support.
pyproject.toml Moves TorchCodec into the shared audio dependency path and configures the appropriate CPU package source.
tutorials/audio/nemo_fastconformer/pipeline.yaml Adds a shared-runner FastConformer pipeline using ASRStage and NeMoASRAdapter.

Sequence Diagram

sequenceDiagram
    participant Runner as Pipeline Runner
    participant Stage as ASRStage
    participant Adapter as NeMoASRAdapter
    participant NeMo as NeMo ASRModel
    Runner->>Stage: setup_on_node()
    Stage->>Adapter: download_weights_on_node()
    Runner->>Stage: setup()
    Stage->>Adapter: load_model(num_gpus)
    Runner->>Stage: process_batch(AudioTask[])
    Stage->>Stage: decode, mono-convert, resample, filter
    Stage->>Adapter: transcribe_batch(items)
    Adapter->>NeMo: transcribe(waveforms)
    NeMo-->>Adapter: hypotheses/text
    Adapter-->>Stage: ASRResult[]
    Stage-->>Runner: updated AudioTask[]
    Runner->>Stage: teardown()
    Stage->>Adapter: unload_model()
Loading

Reviews (12): Last reviewed commit: "Fix ASR adapter consistency after rebase" | Re-trigger Greptile

@mohammadaaftabv
mohammadaaftabv force-pushed the aaftabv/audio-nemo-fastconformer-adapter branch 2 times, most recently from a890d75 to e6329f8 Compare August 5, 2026 17:34
Comment thread nemo_curator/models/asr/nemo_asr.py Outdated
_model: Any = field(default=None, init=False, repr=False)

def __post_init__(self) -> None:
if not self.model_id:

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.

Some of these checks are overkill IMO. Like this and the check for whether enable_local_attention (and others) is a boolean.

Comment thread nemo_curator/models/asr/nemo_asr.py Outdated
msg = f"NeMoASRAdapter requires num_gpus to be 0 or 1, got {num_gpus!r}"
raise ValueError(msg)

import torch

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.

Let's use top-level imports for most of these.

Comment thread tests/models/asr/test_nemo_asr_gpu.py Outdated

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.

Let's move all the tests to test_nemo_asr.

```bash
python nemo_curator/config/run.py \
--config-path ../../tutorials/audio/nemo_fastconformer \
--config-name pipeline \

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.

Can config-name be optional?

Comment thread pyproject.toml Outdated
"nemo_toolkit[asr]>=2.7.2; platform_machine == 'x86_64' and platform_system != 'Darwin'",
"soundfile>=0.12.0",
"torchaudio",
"torchcodec==0.11.1; platform_machine == 'x86_64' and platform_system != 'Darwin'",

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.

Why the pin?

@mohammadaaftabv
mohammadaaftabv force-pushed the aaftabv/audio-nemo-fastconformer-adapter branch from c414de6 to 9e73313 Compare August 6, 2026 19:16
}
},
"outputs": [],
"source": [
"configure_loguru()\n",
"ray_client.stop()"
"configure_loguru()\nray_client.stop()"

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.

Just wondering why do we need configure_loguru here?

Comment thread pyproject.toml Outdated
explicit = true

[[tool.uv.index]]
name = "pytorch-cpu"

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.

I was also wondering why it needs this?

@mohammadaaftabv
mohammadaaftabv force-pushed the aaftabv/audio-nemo-fastconformer-adapter branch from a395afd to 9e38391 Compare August 7, 2026 18:18
@mohammadaaftabv

Copy link
Copy Markdown
Contributor Author

/ok to test 9e38391

Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
@mohammadaaftabv

Copy link
Copy Markdown
Contributor Author

/ok to test 9a7ef2c

Signed-off-by: aaftaabv@gmail.com <aaftaabv@gmail.com>
@mohammadaaftabv

Copy link
Copy Markdown
Contributor Author

/ok to test 16a0c81

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