@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,102 @@
1
+ import { rowOperationFor } from "../../../runtime/row-operations";
2
+ import type { MlxArray } from "@mlx-bun/mlx/array";
3
+ import * as ops from "@mlx-bun/mlx/ops";
4
+ import { materializeCopy } from "@mlx-bun/mlx/materialize";
5
+ import { disposeResources, applyStateChanges } from "../../../runtime/resources";
6
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
7
+ import { targetLacks, type AssistantRowsTarget, type DraftPrefillGroup, type DraftRowGroup, type GroupedDraftProvider, type TargetView } from "../source";
8
+ import { DRAFT_CHECKPOINT_SCHEMA, type DraftRowCheckpoint } from "../../../contracts/mlx/draft-checkpoint";
9
+ import type { AssistantDrafterModel } from "../../../contracts/mlx/drafter";
10
+
11
+ /** The assistant owns only its last true target hidden and coverage. Donor
12
+ * attention stays with target storage; checkpoints contain no duplicate KV. */
13
+ class AssistantRows implements DraftPrefillGroup, DraftRowGroup {
14
+ readonly prefillMode = "tail-split" as const;
15
+ readonly tapLayers: readonly number[] = [];
16
+ #hidden: MlxArray | null = null;
17
+ #processed: number[] = [];
18
+ constructor(readonly target: AssistantRowsTarget, readonly drafter: AssistantDrafterModel,
19
+ readonly namespace: string, checkpoints: readonly (DraftRowCheckpoint | null)[]) { this.append(checkpoints); }
20
+ get rowCount(): number { return this.#processed.length; }
21
+ append(checkpoints: readonly (DraftRowCheckpoint | null)[]): void {
22
+ if (checkpoints.length) applyStateChanges([() => this.prepareAppend(checkpoints)]);
23
+ }
24
+ prepareAppend(checkpoints: readonly (DraftRowCheckpoint | null)[]): PreparedStateChange {
25
+ const processed = [...this.#processed, ...checkpoints.map(checkpoint => checkpoint?.processedTokens ?? 0)];
26
+ const example = this.#hidden ?? checkpoints.find(checkpoint => checkpoint !== null)?.attachment.tensors[0];
27
+ const held: MlxArray[] = []; let hidden: MlxArray | null = null;
28
+ try {
29
+ for (const checkpoint of checkpoints) if (checkpoint && (checkpoint.attachment.schema !== DRAFT_CHECKPOINT_SCHEMA.assistant ||
30
+ checkpoint.attachment.metadata.processedTokens !== checkpoint.processedTokens))
31
+ throw new Error("invalid assistant checkpoint alignment");
32
+ if (example) {
33
+ const zero = (rows: number) => { const value = ops.zeros([rows,1,this.target.hiddenSize],example.dtype); held.push(value); return value; };
34
+ hidden = ops.concatAxis([...(this.rowCount ? [this.#hidden ?? zero(this.rowCount)] : []),
35
+ ...checkpoints.map(checkpoint => checkpoint?.attachment.tensors[0] ?? zero(1))],0);
36
+ }
37
+ } finally { disposeResources(held); }
38
+ return { commit: () => { const old = this.#hidden; this.#hidden=hidden; hidden=old; this.#processed=processed; },
39
+ dispose: () => { hidden?.dispose(); hidden=null; } };
40
+ }
41
+ filterRows(keep: readonly number[]): void {
42
+ if (!keep.length) { this.dispose(); return; }
43
+ using indices = ops.fromInt32([...keep],[keep.length]);
44
+ const next = this.#hidden && keep.length ? ops.takeAxis(this.#hidden,indices,0) : null;
45
+ this.#hidden?.dispose(); this.#hidden=next; this.#processed=keep.map(row=>this.#processed[row]!);
46
+ }
47
+ prefill(tokens: MlxArray, context?: MlxArray): void {
48
+ const [B,N] = tokens.shape as [number,number];
49
+ using tail = context!.slice([0,N-1,0],[B,N,context!.shape[2]!]);
50
+ const hidden = materializeCopy(tail);
51
+ this.#hidden?.dispose(); this.#hidden=hidden; this.#processed=this.#processed.map(count=>count+N);
52
+ }
53
+ materialize(): void { this.#hidden?.eval(); }
54
+ capture(row: number): DraftRowCheckpoint {
55
+ const processedTokens=this.#processed[row]!;
56
+ const hidden=this.#hidden!.slice([row,0,0],[row+1,1,this.target.hiddenSize]);
57
+ return { processedTokens, attachment: { schema:DRAFT_CHECKPOINT_SCHEMA.assistant,metadata:{processedTokens},tensors:[hidden] } };
58
+ }
59
+ draft(pending: readonly number[], depth: number, _steps: readonly number[]): number[][] {
60
+ if (!depth) return pending.map(()=>[]);
61
+ const B=pending.length, owned: MlxArray[]=[];
62
+ const donors=this.target.readDonors();
63
+ let ids=ops.fromInt32([...pending],[B,1]), hidden=this.#hidden!;
64
+ const tokens: MlxArray[]=[];
65
+ const forward=rowOperationFor(this.drafter.forwardRows, B);
66
+ try {
67
+ for(let step=0;step<depth;step++) {
68
+ using emb=this.target.embed(ids);
69
+ const positions=donors.positions.map(position=>position+step);
70
+ const common=positions.every(position=>position===positions[0]);
71
+ using offsets=common ? null : ops.fromInt32(positions,[B]);
72
+ const next=forward(emb,hidden,donors,offsets ?? positions[0]!);
73
+ owned.push(next.nextHidden); tokens.push(next.tokens); hidden=next.nextHidden;
74
+ ids.dispose(); ids=ops.reshape(next.tokens,[B,1]);
75
+ }
76
+ using packed=ops.concatAxis(tokens,0);
77
+ const values=packed.toIntTokens();
78
+ return pending.map((_,row)=>Array.from({length:depth},(_,step)=>values[step*B+row]!));
79
+ } finally { ids.dispose(); donors.dispose(); disposeResources([...owned,...tokens]); }
80
+ }
81
+ commit(accepted: readonly number[], context: MlxArray): void {
82
+ using indices=ops.fromInt32([...accepted],[accepted.length,1,1]);
83
+ const hidden=ops.takeAlongAxis(context,indices,1);
84
+ this.#hidden?.dispose(); this.#hidden=hidden;
85
+ this.#processed=this.#processed.map((count,row)=>count+accepted[row]!+1);
86
+ }
87
+ dispose(): void { this.#hidden?.dispose(); this.#hidden=null; this.#processed=[]; }
88
+ }
89
+
90
+ /** A target without the donor port refuses the pairing before any row state exists. */
91
+ function assistantTarget(target: TargetView): AssistantRowsTarget {
92
+ if (!target.assistantRows) throw targetLacks("assistantRows");
93
+ return target.assistantRows;
94
+ }
95
+
96
+ export function assistantGroups(drafter: AssistantDrafterModel, namespace: string): GroupedDraftProvider {
97
+ return {
98
+ checkpointNamespace: () => namespace,
99
+ openPrefill: ({target,checkpoints}) => new AssistantRows(assistantTarget(target),drafter,namespace,checkpoints),
100
+ open: ({target,checkpoints}) => new AssistantRows(assistantTarget(target),drafter,namespace,checkpoints),
101
+ };
102
+ }
@@ -0,0 +1,124 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+ import type { MlxPhaseDispatch } from "../../phase-dispatch";
4
+ import type { Cache, BatchableCache, PaddedPrefillCache } from "../../../contracts/mlx/cache";
5
+ import type { DraftRowSampling } from "../source";
6
+ import { toLogprobs } from "../../../sampling/index";
7
+ import { disposeResources } from "../../../runtime/resources";
8
+ import { MlxStateRows } from "../../../state/rows";
9
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
10
+ import { evalCacheState } from "../../prefill";
11
+
12
+ export interface MlxDraftLayout extends BatchableCache, PaddedPrefillCache {
13
+ makeEmptyBatch(): MlxDraftLayout;
14
+ }
15
+
16
+ /** The draft graph and sampler operate at B. State snapshots own rewind and
17
+ * membership independently of the graph, target verifier and scheduler. */
18
+ export class MlxAutoregressiveDraftRows {
19
+ #history: MlxStateRows[] = [];
20
+ #drafts: number[][] = [];
21
+ #depth = 0;
22
+ #pendingLast: Array<number | null>;
23
+ constructor(readonly phases: MlxPhaseDispatch,
24
+ readonly state: MlxStateRows<MlxDraftLayout>, readonly sampling: DraftRowSampling | null) {
25
+ this.#pendingLast = Array.from({ length: state.rowCount }, () => null);
26
+ }
27
+ get pendingLast(): readonly (number | null)[] { return this.#pendingLast; }
28
+ prepareAppend(rows: readonly (readonly Cache[])[], pending: readonly (number | null)[]): PreparedStateChange {
29
+ const state = this.state.prepareAppendMany(rows), next = [...this.#pendingLast, ...pending];
30
+ return { commit: () => { state.commit(); this.#pendingLast = next; }, dispose: () => state.dispose() };
31
+ }
32
+ filterRows(keep: readonly number[]): void {
33
+ this.state.filterRows(keep); this.#pendingLast = keep.map(row => this.#pendingLast[row]!);
34
+ }
35
+
36
+ /** One draft-model forward of `sequences` rows of `width` positions: a block of
37
+ * known tokens (`prefillTail`), or one position per row (`decode`). */
38
+ async #forward(ids: MlxArray, sequences: number, width: number, lengths?: readonly number[]): Promise<MlxArray> {
39
+ const padded = lengths?.some(length => length !== width);
40
+ if (padded) for (const cache of this.state.caches) cache.preparePrefill({
41
+ lengths: [...lengths!], rightPadding: lengths!.map(length => width - length),
42
+ });
43
+ const hidden = await this.phases.prompt(width === 1 ? "decode" : "tail", sequences, width)(ids, this.state.caches, null);
44
+ try {
45
+ if (padded) for (const cache of this.state.caches) cache.finalizePrefill();
46
+ return hidden;
47
+ } catch (error) { hidden.dispose(); throw error; }
48
+ }
49
+
50
+ /** A restored full-accept boundary may have one unprocessed draft token.
51
+ * Combine it with the incoming block on device, retaining per-row lengths. */
52
+ async prefill(tokens: MlxArray): Promise<void> {
53
+ const [B, N] = tokens.shape as [number, number];
54
+ if (this.#pendingLast.some(token => token !== null)) {
55
+ using previous = ops.fromInt32(this.#pendingLast.map(token => token ?? 0), [B, 1]);
56
+ using joined = ops.concatAxis([previous, tokens], 1);
57
+ const lengths = this.#pendingLast.map(token => N + Number(token !== null));
58
+ using indices = ops.fromInt32(lengths.flatMap(length => Array.from({ length: N + 1 },
59
+ (_, position) => length === N + 1 ? position : Math.min(position + 1, N))), [B, N + 1]);
60
+ using inputs = ops.takeAlongAxis(joined, indices, 1);
61
+ (await this.#forward(inputs, B, N + 1, lengths)).dispose();
62
+ } else (await this.#forward(tokens, B, N)).dispose();
63
+ this.#pendingLast.fill(null); this.materialize();
64
+ }
65
+ materialize(): void { evalCacheState(this.state.caches); }
66
+
67
+ async draft(pending: readonly number[], depth: number, steps: readonly number[]): Promise<number[][]> {
68
+ this.#releaseHistory();
69
+ this.#depth = depth;
70
+ const tokens: MlxArray[] = [];
71
+ const feeds = pending.map((token, row) => this.#pendingLast[row] == null ? [token] : [this.#pendingLast[row]!, token]);
72
+ const lengths = feeds.map(feed => feed.length), width = Math.max(...lengths);
73
+ let ids = ops.fromInt32(feeds.flatMap(feed => [...feed, ...Array(width - feed.length).fill(0)]), [pending.length, width]);
74
+ try {
75
+ // With zero proposals, consume the pending input to keep companion
76
+ // coverage aligned with the target's one-token verification.
77
+ for (let position = 0; position < Math.max(1, depth); position++) {
78
+ const padded = position === 0 && lengths.some(length => length !== width);
79
+ const positions = position === 0 ? width : 1;
80
+ using hidden = await this.#forward(ids, pending.length, positions, position === 0 ? lengths : undefined);
81
+ this.#history.push(this.state.clone());
82
+ if (depth === 0) break;
83
+ using selection = padded ? ops.fromInt32(lengths.map(length => length - 1), [pending.length, 1, 1]) : null;
84
+ using selected = selection ? ops.takeAlongAxis(hidden, selection, 1)
85
+ : hidden.slice([0, positions - 1, 0], [pending.length, positions, hidden.shape[2]!]);
86
+ using logits = this.phases.logits(pending.length)(selected);
87
+ using flat = ops.reshape(logits, [pending.length, logits.shape.at(-1)!]);
88
+ using logprobs = toLogprobs(flat);
89
+ const token = this.sampling!.sample(logprobs, steps.map(step => step + position));
90
+ tokens.push(token);
91
+ ids.dispose(); ids = ops.reshape(token, [pending.length, 1]);
92
+ }
93
+ if (depth === 0) this.#drafts = pending.map(() => []);
94
+ else {
95
+ using packed = ops.concatAxis(tokens, 0);
96
+ const values = packed.toIntTokens();
97
+ this.#drafts = pending.map((_, row) => Array.from({ length: depth },
98
+ (_, position) => values[position * pending.length + row]!));
99
+ }
100
+ return this.#drafts;
101
+ } catch (error) { this.#releaseHistory(); throw error; }
102
+ finally { ids.dispose(); disposeResources(tokens); }
103
+ }
104
+
105
+ /** Preserve mlx-lm's full-accept re-feed shape: the final draft remains
106
+ * pending and joins the next target token in one forward. Recurrent state
107
+ * uses saved boundaries, never trimming. The lag is companion state. */
108
+ async commit(accepted: readonly number[]): Promise<void> {
109
+ const selected: Cache[][] = [];
110
+ try {
111
+ if (!accepted.every(count => count >= this.#depth - 1)) {
112
+ for (const [row, count] of accepted.entries())
113
+ selected.push(this.#history[Math.min(count, Math.max(0, this.#depth - 1))]!.extractRow(row));
114
+ this.state.mergeRows(selected);
115
+ }
116
+ } finally { disposeResources(selected.flat()); this.#releaseHistory(); }
117
+
118
+ this.#pendingLast = accepted.map((count, row) => this.#depth > 0 && count === this.#depth ? this.#drafts[row]!.at(-1)! : null);
119
+ this.#drafts = []; this.#depth = 0;
120
+ }
121
+
122
+ #releaseHistory(): void { disposeResources(this.#history.splice(0)); }
123
+ dispose(): void { this.#releaseHistory(); this.#drafts = []; this.state.dispose(); }
124
+ }
@@ -0,0 +1,106 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+ import type { Cache } from "../../../contracts/mlx/cache";
4
+ import { type MlxLayerCapture } from "../../../contracts/mlx/graph";
5
+ import type { MlxTokenGraph } from "../../../models/graph";
6
+ import { declaredGraph, tokenPhasesOf } from "../../../models/capabilities";
7
+ import type { MlxForwardWork } from "../../../contracts/mlx/forward-work";
8
+ import { COMPOSITION_DEFAULTS } from "../../../contracts/portable/composition";
9
+ import { MlxPhaseDispatch, promptWorkStep, verifyWorkStep, type MlxPromptStep, type MlxVerifyWindow } from "../../phase-dispatch";
10
+
11
+ /** A target forward's post-final-norm hidden and, when the drafter taps layers,
12
+ * the tapped layers concatenated on the feature axis [B, L, m*H] (null
13
+ * otherwise). Both belong to the caller. */
14
+ export interface MlxTargetForward {
15
+ readonly hidden: MlxArray;
16
+ readonly ctxML: MlxArray | null;
17
+ }
18
+
19
+ /** The target operations the speculative group and the fill group call, bound
20
+ * once from the graph's phases. A replacement graph supplies this entire port,
21
+ * including any hidden taps. The verifier never needs a MlxTokenGraph or an
22
+ * artifact-family check. Each forward names its phase; `work`, when given, is
23
+ * the mixed iteration's hook and runs the same rows inside a shared forward. */
24
+ export interface MlxSpeculativeTargetBinding {
25
+ /** Whether prompts leave their last token pending for the first verify window
26
+ * (the composition's `prefillTailSplit`). */
27
+ readonly prefillTailSplit: boolean;
28
+ /** The graph's composed caches for one row. */
29
+ makeCache(): Cache[];
30
+ /** One prompt step in the phase its planner named, capturing `tapLayers`. */
31
+ prefill(ids: MlxArray, caches: Cache[], step: MlxPromptStep, tapLayers?: readonly number[],
32
+ work?: MlxForwardWork): Promise<MlxTargetForward>;
33
+ /** One new position per sequence through the graph's decode phase, capturing `tapLayers`: a round without
34
+ * drafts, the step plain decode runs. */
35
+ decode(ids: MlxArray, caches: Cache[], tapLayers?: readonly number[], work?: MlxForwardWork): Promise<MlxTargetForward>;
36
+ /** A verify window, capturing `tapLayers`. */
37
+ verify(ids: MlxArray, caches: Cache[], window: MlxVerifyWindow, tapLayers?: readonly number[],
38
+ work?: MlxForwardWork): Promise<MlxTargetForward>;
39
+ /** Whether the forwards can capture these hidden layers (a provider's declared taps). */
40
+ supportsTapLayers(layers: readonly number[]): boolean;
41
+ /** The head over a hidden of `rows` rows (sequences times positions). */
42
+ projectLogits(hidden: MlxArray, rows: number): MlxArray;
43
+ }
44
+
45
+ /** The target graph's phase operations for speculation, bound once. */
46
+ export function bindSpeculativeTargetModel(model: MlxTokenGraph): MlxSpeculativeTargetBinding {
47
+ const dispatch = new MlxPhaseDispatch(tokenPhasesOf(model));
48
+ /** The decoder's layer count, the post-final-norm capture's index; read where a tap needs it. */
49
+ const layerCount = () => model.config.text.numHiddenLayers;
50
+ return {
51
+ prefillTailSplit: (model.composition ?? COMPOSITION_DEFAULTS).prefillTailSplit,
52
+ makeCache: model.makeCache.bind(model),
53
+ prefill: (ids, caches, step, tapLayers, work) => withCapture(tapLayers, layerCount, capture => {
54
+ const own = async () => dispatch.prompt(step.phase, step.sequences, step.tokens)(ids, caches, capture);
55
+ return work ? work(ids, caches, promptWorkStep(step, capture, own)) : own();
56
+ }),
57
+ decode: (ids, caches, tapLayers, work) => withCapture(tapLayers, layerCount, capture => {
58
+ const sequences = ids.shape[0]!;
59
+ const own = async () => dispatch.decode(sequences)(ids, caches, capture);
60
+ return work ? work(ids, caches, { phase: "decode", rows: sequences, capture, own }) : own();
61
+ }),
62
+ verify: (ids, caches, window, tapLayers, work) => withCapture(tapLayers, layerCount, capture => {
63
+ const own = async () => dispatch.verify(window.sequences, window.positions)(ids, caches, capture);
64
+ return work ? work(ids, caches, verifyWorkStep(window, capture, own)) : own();
65
+ }),
66
+ // The capture sees layers 0..nLayers-1 and then the post-final-norm output as index nLayers.
67
+ supportsTapLayers: layers => !layers.length || (declaredGraph(model).graphCapabilities.hiddenLayerTaps && layers.every(layer =>
68
+ Number.isInteger(layer) && layer >= 0 && layer <= layerCount())),
69
+ projectLogits: (hidden, rows) => dispatch.logits(rows)(hidden),
70
+ };
71
+ }
72
+
73
+ /** Run `forward` with a capture of `tapLayers` (none without taps) and return
74
+ * its hidden and the tapped layers [B, L, m*H], concatenated in `tapLayers`
75
+ * order on the feature axis. The capture copies each tapped layer as the
76
+ * graph hands it over (index `layerCount` is the post-final-norm output), so
77
+ * every graph and the mixed iteration capture the same way. */
78
+ async function withCapture(tapLayers: readonly number[] | undefined, layerCount: () => number,
79
+ forward: (capture: MlxLayerCapture | null) => MlxArray | Promise<MlxArray>): Promise<MlxTargetForward> {
80
+ if (!tapLayers?.length) return { hidden: await forward(null), ctxML: null };
81
+ const cap = new Map<number, MlxArray>();
82
+ const layers = new Set(tapLayers);
83
+ let hidden: MlxArray | null = null;
84
+ try {
85
+ hidden = await forward((layer, h) => { if (layers.has(layer)) cap.set(layer, ops.contiguous(h)); });
86
+ const final = layerCount();
87
+ if (layers.has(final) && !cap.has(final)) cap.set(final, ops.contiguous(hidden));
88
+ const perLayer = tapLayers.map(layer => {
89
+ const captured = cap.get(layer);
90
+ if (!captured) throw new Error(`spec tap: layer ${layer} not captured`);
91
+ return captured;
92
+ });
93
+ const ctxML = ops.concatAxis(perLayer, 2);
94
+ for (const [, a] of cap) a.dispose();
95
+ cap.clear();
96
+ const out = { hidden, ctxML };
97
+ hidden = null; // ownership returned to the caller
98
+ return out;
99
+ } finally {
100
+ // On any throw, free the partial captures and the orphaned hidden.
101
+ hidden?.dispose();
102
+ for (const [, a] of cap) a.dispose();
103
+ }
104
+ }
105
+
106
+ export { bindLegacyDraftTarget } from "./draft-target";
@@ -0,0 +1,33 @@
1
+ import { projectedDraftGroups } from "./projected-draft-rows";
2
+ import type { GroupedDraftProvider, TargetView } from "../source";
3
+ import { targetLacks } from "../source";
4
+ import type { DeepspecDrafterModel } from "../../../contracts/mlx/drafter";
5
+ import { rowOperationFor } from "../../../runtime/row-operations";
6
+
7
+ /** Existing pairing requirements are checked once when the provider opens. */
8
+ export function bindDeepspecTarget(target: TargetView, drafter: Pick<DeepspecDrafterModel, "cfg">): void {
9
+ if (!target.hiddenLayerTaps) throw targetLacks("hiddenLayerTaps");
10
+ const layers = drafter.cfg.num_target_layers;
11
+ if (target.hiddenLayerTaps.layerCount !== layers) throw new Error(
12
+ `DeepSpec drafter was trained for a ${layers}-layer target; ` +
13
+ `this model has ${target.hiddenLayerTaps.layerCount} layers — wrong (target, drafter) pairing`);
14
+ }
15
+
16
+ export function deepspecGroups(drafter: DeepspecDrafterModel, namespace: string): GroupedDraftProvider {
17
+ return projectedDraftGroups(namespace, drafter.tapLayers, target => {
18
+ bindDeepspecTarget(target, drafter);
19
+ return {
20
+ namespace, schema: "deepspec-context-v1",
21
+ layers: drafter.cfg.num_hidden_layers,
22
+ // The B × L context rows a prefill or a commit appends index the drafter's context tables.
23
+ project(hidden, positions) {
24
+ const rows = hidden.shape[0]! * hidden.shape[1]!;
25
+ using projected = rowOperationFor(drafter.projectContext, rows)(hidden);
26
+ return rowOperationFor(drafter.projectContextKVRows, rows)(projected, positions);
27
+ },
28
+ draft(context, pending, positions, depth) {
29
+ return drafter.draftRows(context, pending, positions).tokens.map(row => row.slice(0, depth));
30
+ },
31
+ };
32
+ });
33
+ }
@@ -0,0 +1,10 @@
1
+ import { type Cache } from "../../../contracts/mlx/cache";
2
+ import type { MlxTokenGraph } from "../../../models/graph";
3
+ import { declaredGraph } from "../../../models/capabilities";
4
+ import type { TargetView } from "../source";
5
+
6
+ /** The ports a graph declares over its live caches for draft sources; a graph
7
+ * that declares none is a plain target identified by itself. */
8
+ export function bindLegacyDraftTarget(model: MlxTokenGraph, caches: Cache[]): TargetView {
9
+ return declaredGraph(model).draftTarget?.(caches) ?? Object.freeze({ identity: model });
10
+ }
@@ -0,0 +1,128 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { Dtype } from "@mlx-bun/mlx/ffi";
3
+ import { materializeCopy } from "@mlx-bun/mlx/materialize";
4
+ import * as ops from "@mlx-bun/mlx/ops";
5
+ import type { PreparedStateChange } from "../../../contracts/portable/resources";
6
+ import type { NativeMtpHead } from "../../../contracts/mlx/drafter";
7
+ import { applyStateChanges,disposeResources } from "../../../runtime/resources";
8
+ import { toLogprobs } from "../../../sampling/index";
9
+ import { MLACache } from "../../../state/glm52-cache";
10
+ import type { DraftRowSampling } from "../source";
11
+
12
+ /** Native Colibri draft history starts empty, independently of target prompt
13
+ * length. Accepted draft rows are rebuilt from true target hidden states. */
14
+ export class Glm52MtpRows {
15
+ #cache: MLACache;
16
+ #hidden: MlxArray | null = null;
17
+ #processed: number[] = [];
18
+ #drafts: number[][] = [];
19
+ #depth = 0;
20
+
21
+ constructor(readonly graph: NativeMtpHead, readonly sampling: DraftRowSampling | null) {
22
+ this.#cache = this.makeCache();
23
+ }
24
+ makeCache(): MLACache {
25
+ return new MLACache({ ...this.graph.cache, role: "mtp" });
26
+ }
27
+ get rowCount(): number { return this.#processed.length; }
28
+ append(states: readonly (Glm52MtpRowState | null)[]): void {
29
+ applyStateChanges([() => this.prepareAppend(states)]);
30
+ }
31
+ prepareAppend(states: readonly (Glm52MtpRowState | null)[]): PreparedStateChange {
32
+ if (!states.length) return { commit() {}, dispose() {} };
33
+ let cache: MLACache | null = this.makeCache(), hidden: MlxArray | null = null;
34
+ const held: { dispose(): void }[] = [];
35
+ const processed = [...this.#processed, ...states.map(state => state?.processedTokens ?? 0)];
36
+ try {
37
+ cache.mergeRows([...(this.rowCount ? [this.#cache] : []), ...states.map(state => {
38
+ if (state) return state.cache;
39
+ const empty = this.makeCache(); held.push(empty); return empty;
40
+ })]);
41
+ hidden = ops.concatAxis([...(this.#hidden ? [this.#hidden] : []), ...states.map(state => {
42
+ if (state) return state.hidden;
43
+ const zero = ops.zeros([1, 1, this.graph.hiddenSize], Dtype.float32);
44
+ held.push(zero); return zero;
45
+ })], 0);
46
+ } catch (error) { cache.dispose(); hidden?.dispose(); throw error; }
47
+ finally { disposeResources(held); }
48
+ return {
49
+ commit: () => {
50
+ const priorCache = this.#cache, priorHidden = this.#hidden;
51
+ this.#cache = cache!; this.#hidden = hidden; this.#processed = processed;
52
+ cache = priorCache; hidden = priorHidden;
53
+ },
54
+ dispose: () => { cache?.dispose(); hidden?.dispose(); cache = null; hidden = null; },
55
+ };
56
+ }
57
+ prefill(tokens: MlxArray, context: MlxArray): void {
58
+ const [batch, length] = tokens.shape as [number, number];
59
+ using tail = context.slice([0, length - 1, 0], [batch, length, context.shape[2]!]);
60
+ const hidden = materializeCopy(tail);
61
+ this.#hidden?.dispose(); this.#hidden = hidden;
62
+ this.#processed = this.#processed.map(count => count + length);
63
+ }
64
+ materialize(): void {
65
+ ops.evalAll([...this.#cache.state(), ...(this.#hidden ? [this.#hidden] : [])]);
66
+ }
67
+ filterRows(keep: readonly number[]): void {
68
+ if (!keep.length) { this.dispose(); return; }
69
+ using indices = ops.fromInt32([...keep], [keep.length]);
70
+ const hidden = ops.takeAxis(this.#hidden!, indices, 0);
71
+ try { this.#cache.filterRows(keep); } catch (error) { hidden.dispose(); throw error; }
72
+ this.#hidden!.dispose(); this.#hidden = hidden;
73
+ this.#processed = keep.map(row => this.#processed[row]!);
74
+ }
75
+ async draft(pending: readonly number[], depth: number, steps: readonly number[]): Promise<number[][]> {
76
+ const B = pending.length, tokens: MlxArray[] = [];
77
+ let ids: MlxArray | null = ops.fromInt32([...pending], [B, 1]), chained: MlxArray | null = null;
78
+ this.#cache.specRoundBegin();
79
+ try {
80
+ for (let position = 0; position < depth; position++) {
81
+ const hidden = await this.graph.decode(ids!, chained ?? this.#hidden!, this.#cache);
82
+ chained?.dispose(); chained = hidden;
83
+ using logits = this.graph.project(hidden);
84
+ using flat = ops.reshape(logits, [B, logits.shape.at(-1)!]);
85
+ using logprobs = toLogprobs(flat);
86
+ const next = this.sampling!.sample(logprobs, steps.map(step => step + position));
87
+ tokens.push(next); ids!.dispose(); ids = ops.reshape(next, [B, 1]);
88
+ }
89
+ using packed = depth ? ops.concatAxis(tokens, 0) : null;
90
+ const read = packed?.toIntTokens() ?? [];
91
+ this.#drafts = Array.from({ length: B }, (_, row) =>
92
+ Array.from({ length: depth }, (_, position) => read[position * B + row]!));
93
+ this.#depth = depth; return this.#drafts;
94
+ } finally { ids?.dispose(); chained?.dispose(); disposeResources(tokens); }
95
+ }
96
+ async commit(accepted: readonly number[], context: MlxArray): Promise<void> {
97
+ const B = accepted.length, width = Math.max(...accepted);
98
+ if (this.#depth) this.#cache.specRoundRollback(1);
99
+ else this.#cache.specRoundCommit();
100
+ if (width) {
101
+ using ids = ops.fromInt32(this.#drafts.flatMap(tokens => Array.from({ length: width }, (_, i) => tokens[i] ?? 0)), [B, width]);
102
+ using hidden = context.slice([0, 0, 0], [B, width, context.shape[2]!]);
103
+ this.#cache.specRoundBegin();
104
+ // The window it replays names the phase: one accepted token per row is a decode step.
105
+ using output = await (width === 1 ? this.graph.decode(ids, hidden, this.#cache) : this.graph.window(ids, hidden, this.#cache));
106
+ output.eval(); this.#cache.specRoundRollback(accepted);
107
+ }
108
+ using positions = ops.fromInt32([...accepted], [B, 1, 1]);
109
+ const hidden = ops.takeAlongAxis(context, positions, 1);
110
+ this.#hidden!.dispose(); this.#hidden = hidden;
111
+ this.#processed = this.#processed.map((count, row) => count + 1 + accepted[row]!);
112
+ this.#drafts = []; this.#depth = 0;
113
+ }
114
+ extractRow(row: number): Glm52MtpRowState {
115
+ const cache = this.#cache.extractRow(row);
116
+ try {
117
+ using hidden = this.#hidden!.slice([row, 0, 0], [row + 1, 1, this.#hidden!.shape[2]!]);
118
+ return { cache, hidden: materializeCopy(hidden), processedTokens: this.#processed[row]! };
119
+ } catch (error) { cache.dispose(); throw error; }
120
+ }
121
+ dispose(): void {
122
+ this.#cache.dispose(); this.#hidden?.dispose(); this.#hidden = null;
123
+ this.#processed = []; this.#drafts = []; this.#depth = 0;
124
+ }
125
+ }
126
+
127
+ import { Glm52MtpRowState } from "../../../state/speculative/glm52-mtp-state";
128
+ export { type Glm52MtpRowState } from "../../../state/speculative/glm52-mtp-state";
@@ -0,0 +1,78 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { Dtype } from "@mlx-bun/mlx/ffi";
3
+ import * as ops from "@mlx-bun/mlx/ops";
4
+ import { BatchedKVCache } from "../../../state/batched-kv";
5
+ import { AttentionMasks } from "../../../state/attention-read";
6
+ import type { Cache } from "../../../contracts/mlx/cache";
7
+ import { MlxStateRows } from "../../../state/rows";
8
+ import { disposeResources } from "../../../runtime/resources";
9
+
10
+ export interface ProjectedContext { k: MlxArray; v: MlxArray; }
11
+ export interface ContextProjection {
12
+ project(hidden: MlxArray, positions: number | MlxArray): ProjectedContext[];
13
+ }
14
+
15
+ /** Incremental projected context reuses ordinary full-attention row storage.
16
+ * The projection graph owns numerics; this object owns positions and validity.
17
+ * Context enters through the rows' window read, disposed unattended (the
18
+ * contract's append without attending); the drafter reads the stored rows as
19
+ * donor rows (`captureDonorRows`). The layers share one mask memo, so an append
20
+ * builds its per-row mask once. */
21
+ export class MlxProjectedContextRows {
22
+ readonly state: MlxStateRows<BatchedKVCache>;
23
+ constructor(layers: number, readonly graph: ContextProjection) {
24
+ const masks = new AttentionMasks();
25
+ this.state = new MlxStateRows(Array.from({ length: layers }, () => new BatchedKVCache(masks)));
26
+ }
27
+ get positions(): readonly number[] { return this.state.caches[0]!.rowOffsets; }
28
+ prepareAppend(rows: readonly (readonly Cache[])[]) { return this.state.prepareAppendMany(rows); }
29
+ append(hidden: MlxArray, lengths: readonly number[]): void {
30
+ const width = Math.max(...lengths), B = lengths.length, positions = this.positions;
31
+ using context = hidden.slice([0, 0, 0], [B, width, hidden.shape[2]!]);
32
+ using offsets = positions.every(position => position === positions[0]) ? null : ops.fromInt32([...positions], [B]);
33
+ const projected = this.graph.project(context, offsets ?? positions[0]!);
34
+ try {
35
+ for (const [layer, cache] of this.state.caches.entries()) {
36
+ cache.specRoundBegin();
37
+ cache.appendWindow(projected[layer]!.k, projected[layer]!.v).dispose();
38
+ if (lengths.every(length => length === width)) cache.specRoundCommit();
39
+ else cache.specRoundRollback(lengths);
40
+ }
41
+ } finally { disposeResources(projected.flatMap(pair => [pair.k, pair.v])); }
42
+ }
43
+ materialize(): void { ops.evalAll(this.state.caches.flatMap(cache => cache.state())); }
44
+ readAttention() {
45
+ const views: import("../../../contracts/mlx/cache").KvDonorRows[] = [];
46
+ try { for (const cache of this.state.caches) views.push(cache.captureDonorRows()); }
47
+ catch (error) { disposeResources(views.flatMap(view => [view.keys, view.values])); throw error; }
48
+ const masks = new Map<string, MlxArray | null>();
49
+ return {
50
+ attend(layer: number, query: MlxArray, blockKeys: MlxArray, blockValues: MlxArray, scale = 1): MlxArray {
51
+ const view = views[layer]!, width = view.keys.shape[2]!, block = blockKeys.shape[2]!, B = view.offsets.length;
52
+ // Membership and coverage advance together in every context layer.
53
+ const key = `${width}:${block}:${view.keys.dtype}`;
54
+ if (!masks.has(key)) {
55
+ let mask: MlxArray | null = null;
56
+ if (view.starts.some(start => start !== 0) || view.ends.some(end => end !== width)) {
57
+ using columns = ops.arange(0, width, 1, Dtype.int32);
58
+ using lower = ops.fromInt32([...view.starts], [B, 1, 1, 1]);
59
+ using upper = ops.fromInt32([...view.ends], [B, 1, 1, 1]);
60
+ using before = ops.less(columns, lower), after = ops.greaterEqual(columns, upper);
61
+ using invalid = ops.logicalOr(before, after);
62
+ using floats = invalid.astype(view.keys.dtype);
63
+ using prefix = ops.mulScalar(floats, -1e9);
64
+ using suffix = ops.zeros([B, 1, 1, block], view.keys.dtype);
65
+ mask = ops.concatAxis([prefix, suffix], 3);
66
+ }
67
+ masks.set(key, mask);
68
+ }
69
+ using keys = ops.concatAxis([view.keys, blockKeys], 2);
70
+ using values = ops.concatAxis([view.values, blockValues], 2);
71
+ const mask = masks.get(key)!;
72
+ return ops.sdpa(query, keys, values, scale, mask ? "array" : "", mask);
73
+ },
74
+ dispose() { disposeResources([...views.flatMap(view => [view.keys, view.values]), ...[...masks.values()].filter(value => value !== null)]); },
75
+ };
76
+ }
77
+ dispose(): void { this.state.dispose(); }
78
+ }