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,239 @@
1
+ """Rotary Diffusers attention routed through the dense FA2 structure.
2
+
3
+ The adapter is selected by the processor boundary and module slots it can
4
+ reproduce, not by a model or processor class name. It covers the common
5
+ video-transformer form where Q/K are normalised before an optional rotary
6
+ embedding and the processor returns a sequence-major attention result.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import inspect
12
+
13
+ import torch
14
+
15
+ from ..impls.attention_core import bind_dense_attention_best
16
+
17
+
18
+ def _compatible_site(module, processor) -> tuple[bool, str]:
19
+ if not callable(processor):
20
+ return False, "processor is not callable"
21
+ try:
22
+ parameters = inspect.signature(processor.__call__).parameters
23
+ except (TypeError, ValueError, AttributeError):
24
+ return False, "processor call signature is not inspectable"
25
+ if "rotary_emb" not in parameters:
26
+ return False, "processor has no rotary_emb boundary"
27
+ required_modules = ("to_q", "to_k", "to_v", "norm_q", "norm_k")
28
+ for attr in required_modules:
29
+ if not isinstance(getattr(module, attr, None), torch.nn.Module):
30
+ return False, f"attention lacks callable slot {attr!r}"
31
+ try:
32
+ out_proj, out_drop = module.to_out[0], module.to_out[1]
33
+ except (AttributeError, IndexError, KeyError, TypeError):
34
+ return False, "attention lacks the to_out[projection, dropout] slots"
35
+ if not all(isinstance(part, torch.nn.Module)
36
+ for part in (out_proj, out_drop)):
37
+ return False, "attention output slots are not modules"
38
+ heads = getattr(module, "heads", None)
39
+ if not isinstance(heads, int) or heads <= 0:
40
+ return False, "attention lacks a positive integer head count"
41
+ if getattr(module, "add_k_proj", None) is not None:
42
+ return False, "added image KV is not yet an executable form"
43
+ return True, ""
44
+
45
+
46
+ def _projections(attn, hidden_states, encoder_hidden_states):
47
+ context = hidden_states if encoder_hidden_states is None \
48
+ else encoder_hidden_states
49
+ if getattr(attn, "fused_projections", False):
50
+ if getattr(attn, "is_cross_attention", False):
51
+ query = attn.to_q(hidden_states)
52
+ key, value = attn.to_kv(context).chunk(2, dim=-1)
53
+ else:
54
+ query, key, value = attn.to_qkv(hidden_states).chunk(3, dim=-1)
55
+ else:
56
+ query = attn.to_q(hidden_states)
57
+ key = attn.to_k(context)
58
+ value = attn.to_v(context)
59
+ return query, key, value
60
+
61
+
62
+ def _apply_rotary(hidden_states, rotary_emb):
63
+ if rotary_emb is None:
64
+ return hidden_states
65
+ freqs_cos, freqs_sin = rotary_emb
66
+ x1, x2 = hidden_states.unflatten(-1, (-1, 2)).unbind(-1)
67
+ cos = freqs_cos[..., 0::2]
68
+ sin = freqs_sin[..., 1::2]
69
+ out = torch.empty_like(hidden_states)
70
+ out[..., 0::2] = x1 * cos - x2 * sin
71
+ out[..., 1::2] = x1 * sin + x2 * cos
72
+ return out.type_as(hidden_states)
73
+
74
+
75
+ def _qkv(attn, hidden_states, encoder_hidden_states, rotary_emb):
76
+ query, key, value = _projections(
77
+ attn, hidden_states, encoder_hidden_states)
78
+ query = attn.norm_q(query)
79
+ key = attn.norm_k(key)
80
+ query = query.unflatten(2, (attn.heads, -1))
81
+ key = key.unflatten(2, (attn.heads, -1))
82
+ value = value.unflatten(2, (attn.heads, -1))
83
+ query = _apply_rotary(query, rotary_emb)
84
+ key = _apply_rotary(key, rotary_emb)
85
+ return query, key, value
86
+
87
+
88
+ class _Recorder:
89
+ def __init__(self, original, rows):
90
+ self.original = original
91
+ self.rows = rows
92
+
93
+ def __call__(
94
+ self, attn, hidden_states, encoder_hidden_states=None,
95
+ attention_mask=None, rotary_emb=None, *args, **kwargs,
96
+ ):
97
+ if attention_mask is None:
98
+ query, key, value = _qkv(
99
+ attn, hidden_states, encoder_hidden_states, rotary_emb)
100
+ row = {
101
+ "q": query.transpose(1, 2).detach(),
102
+ "key": key.transpose(1, 2).detach(),
103
+ "value": value.transpose(1, 2).detach(),
104
+ "mask": None,
105
+ }
106
+ if self.rows:
107
+ first = self.rows[0]
108
+ expected = tuple(
109
+ (tuple(first[name].shape), first[name].dtype)
110
+ for name in ("q", "key", "value"))
111
+ got = tuple(
112
+ (tuple(row[name].shape), row[name].dtype)
113
+ for name in ("q", "key", "value"))
114
+ if got != expected:
115
+ raise ValueError(
116
+ "attention_core rotary: shape or dtype moved within "
117
+ f"one calibration call: {expected} -> {got}")
118
+ else:
119
+ # Binding needs one real device sample. Subsequent calls only
120
+ # qualify the stable signature; retaining every denoise-step
121
+ # activation would turn calibration length into VRAM usage.
122
+ self.rows.append(row)
123
+ else:
124
+ self.rows.append({"mask": attention_mask.detach()})
125
+ return self.original(
126
+ attn, hidden_states, encoder_hidden_states, attention_mask,
127
+ rotary_emb, *args, **kwargs)
128
+
129
+
130
+ class _FlashRTRotaryAttnProcessor:
131
+ def __init__(self, core, original):
132
+ self.core = core
133
+ self.original = original
134
+
135
+ def __call__(
136
+ self, attn, hidden_states, encoder_hidden_states=None,
137
+ attention_mask=None, rotary_emb=None, *args, **kwargs,
138
+ ):
139
+ if attention_mask is not None:
140
+ return self.original(
141
+ attn, hidden_states, encoder_hidden_states, attention_mask,
142
+ rotary_emb, *args, **kwargs)
143
+ query, key, value = _qkv(
144
+ attn, hidden_states, encoder_hidden_states, rotary_emb)
145
+ projection_dtype = query.dtype
146
+ guard = getattr(self.core, "_frt_guard", None)
147
+ accepted_dtypes = tuple(getattr(guard, "dtypes", ()) or ())
148
+ if accepted_dtypes and projection_dtype not in accepted_dtypes:
149
+ return self.original(
150
+ attn, hidden_states, encoder_hidden_states, attention_mask,
151
+ rotary_emb, *args, **kwargs)
152
+ hidden_states = self.core(
153
+ query.transpose(1, 2), key.transpose(1, 2), value.transpose(1, 2))
154
+ hidden_states = hidden_states.transpose(1, 2).flatten(2, 3)
155
+ hidden_states = hidden_states.type_as(query)
156
+ hidden_states = attn.to_out[0](hidden_states)
157
+ return attn.to_out[1](hidden_states)
158
+
159
+
160
+ class DiffusersRotaryAttentionAdapter:
161
+ """Route capability-compatible rotary processors through Hub FA2."""
162
+
163
+ __name__ = "diffusers_rotary_attention"
164
+
165
+ def __call__(self, model, forward, *, prefix_cadence: bool = False):
166
+ del prefix_cadence
167
+ sites = []
168
+ for path, module in model.named_modules():
169
+ processor = getattr(module, "processor", None)
170
+ compatible, _ = _compatible_site(module, processor)
171
+ if compatible:
172
+ sites.append((path, module, processor))
173
+ if not sites:
174
+ return None
175
+
176
+ captures = [[] for _ in sites]
177
+ for (_, module, original), rows in zip(sites, captures):
178
+ module.processor = _Recorder(original, rows)
179
+ try:
180
+ with torch.no_grad():
181
+ forward()
182
+ finally:
183
+ for _, module, original in sites:
184
+ module.processor = original
185
+
186
+ refused = []
187
+ routes = []
188
+ observed = {}
189
+ variants = {}
190
+ for (path, module, original), rows in zip(sites, captures):
191
+ if not rows:
192
+ refused.append((
193
+ f"{path}.processor",
194
+ "attention_core rotary: compatible processor was not "
195
+ "called during calibration",
196
+ ))
197
+ continue
198
+ if any(row.get("mask") is not None for row in rows):
199
+ refused.append((
200
+ f"{path}.processor",
201
+ "attention_core rotary: live masks are outside the "
202
+ "unmasked executable form",
203
+ ))
204
+ continue
205
+ core = bind_dense_attention_best(rows)
206
+ if core is None:
207
+ refused.append((
208
+ f"{path}.processor",
209
+ "attention_core rotary: Hub FA2 does not cover the "
210
+ "captured head dimension",
211
+ ))
212
+ continue
213
+ routed = _FlashRTRotaryAttnProcessor(core, original)
214
+ routes.append((module, original, routed))
215
+ observed[f"{path}.processor::fa2_core"] = core
216
+ variants[f"{path}.processor"] = {
217
+ "bound": getattr(core, "_frt_variant", "fa2"),
218
+ "superseded": list(
219
+ getattr(core, "_frt_variant_trail", ())),
220
+ }
221
+ if not routes:
222
+ return {}, None, {"refused": refused}
223
+
224
+ def enable():
225
+ for module, _, routed in routes:
226
+ module.processor = routed
227
+
228
+ def disable():
229
+ for module, original, _ in routes:
230
+ module.processor = original
231
+
232
+ enable()
233
+ return {}, None, {
234
+ "revert": [disable],
235
+ "observed": observed,
236
+ "toggle": (enable, disable),
237
+ "refused": refused,
238
+ "attention_variants": variants,
239
+ }
@@ -0,0 +1,252 @@
1
+ """Q/K norm + RoPE adapter for factored two-way attention hosts.
2
+
3
+ The host capability is two independent sibling-QKV projection groups over a
4
+ factored causal/full pack. Each group is consumed by per-head RMSNorm and the
5
+ same pre-expanded rotate-half position table before a factored attention
6
+ processor. This is the Cosmos/MoT form, but the adapter deliberately matches
7
+ those slots and dataflow rather than a model or class name.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import types
13
+
14
+ from ..guard import GuardRefused
15
+ from ..impls.qk_norm_rope import bind_per_head_gqa_qk_norm_rope
16
+ from ..impls.qkv_pack.fp8_static import PackedLinear, StashReader
17
+
18
+
19
+ _PATHS = (
20
+ (
21
+ ("to_q", "to_k", "to_v"),
22
+ ("norm_q", "norm_k"),
23
+ "causal_seq",
24
+ ),
25
+ (
26
+ ("add_q_proj", "add_k_proj", "add_v_proj"),
27
+ ("norm_added_q", "norm_added_k"),
28
+ "full_only_seq",
29
+ ),
30
+ )
31
+
32
+
33
+ def _pack_parts(plan, path: str, attrs: tuple[str, str, str]):
34
+ head = plan.swaps.get(f"{path}.{attrs[0]}")
35
+ key = plan.swaps.get(f"{path}.{attrs[1]}")
36
+ value = plan.swaps.get(f"{path}.{attrs[2]}")
37
+ if not (
38
+ isinstance(head, PackedLinear)
39
+ and isinstance(key, StashReader)
40
+ and isinstance(value, StashReader)
41
+ and key._packed[0] is head
42
+ and value._packed[0] is head
43
+ ):
44
+ return None
45
+ return head
46
+
47
+
48
+ def _epsilon(norm) -> float | None:
49
+ value = getattr(norm, "variance_epsilon", getattr(norm, "eps", None))
50
+ return None if value is None else float(value)
51
+
52
+
53
+ class FactoredQkNormRopeAdapter:
54
+ """Compose two packed QKV groups with per-head Q/K norm and RoPE."""
55
+
56
+ __name__ = "factored_qk_norm_rope"
57
+
58
+ def __call__(self, model, plan):
59
+ routes = []
60
+ observed = {}
61
+ refused = []
62
+
63
+ for path, module in model.named_modules():
64
+ packs = [_pack_parts(plan, path, attrs) for attrs, _, _ in _PATHS]
65
+ if not any(pack is not None for pack in packs):
66
+ continue
67
+ site = f"{path}::factored_qk_norm_rope"
68
+
69
+ def refuse(reason: str) -> None:
70
+ refused.append((site, f"qk_norm_rope refused: {reason}"))
71
+
72
+ if not all(pack is not None for pack in packs):
73
+ refuse("both causal and full QKV groups must be packed")
74
+ continue
75
+ if module.training:
76
+ refuse("training/dropout form is outside the inference seam")
77
+ continue
78
+ if not all(
79
+ hasattr(module, attr)
80
+ for attr in (
81
+ "head_dim",
82
+ "num_attention_heads",
83
+ "num_key_value_heads",
84
+ "dispatch_attention_fn",
85
+ "to_out",
86
+ "to_add_out",
87
+ )
88
+ ):
89
+ refuse("host lacks the complete factored two-way slots")
90
+ continue
91
+ if getattr(module, "cp_mesh", None) is not None:
92
+ refuse("context-parallel packs are outside this single-device seam")
93
+ continue
94
+
95
+ head_dim = int(module.head_dim)
96
+ q_heads = int(module.num_attention_heads)
97
+ kv_heads = int(module.num_key_value_heads)
98
+ if head_dim != 128:
99
+ refuse("current Hub entry requires head_dim=128")
100
+ continue
101
+
102
+ bounds = []
103
+ bad = None
104
+ for pack, (_, norms, key) in zip(packs, _PATHS):
105
+ q_norm = getattr(module, norms[0], None)
106
+ k_norm = getattr(module, norms[1], None)
107
+ q_weight = getattr(q_norm, "weight", None)
108
+ k_weight = getattr(k_norm, "weight", None)
109
+ eps = _epsilon(q_norm)
110
+ if q_weight is None or k_weight is None or eps is None:
111
+ bad = f"{key} Q/K norm weights or epsilon are absent"
112
+ break
113
+ expected = (q_heads * head_dim, kv_heads * head_dim, kv_heads * head_dim)
114
+ if tuple(pack.splits[:3]) != expected:
115
+ bad = f"{key} packed widths {tuple(pack.splits[:3])} != {expected}"
116
+ break
117
+ try:
118
+ bound = bind_per_head_gqa_qk_norm_rope(
119
+ q_weight,
120
+ k_weight,
121
+ row_capacity=pack.rows,
122
+ q_heads=q_heads,
123
+ kv_heads=kv_heads,
124
+ head_dim=head_dim,
125
+ eps=eps,
126
+ )
127
+ except (ValueError, RuntimeError) as exc:
128
+ bad = str(exc)
129
+ break
130
+ bounds.append(bound)
131
+ if bad is not None:
132
+ refuse(bad)
133
+ continue
134
+
135
+ original = module.forward
136
+ had_instance_forward = "forward" in module.__dict__
137
+ causal_pack, full_pack = packs
138
+ causal_bound, full_bound = bounds
139
+
140
+ def routed(
141
+ self,
142
+ pack,
143
+ attention_mask,
144
+ packed_position_embeddings,
145
+ dual_kv_cache=None,
146
+ natten_metadata=None,
147
+ *,
148
+ und_packed=causal_pack,
149
+ gen_packed=full_pack,
150
+ und_bound=causal_bound,
151
+ gen_bound=full_bound,
152
+ ):
153
+ del attention_mask
154
+ if dual_kv_cache is not None:
155
+ raise GuardRefused(
156
+ "qk_norm_rope: factored cache mutation is outside the bound seam"
157
+ )
158
+ if natten_metadata is not None:
159
+ raise GuardRefused(
160
+ "qk_norm_rope: neighborhood attention is outside the bound seam"
161
+ )
162
+ if not isinstance(pack, dict) or not all(
163
+ key in pack for key in ("causal_seq", "full_only_seq")
164
+ ):
165
+ raise GuardRefused(
166
+ "qk_norm_rope: expected a causal/full factored pack"
167
+ )
168
+ try:
169
+ cos_pack, sin_pack = packed_position_embeddings
170
+ und_cos = cos_pack["causal_seq"]
171
+ und_sin = sin_pack["causal_seq"]
172
+ gen_cos = cos_pack["full_only_seq"]
173
+ gen_sin = sin_pack["full_only_seq"]
174
+ except (KeyError, TypeError, ValueError) as exc:
175
+ raise GuardRefused(
176
+ "qk_norm_rope: position tables do not share the factored layout"
177
+ ) from exc
178
+
179
+ q_und, k_und, v_und = und_bound(
180
+ und_packed.joint(pack["causal_seq"]).unsqueeze(0),
181
+ und_cos.unsqueeze(0),
182
+ und_sin.unsqueeze(0),
183
+ )
184
+ q_gen, k_gen, v_gen = gen_bound(
185
+ gen_packed.joint(pack["full_only_seq"]).unsqueeze(0),
186
+ gen_cos.unsqueeze(0),
187
+ gen_sin.unsqueeze(0),
188
+ )
189
+ q_und, k_und, v_und = q_und[0], k_und[0], v_und[0]
190
+ q_gen, k_gen, v_gen = q_gen[0], k_gen[0], v_gen[0]
191
+
192
+ if bool(getattr(getattr(self, "config", None), "freeze_und", False)):
193
+ q_und = q_und.detach()
194
+ k_und = k_und.detach()
195
+ v_und = v_und.detach()
196
+
197
+ query = dict(pack)
198
+ key = dict(pack)
199
+ value = dict(pack)
200
+ query["causal_seq"], query["full_only_seq"] = q_und, q_gen
201
+ key["causal_seq"], key["full_only_seq"] = k_und, k_gen
202
+ value["causal_seq"], value["full_only_seq"] = v_und, v_gen
203
+ attended = self.dispatch_attention_fn(query, key, value)
204
+
205
+ out = dict(pack)
206
+ out["causal_seq"] = self.to_out(attended["causal_seq"])
207
+ out["full_only_seq"] = self.to_add_out(attended["full_only_seq"])
208
+ return out
209
+
210
+ routes.append(
211
+ (
212
+ module,
213
+ packs,
214
+ types.MethodType(routed, module),
215
+ original,
216
+ had_instance_forward,
217
+ )
218
+ )
219
+ observed[f"{path}.causal::per_head_qk_norm_rope"] = causal_bound
220
+ observed[f"{path}.full::per_head_qk_norm_rope"] = full_bound
221
+
222
+ if not routes:
223
+ return {"refused": refused} if refused else None
224
+
225
+ def enable() -> None:
226
+ for module, packs, routed, _, _ in routes:
227
+ for pack in packs:
228
+ pack.enable_joint(3)
229
+ module.forward = routed
230
+
231
+ def disable() -> None:
232
+ for module, packs, _, original, _ in routes:
233
+ module.forward = original
234
+ for pack in packs:
235
+ pack.disable_joint()
236
+
237
+ def revert() -> None:
238
+ for module, packs, _, original, had_instance_forward in routes:
239
+ for pack in packs:
240
+ pack.disable_joint()
241
+ if had_instance_forward:
242
+ module.forward = original
243
+ elif "forward" in module.__dict__:
244
+ del module.forward
245
+
246
+ enable()
247
+ return {
248
+ "observed": observed,
249
+ "revert": [revert],
250
+ "toggle": (enable, disable),
251
+ "refused": refused,
252
+ }
@@ -0,0 +1,99 @@
1
+ """Adapter for hosts exposing a factored two-way attention processor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ..impls.attention_core import bind_two_way_attention
6
+
7
+
8
+ class FactoredTwoWayAttentionAdapter:
9
+ """Route compatible factored attention processors through Hub FA2."""
10
+
11
+ __name__ = "factored_two_way_attention"
12
+
13
+ def __call__(self, model, forward, *, prefix_cadence: bool = False):
14
+ del prefix_cadence
15
+ sites = []
16
+ for path, module in model.named_modules():
17
+ processor = getattr(module, "dispatch_attention_fn", None)
18
+ if callable(processor):
19
+ sites.append((path, module, processor))
20
+ if not sites:
21
+ return None
22
+
23
+ captures: list[list[dict]] = [[] for _ in sites]
24
+ for index, (_, module, original) in enumerate(sites):
25
+ def record(query, key, value, *, i=index, fn=original):
26
+ captures[i].append(
27
+ {"query": query, "key": key, "value": value})
28
+ return fn(query, key, value)
29
+
30
+ module.dispatch_attention_fn = record
31
+ try:
32
+ with __import__("torch").no_grad():
33
+ forward()
34
+ finally:
35
+ for _, module, original in sites:
36
+ module.dispatch_attention_fn = original
37
+
38
+ if not any(captures):
39
+ return None
40
+ if any(not rows for rows in captures):
41
+ raise ValueError(
42
+ "attention_core two_way: only some discovered processors "
43
+ "were called")
44
+
45
+ cores = []
46
+ routes = []
47
+ for (path, module, original), rows in zip(sites, captures):
48
+ first = rows[0]
49
+
50
+ def shape(pack):
51
+ return (
52
+ tuple(pack["causal_seq"].shape),
53
+ tuple(pack["full_only_seq"].shape),
54
+ )
55
+
56
+ expected = (
57
+ shape(first["query"]),
58
+ shape(first["key"]),
59
+ shape(first["value"]),
60
+ )
61
+ for row in rows[1:]:
62
+ got = (
63
+ shape(row["query"]),
64
+ shape(row["key"]),
65
+ shape(row["value"]),
66
+ )
67
+ if got != expected:
68
+ raise ValueError(
69
+ "attention_core two_way: processor shapes move "
70
+ f"within one calibration call: {expected} -> {got}")
71
+ core = bind_two_way_attention(first)
72
+
73
+ def routed(query, key, value, *, bound=core):
74
+ return bound(query, key, value)
75
+
76
+ cores.append((path, core))
77
+ routes.append((module, original, routed))
78
+
79
+ def enable() -> None:
80
+ for module, _, routed in routes:
81
+ module.dispatch_attention_fn = routed
82
+
83
+ def disable() -> None:
84
+ for module, original, _ in routes:
85
+ module.dispatch_attention_fn = original
86
+
87
+ def revert() -> None:
88
+ disable()
89
+
90
+ enable()
91
+ observed = {
92
+ f"{path}.dispatch_attention_fn::fa2_core": core
93
+ for path, core in cores
94
+ }
95
+ return {}, None, {
96
+ "revert": [revert],
97
+ "observed": observed,
98
+ "toggle": (enable, disable),
99
+ }