@mlx-bun/inference 0.0.0-stage → 1.0.0

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 (467) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1475 -2
  3. package/THIRD_PARTY_NOTICES.md +3404 -0
  4. package/dist/native/libmlx_bun_ane.dylib +0 -0
  5. package/dist/native/libmlx_bun_expert_io.dylib +0 -0
  6. package/dist/native/mlx-bun-frame-extract +0 -0
  7. package/examples/fit-model.ts +25 -0
  8. package/examples/qwen3-forward.ts +42 -0
  9. package/examples/qwen3-generate.ts +28 -0
  10. package/examples/trellis-expand.ts +23 -0
  11. package/package.json +129 -4
  12. package/src/adapters/available.ts +110 -0
  13. package/src/adapters/index.ts +3 -0
  14. package/src/adapters/manager.ts +316 -0
  15. package/src/artifacts/auxiliary-files.ts +45 -0
  16. package/src/artifacts/config-dialects.ts +94 -0
  17. package/src/artifacts/config.ts +341 -0
  18. package/src/artifacts/draft-vocabulary.ts +30 -0
  19. package/src/artifacts/expert-offload-build.ts +75 -0
  20. package/src/artifacts/expert-offload.ts +89 -0
  21. package/src/artifacts/experts/index.ts +3 -0
  22. package/src/artifacts/experts/io.ts +386 -0
  23. package/src/artifacts/experts/residency.ts +1134 -0
  24. package/src/artifacts/experts/usage.ts +415 -0
  25. package/src/artifacts/fingerprint.ts +44 -0
  26. package/src/artifacts/glm52/container.ts +305 -0
  27. package/src/artifacts/glm52/expert-layout.ts +249 -0
  28. package/src/artifacts/glm52/index.ts +7 -0
  29. package/src/artifacts/glm52/kernel-layout.ts +124 -0
  30. package/src/artifacts/glm52/layout.ts +152 -0
  31. package/src/artifacts/glm52/memory.ts +500 -0
  32. package/src/artifacts/glm52/resident-weights.ts +135 -0
  33. package/src/artifacts/glm52/weights.ts +503 -0
  34. package/src/artifacts/glm52-config.ts +262 -0
  35. package/src/artifacts/identity.ts +94 -0
  36. package/src/artifacts/index.ts +15 -0
  37. package/src/artifacts/mmap.ts +90 -0
  38. package/src/artifacts/safetensors-writer.ts +249 -0
  39. package/src/artifacts/safetensors.ts +164 -0
  40. package/src/artifacts/turboquant-fold.ts +42 -0
  41. package/src/artifacts/weight-audit.ts +43 -0
  42. package/src/artifacts/weights.ts +255 -0
  43. package/src/contracts/index.ts +2 -0
  44. package/src/contracts/mlx/attention.ts +8 -0
  45. package/src/contracts/mlx/cache.ts +510 -0
  46. package/src/contracts/mlx/checkpoint.ts +10 -0
  47. package/src/contracts/mlx/draft-checkpoint.ts +14 -0
  48. package/src/contracts/mlx/draft-projection.ts +27 -0
  49. package/src/contracts/mlx/draft-target.ts +84 -0
  50. package/src/contracts/mlx/drafter.ts +176 -0
  51. package/src/contracts/mlx/forward-work.ts +23 -0
  52. package/src/contracts/mlx/glm52-weights.ts +25 -0
  53. package/src/contracts/mlx/graph.ts +309 -0
  54. package/src/contracts/mlx/head.ts +6 -0
  55. package/src/contracts/mlx/index.ts +15 -0
  56. package/src/contracts/mlx/kv-codec.ts +9 -0
  57. package/src/contracts/mlx/media.ts +113 -0
  58. package/src/contracts/mlx/positions.ts +16 -0
  59. package/src/contracts/mlx/row-table.ts +2 -0
  60. package/src/contracts/mlx/trainable.ts +103 -0
  61. package/src/contracts/mlx/whisper.ts +96 -0
  62. package/src/contracts/portable/composition.ts +106 -0
  63. package/src/contracts/portable/denoising.ts +17 -0
  64. package/src/contracts/portable/execution.ts +79 -0
  65. package/src/contracts/portable/generation.ts +103 -0
  66. package/src/contracts/portable/graph.ts +80 -0
  67. package/src/contracts/portable/index.ts +15 -0
  68. package/src/contracts/portable/memory-plan.ts +26 -0
  69. package/src/contracts/portable/object-cache.ts +8 -0
  70. package/src/contracts/portable/prefill.ts +63 -0
  71. package/src/contracts/portable/prefix-cache.ts +33 -0
  72. package/src/contracts/portable/prompt.ts +2 -0
  73. package/src/contracts/portable/resources.ts +25 -0
  74. package/src/contracts/portable/rollback.ts +7 -0
  75. package/src/contracts/portable/sampling.ts +44 -0
  76. package/src/contracts/portable/scheduling.ts +54 -0
  77. package/src/contracts/portable/token-delivery.ts +4 -0
  78. package/src/contracts/portable/tsconfig.json +14 -0
  79. package/src/embeddings/index.ts +1 -0
  80. package/src/embeddings/text.ts +85 -0
  81. package/src/execution/admission.ts +59 -0
  82. package/src/execution/batch-group.ts +1240 -0
  83. package/src/execution/batch-types.ts +225 -0
  84. package/src/execution/cancellation.ts +38 -0
  85. package/src/execution/continuation-request.ts +45 -0
  86. package/src/execution/continuation-types.ts +10 -0
  87. package/src/execution/coordinator.ts +70 -0
  88. package/src/execution/denoising-group.ts +180 -0
  89. package/src/execution/engine.ts +56 -0
  90. package/src/execution/fill-group.ts +505 -0
  91. package/src/execution/fit.ts +184 -0
  92. package/src/execution/gateway-binding.ts +178 -0
  93. package/src/execution/grammar-group.ts +224 -0
  94. package/src/execution/index.ts +18 -0
  95. package/src/execution/methods.ts +75 -0
  96. package/src/execution/mixed-iteration.ts +96 -0
  97. package/src/execution/plan.ts +65 -0
  98. package/src/execution/prefill-cohort.ts +100 -0
  99. package/src/execution/prefill-rows.ts +263 -0
  100. package/src/execution/request-policy.ts +19 -0
  101. package/src/execution/row-sampling.ts +49 -0
  102. package/src/execution/scheduler.ts +92 -0
  103. package/src/execution/session.ts +247 -0
  104. package/src/execution/speculative-group.ts +797 -0
  105. package/src/execution/tasks.ts +44 -0
  106. package/src/generation/autoregressive.ts +840 -0
  107. package/src/generation/bindings/autoregressive.ts +104 -0
  108. package/src/generation/bindings/denoising.ts +60 -0
  109. package/src/generation/bindings/prefill-policy.ts +36 -0
  110. package/src/generation/checkpoint-identity.ts +60 -0
  111. package/src/generation/denoising.ts +85 -0
  112. package/src/generation/diffusion.ts +583 -0
  113. package/src/generation/fill/echo-index.ts +188 -0
  114. package/src/generation/fill/index.ts +3 -0
  115. package/src/generation/fill/proposal.ts +69 -0
  116. package/src/generation/fill/schema-rows.ts +506 -0
  117. package/src/generation/fill/session.ts +464 -0
  118. package/src/generation/fill/tool-boundary.ts +103 -0
  119. package/src/generation/fill-append.ts +58 -0
  120. package/src/generation/generate.ts +44 -0
  121. package/src/generation/grammar-step.ts +39 -0
  122. package/src/generation/index.ts +14 -0
  123. package/src/generation/phase-dispatch.ts +115 -0
  124. package/src/generation/prefill.ts +33 -0
  125. package/src/generation/result.ts +49 -0
  126. package/src/generation/scopes.ts +157 -0
  127. package/src/generation/speculative/acceptance.ts +41 -0
  128. package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
  129. package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
  130. package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
  131. package/src/generation/speculative/bindings/binding.ts +106 -0
  132. package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
  133. package/src/generation/speculative/bindings/draft-target.ts +10 -0
  134. package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
  135. package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
  136. package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
  137. package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
  138. package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
  139. package/src/generation/speculative/cache-identity.ts +16 -0
  140. package/src/generation/speculative/draft-kind.ts +149 -0
  141. package/src/generation/speculative/draft-policy.ts +41 -0
  142. package/src/generation/speculative/draft-registry.ts +118 -0
  143. package/src/generation/speculative/draft-vocab.ts +110 -0
  144. package/src/generation/speculative/index.ts +17 -0
  145. package/src/generation/speculative/output.ts +49 -0
  146. package/src/generation/speculative/round.ts +307 -0
  147. package/src/generation/speculative/source.ts +144 -0
  148. package/src/generation/speculative/sources/assistant-source.ts +64 -0
  149. package/src/generation/speculative/sources/deepspec-source.ts +70 -0
  150. package/src/generation/speculative/sources/dflash-source.ts +84 -0
  151. package/src/generation/speculative/sources/dflash2-source.ts +86 -0
  152. package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
  153. package/src/generation/speculative/sources/ngram-source.ts +165 -0
  154. package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
  155. package/src/generation/speculative/sources/two-model.ts +75 -0
  156. package/src/generation/types.ts +194 -0
  157. package/src/index.ts +20 -0
  158. package/src/input/audio/audiotoolbox.ts +112 -0
  159. package/src/input/audio/decode.ts +133 -0
  160. package/src/input/audio/features.ts +202 -0
  161. package/src/input/audio/index.ts +6 -0
  162. package/src/input/audio/transcode.ts +67 -0
  163. package/src/input/audio/whisper-mel.ts +185 -0
  164. package/src/input/audio/whisper-tokenizer.ts +238 -0
  165. package/src/input/chat-template.ts +389 -0
  166. package/src/input/index.ts +6 -0
  167. package/src/input/media-fetch.ts +439 -0
  168. package/src/input/packed-mixed.ts +43 -0
  169. package/src/input/special-tokens.ts +75 -0
  170. package/src/input/token-groups.ts +49 -0
  171. package/src/input/tokenizer.ts +336 -0
  172. package/src/input/tool-call.ts +689 -0
  173. package/src/input/vision/index.ts +3 -0
  174. package/src/input/vision/preprocess.ts +247 -0
  175. package/src/input/vision/prompt.ts +398 -0
  176. package/src/input/vision/qwen3vl-preprocess.ts +409 -0
  177. package/src/input/vision/qwen3vl-prompt.ts +237 -0
  178. package/src/input/vision/video-frames.ts +100 -0
  179. package/src/kernels/ane/fill.ts +101 -0
  180. package/src/kernels/ane/linear.ts +416 -0
  181. package/src/kernels/attention/flash.ts +694 -0
  182. package/src/kernels/attention/kvq4-decode.ts +155 -0
  183. package/src/kernels/attention/masks.ts +40 -0
  184. package/src/kernels/attention/multi-query.ts +193 -0
  185. package/src/kernels/attention/paged.ts +130 -0
  186. package/src/kernels/delta/gated.ts +306 -0
  187. package/src/kernels/delta/index.ts +3 -0
  188. package/src/kernels/delta/silu-rms-heads.ts +69 -0
  189. package/src/kernels/delta/state.ts +74 -0
  190. package/src/kernels/gated-rms-norm.ts +64 -0
  191. package/src/kernels/glm52/layout.ts +37 -0
  192. package/src/kernels/glm52/streamed-experts.ts +360 -0
  193. package/src/kernels/logits.ts +38 -0
  194. package/src/kernels/quantization/affine-row.ts +154 -0
  195. package/src/kernels/quantization/affine-rows.ts +36 -0
  196. package/src/kernels/quantization/affine-split-mma.ts +144 -0
  197. package/src/kernels/quantization/affine3-mma.ts +117 -0
  198. package/src/kernels/quantization/affine3-rows.ts +103 -0
  199. package/src/kernels/quantization/affine4-mma.ts +113 -0
  200. package/src/kernels/quantization/affine4-rows.ts +102 -0
  201. package/src/kernels/residual-rms-norm.ts +65 -0
  202. package/src/kernels/sampling/index.ts +2 -0
  203. package/src/kernels/sampling/normalized-argmax.ts +116 -0
  204. package/src/kernels/sampling/token-bitmask.ts +52 -0
  205. package/src/kernels/softcap.ts +35 -0
  206. package/src/kernels/trellis/codebook.ts +123 -0
  207. package/src/kernels/trellis/down-codebook.ts +184 -0
  208. package/src/kernels/trellis/down-factored.ts +180 -0
  209. package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
  210. package/src/kernels/trellis/down-mma-wide.ts +195 -0
  211. package/src/kernels/trellis/down-mma.ts +145 -0
  212. package/src/kernels/trellis/expand.ts +42 -0
  213. package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
  214. package/src/kernels/trellis/gate-up-codebook.ts +137 -0
  215. package/src/kernels/trellis/gate-up-factored.ts +197 -0
  216. package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
  217. package/src/kernels/trellis/gate-up-mma.ts +148 -0
  218. package/src/kernels/trellis/gate-up.ts +248 -0
  219. package/src/kernels/trellis/geometry.ts +22 -0
  220. package/src/kernels/trellis/index.ts +103 -0
  221. package/src/kernels/trellis/launch.ts +12 -0
  222. package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
  223. package/src/kernels/trellis/mixed-gate-up.ts +146 -0
  224. package/src/kernels/trellis/qualified.ts +34 -0
  225. package/src/kernels/trellis/reduce.ts +150 -0
  226. package/src/kernels/trellis/scatter.ts +407 -0
  227. package/src/kernels/trellis/splitk-prefill.ts +152 -0
  228. package/src/kernels/trellis/tiled-prefill.ts +131 -0
  229. package/src/kernels/trellis/vector-expand.ts +58 -0
  230. package/src/kernels/trellis/wide-prefill.ts +89 -0
  231. package/src/kernels/turboquant/decode.ts +82 -0
  232. package/src/kernels/turboquant/index.ts +5 -0
  233. package/src/kernels/turboquant/inverse.ts +122 -0
  234. package/src/kernels/turboquant/ops.ts +860 -0
  235. package/src/kernels/turboquant/signs.ts +130 -0
  236. package/src/kernels/turboquant/tables.ts +124 -0
  237. package/src/layers/affine-verify-linear.ts +75 -0
  238. package/src/layers/ane-prefill-split.ts +283 -0
  239. package/src/layers/compiled-normalization.ts +41 -0
  240. package/src/layers/delta-heads.ts +23 -0
  241. package/src/layers/dense-embedding.ts +29 -0
  242. package/src/layers/dense-linear.ts +32 -0
  243. package/src/layers/dequantized-linear.ts +72 -0
  244. package/src/layers/folded-attention.ts +42 -0
  245. package/src/layers/geglu.ts +130 -0
  246. package/src/layers/helpers.ts +7 -0
  247. package/src/layers/index.ts +36 -0
  248. package/src/layers/kv4-decode-attention.ts +15 -0
  249. package/src/layers/layer-norm.ts +24 -0
  250. package/src/layers/loading.ts +92 -0
  251. package/src/layers/lora.ts +150 -0
  252. package/src/layers/normalization.ts +9 -0
  253. package/src/layers/prefix-layout.ts +33 -0
  254. package/src/layers/quantized-append-attention.ts +32 -0
  255. package/src/layers/quantized-attention.ts +353 -0
  256. package/src/layers/quantized-embedding.ts +39 -0
  257. package/src/layers/quantized-linear.ts +137 -0
  258. package/src/layers/quantized-switch-linear.ts +37 -0
  259. package/src/layers/qwen-append.ts +12 -0
  260. package/src/layers/qwen-mrope.ts +238 -0
  261. package/src/layers/rope-2d.ts +110 -0
  262. package/src/layers/rope.ts +389 -0
  263. package/src/layers/swiglu.ts +23 -0
  264. package/src/layers/trellis-codebook-down.ts +52 -0
  265. package/src/layers/trellis-codebook-gate-up.ts +42 -0
  266. package/src/layers/trellis-down.ts +117 -0
  267. package/src/layers/trellis-gate-up.ts +71 -0
  268. package/src/layers/trellis-linear.ts +211 -0
  269. package/src/layers/trellis-prompt.ts +102 -0
  270. package/src/layers/trellis-reduce-gate-up.ts +73 -0
  271. package/src/layers/trellis-scatter-down.ts +98 -0
  272. package/src/models/ane-install.ts +74 -0
  273. package/src/models/audio/conformer.ts +656 -0
  274. package/src/models/cache-composition.ts +44 -0
  275. package/src/models/capabilities.ts +89 -0
  276. package/src/models/chat-template.ts +10 -0
  277. package/src/models/composition.ts +107 -0
  278. package/src/models/diffusion-gemma/experts.ts +59 -0
  279. package/src/models/diffusion-gemma/family.ts +21 -0
  280. package/src/models/diffusion-gemma/model.ts +900 -0
  281. package/src/models/diffusion-gemma/vision.ts +549 -0
  282. package/src/models/drafter-loaders.ts +49 -0
  283. package/src/models/drafters.ts +39 -0
  284. package/src/models/factory.ts +351 -0
  285. package/src/models/families.ts +70 -0
  286. package/src/models/family.ts +125 -0
  287. package/src/models/gemma4/assistant-target.ts +46 -0
  288. package/src/models/gemma4/assistant.ts +327 -0
  289. package/src/models/gemma4/blocks.ts +755 -0
  290. package/src/models/gemma4/compiled-decode.ts +452 -0
  291. package/src/models/gemma4/family.ts +39 -0
  292. package/src/models/gemma4/gemma4-12b.ts +677 -0
  293. package/src/models/gemma4/gemma4-26b.ts +544 -0
  294. package/src/models/gemma4/gemma4-e4b.ts +599 -0
  295. package/src/models/gemma4/graphs.ts +147 -0
  296. package/src/models/gemma4/media.ts +88 -0
  297. package/src/models/gemma4/model.ts +860 -0
  298. package/src/models/gemma4/serving-blocks.ts +72 -0
  299. package/src/models/gemma4/trainable.ts +186 -0
  300. package/src/models/gemma4/written-blocks.ts +232 -0
  301. package/src/models/gemma4/written-forward.ts +159 -0
  302. package/src/models/gemma4/written-input.ts +29 -0
  303. package/src/models/glm52/atlas.ts +507 -0
  304. package/src/models/glm52/coupling.ts +388 -0
  305. package/src/models/glm52/dsa.ts +601 -0
  306. package/src/models/glm52/family.ts +20 -0
  307. package/src/models/glm52/mla.ts +812 -0
  308. package/src/models/glm52/model.ts +1127 -0
  309. package/src/models/glm52/moe.ts +223 -0
  310. package/src/models/glm52/mtp.ts +60 -0
  311. package/src/models/glm52/pilot.ts +537 -0
  312. package/src/models/glm52/reference.ts +445 -0
  313. package/src/models/glm52/residency.ts +557 -0
  314. package/src/models/glm52/streamed-experts.ts +447 -0
  315. package/src/models/graph.ts +59 -0
  316. package/src/models/implementation.ts +230 -0
  317. package/src/models/index.ts +15 -0
  318. package/src/models/media-input.ts +15 -0
  319. package/src/models/memory-plan.ts +39 -0
  320. package/src/models/minicpm5/family.ts +29 -0
  321. package/src/models/minicpm5/model.ts +394 -0
  322. package/src/models/minicpm5/trainable.ts +90 -0
  323. package/src/models/profile.ts +337 -0
  324. package/src/models/qwen/blocks.ts +658 -0
  325. package/src/models/qwen/checkpoint.ts +190 -0
  326. package/src/models/qwen/families.ts +57 -0
  327. package/src/models/qwen/media-input.ts +161 -0
  328. package/src/models/qwen/mtp.ts +101 -0
  329. package/src/models/qwen/qwen3-moe.ts +355 -0
  330. package/src/models/qwen/qwen3.ts +323 -0
  331. package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
  332. package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
  333. package/src/models/qwen/qwen3_5.ts +433 -0
  334. package/src/models/qwen/written-layers.ts +285 -0
  335. package/src/models/row-table.ts +1 -0
  336. package/src/models/runtime.ts +73 -0
  337. package/src/models/speculative/deepspec-artifact.ts +10 -0
  338. package/src/models/speculative/deepspec.ts +744 -0
  339. package/src/models/speculative/dflash.ts +553 -0
  340. package/src/models/speculative/dflash2-artifact.ts +9 -0
  341. package/src/models/speculative/dflash2-operations.ts +1 -0
  342. package/src/models/speculative/dflash2.ts +576 -0
  343. package/src/models/speculative/loader.ts +29 -0
  344. package/src/models/support.ts +79 -0
  345. package/src/models/universal/archs.ts +365 -0
  346. package/src/models/universal/blocks.ts +479 -0
  347. package/src/models/universal/dense.ts +255 -0
  348. package/src/models/universal/family.ts +17 -0
  349. package/src/models/vision/qwen3vl.ts +566 -0
  350. package/src/models/vision/siglip.ts +520 -0
  351. package/src/models/vision/unified.ts +176 -0
  352. package/src/models/whisper/family.ts +19 -0
  353. package/src/models/whisper/fast.ts +734 -0
  354. package/src/models/whisper/model.ts +430 -0
  355. package/src/runtime/compiled-trace.ts +16 -0
  356. package/src/runtime/config.ts +77 -0
  357. package/src/runtime/expert-trace.ts +71 -0
  358. package/src/runtime/graph-forward.ts +65 -0
  359. package/src/runtime/integers.ts +6 -0
  360. package/src/runtime/native.ts +16 -0
  361. package/src/runtime/resources.ts +53 -0
  362. package/src/runtime/row-operations.ts +42 -0
  363. package/src/runtime/trace.ts +161 -0
  364. package/src/sampling/curve.ts +122 -0
  365. package/src/sampling/draft.ts +55 -0
  366. package/src/sampling/extras.ts +45 -0
  367. package/src/sampling/filters.ts +134 -0
  368. package/src/sampling/grammar.ts +630 -0
  369. package/src/sampling/hlg.ts +290 -0
  370. package/src/sampling/index.ts +9 -0
  371. package/src/sampling/processors.ts +142 -0
  372. package/src/sampling/sampler.ts +120 -0
  373. package/src/sampling/step.ts +343 -0
  374. package/src/sampling/types.ts +161 -0
  375. package/src/sampling/window-filters.ts +46 -0
  376. package/src/scoring/decisions.ts +96 -0
  377. package/src/scoring/full-sequence.ts +103 -0
  378. package/src/scoring/index.ts +9 -0
  379. package/src/scoring/kl.ts +29 -0
  380. package/src/scoring/perplexity.ts +165 -0
  381. package/src/scoring/response-logps.ts +65 -0
  382. package/src/state/affine-attention.ts +180 -0
  383. package/src/state/attachment.ts +68 -0
  384. package/src/state/attention-read.ts +215 -0
  385. package/src/state/batched-kv.ts +294 -0
  386. package/src/state/batched-mask.ts +445 -0
  387. package/src/state/batched-quant.ts +389 -0
  388. package/src/state/batched-quantized-kv.ts +209 -0
  389. package/src/state/batched-rotating-quant.ts +457 -0
  390. package/src/state/batched-rotating-state.ts +149 -0
  391. package/src/state/batched-rotating.ts +430 -0
  392. package/src/state/batched-row-storage.ts +179 -0
  393. package/src/state/batched-ssm.ts +54 -0
  394. package/src/state/batched-turboquant-kv.ts +144 -0
  395. package/src/state/bf16-first-kv.ts +663 -0
  396. package/src/state/cache-service.ts +592 -0
  397. package/src/state/capabilities.ts +65 -0
  398. package/src/state/checkpoint.ts +46 -0
  399. package/src/state/continuation-persistence.ts +126 -0
  400. package/src/state/decode-step-inputs.ts +31 -0
  401. package/src/state/decoded-kv-donor.ts +12 -0
  402. package/src/state/delayed-quantized-kv.ts +141 -0
  403. package/src/state/delayed-rotating-quantized-kv.ts +178 -0
  404. package/src/state/delayed-turboquant-kv.ts +123 -0
  405. package/src/state/durability.ts +214 -0
  406. package/src/state/encoder-cache.ts +26 -0
  407. package/src/state/full-kv-row-append.ts +33 -0
  408. package/src/state/full-kv-row-donor.ts +54 -0
  409. package/src/state/full-prefill-padding.ts +56 -0
  410. package/src/state/full-prefill-row.ts +112 -0
  411. package/src/state/full-transitioning-kv-rows.ts +70 -0
  412. package/src/state/glm52-cache.ts +783 -0
  413. package/src/state/grouped-heads-kv4-cache.ts +26 -0
  414. package/src/state/index.ts +38 -0
  415. package/src/state/kv-attention-view.ts +77 -0
  416. package/src/state/kv-budget.ts +14 -0
  417. package/src/state/kv-scheme.ts +315 -0
  418. package/src/state/kv-storage.ts +57 -0
  419. package/src/state/kv-tensor-rows.ts +212 -0
  420. package/src/state/kv.ts +324 -0
  421. package/src/state/kv4-head256-cache.ts +46 -0
  422. package/src/state/latent-legos.ts +895 -0
  423. package/src/state/layout.ts +277 -0
  424. package/src/state/leases.ts +26 -0
  425. package/src/state/paged/cache.ts +689 -0
  426. package/src/state/paged/codec.ts +36 -0
  427. package/src/state/paged/rows.ts +223 -0
  428. package/src/state/persistence-types.ts +173 -0
  429. package/src/state/persistence-worker.ts +83 -0
  430. package/src/state/persistence.ts +1036 -0
  431. package/src/state/persistence.worker.d.ts +3 -0
  432. package/src/state/persistence.worker.js +264 -0
  433. package/src/state/prefix-cache.ts +685 -0
  434. package/src/state/quantized-kv.ts +369 -0
  435. package/src/state/quantized-tensor.ts +26 -0
  436. package/src/state/request-policy.ts +48 -0
  437. package/src/state/retention-policy.ts +37 -0
  438. package/src/state/rollback.ts +131 -0
  439. package/src/state/rotating-kv-donor.ts +35 -0
  440. package/src/state/rotating-kv-layout.ts +341 -0
  441. package/src/state/rotating-kv.ts +529 -0
  442. package/src/state/rotating-quantized-kv.ts +616 -0
  443. package/src/state/rotating-row-transaction.ts +35 -0
  444. package/src/state/rows.ts +104 -0
  445. package/src/state/softcap-attention.ts +118 -0
  446. package/src/state/speculative/glm52-mtp-state.ts +38 -0
  447. package/src/state/speculative/qwen-mtp-state.ts +34 -0
  448. package/src/state/speculative-rotating-kv.ts +113 -0
  449. package/src/state/ssd-cache.ts +434 -0
  450. package/src/state/ssm-prefill-padding.ts +39 -0
  451. package/src/state/ssm.ts +422 -0
  452. package/src/state/target-layout.ts +7 -0
  453. package/src/state/tiered-prefix-cache.ts +45 -0
  454. package/src/state/training-cache.ts +300 -0
  455. package/src/state/transitioning-kv-rows.ts +267 -0
  456. package/src/state/turboquant-attention.ts +97 -0
  457. package/src/state/turboquant-codec.ts +90 -0
  458. package/src/state/turboquant-kv.ts +442 -0
  459. package/src/state/views.ts +26 -0
  460. package/src/transcription/index.ts +3 -0
  461. package/src/transcription/silero-vad.ts +379 -0
  462. package/src/transcription/whisper/decode.ts +773 -0
  463. package/src/transcription/whisper/format.ts +74 -0
  464. package/src/transcription/whisper/index.ts +4 -0
  465. package/src/transcription/whisper/timing.ts +274 -0
  466. package/src/transcription/whisper/transcribe.ts +394 -0
  467. package/src/transcription/whisper/types.ts +22 -0
