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,35 @@
1
+ import type { KvDonorAttention } from "./gemma4-base";
2
+ import { BatchedRotatingState } from "./batched-rotating-state";
3
+ import { BatchedRotatingQuantCache } from "./batched-rotating-quant";
4
+ import { plainRowStorage, quantizedRowStorage, temporalStorageView } from "./batched-row-storage";
5
+ import { quantizedDonorAttention } from "./kv-attention-view";
6
+ import { decodedKvDonorAttention } from "./decoded-kv-donor";
7
+ import type { RotatingRing } from "./rotating-row-transaction";
8
+
9
+ /** Capture the same chronological donor rectangle for plain and encoded rings. */
10
+ export function captureRotatingDonorAttention(ring: RotatingRing): KvDonorAttention {
11
+ const position = new BatchedRotatingState(ring.maxSize, []);
12
+ position.restore(ring.positionSnapshot);
13
+ const offsets = position.offsets.map((_, row) => position.validOffset(row));
14
+ const ends = offsets.map((offset, row) => position.activeLength - (position.offsets[row]! - offset));
15
+ // Pending prefill keeps a shared physical rectangle, including when mixed
16
+ // rows are captured separately. Cropping from each row's valid end would
17
+ // give siblings different widths and can discard an earlier valid window.
18
+ const from = position.hasPendingPadding ? 0 : Math.max(0, position.activeLength - ring.maxSize);
19
+ const range = { from, to: position.activeLength };
20
+ const validity = { offsets,
21
+ starts: position.leftPad.map((pad, row) => Math.max(0, pad - from, ends[row]! - from - ring.maxSize)),
22
+ ends: ends.map(end => Math.max(0, end - from)) };
23
+ if (ring instanceof BatchedRotatingQuantCache) {
24
+ const keys = temporalStorageView(quantizedRowStorage, ring.keys!, position, range);
25
+ let values: import("../mlx/ops").QuantizedTensor;
26
+ try { values = temporalStorageView(quantizedRowStorage, ring.values!, position, range); }
27
+ catch (error) { quantizedRowStorage.dispose(keys); throw error; }
28
+ return quantizedDonorAttention(keys, values, validity, ring.groupSize, ring.bits);
29
+ }
30
+ const keys = temporalStorageView(plainRowStorage, ring.keys!, position, range);
31
+ let values: import("../mlx/array").MlxArray;
32
+ try { values = temporalStorageView(plainRowStorage, ring.values!, position, range); }
33
+ catch (error) { keys.dispose(); throw error; }
34
+ return decodedKvDonorAttention({ keys, values, ...validity });
35
+ }
@@ -0,0 +1,262 @@
1
+ import { MlxArray } from "../mlx/array";
2
+ import * as ops from "../mlx/ops";
3
+ import { cloneKvCaches } from "../kv-store";
4
+ import { type BatchableCache, type Cache, type Mask, type PaddedPrefillCache, type PrefillPadding, RotatingKVCache, RotatingQuantizedKVCache } from "./gemma4-base";
5
+ import { BatchedRotatingCache, buildBatchedRotatingMask } from "./batched-rotating";
6
+ import { BatchedRotatingQuantCache } from "./batched-rotating-quant";
7
+ import { BatchedRotatingState, type RotatingPositionSnapshot } from "./batched-rotating-state";
8
+ import { plainRowStorage, quantizedRowStorage, temporalStorageView } from "./batched-row-storage";
9
+ import type { TransitioningKvPositions, TransitionedKvLayout } from "../backends/mlx/transitioning-kv-rows";
10
+ import { captureRotatingDonorAttention } from "./rotating-kv-donor";
11
+
12
+ import { rollbackRotatingRing } from "./rotating-row-transaction";
13
+
14
+ const fields = ["packed", "scales", "biases"] as const;
15
+ type Ring = BatchedRotatingCache | BatchedRotatingQuantCache;
16
+
17
+ function combinedPosition(rings: readonly Ring[]): RotatingPositionSnapshot {
18
+ const states = rings.map(ring => ring.positionSnapshot);
19
+ return { ...states[0]!, offsets: states.map(state => state.offsets[0]!),
20
+ leftPad: states.map(state => state.leftPad[0]!),
21
+ prefillEnds: states[0]!.prefillEnds
22
+ ? states.map(state => state.prefillEnds![0]!) : undefined };
23
+ }
24
+
25
+ /** One logical request with the group's physical ring geometry. This keeps
26
+ * masks stable while different requests cross their precision boundary. */
27
+ export class AlignedRotatingCache implements Cache {
28
+ minimumReusableOffset = 0;
29
+ constructor(public inner: Ring) {}
30
+ get offset(): number { return this.inner.validOffset(0); }
31
+ get affineConversion(): AlignedRotatingCache | undefined { return this.inner instanceof BatchedRotatingCache ? this : undefined; }
32
+ get quantizedAttention() { return this.inner instanceof BatchedRotatingQuantCache ? this.inner : undefined; }
33
+ signature(): string { return this.inner.signature(); }
34
+ captureDonorAttention() { return captureRotatingDonorAttention(this.inner); }
35
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.inner.updateAndFetch(k, v); }
36
+ makeMask(tokens: number, window: number | null): Mask { return this.inner.makeMask(tokens, window); }
37
+ isTrimmable(): boolean { return this.inner.positionSnapshot.totalOffset < this.inner.maxSize; }
38
+ trim(count: number): void { this.inner.trim(count); }
39
+ rollback(before: number, keep: number, preserveColumns: boolean): void {
40
+ this.inner = rollbackRotatingRing(this.inner, [before], keep, preserveColumns);
41
+ }
42
+ state(): MlxArray[] { return this.inner.state(); }
43
+ bytesPerToken(): number {
44
+ const state = this.state(), tokens = state[0]?.shape[2] ?? 0;
45
+ return tokens ? state.reduce((n, a) => n + a.nbytes, 0) / tokens : 0;
46
+ }
47
+ dispose(): void { this.inner.dispose(); }
48
+ extract(limit?: number): Cache {
49
+ const row = this.inner.extractRow(0, limit) ?? (this.inner instanceof BatchedRotatingQuantCache
50
+ ? new RotatingQuantizedKVCache(this.inner.maxSize, this.inner.groupSize, this.inner.bits)
51
+ : new RotatingKVCache(this.inner.maxSize));
52
+ row.minimumReusableOffset = this.minimumReusableOffset;
53
+ return row;
54
+ }
55
+ toQuantized(groupSize: number, bits: number): AlignedRotatingCache {
56
+ const plain = this.inner as BatchedRotatingCache;
57
+ const keys = ops.quantize(plain.keys!, groupSize, bits);
58
+ let values: ops.QuantizedTensor;
59
+ try { values = ops.quantize(plain.values!, groupSize, bits); }
60
+ catch (error) { quantizedRowStorage.dispose(keys); throw error; }
61
+ const next = new AlignedRotatingCache(BatchedRotatingQuantCache.adoptPhysical(keys, values, groupSize, bits, plain.positionSnapshot));
62
+ next.minimumReusableOffset = this.minimumReusableOffset;
63
+ this.dispose();
64
+ return next;
65
+ }
66
+ }
67
+
68
+ /** Borrow the newest valid window from a solo cache, including a temporary
69
+ * oversized block. No dequantization or physical-ring normalization in place. */
70
+ export function rotatingSourcePosition(row: RotatingKVCache | RotatingQuantizedKVCache): BatchedRotatingState {
71
+ const state = new BatchedRotatingState(row.maxSize, [0], [row.offset]);
72
+ const length = row instanceof RotatingKVCache ? row.keys?.shape[2] ?? 0 : row.keys?.packed.shape[2] ?? 0;
73
+ state.restore({ maxSize: row.maxSize, offsets: [row.offset], leftPad: [0], totalOffset: row.offset,
74
+ ringIndex: row.ringIdx, rotated: row.ringIdx < row.offset && row.ringIdx < length });
75
+ return state;
76
+ }
77
+
78
+ /** Take ownership of solo rows and align them once at membership changes. */
79
+ export function alignRotatingRows(rows: Cache[]): AlignedRotatingCache[] {
80
+ const width = Math.max(0, ...rows.map(row => Math.min(row.offset, (row as RotatingKVCache).maxSize)));
81
+ const prototype = rows.find(row => row.state().length > 0) as RotatingKVCache | RotatingQuantizedKVCache | undefined;
82
+ const emptyPlane = (field: "keys" | "values"): MlxArray | null => {
83
+ if (!width || !prototype) return null;
84
+ const tensor = prototype[field]!;
85
+ if (tensor instanceof MlxArray) return ops.zeros([1, tensor.shape[1]!, width, tensor.shape[3]!], tensor.dtype);
86
+ return ops.zeros([1, tensor.packed.shape[1]!, width, tensor.packed.shape[3]! * 32 / (prototype as RotatingQuantizedKVCache).bits], tensor.scales.dtype);
87
+ };
88
+ const result: AlignedRotatingCache[] = [];
89
+ try {
90
+ for (const source of rows) {
91
+ if (!(source instanceof RotatingKVCache || source instanceof RotatingQuantizedKVCache))
92
+ throw new Error(`rotating layout cannot adopt ${source.signature()}`);
93
+ const length = Math.min(source.offset, source.maxSize), pad = width - length;
94
+ const position: RotatingPositionSnapshot = { maxSize: source.maxSize, offsets: [source.offset], leftPad: [pad],
95
+ totalOffset: width, ringIndex: width, rotated: false };
96
+ const state = rotatingSourcePosition(source);
97
+ const options = { from: Math.max(0, state.activeLength - length), to: state.activeLength };
98
+ let inner: Ring;
99
+ if (source instanceof RotatingKVCache) {
100
+ using k = source.keys ? temporalStorageView(plainRowStorage, source.keys, state, options) : null;
101
+ using v = source.values ? temporalStorageView(plainRowStorage, source.values, state, options) : null;
102
+ const keys = k ? plainRowStorage.padLeft(k, pad) : emptyPlane("keys");
103
+ let values: MlxArray | null;
104
+ try { values = v ? plainRowStorage.padLeft(v, pad) : emptyPlane("values"); }
105
+ catch (error) { keys?.dispose(); throw error; }
106
+ inner = BatchedRotatingCache.adoptPhysical(keys, values, position);
107
+ } else {
108
+ const k = source.keys ? temporalStorageView(quantizedRowStorage, source.keys, state, options) : null;
109
+ const v = source.values ? temporalStorageView(quantizedRowStorage, source.values, state, options) : null;
110
+ const empty = (field: "keys" | "values") => {
111
+ using tensor = emptyPlane(field);
112
+ return tensor ? ops.quantize(tensor, source.groupSize, source.bits) : null;
113
+ };
114
+ try {
115
+ const keys = k ? quantizedRowStorage.padLeft(k, pad) : empty("keys");
116
+ let values: ops.QuantizedTensor | null;
117
+ try { values = v ? quantizedRowStorage.padLeft(v, pad) : empty("values"); }
118
+ catch (error) { if (keys) quantizedRowStorage.dispose(keys); throw error; }
119
+ inner = BatchedRotatingQuantCache.adoptPhysical(keys, values, source.groupSize, source.bits, position);
120
+ } finally { if (k) quantizedRowStorage.dispose(k); if (v) quantizedRowStorage.dispose(v); }
121
+ }
122
+ const row = new AlignedRotatingCache(inner); row.minimumReusableOffset = source.minimumReusableOffset ?? 0;
123
+ result.push(row);
124
+ }
125
+ } catch (error) { for (const row of result) row.dispose(); throw error; }
126
+ for (const row of rows) row.dispose();
127
+ return result;
128
+ }
129
+
130
+ /** Position state has no codec or attention arithmetic. */
131
+ export class RotatingKvPositions implements TransitioningKvPositions {
132
+ #state: BatchedRotatingState;
133
+ #rope?: MlxArray;
134
+ constructor(maxSize: number) { this.#state = new BatchedRotatingState(maxSize, []); }
135
+ get rowOffsets(): readonly number[] { return this.#state.offsets; }
136
+ get leftPad(): readonly number[] { return this.#state.leftPad; }
137
+ get offset(): number { return this.#state.totalOffset; }
138
+ get batchSize(): number | null { return this.#state.batchSize || null; }
139
+ get ropeOffsetArr(): MlxArray | undefined {
140
+ if (!this.batchSize) return undefined;
141
+ return this.#rope ??= ops.fromInt32([...this.rowOffsets], [this.batchSize]);
142
+ }
143
+ sync(rows: readonly Cache[]): void {
144
+ this.#rope?.dispose(); this.#rope = undefined;
145
+ if (!rows.length) { this.#state = new BatchedRotatingState(this.#state.maxSize, []); return; }
146
+ this.#state.restore(combinedPosition(rows.map(row => (row as AlignedRotatingCache).inner)));
147
+ }
148
+ filterRows(keep: readonly number[]): void { this.#state.filter(keep); this.#rope?.dispose(); this.#rope = undefined; }
149
+ makeMask(tokens: number, window: number | null): Mask {
150
+ return { mode: "array", arr: buildBatchedRotatingMask(this.#state.batchSize, tokens, this.#state.leftPad,
151
+ this.#state.maxSize, window ?? this.#state.maxSize, this.#state.ringIndex, this.#state.totalOffset, this.#state.rotated,
152
+ tokens > 1 || this.#state.hasPendingPadding) };
153
+ }
154
+ dispose(): void { this.#rope?.dispose(); this.#rope = undefined; this.#state = new BatchedRotatingState(this.#state.maxSize, []); }
155
+ }
156
+
157
+ /** Once all rows use affine storage, concatenate their already-aligned planes
158
+ * without changing ring columns or rebuilding any attention mask. */
159
+ export class RotatingAffineLayout implements TransitionedKvLayout, PaddedPrefillCache {
160
+ #inner?: BatchedRotatingQuantCache;
161
+ #before?: number[];
162
+ #minimum: number[] = [];
163
+ constructor(readonly maxSize: number, readonly groupSize: number, readonly bits: number) {}
164
+ get minimumReusableOffset(): number { return Math.max(0, ...this.#minimum); }
165
+ get quantizedAttention() { return this.#inner; }
166
+ captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
167
+ return captureRotatingDonorAttention(this.#inner!);
168
+ }
169
+ get rowOffsets(): readonly number[] { return this.#inner?.offsetArr ?? []; }
170
+ get leftPad(): readonly number[] { return this.#inner?.leftPad ?? []; }
171
+ get offset(): number { return this.#inner?.offset ?? 0; }
172
+ get batchSize(): number | null { return this.#inner?.batchSize || null; }
173
+ get ropeOffsetArr(): MlxArray | undefined { return this.#inner?.ropeOffsetArr; }
174
+ get maxTokens(): number { return this.maxSize; }
175
+ signature(): string { return `kv:rotating-affine:${this.maxSize}:${this.groupSize}:${this.bits}`; }
176
+ makeEmptyBatch(): RotatingAffineLayout { return new RotatingAffineLayout(this.maxSize, this.groupSize, this.bits); }
177
+ makeMask(tokens: number, window: number | null): Mask { return this.#inner!.makeMask(tokens, window); }
178
+ preparePrefill(padding: PrefillPadding): void { this.#inner!.preparePrefill(padding); }
179
+ finalizePrefill(): void { this.#inner!.finalizePrefill(); }
180
+ bytesPerToken(): number {
181
+ const tokens = this.#inner?.keys?.packed.shape[2] ?? 0;
182
+ return tokens ? this.state().reduce((n, a) => n + a.nbytes, 0) / (tokens * this.#inner!.batchSize) : 0;
183
+ }
184
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * Math.min(tokens, this.maxSize); }
185
+ isTrimmable(): boolean { return this.#inner!.positionSnapshot.totalOffset < this.maxSize; }
186
+ trim(count: number): void { this.#inner!.trim(count); }
187
+ specRoundBegin(): void { this.#before = [...this.rowOffsets]; }
188
+ specRoundCommit(): void { this.#before = undefined; }
189
+ specRoundRollback(keep: number | readonly number[]): void {
190
+ this.#inner = rollbackRotatingRing(this.#inner!, this.#before!, keep);
191
+ this.#before = undefined;
192
+ }
193
+ updateAndFetch(): [MlxArray, MlxArray] { return this.#inner!.updateAndFetch(); }
194
+ releaseRopeArr(): void { this.#inner?.releaseRopeArr(); }
195
+ mergeRows(rows: readonly Cache[]): void {
196
+ const owned: Cache[] = [];
197
+ try {
198
+ for (const row of rows) {
199
+ if (row instanceof RotatingAffineLayout) {
200
+ for (let i = 0; i < (row.batchSize ?? 0); i++) owned.push(row.extractRow(i));
201
+ } else if (row instanceof AlignedRotatingCache) owned.push(row.extract());
202
+ else owned.push(...cloneKvCaches([row]));
203
+ }
204
+ if (!owned.length) { this.dispose(); return; }
205
+ const aligned = alignRotatingRows(owned); owned.length = 0;
206
+ try { this.adoptAlignedRows(aligned); } finally { for (const row of aligned) row.dispose(); }
207
+ } finally { for (const row of owned) row.dispose(); }
208
+ }
209
+ adoptAlignedRows(rows: readonly AlignedRotatingCache[]): void {
210
+ const rings = rows.map(row => (row as AlignedRotatingCache).inner as BatchedRotatingQuantCache);
211
+ const position = combinedPosition(rings);
212
+ if (rings.every(row => !row.keys)) {
213
+ const empty = BatchedRotatingQuantCache.adoptPhysical(null, null, this.groupSize, this.bits,
214
+ position);
215
+ this.#inner?.dispose(); this.#inner = empty; this.#minimum = rows.map(row => row.minimumReusableOffset ?? 0);
216
+ return;
217
+ }
218
+ const merge = (field: "keys" | "values") => {
219
+ const planes: MlxArray[] = [];
220
+ try {
221
+ for (const part of fields) planes.push(ops.concatAxis(rings.map(row => row[field]![part]), 0));
222
+ return { packed: planes[0]!, scales: planes[1]!, biases: planes[2]! };
223
+ } catch (error) { for (const plane of planes) plane.dispose(); throw error; }
224
+ };
225
+ const keys = merge("keys"); let values: ops.QuantizedTensor;
226
+ try { values = merge("values"); } catch (error) { quantizedRowStorage.dispose(keys); throw error; }
227
+ const inner = BatchedRotatingQuantCache.adoptPhysical(keys, values, this.groupSize, this.bits,
228
+ position);
229
+ this.#inner?.dispose(); this.#inner = inner; this.#minimum = rows.map(row => row.minimumReusableOffset ?? 0);
230
+ }
231
+ filterRows(keep: readonly number[]): void { this.#inner!.filterRows(keep); this.#minimum = keep.map(row => this.#minimum[row]!); }
232
+ extractRow(row: number): Cache {
233
+ return this.extract(row);
234
+ }
235
+ protected extract(row: number, limit?: number): Cache {
236
+ const result = this.#inner!.extractRow(row, limit) ?? new RotatingQuantizedKVCache(this.maxSize, this.groupSize, this.bits);
237
+ result.minimumReusableOffset = this.#minimum[row] ?? 0; return result;
238
+ }
239
+ state(): MlxArray[] { return this.#inner?.state() ?? []; }
240
+ dispose(): void { this.#inner?.dispose(); this.#inner = undefined; this.#minimum = []; this.#before = undefined; }
241
+ }
242
+
243
+ /** A committed speculative checkpoint contains only the newest valid window. */
244
+ export class SpeculativeRotatingAffineLayout extends RotatingAffineLayout {
245
+ #ropeForOffset = -1;
246
+ override get ropeOffsetArr(): MlxArray | undefined {
247
+ if (this.#ropeForOffset !== this.offset) {
248
+ this.releaseRopeArr(); this.#ropeForOffset = this.offset;
249
+ }
250
+ return super.ropeOffsetArr;
251
+ }
252
+ override makeEmptyBatch(): SpeculativeRotatingAffineLayout {
253
+ return new SpeculativeRotatingAffineLayout(this.maxSize, this.groupSize, this.bits);
254
+ }
255
+ override makeMask(tokens: number, window: number | null): Mask {
256
+ if (this.leftPad.every(pad => pad === 0) &&
257
+ this.offset + tokens <= Math.min(this.maxSize, window ?? this.maxSize))
258
+ return { mode: tokens === 1 ? "" : "causal", arr: null };
259
+ return super.makeMask(tokens, window);
260
+ }
261
+ override extractRow(row: number): Cache { return this.extract(row, this.maxSize); }
262
+ }
@@ -0,0 +1,35 @@
1
+ import * as ops from "../mlx/ops";
2
+ import { BatchedRotatingCache } from "./batched-rotating";
3
+ import { BatchedRotatingQuantCache } from "./batched-rotating-quant";
4
+ import { rotatingRollbackPlan } from "./batched-rotating-state";
5
+ import { plainRowStorage, quantizedRowStorage, rowRollIndices } from "./batched-row-storage";
6
+
7
+ export type RotatingRing = BatchedRotatingCache | BatchedRotatingQuantCache;
8
+
9
+ /** Retain accepted columns without decoding or recomputing the ring. Ownership
10
+ * moves to the returned ring; aligned members preserve their shared rectangle. */
11
+ export function rollbackRotatingRing(ring: BatchedRotatingCache, before: readonly number[], keep: number | readonly number[], preserveColumns?: boolean): BatchedRotatingCache;
12
+ export function rollbackRotatingRing(ring: BatchedRotatingQuantCache, before: readonly number[], keep: number | readonly number[], preserveColumns?: boolean): BatchedRotatingQuantCache;
13
+ export function rollbackRotatingRing(ring: RotatingRing, before: readonly number[], keep: number | readonly number[], preserveColumns?: boolean): RotatingRing;
14
+ export function rollbackRotatingRing(ring: RotatingRing, before: readonly number[],
15
+ keep: number | readonly number[], preserveColumns = false): RotatingRing {
16
+ const plan = rotatingRollbackPlan(ring.positionSnapshot, before, keep, preserveColumns);
17
+ if ("trim" in plan) { ring.trim(plan.trim); ring.releaseRopeArr(); return ring; }
18
+ let next: RotatingRing;
19
+ if (ring instanceof BatchedRotatingQuantCache) {
20
+ using indices = rowRollIndices(ring.keys!.packed.shape[2]!, plan.shifts);
21
+ const keys = quantizedRowStorage.rollRows(ring.keys!, indices);
22
+ let values: ops.QuantizedTensor;
23
+ try { values = quantizedRowStorage.rollRows(ring.values!, indices); }
24
+ catch (error) { quantizedRowStorage.dispose(keys); throw error; }
25
+ next = BatchedRotatingQuantCache.adoptPhysical(keys, values, ring.groupSize, ring.bits, plan.position);
26
+ } else {
27
+ using indices = rowRollIndices(ring.keys!.shape[2]!, plan.shifts);
28
+ const keys = plainRowStorage.rollRows(ring.keys!, indices);
29
+ let values: import("../mlx/array").MlxArray;
30
+ try { values = plainRowStorage.rollRows(ring.values!, indices); }
31
+ catch (error) { keys.dispose(); throw error; }
32
+ next = BatchedRotatingCache.adoptPhysical(keys, values, plan.position);
33
+ }
34
+ ring.dispose(); return next;
35
+ }
@@ -0,0 +1,101 @@
1
+ import { rotatingSourcePosition } from "./rotating-kv-layout";
2
+ import { MlxArray } from "../mlx/array";
3
+ import * as ops from "../mlx/ops";
4
+ import { type BatchableCache, type Cache, type Mask, type PrefillPadding, RotatingKVCache } from "./gemma4-base";
5
+ import { BatchedRotatingCache } from "./batched-rotating";
6
+ import { rollbackRotatingRing } from "./rotating-row-transaction";
7
+ import { BatchedRotatingState } from "./batched-rotating-state";
8
+ import { plainRowStorage, temporalStorageView } from "./batched-row-storage";
9
+ import { disposeResources } from "../engine/resources";
10
+
11
+ /** Ring transactions retain accepted KV columns, including rounds that cross
12
+ * the window boundary. The verify block already retains the complete history
13
+ * needed by its earliest query; no target recomputation or scheduler policy. */
14
+ export class SpeculativeRotatingKVCache implements BatchableCache {
15
+ #inner: BatchedRotatingCache;
16
+ #before?: number[];
17
+ constructor(readonly maxSize: number) { this.#inner = new BatchedRotatingCache(maxSize, []); }
18
+ signature(): string { return "kv:rotating-plain"; }
19
+ get offset(): number { return this.#inner.offset; }
20
+ get rowOffsets(): readonly number[] { return this.#inner.offsetArr; }
21
+ get leftPad(): readonly number[] { return this.#inner.leftPad; }
22
+ get batchSize(): number { return this.#inner.batchSize; }
23
+ get ropeOffsetArr(): MlxArray { return this.#inner.ropeOffsetArr; }
24
+ makeEmptyBatch(): SpeculativeRotatingKVCache { return new SpeculativeRotatingKVCache(this.maxSize); }
25
+ makeMask(tokens: number, window: number | null): Mask { return this.#inner.makeMask(tokens, window); }
26
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#inner.updateAndFetch(k, v); }
27
+ preparePrefill(padding: PrefillPadding): void { this.#inner.preparePrefill(padding); }
28
+ finalizePrefill(): void { this.#inner.finalizePrefill(); }
29
+ state(): MlxArray[] { return this.#inner.state(); }
30
+ isTrimmable(): boolean { return this.#inner.isTrimmable(); }
31
+ trim(count: number): void { this.#inner.trim(count); }
32
+ bytesPerToken(): number {
33
+ const state = this.state();
34
+ return state.length ? state.reduce((n, a) => n + a.nbytes, 0) / (this.batchSize * state[0]!.shape[2]!) : 0;
35
+ }
36
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * Math.min(tokens, this.maxSize); }
37
+ extractRow(row: number): Cache {
38
+ const result = new RotatingKVCache(this.maxSize);
39
+ if (!this.rowOffsets[row] || !this.#inner.keys || !this.#inner.values) return result;
40
+ const position = new BatchedRotatingState(this.maxSize, []); position.restore(this.#inner.positionSnapshot);
41
+ const range = { row, from: Math.max(0, position.leftPad[row]!, position.activeLength - this.maxSize),
42
+ to: position.activeLength, copy: true };
43
+ const keys = temporalStorageView(plainRowStorage, this.#inner.keys, position, range);
44
+ let values: MlxArray;
45
+ try { values = temporalStorageView(plainRowStorage, this.#inner.values, position, range); }
46
+ catch (error) { keys.dispose(); throw error; }
47
+ result.restoreState(keys, values, this.rowOffsets[row]!, keys.shape[2]!); return result;
48
+ }
49
+ captureDonorRows(): import("./gemma4-base").KvDonorRows {
50
+ const position = new BatchedRotatingState(this.maxSize, []); position.restore(this.#inner.positionSnapshot);
51
+ const from = Math.max(0, position.activeLength - this.maxSize);
52
+ const range = { from, to: position.activeLength };
53
+ const keys = temporalStorageView(plainRowStorage, this.#inner.keys!, position, range); let values: MlxArray;
54
+ try { values = temporalStorageView(plainRowStorage, this.#inner.values!, position, range); }
55
+ catch (error) { keys.dispose(); throw error; }
56
+ return { keys, values, offsets: [...this.rowOffsets], starts: this.leftPad.map(pad => Math.max(0, pad - from)),
57
+ ends: this.rowOffsets.map(() => position.activeLength - from) };
58
+ }
59
+
60
+ filterRows(keep: readonly number[]): void { this.#inner.filterRows(keep); }
61
+ mergeRows(sources: readonly Cache[]): void {
62
+ const only = sources.length === 1 ? sources[0] : undefined;
63
+ if (only instanceof SpeculativeRotatingKVCache) {
64
+ const keys = only.#inner.keys ? ops.copyOf(only.#inner.keys) : null;
65
+ let values: MlxArray | null;
66
+ try { values = only.#inner.values ? ops.copyOf(only.#inner.values) : null; }
67
+ catch (error) { keys?.dispose(); throw error; }
68
+ const next = BatchedRotatingCache.adoptPhysical(keys, values, only.#inner.positionSnapshot);
69
+ this.#inner.dispose(); this.#inner = next; return;
70
+ }
71
+ const held: Cache[] = [], planes: { keys: MlxArray; values: MlxArray }[] = [], offsets: number[] = [];
72
+ try {
73
+ const rows = sources.flatMap(source => source instanceof SpeculativeRotatingKVCache
74
+ ? Array.from({ length: source.batchSize }, (_, row) => { const cache = source.extractRow(row); held.push(cache); return cache; })
75
+ : [source]) as RotatingKVCache[];
76
+ const prototype = rows.find(row => row.keys !== null);
77
+ if (!prototype) {
78
+ this.#inner.dispose(); this.#inner = new BatchedRotatingCache(this.maxSize, rows.map(() => 0)); return;
79
+ }
80
+ for (const cache of rows) {
81
+ const state = rotatingSourcePosition(cache);
82
+ const range = { from: Math.max(0, state.activeLength - this.maxSize), to: state.activeLength };
83
+ const plane = (field: "keys" | "values") => cache[field]
84
+ ? temporalStorageView(plainRowStorage, cache[field]!, state, range)
85
+ : ops.zeros([1, prototype[field]!.shape[1]!, 0, prototype[field]!.shape[3]!], prototype[field]!.dtype);
86
+ const keys = plane("keys"); let values: MlxArray;
87
+ try { values = plane("values"); } catch (error) { keys.dispose(); throw error; }
88
+ planes.push({ keys, values }); offsets.push(cache.offset);
89
+ }
90
+ const next = BatchedRotatingCache.merge(planes, offsets, this.maxSize);
91
+ this.#inner.dispose(); this.#inner = next;
92
+ } finally { disposeResources([...held, ...planes.flatMap(row => [row.keys, row.values])]); }
93
+ }
94
+ specRoundBegin(): void { this.#before = [...this.rowOffsets]; }
95
+ specRoundCommit(): void { this.#before = undefined; }
96
+ specRoundRollback(keep: number | readonly number[]): void {
97
+ this.#inner = rollbackRotatingRing(this.#inner, this.#before!, keep);
98
+ this.#before = undefined;
99
+ }
100
+ dispose(): void { this.#before = undefined; this.#inner.dispose(); }
101
+ }
@@ -0,0 +1,39 @@
1
+ import type { PrefillPadding } from "./gemma4-base";
2
+ import type { MlxArray } from "../mlx/array";
3
+ import { Dtype } from "../mlx/ffi";
4
+ import * as ops from "../mlx/ops";
5
+
6
+ /** ArraysCache's remaining lengths and padding from mlx-lm (MIT).
7
+ * Recurrent state has no token axis to roll: masked tokens must not update it. */
8
+ export class SsmPrefillPadding {
9
+ #left: number[];
10
+ #lengths: number[];
11
+ constructor(padding: PrefillPadding) {
12
+ this.#left = padding.leftPadding ? [...padding.leftPadding] : padding.lengths.map(() => 0);
13
+ this.#lengths = [...padding.lengths];
14
+ }
15
+ convolutionLengths(tokens: number): number[] {
16
+ return this.#lengths.map(length => Math.max(0, Math.min(tokens, length)));
17
+ }
18
+ makeMask(tokens: number): MlxArray {
19
+ const B = this.#lengths.length;
20
+ using arange = ops.arange(0, tokens, 1, Dtype.int32);
21
+ using positions = ops.reshape(arange, [1, tokens]);
22
+ using left = ops.fromInt32(this.#left, [B, 1]);
23
+ using ends = ops.fromInt32(this.#lengths, [B, 1]);
24
+ using after = ops.greaterEqual(positions, left);
25
+ using before = ops.less(positions, ends);
26
+ return ops.logicalAnd(after, before);
27
+ }
28
+ advance(tokens: number): number[] {
29
+ const counts = this.#lengths.map((length, row) => Math.max(0,
30
+ Math.min(tokens, length) - Math.max(0, Math.min(tokens, this.#left[row]!))));
31
+ this.#lengths = this.#lengths.map(length => length - tokens);
32
+ this.#left = this.#left.map(left => left - tokens);
33
+ return counts;
34
+ }
35
+ filter(keep: readonly number[]): void {
36
+ this.#left = keep.map(row => this.#left[row]!);
37
+ this.#lengths = keep.map(row => this.#lengths[row]!);
38
+ }
39
+ }
@@ -56,7 +56,7 @@ export function isDrafterModelType(modelType: string): boolean {
56
56
 
57
57
  /** DiffusionGemma (model_type `diffusion_gemma`): block/masked-diffusion canvas
58
58
  * model. Non-autoregressive — routed through the diffusion engine, not the AR
59
- * loop. See docs/design/diffusion-gemma-port.md. */
59
+ * loop. See docs/design/generic-model-support.md. */
60
60
  export function isDiffusionGemmaConfig(config: ModelConfig): boolean {
61
61
  return config.modelType === "diffusion_gemma";
62
62
  }
@@ -0,0 +1,51 @@
1
+ // Experimental variant 8, k=3 / T=256 / L<=12 only.
2
+ // The word-per-lane kernel uses 24 of 32 SIMD lanes for a 3-bit block.
3
+ // Assign eight outputs to every lane, load adjacent packed words, and align
4
+ // them once so each unrolled decoder has constant shifts. Row accumulation,
5
+ // split boundaries and the separate partial reduction stay identical to v6.
6
+ export const BALANCED_SCATTER_SOURCE = String.raw`
7
+ const uint lane = thread_index_in_simdgroup;
8
+ const uint sg = simdgroup_index_in_threadgroup;
9
+ const uint block = thread_position_in_grid.y * (uint)SG_TG + sg;
10
+ const uint nBlocks = (uint)C / (uint)BT;
11
+ const uint zi = thread_position_in_grid.z;
12
+ const uint sample = zi / (uint)SPLITS;
13
+ const uint split = zi - sample * (uint)SPLITS;
14
+ if (block >= nBlocks || sample >= (uint)M) return;
15
+ const uint wpb = (uint)(BT * K / 32);
16
+ const ulong rowWords = (ulong)(INTERLEAVE ? 2 : nBlocks) * wpb;
17
+ const uint pBase = lane * 8u * (uint)K;
18
+ const uint wi = pBase >> 5u;
19
+ const uint off = pBase & 31u;
20
+ const uint next = (wi + 1u == wpb) ? 0u : wi + 1u;
21
+ const device uint32_t* words = codes + (INTERLEAVE
22
+ ? (ulong)(block / 2) * R * 2 * wpb + (block % 2) * wpb : (ulong)block * wpb);
23
+ const uint rowsPer = ((uint)R + (uint)SPLITS - 1u) / (uint)SPLITS;
24
+ const uint r0 = split * rowsPer;
25
+ const uint r1 = metal::min((uint)R, r0 + rowsPer);
26
+ const device T* xs = x + (ulong)sample * (ulong)R;
27
+ float acc[8];
28
+ #pragma clang loop unroll(full)
29
+ for (uint i = 0; i < 8u; ++i) acc[i] = 0.0f;
30
+ for (uint r = r0; r < r1; ++r) {
31
+ const uint word = words[(ulong)r * rowWords + wi];
32
+ const uint nxt = words[(ulong)r * rowWords + next];
33
+ const uint lo = off == 0u ? word : (word >> off) | (nxt << (32u - off));
34
+ const uint hi = off == 0u ? nxt : nxt >> off;
35
+ const float xr = float(xs[r]);
36
+ const float sr = float(scales[r]);
37
+ #pragma clang loop unroll(full)
38
+ for (uint i = 0; i < 8u; ++i) {
39
+ const uint shift = i * (uint)K;
40
+ uint win = lo >> shift;
41
+ if (shift + (uint)L > 32u) win |= hi << (32u - shift);
42
+ win &= (1u << (uint)L) - 1u;
43
+ const float value = (float)trellis_y(win) * (1.0f / 147.800537109375f);
44
+ acc[i] = metal::fma(value * sr, xr, acc[i]);
45
+ }
46
+ }
47
+ const ulong outBase = ((ulong)sample * (ulong)SPLITS + split) * (ulong)C;
48
+ #pragma clang loop unroll(full)
49
+ for (uint i = 0; i < 8u; ++i)
50
+ partial[outBase + block * (uint)BT + (uint)BT - 1u - lane * 8u - i] = acc[i];
51
+ `;