mlx-bun 0.2.0 → 0.4.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 (433) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +94 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/archive/investigations/openjarvis-fit.md +198 -0
  8. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  9. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  10. package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
  11. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  12. package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
  13. package/docs/design/batching.md +1841 -0
  14. package/docs/design/decode-speed-program.md +2346 -169
  15. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  16. package/docs/design/generic-model-support.md +655 -334
  17. package/docs/design/kv-cache.md +824 -0
  18. package/docs/design/orpo-training.md +673 -1239
  19. package/docs/design/speculative-decoding.md +1990 -0
  20. package/docs/design/turboquant.md +2212 -0
  21. package/docs/design/unified-engine-frontier-plan.md +1833 -727
  22. package/docs/design/web-chat-redesign.md +666 -1083
  23. package/docs/planning/PRODUCT_ROADMAP.md +50 -1
  24. package/docs/planning/release-notes-v0.4.0.md +45 -0
  25. package/docs/planning/video-series.md +2 -2
  26. package/docs/reference/benchmarks.md +3613 -0
  27. package/docs/reference/cli.md +434 -160
  28. package/docs/reference/distribution.md +328 -182
  29. package/docs/reference/environment.md +398 -0
  30. package/docs/reference/glossary.md +39 -0
  31. package/docs/reference/library-api.md +566 -168
  32. package/docs/reference/memory.md +227 -64
  33. package/docs/reference/models.md +113 -6
  34. package/docs/reference/server-api.md +944 -429
  35. package/docs/reference/server-config.md +740 -338
  36. package/docs/reference/training.md +393 -254
  37. package/docs/reference/troubleshooting.md +158 -0
  38. package/package.json +9 -4
  39. package/src/audio/decode.ts +2 -2
  40. package/src/audio/features.ts +2 -2
  41. package/src/backends/mlx/assistant-target.ts +46 -0
  42. package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
  43. package/src/backends/mlx/autoregressive.ts +108 -0
  44. package/src/backends/mlx/batch-group.ts +1790 -0
  45. package/src/backends/mlx/cache-attachment.ts +67 -0
  46. package/src/backends/mlx/cache-layout.ts +31 -0
  47. package/src/backends/mlx/checkpoint-state.ts +53 -0
  48. package/src/backends/mlx/continuation-persistence.ts +106 -0
  49. package/src/backends/mlx/continuation-request.ts +43 -0
  50. package/src/backends/mlx/continuation.ts +77 -0
  51. package/src/backends/mlx/diffusion.ts +39 -0
  52. package/src/backends/mlx/draft-target.ts +32 -0
  53. package/src/backends/mlx/evaluation.ts +105 -0
  54. package/src/backends/mlx/fill-append.ts +45 -0
  55. package/src/backends/mlx/full-kv-row-append.ts +32 -0
  56. package/src/backends/mlx/full-kv-row-donor.ts +54 -0
  57. package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
  58. package/src/backends/mlx/gateway-binding.ts +130 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/kv-codec.ts +9 -0
  61. package/src/backends/mlx/kv-maintenance.ts +133 -0
  62. package/src/backends/mlx/kv-tensor-rows.ts +212 -0
  63. package/src/backends/mlx/memory-client.ts +189 -0
  64. package/src/backends/mlx/methods.ts +90 -0
  65. package/src/backends/mlx/model-prompt.ts +164 -0
  66. package/src/backends/mlx/model-serving.ts +79 -0
  67. package/src/backends/mlx/prefill-cohort.ts +89 -0
  68. package/src/backends/mlx/prefill-rows.ts +200 -0
  69. package/src/backends/mlx/prefill.ts +33 -0
  70. package/src/backends/mlx/projected-context-rows.ts +72 -0
  71. package/src/backends/mlx/request-policy.ts +20 -0
  72. package/src/backends/mlx/request-state-policy.ts +41 -0
  73. package/src/backends/mlx/rollback.ts +89 -0
  74. package/src/backends/mlx/row-sampling.ts +49 -0
  75. package/src/backends/mlx/serial-executor.ts +242 -0
  76. package/src/backends/mlx/speculative-group.ts +359 -0
  77. package/src/backends/mlx/speculative-round.ts +161 -0
  78. package/src/backends/mlx/speculative.ts +114 -0
  79. package/src/backends/mlx/state-rows.ts +104 -0
  80. package/src/backends/mlx/state-views.ts +46 -0
  81. package/src/backends/mlx/target-layout-capability.ts +35 -0
  82. package/src/backends/mlx/text-engine.ts +73 -0
  83. package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
  84. package/src/cli.ts +66 -58
  85. package/src/client.ts +66 -0
  86. package/src/config.ts +21 -4
  87. package/src/contracts/completion.ts +16 -0
  88. package/src/contracts/execution.ts +60 -0
  89. package/src/contracts/generation.ts +103 -0
  90. package/src/contracts/host.ts +7 -0
  91. package/src/contracts/jobs.ts +66 -0
  92. package/src/contracts/pi-provider.ts +30 -0
  93. package/src/contracts/pi.ts +259 -0
  94. package/src/contracts/prefix-cache.ts +23 -0
  95. package/src/contracts/resources.ts +25 -0
  96. package/src/contracts/sampling.ts +23 -0
  97. package/src/contracts/scheduling.ts +45 -0
  98. package/src/contracts/tsconfig.json +14 -0
  99. package/src/diffusion/diffusion-generate.ts +218 -153
  100. package/src/embed.ts +1 -1
  101. package/src/engine/admission.ts +59 -0
  102. package/src/engine/cancellation.ts +38 -0
  103. package/src/engine/engine.ts +56 -0
  104. package/src/engine/execution-coordinator.ts +70 -0
  105. package/src/engine/execution-plan.ts +53 -0
  106. package/src/engine/resources.ts +53 -0
  107. package/src/engine/scheduler.ts +72 -0
  108. package/src/engine/session.ts +247 -0
  109. package/src/eval/README.md +44 -1
  110. package/src/eval/kl.ts +236 -3
  111. package/src/eval/reference-logits.ts +207 -0
  112. package/src/eval/runner.ts +8 -96
  113. package/src/fill/echo-index.ts +188 -0
  114. package/src/fill/fill-session.ts +452 -0
  115. package/src/fill/proposal.ts +68 -0
  116. package/src/fill/schema-rows.ts +506 -0
  117. package/src/fill/tool-boundary.ts +103 -0
  118. package/src/fit.ts +30 -16
  119. package/src/generate.ts +709 -417
  120. package/src/grammar.ts +12 -7
  121. package/src/harness-pi.ts +14 -33
  122. package/src/hub-rest.ts +4 -4
  123. package/src/index.ts +7 -0
  124. package/src/inference/denoising.ts +17 -0
  125. package/src/inference/draft-acceptance.ts +41 -0
  126. package/src/inference/draft-output.ts +49 -0
  127. package/src/inference/graph.ts +33 -0
  128. package/src/inference/prefill.ts +39 -0
  129. package/src/inference/rollback.ts +7 -0
  130. package/src/jobs/db.ts +4 -0
  131. package/src/jobs/index.ts +2 -0
  132. package/src/jobs/job-entry.ts +12 -12
  133. package/src/jobs/runner.ts +98 -13
  134. package/src/jobs/task-client.ts +19 -0
  135. package/src/jobs/types.ts +2 -58
  136. package/src/kv-scheme.ts +70 -12
  137. package/src/kv-store.ts +177 -63
  138. package/src/lab/README.md +18 -0
  139. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  140. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  141. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  142. package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
  143. package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
  144. package/src/library.ts +40 -0
  145. package/src/lora.ts +18 -0
  146. package/src/media-fetch.ts +1 -1
  147. package/src/memory/article.ts +1 -1
  148. package/src/memory/chunk.ts +1 -1
  149. package/src/memory/model.ts +30 -169
  150. package/src/memory/prompts.ts +1 -1
  151. package/src/memory/rest.ts +1 -1
  152. package/src/memory/schedule.ts +1 -1
  153. package/src/memory/stages.ts +4 -1
  154. package/src/memory/vault.ts +1 -3
  155. package/src/mlx/array.ts +11 -11
  156. package/src/mlx/compile.ts +12 -2
  157. package/src/mlx/ffi.ts +58 -11
  158. package/src/mlx/materialize.ts +23 -0
  159. package/src/mlx/metal-kernel.ts +10 -11
  160. package/src/mlx/ops.ts +33 -13
  161. package/src/mlx/quantized-rows.ts +36 -0
  162. package/src/mlx/token-bitmask.ts +52 -0
  163. package/src/mlx/turboquant-kv-decode.ts +82 -0
  164. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  165. package/src/mlx/turboquant-ops.ts +33 -3
  166. package/src/mlx/turboquant-tables.ts +2 -2
  167. package/src/model/artifact-identity.ts +73 -0
  168. package/src/model/batched-kv.ts +235 -0
  169. package/src/model/batched-mask.ts +20 -6
  170. package/src/model/batched-quant.ts +9 -3
  171. package/src/model/batched-quantized-kv.ts +144 -0
  172. package/src/model/batched-rotating-quant.ts +78 -17
  173. package/src/model/batched-rotating-state.ts +76 -0
  174. package/src/model/batched-rotating.ts +65 -23
  175. package/src/model/batched-row-storage.ts +52 -0
  176. package/src/model/batched-ssm.ts +52 -0
  177. package/src/model/batched-turboquant-kv.ts +93 -0
  178. package/src/model/compiled-decode.ts +8 -2
  179. package/src/model/decoded-kv-donor.ts +12 -0
  180. package/src/model/delayed-quantized-kv.ts +72 -0
  181. package/src/model/delayed-rotating-quantized-kv.ts +93 -0
  182. package/src/model/delayed-turboquant-kv.ts +55 -0
  183. package/src/model/diffusion-gemma.ts +8 -8
  184. package/src/model/factory.ts +76 -33
  185. package/src/model/fingerprint.ts +1 -1
  186. package/src/model/flash-attention.ts +3 -3
  187. package/src/model/full-prefill-padding.ts +56 -0
  188. package/src/model/full-prefill-row.ts +105 -0
  189. package/src/model/gemma4-base.ts +179 -80
  190. package/src/model/gemma4.ts +40 -32
  191. package/src/model/generated/gemma4-12b.ts +6 -3
  192. package/src/model/generated/gemma4-26b.ts +6 -3
  193. package/src/model/generated/gemma4-e4b.ts +6 -3
  194. package/src/model/glm52-cache.ts +80 -4
  195. package/src/model/glm52-dsa.ts +13 -9
  196. package/src/model/glm52-memory.ts +1 -1
  197. package/src/model/glm52-mla.ts +12 -6
  198. package/src/model/glm52.ts +19 -40
  199. package/src/model/implementation.ts +69 -0
  200. package/src/model/kv-attention-view.ts +67 -0
  201. package/src/model/minicpm5.ts +17 -15
  202. package/src/model/profile.ts +12 -2
  203. package/src/model/qwen-append.ts +12 -0
  204. package/src/model/qwen-conv.ts +52 -0
  205. package/src/model/qwen3-delta-state.ts +74 -0
  206. package/src/model/qwen3-delta.ts +59 -36
  207. package/src/model/qwen3_5-checkpoint.ts +164 -0
  208. package/src/model/qwen3_5.ts +202 -78
  209. package/src/model/rotating-kv-donor.ts +35 -0
  210. package/src/model/rotating-kv-layout.ts +262 -0
  211. package/src/model/rotating-row-transaction.ts +35 -0
  212. package/src/model/speculative-rotating-kv.ts +101 -0
  213. package/src/model/ssm-prefill-padding.ts +39 -0
  214. package/src/model/support.ts +1 -1
  215. package/src/model/trellis-balanced-scatter.ts +51 -0
  216. package/src/model/trellis-linear.ts +672 -0
  217. package/src/model/trellis-shared-m.ts +151 -0
  218. package/src/model/trellis-shared-scatter.ts +144 -0
  219. package/src/model/trellis-splitk-prefill.ts +158 -0
  220. package/src/model/trellis-tiled-prefill.ts +136 -0
  221. package/src/model/trellis-vector-expand.ts +62 -0
  222. package/src/model/trellis-wide-prefill.ts +94 -0
  223. package/src/model/turboquant-codec.ts +83 -0
  224. package/src/model/universal/dense.ts +15 -14
  225. package/src/model/universal/modules.ts +8 -1
  226. package/src/native-pack.ts +6 -5
  227. package/src/pi-provider.ts +6 -64
  228. package/src/pi-web.ts +8 -315
  229. package/src/preflight.ts +31 -15
  230. package/src/prompt-cache.ts +110 -69
  231. package/src/quantize/allocator.ts +1 -1
  232. package/src/quantize/config-writer.ts +10 -3
  233. package/src/quantize/rotate.ts +4 -4
  234. package/src/quantize/trellis.ts +586 -0
  235. package/src/responses.ts +28 -1
  236. package/src/runtime-config.ts +18 -4
  237. package/src/sampler.ts +40 -29
  238. package/src/serve/batch-scheduler.ts +8 -1671
  239. package/src/serve/chat-request.ts +391 -0
  240. package/src/serve/chat-stage.ts +195 -0
  241. package/src/serve/checkpoint-identity.ts +64 -0
  242. package/src/serve/completion-executor.ts +15 -7
  243. package/src/serve/discovery-routes.ts +12 -13
  244. package/src/serve/generation-gateway.ts +183 -213
  245. package/src/serve/http.ts +159 -0
  246. package/src/serve/inference-request.ts +79 -0
  247. package/src/serve/isolate.ts +297 -105
  248. package/src/serve/lab-routes.ts +3 -0
  249. package/src/serve/lane-registry.ts +2 -1
  250. package/src/serve/model-admin-routes.ts +7 -5
  251. package/src/serve/model-binding.ts +43 -0
  252. package/src/serve/model-host.ts +511 -0
  253. package/src/serve/openai-wire.ts +103 -0
  254. package/src/serve/parent-application.ts +56 -0
  255. package/src/serve/pipeline.ts +33 -0
  256. package/src/serve/preparation.ts +33 -0
  257. package/src/serve/request-plan.ts +23 -18
  258. package/src/serve/request-prep.ts +344 -0
  259. package/src/serve/response-lease.ts +24 -0
  260. package/src/serve/responses-client.ts +69 -0
  261. package/src/serve/session-completion-engine.ts +110 -0
  262. package/src/serve/session-search.ts +1 -1
  263. package/src/serve/static-routes.ts +5 -5
  264. package/src/serve/text-completion-stage.ts +104 -0
  265. package/src/serve/token-streams.ts +519 -0
  266. package/src/server.ts +335 -2711
  267. package/src/spec/assistant-rows.ts +92 -0
  268. package/src/spec/assistant-source.ts +31 -55
  269. package/src/spec/cache-identity.ts +22 -0
  270. package/src/spec/deepspec-rows.ts +29 -0
  271. package/src/spec/deepspec-source.ts +16 -14
  272. package/src/spec/dflash-source.ts +36 -12
  273. package/src/spec/drafter.ts +153 -142
  274. package/src/spec/dspark/ab-stats.ts +3 -3
  275. package/src/spec/dspark/calibration.ts +2 -2
  276. package/src/spec/dspark/deepspec-module.ts +97 -122
  277. package/src/spec/dspark/generate-dflash.ts +2 -2
  278. package/src/spec/dspark/generate.ts +2 -2
  279. package/src/spec/dspark/loader.ts +1 -1
  280. package/src/spec/dspark/module-dflash.ts +129 -172
  281. package/src/spec/dspark/module.ts +3 -3
  282. package/src/spec/dspark/quantize-drafter.ts +1 -1
  283. package/src/spec/generate.ts +1 -1
  284. package/src/spec/glm52-mtp-graph.ts +29 -0
  285. package/src/spec/glm52-mtp-rows.ts +132 -0
  286. package/src/spec/glm52-mtp-source.ts +54 -137
  287. package/src/spec/glm52-mtp-state.ts +33 -0
  288. package/src/spec/ngram-source.ts +93 -21
  289. package/src/spec/projected-draft-rows.ts +73 -0
  290. package/src/spec/qwen-mtp-module.ts +171 -0
  291. package/src/spec/qwen-mtp-rows.ts +219 -0
  292. package/src/spec/qwen-mtp-source.ts +147 -195
  293. package/src/spec/qwen-mtp-state.ts +30 -0
  294. package/src/spec/serve-loop.ts +201 -201
  295. package/src/spec/source.ts +110 -13
  296. package/src/spec/two-model-rows.ts +61 -0
  297. package/src/spec/two-model.ts +44 -102
  298. package/src/ssd-cache.ts +99 -19
  299. package/src/ssd-durability.ts +36 -20
  300. package/src/tool-call.ts +51 -14
  301. package/src/train/flash-cce.ts +2 -2
  302. package/src/train/forward.ts +6 -0
  303. package/src/train/loss.ts +1 -1
  304. package/src/train/prefix-shared.ts +5 -1
  305. package/src/train/segmented.ts +5 -3
  306. package/src/train/trainer.ts +6 -6
  307. package/src/vision/prompt.ts +1 -1
  308. package/src/vision/qwen3vl-tower.ts +52 -10
  309. package/src/web/app.html +1 -1
  310. package/src/web/app.js +62 -60
  311. package/src/web/src/adapters-panel.ts +3 -3
  312. package/src/web/src/assistant.ts +2 -36
  313. package/src/web/src/chat.ts +4 -4
  314. package/src/web/src/composer.ts +4 -4
  315. package/src/web/src/hub.ts +1 -1
  316. package/src/web/src/markdown.ts +0 -0
  317. package/src/web/src/memory-panel.ts +1 -1
  318. package/src/web/src/model-picker.ts +1 -1
  319. package/src/web/src/palette.ts +1 -1
  320. package/src/web/src/protocol.ts +3 -39
  321. package/src/web/src/sessions.ts +1 -1
  322. package/src/web/src/ui-catalog.ts +4 -18
  323. package/src/web-assets.ts +47 -0
  324. package/src/weights.ts +55 -9
  325. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  326. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  327. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  328. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  329. package/docs/archive/investigations/hlg-ab.html +0 -34
  330. package/docs/archive/investigations/hlg-explorer.html +0 -238
  331. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  332. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  333. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  334. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  335. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  336. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  337. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  338. package/docs/archive/investigations/hlg-report.html +0 -262
  339. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  340. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  341. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  342. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  343. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  344. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  345. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  346. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  347. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  348. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  349. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  350. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  351. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  352. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  353. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  354. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  355. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  356. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  357. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  358. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  359. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  360. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  361. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  362. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  363. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  364. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  365. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  366. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  367. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  368. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  369. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  370. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  371. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  372. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  373. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  374. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  375. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  376. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  377. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  378. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  379. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  380. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  381. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  382. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  383. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  384. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  385. package/docs/archive/mlx-bun-lab-report.html +0 -275
  386. package/docs/archive/wwdc-mlx-bun.html +0 -848
  387. package/docs/design/adapters-end-to-end.md +0 -205
  388. package/docs/design/batching-perf-path.md +0 -255
  389. package/docs/design/batching-v2-plan.md +0 -529
  390. package/docs/design/bucketing-stage.md +0 -197
  391. package/docs/design/compat-cli-surface-design.md +0 -230
  392. package/docs/design/docs-reorg-plan.md +0 -165
  393. package/docs/design/dspark-serving-program.md +0 -371
  394. package/docs/design/dspark-speculative-decoding.md +0 -318
  395. package/docs/design/grammar-spec-batching-integration.md +0 -301
  396. package/docs/design/memory-inference-path.md +0 -422
  397. package/docs/design/memory-synthesis.md +0 -287
  398. package/docs/design/memory-system.md +0 -237
  399. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  400. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  401. package/docs/design/omlx-adoption-map.md +0 -98
  402. package/docs/design/orpo-dynamic-lambda.md +0 -220
  403. package/docs/design/orpo-future-enhancements.md +0 -340
  404. package/docs/design/paged-kv-cache.md +0 -214
  405. package/docs/design/parallel-slots.md +0 -567
  406. package/docs/design/parity-tier-dag.md +0 -161
  407. package/docs/design/repo-cleanup-plan.md +0 -231
  408. package/docs/design/runtime-isolation.md +0 -263
  409. package/docs/design/segmented-backward-training.md +0 -543
  410. package/docs/design/spec-decode-larger-targets.md +0 -229
  411. package/docs/design/ssd-kv-cold-tier.md +0 -415
  412. package/docs/design/structured-output.md +0 -484
  413. package/docs/design/tauri-desktop-app.md +0 -127
  414. package/docs/design/the-dreaming-master-plan.md +0 -445
  415. package/docs/design/turboquant-kv.md +0 -235
  416. package/docs/design/turboquant-weights.md +0 -467
  417. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  418. package/docs/design/web-chat-beat-matrix.md +0 -315
  419. package/docs/design/write-pipeline-entity.md +0 -468
  420. package/docs/planning/IDEAS.md +0 -69
  421. package/docs/reference/embedding.md +0 -197
  422. package/docs/reference/features-matrix.md +0 -101
  423. package/docs/reference/orpo-quickstart.md +0 -135
  424. package/src/flags.ts +0 -11
  425. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  426. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  427. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  428. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  429. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  430. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  431. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  432. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  433. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
