Coverage for transformer_lens/model_bridge/supported_architectures/llada2_moe.py: 95%
46 statements
« prev ^ index » next coverage.py v7.10.1, created at 2026-08-11 18:50 +0000
« prev ^ index » next coverage.py v7.10.1, created at 2026-08-11 18:50 +0000
1"""LLaDA 2.0 MoE architecture adapter.
3Ant Group's LLaDA 2.x (``LLaDA2MoeModelLM``, remote code): masked
4block-diffusion language models on a DeepSeek-V3-style MoE decoder —
5fused ``query_key_value`` attention with full-width query/key layernorms,
6per-expert routed MLPs behind a bias-corrected router plus shared
7experts, and dense MLPs on the first ``first_k_dense_replace`` layers.
9Attention is bidirectional (``is_causal = False``) and generation is
10block-diffusion sampling via the model's own ``generate``, reached through
11``bridge.diffusion_generate``; attention delegates to HF and the bridge's
12autoregressive generation stays off. The fused QKV ships no
13HookedTransformer-format weight conversions, so LN folding is disabled.
15The remote forward validates attention_mask strictly: it must be the 4D
16block-diffusion form (batch, 1, seq, seq) — full-ones for full
17bidirectional visibility — not a 2D padding mask. A forward pre-hook
18drops all-ones 2D masks (informationless) and rejects padded ones with a
19clear error instead of the remote validator's opaque failure.
20"""
22from typing import Any
24from transformer_lens.model_bridge.architecture_adapter import ArchitectureAdapter
25from transformer_lens.model_bridge.generalized_components import (
26 AttentionBridge,
27 BlockBridge,
28 EmbeddingBridge,
29 LinearBridge,
30 MoEBridge,
31 RMSNormalizationBridge,
32 RotaryEmbeddingBridge,
33 UnembeddingBridge,
34)
35from transformer_lens.model_bridge.generalized_components.base import (
36 GeneralizedComponent,
37)
38from transformer_lens.model_bridge.supported_architectures.dream import (
39 _register_default_rope_init,
40)
43class _LLaDA2FusedAttentionBridge(AttentionBridge):
44 """Fused query_key_value projection: no separate q/k/v submodules to
45 alias — expose the fused output instead."""
47 hook_aliases = {
48 "hook_qkv": "qkv.hook_out",
49 "hook_z": "o.hook_in",
50 }
53class LLaDA2MoeArchitectureAdapter(ArchitectureAdapter):
54 """Architecture adapter for LLaDA2MoeModelLM models."""
56 applicable_phases: list[int] = [1, 2, 3, 4]
57 supports_generation: bool = False
58 # Semi-autoregressive block remasking, shipped on the model class.
59 native_sampler: str = "generate"
60 # Fused query_key_value with no per-projection conversions to fold into.
61 supports_fold_ln = False
63 def native_sampler_kwargs(self, max_new_tokens: int, prompt_len: int) -> dict:
64 """gen_length must cover whole blocks; block_length caps at the budget."""
65 block_length = min(32, max_new_tokens)
66 blocks = max(1, -(-max_new_tokens // block_length))
67 return {
68 "gen_length": blocks * block_length,
69 "block_length": block_length,
70 "steps": max_new_tokens,
71 }
73 def __init__(self, cfg: Any) -> None:
74 """Initialize the LLaDA 2.0 MoE architecture adapter."""
75 super().__init__(cfg)
77 self._set_rms_rotary_defaults()
79 self.weight_processing_conversions = {}
81 self.component_mapping = {
82 "embed": EmbeddingBridge(name="model.word_embeddings"),
83 "rotary_emb": RotaryEmbeddingBridge(name="model.rotary_emb"),
84 "blocks": BlockBridge(
85 name="model.layers",
86 config=self.cfg,
87 submodules={
88 "ln1": RMSNormalizationBridge(name="input_layernorm", config=self.cfg),
89 "ln2": RMSNormalizationBridge(name="post_attention_layernorm", config=self.cfg),
90 # Bidirectional diffusion attention with fused QKV; the
91 # bridge reimplementation assumes causal masking, so
92 # delegate to HF. The fused projection and dense output
93 # are hookable; q/k layernorms are full-width.
94 "attn": _LLaDA2FusedAttentionBridge(
95 name="attention",
96 config=self.cfg,
97 submodules={
98 "qkv": LinearBridge(name="query_key_value"),
99 "o": LinearBridge(name="dense"),
100 "q_norm": RMSNormalizationBridge(
101 name="query_layernorm", config=self.cfg
102 ),
103 "k_norm": RMSNormalizationBridge(name="key_layernorm", config=self.cfg),
104 },
105 maintain_native_attention=True,
106 ),
107 # Dense on the first first_k_dense_replace layers, routed
108 # MoE elsewhere — gate and shared_experts are optional so
109 # setup skips them on dense layers (deepseek_v3 pattern).
110 "mlp": MoEBridge(
111 name="mlp",
112 config=self.cfg,
113 submodules={
114 "gate": GeneralizedComponent(name="gate", optional=True),
115 "shared_experts": self._gated_mlp(name="shared_experts", optional=True),
116 # Dense-layer projections (absent on MoE layers).
117 "dense_gate": LinearBridge(name="gate_proj", optional=True),
118 "dense_in": LinearBridge(name="up_proj", optional=True),
119 "dense_out": LinearBridge(name="down_proj", optional=True),
120 },
121 ),
122 },
123 ),
124 "ln_final": RMSNormalizationBridge(name="model.norm", config=self.cfg),
125 "unembed": UnembeddingBridge(name="lm_head"),
126 }
128 def prepare_loading(self, model_name: str, model_kwargs: dict) -> None:
129 """Restore the v4 'default' rope init the remote code looks up (Dream shim)."""
130 _register_default_rope_init()
131 super().prepare_loading(model_name, model_kwargs)
133 def setup_hook_compatibility(self, bridge: Any) -> None:
134 """Guard the remote forward against auto-passed 2D padding masks."""
135 model = getattr(bridge, "original_model", None)
136 if model is None or getattr(model, "_llada2_mask_guard", False): 136 ↛ 137line 136 didn't jump to line 137 because the condition on line 136 was never true
137 return
139 def _mask_guard(module: Any, args: Any, kwargs: Any) -> Any:
140 import torch
142 mask = kwargs.get("attention_mask")
143 if mask is None:
144 # Remote forward calls attention_mask.size() unconditionally;
145 # synthesize the full-visibility 4D mask from the input shape.
146 ids = kwargs.get("input_ids", args[0] if args else None)
147 if isinstance(ids, torch.Tensor) and ids.ndim == 2: 147 ↛ 150line 147 didn't jump to line 150 because the condition on line 147 was always true
148 batch, seq = ids.shape
149 kwargs["attention_mask"] = torch.ones(batch, 1, seq, seq, device=ids.device)
150 return args, kwargs
151 if isinstance(mask, torch.Tensor) and mask.ndim == 2:
152 if not bool(mask.all()):
153 raise NotImplementedError(
154 "LLaDA2's remote forward rejects 2D padding masks; "
155 "batched padded inputs are unsupported — pass "
156 "equal-length sequences."
157 )
158 batch, seq = mask.shape
159 kwargs["attention_mask"] = torch.ones(batch, 1, seq, seq, device=mask.device)
160 return args, kwargs
162 model.register_forward_pre_hook(_mask_guard, with_kwargs=True)
163 model._llada2_mask_guard = True
165 def setup_component_testing(self, hf_model: Any, bridge_model: Any = None) -> None:
166 """Delegated attention computes rotary inside HF; nothing to wire."""