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.
- flashrt_structures/__init__.py +174 -0
- flashrt_structures/adapters/__init__.py +55 -0
- flashrt_structures/adapters/diffusers_attention.py +237 -0
- flashrt_structures/adapters/diffusers_rotary_attention.py +239 -0
- flashrt_structures/adapters/factored_qk_norm_rope.py +252 -0
- flashrt_structures/adapters/factored_two_way_attention.py +99 -0
- flashrt_structures/adapters/gemma_attention.py +226 -0
- flashrt_structures/adapters/packed_qkv_rope.py +342 -0
- flashrt_structures/adapters/packed_stream_qk_norm_rope.py +376 -0
- flashrt_structures/adapters/qwen_per_head_qk_norm_rope.py +231 -0
- flashrt_structures/adapters/sglang_engine.py +207 -0
- flashrt_structures/adapters/transformers_attention_interface.py +73 -0
- flashrt_structures/adapters/transformers_gated_delta.py +152 -0
- flashrt_structures/adapters/transformers_gated_delta_fused.py +96 -0
- flashrt_structures/adapters/vllm_engine.py +424 -0
- flashrt_structures/adjudicate.py +85 -0
- flashrt_structures/aot.py +191 -0
- flashrt_structures/autobuild.py +2052 -0
- flashrt_structures/beta/__init__.py +43 -0
- flashrt_structures/beta/conform.py +94 -0
- flashrt_structures/beta/joins.py +113 -0
- flashrt_structures/beta/negotiate.py +84 -0
- flashrt_structures/beta/ports.py +140 -0
- flashrt_structures/decisions.py +80 -0
- flashrt_structures/discover.py +623 -0
- flashrt_structures/explain.py +70 -0
- flashrt_structures/frontdoor.py +572 -0
- flashrt_structures/gates.py +465 -0
- flashrt_structures/guard.py +421 -0
- flashrt_structures/handle.py +189 -0
- flashrt_structures/impls/__init__.py +219 -0
- flashrt_structures/impls/adaln_producer/__init__.py +8 -0
- flashrt_structures/impls/adaln_producer/broker.py +116 -0
- flashrt_structures/impls/adaln_producer/fused.py +388 -0
- flashrt_structures/impls/adarms_stack/__init__.py +8 -0
- flashrt_structures/impls/adarms_stack/fp8_chain.py +832 -0
- flashrt_structures/impls/adarms_stack/region.py +102 -0
- flashrt_structures/impls/attention_core/__init__.py +132 -0
- flashrt_structures/impls/attention_core/fa2_seqused.py +458 -0
- flashrt_structures/impls/attention_core/fa4_cute.py +154 -0
- flashrt_structures/impls/attention_core/fa4_fp8.py +178 -0
- flashrt_structures/impls/attention_core/masked_mha.py +158 -0
- flashrt_structures/impls/attention_core/two_way_fa2.py +220 -0
- flashrt_structures/impls/cadence_static/__init__.py +18 -0
- flashrt_structures/impls/cadence_static/buffers.py +122 -0
- flashrt_structures/impls/cadence_static/cross_attention.py +187 -0
- flashrt_structures/impls/chain_elements.py +89 -0
- flashrt_structures/impls/decode_loop/__init__.py +0 -0
- flashrt_structures/impls/decode_loop/fp8_kv.py +206 -0
- flashrt_structures/impls/decode_loop/mtp_speculative.py +245 -0
- flashrt_structures/impls/decode_loop/whole_step.py +852 -0
- flashrt_structures/impls/decoder_block/__init__.py +6 -0
- flashrt_structures/impls/decoder_block/attn_sublayer.py +110 -0
- flashrt_structures/impls/decoder_block/fused.py +167 -0
- flashrt_structures/impls/decoder_ffn/__init__.py +0 -0
- flashrt_structures/impls/decoder_ffn/fp8_static.py +310 -0
- flashrt_structures/impls/decoder_ffn/fp8_static.yaml +22 -0
- flashrt_structures/impls/decoder_ffn/w4a16_static.py +221 -0
- flashrt_structures/impls/decoder_ffn/w8a16_static.py +183 -0
- flashrt_structures/impls/dit_stack/__init__.py +11 -0
- flashrt_structures/impls/dit_stack/fp4_chain.py +417 -0
- flashrt_structures/impls/dit_stack/region.py +86 -0
- flashrt_structures/impls/fixed_iter/__init__.py +29 -0
- flashrt_structures/impls/fixed_iter/openpi.py +264 -0
- flashrt_structures/impls/fixed_iter/protocol.py +94 -0
- flashrt_structures/impls/gated_delta_core/__init__.py +3 -0
- flashrt_structures/impls/gated_delta_core/fused_layer.py +545 -0
- flashrt_structures/impls/gated_delta_core/hub_v3.py +152 -0
- flashrt_structures/impls/graph_lowering/__init__.py +27 -0
- flashrt_structures/impls/graph_lowering/pi052_denoise.py +179 -0
- flashrt_structures/impls/graph_lowering/protocol.py +76 -0
- flashrt_structures/impls/graph_lowering/qwen3_vl.py +364 -0
- flashrt_structures/impls/linear_proj/__init__.py +0 -0
- flashrt_structures/impls/linear_proj/fp8_static.py +270 -0
- flashrt_structures/impls/linear_proj/nvfp4_balance.py +131 -0
- flashrt_structures/impls/linear_proj/nvfp4_dynamic.py +182 -0
- flashrt_structures/impls/linear_proj/w8a16_static.py +230 -0
- flashrt_structures/impls/modnorm_qkv_chain/__init__.py +0 -0
- flashrt_structures/impls/modnorm_qkv_chain/fp8_ptok_table.py +291 -0
- flashrt_structures/impls/moe_experts/__init__.py +9 -0
- flashrt_structures/impls/moe_experts/nvfp4_dynamic.py +208 -0
- flashrt_structures/impls/moe_experts/nvfp4_w4a16.py +129 -0
- flashrt_structures/impls/norm_fused/__init__.py +3 -0
- flashrt_structures/impls/norm_fused/bf16.py +94 -0
- flashrt_structures/impls/norm_fused/fp8_producer.py +84 -0
- flashrt_structures/impls/patch_projection/__init__.py +3 -0
- flashrt_structures/impls/patch_projection/bf16_flat.py +139 -0
- flashrt_structures/impls/prefill_tower/__init__.py +10 -0
- flashrt_structures/impls/prefill_tower/fp8_chain.py +955 -0
- flashrt_structures/impls/prefill_tower/region.py +99 -0
- flashrt_structures/impls/qk_norm_rope/__init__.py +12 -0
- flashrt_structures/impls/qk_norm_rope/per_head_gqa.py +199 -0
- flashrt_structures/impls/qk_norm_rope/projection_bf16.py +165 -0
- flashrt_structures/impls/qkv_pack/__init__.py +5 -0
- flashrt_structures/impls/qkv_pack/bf16.py +110 -0
- flashrt_structures/impls/qkv_pack/fp8_static.py +435 -0
- flashrt_structures/impls/qkv_pack/nvfp4_balance.py +218 -0
- flashrt_structures/impls/qkv_rope/__init__.py +3 -0
- flashrt_structures/impls/qkv_rope/packed_bias_bf16.py +143 -0
- flashrt_structures/impls/step_table.py +113 -0
- flashrt_structures/impls/vision_ffn/__init__.py +0 -0
- flashrt_structures/impls/vision_ffn/fp8_static.py +261 -0
- flashrt_structures/impls/vision_ffn/nvfp4_balance.py +211 -0
- flashrt_structures/impls/vision_tower/__init__.py +7 -0
- flashrt_structures/impls/vision_tower/fp8_chain.py +533 -0
- flashrt_structures/impls/vision_tower/region.py +91 -0
- flashrt_structures/matrix.py +126 -0
- flashrt_structures/points.py +368 -0
- flashrt_structures/prequantized.py +131 -0
- flashrt_structures/quantize_on_adopt.py +94 -0
- flashrt_structures/recipe.py +438 -0
- flashrt_structures/regions.py +208 -0
- flashrt_structures/schemes.py +490 -0
- flashrt_structures/stages.py +298 -0
- flashrt_structures/storage.py +255 -0
- flashrt_structures/swap.py +398 -0
- flashrt_structures/workspace.py +110 -0
- flashrt_structures-0.2.0.dist-info/METADATA +140 -0
- flashrt_structures-0.2.0.dist-info/RECORD +122 -0
- flashrt_structures-0.2.0.dist-info/WHEEL +5 -0
- flashrt_structures-0.2.0.dist-info/licenses/LICENSE +202 -0
- 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
|