package/src/mlx/ffi.ts CHANGED
@@ -10,10 +10,10 @@
10
10
  // - mlx_optional_int is `{ int32 value; bool has_value }` = 8 bytes, by
11
11
  // value in one register: value in bits 0..31, has_value at bit 32.
12
12
 
13
- import { dlopen, FFIType, ptr, read } from "bun:ffi";
13
+ import { dlopen, FFIType, ptr, read, type Pointer } from "bun:ffi";
14
14
  import { existsSync } from "node:fs";
15
15
  import { dirname, join } from "node:path";
16
- import { nativePackDir } from "../native-pack";
16
+ import { MLX_CORE_VERSION, nativePackDir } from "../native-pack";
17
17
  import { runtimeValue } from "../runtime-config";
18
18
 
19
19
  const { ptr: P, i32, u64, f32, f64, cstring } = FFIType;
@@ -21,7 +21,7 @@ const { ptr: P, i32, u64, f32, f64, cstring } = FFIType;
21
21
  /** libmlxc resolution, in order: explicit env override → next to the
22
22
  * executable (the `bun build --compile` sidecar layout, where
23
23
  * libmlxc.dylib + libmlx.dylib + mlx.metallib ship beside the binary —
24
- * see docs/reference/embedding.md) → the downloaded native-pack cache
24
+ * see docs/reference/distribution.md) → the downloaded native-pack cache
25
25
  * (src/native-pack.ts; populated on first run by the CLI) → homebrew
26
26
  * (arm64, then Intel prefix). Keep in sync with nativeRuntimeDir(). */
