Skip to content

Commit 118ea74

Browse files
committed
fix(engine): patch megatron.core.utils for mbridge on mcore < 0.13 (NPU)
mbridge >= 310e8fb (used on the NPU branch via pyproject.npu.toml) imports ``get_tensor_model_parallel_group_if_none`` from ``megatron.core.utils`` at module load time in its qwen3_vl modules. That symbol does not exist in megatron-core 0.12.1 (the version shipped with MindSpeed on Ascend NPU), so importing mbridge raises ImportError before any AReaL code runs. Mirror the fallback added in ISEEKYAN/mbridge#53 — but inside AReaL, since mbridge is a pinned dependency we don't want to fork. Inject the function into ``megatron.core.utils`` right after the MindSpeed adaptor init and before the first ``import mbridge`` in ``megatron_engine.py``. The shim is a no-op on mcore >= 0.13 (skipped via ``hasattr``) and matches the deprecation-warning behavior of the upstream mcore implementation. Also resolve a latent F821 in the same file: move ``MegatronBridgeLoRA`` into a ``TYPE_CHECKING`` import so the ``bridge_lora: MegatronBridgeLoRA | None`` annotation no longer requires a runtime ``megatron.bridge`` import on NPU (where the package is absent and the import is already deferred to call time).
1 parent 0844e6f commit 118ea74

7 files changed

Lines changed: 162 additions & 0 deletions

File tree

areal/engine/megatron_engine.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ def _ms_sanitized_get_full_args():
4141

4242
_ms_args_basic.get_full_args = _ms_sanitized_get_full_args
4343

44+
# Install mbridge compatibility shims (missing mcore APIs, transformer_engine
45+
# stub) before the first ``import mbridge``. See areal/utils/mbridge_compat.py.
46+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip
47+
4448
import mbridge
4549
import torch
4650
import torch.distributed as dist
@@ -151,6 +155,8 @@ def _ms_sanitized_get_full_args():
151155
from areal.utils.seeding import get_seed
152156

153157
if TYPE_CHECKING:
158+
from megatron.bridge.peft.lora import LoRA as MegatronBridgeLoRA
159+
154160
from areal.api import Scheduler
155161
from areal.api.cli_args import DPOEngineConfig, PPOActorConfig, PPOCriticConfig
156162

