flashrt-structures 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. flashrt_structures/__init__.py +174 -0
  2. flashrt_structures/adapters/__init__.py +55 -0
  3. flashrt_structures/adapters/diffusers_attention.py +237 -0
  4. flashrt_structures/adapters/diffusers_rotary_attention.py +239 -0
  5. flashrt_structures/adapters/factored_qk_norm_rope.py +252 -0
  6. flashrt_structures/adapters/factored_two_way_attention.py +99 -0
  7. flashrt_structures/adapters/gemma_attention.py +226 -0
  8. flashrt_structures/adapters/packed_qkv_rope.py +342 -0
  9. flashrt_structures/adapters/packed_stream_qk_norm_rope.py +376 -0
  10. flashrt_structures/adapters/qwen_per_head_qk_norm_rope.py +231 -0
  11. flashrt_structures/adapters/sglang_engine.py +207 -0
  12. flashrt_structures/adapters/transformers_attention_interface.py +73 -0
  13. flashrt_structures/adapters/transformers_gated_delta.py +152 -0
  14. flashrt_structures/adapters/transformers_gated_delta_fused.py +96 -0
  15. flashrt_structures/adapters/vllm_engine.py +424 -0
  16. flashrt_structures/adjudicate.py +85 -0
  17. flashrt_structures/aot.py +191 -0
  18. flashrt_structures/autobuild.py +2052 -0
  19. flashrt_structures/beta/__init__.py +43 -0
  20. flashrt_structures/beta/conform.py +94 -0
  21. flashrt_structures/beta/joins.py +113 -0
  22. flashrt_structures/beta/negotiate.py +84 -0
  23. flashrt_structures/beta/ports.py +140 -0
  24. flashrt_structures/decisions.py +80 -0
  25. flashrt_structures/discover.py +623 -0
  26. flashrt_structures/explain.py +70 -0
  27. flashrt_structures/frontdoor.py +572 -0
  28. flashrt_structures/gates.py +465 -0
  29. flashrt_structures/guard.py +421 -0
  30. flashrt_structures/handle.py +189 -0
  31. flashrt_structures/impls/__init__.py +219 -0
  32. flashrt_structures/impls/adaln_producer/__init__.py +8 -0
  33. flashrt_structures/impls/adaln_producer/broker.py +116 -0
  34. flashrt_structures/impls/adaln_producer/fused.py +388 -0
  35. flashrt_structures/impls/adarms_stack/__init__.py +8 -0
  36. flashrt_structures/impls/adarms_stack/fp8_chain.py +832 -0
  37. flashrt_structures/impls/adarms_stack/region.py +102 -0
  38. flashrt_structures/impls/attention_core/__init__.py +132 -0
  39. flashrt_structures/impls/attention_core/fa2_seqused.py +458 -0
  40. flashrt_structures/impls/attention_core/fa4_cute.py +154 -0
  41. flashrt_structures/impls/attention_core/fa4_fp8.py +178 -0
  42. flashrt_structures/impls/attention_core/masked_mha.py +158 -0
  43. flashrt_structures/impls/attention_core/two_way_fa2.py +220 -0
  44. flashrt_structures/impls/cadence_static/__init__.py +18 -0
  45. flashrt_structures/impls/cadence_static/buffers.py +122 -0
  46. flashrt_structures/impls/cadence_static/cross_attention.py +187 -0
  47. flashrt_structures/impls/chain_elements.py +89 -0
  48. flashrt_structures/impls/decode_loop/__init__.py +0 -0
  49. flashrt_structures/impls/decode_loop/fp8_kv.py +206 -0
  50. flashrt_structures/impls/decode_loop/mtp_speculative.py +245 -0
  51. flashrt_structures/impls/decode_loop/whole_step.py +852 -0
  52. flashrt_structures/impls/decoder_block/__init__.py +6 -0
  53. flashrt_structures/impls/decoder_block/attn_sublayer.py +110 -0
  54. flashrt_structures/impls/decoder_block/fused.py +167 -0
  55. flashrt_structures/impls/decoder_ffn/__init__.py +0 -0
  56. flashrt_structures/impls/decoder_ffn/fp8_static.py +310 -0
  57. flashrt_structures/impls/decoder_ffn/fp8_static.yaml +22 -0
  58. flashrt_structures/impls/decoder_ffn/w4a16_static.py +221 -0
  59. flashrt_structures/impls/decoder_ffn/w8a16_static.py +183 -0
  60. flashrt_structures/impls/dit_stack/__init__.py +11 -0
  61. flashrt_structures/impls/dit_stack/fp4_chain.py +417 -0
  62. flashrt_structures/impls/dit_stack/region.py +86 -0
  63. flashrt_structures/impls/fixed_iter/__init__.py +29 -0
  64. flashrt_structures/impls/fixed_iter/openpi.py +264 -0
  65. flashrt_structures/impls/fixed_iter/protocol.py +94 -0
  66. flashrt_structures/impls/gated_delta_core/__init__.py +3 -0
  67. flashrt_structures/impls/gated_delta_core/fused_layer.py +545 -0
  68. flashrt_structures/impls/gated_delta_core/hub_v3.py +152 -0
  69. flashrt_structures/impls/graph_lowering/__init__.py +27 -0
  70. flashrt_structures/impls/graph_lowering/pi052_denoise.py +179 -0
  71. flashrt_structures/impls/graph_lowering/protocol.py +76 -0
  72. flashrt_structures/impls/graph_lowering/qwen3_vl.py +364 -0
  73. flashrt_structures/impls/linear_proj/__init__.py +0 -0
  74. flashrt_structures/impls/linear_proj/fp8_static.py +270 -0
  75. flashrt_structures/impls/linear_proj/nvfp4_balance.py +131 -0
  76. flashrt_structures/impls/linear_proj/nvfp4_dynamic.py +182 -0
  77. flashrt_structures/impls/linear_proj/w8a16_static.py +230 -0
  78. flashrt_structures/impls/modnorm_qkv_chain/__init__.py +0 -0
  79. flashrt_structures/impls/modnorm_qkv_chain/fp8_ptok_table.py +291 -0
  80. flashrt_structures/impls/moe_experts/__init__.py +9 -0
  81. flashrt_structures/impls/moe_experts/nvfp4_dynamic.py +208 -0
  82. flashrt_structures/impls/moe_experts/nvfp4_w4a16.py +129 -0
  83. flashrt_structures/impls/norm_fused/__init__.py +3 -0
  84. flashrt_structures/impls/norm_fused/bf16.py +94 -0
  85. flashrt_structures/impls/norm_fused/fp8_producer.py +84 -0
  86. flashrt_structures/impls/patch_projection/__init__.py +3 -0
  87. flashrt_structures/impls/patch_projection/bf16_flat.py +139 -0
  88. flashrt_structures/impls/prefill_tower/__init__.py +10 -0
  89. flashrt_structures/impls/prefill_tower/fp8_chain.py +955 -0
  90. flashrt_structures/impls/prefill_tower/region.py +99 -0
  91. flashrt_structures/impls/qk_norm_rope/__init__.py +12 -0
  92. flashrt_structures/impls/qk_norm_rope/per_head_gqa.py +199 -0
  93. flashrt_structures/impls/qk_norm_rope/projection_bf16.py +165 -0
  94. flashrt_structures/impls/qkv_pack/__init__.py +5 -0
  95. flashrt_structures/impls/qkv_pack/bf16.py +110 -0
  96. flashrt_structures/impls/qkv_pack/fp8_static.py +435 -0
  97. flashrt_structures/impls/qkv_pack/nvfp4_balance.py +218 -0
  98. flashrt_structures/impls/qkv_rope/__init__.py +3 -0
  99. flashrt_structures/impls/qkv_rope/packed_bias_bf16.py +143 -0
  100. flashrt_structures/impls/step_table.py +113 -0
  101. flashrt_structures/impls/vision_ffn/__init__.py +0 -0
  102. flashrt_structures/impls/vision_ffn/fp8_static.py +261 -0
  103. flashrt_structures/impls/vision_ffn/nvfp4_balance.py +211 -0
  104. flashrt_structures/impls/vision_tower/__init__.py +7 -0
  105. flashrt_structures/impls/vision_tower/fp8_chain.py +533 -0
  106. flashrt_structures/impls/vision_tower/region.py +91 -0
  107. flashrt_structures/matrix.py +126 -0
  108. flashrt_structures/points.py +368 -0
  109. flashrt_structures/prequantized.py +131 -0
  110. flashrt_structures/quantize_on_adopt.py +94 -0
  111. flashrt_structures/recipe.py +438 -0
  112. flashrt_structures/regions.py +208 -0
  113. flashrt_structures/schemes.py +490 -0
  114. flashrt_structures/stages.py +298 -0
  115. flashrt_structures/storage.py +255 -0
  116. flashrt_structures/swap.py +398 -0
  117. flashrt_structures/workspace.py +110 -0
  118. flashrt_structures-0.2.0.dist-info/METADATA +140 -0
  119. flashrt_structures-0.2.0.dist-info/RECORD +122 -0
  120. flashrt_structures-0.2.0.dist-info/WHEEL +5 -0
  121. flashrt_structures-0.2.0.dist-info/licenses/LICENSE +202 -0
  122. flashrt_structures-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,376 @@
1
+ """Q/K norm + RoPE adapter for single-stream packed attention hosts.
2
+
3
+ The host capability is one sibling-QKV projection group consumed by
4
+ per-head RMSNorm and a rotate-half rotary table before a dispatched
5
+ self-attention — the plain diffusers processor form, one stream, no
6
+ cross attention, no cache. The factored two-way adapter requires two
7
+ groups; hosts with a single group fell through it entirely, leaving
8
+ their norm/rope chains eager at every layer.
9
+
10
+ Partial rotary is absorbed at assembly time. The per-head kernel
11
+ rotates all 128 channels with a half-split at 64; a host that rotates
12
+ only the leading ``R`` channels (half-split ``R/2``) is served by a
13
+ channel permutation: the two rotary halves move to kernel slots
14
+ ``[0:R/2)`` and ``[64:64+R/2)`` — the kernel's pairing then *is* the
15
+ host's pairing — and the pass-through channels land in slots whose
16
+ tables read ``cos=1, sin=0``. The permutation is one row gather of the
17
+ pack's FP8 weight rows (bit-exact, no requantize), the same gather on
18
+ the norm weights, and a table remap per call. Q and K share the
19
+ permutation, so QK^T is unchanged and nothing downstream un-permutes.
20
+
21
+ The permutation needs the rotary width, which is a runtime fact — so
22
+ it applies lazily on the first routed call (eager warmup), and the
23
+ revert path restores the pack rows with the inverse gather.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import sys
29
+ import types
30
+
31
+ import torch
32
+
33
+ from ..guard import GuardRefused
34
+ from ..impls.qk_norm_rope import bind_per_head_gqa_qk_norm_rope
35
+ from ..impls.qkv_pack.fp8_static import PackedLinear, StashReader
36
+
37
+
38
+ def _pack_at(plan, path: str):
39
+ head = plan.swaps.get(f"{path}.to_q")
40
+ key = plan.swaps.get(f"{path}.to_k")
41
+ value = plan.swaps.get(f"{path}.to_v")
42
+ if not (
43
+ isinstance(head, PackedLinear)
44
+ and isinstance(key, StashReader)
45
+ and isinstance(value, StashReader)
46
+ and key._packed[0] is head
47
+ and value._packed[0] is head
48
+ ):
49
+ return None
50
+ return head
51
+
52
+
53
+ def _epsilon(norm) -> float | None:
54
+ value = getattr(norm, "variance_epsilon", getattr(norm, "eps", None))
55
+ return None if value is None else float(value)
56
+
57
+
58
+ class PackedStreamQkNormRopeAdapter:
59
+ """Compose one packed QKV group with per-head Q/K norm and RoPE."""
60
+
61
+ __name__ = "packed_stream_qk_norm_rope"
62
+
63
+ SMOKE_FLOOR = 0.995
64
+
65
+ def __call__(self, model, plan, probe=None):
66
+ routes = []
67
+ observed = {}
68
+ refused = []
69
+
70
+ for path, module in model.named_modules():
71
+ pack = _pack_at(plan, path)
72
+ if pack is None:
73
+ continue
74
+ if hasattr(module, "add_q_proj"):
75
+ continue # the factored two-way adapter's territory
76
+ site = f"{path}::packed_stream_qk_norm_rope"
77
+
78
+ def refuse(reason: str) -> None:
79
+ refused.append((site, f"qk_norm_rope refused: {reason}"))
80
+
81
+ if module.training:
82
+ refuse("training/dropout form is outside the inference seam")
83
+ continue
84
+ heads = getattr(module, "heads",
85
+ getattr(module, "num_attention_heads", None))
86
+ head_dim = getattr(module, "head_dim", None)
87
+ processor = getattr(module, "processor", None)
88
+ to_out = getattr(module, "to_out", None)
89
+ if (heads is None or head_dim is None or processor is None
90
+ or to_out is None):
91
+ refuse("host lacks the single-stream attention slots")
92
+ continue
93
+ if int(head_dim) != 128:
94
+ refuse("current Hub entry requires head_dim=128")
95
+ continue
96
+ heads = int(heads)
97
+ q_norm = getattr(module, "norm_q", None)
98
+ k_norm = getattr(module, "norm_k", None)
99
+ q_w = getattr(q_norm, "weight", None)
100
+ k_w = getattr(k_norm, "weight", None)
101
+ eps = _epsilon(q_norm)
102
+ if q_w is None or k_w is None or eps is None:
103
+ refuse("Q/K norm weights or epsilon are absent")
104
+ continue
105
+ if tuple(q_w.shape) != (128,) or tuple(k_w.shape) != (128,):
106
+ refuse("per-head norm weights must have shape (head_dim,)")
107
+ continue
108
+ expected = (heads * 128, heads * 128, heads * 128)
109
+ if tuple(pack.splits[:3]) != expected:
110
+ refuse(f"packed widths {tuple(pack.splits[:3])} "
111
+ f"!= {expected}")
112
+ continue
113
+ dispatch = getattr(
114
+ sys.modules.get(type(processor).__module__),
115
+ "dispatch_attention_fn", None)
116
+ if dispatch is None:
117
+ refuse("host processor module lacks dispatch_attention_fn")
118
+ continue
119
+
120
+ try:
121
+ bound = bind_per_head_gqa_qk_norm_rope(
122
+ q_w, k_w, row_capacity=pack.rows, q_heads=heads,
123
+ kv_heads=heads, head_dim=128, eps=eps,
124
+ workspace_lane="stream")
125
+ except (ValueError, RuntimeError) as exc:
126
+ refuse(str(exc))
127
+ continue
128
+
129
+ original = module.forward
130
+ had_instance_forward = "forward" in module.__dict__
131
+ state = {"perm": None, "inv": None, "r": None}
132
+
133
+ def lazy_permute(rotary_dim: int, _pack=pack, _bound=bound,
134
+ _state=state, _heads=heads, _qw=q_w,
135
+ _kw=k_w, _site=site):
136
+ if (torch.cuda.is_available()
137
+ and torch.cuda.is_current_stream_capturing()):
138
+ raise GuardRefused(
139
+ f"qk_norm_rope[{_site}]: the rotary permutation "
140
+ "warms up on the first eager call — run one eager "
141
+ "forward before capturing")
142
+ if rotary_dim % 2 or rotary_dim > 128:
143
+ raise GuardRefused(
144
+ "qk_norm_rope: rotary width must be even and "
145
+ "<= head_dim")
146
+ half = rotary_dim // 2
147
+ if half > 64:
148
+ raise GuardRefused(
149
+ "qk_norm_rope: rotary half exceeds the kernel's "
150
+ "pairing distance")
151
+ perm = torch.empty(128, dtype=torch.long)
152
+ free = list(range(rotary_dim, 128))
153
+ # kernel slot <- host channel
154
+ slot_src = {}
155
+ for i in range(half):
156
+ slot_src[i] = i
157
+ slot_src[64 + i] = half + i
158
+ spare = [s for s in range(128) if s not in slot_src]
159
+ for s, c in zip(spare, free):
160
+ slot_src[s] = c
161
+ for s in range(128):
162
+ perm[s] = slot_src[s]
163
+ inv = torch.empty_like(perm)
164
+ inv[perm] = torch.arange(128)
165
+ _state.update(perm=perm, inv=inv, r=rotary_dim)
166
+ if rotary_dim == 128:
167
+ return # identity pairing, nothing to move
168
+ dev = _pack.w8.device
169
+ pdev = perm.to(dev)
170
+ with torch.no_grad():
171
+ for g in range(2): # q rows, k rows
172
+ base = g * _heads * 128
173
+ for h in range(_heads):
174
+ rows = slice(base + h * 128,
175
+ base + (h + 1) * 128)
176
+ _pack.w8[rows] = _pack.w8[rows][pdev].clone()
177
+ _bound.q_norm_weight.copy_(
178
+ _qw.detach().to(dev, torch.bfloat16)[pdev])
179
+ _bound.k_norm_weight.copy_(
180
+ _kw.detach().to(dev, torch.bfloat16)[pdev])
181
+
182
+ def remap_tables(cos, sin, _state=state, _site=site,
183
+ _lazy_permute=lazy_permute):
184
+ r = cos.shape[-1]
185
+ if _state["r"] is None:
186
+ # early-bound above: a loop-scope free variable here
187
+ # resolves to the LAST route's function — the first
188
+ # route's call then permutes a stranger's weights
189
+ # and never its own (proven: attn0's first call
190
+ # permuted attn1's pack)
191
+ _lazy_permute(r)
192
+ elif _state["r"] != r:
193
+ raise GuardRefused(
194
+ f"qk_norm_rope[{_site}]: rotary width changed "
195
+ "after binding")
196
+ if r == 128:
197
+ return cos, sin
198
+ half = r // 2
199
+ c = cos.new_ones(*cos.shape[:-1], 128)
200
+ s_ = sin.new_zeros(*sin.shape[:-1], 128)
201
+ c[..., :half] = cos[..., :half]
202
+ c[..., 64:64 + half] = cos[..., half:r]
203
+ s_[..., :half] = sin[..., :half]
204
+ s_[..., 64:64 + half] = sin[..., half:r]
205
+ return c, s_
206
+
207
+ def routed(self, hidden_states, rotary_emb=None,
208
+ attention_mask=None, *, _pack=pack, _bound=bound,
209
+ _remap=remap_tables, _dispatch=dispatch,
210
+ _proc=processor, _state=state, _heads=heads):
211
+ if rotary_emb is None:
212
+ # a rotary-less caller (the same attention class in
213
+ # a refiner role): same joint read, the host's own
214
+ # per-head norms, no rope, no kernel. Refused only
215
+ # when this site already permuted for a rotary form
216
+ # — the two forms cannot share one weight layout.
217
+ if _state["r"] is not None:
218
+ raise GuardRefused(
219
+ "qk_norm_rope: this site was bound for "
220
+ "rotary calls and now received none")
221
+ flat = _pack.joint(hidden_states)
222
+ lead = hidden_states.shape[:-1]
223
+ d = _heads * 128
224
+ q_, k_, v_ = flat.split([d, d, d], dim=-1)
225
+ q_ = self.norm_q(q_.unflatten(-1, (_heads, 128))[None])
226
+ k_ = self.norm_k(k_.unflatten(-1, (_heads, 128))[None])
227
+ v_ = v_.unflatten(-1, (_heads, 128))[None]
228
+ out = _dispatch(
229
+ q_, k_, v_, attn_mask=attention_mask,
230
+ dropout_p=0.0, is_causal=False,
231
+ backend=getattr(_proc, "_attention_backend", None),
232
+ parallel_config=getattr(_proc, "_parallel_config",
233
+ None))
234
+ out = out.flatten(2, 3).type_as(q_)
235
+ out = out.reshape(*lead, out.shape[-1])
236
+ for layer in self.to_out:
237
+ out = layer(out)
238
+ return out
239
+ cos, sin = rotary_emb
240
+ cos, sin = _remap(cos.to(torch.bfloat16),
241
+ sin.to(torch.bfloat16))
242
+ flat = _pack.joint(hidden_states)
243
+ lead = hidden_states.shape[:-1]
244
+ packed = flat.reshape(1, -1, flat.shape[-1])
245
+ if cos.dim() == 2:
246
+ cos = cos.unsqueeze(0)
247
+ sin = sin.unsqueeze(0)
248
+ q, k, v = _bound(packed.contiguous(), cos.contiguous(),
249
+ sin.contiguous())
250
+ out = _dispatch(
251
+ q, k, v, attn_mask=attention_mask, dropout_p=0.0,
252
+ is_causal=False,
253
+ backend=getattr(_proc, "_attention_backend", None),
254
+ parallel_config=getattr(_proc, "_parallel_config",
255
+ None))
256
+ out = out.flatten(2, 3).type_as(q)
257
+ out = out.reshape(*lead, out.shape[-1])
258
+ for layer in self.to_out:
259
+ out = layer(out)
260
+ return out
261
+
262
+ routes.append((module, pack, state,
263
+ types.MethodType(routed, module), original,
264
+ had_instance_forward))
265
+ observed[f"{path}::per_head_qk_norm_rope"] = bound
266
+
267
+ if probe is not None and routes:
268
+ # the routed form consumes jointly: open the joint reads for
269
+ # the audition, close them again for whoever is not kept
270
+ for _m, _p, _s2, _f, _o, _h in routes:
271
+ _p.enable_joint(3)
272
+ verdicts: dict[int, tuple] = {}
273
+ hooks = []
274
+ for idx, (module, _pack_m, _st, routed_fn, _orig, _hd) in \
275
+ enumerate(routes):
276
+ def check(mod, args, kwargs, output, _i=idx,
277
+ _fn=routed_fn):
278
+ if _i in verdicts:
279
+ return None
280
+ try:
281
+ got = _fn(*args, **kwargs)
282
+ ref = output.float().flatten()
283
+ cos = torch.nn.functional.cosine_similarity(
284
+ got.float().flatten(), ref, dim=0)
285
+ verdicts[_i] = (float(cos), None)
286
+ except Exception as exc: # noqa: BLE001 — verdict
287
+ verdicts[_i] = (None, f"{type(exc).__name__}: "
288
+ f"{exc}")
289
+ return None
290
+ hooks.append(module.register_forward_hook(
291
+ check, with_kwargs=True))
292
+ try:
293
+ with torch.inference_mode():
294
+ probe()
295
+ finally:
296
+ for h in hooks:
297
+ h.remove()
298
+ kept = []
299
+ for idx, route in enumerate(routes):
300
+ module, _p, st, _fn, _o, _h = route
301
+ path = next(p for p, m in model.named_modules()
302
+ if m is module)
303
+ cos, err = verdicts.get(idx, (None, "never called by "
304
+ "the probe forward"))
305
+ if err is not None:
306
+ refused.append((f"{path}::packed_stream_qk_norm_rope",
307
+ f"qk_norm_rope smoke failed: {err}"))
308
+ continue
309
+ if cos < self.SMOKE_FLOOR:
310
+ refused.append((f"{path}::packed_stream_qk_norm_rope",
311
+ f"qk_norm_rope smoke cos {cos:.6f} "
312
+ f"< {self.SMOKE_FLOOR} on the "
313
+ "probe input"))
314
+ continue
315
+ for key, b in observed.items():
316
+ if key.startswith(path + "::"):
317
+ b._frt_guard.notes["smoke_cos"] = round(cos, 6)
318
+ b._frt_guard.notes["rotary_r"] = st.get("r")
319
+ kept.append(route)
320
+ for route in routes:
321
+ if route not in kept:
322
+ route[1].disable_joint()
323
+ # the failed audition bumped the stash epoch with a
324
+ # joint (stash-skipping) run; clear it so the host
325
+ # form's next sibling read is not falsely refused
326
+ route[1]._stash_epoch = 0
327
+ dropped = {id(r[0]) for r in routes} - {id(r[0])
328
+ for r in kept}
329
+ if dropped:
330
+ observed = {k: v for k, v in observed.items()
331
+ if not any(k.startswith(p + "::")
332
+ for p, m in model.named_modules()
333
+ if id(m) in dropped)}
334
+ routes = kept
335
+
336
+ if not routes:
337
+ return {"refused": refused} if refused else None
338
+
339
+ def enable() -> None:
340
+ for module, pack, _state, routed, _, _ in routes:
341
+ pack.enable_joint(3)
342
+ module.forward = routed
343
+
344
+ def disable() -> None:
345
+ for module, pack, _state, _, original, _ in routes:
346
+ module.forward = original
347
+ pack.disable_joint()
348
+
349
+ def revert() -> None:
350
+ for module, pack, state, _, original, had in routes:
351
+ pack.disable_joint()
352
+ inv = state.get("inv")
353
+ if inv is not None and state.get("r") != 128 \
354
+ and state.get("r") is not None:
355
+ dev = pack.w8.device
356
+ idev = inv.to(dev)
357
+ heads = pack.splits[0] // 128
358
+ with torch.no_grad():
359
+ for g in range(2):
360
+ base = g * heads * 128
361
+ for h in range(heads):
362
+ rows = slice(base + h * 128,
363
+ base + (h + 1) * 128)
364
+ pack.w8[rows] = pack.w8[rows][idev].clone()
365
+ if had:
366
+ module.forward = original
367
+ elif "forward" in module.__dict__:
368
+ del module.forward
369
+
370
+ enable()
371
+ return {
372
+ "observed": observed,
373
+ "revert": [revert],
374
+ "toggle": (enable, disable),
375
+ "refused": refused,
376
+ }
@@ -0,0 +1,231 @@
1
+ """Compose packed QKV with per-head Q/K norm + RoPE in Qwen-style hosts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import types
7
+
8
+ from ..impls.qk_norm_rope import bind_per_head_gqa_qk_norm_rope
9
+ from ..impls.qkv_pack.fp8_static import PackedLinear, StashReader
10
+
11
+
12
+ class PerHeadGqaQkNormRopeAdapter:
13
+ """Route capability-compatible per-head GQA attention through one seam."""
14
+
15
+ __name__ = "per_head_gqa_qk_norm_rope"
16
+
17
+ def __call__(self, model, plan):
18
+ modules = dict(model.named_modules())
19
+ routes = []
20
+ observed = {}
21
+ refused = []
22
+
23
+ for path, module in modules.items():
24
+ pack = plan.swaps.get(f"{path}.q_proj")
25
+ k_reader = plan.swaps.get(f"{path}.k_proj")
26
+ v_reader = plan.swaps.get(f"{path}.v_proj")
27
+ if not (
28
+ isinstance(pack, PackedLinear)
29
+ and isinstance(k_reader, StashReader)
30
+ and isinstance(v_reader, StashReader)
31
+ and k_reader._packed[0] is pack
32
+ and v_reader._packed[0] is pack
33
+ ):
34
+ continue
35
+ site = f"{path}::per_head_qk_norm_rope"
36
+
37
+ def refuse(reason):
38
+ refused.append((site, f"qk_norm_rope refused: {reason}"))
39
+
40
+ if not all(
41
+ hasattr(module, attr)
42
+ for attr in (
43
+ "q_norm",
44
+ "k_norm",
45
+ "head_dim",
46
+ "num_key_value_groups",
47
+ "config",
48
+ "o_proj",
49
+ "scaling",
50
+ )
51
+ ):
52
+ refuse("host lacks the complete per-head GQA attention slots")
53
+ continue
54
+ if module.training:
55
+ refuse("training/dropout form is outside the inference seam")
56
+ continue
57
+ if int(module.head_dim) != 128 or len(pack.splits) < 3:
58
+ refuse("kernel requires head_dim=128 and three packed slots")
59
+ continue
60
+ q_heads, q_rem = divmod(int(pack.splits[0]), 128)
61
+ kv_heads, k_rem = divmod(int(pack.splits[1]), 128)
62
+ if (
63
+ q_rem
64
+ or k_rem
65
+ or int(pack.splits[2]) != int(pack.splits[1])
66
+ or q_heads != kv_heads * int(module.num_key_value_groups)
67
+ ):
68
+ refuse("packed Q/K/V widths do not form the declared GQA")
69
+ continue
70
+ q_weight = getattr(module.q_norm, "weight", None)
71
+ k_weight = getattr(module.k_norm, "weight", None)
72
+ if q_weight is None or k_weight is None:
73
+ refuse("Q/K norm weights are absent")
74
+ continue
75
+ eps = getattr(
76
+ module.q_norm,
77
+ "variance_epsilon",
78
+ getattr(module.q_norm, "eps", None),
79
+ )
80
+ if eps is None:
81
+ refuse("Q/K norm epsilon is absent")
82
+ continue
83
+
84
+ try:
85
+ source = importlib.import_module(type(module).__module__)
86
+ except (ImportError, ValueError) as exc:
87
+ refuse(f"cannot resolve host attention functions: {exc}")
88
+ continue
89
+ eager_attention = getattr(source, "eager_attention_forward", None)
90
+ attention_functions = getattr(source, "ALL_ATTENTION_FUNCTIONS", None)
91
+ if eager_attention is None or attention_functions is None:
92
+ refuse("host module does not expose its attention dispatcher")
93
+ continue
94
+ implementation = getattr(
95
+ getattr(module, "config", None),
96
+ "_attn_implementation",
97
+ None,
98
+ )
99
+ if implementation == "eager":
100
+ attention = eager_attention
101
+ else:
102
+ try:
103
+ attention = attention_functions[implementation]
104
+ except (KeyError, TypeError):
105
+ refuse(
106
+ f"attention implementation {implementation!r} is "
107
+ "not available at bind time")
108
+ continue
109
+ sliding_window = getattr(module, "sliding_window", None)
110
+ scaling = module.scaling
111
+ layer_index = getattr(module, "layer_idx", None)
112
+
113
+ try:
114
+ impl = bind_per_head_gqa_qk_norm_rope(
115
+ q_weight,
116
+ k_weight,
117
+ row_capacity=pack.rows,
118
+ q_heads=q_heads,
119
+ kv_heads=kv_heads,
120
+ head_dim=128,
121
+ eps=float(eps),
122
+ )
123
+ except (ValueError, RuntimeError) as exc:
124
+ refuse(str(exc))
125
+ continue
126
+ original = module.forward
127
+ had_instance_forward = "forward" in module.__dict__
128
+
129
+ def routed(
130
+ self,
131
+ hidden_states,
132
+ position_embeddings,
133
+ attention_mask,
134
+ past_key_values=None,
135
+ cache_position=None,
136
+ *,
137
+ bound=impl,
138
+ packed=pack,
139
+ attention_fn=attention,
140
+ attention_scale=scaling,
141
+ cache_layer=layer_index,
142
+ window=sliding_window,
143
+ **kwargs,
144
+ ):
145
+ batch, tokens, _ = hidden_states.shape
146
+ packed_qkv = packed.joint(hidden_states).view(
147
+ batch, tokens, -1
148
+ )
149
+ cos, sin = position_embeddings
150
+ query, key, value = bound(packed_qkv, cos, sin)
151
+ query = query.transpose(1, 2)
152
+ key = key.transpose(1, 2)
153
+ value = value.transpose(1, 2)
154
+ if past_key_values is not None:
155
+ cache_kwargs = {
156
+ "sin": sin,
157
+ "cos": cos,
158
+ "cache_position": cache_position,
159
+ }
160
+ key, value = past_key_values.update(
161
+ key,
162
+ value,
163
+ cache_layer,
164
+ cache_kwargs,
165
+ )
166
+
167
+ attention_kwargs = dict(
168
+ dropout=0.0,
169
+ scaling=attention_scale,
170
+ **kwargs,
171
+ )
172
+ if window is not None:
173
+ attention_kwargs["sliding_window"] = window
174
+ output, weights = attention_fn(
175
+ self,
176
+ query,
177
+ key,
178
+ value,
179
+ attention_mask,
180
+ **attention_kwargs,
181
+ )
182
+ output = output.reshape(batch, tokens, -1).contiguous()
183
+ # resolve through the module: attach may have seated a
184
+ # structure at o_proj after this route was recorded
185
+ return self.o_proj(output), weights
186
+
187
+ routed_method = types.MethodType(routed, module)
188
+ routes.append(
189
+ (
190
+ module,
191
+ pack,
192
+ routed_method,
193
+ original,
194
+ had_instance_forward,
195
+ )
196
+ )
197
+ observed[site] = impl
198
+
199
+ if not routes:
200
+ return {"refused": refused} if refused else None
201
+
202
+ def enable() -> None:
203
+ for module, pack, routed, _, _ in routes:
204
+ pack.enable_joint(3)
205
+ module.forward = routed
206
+
207
+ def disable() -> None:
208
+ for module, pack, _, original, _ in routes:
209
+ module.forward = original
210
+ pack.disable_joint()
211
+
212
+ def revert() -> None:
213
+ for module, pack, _, original, had_instance_forward in routes:
214
+ pack.disable_joint()
215
+ if had_instance_forward:
216
+ module.forward = original
217
+ elif "forward" in module.__dict__:
218
+ del module.forward
219
+
220
+ enable()
221
+ return {
222
+ "observed": observed,
223
+ "revert": [revert],
224
+ "toggle": (enable, disable),
225
+ "refused": refused,
226
+ }
227
+
228
+
229
+ # Import compatibility for callers that used the original family-labelled
230
+ # name. Registration and receipts use the capability name above.
231
+ QwenPerHeadQkNormRopeAdapter = PerHeadGqaQkNormRopeAdapter