27
27
  function resolveLibmlxc(): string {
@@ -38,8 +38,12 @@ function resolveLibmlxc(): string {
38
38
  }
39
39
 
40
40
  export const LIBMLXC_PATH = resolveLibmlxc();
41
-
42
41
  export const C = dlopen(LIBMLXC_PATH, {
42
+ // linked MLX core version, independent of the C API family
43
+ mlx_version: { args: [P], returns: i32 },
44
+ mlx_string_new: { args: [], returns: u64 },
45
+ mlx_string_data: { args: [u64], returns: cstring },
46
+ mlx_string_free: { args: [u64], returns: i32 },
43
47
  // array lifecycle
44
48
  mlx_array_new: { args: [], returns: u64 },
45
49
  mlx_array_new_data: { args: [P, P, i32, i32], returns: u64 },
@@ -78,6 +82,7 @@ export const C = dlopen(LIBMLXC_PATH, {
78
82
  mlx_device_info_get: { args: [P, u64], returns: i32 },
79
83
  mlx_device_info_free: { args: [u64], returns: i32 },
80
84
  mlx_device_info_get_size: { args: [P, u64, cstring], returns: i32 },
85
+ mlx_device_info_get_string: { args: [P, u64, cstring], returns: i32 },
81
86
  mlx_get_default_device: { args: [P], returns: i32 },
82
87
  mlx_device_free: { args: [u64], returns: i32 },
83
88
  mlx_synchronize: { args: [u64], returns: i32 },
@@ -98,6 +103,10 @@ export const C = dlopen(LIBMLXC_PATH, {
98
103
  mlx_fast_rms_norm: { args: [P, u64, u64, f32, u64], returns: i32 },
99
104
  // (res, x, weight may-be-null, bias may-be-null, eps, stream)
100
105
  mlx_fast_layer_norm: { args: [P, u64, u64, u64, f32, u64], returns: i32 },
106
+ // MLX 0.32.2: mask, sinks, force_fused, stream.
107
+ mlx_fast_scaled_dot_product_attention: {
108
+ args: [P, u64, u64, u64, f32, cstring, u64, u64, FFIType.bool, u64], returns: i32,
109
+ },
101
110
  mlx_matmul: { args: [P, u64, u64, u64], returns: i32 },
102
111
  mlx_contiguous: { args: [P, u64, FFIType.bool, u64], returns: i32 },
103
112
  mlx_copy: { args: [P, u64, u64], returns: i32 },
@@ -108,15 +117,14 @@ export const C = dlopen(LIBMLXC_PATH, {
108
117
  mlx_softmax_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
109
118
  mlx_max_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
110
119
  mlx_sum_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
120
+ // Optional dtype uses the eight-byte value/has_value layout of optional int.
121
+ mlx_cumsum_axis: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64, u64], returns: i32 },
122
+ mlx_cummax_axis: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
111
123
  mlx_maximum: { args: [P, u64, u64, u64], returns: i32 },
112
124
  mlx_expand_dims: { args: [P, u64, i32, u64], returns: i32 },
113
125
  mlx_logical_or: { args: [P, u64, u64, u64], returns: i32 },
114
126
  // (res, x, dims, traditional, opt base, scale, offset, freqs may-be-null, stream)
115
127
  mlx_fast_rope: { args: [P, u64, i32, FFIType.bool, u64, f32, i32, u64, u64], returns: i32 },
116
- // (res, q, k, v, scale, mask_mode, mask may-be-null, sinks may-be-null, stream)
117
- mlx_fast_scaled_dot_product_attention: {
118
- args: [P, u64, u64, u64, f32, cstring, u64, u64, u64], returns: i32,
119
- },
120
128
  // ops (grown as phases need them)
121
129
  mlx_add: { args: [P, u64, u64, u64], returns: i32 },
122
130
  mlx_subtract: { args: [P, u64, u64, u64], returns: i32 },
@@ -150,8 +158,6 @@ export const C = dlopen(LIBMLXC_PATH, {
150
158
  mlx_arange: { args: [P, f64, f64, f64, i32, u64], returns: i32 },
151
159
  mlx_take_along_axis: { args: [P, u64, u64, i32, u64], returns: i32 },
152
160
  mlx_put_along_axis: { args: [P, u64, u64, u64, i32, u64], returns: i32 },
153
- mlx_cumsum: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
154
- mlx_cummax: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
155
161
  mlx_where: { args: [P, u64, u64, u64, u64], returns: i32 },
156
162
  mlx_logsumexp_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
157
163
  mlx_greater_equal: { args: [P, u64, u64, u64], returns: i32 },
@@ -220,7 +226,7 @@ export const C = dlopen(LIBMLXC_PATH, {
220
226
  mlx_save_safetensors: { args: [cstring, u64, u64], returns: i32 },
221
227
  mlx_map_string_to_array_insert: { args: [u64, cstring, u64], returns: i32 },
222
228
  mlx_map_string_to_string_insert: { args: [u64, cstring, cstring], returns: i32 },
223
- // compile via closures (Phase A compiled decode — docs/design/optimization_plan.md).
229
+ // compile via closures (Phase A compiled decode — docs/archive/investigations/optimization_plan.md).
224
230
  // mlx_closure is the usual one-pointer struct; the func-payload variant
225
231
  // carries an id we use to route the trace callback back to JS.
226
232
  mlx_closure_new: { args: [], returns: u64 },
@@ -314,6 +320,22 @@ export function takeMlxError(): string | null {
314
320
  return e;
315
321
  }
316
322
 
323
+ function readRuntimeVersion(): string {
324
+ const slot = new BigUint64Array([C.mlx_string_new()]);
325
+ const p = ptr(slot);
326
+ try {
327
+ if (C.mlx_version(p) !== 0)
328
+ throw new Error(`mlx_version failed: ${takeMlxError() ?? "(no mlx message)"}`);
329
+ return C.mlx_string_data(read.u64(p, 0)).toString();
330
+ } finally { C.mlx_string_free(read.u64(p, 0)); }
331
+ }
332
+
333
+ /** Read once at startup; kernel eligibility must follow the linked core. */
334
+ export const MLX_VERSION = readRuntimeVersion();
335
+ if (MLX_VERSION !== MLX_CORE_VERSION) {
336
+ throw new Error(`mlx-bun requires its bundled MLX ${MLX_CORE_VERSION}; ${LIBMLXC_PATH} loaded MLX ${MLX_VERSION}`);
337
+ }
338
+
317
339
  /** mlx_dtype enum values (array.h). */
318
340
  export const enum Dtype {
319
341
  bool = 0, uint8 = 1, uint16 = 2, uint32 = 3, uint64 = 4,
@@ -405,6 +427,31 @@ export function clearCache(): void {
405
427
  C.mlx_clear_cache();
406
428
  }
407
429
 
430
+ let cachedDeviceArchitecture: string | undefined;
431
+
432
+ /** Native architecture identifier, used to match MLX's hardware dispatch. */
433
+ export function deviceArchitecture(): string {
434
+ if (cachedDeviceArchitecture !== undefined) return cachedDeviceArchitecture;
435
+ const devSlot = new BigUint64Array(1), devPtr = ptr(devSlot);
436
+ if (C.mlx_get_default_device(devPtr) !== 0)
437
+ throw new Error(`mlx_get_default_device failed: ${takeMlxError() ?? ""}`);
438
+ const dev = read.u64(devPtr, 0);
439
+ const infoSlot = new BigUint64Array([C.mlx_device_info_new()]), infoPtr = ptr(infoSlot);
440
+ try {
441
+ if (C.mlx_device_info_get(infoPtr, dev) !== 0)
442
+ throw new Error(`mlx_device_info_get failed: ${takeMlxError() ?? ""}`);
443
+ const out = new BigUint64Array(1), outPtr = ptr(out);
444
+ const key = Buffer.from("architecture\0", "utf8");
445
+ if (C.mlx_device_info_get_string(outPtr, read.u64(infoPtr, 0), ptr(key)) !== 0)
446
+ throw new Error("architecture not in device info");
447
+ // Copy before the device-info object releases the native string.
448
+ return cachedDeviceArchitecture = new CString(Number(read.u64(outPtr, 0)) as Pointer).toString();
449
+ } finally {
450
+ C.mlx_device_info_free(read.u64(infoPtr, 0));
451
+ C.mlx_device_free(dev);
452
+ }
453
+ }
454
+
408
455
  /** Metal's recommended max working-set size for the default device. */
409
456
  export function maxRecommendedWorkingSetSize(): number {
410
457
  const devSlot = new BigUint64Array(1);
@@ -0,0 +1,23 @@
1
+ // Explicit compact storage for a value that must release a larger backing
2
+ // buffer. MLX 0.31.2 Copy::eval shares storage; contiguous() may also alias.
3
+ // MLX's dynamic slice allocates the logical output size and has a native VJP.
4
+ import { MlxArray } from "./array";
5
+ import * as ops from "./ops";
6
+
7
+ let zero: MlxArray | undefined;
8
+
9
+ /** Borrow an array; return an owned, row-contiguous copy on the GPU.
10
+ * Values and dtype are unchanged. Evaluation stays lazy and never reads data
11
+ * back to the host. Use only when independent compact storage is required;
12
+ * immutable array aliases normally avoid this allocation and dispatch.
13
+ * Slice sizes are concrete, so use shape-specialized compiled graphs. */
14
+ export function materializeCopy(input: MlxArray): MlxArray {
15
+ zero ??= MlxArray.fromInt32(new Int32Array([0]), []);
16
+ if (input.ndim > 0) return ops.sliceDynamic(input, zero, [0], input.shape);
17
+ const vector = ops.reshape(input, [1]);
18
+ try {
19
+ const copy = ops.sliceDynamic(vector, zero, [0], [1]);
20
+ try { return ops.reshape(copy, []); }
21
+ finally { copy.dispose(); }
22
+ } finally { vector.dispose(); }
23
+ }
@@ -1,4 +1,4 @@
1
- // mx.fast.metal_kernel from Bun (docs/design/optimization_plan.md Phase E): the
1
+ // mx.fast.metal_kernel from Bun (docs/archive/investigations/optimization_plan.md Phase E): the
2
2
  // intended MLX extension point for custom Metal kernels — no fork. The
3
3
  // kernel body is Metal Shading Language; mlx generates the signature
4
4
  // from input/output names (inputs by name, outputs by name, plus
@@ -29,15 +29,15 @@ export interface MetalKernelSpec {
29
29
  }
30
30
 
31
31
  export interface MetalKernelCall {
32
- /** Concrete output shapes/dtypes. Provide this OR `outputShapeFn` (the latter
33
- * makes the kernel derive its outputs from the input shapes, like a real MLX
34
- * primitive — required to compose inside an mx.compile'd / shapeless closure,
35
- * where the wrapper must answer "given these input shapes, what comes out?"). */
32
+ /** Concrete output shapes/dtypes. Provide this OR `outputShapeFn`.
33
+ * Both are captured when apply() builds the custom primitive. */
36
34
  outputs?: { shape: number[]; dtype: Dtype }[];
37
- /** Derive output shapes/dtypes from the (current/traced) input shapes. */
35
+ /** Derive outputs during eager construction or a shape-specific compile trace.
36
+ * This JS callback is not native shape inference: MLX's CustomKernel lacks
37
+ * output_shapes(), so it cannot participate in shapeless replay. */
38
38
  outputShapeFn?: (inputs: MlxArray[]) => { shape: number[]; dtype: Dtype }[];
39
- /** Concrete launch grid, OR derive it from inputs (so it tracks shape under
40
- * compile replay the same way `outputShapeFn` tracks output shapes). */
39
+ /** Concrete launch grid, OR derive it during eager construction or tracing.
40
+ * Native replay reuses the captured grid without invoking this callback. */
41
41
  grid: [number, number, number] | ((inputs: MlxArray[]) => [number, number, number]);
42
42
  threadGroup: [number, number, number];
43
43
  templateInts?: Record<string, number>;
@@ -73,9 +73,8 @@ export class MetalKernel {
73
73
  // flash-CCE pin bug). Pure-additive: holds references, changes no logic.
74
74
  const keepAlive: unknown[] = [];
75
75
  try {
76
- // Derive outputs + grid from the (current) input shapes when a function is
77
- // given — so the SAME kernel object answers correctly for any input shape
78
- // during a compile/replay trace, instead of baking in one call's shapes.
76
+ // Resolve callbacks once for this eager call or shape-specific trace.
77
+ // A compiled graph captures these values; replay does not call JS again.
79
78
  const outputs = call.outputShapeFn ? call.outputShapeFn(inputs) : call.outputs;
80
79
  if (!outputs) throw new Error("metal_kernel: provide outputs or outputShapeFn");
81
80
  const grid = typeof call.grid === "function" ? call.grid(inputs) : call.grid;
package/src/mlx/ops.ts CHANGED
@@ -170,7 +170,7 @@ export function ropeScaled(
170
170
  * of a baked int — required inside compiled decode graphs, where the
171
171
  * offset changes every step but the graph must not. Same kernel as
172
172
  * `rope`; bit-exactness vs the static form is asserted in
173
- * tests/compile.test.ts. */
173
+ * tests/unit/compile.test.ts. */
174
174
  export function ropeDynamic(
175
175
  x: MlxArray, dims: number, base: number | null, offset: MlxArray,
176
176
  freqs: MlxArray | null, s: S = gpuStream,
@@ -189,7 +189,7 @@ export function ropeDynamic(
189
189
  * the dynamic twin of `ropeScaled`, for the universal rope factory's
190
190
  * batched-decode path (per-row [B] offsets under left-padding). Same
191
191
  * kernel as `ropeDynamic`; static/dynamic bit-exactness is asserted in
192
- * tests/compile.test.ts. */
192
+ * tests/unit/compile.test.ts. */
193
193
  export function ropeScaledDynamic(
194
194
  x: MlxArray, dims: number, traditional: boolean, base: number | null,
195
195
  scale: number, offset: MlxArray, freqs: MlxArray | null, s: S = gpuStream,
@@ -213,7 +213,7 @@ export function sdpa(
213
213
  outArray("fast_sdpa", (o) =>
214
214
  C.mlx_fast_scaled_dot_product_attention(
215
215
  o, q.handle, k.handle, v.handle, scale, ptr(cstr(maskMode)),
216
- maskArr?.handle ?? 0n, 0n, s,
216
+ maskArr?.handle ?? 0n, 0n, false, s,
217
217
  ),
218
218
  ),
219
219
  );
@@ -314,7 +314,8 @@ export function takeAxis(a: MlxArray, indices: MlxArray, axis: number, s: S = gp
314
314
  }
315
315
 
316
316
  export function reshape(a: MlxArray, shape: number[], s: S = gpuStream): MlxArray {
317
- const buf = new Int32Array(shape);
317
+ // bun:ffi cannot take a pointer to an empty view; ndim=0 ignores the dummy.
318
+ const buf = new Int32Array(shape.length ? shape : [0]);
318
319
  return new MlxArray(
319
320
  outArray("reshape", (o) => C.mlx_reshape(o, a.handle, ptr(buf), BigInt(shape.length), s)),
320
321
  );
@@ -454,14 +455,14 @@ export function putAlongAxis(
454
455
 
455
456
  export function cumsum(a: MlxArray, axis: number, s: S = gpuStream): MlxArray {
456
457
  return new MlxArray(
457
- outArray("cumsum", (o) => C.mlx_cumsum(o, a.handle, axis, false, true, s)),
458
+ outArray("cumsum", (o) => C.mlx_cumsum_axis(o, a.handle, axis, false, true, 0n, s)),
458
459
  );
459
460
  }
460
461
 
461
462
  /** Cumulative maximum along `axis` (inclusive, forward) — mlx.core.cummax. */
462
463
  export function cummax(a: MlxArray, axis: number, s: S = gpuStream): MlxArray {
463
464
  return new MlxArray(
464
- outArray("cummax", (o) => C.mlx_cummax(o, a.handle, axis, false, true, s)),
465
+ outArray("cummax", (o) => C.mlx_cummax_axis(o, a.handle, axis, false, true, s)),
465
466
  );
466
467
  }
467
468
 
@@ -476,10 +477,13 @@ export function where(cond: MlxArray, x: MlxArray, y: MlxArray, s: S = gpuStream
476
477
  // (lab/repro/bun-ffi-f64/) traced the root cause to a Bun bug: after DFG
477
478
  // tier-up, typed-array reads following a bun:ffi call return stale values
478
479
  // (the JIT eliminates the load across the native call). Not f64 marshaling
479
- // — args reach C intact. See PLAN.md Phase 4 findings. Large constant
480
- // ranges (topP's vocab arange) are cached for the process lifetime.
481
- const arangeCache = new Map<string, MlxArray>();
480
+ // — args reach C intact. See PLAN.md Phase 4 findings. Reuse large ranges
481
+ // across attention layers and sampling, but bound their retained storage:
482
+ // causal masks request a new range on every round once context exceeds 64K.
483
+ const arangeCache = new Map<string, { array: MlxArray; bytes: number }>();
482
484
  const ARANGE_CACHE_MIN = 65536;
485
+ const ARANGE_CACHE_BYTES = 8 * 1024 * 1024;
486
+ let arangeCacheBytes = 0;
483
487
 
484
488
  export function arange(start: number, stop: number, step: number, dtype: Dtype, s: S = gpuStream): MlxArray {
485
489
  if (!Number.isInteger(start) || !Number.isInteger(step) || step === 0)
@@ -487,7 +491,11 @@ export function arange(start: number, stop: number, step: number, dtype: Dtype,
487
491
  const n = Math.max(0, Math.ceil((stop - start) / step));
488
492
  const key = `${start}|${step}|${n}|${dtype}`;
489
493
  const cached = arangeCache.get(key);
490
- if (cached) return cached.slice([0], [n], s);
494
+ if (cached) {
495
+ arangeCache.delete(key);
496
+ arangeCache.set(key, cached);
497
+ return cached.array.slice([0], [n], s);
498
+ }
491
499
 
492
500
  let arr: MlxArray;
493
501
  if (dtype === Dtype.int32) {
@@ -503,8 +511,20 @@ export function arange(start: number, stop: number, step: number, dtype: Dtype,
503
511
  arr = new MlxArray(outArray("arange", (o) => C.mlx_arange(o, start, stop, step, dtype, s)));
504
512
  }
505
513
  if (n >= ARANGE_CACHE_MIN) {
506
- arangeCache.set(key, arr);
507
- return arr.slice([0], [n], s);
514
+ const bytes = arr.nbytes;
515
+ if (bytes <= ARANGE_CACHE_BYTES) {
516
+ while (arangeCacheBytes + bytes > ARANGE_CACHE_BYTES) {
517
+ const [oldKey, old] = arangeCache.entries().next().value!;
518
+ arangeCache.delete(oldKey);
519
+ arangeCacheBytes -= old.bytes;
520
+ // Returned slices retain their own native reference, including lazy
521
+ // views that have not been evaluated when this owner is evicted.
522
+ old.array.dispose();
523
+ }
524
+ arangeCache.set(key, { array: arr, bytes });
525
+ arangeCacheBytes += bytes;
526
+ return arr.slice([0], [n], s);
527
+ }
508
528
  }
509
529
  return arr;
510
530
  }
@@ -611,7 +631,7 @@ export function sliceDynamic(
611
631
  /** slice_update with the start index as an ARRAY (one int32 per entry in
612
632
  * `axes`) — the compiled-decode form of the per-step cache write. Same
613
633
  * write as `sliceUpdate`; bit-exactness vs the static form is asserted
614
- * in tests/compile.test.ts. */
634
+ * in tests/unit/compile.test.ts. */
615
635
  export function sliceUpdateDynamic(
616
636
  src: MlxArray, update: MlxArray, start: MlxArray, axes: number[], s: S = gpuStream,
617
637
  ): MlxArray {
@@ -0,0 +1,36 @@
1
+ import type { MlxArray } from "./array";
2
+ import * as ops from "./ops";
3
+
4
+ /** Run independent affine M=1 matrix-vector products in one native batched call.
5
+ * Weight/scales/bias views share their original buffers. MLX 0.32.2's
6
+ * flattened M>=2 path uses different arithmetic, so appending known tokens
7
+ * must explicitly select this layout when preserving decode state.
8
+ */
9
+ export function quantizedMatmulRows(
10
+ x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray | null,
11
+ spec: ops.QuantSpec,
12
+ ): MlxArray {
13
+ const shape = x.shape, width = shape[shape.length - 1]!;
14
+ const rows = shape.reduce((a, b) => a * b, 1) / width;
15
+ if (rows === 1) return ops.quantizedMatmul(x, w, scales, biases, spec);
16
+ if (spec.mode !== "affine")
17
+ throw new Error("Independent-row batched matmul requires affine quantization");
18
+ if (w.shape.length !== 2 || scales.shape.length !== 2 || (biases && biases.shape.length !== 2))
19
+ throw new Error("Independent-row matmul requires shared two-dimensional weights");
20
+ const views: MlxArray[] = [];
21
+ const view = (value: MlxArray, newShape: number[]) => {
22
+ const result = ops.reshape(value, newShape); views.push(result); return result;
23
+ };
24
+ let output: MlxArray | undefined;
25
+ try {
26
+ const input = view(x, [rows, 1, width]);
27
+ const weight = view(w, [1, ...w.shape]);
28
+ const scale = view(scales, [1, ...scales.shape]);
29
+ const bias = biases ? view(biases, [1, ...biases.shape]) : null;
30
+ output = ops.quantizedMatmul(input, weight, scale, bias, spec);
31
+ return ops.reshape(output, [...shape.slice(0, -1), output.shape.at(-1)!]);
32
+ } finally {
33
+ output?.dispose();
34
+ for (const value of views) value.dispose();
35
+ }
36
+ }
@@ -0,0 +1,52 @@
1
+ // XGrammar's additive 0/-Infinity mask contract, decoded from packed bits.
2
+ // Reference: mlc-ai/xgrammar b5048784, apply_token_bitmask_mlx.py (Apache-2.0).
3
+ import { MlxArray } from "./array";
4
+ import { CompiledFunction } from "./compile";
5
+ import { Dtype } from "./ffi";
6
+ import { MetalKernel } from "./metal-kernel";
7
+
8
+ let compiled: CompiledFunction | undefined;
9
+ let kernel: MetalKernel | undefined;
10
+
11
+ function maskGraph(logits: MlxArray, bits: MlxArray): MlxArray {
12
+ kernel ??= new MetalKernel({
13
+ name: "mlx_bun_token_bitmask",
14
+ inputNames: ["logits", "bits"],
15
+ outputNames: ["out"],
16
+ source: String.raw`
17
+ const uint i = thread_position_in_grid.x;
18
+ if (i >= SIZE) return;
19
+ const uint id = i % V;
20
+ const uint word = uint(bits[id >> 5]);
21
+ const bool valid = ((word >> (id & 31)) & 1u) != 0;
22
+ out[i] = logits[i] + T(valid ? 0.0f : -INFINITY);
23
+ `,
24
+ });
25
+ return kernel.apply([logits, bits], {
26
+ outputs: [{ shape: logits.shape, dtype: logits.dtype }],
27
+ grid: [logits.size, 1, 1],
28
+ threadGroup: [128, 1, 1],
29
+ templateDtypes: { T: logits.dtype },
30
+ templateInts: { V: logits.shape.at(-1)!, SIZE: logits.size },
31
+ })[0]!;
32
+ }
33
+
34
+ /** Apply one little-endian token bitmask to every row along the final axis.
35
+ * Borrow logits; return an owned lazy result. Copy the mutable host bits now,
36
+ * so a matcher can advance before GPU evaluation. Missing words reject tokens.
37
+ * Preserve the additive-mask expression, including NaNs and signed zero.
38
+ * Shape-specialized compilation handles bf16/f16/f32 and strided inputs. */
39
+ export function applyTokenBitmask(logits: MlxArray, bits: Int32Array): MlxArray {
40
+ const vocab = logits.shape.at(-1);
41
+ if (!vocab || logits.size === 0 || logits.size > 0x7fffffff)
42
+ throw new Error("token bitmask requires nonempty logits with at most 2^31-1 elements");
43
+ if (![Dtype.bfloat16, Dtype.float16, Dtype.float32].includes(logits.dtype))
44
+ throw new Error("token bitmask requires bf16, f16 or f32 logits");
45
+ const copy = new Int32Array(Math.ceil(vocab / 32));
46
+ copy.set(bits.subarray(0, copy.length));
47
+ const deviceBits = MlxArray.fromInt32(copy, [copy.length]);
48
+ try {
49
+ compiled ??= new CompiledFunction(([x, mask]) => [maskGraph(x!, mask!)], false);
50
+ return compiled.apply([logits, deviceBits])[0]!;
51
+ } finally { deviceBits.dispose(); }
52
+ }
@@ -0,0 +1,82 @@
1
+ // Exact byte-packed TurboQuant K/V decoding. The caller owns output arrays.
2
+ // Rotation and cache policy belong to the existing codec/cache layers.
3
+ import { type MlxArray, gpuStream } from './array';
4
+ import { Dtype, type MlxHandle } from './ffi';
5
+ import { isShapelessTracing } from './compile';
6
+ import { MetalKernel } from './metal-kernel';
7
+
8
+ export type PackedKvArrays = readonly [
9
+ keyIndices: MlxArray, keyScales: MlxArray, keyZeros: MlxArray,
10
+ valueIndices: MlxArray, valueScales: MlxArray,
11
+ ];
12
+
13
+ const source = `
14
+ uint col = thread_position_in_grid.x, pos = thread_position_in_grid.y;
15
+ uint bh = thread_position_in_grid.z;
16
+ if (col >= D || pos >= vp_shape[2] || bh >= vp_shape[0] * vp_shape[1]) return;
17
+ uint b = bh / vp_shape[1], h = bh % vp_shape[1];
18
+ uint ko = b * kp_strides[0] + h * kp_strides[1] + pos * kp_strides[2];
19
+ uint vpo = b * vp_strides[0] + h * vp_strides[1] + pos * vp_strides[2];
20
+ uint kso = b * ks_strides[0] + h * ks_strides[1] + pos * ks_strides[2] + (col / KG) * ks_strides[3];
21
+ uint kzo = b * kz_strides[0] + h * kz_strides[1] + pos * kz_strides[2] + (col / KG) * kz_strides[3];
22
+ uint vso = b * vs_strides[0] + h * vs_strides[1] + pos * vs_strides[2] + (col / VG) * vs_strides[3];
23
+ int key_code;
24
+ if (KBITS == 8) key_code = int(kp[ko + col * kp_strides[3]]);
25
+ else {
26
+ uint bit = col * KBITS, byte = bit / 8u, shift = bit % 8u;
27
+ uint word = uint(kp[ko + byte * kp_strides[3]]);
28
+ if (shift + KBITS > 8u) word |= uint(kp[ko + (byte + 1u) * kp_strides[3]]) << 8u;
29
+ key_code = int((word >> shift) & ((1u << KBITS) - 1u));
30
+ }
31
+ // Keep the incumbent f32 addition before multiplication.
32
+ float shifted = float(key_code) + float(kz[kzo]);
33
+ uint i = (bh * vp_shape[2] + pos) * D + col;
34
+ outK[i] = OutK(shifted * float(ks[kso]));
35
+ uint bit = col * VBITS, byte = bit / 8u, shift = bit % 8u;
36
+ uint word = uint(vp[vpo + byte * vp_strides[3]]);
37
+ if (shift + VBITS > 8u) word |= uint(vp[vpo + (byte + 1u) * vp_strides[3]]) << 8u;
38
+ uint code = (word >> shift) & ((1u << VBITS) - 1u);
39
+ outV[i] = OutV(centroids[code] * float(vs[vso]));
40
+ `;
41
+
42
+ let kernel: MetalKernel | undefined;
43
+ function jointKernel(): MetalKernel {
44
+ return kernel ??= new MetalKernel({
45
+ name: 'turboquant_kv_decode',
46
+ inputNames: ['kp', 'ks', 'kz', 'vp', 'vs', 'centroids'],
47
+ outputNames: ['outK', 'outV'], source, ensureRowContiguous: false,
48
+ });
49
+ }
50
+
51
+ /** Return bf16 K and rotated V (f32 for eager inverse rotation, bf16 for
52
+ * deferred rotation). Null means the ordinary codec must handle this input.
53
+ * Input arrays and the shared centroid table remain borrowed. */
54
+ export function tryJointDecodePackedKv(
55
+ inputs: PackedKvArrays, centroids: MlxArray, kBits: number, vBits: number,
56
+ headDim: number, deferV: boolean, stream: MlxHandle = gpuStream,
57
+ ): [MlxArray, MlxArray] | null {
58
+ if (stream !== gpuStream || isShapelessTracing() ||
59
+ ![64, 128, 256, 512].includes(headDim) ||
60
+ ![2, 4, 5, 8].includes(kBits) || ![2, 3, 4, 5, 8].includes(vBits)) return null;
61
+ const [kp, ks, kz, vp, vs] = inputs;
62
+ if (kp.dtype !== (kBits === 8 ? Dtype.int8 : Dtype.uint8) ||
63
+ vp.dtype !== Dtype.uint8 || centroids.dtype !== Dtype.float32 ||
64
+ centroids.ndim !== 1 || centroids.size !== (1 << vBits)) return null;
65
+ const shapes = inputs.map(a => a.shape);
66
+ if (shapes.some(shape => shape.length !== 4)) return null;
67
+ const [b, h, n] = shapes[3]!;
68
+ if (!b || !h || !n || b * h * n * headDim >= 2 ** 32 ||
69
+ shapes.some(shape => shape[0] !== b || shape[1] !== h || shape[2] !== n) ||
70
+ shapes[0]![3] !== headDim * kBits / 8 || shapes[3]![3] !== headDim * vBits / 8 ||
71
+ shapes[1]![3] !== shapes[2]![3]) return null;
72
+ const keyGroup = headDim / shapes[1]![3]!, valueGroup = headDim / shapes[4]![3]!;
73
+ if (![32, 64].includes(keyGroup) || ![32, 64].includes(valueGroup) ||
74
+ [ks, kz, vs].some(a => ![Dtype.float16, Dtype.bfloat16, Dtype.float32].includes(a.dtype))) return null;
75
+ const shape = [b, h, n, headDim], valueDtype = deferV ? Dtype.bfloat16 : Dtype.float32;
76
+ return jointKernel().apply([...inputs, centroids], {
77
+ templateDtypes: { OutK: Dtype.bfloat16, OutV: valueDtype },
78
+ templateInts: { D: headDim, KBITS: kBits, VBITS: vBits, KG: keyGroup, VG: valueGroup },
79
+ grid: [headDim, n, b * h], threadGroup: [Math.min(headDim, 256), Math.max(1, 256 / headDim), 1],
80
+ outputs: [{ shape, dtype: Dtype.bfloat16 }, { shape, dtype: valueDtype }], stream,
81
+ }) as [MlxArray, MlxArray];
82
+ }
@@ -0,0 +1,122 @@
1
+ // Joint packed K/V decode with eager inverse rotation in the measured D256 regime.
2
+ // Hadamard butterfly/layout follows MLX 0.31.2,
3
+ // mlx/backend/metal/kernels/hadamard.h, Copyright © 2024 Apple Inc., MIT.
4
+ import { type MlxArray, gpuStream } from "./array";
5
+ import { Dtype, type MlxHandle } from "./ffi";
6
+ import { isShapelessTracing } from "./compile";
7
+ import { MetalKernel } from "./metal-kernel";
8
+ import { type PackedKvArrays } from "./turboquant-kv-decode";
9
+
10
+ const header = `
11
+ template <short R> inline void tq_inverse_radix(thread float* x) {
12
+ constexpr short logR = __builtin_ctz(R);
13
+ short h = 1;
14
+ #pragma unroll
15
+ for (short s = 0; s < logR; s++) {
16
+ #pragma unroll
17
+ for (short i = 0; i < R / 2; i++) {
18
+ short k = i & (h - 1), j = ((i - k) << 1) + k;
19
+ float a = x[j], b = x[j + h];
20
+ x[j] = a + b; x[j + h] = a - b;
21
+ }
22
+ h <<= 1;
23
+ }
24
+ }
25
+ `;
26
+ const source = `
27
+ constexpr short D = 256, R = 16, NT = 16, LOG_R = 4;
28
+ uint pos = thread_position_in_grid.y, bh = thread_position_in_grid.z;
29
+ short i = thread_position_in_grid.x;
30
+ bool valid = pos < vp_shape[2];
31
+ uint b = bh / vp_shape[1], hidx = bh % vp_shape[1];
32
+ uint ko = b * kp_strides[0] + hidx * kp_strides[1] + pos * kp_strides[2];
33
+ uint vo = b * vp_strides[0] + hidx * vp_strides[1] + pos * vp_strides[2];
34
+ uint kso = b * ks_strides[0] + hidx * ks_strides[1] + pos * ks_strides[2];
35
+ uint kzo = b * kz_strides[0] + hidx * kz_strides[1] + pos * kz_strides[2];
36
+ uint vso = b * vs_strides[0] + hidx * vs_strides[1] + pos * vs_strides[2];
37
+ uint row = (bh * vp_shape[2] + pos) * D;
38
+ threadgroup float storage[D * 16];
39
+ threadgroup float* buf = storage + thread_position_in_threadgroup.y * D;
40
+ #pragma unroll
41
+ for (short j = 0; j < R / 4; j++) {
42
+ short index = j * 4 * NT + i * 4;
43
+ #pragma unroll
44
+ for (short r = 0; r < 4; r++) {
45
+ uint col = index + r;
46
+ if (!valid) { buf[col] = 0.0f; continue; }
47
+ int key_code = int(kp[ko + col * kp_strides[3]]);
48
+ float shifted = float(key_code) + float(kz[kzo + (col / 32) * kz_strides[3]]);
49
+ outK[row + col] = OutK(shifted * float(ks[kso + (col / 32) * ks_strides[3]]));
50
+ uint bit = col * 3u, byte = bit / 8u, shift = bit % 8u;
51
+ uint word = uint(vp[vo + byte * vp_strides[3]]);
52
+ if (shift + 3u > 8u) word |= uint(vp[vo + (byte + 1u) * vp_strides[3]]) << 8u;
53
+ uint code = (word >> shift) & 7u;
54
+ buf[col] = centroids[code * centroids_strides[0]] * float(vs[vso + (col / 32) * vs_strides[3]]);
55
+ }
56
+ }
57
+ threadgroup_barrier(mem_flags::mem_threadgroup);
58
+ float x[R]; short h = 1;
59
+ #pragma unroll
60
+ for (short s = 0; s < 2; s++) {
61
+ short k = i & (h - 1), j = ((i - k) << LOG_R) + k;
62
+ #pragma unroll
63
+ for (short r = 0; r < R; r++) x[r] = buf[j + h * r];
64
+ tq_inverse_radix<R>(x);
65
+ #pragma unroll
66
+ for (short r = 0; r < R; r++) buf[j + h * r] = x[r];
67
+ h <<= LOG_R;
68
+ threadgroup_barrier(mem_flags::mem_threadgroup);
69
+ }
70
+ #pragma unroll
71
+ for (short j = 0; j < R / 4; j++) {
72
+ short index = j * 4 * NT + i * 4;
73
+ #pragma unroll
74
+ for (short r = 0; r < 4; r++) {
75
+ uint col = index + r;
76
+ float normalized = buf[col] * 0.0625f;
77
+ if (valid) outV[row + col] = OutV(normalized * signs[col * signs_strides[0]]);
78
+ }
79
+ }
80
+ `;
81
+
82
+ let kernel: MetalKernel | undefined;
83
+ function inverseKernel(): MetalKernel {
84
+ return kernel ??= new MetalKernel({
85
+ name: "turboquant_kv_inverse256",
86
+ inputNames: ["kp", "ks", "kz", "vp", "vs", "centroids", "signs"],
87
+ outputNames: ["outK", "outV"], header, source, ensureRowContiguous: false,
88
+ });
89
+ }
90
+
91
+ /** Borrow packed inputs and codec tables; return owned bf16 K/V or null.
92
+ * Only the measured eager D256/k8v3/B1/H4/fp16/group32 regime is eligible.
93
+ * Other regimes continue through the existing joint decoder or original codec.
94
+ * N affects dispatch size but is never a shader template parameter. */
95
+ export function tryDecodePackedKvInverse256(
96
+ inputs: PackedKvArrays, centroids: MlxArray, signs: MlxArray,
97
+ kBits: number, vBits: number, headDim: number, s: MlxHandle = gpuStream,
98
+ ): [MlxArray, MlxArray] | null {
99
+ if (s !== gpuStream || isShapelessTracing() || headDim !== 256 || kBits !== 8 || vBits !== 3)
100
+ return null;
101
+ const [kp, ks, kz, vp, vs] = inputs;
102
+ const shape = kp.shape;
103
+ if (shape.length !== 4 || shape[0] !== 1 || shape[1] !== 4 || shape[3] !== 256)
104
+ return null;
105
+ const n = shape[2]!;
106
+ if (n < 8192 || n * 1024 >= 2 ** 32) return null;
107
+ const samePrefix = (a: MlxArray, width: number) => {
108
+ const x = a.shape;
109
+ return x.length === 4 && x[0] === 1 && x[1] === 4 && x[2] === n && x[3] === width;
110
+ };
111
+ if (kp.dtype !== Dtype.int8 || vp.dtype !== Dtype.uint8 || !samePrefix(vp, 96)) return null;
112
+ if (![ks, kz, vs].every(a => a.dtype === Dtype.float16 && samePrefix(a, 8))) return null;
113
+ if (centroids.dtype !== Dtype.float32 || centroids.shape.length !== 1 || centroids.shape[0] !== 8)
114
+ return null;
115
+ if (signs.dtype !== Dtype.float32 || signs.shape.length !== 1 || signs.shape[0] !== 256)
116
+ return null;
117
+ return inverseKernel().apply([...inputs, centroids, signs], {
118
+ templateDtypes: { OutK: Dtype.bfloat16, OutV: Dtype.bfloat16 },
119
+ grid: [16, Math.ceil(n / 16) * 16, 4], threadGroup: [16, 16, 1],
120
+ outputs: [{ shape, dtype: Dtype.bfloat16 }, { shape, dtype: Dtype.bfloat16 }], stream: s,
121
+ }) as [MlxArray, MlxArray];
122
+ }