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,105 @@
1
+ import type { MlxArray } from "../mlx/array";
2
+ import { materializeCopy } from "../mlx/materialize";
3
+ import { withResource, disposeResources } from "../engine/resources";
4
+ import { leaseCacheState } from "../backends/mlx/state-views";
5
+ import { FullPrefillPadding } from "./full-prefill-padding";
6
+ import { KVCache, QuantizedKVCache, TurboQuantKVCache, type Cache, type Mask, type PrefillPadding } from "./gemma4-base";
7
+
8
+ type FullCache = KVCache | QuantizedKVCache | TurboQuantKVCache;
9
+
10
+ /** A physical full-attention row with separate valid-token coverage. Codecs
11
+ * consume the same physical tensors; padding never advances their threshold. */
12
+ export class FullPrefillRow implements Cache {
13
+ readonly padding = new FullPrefillPadding();
14
+ leftPadding = 0;
15
+ constructor(public inner: FullCache) {}
16
+ get minimumReusableOffset(): number { return (this.inner as Cache).minimumReusableOffset ?? 0; }
17
+ set minimumReusableOffset(value: number) { (this.inner as Cache).minimumReusableOffset = value; }
18
+ get positionOffset(): number { return this.inner.offset - this.leftPadding; }
19
+ get physicalLength(): number { return this.inner.offset; }
20
+ get offset(): number { return Math.max(0, this.padding.validOffset(this.positionOffset, 0)); }
21
+ get stateNeedsDispose(): boolean { return (this.inner as Cache).stateNeedsDispose ?? false; }
22
+ get affineConversion(): FullPrefillRow | undefined { return this.inner instanceof KVCache ? this : undefined; }
23
+ get turboConversion(): FullPrefillRow | undefined { return this.inner instanceof KVCache ? this : undefined; }
24
+ get quantizedAttention() { return (this.inner as Cache).quantizedAttention; }
25
+ get rotatedValueAttention() { return (this.inner as Cache).rotatedValueAttention; }
26
+ signature(): string { return this.inner.signature(); }
27
+ state(): MlxArray[] { return this.inner.state(); }
28
+ makeMask(tokens: number, window: number | null): Mask { return this.inner.makeMask(tokens, window); }
29
+ isTrimmable(): boolean { return true; }
30
+ trim(count: number): void { this.inner.trim(count); }
31
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.inner.updateAndFetch(k, v); }
32
+ bytesPerToken(): number { return (this.inner as Cache).bytesPerToken?.() ?? 0; }
33
+ preparePrefill(padding: PrefillPadding): void {
34
+ const positions = { rowOffsets: [this.positionOffset], leftPad: [this.leftPadding] };
35
+ this.padding.prepare(positions, padding);
36
+ this.leftPadding = positions.leftPad[0]!;
37
+ }
38
+ finalizePrefill(): void {
39
+ const positions = { rowOffsets: [this.positionOffset], leftPad: [this.leftPadding] };
40
+ const next = withResource(leaseCacheState(this.inner), state => this.padding.finalize(state, positions));
41
+ if (next) this.replace(next, this.inner.offset);
42
+ this.leftPadding = positions.leftPad[0]!;
43
+ }
44
+ removeLeftPadding(count: number): void {
45
+ if (!count) return;
46
+ const next: MlxArray[] = [];
47
+ try {
48
+ withResource(leaseCacheState(this.inner), state => {
49
+ for (const plane of state) next.push(plane.slice([0, 0, count, 0], [...plane.shape]));
50
+ });
51
+ this.replace(next, this.inner.offset - count); next.length = 0;
52
+ this.leftPadding -= count;
53
+ } finally { disposeResources(next); }
54
+ }
55
+ private restored(planes: MlxArray[], offset: number): FullCache {
56
+ const source = this.inner;
57
+ if (source instanceof KVCache) {
58
+ const result = new KVCache();
59
+ if (planes.length) result.restoreState(planes[0]!, planes[1]!, offset);
60
+ return result;
61
+ }
62
+ if (source instanceof QuantizedKVCache) {
63
+ const result = new QuantizedKVCache(source.groupSize, source.bits);
64
+ if (planes.length) result.restoreState({ packed: planes[0]!, scales: planes[1]!, biases: planes[2]! },
65
+ { packed: planes[3]!, scales: planes[4]!, biases: planes[5]! }, offset);
66
+ return result;
67
+ }
68
+ const result = new TurboQuantKVCache(source.kBits, source.vBits);
69
+ if (planes.length) result.restoreState({ kIdx: planes[0]!, kScales: planes[1]!, kZeros: planes[2]!,
70
+ vPacked: planes[3]!, vScales: planes[4]! }, offset, source.headDim!);
71
+ return result;
72
+ }
73
+ private replace(planes: MlxArray[], offset: number): void {
74
+ const next = this.restored(planes, offset), previous = this.inner;
75
+ (next as Cache).minimumReusableOffset = this.minimumReusableOffset;
76
+ this.inner = next; previous.dispose();
77
+ }
78
+ extract(): Cache {
79
+ const count = this.offset, planes: MlxArray[] = [];
80
+ try {
81
+ if (count) withResource(leaseCacheState(this.inner), state => {
82
+ for (const plane of state) {
83
+ using view = plane.slice([0, 0, this.leftPadding, 0], [1, plane.shape[1]!, this.leftPadding + count, plane.shape[3]!]);
84
+ planes.push(materializeCopy(view));
85
+ }
86
+ });
87
+ const result = this.restored(planes, count); planes.length = 0;
88
+ (result as Cache).minimumReusableOffset = this.minimumReusableOffset;
89
+ return result;
90
+ } finally { disposeResources(planes); }
91
+ }
92
+ toQuantized(groupSize: number, bits: number): FullPrefillRow {
93
+ this.inner = (this.inner as KVCache).toQuantized(groupSize, bits);
94
+ return this;
95
+ }
96
+ toTurboQuantized(kBits: number, vBits: number): FullPrefillRow {
97
+ this.inner = TurboQuantKVCache.fromKVCache(this.inner as KVCache, kBits, vBits);
98
+ return this;
99
+ }
100
+ dispose(): void { this.inner.dispose(); this.padding.clear(); }
101
+ }
102
+
103
+ export function fullRowInner(row: Cache): Cache { return row instanceof FullPrefillRow ? row.inner : row; }
104
+ export function fullRowPadding(row: Cache): number { return row instanceof FullPrefillRow ? row.leftPadding : 0; }
105
+ export function fullRowPhysicalLength(row: Cache): number { return row instanceof FullPrefillRow ? row.physicalLength : row.offset; }
@@ -1,5 +1,5 @@
1
1
  // gemma4-base — the config-INDEPENDENT machinery shared by every
2
- // architecture variant (docs/design/optimization_plan.md Phase B: pure code
2
+ // architecture variant (docs/archive/investigations/optimization_plan.md Phase B: pure code
3
3
  // movement out of the gemma4.ts monolith; every definition here is
4
4
  // verbatim from it). Cache classes, quantized-KV SDPA, masks, the
5
5
  // quantized primitives + LoRA machinery, and small graph helpers live
@@ -7,14 +7,19 @@
7
7
  // DecoderLayer, Gemma4Model) stays in gemma4.ts and becomes the
8
8
  // per-model generated code in Phase C.
9
9
 
10
+ import { decodedKvDonorAttention } from "./decoded-kv-donor";
10
11
  import type { ModelConfig } from "../config";
11
12
  import { quantFor } from "../config";
12
13
  import type { Weights } from "../weights";
13
14
  import { MlxArray } from "../mlx/array";
14
15
  import { Dtype } from "../mlx/ffi";
15
16
  import * as ops from "../mlx/ops";
17
+ import { quantizedMatmulRows } from "../mlx/quantized-rows";
16
18
  import { expertOffloadArray } from "../expert-offload";
17
19
  import * as tq from "../mlx/turboquant-ops";
20
+ import type { KvCodec } from "../backends/mlx/kv-codec";
21
+ import { TurboQuantCodec, disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
22
+ export { disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
18
23
  import { runtimeValue } from "../runtime-config";
19
24
 
20
25
  export type MaskMode = "" | "causal";
@@ -155,8 +160,10 @@ export class QuantizedLinear {
155
160
  return this._scales.shape[0]!;
156
161
  }
157
162
 
158
- forward(x: MlxArray): MlxArray {
159
- let out = ops.quantizedMatmul(
163
+ forward(x: MlxArray, independentRows = false): MlxArray {
164
+ let out = independentRows ? quantizedMatmulRows(
165
+ x, this._w, this._scales, this._biases, this._spec,
166
+ ) : ops.quantizedMatmul(
160
167
  x,
161
168
  this._w,
162
169
  this._scales,
@@ -246,9 +253,9 @@ export class QuantizedEmbedding {
246
253
 
247
254
  /** Fetched KV handed from a donor layer to its sharers (and to the
248
255
  * speculative drafter). Owned by forwardLayers for the pass.
249
- * `offsetArr` is set only under compiled decode (trace adapters): the
250
- * RoPE offset as an array VALUE so the compiled graph replays at any
251
- * position (see src/model/compiled-decode.ts). */
256
+ * `offsetArr` is an owned pre-write position snapshot. Donor and sharing
257
+ * attention layers keep it until the fetched KV is released, even if the
258
+ * cache advances or replaces its borrowed row-position array. */
252
259
  export type SharedKv =
253
260
  | { kind: "plain"; keys: MlxArray; values: MlxArray; offset: number;
254
261
  offsetArr?: MlxArray;
@@ -257,14 +264,95 @@ export type SharedKv =
257
264
  * (tq.unrotateValues) after sdpa. Attention is linear in V, so the
258
265
  * result is the same decode, just transformed once per query row
259
266
  * instead of once per cached token. */
260
- vRotated?: boolean }
267
+ vRotated?: boolean;
268
+ restoreValues?: (output: MlxArray) => MlxArray }
261
269
  | { kind: "quant"; keys: ops.QuantizedTensor; values: ops.QuantizedTensor;
262
- offset: number; groupSize: number; bits: number; offsetArr?: MlxArray };
270
+ offset: number; groupSize: number; bits: number; offsetArr?: MlxArray }
271
+ | { kind: "view"; attention: KvAttentionView; offset: number; offsetArr?: MlxArray };
272
+
273
+ /** Retain an independent array handle for positions used across cache writes.
274
+ * The contiguous view preserves the integer values and shares their storage;
275
+ * callers release it with the fetched attention state. */
276
+ export function captureRopeOffsets(cache: Pick<Cache, "ropeOffsetArr">): MlxArray | undefined {
277
+ const offsets = cache.ropeOffsetArr;
278
+ return offsets ? ops.contiguous(offsets) : undefined;
279
+ }
280
+
281
+ /** Quantized attention consumes a numerical storage port, independent of the
282
+ * concrete layout used for row positions, retention and persistence. */
283
+ export interface QuantizedAttentionState {
284
+ readonly groupSize: number;
285
+ readonly bits: number;
286
+ updateAndFetchQuantized(k: MlxArray, v: MlxArray): [ops.QuantizedTensor, ops.QuantizedTensor];
287
+ }
288
+
289
+ /** Values may stay in the codec's rotated domain until after attention. */
290
+ export interface RotatedValueAttentionState {
291
+ /** Capture a fetched value domain independently of later row changes. */
292
+ captureValueTransform?(): (output: MlxArray) => MlxArray;
293
+ updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
294
+ }
295
+
296
+ /** A captured numerical view supports multiple queries without appending KV
297
+ * again. It owns its tensor handles independently of later cache membership or
298
+ * precision changes. Query/mask inputs are borrowed; outputs are owned. */
299
+ export interface KvAttentionView {
300
+ attend(q: MlxArray, scale: number, mask: Mask): MlxArray;
301
+ dispose(): void;
302
+ }
303
+
304
+ /** Storage appends once and hands every attention consumer the same view. */
305
+ export interface KvAttentionState {
306
+ appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView;
307
+ }
308
+
309
+ /** Owned read-only planes with validity separate from physical padding. */
310
+ export interface KvDonorRows {
311
+ readonly keys: MlxArray;
312
+ readonly values: MlxArray;
313
+ readonly offsets: readonly number[];
314
+ readonly starts: readonly number[];
315
+ readonly ends: readonly number[];
316
+ }
317
+
318
+ /** Captured Q-only attention hides plain or encoded donor storage. */
319
+ export interface KvDonorAttention extends KvAttentionView {
320
+ readonly width: number;
321
+ readonly dtype: Dtype;
322
+ readonly offsets: readonly number[];
323
+ readonly starts: readonly number[];
324
+ readonly ends: readonly number[];
325
+ }
263
326
 
264
327
  export interface Cache {
328
+ captureDonorRows?(): KvDonorRows;
329
+ captureDonorAttention?(): KvDonorAttention;
330
+ /** A preparation method commits each request's own precision boundary.
331
+ * Splitting a forward for a sibling must not move that boundary. */
332
+ readonly prefillMaintenance?: {
333
+ beginPrefill(): void;
334
+ commitPrefill(rows: readonly number[]): void;
335
+ endPrefill(): void;
336
+ };
337
+ /** Optional representation-owned affine conversion. Logical position can
338
+ * differ from the physical write head in a padded rotating layout. */
339
+ readonly affineConversion?: { readonly offset: number; toQuantized(groupSize: number, bits: number): Cache };
340
+ readonly turboConversion?: { readonly offset: number; toTurboQuantized(kBits: number, vBits: number): Cache };
341
+ readonly attentionState?: KvAttentionState;
342
+ /** Earliest prefix whose retained representation can resume generation.
343
+ * Irreversible transitions may preserve bytes while invalidating older
344
+ * precision boundaries. Reuse policy must not trim below this offset. */
345
+ minimumReusableOffset?: number;
346
+ readonly rotatedValueAttention?: RotatedValueAttentionState;
347
+ readonly quantizedAttention?: QuantizedAttentionState;
265
348
  offset: number;
266
- /** Stable storage identity for compatibility guards and persistence. */
267
- signature?(): string;
349
+ /** Stable storage identity for compatibility guards and persistence.
350
+ * REQUIRED: every capability predicate (isPlainKvCache, isRotating*,
351
+ * kv-store codecs) is a string compare on this value, so a missing
352
+ * override used to fail OPEN into wrong routing (PRs #42/#43:
353
+ * BatchedRotatingCache shipped without one and batched joins dropped
354
+ * running rows). Wrappers report their own kind plus the inner kind. */
355
+ signature(): string;
268
356
  /** Physical cache storage consumed by one additional token for one row.
269
357
  * Recurrent caches return 0 because their state does not grow with the
270
358
  * sequence. Optional for stateless/training adapters that are never
@@ -303,7 +391,7 @@ export interface Cache {
303
391
  }
304
392
 
305
393
  export function cacheSignature(cache: Cache | undefined): string {
306
- return cache?.signature?.() ?? "unknown";
394
+ return cache ? cache.signature() : "unknown";
307
395
  }
308
396
 
309
397
  export interface RowBatchCache extends Cache {
@@ -356,6 +444,19 @@ export interface BatchableCache extends Cache {
356
444
  projectedBytes(tokens: number): number;
357
445
  }
358
446
 
447
+ /** Padding is interpreted by cache geometry, independently of scheduling.
448
+ * Left padding initializes an empty group; lengths count input columns before
449
+ * trailing padding. Finalize before ordinary decode or publishing checkpoints. */
450
+ export interface PrefillPadding {
451
+ readonly leftPadding?: readonly number[];
452
+ readonly lengths: readonly number[];
453
+ readonly rightPadding?: readonly number[];
454
+ }
455
+ export interface PaddedPrefillCache extends Cache {
456
+ preparePrefill(padding: PrefillPadding): void;
457
+ finalizePrefill(): void;
458
+ }
459
+
359
460
  export function isBatchableCache(cache: Cache): cache is BatchableCache {
360
461
  const candidate = cache as Partial<BatchableCache>;
361
462
  return (
@@ -479,6 +580,13 @@ export class KVCache implements Cache {
479
580
  }
480
581
 
481
582
  /** Chronological (K, V) view sliced to offset (drafter donor read). */
583
+ captureDonorRows(): KvDonorRows {
584
+ const [keys, values] = this.temporalView();
585
+ const B = keys.shape[0]!, width = keys.shape[2]!;
586
+ return { keys, values, offsets: Array(B).fill(this.offset),
587
+ starts: Array(B).fill(0), ends: Array(B).fill(width) };
588
+ }
589
+
482
590
  temporalView(): [MlxArray, MlxArray] {
483
591
  if (!this.keys || !this.values) throw new Error("cache is empty");
484
592
  const [B, H, , D] = this.keys.shape as [number, number, number, number];
@@ -570,6 +678,7 @@ export class KVCache implements Cache {
570
678
  * quantization is NYI upstream; sliding layers are window-capped
571
679
  * anyway). Attention dispatches to quantizedSdpa for these. */
572
680
  export class QuantizedKVCache implements Cache {
681
+ minimumReusableOffset?: number;
573
682
  static readonly STEP = 256;
574
683
  /** Set only by compiled-decode trace adapters (see Cache). */
575
684
  readonly ropeOffsetArr?: MlxArray;
@@ -579,6 +688,8 @@ export class QuantizedKVCache implements Cache {
579
688
 
580
689
  constructor(readonly groupSize: number, readonly bits: number) {}
581
690
 
691
+ get quantizedAttention(): QuantizedAttentionState { return this; }
692
+
582
693
  signature(): string { return `kv:quant:${this.bits}:${this.groupSize}`; }
583
694
 
584
695
  bytesPerToken(): number {
@@ -766,6 +877,16 @@ export class QuantizedKVCache implements Cache {
766
877
  /** Chronological (K, V) triples sliced to offset — the quantized twin of
767
878
  * KVCache.temporalView (batched merge/extend/filter reads). Caller owns
768
879
  * the returned views. */
880
+ captureDonorAttention(): KvDonorAttention {
881
+ const [keys, values] = this.temporalView();
882
+ const B = keys.packed.shape[0]!, width = keys.packed.shape[2]!;
883
+ return { width, dtype: keys.scales.dtype, offsets: Array(B).fill(this.offset),
884
+ starts: Array(B).fill(0), ends: Array(B).fill(width),
885
+ attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, this.groupSize, this.bits),
886
+ dispose() { disposeTriple(keys); disposeTriple(values); },
887
+ };
888
+ }
889
+
769
890
  temporalView(): [ops.QuantizedTensor, ops.QuantizedTensor] {
770
891
  if (!this.keys || !this.values) throw new Error("cache is empty");
771
892
  const cut = (t: ops.QuantizedTensor): ops.QuantizedTensor => {
@@ -790,6 +911,7 @@ export class QuantizedKVCache implements Cache {
790
911
  * entries, so decode attends over at most the window. RoPE offsets use
791
912
  * the true position; masks use the buffer-clamped offset. */
792
913
  export class RotatingKVCache implements Cache {
914
+ declare minimumReusableOffset?: number;
793
915
  static readonly STEP = 256;
794
916
  /** Set only by compiled-decode trace adapters (see Cache). */
795
917
  readonly ropeOffsetArr?: MlxArray;
@@ -1057,6 +1179,13 @@ export class RotatingKVCache implements Cache {
1057
1179
 
1058
1180
  /** Chronological (K, V) view, valid length min(offset, maxSize)
1059
1181
  * (port of optiq kv_view._read_cache_temporal). */
1182
+ captureDonorRows(): KvDonorRows {
1183
+ const [keys, values] = this.temporalView();
1184
+ const B = keys.shape[0]!, width = keys.shape[2]!;
1185
+ return { keys, values, offsets: Array(B).fill(this.offset),
1186
+ starts: Array(B).fill(0), ends: Array(B).fill(width) };
1187
+ }
1188
+
1060
1189
  temporalView(): [MlxArray, MlxArray] {
1061
1190
  if (!this.keys || !this.values) throw new Error("cache is empty");
1062
1191
  const tk = this.#temporalOrder(this.keys);
@@ -1125,6 +1254,8 @@ export const disposeTriple = (t: ops.QuantizedTensor): void => {
1125
1254
  * + SDPA patches are unnecessary here: our SharedKv carries
1126
1255
  * groupSize/bits through the donor→sharer plumbing explicitly. */
1127
1256
  export class RotatingQuantizedKVCache implements Cache {
1257
+ get quantizedAttention(): QuantizedAttentionState { return this; }
1258
+ declare minimumReusableOffset?: number;
1128
1259
  static readonly STEP = 256;
1129
1260
  /** Set only by compiled-decode trace adapters (see Cache). */
1130
1261
  readonly ropeOffsetArr?: MlxArray;
@@ -1382,6 +1513,16 @@ export class RotatingQuantizedKVCache implements Cache {
1382
1513
  /** Chronological (K, V) triples cut to the valid window — the quantized
1383
1514
  * twin of RotatingKVCache.temporalView (batched merge reads, Phase 3
1384
1515
  * milestone 2). Caller owns the returned views. */
1516
+ captureDonorAttention(): KvDonorAttention {
1517
+ const [keys, values] = this.temporalView();
1518
+ const B = keys.packed.shape[0]!, width = keys.packed.shape[2]!;
1519
+ return { width, dtype: keys.scales.dtype, offsets: Array(B).fill(this.offset),
1520
+ starts: Array(B).fill(0), ends: Array(B).fill(width),
1521
+ attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, this.groupSize, this.bits),
1522
+ dispose() { disposeTriple(keys); disposeTriple(values); },
1523
+ };
1524
+ }
1525
+
1385
1526
  temporalView(): [ops.QuantizedTensor, ops.QuantizedTensor] {
1386
1527
  if (!this.keys || !this.values) throw new Error("cache is empty");
1387
1528
  const valid = Math.min(this.offset, this.maxSize);
@@ -1496,29 +1637,7 @@ export class RotatingQuantizedKVCache implements Cache {
1496
1637
  }
1497
1638
  }
1498
1639
 
1499
- /** One TurboQuant-encoded (K, V) storage tuple — the 5 arrays kv-store.ts
1500
- * and toQuantized/fromKVCache pass around. Not `ops.QuantizedTensor`
1501
- * (mlx's affine int4/int8 scheme): this is the rotation + Lloyd-Max
1502
- * layout (docs/design/turboquant-kv.md), asymmetric-affine for keys,
1503
- * FWHT+Lloyd-Max for values. Field order is the kv-store.ts tensor-slot
1504
- * contract — do not reorder without updating snapshotCache/loadKvCache. */
1505
- export interface TurboQuantTensor {
1506
- kIdx: MlxArray;
1507
- kScales: MlxArray;
1508
- kZeros: MlxArray;
1509
- vPacked: MlxArray;
1510
- vScales: MlxArray;
1511
- }
1512
-
1513
- export const disposeTurboQuant = (t: TurboQuantTensor): void => {
1514
- t.kIdx.dispose();
1515
- t.kScales.dispose();
1516
- t.kZeros.dispose();
1517
- t.vPacked.dispose();
1518
- t.vScales.dispose();
1519
- };
1520
-
1521
- /** TurboQuant KV cache — v1 (docs/design/turboquant-kv.md): dequantize-
1640
+ /** TurboQuant KV cache — v1 (docs/design/turboquant.md): dequantize-
1522
1641
  * on-fetch. Deliberately does NOT subclass KVCache/RotatingKVCache (or
1523
1642
  * QuantizedKVCache) — a novel class fails every generated-file
1524
1643
  * `#matches()` guard and every batching `instanceof` allow-list, so it
@@ -1537,6 +1656,7 @@ export const disposeTurboQuant = (t: TurboQuantTensor): void => {
1537
1656
  * a full-window dequant every step; the deferred-InvFWHT trick is a
1538
1657
  * documented non-goal until the quality gate passes. */
1539
1658
  export class TurboQuantKVCache implements Cache {
1659
+ minimumReusableOffset = 0;
1540
1660
  readonly stateNeedsDispose = true;
1541
1661
  static readonly STEP = 256;
1542
1662
  /** Set only by compiled-decode trace adapters (see Cache) — TurboQuant
@@ -1548,9 +1668,23 @@ export class TurboQuantKVCache implements Cache {
1548
1668
  offset = 0;
1549
1669
  /** Validated lazily on first update, once head_dim is known. */
1550
1670
  #headDim: number | null = null;
1671
+ /** Experimental operation selection, captured once for this cache. */
1672
+ readonly #codec: KvCodec<TurboQuantTensor>;
1551
1673
 
1552
- constructor(readonly kBits: number, readonly vBits: number) {}
1674
+ constructor(readonly kBits: number, readonly vBits: number) {
1675
+ this.#codec = new TurboQuantCodec(kBits, vBits, process.env.MLX_BUN_TURBOQUANT_FUSED_DECODE === "1");
1676
+ }
1677
+
1678
+ captureDonorRows(): KvDonorRows {
1679
+ if (!this.#kv || this.#headDim === null) throw new Error("cache is empty");
1680
+ const [keys, values] = this.#decode(this.#kv, this.offset, this.#headDim, false);
1681
+ const B = keys.shape[0]!;
1682
+ return { keys, values, offsets: Array(B).fill(this.offset),
1683
+ starts: Array(B).fill(0), ends: Array(B).fill(this.offset) };
1684
+ }
1685
+ captureDonorAttention(): KvDonorAttention { return decodedKvDonorAttention(this.captureDonorRows()); }
1553
1686
 
1687
+ get rotatedValueAttention(): RotatedValueAttentionState { return this; }
1554
1688
  signature(): string { return `kv:turboquant:${this.kBits}:${this.vBits}`; }
1555
1689
 
1556
1690
  bytesPerToken(): number {
@@ -1602,48 +1736,9 @@ export class TurboQuantKVCache implements Cache {
1602
1736
  return this.#kv ? this.#kv.kIdx.shape[2]! : 0;
1603
1737
  }
1604
1738
 
1605
- /** Encode k/v [B,H,L,D] into a fresh TurboQuantTensor (packed at rest). */
1606
- #encode(k: MlxArray, v: MlxArray): TurboQuantTensor {
1607
- const signed = this.kBits === 8;
1608
- const kEnc = tq.encodeKeys(k, this.kBits, signed);
1609
- const kIdxPacked = this.kBits < 8 ? tq.packBits(kEnc.indices, this.kBits) : kEnc.indices;
1610
- if (kIdxPacked !== kEnc.indices) kEnc.indices.dispose();
1611
- const vEnc = tq.encodeValues(v, this.vBits);
1612
- const vIdxPacked = this.vBits < 8 ? tq.packBits(vEnc.indices, this.vBits) : vEnc.indices;
1613
- if (vIdxPacked !== vEnc.indices) vEnc.indices.dispose();
1614
- return {
1615
- kIdx: kIdxPacked, kScales: kEnc.scales, kZeros: kEnc.zeros,
1616
- vPacked: vIdxPacked, vScales: vEnc.scales,
1617
- };
1618
- }
1619
-
1620
- /** Decode a TurboQuantTensor's active [B,H,upTo,*] window back to
1621
- * bf16 [B,H,upTo,headDim] (k, v). Caller owns the returned arrays.
1622
- * deferV leaves V in the rotated domain (decodeValuesRotated) — the
1623
- * caller un-rotates its attention output instead (tq.unrotateValues). */
1739
+ #encode(k: MlxArray, v: MlxArray): TurboQuantTensor { return this.#codec.encode(k, v); }
1624
1740
  #decode(t: TurboQuantTensor, upTo: number, headDim: number, deferV = false): [MlxArray, MlxArray] {
1625
- const cut = (a: MlxArray): MlxArray => {
1626
- const [B, H, , D] = a.shape as [number, number, number, number];
1627
- return a.slice([0, 0, 0, 0], [B, H, upTo, D]);
1628
- };
1629
- const kIdxCut = cut(t.kIdx);
1630
- const kIdxUnpacked = this.kBits < 8 ? tq.unpackBits(kIdxCut, this.kBits, headDim) : kIdxCut;
1631
- if (kIdxUnpacked !== kIdxCut) kIdxCut.dispose();
1632
- const kScalesCut = cut(t.kScales);
1633
- const kZerosCut = cut(t.kZeros);
1634
- const k = tq.decodeKeys(kIdxUnpacked, kScalesCut, kZerosCut);
1635
- for (const a of [kIdxUnpacked, kScalesCut, kZerosCut]) a.dispose();
1636
-
1637
- const vPackedCut = cut(t.vPacked);
1638
- const vIdxUnpacked = this.vBits < 8 ? tq.unpackBits(vPackedCut, this.vBits, headDim) : vPackedCut;
1639
- if (vIdxUnpacked !== vPackedCut) vPackedCut.dispose();
1640
- const vScalesCut = cut(t.vScales);
1641
- const v = deferV
1642
- ? tq.decodeValuesRotated(vIdxUnpacked, vScalesCut, this.vBits)
1643
- : tq.decodeValues(vIdxUnpacked, vScalesCut, this.vBits);
1644
- for (const a of [vIdxUnpacked, vScalesCut]) a.dispose();
1645
-
1646
- return [k, v];
1741
+ return this.#codec.decode(t, upTo, headDim, deferV);
1647
1742
  }
1648
1743
 
1649
1744
  /** Quantize the newly-appended k/v, append into growable packed
@@ -1788,6 +1883,7 @@ export class TurboQuantKVCache implements Cache {
1788
1883
  q.#headDim = D;
1789
1884
  }
1790
1885
  q.offset = cache.offset;
1886
+ q.minimumReusableOffset = (cache as Cache).minimumReusableOffset ?? 0;
1791
1887
  cache.dispose();
1792
1888
  return q;
1793
1889
  }
@@ -1796,6 +1892,7 @@ export class TurboQuantKVCache implements Cache {
1796
1892
  if (this.#kv) disposeTurboQuant(this.#kv);
1797
1893
  this.#kv = null;
1798
1894
  this.offset = 0;
1895
+ this.minimumReusableOffset = 0;
1799
1896
  }
1800
1897
  }
1801
1898
 
@@ -1868,9 +1965,11 @@ export function quantizedSdpaUnfused(
1868
1965
  }
1869
1966
  }
1870
1967
  if (maskArr) {
1871
- const ninf = ops.scalarLike(FINFO_MIN[scores.dtype] ?? -3.4e38, scores);
1872
- const masked = ops.where(maskArr, scores, ninf);
1873
- ninf.dispose();
1968
+ let masked: MlxArray;
1969
+ if (maskArr.dtype === Dtype.bool) {
1970
+ using ninf = ops.scalarLike(FINFO_MIN[scores.dtype] ?? -3.4e38, scores);
1971
+ masked = ops.where(maskArr, scores, ninf);
1972
+ } else masked = ops.add(scores, maskArr);
1874
1973
  if (ownsMask) maskArr.dispose();
1875
1974
  owned.push(masked);
1876
1975
  scores = masked;
@@ -2080,7 +2179,7 @@ export function quantizedSdpaTiled(
2080
2179
  function fusedSdpaSupported(q: MlxArray, mask: Mask, groupSize: number, bits: number): boolean {
2081
2180
  // Escape hatch mirroring optiq serve's --no-fused-kv: forces the
2082
2181
  // stock unfused path everywhere. Also the A/B lever for
2083
- // scripts/bench-fused-prefill.ts. Read per call (cheap next to the
2182
+ // scripts/bench-levers.ts fused-prefill. Read per call (cheap next to the
2084
2183
  // FFI work) so tests and paired A/B harnesses can flip it in-process.
2085
2184
  if (runtimeValue("MLX_BUN_NO_FUSED_SDPA") === "1") return false;
2086
2185
  if (bits !== 4 && bits !== 8) return false;