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
@@ -0,0 +1,93 @@
1
+ import { decodedKvDonorAttention } from "./decoded-kv-donor";
2
+ import type { MlxArray } from "../mlx/array";
3
+ import { KvTensorRows, type KvTensorRowView } from "../backends/mlx/kv-tensor-rows";
4
+ import { disposeResources } from "../engine/resources";
5
+ import { TurboQuantKVCache, type BatchableCache, type Cache, type Mask, type RotatedValueAttentionState, type PaddedPrefillCache, type PrefillPadding } from "./gemma4-base";
6
+ import { TurboQuantCodec, disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
7
+
8
+ const fields = ["kIdx", "kScales", "kZeros", "vPacked", "vScales"] as const;
9
+ function encoded(planes: readonly MlxArray[]): TurboQuantTensor {
10
+ return { kIdx: planes[0]!, kScales: planes[1]!, kZeros: planes[2]!, vPacked: planes[3]!, vScales: planes[4]! };
11
+ }
12
+
13
+ /** The existing TurboQuant codec over shared tensor-row storage. Row changes
14
+ * copy encoded bytes without decoding, rotating or requantizing their values. */
15
+ export class BatchedTurboQuantKVCache implements BatchableCache, PaddedPrefillCache {
16
+ readonly #storage = new KvTensorRows();
17
+ readonly #codec: TurboQuantCodec;
18
+ #reuseOffsets: number[] = [];
19
+ #headDim: number | null = null;
20
+ constructor(readonly kBits: number, readonly vBits: number) {
21
+ this.#codec = new TurboQuantCodec(kBits, vBits, process.env.MLX_BUN_TURBOQUANT_FUSED_DECODE === "1");
22
+ }
23
+ restorePrefillEnds(ends: readonly number[] | undefined): void { this.#storage.restorePrefillEnds(ends); }
24
+ preparePrefill(padding: PrefillPadding): void {
25
+ this.#storage.preparePrefill(padding);
26
+ if (!this.#reuseOffsets.length) this.#reuseOffsets = padding.lengths.map(() => 0);
27
+ }
28
+ finalizePrefill(): void { this.#storage.finalizePrefill(); }
29
+ captureDonorRows(): import("./gemma4-base").KvDonorRows {
30
+ if (!this.#storage.planes.length || this.#headDim === null) throw new Error("cache is empty");
31
+ const [keys, values] = this.#codec.decode(encoded(this.#storage.planes), this.offset, this.#headDim, false);
32
+ return { keys, values, ...this.#storage.captureDonorValidity() };
33
+ }
34
+ captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
35
+ return decodedKvDonorAttention(this.captureDonorRows());
36
+ }
37
+ get rotatedValueAttention(): RotatedValueAttentionState { return this; }
38
+ signature(): string { return `kv:batched-turboquant:${this.kBits}:${this.vBits}`; }
39
+ get minimumReusableOffset(): number { return Math.max(0, ...this.#reuseOffsets); }
40
+ get headDim(): number | null { return this.#headDim; }
41
+ get rowOffsets(): readonly number[] { return this.#storage.rowOffsets; }
42
+ get leftPad(): readonly number[] { return this.#storage.leftPad; }
43
+ get batchSize(): number | null { return this.#storage.batchSize; }
44
+ get offset(): number { return this.#storage.offset; }
45
+ get ropeOffsetArr(): MlxArray | undefined { return this.#storage.ropeOffsetArr; }
46
+ makeEmptyBatch(): BatchedTurboQuantKVCache { return new BatchedTurboQuantKVCache(this.kBits, this.vBits); }
47
+ bytesPerToken(): number { return this.#storage.bytesPerToken(); }
48
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * tokens; }
49
+ makeMask(tokens: number, window: number | null): Mask { return this.#storage.makeMask(tokens, window); }
50
+ isTrimmable(): boolean { return true; }
51
+ trim(count: number): void { this.#storage.trim(count); }
52
+ specRoundBegin(): void { this.#storage.specRoundBegin(); }
53
+ specRoundCommit(): void { this.#storage.specRoundCommit(); }
54
+ specRoundRollback(keep: number | readonly number[]): void { this.#storage.specRoundRollback(keep); }
55
+ #append(k: MlxArray, v: MlxArray, defer: boolean): [MlxArray, MlxArray] {
56
+ const packed = this.#codec.encode(k, v);
57
+ try {
58
+ this.#storage.append(fields.map(field => packed[field]));
59
+ this.#headDim = k.shape[3]!;
60
+ return this.#codec.decode(encoded(this.#storage.planes), this.offset, this.#headDim, defer);
61
+ } finally { disposeTurboQuant(packed); }
62
+ }
63
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, false); }
64
+ updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, true); }
65
+ mergeRows(rows: readonly Cache[]): void {
66
+ const views: KvTensorRowView[] = [], held: MlxArray[] = [];
67
+ const reuseOffsets: number[] = [];
68
+ let dim = this.#headDim;
69
+ try {
70
+ for (const row of rows) {
71
+ if (row instanceof BatchedTurboQuantKVCache) {
72
+ views.push(row.#storage); dim ??= row.headDim; reuseOffsets.push(...row.#reuseOffsets);
73
+ } else if (row instanceof TurboQuantKVCache) {
74
+ const planes = row.state(); held.push(...planes); reuseOffsets.push(row.minimumReusableOffset);
75
+ views.push({ planes, rowOffsets: [row.offset], leftPad: [0] }); dim ??= row.headDim;
76
+ } else throw new Error(`TurboQuant row layout cannot merge ${row.signature()}`);
77
+ }
78
+ this.#storage.mergeRows(views); this.#headDim = dim; this.#reuseOffsets = reuseOffsets;
79
+ } finally { disposeResources(held); }
80
+ }
81
+ alignRows(leftPad: readonly number[]): void { this.#storage.alignRows(leftPad); }
82
+ filterRows(keep: readonly number[]): void {
83
+ this.#storage.filterRows(keep); this.#reuseOffsets = keep.map(row => this.#reuseOffsets[row]!);
84
+ }
85
+ state(): MlxArray[] { return this.#storage.planes; }
86
+ extractRow(row: number): TurboQuantKVCache {
87
+ const result = new TurboQuantKVCache(this.kBits, this.vBits), count = this.rowOffsets[row]!;
88
+ if (count) result.restoreState(encoded(this.#storage.extractRow(row)), count, this.#headDim!);
89
+ result.minimumReusableOffset = this.#reuseOffsets[row] ?? 0;
90
+ return result;
91
+ }
92
+ dispose(): void { this.#storage.dispose(); this.#headDim = null; this.#reuseOffsets = []; }
93
+ }
@@ -1,4 +1,4 @@
1
- // Compiled decode step (Phase A of docs/design/optimization_plan.md).
1
+ // Compiled decode step (Phase A of docs/archive/investigations/optimization_plan.md).
2
2
  //
3
3
  // The single-token decode graph is identical token-to-token except for
4
4
  // integer state (RoPE offset, cache write position, active length). This
@@ -277,7 +277,10 @@ function decodePhase(c: AnyCache): "concat" | "ring" {
277
277
  }
278
278
 
279
279
  function disposeShared(s: SharedKv): void {
280
- if (s.kind === "plain") {
280
+ if (s.kind === "view") {
281
+ s.attention.dispose();
282
+ s.offsetArr?.dispose();
283
+ } else if (s.kind === "plain") {
281
284
  s.keys.dispose();
282
285
  s.values.dispose();
283
286
  } else {
@@ -381,6 +384,9 @@ export class CompiledDecode {
381
384
  readonly #segmented: boolean;
382
385
 
383
386
  private constructor(readonly model: Gemma4Model) {
387
+ // A fresh resume has no prefill to evaluate lazy FP32 constants. Capture
388
+ // their values, not their construction graph, in every compiled closure.
389
+ model.materializeGraphConstants();
384
390
  const t = model.config.text;
385
391
  this.#segmented =
386
392
  t.numKvSharedLayers === 0 && !t.enableMoeBlock && model.perLayerWidth === 0;
@@ -0,0 +1,12 @@
1
+ import * as ops from "../mlx/ops";
2
+ import type { KvDonorAttention, KvDonorRows } from "./gemma4-base";
3
+
4
+ /** Takes ownership of decoded donor planes. Assistant attention uses decoded
5
+ * values, preserving its SDPA arithmetic rather than moving inverse rotation. */
6
+ export function decodedKvDonorAttention(view: KvDonorRows): KvDonorAttention {
7
+ const { keys, values, offsets, starts, ends } = view;
8
+ return { width: keys.shape[2]!, dtype: keys.dtype, offsets, starts, ends,
9
+ attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
10
+ dispose() { keys.dispose(); values.dispose(); },
11
+ };
12
+ }
@@ -0,0 +1,72 @@
1
+ import { captureFullKvDonorAttention } from "../backends/mlx/full-kv-row-donor";
2
+ import { appendFullKvRows } from "../backends/mlx/full-kv-row-append";
3
+ import { fullRowPadding, fullRowPhysicalLength } from "./full-prefill-row";
4
+ import { captureKvAttention } from "./kv-attention-view";
5
+ import type { MlxArray } from "../mlx/array";
6
+ import * as ops from "../mlx/ops";
7
+ import { FullTransitioningKvRows } from "../backends/mlx/full-transitioning-kv-rows";
8
+ import { BatchedQuantizedKVCache } from "./batched-quantized-kv";
9
+ import { QuantizedKVCache, type Cache, type Mask, type KvAttentionState, type KvAttentionView } from "./gemma4-base";
10
+
11
+ /** Affine storage keeps its native quantized-attention arithmetic while rows
12
+ * cross the conversion boundary independently. Once all rows convert, their
13
+ * packed planes use the existing batched attention implementation. */
14
+ export class DelayedQuantizedKVCache extends FullTransitioningKvRows<BatchedQuantizedKVCache> implements KvAttentionState {
15
+ constructor(readonly groupSize: number, readonly bits: number, readonly start: number,
16
+ readonly maintain: (rows: Cache[]) => void, row?: Cache) {
17
+ super({ signature: `kv:delayed-quant:${bits}:${groupSize}:${start}`, maintain,
18
+ converted: row => row instanceof QuantizedKVCache,
19
+ makeLayout: () => new BatchedQuantizedKVCache(groupSize, bits) }, row);
20
+ }
21
+ get attentionState(): KvAttentionState { return this; }
22
+ captureDonorAttention() {
23
+ return this.packed?.captureDonorAttention() ?? captureFullKvDonorAttention(this.rows, this.leftPad, this.offset);
24
+ }
25
+ makeEmptyBatch(): DelayedQuantizedKVCache { return new DelayedQuantizedKVCache(this.groupSize, this.bits, this.start, this.maintain); }
26
+ updateAndFetch(): [MlxArray, MlxArray] { throw new Error("mixed precision rows use their attention state"); }
27
+ appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView {
28
+ this.advance();
29
+ if (this.packed) return captureKvAttention(this.packed, k, v);
30
+ if (this.rows.every(row => !row.quantizedAttention)) {
31
+ const [keys, values] = appendFullKvRows(this.rows, this.leftPad, this.offset + k.shape[2]!, k, v);
32
+ try { this.syncPositions(true); } catch (error) { keys.dispose(); values.dispose(); throw error; }
33
+ return { attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
34
+ dispose() { keys.dispose(); values.dispose(); } };
35
+ }
36
+ const views: KvAttentionView[] = [], pads = this.rows.map((row, index) => this.leftPad[index]! - fullRowPadding(row));
37
+ const lengths = this.rows.map(cache => fullRowPhysicalLength(cache) + k.shape[2]!);
38
+ try {
39
+ for (const [row, cache] of this.rows.entries()) {
40
+ const slice = (a: MlxArray) => a.slice([row, 0, 0, 0], [row + 1, a.shape[1]!, a.shape[2]!, a.shape[3]!]);
41
+ using kr = slice(k), vr = slice(v);
42
+ views.push(captureKvAttention(cache, kr, vr));
43
+ }
44
+ this.syncPositions(true);
45
+ } catch (error) { for (const view of views) view.dispose(); throw error; }
46
+ return {
47
+ attend(q, scale, mask) {
48
+ const outputs: MlxArray[] = [];
49
+ try {
50
+ for (const [row, view] of views.entries()) {
51
+ using qr = q.slice([row, 0, 0, 0], [row + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
52
+ // Capture alignment with the KV tensors, never consult live rows.
53
+ const start = mask.arr?.shape.map(() => 0), end = mask.arr ? [...mask.arr.shape] : undefined;
54
+ if (start && end) {
55
+ if (end.length === 4 && end[0]! > 1) { start[0] = row; end[0] = row + 1; }
56
+ start[start.length - 1] = pads[row]!;
57
+ end[end.length - 1] = pads[row]! + lengths[row]!;
58
+ }
59
+ using arr = mask.arr ? mask.arr.slice(start!, end!) : null;
60
+ outputs.push(view.attend(qr, scale, { mode: mask.mode, arr }));
61
+ }
62
+ return outputs.length === 1 ? ops.contiguous(outputs[0]!) : ops.concatAxis(outputs, 0);
63
+ } finally { for (const output of outputs) output.dispose(); }
64
+ },
65
+ dispose() { for (const view of views) view.dispose(); },
66
+ };
67
+ }
68
+ updateAndAttend(q: MlxArray, k: MlxArray, v: MlxArray, scale: number, mask: Mask): MlxArray {
69
+ const view = this.appendAndFetch(k, v);
70
+ try { return view.attend(q, scale, mask); } finally { view.dispose(); }
71
+ }
72
+ }
@@ -0,0 +1,93 @@
1
+ import type { MlxArray } from "../mlx/array";
2
+ import * as ops from "../mlx/ops";
3
+ import { RotatingKVCache, type Cache, type KvAttentionState, type KvAttentionView, type PaddedPrefillCache, type PrefillPadding } from "./gemma4-base";
4
+ import { SpeculativeTransitioningKvRows } from "../backends/mlx/transitioning-kv-rows";
5
+ import { AlignedRotatingCache, alignRotatingRows, RotatingAffineLayout, SpeculativeRotatingAffineLayout, RotatingKvPositions } from "./rotating-kv-layout";
6
+ import { BatchedRotatingQuantCache } from "./batched-rotating-quant";
7
+ import { captureKvAttention, combineKvDonorAttention } from "./kv-attention-view";
8
+
9
+ /** Precision changes preserve each row's physical columns. The model owns
10
+ * queries and scale; the shared lifecycle owns membership and conversion. */
11
+ export class DelayedRotatingQuantizedKVCache extends SpeculativeTransitioningKvRows<RotatingAffineLayout> implements KvAttentionState, PaddedPrefillCache {
12
+ constructor(readonly maxSize: number, readonly groupSize: number, readonly bits: number, readonly start: number,
13
+ readonly maintain: (rows: Cache[]) => void, row?: Cache, readonly speculative = false) {
14
+ super({ signature: `kv:delayed-rotating-quant:${maxSize}:${bits}:${groupSize}:${start}`, maintain,
15
+ converted: row => row instanceof AlignedRotatingCache && row.inner instanceof BatchedRotatingQuantCache,
16
+ makeLayout: () => speculative ? new SpeculativeRotatingAffineLayout(maxSize, groupSize, bits) : new RotatingAffineLayout(maxSize, groupSize, bits), prepareRows: alignRotatingRows,
17
+ packRows: (layout, rows) => layout.adoptAlignedRows(rows as readonly AlignedRotatingCache[]),
18
+ extractRow: row => (row as AlignedRotatingCache).extract(speculative ? maxSize : undefined),
19
+ rollbackRow: (row, before, keep, preserve) => (row as AlignedRotatingCache).rollback(before, keep, preserve) }, row, new RotatingKvPositions(maxSize));
20
+ }
21
+ get attentionState(): KvAttentionState { return this; }
22
+ captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
23
+ if (this.packed) return this.packed.captureDonorAttention();
24
+ const views: import("./gemma4-base").KvDonorAttention[] = [];
25
+ try {
26
+ for (const row of this.rows) views.push((row as AlignedRotatingCache).captureDonorAttention());
27
+ return combineKvDonorAttention(views);
28
+ } catch (error) { for (const view of views) view.dispose(); throw error; }
29
+ }
30
+ override makeMask(tokens: number, window: number | null) {
31
+ if (this.speculative && this.leftPad.every(pad => pad === 0) &&
32
+ this.offset + tokens <= Math.min(this.maxSize, window ?? this.maxSize))
33
+ return { mode: tokens === 1 ? "" as const : "causal" as const, arr: null };
34
+ return super.makeMask(tokens, window);
35
+ }
36
+ preparePrefill(padding: PrefillPadding): void {
37
+ if (!this.batchSize) {
38
+ const rows = padding.lengths.map(() => new RotatingKVCache(this.maxSize));
39
+ try { this.mergeRows(rows); } finally { for (const row of rows) row.dispose(); }
40
+ }
41
+ if (this.packed) { this.packed.preparePrefill(padding); return; }
42
+ // Every physical row follows the same block geometry, even when only a
43
+ // sibling has right padding. The token lengths still belong to each row.
44
+ const rightPaddedBatch = padding.rightPadding?.some(pad => pad > 0);
45
+ for (const [row, cache] of this.rows.entries()) (cache as AlignedRotatingCache).inner.preparePrefill({
46
+ lengths: [padding.lengths[row]!],
47
+ ...(padding.leftPadding ? { leftPadding: [padding.leftPadding[row]!] } : {}),
48
+ ...(padding.rightPadding ? { rightPadding: [padding.rightPadding[row]!] } : {}),
49
+ }, rightPaddedBatch);
50
+ this.syncPositions(true);
51
+ }
52
+ finalizePrefill(): void {
53
+ if (this.packed) { this.packed.finalizePrefill(); return; }
54
+ for (const cache of this.rows) (cache as AlignedRotatingCache).inner.finalizePrefill();
55
+ this.syncPositions(true);
56
+ }
57
+ get maxTokens(): number { return this.maxSize; }
58
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * Math.min(tokens, this.maxSize); }
59
+ makeEmptyBatch(): DelayedRotatingQuantizedKVCache {
60
+ return new DelayedRotatingQuantizedKVCache(this.maxSize, this.groupSize, this.bits, this.start, this.maintain, undefined, this.speculative);
61
+ }
62
+ updateAndFetch(): [MlxArray, MlxArray] { throw new Error("mixed precision rows use their attention state"); }
63
+ releaseRopeArr(): void { this.packed?.releaseRopeArr(); }
64
+ appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView {
65
+ this.advance();
66
+ if (this.packed) return captureKvAttention(this.packed, k, v);
67
+ const views: KvAttentionView[] = [];
68
+ try {
69
+ for (const [row, cache] of this.rows.entries()) {
70
+ const slice = (a: MlxArray) => a.slice([row, 0, 0, 0], [row + 1, a.shape[1]!, a.shape[2]!, a.shape[3]!]);
71
+ using kr = slice(k), vr = slice(v);
72
+ views.push(captureKvAttention(cache, kr, vr));
73
+ }
74
+ this.syncPositions(true);
75
+ } catch (error) { for (const view of views) view.dispose(); throw error; }
76
+ return {
77
+ attend(q, scale, mask) {
78
+ const outputs: MlxArray[] = [];
79
+ try {
80
+ for (const [row, view] of views.entries()) {
81
+ using qr = q.slice([row, 0, 0, 0], [row + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
82
+ const start = mask.arr?.shape.map(() => 0), end = mask.arr ? [...mask.arr.shape] : undefined;
83
+ if (start && end && end.length === 4 && end[0]! > 1) { start[0] = row; end[0] = row + 1; }
84
+ using arr = mask.arr ? mask.arr.slice(start!, end!) : null;
85
+ outputs.push(view.attend(qr, scale, { mode: mask.mode, arr }));
86
+ }
87
+ return outputs.length === 1 ? ops.contiguous(outputs[0]!) : ops.concatAxis(outputs, 0);
88
+ } finally { for (const output of outputs) output.dispose(); }
89
+ },
90
+ dispose() { for (const view of views) view.dispose(); },
91
+ };
92
+ }
93
+ }
@@ -0,0 +1,55 @@
1
+ import { captureFullKvDonorRows } from "../backends/mlx/full-kv-row-donor";
2
+ import { decodedKvDonorAttention } from "./decoded-kv-donor";
3
+ import { appendFullKvRows } from "../backends/mlx/full-kv-row-append";
4
+ import type { MlxArray } from "../mlx/array";
5
+ import { Dtype } from "../mlx/ffi";
6
+ import * as ops from "../mlx/ops";
7
+ import { unrotateValues } from "../mlx/turboquant-ops";
8
+ import { type Cache, type RotatedValueAttentionState, TurboQuantKVCache } from "./gemma4-base";
9
+ import { BatchedTurboQuantKVCache } from "./batched-turboquant-kv";
10
+ import { FullTransitioningKvRows } from "../backends/mlx/full-transitioning-kv-rows";
11
+
12
+ /** TurboQuant tensor access and value-domain handling over the shared
13
+ * row-transition lifecycle. Model projections and attention remain batched. */
14
+ export class DelayedTurboQuantKVCache extends FullTransitioningKvRows<BatchedTurboQuantKVCache> implements RotatedValueAttentionState {
15
+ #rotated: boolean[] = [];
16
+ constructor(readonly kBits: number, readonly vBits: number, readonly start: number,
17
+ readonly maintain: (rows: Cache[]) => void, row?: Cache) {
18
+ super({ signature: `kv:delayed-turboquant:${kBits}:${vBits}:${start}`, maintain,
19
+ converted: row => row instanceof TurboQuantKVCache,
20
+ makeLayout: () => new BatchedTurboQuantKVCache(kBits, vBits) }, row);
21
+ }
22
+ captureDonorRows(): import("./gemma4-base").KvDonorRows {
23
+ return this.packed?.captureDonorRows() ?? captureFullKvDonorRows(this.rows, this.leftPad, this.offset);
24
+ }
25
+ captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
26
+ return decodedKvDonorAttention(this.captureDonorRows());
27
+ }
28
+ get rotatedValueAttention(): RotatedValueAttentionState { return this; }
29
+ #append(k: MlxArray, v: MlxArray, deferred: boolean): [MlxArray, MlxArray] {
30
+ this.advance();
31
+ if (this.packed) {
32
+ this.#rotated = Array.from({ length: this.batchSize! }, () => true);
33
+ return deferred ? this.packed.updateAndFetchDeferredV(k, v) : this.packed.updateAndFetch(k, v);
34
+ }
35
+ this.#rotated = this.rows.map(row => !!row.rotatedValueAttention);
36
+ const result = appendFullKvRows(this.rows, this.leftPad, this.offset + k.shape[2]!, k, v, deferred);
37
+ try { this.syncPositions(true); } catch (error) { result[0].dispose(); result[1].dispose(); throw error; }
38
+ return result;
39
+ }
40
+
41
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, false); }
42
+ updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, true); }
43
+ captureValueTransform(): (output: MlxArray) => MlxArray {
44
+ const rotated = [...this.#rotated];
45
+ if (rotated.every(value => value)) return unrotateValues;
46
+ if (rotated.every(value => !value)) return output => ops.contiguous(output);
47
+ return output => {
48
+ using restored = unrotateValues(output);
49
+ using flags = ops.fromInt32(rotated.map(value => Number(value)), [rotated.length, 1, 1, 1]);
50
+ using mask = flags.astype(Dtype.bool);
51
+ return ops.where(mask, restored, output);
52
+ };
53
+ }
54
+ makeEmptyBatch(): DelayedTurboQuantKVCache { return new DelayedTurboQuantKVCache(this.kBits, this.vBits, this.start, this.maintain); }
55
+ }
@@ -1,3 +1,4 @@
1
+ import { cleanupFailure, disposeResources } from "../engine/resources";
1
2
  // DiffusionGemma-26B-A4B-it (model_type "diffusion_gemma") — the first
2
3
  // NON-autoregressive model in the codebase. Instead of a left-to-right AR loop
3
4
  // it runs an ENCODER prefill over the prompt (builds a KV cache) and then a
@@ -10,7 +11,7 @@
10
11
  // Ported verbatim from optiq/vlm/_mlxvlm/models/diffusion_gemma/language.py.
11
12
  // Reuses mlx-bun's quantized primitives (QuantizedLinear/Embedding/SwitchLinear,
12
13
  // RMSNorm, KVCache/RotatingKVCache) from gemma4-base. Architecture deltas vs
13
- // gemma4 (verified against the checkpoint, see docs/design/diffusion-gemma-port.md):
14
+ // gemma4 (verified against the checkpoint, see docs/design/generic-model-support.md):
14
15
  // - attention scale=1.0, NO attention softcap (only final-logit softcap 30 fp32)
15
16
  // - QK/V-norm POST-proj / PRE-RoPE; v_norm is RMSNormNoScale; V gets no RoPE
16
17
  // - full-attention layers (5,11,17,23,29) reuse k as v (no v_proj), hd=512 kv=2,
@@ -626,8 +627,8 @@ export class DiffusionGemmaModel {
626
627
  /** Prefill from a pre-spliced image+text prompt → populated cache. */
627
628
  prefillVision(splicedIds: number[], pixels: MlxArray): Cache[] {
628
629
  const cache = this.makeCache();
629
- this.encodeVisionPrefill(splicedIds, pixels, cache);
630
- return cache;
630
+ try { this.encodeVisionPrefill(splicedIds, pixels, cache); return cache; }
631
+ catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
631
632
  }
632
633
 
633
634
  /** One KVCache (full) / RotatingKVCache (sliding) per layer — the encoder
@@ -899,16 +900,15 @@ export class DiffusionGemmaModel {
899
900
  * loop reuses it across all steps of a canvas block). */
900
901
  prefill(promptIds: number[]): Cache[] {
901
902
  const cache = this.makeCache();
902
- this.encodePrompt(promptIds, cache);
903
- return cache;
903
+ try { this.encodePrompt(promptIds, cache); return cache; }
904
+ catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
904
905
  }
905
906
 
906
907
  /** One denoising-step decoder pass -> softcapped logits [1, canvas, vocab]. */
907
908
  decoderLogits(canvasArr: MlxArray, cache: Cache[], scEmbeddings: MlxArray | null): MlxArray {
908
909
  const h = this.decodeCanvasArr(canvasArr, cache, null, scEmbeddings);
909
- const logits = this.logitsFromHidden(h);
910
- h.dispose();
911
- return logits;
910
+ try { return this.logitsFromHidden(h); }
911
+ finally { h.dispose(); }
912
912
  }
913
913
 
914
914
  /** Dequantized embedding table (once per call), for self-conditioning soft
@@ -24,16 +24,55 @@ import {
24
24
  } from "./glm52-memory";
25
25
  import { UniversalDenseModel } from "./universal/dense";
26
26
  import { genericArgsFor } from "./universal/archs";
27
+ import {
28
+ ModelImplementationRegistry,
29
+ type ModelImplementation,
30
+ type ModelImplementationProvider,
31
+ } from "./implementation";
27
32
  import {
28
33
  assertResolvedModelProfile,
29
34
  resolveModelProfile,
30
35
  type ResolvedModelProfile,
36
+ type ResolveModelProfileOptions,
37
+ type ModelGraph,
31
38
  } from "./profile";
32
39
 
33
40
  export type RuntimeModel =
34
41
  | Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model | Qwen3MoeModel
35
42
  | DiffusionGemmaModel | Glm52Model | UniversalDenseModel;
36
43
 
44
+ function residentImplementation(
45
+ id: string,
46
+ graph: ModelGraph,
47
+ create: ModelImplementation<Weights, RuntimeModel>["create"],
48
+ ): ModelImplementation<Weights, RuntimeModel> {
49
+ return { id, graph, loader: "safetensors",
50
+ loop: graph === "diffusion-gemma" ? "diffusion" : "autoregressive", create };
51
+ }
52
+
53
+ /** Engine-owned registrations. Exact quant profiles can name additional
54
+ * implementations in a composed registry, without changing sessions or files. */
55
+ export const MLX_MODEL_IMPLEMENTATIONS = new ModelImplementationRegistry<Weights, RuntimeModel>([
56
+ residentImplementation("diffusion-gemma", "diffusion-gemma", (weights, config) => new DiffusionGemmaModel(weights, config)),
57
+ residentImplementation("minicpm5", "minicpm5", (weights, config) => new MiniCPM5Model(weights, config)),
58
+ residentImplementation("qwen3.5", "qwen3.5", (weights, config) => new Qwen35Model(weights, config)),
59
+ residentImplementation("qwen3-moe", "qwen3-moe", (weights, config) => new Qwen3MoeModel(weights, config)),
60
+ residentImplementation("qwen3", "qwen3", (weights, config) => new Qwen3Model(weights, config)),
61
+ residentImplementation("gemma4", "gemma4", (weights, config) => new Gemma4Model(weights, config)),
62
+ residentImplementation("gemma4-generated", "gemma4", (weights, config) => {
63
+ const Graph = GENERATED.get(configFingerprint(config));
64
+ if (!Graph) throw new Error("no generated Gemma graph for this config; refusing to fall back");
65
+ return new Graph(weights, config);
66
+ }),
67
+ residentImplementation("universal-dense", "universal-dense", (weights, config) =>
68
+ new UniversalDenseModel(weights, config, genericArgsFor(config)!)),
69
+ ]);
70
+
71
+ export interface ModelOpenOptions<Model = RuntimeModel> extends Glm52RuntimeOpenOptions {
72
+ readonly profiles?: ResolveModelProfileOptions;
73
+ readonly implementations?: ModelImplementationProvider<Weights, Model>;
74
+ }
75
+
37
76
  export interface Glm52RuntimeOpenOptions {
38
77
  /** Whole-process ceiling. Defaults to the smaller of the validated 25 GiB
39
78
  * preset and physical RAM. */
@@ -100,47 +139,51 @@ export async function openGlm52RuntimeModel(
100
139
  * model.safetensors.index.json, so it must bypass Weights.open and use its
101
140
  * dedicated header catalog/tensor source.
102
141
  */
103
- export async function openModel(
142
+ export function openModel<Model>(
104
143
  modelDir: string,
105
- options: Glm52RuntimeOpenOptions = {},
106
- ): Promise<RuntimeModel> {
144
+ options: ModelOpenOptions<Model> & { readonly implementations: ModelImplementationProvider<Weights, Model> },
145
+ ): Promise<Model>;
146
+ export function openModel(modelDir: string, options?: ModelOpenOptions): Promise<RuntimeModel>;
147
+ export async function openModel<Model>(
148
+ modelDir: string,
149
+ options: ModelOpenOptions<Model> = {},
150
+ ): Promise<Model | RuntimeModel> {
107
151
  const config = await loadModelConfig(modelDir);
108
- const profile = resolveModelProfile(config);
109
- if (profile.profile.execution.loader === "colibri")
152
+ const profile = resolveModelProfile(config, options.profiles);
153
+ if (profile.profile.execution.loader === "colibri") {
154
+ if (options.implementations || profile.profile.execution.implementation !== undefined)
155
+ throw new Error("custom Colibri implementations require a streamed loader binding; refusing to fall back");
110
156
  return (await openGlm52RuntimeModel(modelDir, options)).model;
111
- return createModel(await Weights.open(modelDir), config, profile);
157
+ }
158
+ // Resolve before opening weights; missing or incompatible code allocates nothing.
159
+ const implementation = (options.implementations ?? MLX_MODEL_IMPLEMENTATIONS).select(config, profile);
160
+ const weights = await Weights.open(modelDir);
161
+ try { return implementation.create(weights, config, profile); }
162
+ catch (error) { weights.dispose(); throw error; }
112
163
  }
113
164
 
114
- /** Construct the graph named by a previously validated profile. */
165
+ /** Construct the binding named by a validated profile. A supplied registry can
166
+ * return any backend-owned interface; the compatibility default returns the
167
+ * legacy model union. The caller retains ownership of the supplied weights. */
168
+ export function createModel<Model>(
169
+ weights: Weights, config: ModelConfig, resolved: ResolvedModelProfile | undefined,
170
+ implementations: ModelImplementationProvider<Weights, Model>,
171
+ ): Model;
115
172
  export function createModel(
173
+ weights: Weights, config: ModelConfig, resolved?: ResolvedModelProfile,
174
+ implementations?: ModelImplementationProvider<Weights, RuntimeModel>,
175
+ ): RuntimeModel;
176
+ export function createModel<Model>(
116
177
  weights: Weights,
117
178
  config: ModelConfig,
118
179
  resolved: ResolvedModelProfile = resolveModelProfile(config),
119
- ): RuntimeModel {
180
+ implementations: ModelImplementationProvider<Weights, Model> | ModelImplementationProvider<Weights, RuntimeModel> = MLX_MODEL_IMPLEMENTATIONS,
181
+ ): Model | RuntimeModel {
120
182
  assertResolvedModelProfile(config, resolved);
121
- const execution = resolved.profile.execution;
122
- switch (execution.graph) {
123
- case "glm5.2":
124
- throw new Error(
125
- "glm_moe_dsa uses the direct Colibri container; construct it with " +
126
- "openModel(modelDir) or Glm52Model.open(modelDir)",
127
- );
128
- // DiffusionGemma is non-autoregressive. The profile pairs this graph with
129
- // the denoising loop; generate() recognizes the constructed model surface.
130
- case "diffusion-gemma": return new DiffusionGemmaModel(weights, config);
131
- case "minicpm5": return new MiniCPM5Model(weights, config);
132
- case "qwen3.5": return new Qwen35Model(weights, config);
133
- case "qwen3-moe": return new Qwen3MoeModel(weights, config);
134
- case "qwen3": return new Qwen3Model(weights, config);
135
- case "gemma4": {
136
- // Generated specializations remain exact config matches. Output-changing
137
- // request methods stay independently resolved and cannot be fallbacks.
138
- const cls = execution.specialization === "generated"
139
- ? GENERATED.get(configFingerprint(config))!
140
- : Gemma4Model;
141
- return new cls(weights, config);
142
- }
143
- case "universal-dense":
144
- return new UniversalDenseModel(weights, config, genericArgsFor(config)!);
145
- }
183
+ if (resolved.profile.execution.loader === "colibri")
184
+ throw new Error(
185
+ "glm_moe_dsa uses the direct Colibri container; construct it with " +
186
+ "openModel(modelDir) or Glm52Model.open(modelDir)",
187
+ );
188
+ return implementations.select(config, resolved).create(weights, config, resolved);
146
189
  }
@@ -1,4 +1,4 @@
1
- // Config fingerprint for generated-model dispatch (docs/design/optimization_plan.md
1
+ // Config fingerprint for generated-model dispatch (docs/archive/investigations/optimization_plan.md
2
2
  // Phase C): a stable hash over every config field that changes the
3
3
  // decode graph structure, plus the kv_config quant layout. A generated
4
4
  // specialization is used only when the fingerprint of the loaded config
@@ -545,7 +545,7 @@ export function flashBackward(
545
545
  // (dK_j[d] → [kv_row][d]); no post-transpose. (An earlier "fix" transposed
546
546
  // it on the false belief the buffer was swapped — that transpose is only the
547
547
  // identity at Tkv==D and corrupted dK for Tkv≠D; removed after
548
- // scripts/experiments/flash-dkv-debug.ts showed the raw output is correct.)
548
+ // flash-dkv-debug.ts (deleted 2026-08-23; git history) showed the raw output is correct.)
549
549
  return [dq[0]!, dkv[0]!, dkv[1]!];
550
550
  } finally {
551
551
  Dvec.dispose();
@@ -621,8 +621,8 @@ export function flashAttention(
621
621
  // - This flash kernel is the L2 path: a port of mlx-optiq's
622
622
  // flash_attention_metal (O(L) backward memory). Its parity oracle is OPTIQ,
623
623
  // not finite differences. Confirmed: optiq's flash dK == ops.sdpa to f16
624
- // (scripts/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
625
- // (tests/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
624
+ // (scripts/oracle/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
625
+ // (tests/unit/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
626
626
  //
627
627
  // Two real port bugs were fixed to reach that parity (the original diverged
628
628
  // ~100% from optiq on dK): a spurious dK transpose in flashBackward (corrupted
@@ -0,0 +1,56 @@
1
+ import type { MlxArray } from "../mlx/array";
2
+ import * as ops from "../mlx/ops";
3
+ import type { PrefillPadding } from "./gemma4-base";
4
+ import { rowRollIndices } from "./batched-row-storage";
5
+ import { disposeResources } from "../engine/resources";
6
+
7
+ interface Positions { rowOffsets: number[]; leftPad: number[] }
8
+
9
+ /** mlx-lm BatchKVCache.prepare/finalize (MIT), over arbitrary stored planes.
10
+ * Padding changes token positions; the representation's codec is uninvolved. */
11
+ export class FullPrefillPadding {
12
+ constructor(readonly tokenAxis = 2) {}
13
+ #ends?: number[];
14
+ get endOffsets(): readonly number[] | undefined { return this.#ends; }
15
+ restoreEnds(ends: readonly number[] | undefined): void { this.#ends = ends ? [...ends] : undefined; }
16
+ validOffset(offset: number, row: number): number { return Math.min(offset, this.#ends?.[row] ?? offset); }
17
+
18
+ prepare(positions: Positions, padding: PrefillPadding): void {
19
+ if (!positions.rowOffsets.length) {
20
+ positions.rowOffsets = padding.lengths.map(() => 0);
21
+ positions.leftPad = [...positions.rowOffsets];
22
+ }
23
+ if (padding.leftPadding) {
24
+ positions.leftPad = positions.leftPad.map((pad, row) => pad + padding.leftPadding![row]!);
25
+ positions.rowOffsets = positions.rowOffsets.map((offset, row) => offset - padding.leftPadding![row]!);
26
+ }
27
+ // Like rotating storage, retain absolute valid ends. A cohort can finish
28
+ // before its planned width when a longer request leaves during prefill.
29
+ this.#ends = padding.rightPadding?.some(pad => pad > 0)
30
+ ? padding.lengths.map((length, row) => positions.rowOffsets[row]! + length) : undefined;
31
+ }
32
+
33
+ /** Borrow planes; return owned replacements only when padding needs moving.
34
+ * Stage every plane before committing positions, including on FFI failure. */
35
+ finalize(planes: readonly MlxArray[], positions: Positions): MlxArray[] | undefined {
36
+ if (!this.#ends) return;
37
+ const padding = this.#ends.map((end, row) => Math.max(0, positions.rowOffsets[row]! - end));
38
+ if (!padding.some(pad => pad > 0)) { this.#ends = undefined; return; }
39
+ const next: MlxArray[] = [];
40
+ try {
41
+ if (planes.length) {
42
+ const width = planes[0]!.shape[this.tokenAxis]!;
43
+ using indices = rowRollIndices(width, padding);
44
+ using compressed = this.tokenAxis === 1 ? ops.reshape(indices, [padding.length, width, 1]) : null;
45
+ for (const plane of planes) next.push(ops.takeAlongAxis(plane, compressed ?? indices, this.tokenAxis));
46
+ }
47
+ } catch (error) { disposeResources(next); throw error; }
48
+ positions.rowOffsets = positions.rowOffsets.map((offset, row) => offset - padding[row]!);
49
+ positions.leftPad = positions.leftPad.map((pad, row) => pad + padding[row]!);
50
+ this.#ends = undefined;
51
+ return next;
52
+ }
53
+
54
+ filter(keep: readonly number[]): void { if (this.#ends) this.#ends = keep.map(row => this.#ends![row]!); }
55
+ clear(): void { this.#ends = undefined; }
56
+ }