Skip to content

Add support for Nemotron Omni - #46509

Merged
ArthurZucker merged 56 commits into
huggingface:mainfrom
meatybobby:bobchen/nemotron_omni
Sep 24, 2026
Merged

ArthurZucker merged 56 commits into
huggingface:mainfrom
meatybobby:bobchen/nemotron_omni

Conversation

@meatybobby

@meatybobby meatybobby commented Jun 8, 2026 •

Copy link
Copy Markdown
Contributor

CPU CI GPU run-slow

What does this PR do?

Add NVIDIA Nemotron Omni support (This PR depends on radio PR #46425 )

Code Agent Policy

The Transformers repo is currently being overwhelmed by a large number of PRs and issue comments written by
code agents. We are currently bottlenecked by our ability to review and respond to them. As a result,
we ask that new users do not submit pure code agent PRs at this time.
You may use code agents in drafting or to help you diagnose issues. We'd also ask autonomous "OpenClaw"-like agents
not to open any PRs or issues for the moment.

PRs that appear to be fully agent-written will probably be closed without review, and we may block users who do this
repeatedly or maliciously.

This is a rapidly-evolving situation that's causing significant shockwaves in the open-source community. As a result,
this policy is likely to be updated regularly in the near future. For more information, please read CONTRIBUTING.md.

  • I confirm that this is not a pure code agent PR.

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you read the contributor guideline,
    Pull Request section?
  • Was this discussed/approved via a Github issue or the forum? Please add a link
    to it if that's the case.
  • Did you make sure to update the documentation with your changes? Here are the
    documentation guidelines, and
    here are tips on formatting docstrings.
  • Did you write any new necessary tests?

Who can review?

Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.

@ArthurZucker ArthurZucker reopened this Jun 15, 2026
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@meatybobby

Copy link
Copy Markdown
Contributor Author

@ArthurZucker This PR is ready to be merged. Could we start the reviewing process?

@ArthurZucker

Copy link
Copy Markdown
Collaborator

Yes sorry went off this week

pass


class EfficientVideoSampling:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this needs the model prefixing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

also if its just for a func not sure we need a clas?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

keep only func here

Comment on lines +155 to +157
parakeet_config = AutoConfig.for_model("parakeet_encoder", **config_dict)
self.config = parakeet_config
self.encoder = AutoModel.from_config(parakeet_config)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the config should just have a config saved. and at this point you create a wrapper around something that should just be used directly this is too many layers

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Directly use ParakeetEncoderConfig now

return x

def extract_feature(self, pixel_values):
if isinstance(pixel_values, (list, tuple)):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the whole pixel shuffle exists in other models, it should be isolated as this shows it does not belong here. Isolated and done with the mlp1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Isolate in NemotronH_Omni_Reasoning_V3VisionProjector now

orig_projection = embeddings.patch_projection
embeddings.patch_projection = embeddings.video_patch_projection
try:
vit_embeds = self.vision_model(x).features

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

that is not super idea no? why do we have to do try catch it should be predictable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

video_patch_projection moved to inside Radio instead of replace patch_projection now.

Comment on lines +506 to +537
if image_vit_embeds is not None:
image_mask = input_ids_copy == self.img_context_token_id
assert image_mask.sum() != 0, "No image tokens found in input_ids"
inputs_embeds[image_mask] = image_vit_embeds.reshape(-1, C).to(
inputs_embeds.device, inputs_embeds.dtype
)

if video_vit_embeds is not None:
if B > 1:
raise NotImplementedError("Video is not supported for batch size > 1")
video_mask = input_ids_copy == self.img_context_token_id
assert video_mask.sum() != 0, "No video tokens found in input_ids"
inputs_embeds[video_mask] = video_vit_embeds.reshape(-1, C).to(
inputs_embeds.device, inputs_embeds.dtype
)

if sound_embeds is not None and self.sound_context_token_id is not None:
sound_mask = input_ids_copy == self.sound_context_token_id
assert sound_mask.sum() != 0, "No sound tokens found in input_ids"
if sound_embeds.dim() == 3 and sound_embeds.shape[0] > 1 and sound_attention_mask is not None:
natural_input_lengths = sound_attention_mask.sum(-1) + 1
output_lengths = self.sound_encoder.encoder._get_subsampling_output_length(natural_input_lengths)
flat = torch.cat(
[sound_embeds[i, : int(n)] for i, n in enumerate(output_lengths.tolist())],
dim=0,
)
else:
flat = sound_embeds.reshape(-1, C)
assert sound_mask.sum().item() == flat.shape[0], (
f"sound token count ({sound_mask.sum().item()}) != encoder output count ({flat.shape[0]})"
)
inputs_embeds[sound_mask] = flat.to(inputs_embeds.device, inputs_embeds.dtype)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

you should take inspiration from other models on how we usally do this specific part! an example can be found in

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Follow Qwen3OmniMoe for generate path now

Comment on lines +415 to +426
def extract_sound_feature(
self,
input_features: torch.Tensor,
attention_mask: torch.Tensor | None = None,
) -> torch.Tensor:
if self.sound_encoder is None:
raise RuntimeError("Sound encoder not initialized. Check if sound_config is provided.")
sound_embeds = self.sound_encoder(input_features, attention_mask)
sound_embeds = sound_embeds.to(dtype=torch.bfloat16)
sound_embeds = self.sound_projection(sound_embeds)
return sound_embeds

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why not isolate this as audioProjector and have a single forward for it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Isolate as NemotronH_Omni_Reasoning_V3SoundProjector now

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

dropping the first review! as always we need to standardize cf the other omni models!

from transformers import NemotronH_Omni_Reasoning_V3


class NemotronHOmniModelTester:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

you can probably inherit from causal LM tester?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

see other omni models and which class they inherit from!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Inherit GenerationTesterMixin now

@meatybobby

Copy link
Copy Markdown
Contributor Author

We need to have this a if else in the init in that case! We try to avoid this, but here it seems the only choice unless you inherit in the modular from it to use directly!

Added if-else in init now.

@meatybobby

Copy link
Copy Markdown
Contributor Author

@ArthurZucker The PR is ready. Could we merge this PR if there is no other concern?

@vasqu

vasqu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

cc @VI-Arthur

@vasqu vasqu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sorry for the delays!

# NemotronH Omni

NemotronH Omni is a multimodal reasoning model from NVIDIA that pairs the [NemotronH](./nemotron_h) hybrid
Mamba-Transformer language model with a [RADIO](./radio) vision encoder and an optional Parakeet-based sound encoder.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Mamba-Transformer language model with a [RADIO](./radio) vision encoder and an optional Parakeet-based sound encoder.
Mamba-Transformer language model with a [RADIO](./radio) vision encoder and an Parakeet-based sound encoder.

nit but it sounds a bit off with the optional. I'd assume for omni == always all modalities no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We will reuse model class for the model without audio encoder

@meatybobby

Copy link
Copy Markdown
Contributor Author

@vasqu The comments are resolved. Could you review them again? Thanks

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's go! Thanks for you efforts and bearing with us.
Omni models are very hard in general, the hardest to standardize. 🤗

@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: auto, nemotron_h, nemotron_h_omni, radio

@ArthurZucker

Copy link
Copy Markdown
Collaborator

run-slow: auto, nemotron_h, nemotron_h_omni, radio

@github-actions

Copy link
Copy Markdown
Contributor

AMD CI

Workflow Run ⚙️

This comment contains run-slow, running the specified jobs on AMD:

models: ["models/auto", "models/nemotron_h", "models/nemotron_h_omni", "models/radio"]

@github-actions

Copy link
Copy Markdown
Contributor

Nvidia CI

Workflow Run ⚙️

This comment contains run-slow, running the specified jobs on Nvidia:

models: ["models/auto", "models/nemotron_h", "models/nemotron_h_omni", "models/radio"]
quantizations: []

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 35942958744:2
Result: failure | Jobs: 16 | Tests: 168,253 | Failures: 1 | Duration: 14h 57m

@github-actions

Copy link
Copy Markdown
Contributor

CI Results (AMD)

Workflow Run ⚙️

Commit Info

Context Commit Description
RUN 6bd29722 workflow commit (merge commit)
PR 9b9e05c4 branch commit (from PR)
main f3247073 base commit (on main)

Model CI Report

❌ 10 new failed tests from this PR 😭

  • nemotron_h_omni:
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_eager_matches_sdpa_generate (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_sdpa_can_compile_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_sdpa_can_dispatch_on_flash (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_eager_matches_fa2_generate (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_eager_matches_sdpa_generate (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_flash_attn_2_from_config (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_flash_attn_2_inference_equivalence (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_flash_attn_2_inference_equivalence_right_padding (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_sdpa_can_compile_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronH_Omni_Reasoning_V3IntegrationTest::test_video_generation (✅ ⟹ ❌)

@github-actions

Copy link
Copy Markdown
Contributor

CI Results (Nvidia)

Workflow Run ⚙️

Commit Info

Context Commit Description
RUN 6bd29722 workflow commit (merge commit)
PR 9b9e05c4 branch commit (from PR)
main f3247073 base commit (on main)

Model CI Report

❌ 21 new failed tests from this PR 😭

  • nemotron_h_omni:
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_eager_matches_sdpa_generate (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_onnx_export_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_onnx_export_generate_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_onnx_export_generate_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_onnx_export_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_sdpa_can_compile_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_sdpa_can_dispatch_on_flash (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_torch_export_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_torch_export_generate_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_torch_export_generate_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniVision2TextModelTest::test_torch_export_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_eager_matches_sdpa_generate (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_onnx_export_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_onnx_export_generate_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_onnx_export_generate_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_onnx_export_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_sdpa_can_compile_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_torch_export_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_torch_export_generate_dynamic (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_torch_export_generate_static (✅ ⟹ ❌)
    tests/models/nemotron_h_omni/test_modeling_nemotron_h_omni.py::NemotronHOmniAudio2TextModelTest::test_torch_export_static (✅ ⟹ ❌)

@ArthurZucker

Copy link
Copy Markdown
Collaborator

I’ll check the failures and fixe but it’s ready to merge otherwise !

@ArthurZucker

Copy link
Copy Markdown
Collaborator

OK!
The failing tests are somewhat unrelated. Parakeet is at fault here, I am opening this #49070 for the fix and merging

@ArthurZucker
ArthurZucker merged commit 4b28d51 into huggingface:main Sep 24, 2026
114 of 118 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants