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,424 @@
1
+ """vLLM engine family: explicit assembly onto a serving host's model.
2
+
3
+ A vLLM model is an ``nn.Module`` tree living inside the engine process,
4
+ but its seams do not match the static module patterns discovery reads:
5
+ projections are merged/parallel classes whose ``forward`` returns
6
+ ``(out, bias)``, expert weights live stacked on a routed-experts child,
7
+ and the LM head is consulted through ``quant_method.apply`` rather than
8
+ module forward. This adapter recognises those seams by structure —
9
+ 2-D ``weight`` plus a ``quant_method`` slot for projections, a
10
+ ``w13_weight``/``w2_weight`` pair for an expert bank, a ``lm_head``
11
+ whose vocabulary row count may exceed a quantize entry's grid limit —
12
+ and never by class or model name.
13
+
14
+ Three engine facts shape the assembly, each carried here so callers do
15
+ not rediscover them:
16
+
17
+ - **Seats must be installed after weights load and before the engine's
18
+ first trace.** vLLM's compiled artifact resolves parameters by tree
19
+ path; a post-compile swap either raises ``KeyError`` or is silently
20
+ bypassed. ``install_load_hook`` patches the model runner's
21
+ ``load_model`` for exactly this window.
22
+ - **A Python shape branch dies in the compiled form.** vLLM traces with
23
+ guard evaluation off, so band dispatch (decode rows to the packed
24
+ bank, prefill rows to the retained host) lives inside a custom op:
25
+ its body re-runs at capture (decode sizes bake the seam branch into
26
+ the graphs) and eagerly at prefill.
27
+ - **The head is intercepted at ``quant_method``**, and binds as row
28
+ slabs when the vocabulary exceeds the quantize entry's row support.
29
+
30
+ Everything installed through :func:`attach_engine` goes through
31
+ ``swap.attach``; the returned handle detaches bit-exactly. The expert
32
+ bank and head interceptions are host mutations recorded as ``revert``
33
+ callables on the same handle, so one ``detach`` restores all of it.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import os
39
+ from typing import Any
40
+
41
+ import torch
42
+ from torch import nn
43
+
44
+ from .. import swap as _swap
45
+ from ..impls.linear_proj import nvfp4_dynamic as _linear
46
+ from ..impls.moe_experts import nvfp4_w4a16 as _experts_w4a16
47
+ from ..impls.moe_experts import nvfp4_dynamic as _experts_w4a4
48
+
49
+ #: dense projection seams, by dataflow position suffix. These are
50
+ #: positions in the qwen3_5 family's dataflow, not module identities;
51
+ #: a host that lacks one simply contributes no seat.
52
+ DENSE_SEAT_SUFFIXES = (
53
+ "linear_attn.out_proj", "linear_attn.in_proj_qkvz",
54
+ "self_attn.qkv_proj", "self_attn.o_proj",
55
+ "shared_expert.gate_up_proj", "shared_expert.down_proj",
56
+ "mlp.gate_up_proj", "mlp.down_proj",
57
+ )
58
+
59
+ _SEATS_BY_IDX: dict[int, Any] = {}
60
+
61
+
62
+ # Registered at import: the registration itself must never sit on a
63
+ # traced path — a lazy first call lands inside dynamo and the schema
64
+ # inference graph-breaks the host's compiled forward.
65
+ @torch.library.custom_op("flash_rt_structures::vllm_moe_seat",
66
+ mutates_args=())
67
+ def _vllm_moe_seat_op(hidden: torch.Tensor, router_logits: torch.Tensor,
68
+ top_idx: torch.Tensor, top_w: torch.Tensor,
69
+ idx: int) -> torch.Tensor:
70
+ return _SEATS_BY_IDX[idx].run(hidden, router_logits, top_idx, top_w)
71
+
72
+
73
+ @_vllm_moe_seat_op.register_fake
74
+ def _(hidden, router_logits, top_idx, top_w, idx):
75
+ return torch.empty_like(hidden)
76
+
77
+
78
+ class _ProjSeat(nn.Module):
79
+ """Preserves the engine's ``(out, bias)`` projection contract."""
80
+
81
+ def __init__(self, seam):
82
+ super().__init__()
83
+ self.seam = seam
84
+
85
+ def forward(self, x, *args, **kwargs):
86
+ return self.seam(x), None
87
+
88
+
89
+ class _MoESeat(nn.Module):
90
+ """Stands where the fused-MoE module stood: routing here, bank in
91
+ the seam, the host's own shared-expert module added back (it owned
92
+ it too, and its projections may themselves carry seats), and a
93
+ declared band — decode rows walk the packed bank, prefill rows go
94
+ to the retained host module."""
95
+
96
+ #: rows above which the retained host module serves the batch.
97
+ #: Measured, not assumed. A routed-MoE decode does not amortise the
98
+ #: way a dense one does — eight tokens pick their own top-8 experts,
99
+ #: so expert traffic grows with the batch instead of being shared,
100
+ #: and a packed bank keeps paying well past batch one. Measured on
101
+ #: Thor against vLLM 0.26 (35B-A3B, 128-token generations): 2.10x at
102
+ #: batch 1, 2.45x at 4, 2.51x at 8, 1.65x at 16. The earlier value
103
+ #: of 8 handed batch-16 traffic back to the host and threw that
104
+ #: 1.65x away — the arm measured 0.98x, because at that batch the
105
+ #: dense seats alone are worth nothing while the MoE seat is worth
106
+ #: everything. 16 is the largest batch measured to pay; sweep with
107
+ #: ``FRT_MOE_BAND_T`` before raising it further.
108
+ BAND_T = int(os.environ.get("FRT_MOE_BAND_T", "16"))
109
+
110
+ def __init__(self, seam, top_k, renormalize, shared, host):
111
+ super().__init__()
112
+ self.seam = seam
113
+ self.top_k = top_k
114
+ self.renormalize = renormalize
115
+ self.shared = shared
116
+ self.host = host
117
+ self.host_internal = bool(getattr(host, "is_internal_router", False))
118
+ self.is_internal_router = False # the host block branches on this
119
+ self._frt_host_serving = True # prefill band runs through host
120
+ self.idx = len(_SEATS_BY_IDX)
121
+ _SEATS_BY_IDX[self.idx] = self
122
+
123
+ def run(self, hidden_states, router_logits, top_idx, top_w):
124
+ if hidden_states.shape[0] > self.BAND_T and self.host is not None:
125
+ logits = (hidden_states if self.host_internal else router_logits)
126
+ return self.host(hidden_states=hidden_states,
127
+ router_logits=logits)
128
+ out = self.seam(hidden_states, top_idx, top_w)
129
+ if self.shared is not None:
130
+ out = out + self.shared(hidden_states)
131
+ return out.to(hidden_states.dtype)
132
+
133
+ def forward(self, hidden_states, router_logits):
134
+ # routing stays in the traced region so inductor fuses the
135
+ # softmax/topk/renormalize chain; the opaque op keeps only what
136
+ # tracing would freeze — the band branch and the bank walk
137
+ w = torch.softmax(router_logits.float(), dim=-1)
138
+ tw, ti = torch.topk(w, self.top_k, dim=-1)
139
+ if self.renormalize:
140
+ tw = tw / tw.sum(dim=-1, keepdim=True)
141
+ return _vllm_moe_seat_op(hidden_states, router_logits, ti, tw,
142
+ self.idx)
143
+
144
+
145
+ class _SlabbedHeadMethod:
146
+ """Stands in for the LM head's quant method: the engine computes
147
+ logits through ``quant_method.apply``, never module forward."""
148
+
149
+ def __init__(self, seams, orig):
150
+ self.seams = seams
151
+ self.orig = orig
152
+
153
+ def apply(self, layer, x, bias=None):
154
+ xb = x.to(torch.bfloat16)
155
+ y = torch.cat([s(xb) for s in self.seams], dim=-1)
156
+ if bias is not None:
157
+ y = y + bias
158
+ return y.to(x.dtype)
159
+
160
+ def __getattr__(self, name):
161
+ return getattr(self.orig, name)
162
+
163
+
164
+ def _is_projection(module) -> bool:
165
+ w = getattr(module, "weight", None)
166
+ return (isinstance(w, torch.Tensor) and w.dim() == 2
167
+ and hasattr(module, "quant_method"))
168
+
169
+
170
+ def _expert_holder(module):
171
+ for _, child in module.named_modules():
172
+ if torch.is_tensor(getattr(child, "w13_weight", None)):
173
+ return child
174
+ return None
175
+
176
+
177
+ class _NoSeats:
178
+ """The handle shape for a host where nothing could be seated.
179
+
180
+ Every seat refusing is a normal outcome, not an error: the hub may be
181
+ unreachable, this architecture may have no build, the memory budget
182
+ may leave no room. What must not happen is the engine dying because
183
+ its accelerator was absent — a server that fails to start is worse
184
+ than one that starts unaccelerated. So the refusals are reported and
185
+ the host is handed back untouched, with a handle of the same shape so
186
+ callers need no special case.
187
+ """
188
+
189
+ def __init__(self, refused, reverts):
190
+ self.notes = {"refused": refused, "head_slabs": 0, "seated": 0}
191
+ self._reverts = list(reverts)
192
+
193
+ def detach(self):
194
+ for fn in reversed(self._reverts):
195
+ fn()
196
+ self._reverts.clear()
197
+
198
+ def report(self):
199
+ return {}
200
+
201
+ def summary(self):
202
+ return {"seams": 0, "guarded_calls": 0, "fallbacks": 0,
203
+ "seams_fell_back": [], "seams_self_detached": [],
204
+ "seams_never_called": [], "clean": True,
205
+ "refused": len(self.notes["refused"])}
206
+
207
+
208
+ def attach_engine(model, *, seats=DENSE_SEAT_SUFFIXES, experts=True,
209
+ head=True, use_gemv=None, verbose=True, strict=False):
210
+ """Seat a vLLM model: dense projections, expert banks, LM head.
211
+
212
+ Call between weight load and the engine's first trace (see
213
+ :func:`install_load_hook`). Returns the ``swap.attach`` handle;
214
+ ``handle.detach()`` restores the module tree, the expert modules
215
+ and the head's quant method.
216
+ """
217
+ if use_gemv is None:
218
+ cc = torch.cuda.get_device_capability()
219
+ use_gemv = cc >= (12, 0) # the warp-split GEMV entry's own arch
220
+ if not use_gemv:
221
+ orig_init = _linear.LinearProjNvfp4Dynamic.__init__
222
+
223
+ def _init(self, *a, **kw):
224
+ orig_init(self, *a, **kw)
225
+ self._gemv = None
226
+ _linear.LinearProjNvfp4Dynamic.__init__ = _init
227
+
228
+ swaps: dict[str, nn.Module] = {}
229
+ reverts: list = []
230
+ refused: list = []
231
+ modules = dict(model.named_modules())
232
+
233
+ # dense projections, smallest first: on tight cards early frees
234
+ # make room for the big binds
235
+ targets = [(n, m) for n, m in modules.items()
236
+ if any(n.endswith(s) for s in seats) and _is_projection(m)]
237
+ targets.sort(key=lambda t: t[1].weight.numel())
238
+ for name, mod in targets:
239
+ try:
240
+ seam, _ = _linear.bind_proj_seam({"w": mod.weight.data})
241
+ swaps[name] = _ProjSeat(seam)
242
+ except Exception as e:
243
+ refused.append((name, repr(e)[:120]))
244
+
245
+ if experts:
246
+ impl = (_experts_w4a4 if use_gemv else _experts_w4a16)
247
+ for name, mod in modules.items():
248
+ if not name.endswith("mlp.experts"):
249
+ continue
250
+ holder = _expert_holder(mod)
251
+ if holder is None:
252
+ continue
253
+ try:
254
+ seam, _ = impl.bind_experts_seam(
255
+ {"gate_up_proj": holder.w13_weight.data,
256
+ "down_proj": holder.w2_weight.data},
257
+ act_fn=torch.nn.functional.silu)
258
+ top_k = (getattr(mod, "top_k", None)
259
+ or getattr(getattr(mod, "moe_config", None),
260
+ "experts_per_token", None) or 8)
261
+ renorm = getattr(mod, "renormalize", None)
262
+ parent = modules[name.rsplit(".experts", 1)[0]]
263
+ swaps[name] = _MoESeat(
264
+ seam, int(top_k),
265
+ True if renorm is None else bool(renorm),
266
+ getattr(parent, "shared_expert", None), mod)
267
+ except Exception as e:
268
+ refused.append((name, repr(e)[:120]))
269
+
270
+ head_slabs = 0
271
+ if head:
272
+ lm = next((m for n, m in modules.items()
273
+ if n.endswith("lm_head")
274
+ and isinstance(getattr(m, "weight", None), torch.Tensor)),
275
+ None)
276
+ if lm is not None:
277
+ try:
278
+ rows = lm.weight.shape[0]
279
+ slab = -(-rows // 4) // 64 * 64
280
+ seams = [
281
+ _linear.bind_proj_seam(
282
+ {"w": lm.weight.data[lo:lo + slab]})[0]
283
+ for lo in range(0, rows, slab)]
284
+ orig_method = lm.quant_method
285
+ lm.quant_method = _SlabbedHeadMethod(seams, orig_method)
286
+ reverts.append(
287
+ lambda lm=lm, m=orig_method: setattr(
288
+ lm, "quant_method", m))
289
+ head_slabs = len(seams)
290
+ except Exception as e:
291
+ refused.append(("lm_head", repr(e)[:120]))
292
+
293
+ model.eval()
294
+ if not swaps:
295
+ if strict:
296
+ raise RuntimeError(
297
+ "refused: no seat could be bound on this host (%d refusals; "
298
+ "first: %s). Pass strict=False to let the engine start "
299
+ "unaccelerated." % (len(refused),
300
+ refused[0][1] if refused else "none"))
301
+ if verbose:
302
+ print(f"[structures.vllm] 0 seats, {len(refused)} refused — "
303
+ f"host runs unmodified", flush=True)
304
+ for name, why in refused[:3]:
305
+ print(f"[structures.vllm] {name}: {why}", flush=True)
306
+ handle = _NoSeats(refused, reverts)
307
+ handle.notes["refused"] = refused
308
+ return handle
309
+ handle = _swap.attach(model, swaps, revert=reverts)
310
+ if verbose:
311
+ print(f"[structures.vllm] {len(swaps)} seats "
312
+ f"({head_slabs} head slabs), {len(refused)} refused",
313
+ flush=True)
314
+ handle.notes = {"refused": refused, "head_slabs": head_slabs}
315
+ return handle
316
+
317
+
318
+ #: every handle :func:`install_load_hook` has seated, in order. A caller
319
+ #: that wants certainty rather than a log line asserts on this after the
320
+ #: engine is up: empty means the hook never fired.
321
+ _ATTACHED: list = []
322
+
323
+
324
+ def attached() -> list:
325
+ """The handles seated so far. Empty after an engine came up means the
326
+ patch never reached the process that loaded the model — see
327
+ :func:`install_load_hook` on the start method."""
328
+ return list(_ATTACHED)
329
+
330
+
331
+ def _patch_would_not_survive() -> str | None:
332
+ """Why a patch made here would not exist in the worker process.
333
+
334
+ The engine starts its worker with ``fork`` by default, which
335
+ inherits this patch, and that is why the four-line integration
336
+ works at all. But it switches to ``spawn`` under conditions the
337
+ caller can walk into without noticing — a spawned worker re-imports
338
+ the engine from scratch and the patch is simply not there.
339
+
340
+ The failure is silent: patching succeeds here, the hook never fires,
341
+ and the run comes out at baseline speed with no error anywhere. That
342
+ is the one outcome this layer refuses to produce, so it is checked
343
+ before the caller builds an engine rather than discovered afterwards
344
+ from a missing log line.
345
+ """
346
+ if os.environ.get("VLLM_ENABLE_V1_MULTIPROCESSING", "1") == "0":
347
+ return None # the engine runs in this process
348
+ if os.environ.get("VLLM_WORKER_MULTIPROC_METHOD") == "spawn":
349
+ return "VLLM_WORKER_MULTIPROC_METHOD is set to 'spawn'"
350
+ if torch.cuda.is_initialized():
351
+ return ("CUDA is already initialized in this process, and the "
352
+ "engine forces 'spawn' when it is")
353
+ return None
354
+
355
+
356
+ def install_load_hook(*, on_attached=None, allow_spawn=False,
357
+ **attach_kwargs):
358
+ """Patch every importable vLLM model-runner so :func:`attach_engine`
359
+ runs after weights load and before the engine's first trace. Set
360
+ ``VLLM_DISABLE_COMPILE_CACHE=1``: the engine's compile cache key
361
+ does not see the module tree, and a stale artifact resolves
362
+ parameters that the seats replaced.
363
+
364
+ Call this before touching CUDA. The engine forks its worker by
365
+ default, which is what carries this patch into the process that
366
+ loads the model, but it switches to spawning one the moment CUDA is
367
+ already initialized here — and a spawned worker re-imports the
368
+ engine without the patch. Nothing raises in that case: the seats
369
+ simply never go in and the run comes out at baseline. So the
370
+ condition is refused here instead, with the two ways out. Pass
371
+ ``allow_spawn=True`` to proceed anyway.
372
+ """
373
+ import importlib
374
+ import os
375
+
376
+ # Find the runners before changing anything: "there is no host here"
377
+ # is the more fundamental refusal, and a caller without vLLM should
378
+ # hear that rather than a lecture about start methods. Nothing is
379
+ # mutated until both questions have been answered.
380
+ found = []
381
+ for modname in ("vllm.v1.worker.gpu.model_runner",
382
+ "vllm.v1.worker.gpu_model_runner",
383
+ "vllm.v2.worker.gpu_model_runner"):
384
+ try:
385
+ module = importlib.import_module(modname)
386
+ except ImportError:
387
+ continue
388
+ runner = getattr(module, "GPUModelRunner", None)
389
+ if runner is None or not hasattr(runner, "load_model"):
390
+ continue
391
+ found.append((modname, runner))
392
+ if not found:
393
+ raise RuntimeError(
394
+ "refused: no vLLM model runner found to hook; the engine "
395
+ "layout is outside this adapter's profile")
396
+
397
+ lost = None if allow_spawn else _patch_would_not_survive()
398
+ if lost is not None:
399
+ raise RuntimeError(
400
+ "refused: this patch would not reach the process that loads "
401
+ "the model — " + lost + ".\n"
402
+ "The engine would start its worker with 'spawn', which "
403
+ "re-imports it from scratch, so the seats would never be "
404
+ "installed and the run would come out at baseline speed "
405
+ "with nothing raised anywhere.\n"
406
+ "Either call install_load_hook() before anything touches "
407
+ "CUDA, or run the engine in this process with "
408
+ "VLLM_ENABLE_V1_MULTIPROCESSING=0. Pass allow_spawn=True to "
409
+ "proceed regardless.")
410
+
411
+ os.environ.setdefault("VLLM_DISABLE_COMPILE_CACHE", "1")
412
+ patched = []
413
+ for modname, runner in found:
414
+ orig = runner.load_model
415
+
416
+ def load_model(self, *a, __orig=orig, **kw):
417
+ __orig(self, *a, **kw)
418
+ handle = attach_engine(self.model, **attach_kwargs)
419
+ _ATTACHED.append(handle)
420
+ if on_attached is not None:
421
+ on_attached(handle)
422
+ runner.load_model = load_model
423
+ patched.append(modname)
424
+ return patched
@@ -0,0 +1,85 @@
1
+ """Form adjudication: candidates declared, facts qualify, measurement seats.
2
+
3
+ No form is ever hard-coded to a shape or a device. A seat family
4
+ declares its candidates; qualification predicates read device-neutral
5
+ facts; the qualified candidates are timed on the calibrated shapes and
6
+ the measurement seats the winner. Precision precedes speed twice over:
7
+ a candidate enters only above the parity floor (0.99 — the house
8
+ tradeoff-and-calibrate line), and within the win margin the
9
+ higher-precision form takes the seat, so noise never flips one.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+ from typing import Any, Callable, Sequence
16
+
17
+ import torch
18
+
19
+ #: the house parity floor: at or above this, a form is inside the
20
+ #: tradeoff-and-calibration band and speed may decide
21
+ PARITY_FLOOR = 0.99
22
+
23
+
24
+ @dataclass
25
+ class FormCandidate:
26
+ name: str
27
+ build: Callable[[], Any]
28
+ run: Callable[[Any], Any]
29
+ qualify: Callable[[], bool] = lambda: True
30
+ #: tie-break inside the win margin: lower = higher precision
31
+ precision_rank: int = 0
32
+
33
+
34
+ def adjudicate(seat: str, candidates: Sequence[FormCandidate], *,
35
+ margin: float = 0.03, iters: int = 30,
36
+ notes: dict | None = None):
37
+ """Measure the qualified candidates; return (winner_name, built).
38
+
39
+ Every candidate's outcome — unqualified, refused (with reason), or
40
+ its measured milliseconds — lands in ``notes["adjudication"]``.
41
+ A candidate that cannot build or run loses by default. Returns
42
+ ``(None, None)`` when nothing survives.
43
+ """
44
+ entries, built = [], {}
45
+ for cand in candidates:
46
+ try:
47
+ if not cand.qualify():
48
+ entries.append({"name": cand.name,
49
+ "outcome": "unqualified"})
50
+ continue
51
+ built[cand.name] = cand.build()
52
+ except Exception as exc: # noqa: BLE001 — loses
53
+ entries.append({"name": cand.name,
54
+ "outcome": f"refused: {str(exc)[:80]}"})
55
+ timed = []
56
+ for cand in candidates:
57
+ if cand.name not in built:
58
+ continue
59
+ b = built[cand.name]
60
+ with torch.no_grad():
61
+ for _ in range(5):
62
+ cand.run(b)
63
+ torch.cuda.synchronize()
64
+ start = torch.cuda.Event(True)
65
+ end = torch.cuda.Event(True)
66
+ start.record()
67
+ for _ in range(iters):
68
+ cand.run(b)
69
+ end.record()
70
+ torch.cuda.synchronize()
71
+ ms = start.elapsed_time(end) / iters
72
+ timed.append((ms, cand.precision_rank, cand.name))
73
+ entries.append({"name": cand.name, "ms": round(ms, 4)})
74
+ winner = None
75
+ if timed:
76
+ timed.sort()
77
+ best = timed[0][0]
78
+ close = [t for t in timed if t[0] <= best * (1.0 + margin)]
79
+ close.sort(key=lambda t: (t[1], t[0]))
80
+ winner = close[0][2]
81
+ if notes is not None:
82
+ notes.setdefault("adjudication", []).append(
83
+ {"seat": seat, "winner": winner, "margin": margin,
84
+ "entries": entries})
85
+ return winner, (built.get(winner) if winner else None)