@@ -0,0 +1,184 @@
1
+ // Memory contracts — `mlx-bun fit`. Every term is deterministic:
2
+ // weights — safetensors byte sizes (registry)
3
+ // KV bytes/token — config: layers × kv_heads × head_dim × bytes;
4
+ // sliding-window layers saturate at the window
5
+ // prefill transient — chunk size we choose × calibrated bytes/token
6
+ // machine — RAM (queried) + Metal wired ceiling fraction
7
+ //
8
+ // Calibration constants come from measured runs on the reference M4 Pro.
9
+
10
+ import { totalmem } from "node:os";
11
+ import type { ModelConfig } from "../artifacts/config";
12
+ import type { MemoryPlan } from "../contracts/portable/memory-plan";
13
+ import { moeOf } from "../models/support";
14
+ import { kvBytesAt, sdpaFallbackBytes, type KvSchemeOptions } from "../state/kv-scheme";
15
+
16
+ /** Decode-efficiency vs theoretical bandwidth ceiling, measured on the
17
+ * reference machine (24.9 tok/s vs 30.3 ceiling @600 ctx). */
18
+ export const DECODE_EFFICIENCY = 0.82;
19
+ /** MoE decode efficiency vs the active-bytes ceiling. RECALIBRATED from
20
+ * a cleared-machine matrix: 26B-A4B measured 54.5 tok/s
21
+ * (python 55.7 — parity) vs ~71 tok/s raw ceiling → 0.76. The earlier
22
+ * 0.42 came from a session where BOTH stacks were memory-degraded
23
+ * (32.3/33.0 "parity" — equally wrong, mutually consistent). */
24
+ export const MOE_DECODE_EFFICIENCY = 0.76;
25
+ /** Prefill transient bytes per chunk token (measured: ~1.1 GB @ 2048). */
26
+ export const TRANSIENT_PER_TOKEN = 0.55e6;
27
+ /** Fraction of unified RAM usable as GPU working set (Metal's
28
+ * recommendedMaxWorkingSetSize is ~75% on consumer SKUs). */
29
+ export const WIRED_FRACTION = 0.75;
30
+ export const DEFAULT_CHUNK = 2048;
31
+
32
+ export interface MachineSpec {
33
+ name: string;
34
+ ramBytes: number;
35
+ bandwidthGBs: number;
36
+ }
37
+
38
+ /** Representative Apple Silicon SKUs (memory bandwidth GB/s). */
39
+ export const APPLE_SKUS: { chip: string; bandwidthGBs: number; ramOptions: number[] }[] = [
40
+ { chip: "M1", bandwidthGBs: 68, ramOptions: [8, 16] },
41
+ { chip: "M1 Pro", bandwidthGBs: 200, ramOptions: [16, 32] },
42
+ { chip: "M1 Max", bandwidthGBs: 400, ramOptions: [32, 64] },
43
+ { chip: "M1 Ultra", bandwidthGBs: 800, ramOptions: [64, 128] },
44
+ { chip: "M2", bandwidthGBs: 100, ramOptions: [8, 16, 24] },
45
+ { chip: "M2 Pro", bandwidthGBs: 200, ramOptions: [16, 32] },
46
+ { chip: "M2 Max", bandwidthGBs: 400, ramOptions: [32, 64, 96] },
47
+ { chip: "M2 Ultra", bandwidthGBs: 800, ramOptions: [64, 128, 192] },
48
+ { chip: "M3", bandwidthGBs: 100, ramOptions: [8, 16, 24] },
49
+ { chip: "M3 Pro", bandwidthGBs: 150, ramOptions: [18, 36] },
50
+ { chip: "M3 Max", bandwidthGBs: 400, ramOptions: [36, 48, 64, 96, 128] },
51
+ { chip: "M3 Ultra", bandwidthGBs: 819, ramOptions: [96, 256, 512] },
52
+ { chip: "M4", bandwidthGBs: 120, ramOptions: [16, 24, 32] },
53
+ { chip: "M4 Pro", bandwidthGBs: 273, ramOptions: [24, 48] },
54
+ { chip: "M4 Max", bandwidthGBs: 546, ramOptions: [36, 48, 64, 128] },
55
+ ];
56
+
57
+ /** Chip name from sysctl ("M1 Max") + its bandwidth from APPLE_SKUS. */
58
+ export function detectChip(): { name: string | null; bandwidthGBs: number | null } {
59
+ try {
60
+ const proc = Bun.spawnSync(["sysctl", "-n", "machdep.cpu.brand_string"]);
61
+ const name = proc.stdout.toString().trim().replace(/^Apple\s+/, "");
62
+ if (!name) return { name: null, bandwidthGBs: null };
63
+ const sku = APPLE_SKUS.find((s) => s.chip === name);
64
+ return { name, bandwidthGBs: sku?.bandwidthGBs ?? null };
65
+ } catch {
66
+ return { name: null, bandwidthGBs: null };
67
+ }
68
+ }
69
+
70
+ export function thisMachine(bandwidthGBs?: number): MachineSpec {
71
+ // Default bandwidth: the detected chip's table entry; 273 (M4 Pro, the
72
+ // original dev machine) only as the last resort.
73
+ const bw = bandwidthGBs ?? detectChip().bandwidthGBs ?? 273;
74
+ return { name: "this machine", ramBytes: totalmem(), bandwidthGBs: bw };
75
+ }
76
+
77
+
78
+ export interface FitReport extends MemoryPlan {
79
+ readonly strategy: "generic-kv";
80
+ readonly predictedDecodeTps: number;
81
+ }
82
+
83
+ export function fit(
84
+ config: ModelConfig,
85
+ weightsBytes: number,
86
+ ctx: number,
87
+ machine: MachineSpec = thisMachine(),
88
+ chunk: number = DEFAULT_CHUNK,
89
+ /** Bytes of `.experts.` tensors (registry). MoE decode reads only
90
+ * top_k/num_experts of them per token; residency still needs all. */
91
+ expertsBytes = 0,
92
+ /** Explicit memory budget in bytes (admission control). When set it
93
+ * replaces the machine-derived usable ceiling (ram × WIRED_FRACTION)
94
+ * outright — the budget IS the usable envelope. */
95
+ usableBytes?: number,
96
+ /** Active KV-quant scheme; a quantized cache holds more context in the
97
+ * same budget, so the solved ceiling (and admission) must bill it. */
98
+ kvScheme?: KvSchemeOptions,
99
+ ): FitReport {
100
+ const usable = usableBytes ?? machine.ramBytes * WIRED_FRACTION;
101
+ // Prefill transient = the calibrated per-chunk-token constant + the
102
+ // SDPA-fallback scores tensor (heads × min(chunk, ctx) × ctx × 2 B) for
103
+ // attention layers whose head dim MLX cannot fuse (kv-scheme.ts). The
104
+ // second term is what grows with context on Qwen3.x (head_dim 256) and
105
+ // Gemma 4 (256/512) and was unbilled before 2026-09-08.
106
+ const transientAt = (n: number) =>
107
+ Math.min(chunk, n) * TRANSIENT_PER_TOKEN + sdpaFallbackBytes(config, chunk, n);
108
+ const transient = transientAt(ctx);
109
+ const kv = kvBytesAt(config, ctx, kvScheme);
110
+ const total = weightsBytes + kv + transient;
111
+
112
+ // solve max context: weights + kv(n) + transient(n) ≤ usable. The same
113
+ // formula `fits` uses, so the solved ceiling always fits. It is monotone
114
+ // in n (KV linear, sliding saturating at the window, scores quadratic
115
+ // below the chunk then linear), so bisect over [0, maxPositionEmbeddings].
116
+ const totalAt = (n: number) => weightsBytes + kvBytesAt(config, n, kvScheme) + transientAt(n);
117
+ let maxCtx = 0;
118
+ {
119
+ let lo = 0;
120
+ let hi = Math.max(0, Math.floor(config.text.maxPositionEmbeddings));
121
+ if (totalAt(hi) <= usable) lo = hi;
122
+ else {
123
+ // invariant: totalAt(lo) ≤ usable (lo = 0 may itself not fit → 0)
124
+ if (totalAt(0) > usable) hi = 0;
125
+ while (hi - lo > 1) {
126
+ const mid = Math.floor((lo + hi) / 2);
127
+ if (totalAt(mid) <= usable) lo = mid;
128
+ else hi = mid;
129
+ }
130
+ }
131
+ maxCtx = lo;
132
+ }
133
+
134
+ // decode reads all weights + the KV cache once per token — except MoE
135
+ // expert weights, where only top_k of num_experts are touched per token
136
+ const moe = moeOf(config);
137
+ const isMoe = moe !== null;
138
+ const expertsSkipped = moe ? expertsBytes * (1 - moe.topK / moe.experts) : 0;
139
+ const bytesPerToken = weightsBytes - expertsSkipped + kv;
140
+ const predictedDecodeTps =
141
+ ((machine.bandwidthGBs * 1e9) / bytesPerToken) *
142
+ (isMoe ? MOE_DECODE_EFFICIENCY : DECODE_EFFICIENCY);
143
+
144
+ return {
145
+ schemaVersion: 1,
146
+ strategy: "generic-kv",
147
+ fits: total <= usable,
148
+ contextTokens: ctx,
149
+ weightsBytes,
150
+ kvBytes: kv,
151
+ transientBytes: transient,
152
+ reserveBytes: 0,
153
+ totalBytes: total,
154
+ usableBytes: usable,
155
+ maxSafeContext: maxCtx,
156
+ predictedDecodeTps,
157
+ };
158
+ }
159
+
160
+ /** The SKU matrix: which Apple Silicon configs run this model at `ctx`. */
161
+ export function skuMatrix(
162
+ config: ModelConfig, weightsBytes: number, ctx: number, expertsBytes = 0,
163
+ kvScheme?: KvSchemeOptions,
164
+ ): { sku: string; ramGB: number; fits: boolean; maxContext: number; decodeTps: number }[] {
165
+ const rows: ReturnType<typeof skuMatrix> = [];
166
+ for (const sku of APPLE_SKUS) {
167
+ for (const ram of sku.ramOptions) {
168
+ const m: MachineSpec = {
169
+ name: `${sku.chip} ${ram}GB`,
170
+ ramBytes: ram * 2 ** 30,
171
+ bandwidthGBs: sku.bandwidthGBs,
172
+ };
173
+ const r = fit(config, weightsBytes, ctx, m, DEFAULT_CHUNK, expertsBytes, undefined, kvScheme);
174
+ rows.push({
175
+ sku: sku.chip,
176
+ ramGB: ram,
177
+ fits: r.fits,
178
+ maxContext: r.maxSafeContext,
179
+ decodeTps: r.predictedDecodeTps,
180
+ });
181
+ }
182
+ }
183
+ return rows;
184
+ }
@@ -0,0 +1,178 @@
1
+ import { pagedDecodeCompiles } from "../state/layout";
2
+ import { speculativePrefixNamespace, captureSpeculativeOptions } from "../generation/speculative/cache-identity";
3
+ import { createOrdinaryContinuationRequest } from "./continuation-request";
4
+ import type { ExecutionContext } from "../contracts/portable/scheduling";
5
+ import type { ModelConfig } from "../artifacts/config";
6
+ import type { MlxTokenGraph } from "../models/graph";
7
+ import { declaredGraph, graphCapabilitiesOf } from "../models/capabilities";
8
+ import { BF16_KV, COMPOSITION_DEFAULTS, type KvStorageScheme } from "../contracts/portable/composition";
9
+ import { runtimeConfig, type RuntimeConfig } from "../runtime/config";
10
+ import type { CacheServiceGraph } from "../state/cache-service";
11
+ import { disposeResources } from "../runtime/resources";
12
+ import { MlxBatchExecutionGroup } from "./batch-group";
13
+ import { type MlxBatchExecutionGroupOptions, type MlxGroupMethodRequest } from "./batch-types";
14
+ import type { DraftProvider } from "../generation/speculative/source";
15
+ import { constraintDraftProvider } from "../generation/speculative/sources/ngram-source";
16
+ import { bindSpeculativeGroupRequests } from "./speculative-group";
17
+ import { bindGrammarGroupRequests } from "./grammar-group";
18
+ import { bindFillGroupRequests } from "./fill-group";
19
+ import { bindDenoisingGroupRequests } from "./denoising-group";
20
+ import { bindLegacyDenoisingModel } from "../generation/bindings/denoising";
21
+ import type { GenerateOptions } from "../generation/index";
22
+ import type { ExecutionRequirements, ResolvedExecution } from "../contracts/portable/execution";
23
+ import type { GraphCapabilities } from "../contracts/portable/graph";
24
+ import { resolveExecution } from "./plan";
25
+ import type { MlxPromptInput, Vision } from "../contracts/mlx/media";
26
+ import type { AdaptiveDraftGateSettings } from "../contracts/mlx/graph";
27
+
28
+ export interface MlxBatchGroup extends Pick<MlxBatchExecutionGroup,
29
+ "activeRows" | "pendingRows" | "submit" | "kick" | "close"> {
30
+ runPreparation?<T>(work: () => Promise<T>, signal?: AbortSignal): Promise<T>;
31
+ }
32
+
33
+ /** Planning consumes the graph's declared capabilities (`declaredGraph`), the
34
+ * request, and dynamic state; it never inspects a model's class, type, or name,
35
+ * nor how the composition's caches store keys and values: every cache lego
36
+ * serves every method. Request state, prefix reuse and persistence are the
37
+ * cache service's (`state/cache-service`), handed to `createBatchGroup`. */
38
+ export interface MlxGatewayBinding {
39
+ mediaInput?(input: Vision): MlxPromptInput;
40
+ /** A replayable ordinary request when the plan qualified one: its sampler
41
+ * and the key the cache service checkpoints it under every `interval` tokens
42
+ * (the service's `checkpointEveryTokens`). */
43
+ continuationRequest?(execution: ResolvedExecution | undefined, options: GenerateOptions, prompt: number[],
44
+ onToken: Parameters<typeof createOrdinaryContinuationRequest>[0]["onToken"],
45
+ interval: number | undefined): ReturnType<typeof createOrdinaryContinuationRequest> | undefined;
46
+ readonly config: ModelConfig;
47
+ readonly runtime: RuntimeConfig;
48
+ /** What the composition's cache service builds request state from: the
49
+ * bound graph's caches, config and composition. */
50
+ readonly graph: CacheServiceGraph;
51
+ /** What the bound graph declares, as resolved when it was bound. */
52
+ readonly capabilities: GraphCapabilities;
53
+ /** `scheduling.checkpoints`: the cache service keeps generation checkpoints. */
54
+ plan(request: ExecutionRequirements, options: GenerateOptions, scheduling: { checkpoints: boolean }): ResolvedExecution;
55
+ bindAdapterContext?(adapters: string[], key: string): ExecutionContext;
56
+ /** Whether a request's prefix may be reused and its state kept (the batch
57
+ * request's `reusesPrefix`): media token IDs alone do not identify prepared
58
+ * embeddings, so an unidentified media prefix takes fresh state. */
59
+ reusesPrefix(execution: ResolvedExecution | undefined): boolean;
60
+ /** The prefix namespace of a request whose own namespace is `base` (its
61
+ * adapters, a prepared media prefix): its method's over it. The cache service
62
+ * adds the composition's paging. Null: the method keeps no prefixes. */
63
+ prefixNamespace?(execution: ResolvedExecution | undefined, options: GenerateOptions, base: string): string | null;
64
+ methodRequest?(execution: ResolvedExecution | undefined, options: GenerateOptions): MlxGroupMethodRequest | undefined;
65
+ createBatchGroup(options: MlxBatchExecutionGroupOptions): MlxBatchGroup;
66
+ }
67
+
68
+ /** `adaptiveDepth`: each round drafts up to `numDraftTokens`, chosen from the
69
+ * decayed acceptance rate and the graph's measured `verifyRoundCosts` (the
70
+ * graph must declare them) by a gate with the settings `gate` (the graph's
71
+ * `adaptiveDraftGates` for the drafter's kind); otherwise every round drafts
72
+ * `numDraftTokens`.
73
+ * The graph's composition names its KV scheme and scheduler switches (a
74
+ * graph without one runs the defaults): every request's, so the binding
75
+ * reads them once, here. */
76
+ export function bindMlxGateway(model: MlxTokenGraph,
77
+ draft?: { provider: DraftProvider; numDraftTokens: number; adaptiveDepth?: boolean; gate?: AdaptiveDraftGateSettings }): MlxGatewayBinding {
78
+ const kv: KvStorageScheme = model.composition?.kv ?? BF16_KV;
79
+ // The composition facts this binding plans with: the graph's record, else the defaults.
80
+ const facts = model.composition ?? COMPOSITION_DEFAULTS;
81
+ const runtime = runtimeConfig();
82
+ // The declarations are resolved once, when the graph is bound; planning reads
83
+ // only these and the request.
84
+ const graph = declaredGraph(model);
85
+ const caps = graphCapabilitiesOf(graph);
86
+ const verification = caps.speculation;
87
+ // Denoising rows interleave through their own grouped method. Token-level
88
+ // methods (speculation, grammar proposals, fill) never bind to this graph.
89
+ const denoising = caps.method === "denoising" ? bindDenoisingGroupRequests(bindLegacyDenoisingModel(model)) : undefined;
90
+ // Grouped speculation binds any provider whose taps the graph's forward
91
+ // captures; one that cannot bind is refused by placement rather than served
92
+ // ordinarily.
93
+ const speculative = !denoising && draft?.provider.grouped
94
+ ? bindSpeculativeGroupRequests(model, draft.provider, draft.numDraftTokens, draft.adaptiveDepth ?? false, draft.gate) : undefined;
95
+ // Grammar jump: verified proposals where the graph's verifier qualifies them,
96
+ // committed spans otherwise.
97
+ const grammarProvider = caps.method === "autoregressive" && verification.grammarProposals && facts.grammarJump
98
+ ? constraintDraftProvider() : undefined;
99
+ const grammarProposals = grammarProvider ? bindSpeculativeGroupRequests(model, grammarProvider,
100
+ Math.max(1, Math.trunc(runtime.number("MLX_BUN_GRAMMAR_DRAFT_TOKENS", 3)))) : undefined;
101
+ const grammarSpans = caps.method === "autoregressive" && !verification.grammarProposals
102
+ ? bindGrammarGroupRequests(model) : undefined;
103
+ const adapterState = model.loraState;
104
+ const fillRequests = !denoising ? bindFillGroupRequests(model) : undefined;
105
+ const mediaInput = caps.media && caps.media.input !== "pixels"
106
+ ? (input: Vision): MlxPromptInput => graph.bindMediaInput!(input) : undefined;
107
+ return {
108
+ mediaInput,
109
+ config: model.config, runtime, graph: model, capabilities: caps,
110
+ continuationRequest(execution, options, prompt, onToken, interval) {
111
+ if (!execution?.checkpoint || execution.mechanism !== "continuous") return undefined;
112
+ if (interval === undefined) throw new Error("qualified continuation requires bound persistence services");
113
+ // The cache service keys checkpoints in the request state's namespace, as its prefixes are.
114
+ return createOrdinaryContinuationRequest({ options, prompt, onToken, execution, interval });
115
+ },
116
+ // Media token IDs alone do not identify the prepared embeddings, so an
117
+ // unidentified media prefix takes fresh state and reuses none.
118
+ reusesPrefix: execution => !(execution?.method === "autoregressive" && !execution.promptCache),
119
+ // The method's namespace: the request's base, or the speculative group's over it.
120
+ prefixNamespace: (execution, options, base) => {
121
+ if (execution?.method !== "speculative") return base;
122
+ const namespace = (execution.grammarJump ? grammarProvider : draft?.provider)?.grouped?.checkpointNamespace?.();
123
+ return namespace === undefined ? null : speculativePrefixNamespace(namespace, base, captureSpeculativeOptions(options));
124
+ },
125
+ methodRequest: (execution, options) => execution?.method === "denoising" ? denoising?.(options)
126
+ : execution?.method === "speculative"
127
+ ? (execution.grammarJump ? grammarProposals : speculative)?.(
128
+ options.fill && !execution.fill ? { ...options, fill: undefined } : options)
129
+ : execution?.grammarJump ? grammarSpans?.(options)
130
+ : execution?.fill ? fillRequests?.(options) : undefined,
131
+ ...(adapterState ? { bindAdapterContext(adapters: string[], key: string): ExecutionContext {
132
+ // Denoising applies each row's adapters around that row's own units, so
133
+ // its rows share one neutral group context whatever their adapters.
134
+ if (denoising) return { key: "", enter: () => () => {} };
135
+ const selected = [...adapters];
136
+ return { key, enter() {
137
+ const previous = adapterState.active;
138
+ adapterState.active = selected;
139
+ return () => { adapterState.active = previous; };
140
+ } };
141
+ } } : {}),
142
+ plan(request, options, scheduling) {
143
+ const sharedMethod = request.hasDraft ? speculative : grammarProposals;
144
+ const provider = request.hasDraft ? draft?.provider : grammarProvider;
145
+ // A graph whose verifier does not qualify adapters, or a provider that
146
+ // cannot serve the target's adapters, serves adapter requests ordinarily
147
+ // even when a draft is configured.
148
+ const sharedSpeculativeAdapters = verification.adapters && !!sharedMethod &&
149
+ provider?.grouped?.supportsTargetAdapters === true;
150
+ // Such a drafted request opens no draft provider and keeps the ordinary checkpoint rules.
151
+ const ignoredDraft = request.hasDraft && request.hasAdapters && !sharedSpeculativeAdapters;
152
+ return resolveExecution(request, {
153
+ checkpoints: scheduling.checkpoints,
154
+ sharedCheckpoints: scheduling.checkpoints &&
155
+ (!request.hasDraft || ignoredDraft) && !request.hasVision && !request.hasGrammar &&
156
+ !request.wantsLogprobs && !options.fill,
157
+ mediaBatch: !!mediaInput,
158
+ mediaPrefixCache: runtime.flag("MLX_BUN_MEDIA_PREFIX_CACHE", true),
159
+ groupedMethods: denoising ? ["denoising"] : sharedMethod ? ["autoregressive", "speculative"] : ["autoregressive"],
160
+ sharedGrammarProposals: !!grammarProposals,
161
+ // Committed spans take ordinary grammar rows; prepared media keeps masking.
162
+ sharedGrammarJump: !!grammarSpans && !request.hasVision,
163
+ sharedFill: !!fillRequests && !!options.fill,
164
+ sharedSpeculativeEcho: verification.externalTokens && !!options.fill?.plan.echo &&
165
+ provider?.grouped?.supportsExternalTokens === true,
166
+ speculativeLogprobs: !!sharedMethod && verification.logprobs,
167
+ sharedSpeculativeAdapters,
168
+ method: caps.method,
169
+ compiledDecode: caps.compiledDecode,
170
+ // The batch group coordinates per-row grammar for every token method;
171
+ // masks come from the shared sampler, so no graph qualifies or declines
172
+ // them. Denoising samples canvases, which an AR token mask cannot apply to.
173
+ grammarBatch: !denoising,
174
+ }, { fill: !!options.fill, compiledDecode: (!model.composition?.pagedKv || pagedDecodeCompiles(model.composition.pagedKv, model.composition.kv)), grammarJump: facts.grammarJump });
175
+ },
176
+ createBatchGroup: (options) => new MlxBatchExecutionGroup(model, options),
177
+ };
178
+ }
@@ -0,0 +1,224 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+ import { clearCache } from "@mlx-bun/mlx/ffi";
4
+ import type { Cache } from "../contracts/mlx/cache";
5
+ import type { MlxTokenGraph } from "../models/graph";
6
+ import type { GenerateOptions } from "../generation/index";
7
+ import { MlxPhaseDispatch } from "../generation/phase-dispatch";
8
+ import { tokenPhasesOf } from "../models/capabilities";
9
+ import { appendGrammarSpan } from "../generation/grammar-step";
10
+ import { makeStepSampler, type DeviceStepSampler } from "../sampling/index";
11
+ import { cleanupFailure, disposeResources } from "../runtime/resources";
12
+ import { snapshotGenerationPolicy } from "./request-policy";
13
+ import { MlxPrefillCohort } from "./prefill-cohort";
14
+ import type { MlxGroupedMethod, MlxGroupMethodHost, MlxGroupMethodRequest, MlxGroupPreparation, Row } from "./batch-types";
15
+
16
+ interface RequestState {
17
+ sampling: DeviceStepSampler;
18
+ caches: Cache[];
19
+ retain?: () => void;
20
+ pending: MlxArray | null;
21
+ }
22
+
23
+ /** Preserve committed grammar spans while the shared scheduler interleaves
24
+ * active requests. Graph calls remain B1: spans of different lengths are not
25
+ * padded, split, or verified. The gateway owns each borrowed grammar matcher. */
26
+ export function bindGrammarGroupRequests(model: MlxTokenGraph) {
27
+ return (input: GenerateOptions): MlxGroupMethodRequest => ({
28
+ key: "grammar-forced-span", data: snapshotGenerationPolicy(input),
29
+ open: host => new GrammarGroup(host, model),
30
+ });
31
+ }
32
+
33
+ class GrammarGroup implements MlxGroupedMethod {
34
+ readonly #dispatch: MlxPhaseDispatch;
35
+ readonly #requests = new Map<Row, RequestState>();
36
+ #next = 0;
37
+
38
+ constructor(readonly host: MlxGroupMethodHost, readonly model: MlxTokenGraph) {
39
+ this.#dispatch = new MlxPhaseDispatch(tokenPhasesOf(model));
40
+ }
41
+
42
+ prepare(row: Row): MlxGroupPreparation {
43
+ const options = row.req.method!.data as GenerateOptions;
44
+ if (!row.req.grammar || options.logprobs || (options.topLogprobs ?? 0) > 0 ||
45
+ row.req.continuation || row.req.promptInput)
46
+ throw new Error("forced grammar span requires an ordinary grammar request without logprobs, media, or continuation");
47
+ const sampling = makeStepSampler(options, { tokenRepresentation: "device", grammarWait: "external",
48
+ historyUpdate: "manual", initialHistory: row.req.promptIds });
49
+ this.#requests.set(row, { sampling, caches: [], pending: null });
50
+ const target = new MlxPrefillCohort({
51
+ chunkSize: this.host.prefillChunkSize,
52
+ tailSplit: this.host.prefillTailSplit,
53
+ cache: this.host.cache, maintain: this.host.kvMaintenance,
54
+ forward: async (ids, caches, step) => this.#dispatch.prompt(step.phase, step.sequences, step.tokens)(ids, caches, null),
55
+ project: (hidden, _caches, _completed, step) => {
56
+ const [, length, width] = hidden.shape as [number, number, number];
57
+ using last = hidden.slice([0, length - 1, 0], [1, length, width]);
58
+ return this.#dispatch.logits(step.sequences)(last);
59
+ },
60
+ complete: async (state, logits) => {
61
+ const request = this.#requests.get(row)!;
62
+ await row.req.grammar!.ready();
63
+ row.req.signal?.throwIfAborted();
64
+ request.pending = request.sampling.sample(logits, 0).token;
65
+ ops.asyncEvalAll([request.pending]);
66
+ // No publication here: the first complete grammar span must enter KV
67
+ // before even token zero becomes visible to an output callback.
68
+ request.caches = state.solo; state.solo = [];
69
+ request.retain = state.retain; state.retain = undefined;
70
+ row.sampled = 1;
71
+ this.host.join(row);
72
+ },
73
+ reject: (failed, error) => {
74
+ try { this.#close(failed); }
75
+ catch (cleanup) { error = new AggregateError([error, cleanup], "grammar preparation cleanup failed"); }
76
+ failed.reject(error);
77
+ },
78
+ });
79
+ const preparation: MlxGroupPreparation = {
80
+ get rows() { return target.rows; },
81
+ get tokenWeight() { return target.tokenWeight; },
82
+ canAdmit: false, supportsMixedWork: false,
83
+ advance: () => target.advance(),
84
+ dispose: () => disposeResources([target, { dispose: () => {
85
+ if (!this.host.rows.includes(row)) this.#close(row);
86
+ } }]),
87
+ };
88
+ try { target.admit(row); return preparation; }
89
+ catch (error) { return cleanupFailure(error, () => preparation.dispose()); }
90
+ }
91
+
92
+ async advance(): Promise<void> {
93
+ const live = this.host.rows.flatMap((row, index) => row.req.signal?.aborted ? [] : [index]);
94
+ if (live.length !== this.host.rows.length) {
95
+ const cancelled = this.host.rows.filter(row => row.req.signal?.aborted).map(row => {
96
+ let error = row.req.signal!.reason;
97
+ try { this.#close(row); }
98
+ catch (cleanup) { error = new AggregateError([error, cleanup], "cancelled grammar cleanup failed"); }
99
+ return { row, error };
100
+ });
101
+ // Filter membership only after every cancelled owner has closed. A
102
+ // request must not settle while its tensors are still request-owned.
103
+ this.host.filterRows(live);
104
+ for (const { row, error } of cancelled) row.reject(error);
105
+ }
106
+ if (!this.host.rows.length) return;
107
+ const index = this.#next % this.host.rows.length;
108
+ const row = this.host.rows[index]!;
109
+ this.#next = index + 1;
110
+ try { await this.#step(row); }
111
+ catch (error) {
112
+ try { this.#retire(row); }
113
+ catch (cleanup) { error = new AggregateError([error, cleanup], "grammar step cleanup failed"); }
114
+ row.reject(error);
115
+ }
116
+ }
117
+
118
+ async #step(row: Row): Promise<void> {
119
+ const state = this.#requests.get(row)!;
120
+ const grammar = row.req.grammar!;
121
+ row.req.signal?.throwIfAborted();
122
+ const current = state.pending!;
123
+ const token = ops.itemUint32(current);
124
+ const step = row.generated;
125
+ let forced: number[] | null = null;
126
+ if (step + 1 < row.req.maxTokens) {
127
+ grammar.accept(token);
128
+ await grammar.ready();
129
+ if (!grammar.isTerminated) {
130
+ forced = grammar.jumpForward(row.req.maxTokens - (step + 1));
131
+ if (forced) await grammar.ready();
132
+ }
133
+ }
134
+ let next: MlxArray | null = null;
135
+ try {
136
+ if (forced) {
137
+ state.sampling.commitDevice(current);
138
+ state.sampling.commitNumbers(forced);
139
+ const after = step + 1 + forced.length;
140
+ await appendGrammarSpan(this.#dispatch, state.caches, [token, ...forced],
141
+ ids => { row.fed.push(...ids); },
142
+ after < row.req.maxTokens && !grammar.isTerminated
143
+ ? logits => { next = state.sampling.sample(logits, after).token; } : undefined);
144
+ } else if (step + 1 < row.req.maxTokens && !grammar.isTerminated) {
145
+ state.sampling.commitDevice(current);
146
+ using ids = ops.reshape(current, [1, 1]);
147
+ using hidden = await this.#dispatch.decode(1)(ids, state.caches, null);
148
+ using logits = this.#dispatch.logits(1)(hidden);
149
+ next = state.sampling.sample(logits, step + 1).token;
150
+ row.fed.push(token);
151
+ }
152
+ if (next) ops.asyncEvalAll([next]);
153
+ row.req.signal?.throwIfAborted();
154
+ current.dispose(); state.pending = null;
155
+ row.generated++;
156
+ let stop = row.req.eosTokenIds.includes(token);
157
+ if (!stop) {
158
+ stop = await this.host.publish(row, token) === false;
159
+ if (!stop) {
160
+ row.req.signal?.throwIfAborted();
161
+ if ((row.generated - 1) % 256 === 0) clearCache();
162
+ for (const id of forced ?? []) {
163
+ row.req.signal?.throwIfAborted();
164
+ row.generated++;
165
+ if (await this.host.publish(row, id) === false) { stop = true; break; }
166
+ if ((row.generated - 1) % 256 === 0) clearCache();
167
+ }
168
+ }
169
+ }
170
+ row.req.signal?.throwIfAborted();
171
+ row.sampled = row.generated + Number(next !== null);
172
+ if (stop || !next) {
173
+ const unused = next; next = null; unused?.dispose();
174
+ this.#complete(row, stop || row.generated < row.req.maxTokens ? "stop" : "length");
175
+ } else { state.pending = next; next = null; }
176
+ } finally { next?.dispose(); }
177
+ }
178
+
179
+ #complete(row: Row, reason: "stop" | "length"): void {
180
+ const state = this.#requests.get(row)!;
181
+ let caches = state.caches; state.caches = [];
182
+ let retain = state.retain; state.retain = undefined;
183
+ const release = () => {
184
+ const owned = caches; caches = [];
185
+ const lease = retain; retain = undefined;
186
+ disposeResources([...owned, { dispose: () => lease?.() }]);
187
+ };
188
+ try {
189
+ // Match the direct generator: pending/sampler cleanup must succeed
190
+ // before a final prefix can become visible to another request.
191
+ this.#retire(row);
192
+ row.req.signal?.throwIfAborted();
193
+ if (this.host.cache.keeps(row.req) && !row.fedTainted) {
194
+ this.host.cache.publish(row.req, row.cacheNamespace ?? "", [...row.req.promptIds, ...row.fed], { caches, retain });
195
+ caches = []; retain = undefined;
196
+ }
197
+ } catch (error) { return cleanupFailure(error, release); }
198
+ release();
199
+ this.host.finish(row, reason);
200
+ }
201
+
202
+ #retire(row: Row): void {
203
+ this.host.filterRows(this.host.rows.flatMap((other, index) => other === row ? [] : [index]));
204
+ }
205
+
206
+ filterRows(keep: readonly number[], _discard: boolean): void {
207
+ const retained = new Set(keep.map(index => this.host.rows[index]!));
208
+ const removed = this.host.rows.filter(row => !retained.has(row));
209
+ this.#next = keep.filter(index => index < this.#next).length;
210
+ disposeResources(removed.map(row => ({ dispose: () => this.#close(row) })));
211
+ }
212
+
213
+ #close(row: Row): void {
214
+ const state = this.#requests.get(row);
215
+ if (!state) return;
216
+ this.#requests.delete(row);
217
+ disposeResources([...(state.pending ? [state.pending] : []), state.sampling,
218
+ ...state.caches, { dispose: () => state.retain?.() }]);
219
+ }
220
+
221
+ dispose(): void {
222
+ disposeResources([...this.#requests.keys()].map(row => ({ dispose: () => this.#close(row) })));
223
+ }
224
+ }
@@ -0,0 +1,18 @@
1
+ export * from "./engine";
2
+ export * from "./session";
3
+ export * from "./cancellation";
4
+ export * from "./admission";
5
+ export * from "./scheduler";
6
+ export * from "./coordinator";
7
+ export * from "./tasks";
8
+ export * from "./plan";
9
+ export * from "./methods";
10
+ export * from "./batch-group";
11
+ export * from "./gateway-binding";
12
+ export * from "./row-sampling";
13
+ export * from "./prefill-cohort";
14
+ export * from "./prefill-rows";
15
+ export * from "./continuation-types";
16
+ export * from "./continuation-request";
17
+ export * from "../runtime/resources";
18
+ export * from "../runtime/config";