Skip to content

Pass kwargs to the Mamba2 mixer in Nemotron-H, Falcon-H1 and Mamba2 - #48490

Merged
vasqu merged 3 commits into
huggingface:mainfrom
kfastino:nemotron-h-thread-mixer-kwargs
Sep 4, 2026
Merged

vasqu merged 3 commits into
huggingface:mainfrom
kfastino:nemotron-h-thread-mixer-kwargs

Conversation

@kfastino

@kfastino kfastino commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

CPU CI GPU run-slow

What does this PR do?

Three models with a Mamba2 mixer never deliver kernel kwargs to it, because the block accepts **kwargs and then drops them at the mixer call:

# nemotron_h, before
if self.block_type == "linear_attention":
    hidden_states = self.mixer(hidden_states, cache_params=past_key_values, attention_mask=attention_mask)

The Mamba2 mixer is already written to receive them — it splats them into all three kernel calls (mamba2_split_conv1d_scan_combined via fused_kwargs, causal_conv1d_fn, and mamba2_chunk_scan) — so this is the missing hop.

The main consequence is seq_idx, which the Mamba2 kernels use to keep state from bleeding across concatenated sequences. Without it there is no way to run these models on packed / variable-length batches: the conv and scan treat a packed row as one long sequence. With this change, model(input_ids, seq_idx=seq_idx) reaches the kernels.

Which models

Checked all six models with a Mamba2 mixer, on both hops:

model model → block block → mixer
bamba passes **kwargs passes **kwargs
granitemoehybrid passes **kwargs passes **kwargs
zamba2 passes **kwargs passes **kwargs
nemotron_h passes **kwargs fixed here
falcon_h1 fixed here fixed here
mamba2 fixed here fixed here

bamba, granitemoehybrid and zamba2 already forward kwargs in exactly this position, so this brings the other three in line rather than introducing a new pattern. falcon_h1 and mamba2 lost them at both hops.

nemotron_h and falcon_h1 are changed through their modular files and regenerated; mamba2 has no modular file.

Tests

test_kwargs_reach_mamba2_mixer plus a create_and_check_kwargs_reach_mamba2_mixer tester method for each of the three models, asserting a kwarg handed to the model arrives at the mixer. Each fails without its corresponding fix.

tests/models/nemotron_h tests/models/falcon_h1 tests/models/mamba2
389 passed, 462 skipped, 2808 subtests passed

utils/check_modular_conversion.py and ruff check / ruff format are clean.

Context

Found while adding Mamba2 packing support to Unsloth (unslothai/unsloth#9812). Without this, reaching the kernels from outside requires rebinding the kernel symbol in the modeling module's globals, which this makes unnecessary.

Who can review?

@ArthurZucker @vasqu

NemotronHBlock forwards **kwargs to the attention mixer but dropped them
for the linear-attention branch, so kernel kwargs such as seq_idx never
reached the mixer. The mixer already splats them into the fused
conv1d+scan, the conv fallback and the chunk scan, and Bamba,
GraniteMoEHybrid and Zamba2 all pass them through.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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.

Just some small comments, is this issue on other models? E.g. the mamba2 related models could all have the same issue, could we cross check pls

model(input_ids, seq_idx=seq_idx)

self.assertTrue(seen, "the linear-attention mixer was never called")
self.assertIn("seq_idx", seen[0])

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.

Can we change the test design as in test_mamba2_slow_vs_fast_forward so we actually do the heavy lifting in the tester

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.

Done — moved to create_and_check_kwargs_reach_mamba2_mixer on the tester, with the test method just calling it. Added the same pair to the falcon_h1 and mamba2 testers.

def recording_forward(*args, **kwargs):
seen.append(set(kwargs))
# The torch fallback kernels do not implement seq_idx, so only record it.
kwargs.pop("seq_idx", None)

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.

Imo this pop is not needed 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.

Correct, removed. The three fallbacks (mamba2_split_conv1d_scan_combined, causal_conv1d_fn, mamba2_chunk_scan) all take **kwargs, so seq_idx passes through harmlessly on the torch path — I had added the pop defensively without checking.

Same gap as nemotron_h: the block accepts **kwargs but drops them at the
mixer, so kernel kwargs such as seq_idx never reach the kernels. In these
two the model also did not pass kwargs down to the block.

Also move the nemotron_h test into the tester and drop the seq_idx pop,
which is not needed since the torch fallback kernels take **kwargs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kfastino

kfastino commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Cross-checked all six models with a Mamba2 mixer, on both hops (model → block, block → mixer):

model model → block block → mixer
bamba passes **kwargs passes **kwargs
granitemoehybrid passes **kwargs passes **kwargs
zamba2 passes **kwargs passes **kwargs
nemotron_h passes **kwargs dropped
falcon_h1 dropped dropped
mamba2 dropped dropped

So you were right that it was not just Nemotron-H. falcon_h1 and mamba2 are worse — they lose kwargs at both hops, and in both the block signature already accepts **kwargs and just doesn't forward them. Fixed both in 9c909bc (falcon_h1 through its modular file, mamba2 directly since it has none), with the same tester/test pair added to each so all three are covered rather than only the one I happened to need.

389 passed, 462 skipped, 2808 subtests passed across the three test files, and check_modular_conversion is clean. Each new test fails without its corresponding fix.

Two things I noticed but left alone, happy to fold in either way:

  • create_and_check_mamba2_slow_vs_fast_forward in the nemotron_h and mamba2 testers still calls mixer.cuda_kernels_forward / mixer.torch_forward, which no longer exist after the mixer refactor. Both are gated on require_kernels + accelerator so they skip in normal CI and the staleness is invisible — they'd raise AttributeError on a machine that satisfies the guards.
  • falcon_h1's mamba_attention_mask means its block passes a different mask to the mixer than to attention, so anything mask-shaped in **kwargs would now reach the mixer alongside it. Not a problem for seq_idx, but it's the one place where the three models aren't symmetric.

@kfastino kfastino changed the title Pass kwargs to the Nemotron-H Mamba2 mixer Pass kwargs to the Mamba2 mixer in Nemotron-H, Falcon-H1 and Mamba2 Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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

run-slow: falcon_h1, mamba2, nemotron_h

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 33661231493:2
Result: success | Jobs: 5 | Tests: 353 | Failures: 0 | Duration: 4m 2s

@kfastino
kfastino requested a review from vasqu September 3, 2026 22:29

@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.

Thanks, looks good now

@vasqu
vasqu enabled auto-merge September 4, 2026 12:28
@vasqu
vasqu added this pull request to the merge queue Sep 4, 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.

Merged via the queue into huggingface:main with commit 86a6c97 Sep 4, 2026
38 checks passed
sbucaille pushed a commit to sbucaille/transformers that referenced this pull request Sep 16, 2026
…uggingface#48490)

* Pass kwargs to the Nemotron-H Mamba2 mixer

NemotronHBlock forwards **kwargs to the attention mixer but dropped them
for the linear-attention branch, so kernel kwargs such as seq_idx never
reached the mixer. The mixer already splats them into the fused
conv1d+scan, the conv fallback and the chunk scan, and Bamba,
GraniteMoEHybrid and Zamba2 all pass them through.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Pass kwargs to the Mamba2 mixer in falcon_h1 and mamba2 too

Same gap as nemotron_h: the block accepts **kwargs but drops them at the
mixer, so kernel kwargs such as seq_idx never reach the kernels. In these
two the model also did not pass kwargs down to the block.

Also move the nemotron_h test into the tester and drop the seq_idx pop,
which is not needed since the torch fallback kernels take **kwargs.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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