@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,95 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+ import { KVCache } from "../../../state/kv";
4
+ import { type Cache } from "../../../contracts/mlx/cache";
5
+ import { MlxProjectedContextRows } from "./projected-context-rows";
6
+ import { captureCacheAttachment, restoreCacheAttachment } from "../../../state/attachment";
7
+ import { applyStateChanges, disposeResources } from "../../../runtime/resources";
8
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
9
+ import type { DraftPrefillGroup, DraftRowGroup, GroupedDraftProvider, TargetView } from "../source";
10
+ import type { DraftRowCheckpoint } from "../../../contracts/mlx/draft-checkpoint";
11
+
12
+ export interface ProjectedDraftMethod {
13
+ namespace: string;
14
+ schema: string;
15
+ layers: number;
16
+ project(hidden: MlxArray, positions: number | MlxArray): { k: MlxArray; v: MlxArray }[];
17
+ draft(context: ReturnType<MlxProjectedContextRows["readAttention"]>, pending: readonly number[], positions: number | MlxArray, depth: number): number[][];
18
+ /** Optional device-resident proposals [B, depth], row-major: the round reads
19
+ * them back together with the verify samples. */
20
+ draftDevice?(context: ReturnType<MlxProjectedContextRows["readAttention"]>, pending: readonly number[], positions: number | MlxArray, depth: number): MlxArray;
21
+ }
22
+
23
+ /** Provider numerics compose with shared context membership and persistence. */
24
+ class ProjectedDraftRows implements DraftPrefillGroup, DraftRowGroup {
25
+ readonly prefillMode = "tail-split" as const;
26
+ readonly tapLayers: readonly number[];
27
+ readonly context: MlxProjectedContextRows;
28
+ constructor(readonly method: ProjectedDraftMethod, tapLayers: readonly number[],
29
+ checkpoints: readonly (DraftRowCheckpoint | null)[]) {
30
+ this.tapLayers = tapLayers;
31
+ this.context = new MlxProjectedContextRows(method.layers, { project: method.project });
32
+ try { this.append(checkpoints); } catch (error) { this.dispose(); throw error; }
33
+ }
34
+ get namespace(): string { return this.method.namespace; }
35
+ get rowCount(): number { return this.context.state.rowCount; }
36
+ prepareAppend(checkpoints: readonly (DraftRowCheckpoint | null)[]): PreparedStateChange {
37
+ const restored: Cache[][] = [];
38
+ const empty = () => Array.from({ length: this.method.layers }, () => new KVCache());
39
+ try {
40
+ for (const checkpoint of checkpoints) restored.push(checkpoint ? restoreCacheAttachment(checkpoint.attachment, empty) : empty());
41
+ return this.context.prepareAppend(restored);
42
+ } finally { disposeResources(restored.flat()); }
43
+ }
44
+ append(checkpoints: readonly (DraftRowCheckpoint | null)[]): void {
45
+ if (checkpoints.length) applyStateChanges([() => this.prepareAppend(checkpoints)]);
46
+ }
47
+ prefill(tokens: MlxArray, hidden?: MlxArray): void {
48
+ this.context.append(hidden!, Array(this.rowCount).fill(tokens.shape[1]!)); this.materialize();
49
+ }
50
+ materialize(): void { this.context.materialize(); }
51
+ filterRows(keep: readonly number[]): void { this.context.state.filterRows(keep); }
52
+ capture(row: number): DraftRowCheckpoint {
53
+ const caches = this.context.state.extractRow(row);
54
+ try {
55
+ const processedTokens = caches[0]!.offset;
56
+ return { processedTokens, attachment: captureCacheAttachment(this.method.schema, caches, { processedTokens }) };
57
+ } finally { disposeResources(caches); }
58
+ }
59
+ draft(pending: readonly number[], depth: number): number[][] {
60
+ if (!depth) return pending.map(() => []);
61
+ const positions = this.context.positions, donors = this.context.readAttention();
62
+ using offsets = positions.every(position => position === positions[0]) ? null : ops.fromInt32([...positions], [pending.length]);
63
+ try { return this.method.draft(donors, pending, offsets ?? positions[0]!, depth); }
64
+ finally { donors.dispose(); }
65
+ }
66
+ draftDevice(pending: readonly number[], depth: number):
67
+ { tokens: MlxArray; resolve(read: readonly number[]): number[][] } | null {
68
+ if (!depth || !this.method.draftDevice) return null;
69
+ const positions = this.context.positions, donors = this.context.readAttention();
70
+ using offsets = positions.every(position => position === positions[0]) ? null : ops.fromInt32([...positions], [pending.length]);
71
+ try {
72
+ const tokens = this.method.draftDevice(donors, pending, offsets ?? positions[0]!, depth);
73
+ const width = tokens.shape[1]!;
74
+ return { tokens, resolve: read => pending.map((_, row) => read.slice(row * width, (row + 1) * width)) };
75
+ } finally { donors.dispose(); }
76
+ }
77
+ preview(pending: readonly number[], depth: number): MlxArray | null {
78
+ return this.draftDevice(pending, depth)?.tokens ?? null;
79
+ }
80
+ commit(accepted: readonly number[], hidden: MlxArray): void {
81
+ this.context.append(hidden, accepted.map(count => count + 1));
82
+ }
83
+ dispose(): void { this.context.dispose(); }
84
+ }
85
+
86
+ /** Projected rows tap the target: the provider declares the one immutable
87
+ * layer list its rows consume, so bindings check it before any row opens. */
88
+ export function projectedDraftGroups(namespace: string, tapLayers: readonly number[],
89
+ bind: (target: TargetView) => ProjectedDraftMethod): GroupedDraftProvider {
90
+ const taps = Object.freeze([...tapLayers]);
91
+ const open = ({ target, checkpoints }: {
92
+ target: TargetView; checkpoints: readonly (DraftRowCheckpoint | null)[];
93
+ }) => new ProjectedDraftRows(bind(target), taps, checkpoints);
94
+ return { checkpointNamespace: () => namespace, targetTapLayers: () => taps, open, openPrefill: open };
95
+ }
@@ -0,0 +1,336 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { materializeCopy } from "@mlx-bun/mlx/materialize";
3
+ import * as ops from "@mlx-bun/mlx/ops";
4
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
5
+ import { normalizedArgmax } from "../../../kernels/sampling/normalized-argmax";
6
+ import type { RecurrentMtpModule } from "../../../contracts/mlx/drafter";
7
+ import { type MlxRowTable } from "../../../contracts/mlx/graph";
8
+ import { mapOperations, rowOperationFor } from "../../../runtime/row-operations";
9
+ import { applyStateChanges,cleanupFailure,disposeResources } from "../../../runtime/resources";
10
+ import { toLogprobs } from "../../../sampling/index";
11
+ import { BatchedKVCache } from "../../../state/batched-kv";
12
+ import { KVCache } from "../../../state/kv";
13
+ import { DraftVocabularyHead } from "../draft-vocab";
14
+ import type { DraftRowSampling,RecurrentMtpTarget } from "../source";
15
+
16
+ /** Sampling owns each request's RNG policy. Scores are [B,V] log-probabilities;
17
+ * the returned owned [B] token array stays on device through the draft chain. */
18
+ export type MtpRowSampling = DraftRowSampling;
19
+
20
+ /** One draft step's token selection: the module output [B, 1, H] (borrowed) and
21
+ * each row's step in, owned full-vocabulary ids [B] out. */
22
+ type MtpSelect = (output: MlxArray, steps: readonly number[]) => MlxArray;
23
+
24
+ /** The selection the draft group bound, per exact row count of the rows it drafts
25
+ * for: the listed vocabulary's projection and each row's coupled draw over it,
26
+ * the full head and its fused normalized argmax, or the full head and each
27
+ * row's sampler over its log-probabilities. Resolved once when the rows open;
28
+ * `owned` receives the listed projection, which the rows dispose. */
29
+ function bindMtpSelection(target: RecurrentMtpTarget, sampling: MtpRowSampling,
30
+ owned: { vocabulary: DraftVocabularyHead | null }): MlxRowTable<MtpSelect> {
31
+ const selection = sampling.selection;
32
+ if (selection.kind === "listed") {
33
+ // Frequency-ranked draft vocabulary: project onto the listed rows only.
34
+ // The target still verifies over the full vocabulary.
35
+ if (!target.vocabularyHead) throw new Error("a listed draft vocabulary needs the target's quantized vocabulary head");
36
+ const head = owned.vocabulary = new DraftVocabularyHead(target.vocabularyHead, selection.ids);
37
+ return Object.freeze({
38
+ default: (output: MlxArray, steps: readonly number[]) => {
39
+ using subsetLogits = head.project(output);
40
+ using subsetFlat = ops.reshape(subsetLogits, [steps.length, head.size]);
41
+ return selection.sample(subsetFlat, head, steps);
42
+ },
43
+ });
44
+ }
45
+ // Greedy drafting: one fused normalized-argmax over [B,V]; no logsumexp
46
+ // pass, no per-row top-p/top-k sort, no categorical draw.
47
+ if (selection.kind === "greedy") return mapOperations(target.logits, head => (output: MlxArray, steps: readonly number[]) => {
48
+ using logits = head(output);
49
+ using flat = ops.reshape(logits, [steps.length, logits.shape.at(-1)!]);
50
+ return normalizedArgmax(flat);
51
+ }) as MlxRowTable<MtpSelect>;
52
+ return mapOperations(target.logits, head => (output: MlxArray, steps: readonly number[]) => {
53
+ using logits = head(output);
54
+ using flat = ops.reshape(logits, [steps.length, logits.shape.at(-1)!]);
55
+ using logprobs = toLogprobs(flat);
56
+ return sampling.sample(logprobs, steps);
57
+ }) as MlxRowTable<MtpSelect>;
58
+ }
59
+
60
+ /** Prefill membership drafts nothing, so its rows bind no selection. */
61
+ const prefillOnly: MlxRowTable<MtpSelect> = Object.freeze({
62
+ default: () => {
63
+ throw new Error("Qwen MTP prefill rows do not draft");
64
+ },
65
+ });
66
+
67
+ /** The same draft graph and state lifecycle for one or several requests.
68
+ * Admission, target verification, output publication and persistence belong to
69
+ * the caller. Rows remain fixed within a draft/commit round. */
70
+ export class QwenMtpRows {
71
+ #cache = new BatchedKVCache();
72
+ #hidden: MlxArray | null = null;
73
+ #prefilled: boolean[] = [];
74
+ #drafts: number[][] = [];
75
+ #depth = 0;
76
+ /** The listed vocabulary's row subset of the target's projection, when the
77
+ * bound selection drafts over it. */
78
+ readonly #owned: { vocabulary: DraftVocabularyHead | null } = { vocabulary: null };
79
+ /** The draft token selection, bound once from the group's sampling. */
80
+ readonly #select: MlxRowTable<MtpSelect>;
81
+
82
+ constructor(
83
+ readonly target: RecurrentMtpTarget,
84
+ readonly module: RecurrentMtpModule,
85
+ sampling: MtpRowSampling | null,
86
+ states: readonly (MtpRowState | null)[],
87
+ ) {
88
+ this.#select = sampling ? bindMtpSelection(target, sampling, this.#owned) : prefillOnly;
89
+ try { this.append(states); }
90
+ catch (error) { cleanupFailure(error, () => this.dispose()); }
91
+ }
92
+
93
+ get rowCount(): number { return this.#cache.rowOffsets.length; }
94
+
95
+ /** Add committed state at a method boundary. Inputs remain caller-owned. */
96
+ append(states: readonly (MtpRowState | null)[]): void {
97
+ if (!states.length) return;
98
+ applyStateChanges([() => this.prepareAppend(states)]);
99
+ }
100
+
101
+ prepareAppend(states: readonly (MtpRowState | null)[]): PreparedStateChange {
102
+ if (!states.length) return { commit() {}, dispose() {} };
103
+ const prefilled = [...this.#prefilled, ...states.map(state => state !== null)];
104
+ const example = this.#hidden ?? states.find(state => state !== null)?.hidden;
105
+ let hidden: MlxArray | null = null;
106
+ let cache: BatchedKVCache | null = new BatchedKVCache();
107
+ const held: { dispose(): void }[] = [];
108
+ try {
109
+ if (example) {
110
+ const zero = (rows: number) => {
111
+ const array = ops.zeros([rows, 1, this.target.hiddenSize], example.dtype);
112
+ held.push(array); return array;
113
+ };
114
+ hidden = ops.concatAxis([
115
+ ...(this.rowCount ? [this.#hidden ?? zero(this.rowCount)] : []),
116
+ ...states.map(state => state?.hidden ?? zero(1)),
117
+ ], 0);
118
+ }
119
+ cache.mergeRows([...(this.rowCount ? [this.#cache] : []), ...states.map(state => {
120
+ if (state) return state.cache;
121
+ const empty = new KVCache(); held.push(empty); return empty;
122
+ })]);
123
+ } catch (error) { return cleanupFailure(error, () => disposeResources([cache!, ...(hidden ? [hidden] : [])])); }
124
+ finally { disposeResources(held); }
125
+ return {
126
+ commit: () => {
127
+ const previousCache = this.#cache, previousHidden = this.#hidden;
128
+ this.#cache = cache!; this.#hidden = hidden;
129
+ this.#prefilled = prefilled;
130
+ cache = previousCache; hidden = previousHidden;
131
+ },
132
+ dispose: () => {
133
+ const held = [cache, hidden].filter(value => value !== null);
134
+ cache = null; hidden = null;
135
+ disposeResources(held);
136
+ },
137
+ };
138
+ }
139
+
140
+ /** Retire/reorder committed requests without involving their sampler policy. */
141
+ filterRows(keep: readonly number[]): void {
142
+ // A drained group keeps its bound selection for the requests that rejoin it.
143
+ if (!keep.length) { this.#releaseRows(); return; }
144
+ using indices = ops.fromInt32([...keep], [keep.length]);
145
+ const hidden = this.#hidden ? ops.takeAxis(this.#hidden, indices, 0) : null;
146
+ try { this.#cache.filterRows(keep); }
147
+ catch (error) { hidden?.dispose(); throw error; }
148
+ this.#hidden?.dispose(); this.#hidden = hidden;
149
+ this.#prefilled = keep.map(row => this.#prefilled[row]!);
150
+ }
151
+
152
+ /** Build companion KV from borrowed new target tokens/taps at the same B.
153
+ * The first token bridges to each restored row's preceding true hidden.
154
+ * Cold rows discard that bridge. All later rows pair token[p+1] with h[p].
155
+ * Only KV and the final true hidden are evaluated; unused attention/MLP
156
+ * outputs remain outside the evaluated prefill graph. */
157
+ prefill(tokens: MlxArray, context: MlxArray): void {
158
+ const [B, length] = tokens.shape as [number, number];
159
+ const H = context.shape[2]!;
160
+ if (this.#prefilled.some(Boolean)) {
161
+ using first = tokens.slice([0, 0], [B, 1]);
162
+ using embeds = this.target.embed(first);
163
+ const mixed = this.#prefilled.some(value => !value);
164
+ if (mixed) this.#cache.specRoundBegin();
165
+ rowOperationFor(this.module.decode, B)(embeds, this.#hidden!, this.#cache).dispose();
166
+ if (mixed) this.#cache.specRoundRollback(this.#prefilled.map(Number));
167
+ }
168
+ for (let pos = 0; pos + 1 < length; pos += 2048) {
169
+ const end = Math.min(pos + 2048, length - 1);
170
+ using ids = tokens.slice([0, pos + 1], [B, end + 1]);
171
+ using embeds = this.target.embed(ids);
172
+ using hidden = context.slice([0, pos, 0], [B, end, H]);
173
+ // A single position per row appends through the one-position read.
174
+ (end - pos === 1 ? rowOperationFor(this.module.decode, B) : this.module.window)(embeds, hidden, this.#cache).dispose();
175
+ ops.evalAll(this.#cache.state());
176
+ }
177
+ using tail = context.slice([0, length - 1, 0], [B, length, H]);
178
+ const hidden = materializeCopy(tail);
179
+ this.#hidden?.dispose(); this.#hidden = hidden;
180
+ ops.evalAll([hidden, ...this.#cache.state()]);
181
+ this.#prefilled.fill(true);
182
+ }
183
+
184
+ materialize(): void {
185
+ const state = [...this.#cache.state(), ...(this.#hidden ? [this.#hidden] : [])];
186
+ if (state.length) ops.evalAll(state);
187
+ }
188
+
189
+ /** Align the companion to verified external continuations through the same
190
+ * true-hidden prefill graph. Different rows retain different prefix lengths. */
191
+ consume(tokens: MlxArray, context: MlxArray, lengths: readonly number[]): void {
192
+ this.#cache.specRoundBegin();
193
+ this.prefill(tokens, context);
194
+ this.#cache.specRoundRollback([...lengths]);
195
+ using positions = ops.fromInt32(lengths.map(length => length - 1), [lengths.length, 1, 1]);
196
+ const hidden = ops.takeAlongAxis(context, positions, 1);
197
+ this.#hidden!.dispose(); this.#hidden = hidden;
198
+ }
199
+
200
+ /** Nonnegative depth, with every request's first pending token already known.
201
+ * Build the complete device dependency chain before reading proposal IDs. */
202
+ draft(pending: readonly number[], depth: number, steps: readonly number[]): number[][] {
203
+ if (depth === 0) return this.#chain(pending, depth, steps) as number[][];
204
+ using packed = this.#chain(pending, depth, steps) as MlxArray;
205
+ return this.#resolve(packed.toIntTokens(), pending.length, depth, true);
206
+ }
207
+
208
+ /** The same chain with the proposal IDs left on the device ([B, depth]), so the
209
+ * caller can build its verify graph before anything is read back. `resolve`
210
+ * takes the row-major host copy of `tokens`. Null at depth 0. */
211
+ draftDevice(pending: readonly number[], depth: number, steps: readonly number[]):
212
+ { tokens: MlxArray; resolve(read: readonly number[]): number[][] } | null {
213
+ if (depth === 0) return null;
214
+ const B = pending.length;
215
+ using packed = this.#chain(pending, depth, steps) as MlxArray; // [depth * B], position-major
216
+ using grid = ops.reshape(packed, [depth, B]);
217
+ using rows = ops.transposeAxes(grid, [1, 0]);
218
+ return { tokens: ops.contiguous(rows), resolve: read => this.#resolve(read, B, depth, false) };
219
+ }
220
+
221
+ #resolve(read: readonly number[], B: number, depth: number, positionMajor: boolean): number[][] {
222
+ this.#drafts = Array.from({ length: B }, (_, row) =>
223
+ Array.from({ length: depth }, (_, position) => read[positionMajor ? position * B + row : row * depth + position]!));
224
+ return this.#drafts;
225
+ }
226
+
227
+ /** The first proposal `draftDevice` would make from `pending` ([B, 1], owned, left on the device), drafted under a
228
+ * round of the head's own cache that rolls back, so the rows' state is unchanged (`DraftRowGroup.preview`). One
229
+ * position whatever `depth` asks: each further one costs another head step, and one is what a verify round's
230
+ * probe drafts. Null at depth 0. */
231
+ preview(pending: readonly number[], depth: number, steps: readonly number[]): MlxArray | null {
232
+ if (depth === 0) return null;
233
+ const B = pending.length;
234
+ this.#cache.specRoundBegin();
235
+ try {
236
+ using packed = this.#proposals(pending, 1, steps); // [B]
237
+ return ops.reshape(packed, [B, 1]);
238
+ } finally { this.#cache.specRoundRollback(0); }
239
+ }
240
+
241
+ /** Build the draft chain. Returns the packed device tokens [depth * B]
242
+ * (position-major), or the empty proposals at depth 0. */
243
+ #chain(pending: readonly number[], depth: number, steps: readonly number[]): MlxArray | number[][] {
244
+ const B = pending.length;
245
+ this.#cache.specRoundBegin();
246
+ // A zero-proposal round still processes the pending target token. Keep
247
+ // its companion KV aligned so a later round can resume speculation.
248
+ if (depth === 0) {
249
+ using ids = ops.fromInt32([...pending], [B, 1]);
250
+ using embeds = this.target.embed(ids);
251
+ rowOperationFor(this.module.decode, B)(embeds, this.#hidden!, this.#cache).dispose();
252
+ this.#drafts = Array.from({ length: B }, () => []);
253
+ this.#depth = 0;
254
+ this.#hidden!.dispose(); this.#hidden = null;
255
+ return this.#drafts;
256
+ }
257
+ const packed = this.#proposals(pending, depth, steps);
258
+ this.#depth = depth;
259
+ this.#hidden!.dispose(); this.#hidden = null;
260
+ return packed;
261
+ }
262
+
263
+ /** The draft steps from each row's pending token and the true hidden before it, appended to the head's cache:
264
+ * the packed device tokens [depth * B] (position-major), owned. Changes no other state. */
265
+ #proposals(pending: readonly number[], depth: number, steps: readonly number[]): MlxArray {
266
+ const B = pending.length;
267
+ const tokens: MlxArray[] = [];
268
+ let ids: MlxArray | null = ops.fromInt32([...pending], [B, 1]);
269
+ let chained: MlxArray | null = null;
270
+ try {
271
+ const select = rowOperationFor(this.#select, B);
272
+ for (let position = 0; position < depth; position++) {
273
+ using embeds = this.target.embed(ids!);
274
+ const output = rowOperationFor(this.module.decode, B)(embeds, chained ?? this.#hidden!, this.#cache);
275
+ chained?.dispose(); chained = output;
276
+ const token = select(output, steps.map(step => step + position));
277
+ tokens.push(token);
278
+ ids!.dispose(); ids = ops.reshape(token, [B, 1]);
279
+ }
280
+ return ops.concatAxis(tokens, 0);
281
+ } finally {
282
+ ids?.dispose(); chained?.dispose();
283
+ for (const token of tokens) token.dispose();
284
+ }
285
+ }
286
+
287
+ /** Resolve draft storage using each request's accepted prefix. The verify
288
+ * context is borrowed [B,depth+1,H], before the target's final norm. */
289
+ commit(accepted: readonly number[], context: MlxArray): void {
290
+ const B = accepted.length, depth = this.#depth;
291
+ const appended = Math.max(1, depth);
292
+ const kept = accepted.map(count => Math.min(count + 1, appended));
293
+ if (kept.every(count => count === appended)) this.#cache.specRoundCommit();
294
+ else this.#cache.specRoundRollback(kept);
295
+
296
+ // Full acceptance needs the last accepted token's missing draft row,
297
+ // using the TRUE target hidden at its preceding position. Other requests
298
+ // keep their current coverage; their temporary append is never retained.
299
+ const extra = accepted.map(count => Number(depth > 0 && count === depth));
300
+ if (extra.some(Boolean)) {
301
+ using ids = ops.fromInt32(this.#drafts.map(row => row[depth - 1]!), [B, 1]);
302
+ using embeds = this.target.embed(ids);
303
+ using hidden = context.slice([0, depth - 1, 0], [B, depth, context.shape[2]!]);
304
+ this.#cache.specRoundBegin();
305
+ rowOperationFor(this.module.decode, B)(embeds, hidden, this.#cache).dispose();
306
+ this.#cache.specRoundRollback(extra);
307
+ }
308
+ using positions = ops.fromInt32([...accepted], [B, 1, 1]);
309
+ this.#hidden = ops.takeAlongAxis(context, positions, 1);
310
+ this.#drafts = []; this.#depth = 0;
311
+ }
312
+
313
+ /** Only between committed rounds. Returned state is independent of the
314
+ * group's storage and uses the existing single-request cache representation. */
315
+ extractRow(row: number): MtpRowState {
316
+ const cache = this.#cache.extractRow(row);
317
+ try {
318
+ using view = this.#hidden!.slice([row, 0, 0], [row + 1, 1, this.#hidden!.shape[2]!]);
319
+ return { cache, hidden: materializeCopy(view) };
320
+ } catch (error) { cache.dispose(); throw error; }
321
+ }
322
+
323
+ /** Release every row's state; the bound selection stays. */
324
+ #releaseRows(): void {
325
+ this.#cache.dispose(); this.#hidden?.dispose(); this.#hidden = null;
326
+ this.#drafts = []; this.#depth = 0; this.#prefilled = [];
327
+ }
328
+
329
+ dispose(): void {
330
+ this.#owned.vocabulary?.dispose(); this.#owned.vocabulary = null;
331
+ this.#releaseRows();
332
+ }
333
+ }
334
+
335
+ import { MtpRowState } from "../../../state/speculative/qwen-mtp-state";
336
+ export { type MtpRowState } from "../../../state/speculative/qwen-mtp-state";
@@ -0,0 +1,63 @@
1
+ import type { MlxAutoregressiveBinding } from "../../bindings/autoregressive";
2
+ import { MlxAutoregressiveDraftRows, type MlxDraftLayout } from "./autoregressive-rows";
3
+ import { prefillCacheLayout } from "../../../state/layout";
4
+ import { MlxStateRows } from "../../../state/rows";
5
+ import { captureCacheAttachment, restoreCacheAttachment } from "../../../state/attachment";
6
+ import { applyStateChanges, cleanupFailure, disposeResources } from "../../../runtime/resources";
7
+ import type { Cache } from "../../../contracts/mlx/cache";
8
+ import type { DraftPrefillGroup, DraftRowGroup, DraftRowSampling, GroupedDraftProvider } from "../source";
9
+ import type { DraftRowCheckpoint } from "../../../contracts/mlx/draft-checkpoint";
10
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
11
+ import type { MlxArray } from "@mlx-bun/mlx/array";
12
+
13
+ export interface StandaloneDraftRows extends DraftPrefillGroup {
14
+ draft: DraftRowGroup["draft"];
15
+ commit(accepted: readonly number[], context?: MlxArray): Promise<void>;
16
+ }
17
+
18
+ /** The standalone model supplies graph/state capabilities once. Target
19
+ * verification, scheduling and cache tiers use the existing provider ports. */
20
+ export function openStandaloneDraftRows(binding: MlxAutoregressiveBinding, namespace: string,
21
+ sampling: DraftRowSampling | null, checkpoints: readonly (DraftRowCheckpoint | null)[]): StandaloneDraftRows {
22
+ const empty = binding.makeCache(), layouts: MlxDraftLayout[] = [];
23
+ try { for (const cache of empty) layouts.push(prefillCacheLayout(cache) as MlxDraftLayout); }
24
+ catch (error) { disposeResources(layouts); throw error; }
25
+ finally { disposeResources(empty); }
26
+ const state = new MlxStateRows(layouts), rows = new MlxAutoregressiveDraftRows(binding.phases, state, sampling);
27
+ const prepareAppend = (checkpoints: readonly (DraftRowCheckpoint | null)[]): PreparedStateChange => {
28
+ const restored: Cache[][] = [], pending: Array<number | null> = [];
29
+ try {
30
+ for (const checkpoint of checkpoints) {
31
+ restored.push(checkpoint ? restoreCacheAttachment(checkpoint.attachment, binding.makeCache) : binding.makeCache());
32
+ pending.push(checkpoint && checkpoint.attachment.metadata.pendingLast !== false ? Number(checkpoint.attachment.metadata.pendingLast) : null);
33
+ }
34
+ return rows.prepareAppend(restored, pending);
35
+ } finally { disposeResources(restored.flat()); }
36
+ };
37
+ const append = (checkpoints: readonly (DraftRowCheckpoint | null)[]) => applyStateChanges([() => prepareAppend(checkpoints)]);
38
+ try { append(checkpoints); }
39
+ catch (error) { return cleanupFailure(error, () => rows.dispose()); }
40
+ return {
41
+ namespace, prefillMode: "tail-split", tapLayers: [],
42
+ get rowCount() { return state.rowCount; },
43
+ append, prepareAppend, prefill: tokens => rows.prefill(tokens), materialize: () => rows.materialize(),
44
+ filterRows: keep => rows.filterRows(keep), draft: rows.draft.bind(rows), commit: accepted => rows.commit(accepted),
45
+ capture(row): DraftRowCheckpoint {
46
+ const caches = state.extractRow(row), pendingLast = rows.pendingLast[row] ?? null;
47
+ try {
48
+ const processedTokens = caches[0]!.offset + Number(pendingLast !== null);
49
+ return { processedTokens, attachment: captureCacheAttachment("standalone-draft-v1", caches,
50
+ { processedTokens, pendingLast: pendingLast ?? false }) };
51
+ } finally { disposeResources(caches); }
52
+ },
53
+ dispose: () => rows.dispose(),
54
+ };
55
+ }
56
+
57
+ export function standaloneDraftGroups(binding: MlxAutoregressiveBinding, namespace: string): GroupedDraftProvider {
58
+ return {
59
+ checkpointNamespace: () => namespace,
60
+ open: options => openStandaloneDraftRows(binding, namespace, options.sampling, options.checkpoints),
61
+ openPrefill: options => openStandaloneDraftRows(binding, namespace, null, options.checkpoints),
62
+ };
63
+ }
@@ -0,0 +1,16 @@
1
+ import type { GenerateOptions } from "../index";
2
+
3
+ /** Snapshot the request's configuration when it is bound. The KV scheme is
4
+ * the composition's, shared by every request, so nothing here copies it. */
5
+ export function captureSpeculativeOptions(options: GenerateOptions): GenerateOptions {
6
+ return { ...options };
7
+ }
8
+
9
+ /** The prompt-cache namespace of a speculative method's entries: its method
10
+ * namespace and adapters. Execution and persistence consume the same captured
11
+ * settings; the store holding the entries is keyed by the composition's KV
12
+ * scheme. */
13
+ export function speculativePrefixNamespace(method: string | undefined, adapters: string | string[],
14
+ _options: GenerateOptions): string {
15
+ return JSON.stringify({ method, adapters });
16
+ }
@@ -0,0 +1,149 @@
1
+ import { isAssistantModelType, isDeepspecDrafterConfig, isDflash2DrafterConfig, isMtpModelType, NATIVE_MTP_DRAFT } from "../../models/drafters";
2
+ import type { DraftProvider } from "./source";
3
+ import { DraftProviderRegistry, type DraftLoadRequest, type DraftProviderKind, type LoadedDraft } from "./draft-registry";
4
+
5
+ export const DRAFT_KINDS = ["dspark", "deepspec", "dflash2", "assistant", "mtp", "two-model", "ngram"] as const;
6
+ export type DraftKind = typeof DRAFT_KINDS[number];
7
+
8
+ /** The library's draft providers. Each entry is the provider's own convention
9
+ * for recognizing its artifact (files only, never MLX) and for loading it; the
10
+ * provider modules load on demand. All providers share ONE serve loop
11
+ * (run.ts). "ngram" has no artifact (model-free prompt lookup,
12
+ * sources/ngram-source.ts) and is chosen only by name.
13
+ * The two-model provider, a full second model, is the catch-all: it accepts
14
+ * whatever no other provider recognizes. */
15
+ export function defaultDraftProviders(): DraftProviderRegistry {
16
+ return new DraftProviderRegistry()
17
+ .register(dspark).register(deepspec).register(dflash2).register(assistant).register(mtp).register(twoModel).register(ngram)
18
+ .registerNative(nativeMtp);
19
+ }
20
+
21
+ /** Detect the draft artifact's kind among the library's providers. */
22
+ export async function detectDraftKind(dir: string): Promise<DraftKind> {
23
+ return await defaultDraftProviders().detect(dir) as DraftKind;
24
+ }
25
+
26
+ const artifactDir = (request: DraftLoadRequest): string => {
27
+ if (request.dir === undefined) throw new Error("this draft provider loads an artifact directory");
28
+ return request.dir;
29
+ };
30
+ const fixedWidth = (request: DraftLoadRequest, fallback: number) => Math.max(1, request.numDraftTokens ?? fallback);
31
+ /** A block-trained drafter never verifies more positions than it was trained for. */
32
+ const pinned = (request: DraftLoadRequest, gamma: number) => Math.max(1, Math.min(request.numDraftTokens ?? gamma, gamma));
33
+
34
+ /** Our trained module: dspark.json beside the weights. */
35
+ const dspark: DraftProviderKind = {
36
+ kind: "dspark", artifact: true,
37
+ detect: artifact => artifact.has("dspark.json"),
38
+ async load(request) {
39
+ const { DflashProvider } = await import("./sources/dflash-source");
40
+ const provider = await DflashProvider.load(artifactDir(request));
41
+ return { provider, numDraftTokens: pinned(request, provider.gamma) };
42
+ },
43
+ };
44
+
45
+ /** DeepSeek's released DSpark drafters (DeepSpec reference): no dspark.json,
46
+ * a plain HF config stamped Gemma4DSparkModel. The width pins to their
47
+ * config's block_size (e.g. 7 for dspark_gemma4_12b_block7). */
48
+ const deepspec: DraftProviderKind = {
49
+ kind: "deepspec", artifact: true,
50
+ detect: async artifact => isDeepspecDrafterConfig(await artifact.config()),
51
+ async load(request) {
52
+ const { DeepspecProvider } = await import("./sources/deepspec-source");
53
+ const provider = await DeepspecProvider.load(artifactDir(request));
54
+ return { provider, numDraftTokens: pinned(request, provider.gamma) };
55
+ },
56
+ };
57
+
58
+ /** DFlash 2 block drafters (z-lab/incoai): a plain HF config stamped
59
+ * DFlash2DraftModel. The width pins to block_size − 1 (7). Projections are
60
+ * quantized to 4 bits at load (1.0 GB resident instead of the 3.85 GB BF16
61
+ * checkpoint). */
62
+ const dflash2: DraftProviderKind = {
63
+ kind: "dflash2", artifact: true,
64
+ detect: async artifact => isDflash2DrafterConfig(await artifact.config()),
65
+ async load(request) {
66
+ const { Dflash2Provider } = await import("./sources/dflash2-source");
67
+ const provider = await Dflash2Provider.load(artifactDir(request), { bits: 4 });
68
+ return { provider, numDraftTokens: pinned(request, provider.gamma) };
69
+ },
70
+ };
71
+
72
+ /** The optiq KV-borrowing drafter (a *_assistant config). */
73
+ const assistant: DraftProviderKind = {
74
+ kind: "assistant", artifact: true,
75
+ detect: async artifact => isAssistantModelType(String((await artifact.config())?.model_type ?? "")),
76
+ async load(request) {
77
+ const { AssistantProvider } = await import("./sources/assistant-source");
78
+ return { provider: await AssistantProvider.load(artifactDir(request)), numDraftTokens: fixedWidth(request, 3) };
79
+ },
80
+ };
81
+
82
+ /** Native MTP heads split from a qwen3_5-family release
83
+ * (mlx-community/Qwen3.8-27B-MTP-*). The target's recurrent DeltaNet caches
84
+ * roll back via the serve loop's spec-round snapshot/replay contract
85
+ * (SSMCache.specRound*). A target artifact may bundle the head at `mtp/`. */
86
+ const mtp: DraftProviderKind = {
87
+ kind: "mtp", artifact: true, bundledCompanion: "mtp",
88
+ detect: async artifact => isMtpModelType(String((await artifact.config())?.model_type ?? "")),
89
+ async load(request) {
90
+ const dir = artifactDir(request);
91
+ const { QwenMtpProvider } = await import("./sources/qwen-mtp-source");
92
+ const provider = await QwenMtpProvider.load(dir);
93
+ // Default the round width to the head's trained block (block_size 3 → 2
94
+ // recursive drafts + the pending row per round; the head was trained
95
+ // multi-step, so an explicit larger width is allowed but acceptance
96
+ // decides whether it pays). A graph may declare the adaptive width it
97
+ // measured for this kind (`adaptiveDraftWidths`).
98
+ const block = (await Bun.file(`${dir}/config.json`).json() as { block_size?: number }).block_size;
99
+ const numDraftTokens = request.numDraftTokens === undefined && typeof block === "number"
100
+ ? Math.max(1, block - 1) : fixedWidth(request, 3);
101
+ return { provider, numDraftTokens };
102
+ },
103
+ };
104
+
105
+ const PROBE = "The 3 quick brown foxes jumped över the lazy dog?! 🦊";
106
+
107
+ /** A full second model (mlx-lm parity). It ships its own tokenizer, and exact
108
+ * token-match acceptance is meaningful only when both models tokenize
109
+ * identically: a probe that ENCODES differently means different families —
110
+ * refuse instead of silently accepting ~0% of drafts. */
111
+ const twoModel: DraftProviderKind = {
112
+ kind: "two-model", artifact: true, fallback: true,
113
+ detect: async () => true,
114
+ async load(request) {
115
+ const dir = artifactDir(request);
116
+ const { TwoModelProvider } = await import("./sources/two-model");
117
+ const { loadTokenizer } = await import("../../input");
118
+ const provider = await TwoModelProvider.load(dir, request.target.vocabSize);
119
+ try {
120
+ const draftTokenizer = await loadTokenizer(dir);
121
+ if (JSON.stringify(request.target.tokenizer.encode(PROBE)) !== JSON.stringify(draftTokenizer.encode(PROBE)))
122
+ throw new Error(
123
+ `the draft model's tokenizer differs from the target's (probe string encodes ` +
124
+ `differently) — speculation needs the same tokenizer family`);
125
+ } catch (error) { provider.dispose(); throw error; }
126
+ return { provider, numDraftTokens: fixedWidth(request, 3) };
127
+ },
128
+ };
129
+
130
+ /** Model-free prompt lookup: no artifact, weightsBytes 0, open() never throws.
131
+ * Default γ=10 per the reference implementation — drafting is free, so wide
132
+ * blocks cost only verify-window width when wrong. */
133
+ const ngram: DraftProviderKind = {
134
+ kind: "ngram", artifact: false,
135
+ async load(request): Promise<LoadedDraft> {
136
+ const { NgramProvider } = await import("./sources/ngram-source");
137
+ return { provider: new NgramProvider({ max: request.ngram?.max, min: request.ngram?.min }), numDraftTokens: fixedWidth(request, 10) };
138
+ },
139
+ };
140
+
141
+ /** The checkpoint-native MTP row of a graph that declares it: the graph
142
+ * declares the head and its width; this binds the provider that drives it. */
143
+ const nativeMtp = {
144
+ kind: NATIVE_MTP_DRAFT,
145
+ async create(graph: object): Promise<DraftProvider> {
146
+ const { Glm52NativeMtpProvider } = await import("./sources/glm52-mtp-source");
147
+ return new Glm52NativeMtpProvider(graph);
148
+ },
149
+ };