Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5d49c8b
fix(mcore): support process group contract drift
svcnemo-autobot Jul 29, 2026
6ccdc2e
fix(tests): package conversion examples
svcnemo-autobot Jul 29, 2026
dee6739
build(ci): bake diffusion codecs into test image
svcnemo-autobot Jul 29, 2026
657e01f
build(ci): verify diffusion codec wheels
svcnemo-autobot Jul 29, 2026
25191b1
fix(ci): allow reviewed ARM64 codec wheel
svcnemo-autobot Jul 29, 2026
52e4421
fix(ci): allow reviewed ARM64 av wheel
svcnemo-autobot Jul 29, 2026
a8ecb16
chore(deps): update MCore main and dev commits
svcnemo-autobot Jul 29, 2026
89114ee
fix(ci): skip MCore setuptools helper import
svcnemo-autobot Jul 30, 2026
b5e4b8a
fix(conversion): use installed NeMo Run
svcnemo-autobot Jul 30, 2026
d1c339e
fix(tests): prefer staged Qwen media fixtures
ko3n1g Jul 30, 2026
a8b966f
fix(conversion): support installed NeMo Run executor
svcnemo-autobot Jul 30, 2026
1f916d0
fix(tests): prefer staged Megatron-LM release assets
ko3n1g Jul 30, 2026
a54973d
fix(tests): share Megatron-LM release fixture path
ko3n1g Jul 30, 2026
47ebf8d
Merge remote-tracking branch 'upstream/pr-5188' into fix/mcore-compat…
svcnemo-autobot Jul 30, 2026
6dfa944
fix(ernie): accept MCore rotary keyword arguments
svcnemo-autobot Jul 30, 2026
e08634e
Merge remote-tracking branch 'upstream/main' into fix/mcore-compat-26…
svcnemo-autobot Jul 30, 2026
c35f218
chore(compat): drop superseded diffusion setup
svcnemo-autobot Jul 30, 2026
f7104fb
chore(deps): refresh MCore main and dev commits
svcnemo-autobot Jul 30, 2026
fb5300f
chore(deps): refresh MCore dev validation lock
svcnemo-autobot Jul 30, 2026
85e4e66
fix(ernie-vl): preserve packed rotary embeddings with CP
svcnemo-autobot Jul 30, 2026
5835d0a
Merge remote-tracking branch 'origin/fix/mcore-compat-260729' into ch…
svcnemo-autobot Jul 30, 2026
2e63316
chore(deps): refresh MCore validation commits
svcnemo-autobot Jul 31, 2026
9b2137a
chore(deps): record MCore validation heads
svcnemo-autobot Jul 31, 2026
fb9f82a
chore: merge current Bridge compatibility base
svcnemo-autobot Jul 31, 2026
6074595
chore(deps): validate MCore expert TP fix
svcnemo-autobot Jul 31, 2026
4782053
chore: merge upstream Bridge main
svcnemo-autobot Jul 31, 2026
e71ae26
chore: merge current Bridge main
svcnemo-autobot Jul 31, 2026
6385511
fix(gemma-vl): select available attention backend
svcnemo-autobot Jul 31, 2026
3ca105f
fix(gemma-vl): select available attention backend
svcnemo-autobot Jul 31, 2026
9ee6001
fix(gemma-vl): permit automatic attention selection
svcnemo-autobot Jul 31, 2026
3c1c00c
Merge commit '9ee600113cff48a2310d45f7e1ef3a7d09d13a49' into chore/bu…
svcnemo-autobot Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .dev.commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0e6ac576fba8995fca541ceb6af5f35eb0e4f16e
af91b6b5cc5aec01458b8e6d24ccb0f4717e49e3
2 changes: 1 addition & 1 deletion .main.commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cd4afffa648426a959dc7cb1e24b5ce7d0c3ff54
6513e3e23d6b5eda6a1c934990b15e804237732b
2 changes: 1 addition & 1 deletion 3rdparty/Megatron-LM
Submodule Megatron-LM updated 1108 files
3 changes: 3 additions & 0 deletions docker/common/import_check_skip.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ rec2idx
# Pytest plugin shipped by `hypothesis`; eagerly imports `pytest`, which is not
# installed in the fw_base/fw_final runtime venvs (test-time-only dependency):
_hypothesis_pytestplugin

