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,174 @@
1
+ """FlashRT structures — verified, composable model sub-blocks.
2
+
3
+ A structure is a versioned specification of one model region: boundary
4
+ tensors, framework-neutral weight slots, a plain reference implementation
5
+ used as ground truth, and qualification gates. This package hosts the
6
+ structure catalog and its registry. Implementations, host adapters, and
7
+ the qualification harness build on top of these specifications.
8
+ """
9
+
10
+ try:
11
+ from flash_rt.catalog.binding import (
12
+ BindingSpec,
13
+ CoverageSegment,
14
+ list_bindings,
15
+ load_binding,
16
+ )
17
+ from flash_rt.catalog.registry import StructureSpec, list_structures, load
18
+ except ImportError as _e: # pragma: no cover - depends on the installed flash-rt
19
+ raise ImportError(
20
+ "flashrt-structures needs a flash-rt that ships the structure "
21
+ "catalog as flash_rt.catalog (FlashRT main after the structures "
22
+ "split; the 0.1.0 wheel on PyPI predates it and still carries the "
23
+ "layer as flash_rt.structures). Install FlashRT from "
24
+ "https://github.com/flashrt-project/FlashRT or upgrade flash-rt."
25
+ ) from _e
26
+
27
+
28
+ def get(name):
29
+ """Explicit door: pull one structure and bind it yourself.
30
+
31
+ Mirrors ``kernels.get_kernel``: ``get("decoder_ffn").bind(module,
32
+ calibration=[...])`` returns a gated drop-in replacement you plug in
33
+ where you choose. See :mod:`flashrt_structures.handle`.
34
+ """
35
+ from flashrt_structures.handle import get as _get
36
+
37
+ return _get(name)
38
+
39
+
40
+ def capture(fn, **kwargs):
41
+ """Capture door: graph a hot stage with declared swap windows.
42
+
43
+ See :func:`flashrt_structures.stages.capture`.
44
+ """
45
+ from flashrt_structures.stages import capture as _capture
46
+
47
+ return _capture(fn, **kwargs)
48
+
49
+
50
+ def auto_swaps(model, forward, **kwargs):
51
+ """Distribution layer: discover, calibrate, bind — one pass, no
52
+ per-seam scaffolding. Returns an :class:`AutoPlan` of swaps.
53
+
54
+ See :func:`flashrt_structures.autobuild.auto_swaps`.
55
+ """
56
+ from flashrt_structures.autobuild import auto_swaps as _auto
57
+
58
+ return _auto(model, forward, **kwargs)
59
+
60
+
61
+ def run_recipe(recipe, model, ctx=None, **kwargs):
62
+ """Recipe door: assemble declared levers, audit same-process on the
63
+ graph, certify or refuse — one call, one receipt.
64
+
65
+ See :mod:`flashrt_structures.recipe` for ``Recipe``/``Lever``/
66
+ ``Gates`` and the switch lifecycle.
67
+ """
68
+ from flashrt_structures.recipe import run_recipe as _run
69
+
70
+ return _run(recipe, model, ctx, **kwargs)
71
+
72
+
73
+ def attach(model, forward, **kwargs):
74
+ """One-call front door: discover, calibrate, gate, activate.
75
+
76
+ See :func:`flashrt_structures.frontdoor.attach`. Imported lazily so
77
+ that spec-only consumers do not pay for torch-side machinery.
78
+ """
79
+ from flashrt_structures.frontdoor import attach as _attach
80
+
81
+ return _attach(model, forward, **kwargs)
82
+
83
+
84
+ def adopt_prequantized(model, fmt="ct_nvfp4", **kwargs):
85
+ """Checkpoint door: adopt an already-quantized checkpoint by
86
+ converting its packed projections into structure impls.
87
+
88
+ See :func:`flashrt_structures.prequantized.adopt_prequantized`.
89
+ """
90
+ from flashrt_structures.prequantized import (
91
+ adopt_prequantized as _adopt)
92
+
93
+ return _adopt(model, fmt, **kwargs)
94
+
95
+
96
+ def quantize_on_adopt(model, fmt="moe_experts_nvfp4", **kwargs):
97
+ """Checkpoint door: quantize a full-precision checkpoint that cannot
98
+ fit the card, converting its dominant structure family (a sparse-MoE
99
+ expert bank) into structure impls at load time.
100
+
101
+ See :func:`flashrt_structures.quantize_on_adopt.quantize_on_adopt`.
102
+ """
103
+ from flashrt_structures.quantize_on_adopt import (
104
+ quantize_on_adopt as _adopt)
105
+
106
+ return _adopt(model, fmt, **kwargs)
107
+
108
+
109
+ def explain(plan):
110
+ """Coverage table for one plan: bound / routed / kept / refused,
111
+ each with its reason. See :mod:`flashrt_structures.explain`."""
112
+ from flashrt_structures.explain import explain as _explain
113
+
114
+ return _explain(plan)
115
+
116
+
117
+ def decode_loop(model, *, max_len, compile_step=True,
118
+ compile_prefill=True, kv_band=None):
119
+ """Serving door: the whole-loop decode form (static cache + compiled
120
+ step + whole-step CUDA graph) over whatever structures are attached.
121
+
122
+ See :mod:`flashrt_structures.impls.decode_loop.whole_step`.
123
+ """
124
+ from flashrt_structures.impls.decode_loop.whole_step import (
125
+ build_decode_loop)
126
+
127
+ return build_decode_loop(model, max_len=max_len,
128
+ compile_step=compile_step,
129
+ compile_prefill=compile_prefill,
130
+ kv_band=kv_band)
131
+
132
+
133
+ def aot_package(module, args=(), kwargs=None,
134
+ package_path="module_aot.pt2", **opts):
135
+ """Whole-graph door: export the swapped module and AOT-compile it
136
+ into a reusable package (graph breaks are defects, not fallbacks).
137
+
138
+ See :mod:`flashrt_structures.aot`.
139
+ """
140
+ from flashrt_structures.aot import aot_package as _pkg
141
+
142
+ return _pkg(module, args=args, kwargs=kwargs,
143
+ package_path=package_path, **opts)
144
+
145
+
146
+ def aot_load(package_path, weights=None):
147
+ """Load an AOT package back as a callable graph."""
148
+ from flashrt_structures.aot import aot_load as _load
149
+
150
+ return _load(package_path, weights=weights)
151
+
152
+
153
+ from . import schemes # noqa: E402 (registry: quantisation schemes)
154
+
155
+ __all__ = [
156
+ "BindingSpec",
157
+ "CoverageSegment",
158
+ "StructureSpec",
159
+ "adopt_prequantized",
160
+ "aot_load",
161
+ "aot_package",
162
+ "decode_loop",
163
+ "explain",
164
+ "attach",
165
+ "capture",
166
+ "get",
167
+ "list_bindings",
168
+ "list_structures",
169
+ "load",
170
+ "load_binding",
171
+ "quantize_on_adopt",
172
+ "run_recipe",
173
+ "schemes",
174
+ ]
@@ -0,0 +1,55 @@
1
+ """Host-family adapters — where a structure's seam is host-specific.
2
+
3
+ Importing this package registers the built-in adapters with autobuild.
4
+ Attention seams (attention_core) live here because where the attention
5
+ math runs differs by host family; a static module pattern cannot find
6
+ them, so each family gets a small adapter.
7
+ """
8
+ from ..autobuild import (
9
+ register_attention_adapter,
10
+ register_gated_delta_adapter,
11
+ register_qk_norm_rope_adapter,
12
+ register_qkv_rope_adapter,
13
+ )
14
+ from .diffusers_attention import DiffusersAttentionAdapter
15
+ from .diffusers_rotary_attention import DiffusersRotaryAttentionAdapter
16
+ from .factored_two_way_attention import FactoredTwoWayAttentionAdapter
17
+ from .factored_qk_norm_rope import FactoredQkNormRopeAdapter
18
+ from .packed_stream_qk_norm_rope import PackedStreamQkNormRopeAdapter
19
+ from .gemma_attention import GemmaAttentionAdapter
20
+ from .transformers_gated_delta import TransformersGatedDeltaAdapter
21
+ from .transformers_gated_delta_fused import (
22
+ TransformersGatedDeltaFusedAdapter,
23
+ )
24
+ from .qwen_per_head_qk_norm_rope import (
25
+ PerHeadGqaQkNormRopeAdapter,
26
+ QwenPerHeadQkNormRopeAdapter,
27
+ )
28
+ from .packed_qkv_rope import PackedQkvRopeAdapter
29
+
30
+ register_qk_norm_rope_adapter(PerHeadGqaQkNormRopeAdapter())
31
+ register_qk_norm_rope_adapter(FactoredQkNormRopeAdapter())
32
+ register_qk_norm_rope_adapter(PackedStreamQkNormRopeAdapter())
33
+ register_qkv_rope_adapter(PackedQkvRopeAdapter())
34
+ register_attention_adapter(GemmaAttentionAdapter())
35
+ register_attention_adapter(FactoredTwoWayAttentionAdapter())
36
+ register_attention_adapter(DiffusersRotaryAttentionAdapter())
37
+ register_attention_adapter(DiffusersAttentionAdapter())
38
+ # the fused-layer form is tried first; it refuses cleanly (missing
39
+ # package entries, out-of-profile layers) and the ladder falls
40
+ # through to the callable-slot form
41
+ register_gated_delta_adapter(TransformersGatedDeltaFusedAdapter())
42
+ register_gated_delta_adapter(TransformersGatedDeltaAdapter())
43
+
44
+ __all__ = [
45
+ "DiffusersAttentionAdapter",
46
+ "DiffusersRotaryAttentionAdapter",
47
+ "GemmaAttentionAdapter",
48
+ "TransformersGatedDeltaAdapter",
49
+ "FactoredTwoWayAttentionAdapter",
50
+ "FactoredQkNormRopeAdapter",
51
+ "PackedStreamQkNormRopeAdapter",
52
+ "PerHeadGqaQkNormRopeAdapter",
53
+ "QwenPerHeadQkNormRopeAdapter",
54
+ "PackedQkvRopeAdapter",
55
+ ]
@@ -0,0 +1,237 @@
1
+ """Attention adapter for capability-compatible Diffusers attention hosts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import torch
6
+
7
+ from ..impls.attention_core import bind_dense_attention_best
8
+
9
+
10
+ def _compatible_site(module, processor) -> tuple[bool, str]:
11
+ """Whether ``module`` exposes the processor contract reproduced below.
12
+
13
+ Processor class names are deliberately irrelevant. The adapter owns the
14
+ projection/output dataflow, so it admits only sites exposing every slot it
15
+ reads and a callable processor that accepts the ordinary Diffusers
16
+ ``(attention, hidden_states, ...)`` boundary.
17
+ """
18
+ if not callable(processor):
19
+ return False, "processor is not callable"
20
+ for attr in ("to_q", "to_k", "to_v"):
21
+ if not isinstance(getattr(module, attr, None), torch.nn.Module):
22
+ return False, f"attention lacks callable projection slot {attr!r}"
23
+ try:
24
+ out_proj, out_drop = module.to_out[0], module.to_out[1]
25
+ except (AttributeError, IndexError, KeyError, TypeError):
26
+ return False, "attention lacks the to_out[projection, dropout] slots"
27
+ if not all(isinstance(part, torch.nn.Module)
28
+ for part in (out_proj, out_drop)):
29
+ return False, "attention output slots are not modules"
30
+ heads = getattr(module, "heads", None)
31
+ if not isinstance(heads, int) or heads <= 0:
32
+ return False, "attention lacks a positive integer head count"
33
+ required_state = (
34
+ "spatial_norm", "group_norm", "norm_cross", "norm_q", "norm_k",
35
+ "residual_connection", "rescale_output_factor",
36
+ )
37
+ missing = [name for name in required_state if not hasattr(module, name)]
38
+ if missing:
39
+ return False, f"attention lacks processor state {missing}"
40
+ if (getattr(module, "norm_cross", False)
41
+ and not callable(getattr(module, "norm_encoder_hidden_states",
42
+ None))):
43
+ return False, "cross normalization is enabled but has no callable"
44
+ return True, ""
45
+
46
+
47
+ def _qkv(attn, hidden_states, encoder_hidden_states, attention_mask, temb):
48
+ """Reproduce the capability-compatible Diffusers projection half."""
49
+ if attn.spatial_norm is not None:
50
+ hidden_states = attn.spatial_norm(hidden_states, temb)
51
+ if hidden_states.ndim == 4:
52
+ batch_size, channel, height, width = hidden_states.shape
53
+ hidden_states = hidden_states.view(
54
+ batch_size, channel, height * width).transpose(1, 2)
55
+ batch_size, sequence_length, _ = (
56
+ hidden_states.shape
57
+ if encoder_hidden_states is None else encoder_hidden_states.shape
58
+ )
59
+ if attention_mask is not None:
60
+ attention_mask = attn.prepare_attention_mask(
61
+ attention_mask, sequence_length, batch_size)
62
+ attention_mask = attention_mask.view(
63
+ batch_size, attn.heads, -1, attention_mask.shape[-1])
64
+ if attn.group_norm is not None:
65
+ hidden_states = attn.group_norm(
66
+ hidden_states.transpose(1, 2)).transpose(1, 2)
67
+ query = attn.to_q(hidden_states)
68
+ if encoder_hidden_states is None:
69
+ encoder_hidden_states = hidden_states
70
+ elif attn.norm_cross:
71
+ encoder_hidden_states = attn.norm_encoder_hidden_states(
72
+ encoder_hidden_states)
73
+ key = attn.to_k(encoder_hidden_states)
74
+ value = attn.to_v(encoder_hidden_states)
75
+ head_dim = key.shape[-1] // attn.heads
76
+ query = query.view(
77
+ batch_size, -1, attn.heads, head_dim).transpose(1, 2)
78
+ key = key.view(
79
+ batch_size, -1, attn.heads, head_dim).transpose(1, 2)
80
+ value = value.view(
81
+ batch_size, -1, attn.heads, head_dim).transpose(1, 2)
82
+ if attn.norm_q is not None:
83
+ query = attn.norm_q(query)
84
+ if attn.norm_k is not None:
85
+ key = attn.norm_k(key)
86
+ return query, key, value, attention_mask
87
+
88
+
89
+ class _Recorder:
90
+ def __init__(self, original, rows):
91
+ self.original = original
92
+ self.rows = rows
93
+
94
+ def __call__(
95
+ self, attn, hidden_states, encoder_hidden_states=None,
96
+ attention_mask=None, temb=None, *args, **kwargs,
97
+ ):
98
+ query, key, value, mask = _qkv(
99
+ attn, hidden_states, encoder_hidden_states, attention_mask, temb)
100
+ self.rows.append({
101
+ "q": query.detach(),
102
+ "key": key.detach(),
103
+ "value": value.detach(),
104
+ "mask": mask.detach() if mask is not None else None,
105
+ })
106
+ return self.original(
107
+ attn, hidden_states, encoder_hidden_states, attention_mask, temb,
108
+ *args, **kwargs)
109
+
110
+
111
+ class _FlashRTDenseAttnProcessor:
112
+ """Diffusers processor with only the SDPA body replaced by FA2."""
113
+
114
+ def __init__(self, core, original):
115
+ self.core = core
116
+ self.original = original
117
+
118
+ def __call__(
119
+ self, attn, hidden_states, encoder_hidden_states=None,
120
+ attention_mask=None, temb=None, *args, **kwargs,
121
+ ):
122
+ # A live mask is served only by a core that baked this site's
123
+ # fixed mask pattern at bind time (packed ranges); a maskless
124
+ # core keeps the host path rather than reuse a frozen mask.
125
+ if attention_mask is not None and not getattr(
126
+ self.core, "allowed_ranges", ()):
127
+ return self.original(
128
+ attn, hidden_states, encoder_hidden_states, attention_mask,
129
+ temb, *args, **kwargs)
130
+ residual = hidden_states
131
+ input_ndim = hidden_states.ndim
132
+ if input_ndim == 4:
133
+ batch_size, channel, height, width = hidden_states.shape
134
+ query, key, value, mask = _qkv(
135
+ attn, hidden_states, encoder_hidden_states, attention_mask, temb)
136
+ projection_dtype = query.dtype
137
+ guard = getattr(self.core, "_frt_guard", None)
138
+ accepted_dtypes = tuple(getattr(guard, "dtypes", ()) or ())
139
+ if accepted_dtypes and projection_dtype not in accepted_dtypes:
140
+ # An upstream composition can change the effective projection
141
+ # dtype after this adapter calibrated. A hidden cast here would
142
+ # silently change the declared boundary and add hot-path work.
143
+ # Keep the host path; the core's zero call count makes the missed
144
+ # route visible to the final-form gate.
145
+ return self.original(
146
+ attn, hidden_states, encoder_hidden_states, attention_mask,
147
+ temb, *args, **kwargs)
148
+ hidden_states = self.core(query, key, value)
149
+ hidden_states = hidden_states.transpose(1, 2).reshape(
150
+ query.shape[0], -1, attn.heads * query.shape[-1])
151
+ hidden_states = hidden_states.to(projection_dtype)
152
+ hidden_states = attn.to_out[0](hidden_states)
153
+ hidden_states = attn.to_out[1](hidden_states)
154
+ if input_ndim == 4:
155
+ hidden_states = hidden_states.transpose(-1, -2).reshape(
156
+ batch_size, channel, height, width)
157
+ if attn.residual_connection:
158
+ hidden_states = hidden_states + residual
159
+ return hidden_states / attn.rescale_output_factor
160
+
161
+
162
+ class DiffusersAttentionAdapter:
163
+ """Route called Diffusers SDPA processors through stateless Hub FA2."""
164
+
165
+ __name__ = "diffusers_attention"
166
+
167
+ def __call__(self, model, forward, *, prefix_cadence: bool = False):
168
+ del prefix_cadence
169
+ sites = []
170
+ for path, module in model.named_modules():
171
+ processor = getattr(module, "processor", None)
172
+ compatible, _ = _compatible_site(module, processor)
173
+ if compatible:
174
+ sites.append((path, module, processor))
175
+ if not sites:
176
+ return None
177
+
178
+ refused = []
179
+ captures = [[] for _ in sites]
180
+ for (_, module, original), rows in zip(sites, captures):
181
+ module.processor = _Recorder(original, rows)
182
+ try:
183
+ with torch.no_grad():
184
+ forward()
185
+ finally:
186
+ for _, module, original in sites:
187
+ module.processor = original
188
+
189
+ routes = []
190
+ observed = {}
191
+ variants = {}
192
+ for (path, module, original), rows in zip(sites, captures):
193
+ if not rows:
194
+ refused.append((
195
+ f"{path}.processor",
196
+ "attention_core dense: compatible processor was not "
197
+ "called during calibration",
198
+ ))
199
+ continue
200
+ core = bind_dense_attention_best(rows)
201
+ if core is None:
202
+ refused.append((
203
+ f"{path}.processor",
204
+ "attention_core dense: published Hub artifact does not "
205
+ "cover the captured head dimension or mask form",
206
+ ))
207
+ continue
208
+ routed = _FlashRTDenseAttnProcessor(core, original)
209
+ routes.append((module, original, routed))
210
+ observed[f"{path}.processor::fa2_core"] = core
211
+ variants[f"{path}.processor"] = {
212
+ "bound": getattr(core, "_frt_variant", "fa2"),
213
+ "superseded": list(
214
+ getattr(core, "_frt_variant_trail", ())),
215
+ }
216
+ if not routes:
217
+ return {}, None, {"refused": refused}
218
+
219
+ def enable() -> None:
220
+ for module, _, routed in routes:
221
+ module.processor = routed
222
+
223
+ def disable() -> None:
224
+ for module, original, _ in routes:
225
+ module.processor = original
226
+
227
+ def revert() -> None:
228
+ disable()
229
+
230
+ enable()
231
+ return {}, None, {
232
+ "revert": [revert],
233
+ "observed": observed,
234
+ "toggle": (enable, disable),
235
+ "refused": refused,
236
+ "attention_variants": variants,
237
+ }