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