areal/models/mcore/bailing_moe_bridge.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"""
99

1010
import torch
11+
12+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip # must precede mbridge import
1113
from mbridge.core import LLMBridge, register_model
1214
from megatron.core.transformer import MLATransformerConfig
1315
from megatron.core.transformer.enums import AttnBackend

areal/models/mcore/hf_load.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import torch
1111
import torch.distributed as dist
12+
13+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip # must precede mbridge import
1214
from mbridge.core.bridge import Bridge
1315
from megatron.core import parallel_state as mpu
1416
from megatron.core.fp8_utils import is_float8tensor

areal/models/mcore/hf_save.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import numpy as np
1111
import torch
1212
import torch.distributed as dist
13+
14+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip # must precede mbridge import
1315
from mbridge.core import Bridge
1416
from mbridge.core.util import unwrap_model
1517
from megatron.core import parallel_state as mpu

areal/models/mcore/registry.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from typing import Any
55

66
import torch
7+
8+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip # must precede mbridge import
79
from mbridge.core.bridge import Bridge
810
from megatron.core import parallel_state as mpu
911
from megatron.core import tensor_parallel

areal/models/tree_attn/module_megatron.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from contextlib import contextmanager
44

55
import torch
6+
7+
import areal.utils.mbridge_compat # noqa: F401, I001 # isort: skip # must precede mbridge import
68
from mbridge.core import LLMBridge
79
from megatron.core.packed_seq_params import PackedSeqParams
810
from megatron.core.transformer import TransformerConfig

areal/utils/mbridge_compat.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
"""mbridge compatibility shims for environments shipping older megatron-core
3+
or missing CUDA-only optional dependencies (e.g. NPU + MindSpeed).
4+
5+
Importing this module installs two shims, both idempotent / no-op when
6+
unnecessary:
7+
8+
1. ``megatron.core.utils.get_tensor_model_parallel_group_if_none`` — added in
9+
mcore 0.13. mbridge >= 310e8fb imports this at module load time
10+
(qwen3_vl). On NPU we ship MindSpeed's mcore 0.12.1 which lacks the
11+
symbol; this shim mirrors the upstream PR
12+
https://github.com/ISEEKYAN/mbridge/pull/53.
13+
14+
2. ``transformer_engine`` (and ``.pytorch`` / ``.common.recipe``) — required
15+
by ``megatron.core.extensions.transformer_engine`` (pulled in
16+
transitively by ``mbridge.models.gemma3``). transformer_engine is
17+
CUDA-only and not available on Ascend NPU. Register inert stub modules
18+
so the unconditional ``import transformer_engine as te`` and
19+
class-statement bases like ``class TELinear(te.pytorch.Linear)`` succeed
20+
at import time. Anything that actually instantiates these stubs at
21+
runtime raises a clear error.
22+
23+
Import this module at the top of any AReaL file that does ``import mbridge``
24+
(or any transitive equivalent) so the shims land before mbridge's
25+
``__init__.py`` cascades.
26+
"""
27+
28+
from __future__ import annotations
29+
30+
import sys
31+
import types
32+
import warnings
33+
34+
35+
def _install_get_tensor_model_parallel_group_if_none() -> None:
36+
import megatron.core.utils as mcore_utils
37+
from megatron.core import parallel_state
38+
39+
if hasattr(mcore_utils, "get_tensor_model_parallel_group_if_none"):
40+
return
41+
42+
import torch
43+
44+
def get_tensor_model_parallel_group_if_none(
45+
tp_group, is_expert: bool = False, check_initialized: bool = True
46+
):
47+
if not torch.distributed.is_initialized():
48+
return None
49+
if tp_group is None:
50+
if torch.distributed.get_rank() == 0:
51+
warnings.warn(
52+
"tp_group is None, using default tp group. "
53+
"Passing tp_group will be mandatory soon",
54+
DeprecationWarning,
55+
stacklevel=2,
56+
)
57+
if is_expert:
58+
tp_group = parallel_state.get_expert_tensor_parallel_group(
59+
check_initialized=check_initialized
60+
)
61+
else:
62+
tp_group = parallel_state.get_tensor_model_parallel_group(
63+
check_initialized=check_initialized
64+
)
65+
return tp_group
66+
67+
mcore_utils.get_tensor_model_parallel_group_if_none = (
68+
get_tensor_model_parallel_group_if_none
69+
)
70+
71+
72+
def _install_transformer_engine_stub() -> None:
73+
"""Register a stub ``transformer_engine`` package if the real one isn't
74+
importable. Only the surface area touched at module-import time is
75+
covered: any attribute resolves to a class that supports subclassing
76+
(``class Foo(te.pytorch.Bar)``) but raises on instantiation.
77+
"""
78+
if "transformer_engine" in sys.modules:
79+
return
80+
try: # real package available — nothing to do.
81+
import transformer_engine # noqa: F401 # type: ignore[import-not-found]
82+
83+
return
84+
except ImportError:
85+
pass
86+
87+
class _StubMeta(type):
88+
"""Metaclass: any attribute lookup on a stub class returns _StubBase.
89+
90+
Lets ``te.pytorch.distributed.CudaRNGStatesTracker`` resolve through
91+
nested attribute chains where ``distributed`` is a class-level stub
92+
rather than a registered submodule.
93+
"""
94+
95+
def __getattr__(cls, name: str):
96+
if name.startswith("_"):
97+
raise AttributeError(name)
98+
return _StubBase
99+
100+
class _StubBase(metaclass=_StubMeta):
101+
"""Inert base class for stubbed TE classes.
102+
103+
Subclassing is allowed (so ``class Foo(te.pytorch.Linear)`` works at
104+
import time); instantiation raises a clear error.
105+
"""
106+
107+
def __init__(self, *_args, **_kwargs):
108+
raise RuntimeError(
109+
"transformer_engine is not available in this environment "
110+
"(CUDA-only). The code path that instantiated this class is "
111+
"unsupported on NPU."
112+
)
113+
114+
class _StubModule(types.ModuleType):
115+
"""ModuleType that returns _StubBase for any unknown attribute."""
116+
117+
def __getattr__(self, name: str):
118+
if name.startswith("_"):
119+
raise AttributeError(name)
120+
return _StubBase
121+
122+
def _register(path: str) -> _StubModule:
123+
mod = _StubModule(path)
124+
sys.modules[path] = mod
125+
return mod
126+
127+
te = _register("transformer_engine")
128+
te.pytorch = _register("transformer_engine.pytorch")
129+
te.common = _register("transformer_engine.common")
130+
te.common.recipe = _register("transformer_engine.common.recipe")
131+
132+
# mcore's get_te_version() reads ``te.__version__`` first, then falls back
133+
# to ``importlib.metadata.version("transformer-engine")``. The fallback
134+
# raises PackageNotFoundError on NPU. Report a very high version so all
135+
# ``is_te_min_version(...)`` checks short-circuit to True; the actual code
136+
# paths gated on it only run if a TE-using model is instantiated, which
137+
# would already raise via ``_StubBase.__init__``.
138+
te.__version__ = "999.0.0"
139+
140+
141+
def apply() -> None:
142+
_install_transformer_engine_stub()
143+
_install_get_tensor_model_parallel_group_if_none()
144+
145+
146+
apply()

0 commit comments

Comments
 (0)