# MCore's setuptools build helper executes setup() when imported; it is not a runtime module:
setup
2 changes: 2 additions & 0 deletions examples/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
"""Megatron Bridge examples package."""
2 changes: 2 additions & 0 deletions examples/conversion/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
"""Checkpoint conversion examples."""
2 changes: 2 additions & 0 deletions examples/conversion/adapter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
"""Adapter conversion examples."""
5 changes: 3 additions & 2 deletions scripts/conversion/setup_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,13 @@ def _build_executor(
task_count = args.gpus_per_node if args.device == "gpu" else 1
launcher = run.Torchrun() if args.executor == "local" and args.device == "gpu" else None
if args.executor == "local":
return run.LocalExecutor(
nodes=1,
executor = run.LocalExecutor(
ntasks_per_node=task_count,
launcher=launcher,
packager=run.Packager(),
)
executor.nodes = 1
return executor

gpu_kwargs = {}
if args.device == "gpu" and not args.no_gpu_resource_request:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ def forward(
position_ids: torch.Tensor,
mrope_section,
cp_group=None,
return_raw_freqs: bool = False,
packed_seq: bool = False,
) -> Tensor:
"""Compute ERNIE-style interleaved M-RoPE embeddings.

Expand All @@ -175,10 +177,14 @@ def forward(
mrope_section: Ignored (kept for API compatibility). ERNIE uses
freq_allocation instead.
cp_group: Context parallel group.
return_raw_freqs: Ignored because ERNIE uses interleaved rotary embeddings.
packed_seq: Whether the sequence uses THD packing. Packed sequences retain
their full rotary embedding for packed context-parallel handling.

Returns:
Tensor: RoPE embedding of shape [seq_len, batch, 1, head_dim].
"""
del return_raw_freqs
device = self.inv_freq.device
seq = position_ids.to(device=device, dtype=self.inv_freq.dtype)
# seq: [3, bs, seq_len]
Expand Down Expand Up @@ -237,7 +243,7 @@ def forward(

if cp_group is None:
cp_group = self.cp_group
if cp_group is not None and cp_group.size() > 1:
if cp_group is not None and cp_group.size() > 1 and not packed_seq:
emb = get_pos_emb_on_this_cp_rank(emb, 0, cp_group)
return emb

Expand Down
4 changes: 2 additions & 2 deletions src/megatron/bridge/models/gemma_vl/gemma3_vl_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ class Gemma3VLModelProvider(Gemma3ModelProvider):
def provide(self, pre_process=None, post_process=None, vp_stage=None) -> Gemma3VLModel:
if not self.is_vision_language:
raise ValueError("Gemma 3 VL requires is_vision_language=True.")
if self.attention_backend is not AttnBackend.fused:
raise ValueError("Gemma 3 VL requires the fused attention backend for attention bias.")
if self.attention_backend not in (AttnBackend.auto, AttnBackend.fused):
raise ValueError("Gemma 3 VL requires automatic or fused attention for attention bias.")
if self.context_parallel_size != 1:
raise ValueError("Gemma 3 VL does not support context parallelism with attention bias.")

Expand Down
33 changes: 29 additions & 4 deletions src/megatron/bridge/models/megatron_mimo/megatron_mimo_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,28 @@
from megatron.core.hyper_comm_grid import HyperCommGrid


_GTP_REMAT_PROCESS_GROUP_ALIASES = (
"dp_cp_gtp_remat",
"expt_dp_gtp_remat",
"tp_ep_pp_with_egtp_remat",
)


def _get_gtp_remat_process_group_kwargs(
dp_cp_group: torch.distributed.ProcessGroup,
expt_dp_group: torch.distributed.ProcessGroup,
tp_ep_pp_group: torch.distributed.ProcessGroup,
) -> Dict[str, torch.distributed.ProcessGroup]:
"""Return GTP-remat aliases supported by the installed MCore contract."""
process_groups = {
"dp_cp_gtp_remat": dp_cp_group,
"expt_dp_gtp_remat": expt_dp_group,
"tp_ep_pp_with_egtp_remat": tp_ep_pp_group,
}
declared_fields = ProcessGroupCollection.__dataclass_fields__
return {name: process_groups[name] for name in _GTP_REMAT_PROCESS_GROUP_ALIASES if name in declared_fields}


@dataclass
class MegatronMIMOInfra:
"""MegatronMIMO infrastructure metadata (separate from model).
Expand Down Expand Up @@ -297,8 +319,13 @@ def _get_pg_collections_from_grids(
expt_dp_group = grid.get_pg(["expt_dp"], view=EXPERT_VIEW_NAME)
tp_ep_pp_group = grid.get_pg(["expt_tp", "ep", "pp"], view=EXPERT_VIEW_NAME)

# HyperCommGrid does not define GTP-remat axes, so the GTP-inclusive
# HyperCommGrid does not define GTP-remat axes, so supported GTP-inclusive
# process groups collapse to their existing non-GTP equivalents.
gtp_remat_process_groups = _get_gtp_remat_process_group_kwargs(
dp_cp_group,
expt_dp_group,
tp_ep_pp_group,
)
pg_collections[module_name] = ProcessGroupCollection(
tp=grid.get_pg(["tp"]),
dp=grid.get_pg(["dp"]),
Expand All @@ -308,19 +335,17 @@ def _get_pg_collections_from_grids(
expt_tp=grid.get_pg(["expt_tp"], view=EXPERT_VIEW_NAME),
expt_dp=expt_dp_group,
dp_cp=dp_cp_group,
dp_cp_gtp_remat=dp_cp_group,
expt_dp_gtp_remat=expt_dp_group,
intra_dp_cp=dp_cp_group,
tp_cp=grid.get_pg(["tp", "cp"]),
tp_dp_cp=grid.get_pg(["tp", "dp", "cp"]),
mp=grid.get_pg(["tp", "pp"]),
tp_ep=grid.get_pg(["expt_tp", "ep"], view=EXPERT_VIEW_NAME),
tp_ep_pp=tp_ep_pp_group,
tp_ep_pp_with_egtp_remat=tp_ep_pp_group,
intra_expt_dp=expt_dp_group,
intra_dist_opt=grid.get_pg(["tp", "cp", "dp", "pp"]),
pos_embd=pos_embd_pg if first_stage else None,
embd=embd_pg if (first_stage or last_stage) else None,
**gtp_remat_process_groups,
)
else:
pg_collections[module_name] = None
Expand Down
12 changes: 6 additions & 6 deletions src/megatron/bridge/recipes/gemma3_vl/h100/gemma3_vl.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def gemma3_vl_4b_sft_1gpu_h100_bf16_config() -> ConfigContainer:
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down Expand Up @@ -182,7 +182,7 @@ def gemma3_vl_12b_sft_4gpu_h100_bf16_config() -> ConfigContainer:
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down Expand Up @@ -294,7 +294,7 @@ def gemma3_vl_27b_sft_16gpu_h100_bf16_config() -> ConfigContainer:
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down Expand Up @@ -415,7 +415,7 @@ def gemma3_vl_4b_peft_1gpu_h100_bf16_config(peft_scheme: str | PEFT = "lora") ->
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down Expand Up @@ -536,7 +536,7 @@ def gemma3_vl_12b_peft_1gpu_h100_bf16_config(peft_scheme: str | PEFT = "lora") -
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down Expand Up @@ -657,7 +657,7 @@ def gemma3_vl_27b_peft_4gpu_h100_bf16_config(peft_scheme: str | PEFT = "lora") -
cfg.model.cuda_graph_warmup_steps = 3

# Kernel selections
cfg.model.attention_backend = AttnBackend.fused
cfg.model.attention_backend = AttnBackend.auto
cfg.model.cross_entropy_loss_fusion = True
cfg.model.cross_entropy_fusion_impl = "native"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,13 @@ def test_local_cpu_executor_uses_one_process_without_launcher():
module = _load_setup_conversion_module()
captured = {}
module.run.Packager = lambda: "packager"
module.run.LocalExecutor = lambda **kwargs: captured.update(kwargs) or types.SimpleNamespace(**kwargs)

def local_executor(**kwargs):
assert "nodes" not in kwargs
captured.update(kwargs)
return types.SimpleNamespace(**kwargs)

module.run.LocalExecutor = local_executor
args = _parse(module)

executor = module._build_executor(args, [], [])
Expand Down
47 changes: 47 additions & 0 deletions tests/unit_tests/models/ernie_vl/test_ernie45_vl_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
ErnieMultiTypeMoE,
MultiTypeMoeSubmodules,
)
from megatron.bridge.models.ernie_vl.modeling_ernie45_vl.model import ErnieMultimodalRotaryEmbedding


def _make_vision_config():
Expand Down Expand Up @@ -469,6 +470,52 @@ def test_offset_row_parallel_mapping_resolve(self):
assert "64" in resolved.hf_param


class TestErnieMultimodalRotaryEmbedding:
"""Test ERNIE multimodal rotary context-parallel handling."""

@pytest.fixture
def rotary_embedding(self):
"""Create a CPU rotary embedding without invoking the CUDA constructor."""
rotary_embedding = ErnieMultimodalRotaryEmbedding.__new__(ErnieMultimodalRotaryEmbedding)
torch.nn.Module.__init__(rotary_embedding)
rotary_embedding.inv_freq = torch.tensor([1.0, 0.5])
rotary_embedding.seq_len_interpolation_factor = None
rotary_embedding.freq_allocation = 0
rotary_embedding.cp_group = None
return rotary_embedding

@pytest.fixture
def position_ids(self):
"""Create distinct temporal, height, and width positions."""
return torch.arange(12).reshape(3, 1, 4)

def test_unpacked_sequence_slices_context_parallel_embedding(self, rotary_embedding, position_ids):
cp_group = Mock()
cp_group.size.return_value = 2

with patch(
"megatron.bridge.models.ernie_vl.modeling_ernie45_vl.model.get_pos_emb_on_this_cp_rank",
side_effect=lambda embedding, *_: embedding[:2],
) as get_cp_embedding:
output = rotary_embedding(position_ids, [1, 1, 0], cp_group=cp_group)

assert output.shape == (2, 1, 1, 4)
get_cp_embedding.assert_called_once()
assert get_cp_embedding.call_args.args[1:] == (0, cp_group)

def test_packed_sequence_retains_full_context_parallel_embedding(self, rotary_embedding, position_ids):
cp_group = Mock()
cp_group.size.return_value = 2

with patch(
"megatron.bridge.models.ernie_vl.modeling_ernie45_vl.model.get_pos_emb_on_this_cp_rank"
) as get_cp_embedding:
output = rotary_embedding(position_ids, [1, 1, 0], cp_group=cp_group, packed_seq=True)

assert output.shape == (4, 1, 1, 4)
get_cp_embedding.assert_not_called()


class TestErnie45VLModelProvider:
"""Test Ernie45VLModelProvider class."""

Expand Down
2 changes: 1 addition & 1 deletion tests/unit_tests/models/gemma/test_gemma4_modeling.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ def test_moe_global_rotary_matches_hf_proportional_coordinate_layout(self):
hidden_states = torch.arange(1, head_dim + 1, dtype=torch.float32).view(1, 1, 1, head_dim)
freqs = rotary.get_freqs_non_repeated(1, offset=position)
freqs = torch.cat((freqs, freqs), dim=-1)[:, None, None, :]
config = SimpleNamespace(apply_rope_fusion=False, rotary_interleaved=False)
config = SimpleNamespace(apply_rope_fusion=False, rotary_interleaved=False, mrope_section=None)
actual = apply_rotary_pos_emb(hidden_states, freqs, config, cp_group=object())

expected = hidden_states.clone()
Expand Down
16 changes: 15 additions & 1 deletion tests/unit_tests/models/gemma_vl/test_gemma3_vl_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,20 @@ def test_gemma3_vl_rejects_causal_override(self):
with pytest.raises(ValueError, match="requires is_vision_language=True"):
provider.provide()

def test_gemma3_vl_accepts_auto_attention_override(self):
"""Test that automatic backend selection preserves the VL attention bias."""
provider = Gemma3VLModelProvider(
num_layers=28,
hidden_size=2560,
num_attention_heads=10,
)
provider.attention_backend = AttnBackend.auto

with patch("megatron.bridge.models.gemma_vl.gemma3_vl_provider.Gemma3VLModel") as model:
provider.provide()

model.assert_called_once()

def test_gemma3_vl_rejects_flash_attention_override(self):
"""Test that runtime overrides cannot select a backend that drops the VL mask."""
provider = Gemma3VLModelProvider(
Expand All @@ -222,7 +236,7 @@ def test_gemma3_vl_rejects_flash_attention_override(self):
)
provider.attention_backend = AttnBackend.flash

with pytest.raises(ValueError, match="requires the fused attention backend"):
with pytest.raises(ValueError, match="requires automatic or fused attention"):
provider.provide()

def test_gemma3_vl_custom_vision_projector_config(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
MegatronMIMOParallelismConfig,
ModuleParallelismConfig,
)
from megatron.bridge.models.megatron_mimo.megatron_mimo_provider import _get_gtp_remat_process_group_kwargs


class FakeStandardProvider:
Expand Down Expand Up @@ -683,6 +684,34 @@ def test_is_pp_last_stage_none_group(self):
class TestProcessGroupCollectionWithEmbeddingGroups:
"""Test that ProcessGroupCollection includes embedding groups."""

@patch(
"megatron.bridge.models.megatron_mimo.megatron_mimo_provider.ProcessGroupCollection.__dataclass_fields__",
{"tp": Mock()},
)
def test_gtp_remat_aliases_omitted_for_older_mcore_contract(self):
"""Test that aliases absent from the MCore contract are not passed to its constructor."""
assert _get_gtp_remat_process_group_kwargs(Mock(), Mock(), Mock()) == {}

@patch(
"megatron.bridge.models.megatron_mimo.megatron_mimo_provider.ProcessGroupCollection.__dataclass_fields__",
{
"dp_cp_gtp_remat": Mock(),
"expt_dp_gtp_remat": Mock(),
"tp_ep_pp_with_egtp_remat": Mock(),
},
)
def test_gtp_remat_aliases_preserved_for_current_mcore_contract(self):
"""Test that current MCore receives all collapsed GTP-remat aliases."""
dp_cp_group = Mock()
expt_dp_group = Mock()
tp_ep_pp_group = Mock()

assert _get_gtp_remat_process_group_kwargs(dp_cp_group, expt_dp_group, tp_ep_pp_group) == {
"dp_cp_gtp_remat": dp_cp_group,
"expt_dp_gtp_remat": expt_dp_group,
"tp_ep_pp_with_egtp_remat": tp_ep_pp_group,
}

@patch("megatron.bridge.models.megatron_mimo.megatron_mimo_provider.is_pp_last_stage")
@patch("megatron.bridge.models.megatron_mimo.megatron_mimo_provider.is_pp_first_stage")
@patch("megatron.bridge.models.megatron_mimo.megatron_mimo_provider.populate_embedding_and_position_groups")
Expand Down Expand Up @@ -835,15 +864,17 @@ def test_pg_collection_preserves_dense_and_expert_view_contracts(
assert pgc.ep == mock_ep
assert pgc.expt_tp == mock_expt_tp
assert pgc.expt_dp == mock_expt_dp
assert pgc.expt_dp_gtp_remat == mock_expt_dp
assert pgc.dp_cp == mock_dp_cp
assert pgc.dp_cp_gtp_remat == mock_dp_cp
assert pgc.intra_dp_cp == mock_dp_cp
if "expt_dp_gtp_remat" in pgc.__dataclass_fields__:
assert pgc.expt_dp_gtp_remat == mock_expt_dp
assert pgc.dp_cp_gtp_remat == mock_dp_cp
assert pgc.tp_cp == mock_tp_cp
assert pgc.tp_dp_cp == mock_tp_dp_cp
assert pgc.mp == mock_mp
assert pgc.tp_ep == mock_tp_ep
assert pgc.tp_ep_pp == mock_tp_ep_pp
assert pgc.tp_ep_pp_with_egtp_remat == mock_tp_ep_pp
if "tp_ep_pp_with_egtp_remat" in pgc.__dataclass_fields__:
assert pgc.tp_ep_pp_with_egtp_remat == mock_tp_ep_pp
assert pgc.intra_expt_dp == mock_expt_dp
assert pgc.intra_dist_opt == mock_intra_dist_opt
2 changes: 1 addition & 1 deletion tests/unit_tests/recipes/test_gemma3_vl_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def _assert_basic_config(cfg):
assert cfg.tokenizer is not None
assert cfg.checkpoint is not None
assert cfg.rng is not None
assert cfg.model.attention_backend is AttnBackend.fused
assert cfg.model.attention_backend is AttnBackend.auto
assert cfg.dataset.enable_in_batch_packing is False

assert cfg.train.global_batch_size >= 1
Expand Down
Loading
Loading