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,67 @@
1
+ import type { Cache } from "../../model/gemma4-base";
2
+ import type { MlxArray } from "../../mlx/array";
3
+ import * as ops from "../../mlx/ops";
4
+ import { legacyCacheCodecs, type CacheCodecProvider, type CacheHeaderEntry,
5
+ type SnapshotContext } from "../../kv-store";
6
+ import { disposeResources } from "../../engine/resources";
7
+ import type { CheckpointAttachment } from "./checkpoint-state";
8
+ type TensorSlot = SnapshotContext["slots"][number];
9
+
10
+ /** Reuse the backend cache codecs for method-owned companion state. Tensor
11
+ * slots address attachment indices here; the persistence tier supplies the
12
+ * outer file offsets and checksums. No host serialization of tensors occurs. */
13
+ export function captureCacheAttachment(schema: string, caches: readonly Cache[],
14
+ metadata: CheckpointAttachment["metadata"], codecs: CacheCodecProvider = legacyCacheCodecs): CheckpointAttachment {
15
+ const tensors: MlxArray[] = [], headers: Array<CacheHeaderEntry | null> = [];
16
+ try {
17
+ for (const cache of caches) {
18
+ if (!cache.offset) { headers.push(null); continue; }
19
+ const slots: TensorSlot[] = [];
20
+ const liveSlice = (array: MlxArray, length: number, axis = 2) => {
21
+ const end = [...array.shape]; end[axis] = length;
22
+ return array.slice(array.shape.map(() => 0), end);
23
+ };
24
+ const push = (array: MlxArray, owned: boolean) => {
25
+ const value = owned ? array : ops.copyOf(array);
26
+ const off = tensors.length; tensors.push(value);
27
+ slots.push({ off, bytes: value.nbytes, shape: [...value.shape], dtype: value.dtype, hash: "" });
28
+ };
29
+ const context: SnapshotContext = { slots, push, liveSlice,
30
+ liveMlaSlice: (array, length) => liveSlice(array, length, 1),
31
+ pushTriple(triple, length) {
32
+ for (const array of [triple.packed, triple.scales, triple.biases])
33
+ push(length === null ? array : liveSlice(array, length), length !== null);
34
+ },
35
+ };
36
+ const entry = codecs.forCache(cache).snapshot(cache, context);
37
+ if (cache.minimumReusableOffset !== undefined) entry.minimumReusableOffset = cache.minimumReusableOffset;
38
+ headers.push(entry);
39
+ }
40
+ return { schema, metadata: { ...metadata, cacheCodecs: codecs.id, cacheHeaders: JSON.stringify(headers) }, tensors };
41
+ } catch (error) { disposeResources(tensors); throw error; }
42
+ }
43
+
44
+ /** Returned caches own immutable aliases. The caller keeps an external SSD
45
+ * lease until its normal state materialization boundary has completed. */
46
+ export function restoreCacheAttachment(attachment: CheckpointAttachment, makeCache: () => Cache[],
47
+ codecs: CacheCodecProvider = legacyCacheCodecs): Cache[] {
48
+ if (attachment.metadata.cacheCodecs !== codecs.id) throw new Error("companion cache codec mismatch");
49
+ const headers = JSON.parse(attachment.metadata.cacheHeaders as string) as Array<CacheHeaderEntry | null>;
50
+ const caches = makeCache(), pending: MlxArray[] = [];
51
+ const arr = (slot: TensorSlot) => {
52
+ const value = ops.copyOf(attachment.tensors[slot.off]!); pending.push(value); return value;
53
+ };
54
+ try {
55
+ for (const [index, entry] of headers.entries()) {
56
+ if (!entry) continue;
57
+ const cache = codecs.forHeader(entry).load(entry, { path: attachment.schema, arr, grownArr: arr,
58
+ triple: (slots, at) => ({ packed: arr(slots[at]!), scales: arr(slots[at + 1]!), biases: arr(slots[at + 2]!) }),
59
+ });
60
+ pending.length = 0;
61
+ cache.minimumReusableOffset = entry.minimumReusableOffset ?? 0;
62
+ const previous = caches[index]; caches[index] = cache;
63
+ previous?.dispose();
64
+ }
65
+ return caches;
66
+ } catch (error) { disposeResources([...pending, ...caches]); throw error; }
67
+ }
@@ -0,0 +1,31 @@
1
+ import { TurboQuantKVCache, RotatingKVCache, RotatingQuantizedKVCache, isBatchableCache, type BatchableCache, type Cache } from "../../model/gemma4-base";
2
+ import { DelayedRotatingQuantizedKVCache } from "../../model/delayed-rotating-quantized-kv";
3
+ import { RotatingAffineLayout } from "../../model/rotating-kv-layout";
4
+ import { BatchedTurboQuantKVCache } from "../../model/batched-turboquant-kv";
5
+ import { targetRowLayoutFactory, type TargetRowLayout } from "./target-layout-capability";
6
+ import { PagedKVCache } from "../../lab/paged-kv/paged-kv";
7
+ import { PagedKvRows } from "../../lab/paged-kv/paged-kv-rows";
8
+
9
+ /** Bind encoded state to its row layout. Execution owners only move rows. */
10
+ export function ownedCacheLayout(cache: Cache): BatchableCache | undefined {
11
+ if (cache instanceof PagedKVCache) return new PagedKvRows(cache.capacityTokens, cache.blockSize);
12
+ if (isBatchableCache(cache)) return cache.makeEmptyBatch();
13
+ if (cache instanceof TurboQuantKVCache) return new BatchedTurboQuantKVCache(cache.kBits, cache.vBits);
14
+ return undefined;
15
+ }
16
+
17
+ /** Prefill reuses decode's layouts; shape selection belongs to storage. */
18
+ export function prefillCacheLayout(cache: Cache): BatchableCache {
19
+ if (cache instanceof PagedKVCache) return new PagedKvRows(cache.capacityTokens, cache.blockSize);
20
+ if (isBatchableCache(cache)) return cache.makeEmptyBatch();
21
+ if (cache instanceof RotatingKVCache) return new DelayedRotatingQuantizedKVCache(cache.maxSize, 64, 4, Infinity, () => {});
22
+ if (cache instanceof RotatingQuantizedKVCache) return new RotatingAffineLayout(cache.maxSize, cache.groupSize, cache.bits);
23
+ return targetCacheLayout(cache);
24
+ }
25
+
26
+ /** Target transaction layouts retain each source cache's numerical codec. */
27
+ export function targetCacheLayout(cache: Cache): TargetRowLayout {
28
+ const make = targetRowLayoutFactory(cache);
29
+ if (!make) throw new Error(`No target row layout for ${cache.signature()}`);
30
+ return make();
31
+ }
@@ -0,0 +1,53 @@
1
+ import type { Cache } from "../../model/gemma4-base";
2
+ import * as ops from "../../mlx/ops";
3
+ import { leaseCacheStates } from "./state-views";
4
+ import { withResource } from "../../engine/resources";
5
+ import type { MlxArray } from "../../mlx/array";
6
+ import { disposeResources, cleanupFailure } from "../../engine/resources";
7
+
8
+ /** Generation-method state accompanying the target cache. The method owns
9
+ * its schema and alignment; storage owns these immutable tensor snapshots.
10
+ * Attachments require an exact prefix boundary and are never trimmed. */
11
+ export interface CheckpointAttachment {
12
+ schema: string;
13
+ metadata: Record<string, string | number | boolean>;
14
+ tensors: MlxArray[];
15
+ }
16
+
17
+ export function attachmentBytes(attachments: readonly CheckpointAttachment[] = []): number {
18
+ return attachments.reduce((sum, entry) =>
19
+ sum + entry.tensors.reduce((bytes, tensor) => bytes + tensor.nbytes, 0), 0);
20
+ }
21
+
22
+ export function disposeAttachments(attachments: readonly CheckpointAttachment[] = []): void {
23
+ disposeResources(attachments.flatMap((entry) => entry.tensors));
24
+ }
25
+
26
+ export function cloneAttachments(attachments: readonly CheckpointAttachment[] = []): CheckpointAttachment[] {
27
+ const held: MlxArray[] = [];
28
+ try {
29
+ return attachments.map((entry) => ({
30
+ schema: entry.schema, metadata: { ...entry.metadata },
31
+ tensors: entry.tensors.map((tensor) => {
32
+ const copy = tensor.slice(tensor.shape.map(() => 0), [...tensor.shape]);
33
+ held.push(copy);
34
+ return copy;
35
+ }),
36
+ }));
37
+ } catch (error) { return cleanupFailure(error, () => disposeResources(held)); }
38
+ }
39
+
40
+ /** Shared cache port for MLX execution methods and their companion state. */
41
+ export type MlxPrefixCache = import("../../contracts/prefix-cache").PrefixCache<
42
+ import("../../model/gemma4").Cache[], CheckpointAttachment[]
43
+ >;
44
+
45
+ /** Resolve snapshot copies before storage retains them. A deferred compact
46
+ * row still owns its full parent batch; persistence may wait until idle.
47
+ * This only settles GPU state and performs no serialization or disk I/O. */
48
+ export function materializeCheckpoint(caches: Cache[], attachments: readonly CheckpointAttachment[] = []): void {
49
+ withResource(leaseCacheStates(caches), state => {
50
+ const tensors = [...state, ...attachments.flatMap(attachment => attachment.tensors)];
51
+ if (tensors.length) ops.evalAll(tensors);
52
+ });
53
+ }
@@ -0,0 +1,106 @@
1
+ import { SpillQueue, type SpillItem } from "../../kv-store";
2
+ import { cacheBytes } from "../../prompt-cache";
3
+ import { disposeResources } from "../../engine/resources";
4
+ import type { ContinuationStore, OrdinaryContinuationState } from "./continuation";
5
+
6
+ type Metadata = Parameters<ContinuationStore["storeGenerationCheckpoint"]>[2];
7
+ interface Attempt { key: string; completed: boolean; pending: number; failed: boolean; version: number; retired: boolean }
8
+ interface Job { attempt: Attempt; metadata?: Metadata; cleanup?: number }
9
+
10
+ /** One coordinator per checkpoint store. Queue owns snapshots through disk
11
+ * completion; per-key attempts order cleanup before subsequent writes. */
12
+ export class ContinuationPersistence {
13
+ readonly #attempts = new Map<string, Attempt>();
14
+ readonly #jobs = new WeakMap<SpillItem["caches"], Job>();
15
+ readonly #queue: SpillQueue;
16
+ readonly #cleanupFailures = new Map<string, Set<number>>();
17
+ readonly #cleanupPending = new Map<string, Set<number>>();
18
+ readonly #cleanupSucceeded = new Map<string, number>();
19
+ #nextBarrier = 0;
20
+ constructor(readonly store: ContinuationStore, options: {
21
+ maxBytes: number;
22
+ /** All blocking tensor serialization runs through the owner's idle gate. */
23
+ runStep: <T>(step: () => T) => Promise<T>;
24
+ }) {
25
+ this.#queue = new SpillQueue(options.maxBytes, cacheBytes, async item => {
26
+ const job = this.#jobs.get(item.caches)!;
27
+ if (job.cleanup) {
28
+ await options.runStep(() => store.removeGenerationCheckpoints(job.attempt.key));
29
+ return true;
30
+ }
31
+ const current = () => this.#attempts.get(job.attempt.key) === job.attempt && !job.attempt.completed;
32
+ if (!current()) return true;
33
+ const stored = await store.storeGenerationCheckpoint(item.tokens, item.caches, job.metadata!, options.runStep);
34
+ // No newer queued write can run until this callback returns. An old
35
+ // in-flight rename can therefore be removed without deleting new work.
36
+ if (!current()) await options.runStep(() => store.removeGenerationCheckpoints(job.attempt.key));
37
+ return stored;
38
+ }, disposeResources, item => !this.#jobs.get(item.caches)?.cleanup);
39
+ }
40
+ /** A completed response is ineligible immediately, before queued unlink. */
41
+ canRestore(key: string): boolean { return !this.#cleanupPending.has(key) && !this.#cleanupFailures.has(key); }
42
+ begin(key: string): Attempt {
43
+ const attempt = { key, completed: false, pending: 0, failed: false, version: 0, retired: false };
44
+ this.#attempts.set(key, attempt);
45
+ this.#queue.cancelWhere(item => !this.#jobs.get(item.caches)?.cleanup && this.#jobs.get(item.caches)?.attempt.key === key);
46
+ return attempt;
47
+ }
48
+ /** Takes ownership even when a completed/superseded attempt declines it. */
49
+ enqueue(attempt: Attempt, state: Omit<OrdinaryContinuationState, "seed">, metadata: Metadata): void {
50
+ if (attempt.completed || this.#attempts.get(attempt.key) !== attempt) {
51
+ disposeResources(state.caches); return;
52
+ }
53
+ this.#jobs.set(state.caches, { attempt, metadata: { ...metadata } });
54
+ attempt.pending++;
55
+ const version = ++attempt.version;
56
+ let completion: Promise<boolean>;
57
+ try { completion = this.#queue.enqueue({ caches: state.caches, tokens: [...state.cacheTokens], ns: attempt.key }); }
58
+ catch (error) { attempt.pending--; disposeResources(state.caches); throw error; }
59
+ void completion.then(stored => {
60
+ attempt.pending--;
61
+ if (version === attempt.version && !attempt.completed) attempt.failed = !stored;
62
+ this.#releaseSettled(attempt);
63
+ });
64
+ }
65
+ complete(attempt: Attempt): void {
66
+ attempt.completed = true;
67
+ this.#queue.cancelWhere(item => !this.#jobs.get(item.caches)?.cleanup && this.#jobs.get(item.caches)?.attempt === attempt);
68
+ if (this.#attempts.get(attempt.key) === attempt) {
69
+ this.#attempts.delete(attempt.key);
70
+ const barrier = ++this.#nextBarrier;
71
+ const pending = this.#cleanupPending.get(attempt.key) ?? new Set<number>();
72
+ pending.add(barrier); this.#cleanupPending.set(attempt.key, pending);
73
+ const caches: SpillItem["caches"] = [];
74
+ this.#jobs.set(caches, { attempt, cleanup: barrier });
75
+ void this.#queue.enqueue({ caches, tokens: [], ns: attempt.key }).then(stored => {
76
+ const key = attempt.key;
77
+ const outstanding = this.#cleanupPending.get(key)!;
78
+ outstanding.delete(barrier);
79
+ if (!outstanding.size) this.#cleanupPending.delete(key);
80
+ const failures = this.#cleanupFailures.get(key) ?? new Set<number>();
81
+ if (stored) {
82
+ this.#cleanupSucceeded.set(key, Math.max(barrier, this.#cleanupSucceeded.get(key) ?? 0));
83
+ // A successful unlink covers older failures, never a newer barrier.
84
+ for (const failed of failures) if (failed <= barrier) failures.delete(failed);
85
+ } else if (barrier > (this.#cleanupSucceeded.get(key) ?? 0)) failures.add(barrier);
86
+ if (failures.size) this.#cleanupFailures.set(key, failures);
87
+ else this.#cleanupFailures.delete(key);
88
+ if (!outstanding.size) this.#cleanupSucceeded.delete(key);
89
+ });
90
+ }
91
+ }
92
+ /** Request cancellation/disposal preserves queued and durable state. */
93
+ release(attempt: Attempt): void { attempt.retired = true; this.#releaseSettled(attempt); }
94
+ #releaseSettled(attempt: Attempt): void {
95
+ if (attempt.retired && attempt.pending === 0 && !attempt.failed && this.#attempts.get(attempt.key) === attempt)
96
+ this.#attempts.delete(attempt.key);
97
+ }
98
+ get stats() { return { pendingCount: this.#queue.pendingCount, pendingBytes: this.#queue.pendingBytes,
99
+ dropped: this.#queue.droppedCount, failed: this.#queue.failedCount }; }
100
+ async flush(): Promise<{ durable: boolean; pendingBytes: number; failed: number }> {
101
+ await this.#queue.drain();
102
+ const active = [...this.#attempts.values()].filter(attempt => !attempt.completed);
103
+ return { durable: this.#cleanupFailures.size === 0 && active.every(attempt => attempt.pending === 0 && !attempt.failed),
104
+ pendingBytes: this.#queue.pendingBytes, failed: this.#cleanupFailures.size + active.filter(attempt => attempt.failed).length };
105
+ }
106
+ }
@@ -0,0 +1,43 @@
1
+ import { snapshotGenerationPolicy } from "./request-policy";
2
+ import type { GenerateOptions } from "../../generate";
3
+ import type { ResolvedExecution } from "../../contracts/execution";
4
+ import { makeStepSampler } from "../../sampler";
5
+ import { createRowSampling } from "./row-sampling";
6
+ import { bindContinuationPolicy, type OrdinaryContinuation } from "./continuation";
7
+
8
+ /** Optional policy and sampler recovery for the existing ordinary driver.
9
+ * KV layout and delayed conversion remain owned by cache maintenance and persistence. */
10
+ export function createOrdinaryContinuationRequest(input: Omit<Parameters<typeof bindContinuationPolicy>[0], "namespace"> & {
11
+ interval: number;
12
+ persistence: import("./continuation-persistence").ContinuationPersistence;
13
+ onToken: Parameters<typeof createRowSampling>[1];
14
+ }) {
15
+ const options: GenerateOptions = snapshotGenerationPolicy(input.options);
16
+ if (!Number.isSafeInteger(input.interval) || input.interval < 1) throw new Error("continuation interval must be positive");
17
+ if (options.grammar || options.logprobs || options.topLogprobs || options.fill || options.pagedKv ||
18
+ input.execution.method !== "autoregressive")
19
+ throw new Error("ordinary continuation requires replayable ordinary sampling");
20
+ const make = (seed = options.seed, history: readonly number[] = input.prompt) => createRowSampling(makeStepSampler(
21
+ { ...options, seed }, { tokenRepresentation: "device", grammarWait: "external", historyUpdate: "after-sample",
22
+ initialHistory: [...history] }), input.onToken);
23
+ let sampling = make();
24
+ let policy: ReturnType<typeof bindContinuationPolicy> | undefined;
25
+ const continuation: OrdinaryContinuation = {
26
+ interval: input.interval,
27
+ restore(namespace) { policy = bindContinuationPolicy({ ...input, options, namespace }); return policy.restore(); },
28
+ resumeSampling(state) {
29
+ const next = make(state.seed, [...state.cacheTokens, state.pendingToken]);
30
+ const previous = sampling; sampling = next; previous.dispose();
31
+ },
32
+ captureOwned: state => policy!.captureOwned(state),
33
+ complete: () => policy?.complete(),
34
+ };
35
+ return {
36
+ continuation,
37
+ sample: ((...args) => sampling.sample(...args)) as ReturnType<typeof createRowSampling>["sample"],
38
+ // History-free greedy sampling needs no recovery work in the fast path.
39
+ plainGreedy: sampling.plainGreedy,
40
+ onToken: input.onToken,
41
+ dispose: () => { policy?.release(); sampling.dispose(); },
42
+ };
43
+ }
@@ -0,0 +1,77 @@
1
+ import type { ContinuationPersistence } from "./continuation-persistence";
2
+ import type { Cache } from "../../model/gemma4-base";
3
+ import type { GenerateOptions } from "../../generate";
4
+ import type { ResolvedExecution } from "../../contracts/execution";
5
+ import type { SsdCacheStore } from "../../ssd-cache";
6
+ import { generationCheckpointKey } from "../../serve/checkpoint-identity";
7
+ import { disposeResources } from "../../engine/resources";
8
+
9
+ export interface OrdinaryContinuationState {
10
+ caches: Cache[];
11
+ cacheTokens: number[];
12
+ generatedTokens: number;
13
+ pendingToken: number;
14
+ seed: number;
15
+ }
16
+ /** Request-owned policy. Restore and captureOwned transfer cache ownership.
17
+ * No store or key knowledge enters the driver. */
18
+ export interface OrdinaryContinuation {
19
+ readonly interval: number;
20
+ restore(namespace: string): OrdinaryContinuationState | null;
21
+ resumeSampling(state: Omit<OrdinaryContinuationState, "caches">): void;
22
+ captureOwned(state: Omit<OrdinaryContinuationState, "seed">): void;
23
+ complete(): void;
24
+ }
25
+ export type ContinuationStore = Pick<SsdCacheStore, "findGenerationCheckpoint" | "restore" |
26
+ "storeGenerationCheckpoint" | "removeGenerationCheckpoints">;
27
+
28
+ /** Shared persistence policy for compatibility and grouped ordinary drivers. */
29
+ export function bindContinuationPolicy(input: {
30
+ store: ContinuationStore;
31
+ restore: (entry: NonNullable<ReturnType<ContinuationStore["findGenerationCheckpoint"]>>) => ReturnType<ContinuationStore["restore"]>;
32
+ prompt: number[]; options: GenerateOptions; execution: ResolvedExecution;
33
+ identity: unknown; namespace: string;
34
+ persistence?: ContinuationPersistence;
35
+ }) {
36
+ const { store, options, prompt, namespace } = input;
37
+ const key = generationCheckpointKey(prompt, options, namespace, input.execution, input.identity);
38
+ let seed = options.seed ?? 0;
39
+ const attempt = input.persistence?.begin(key);
40
+ return {
41
+ key,
42
+ restore(): OrdinaryContinuationState | null {
43
+ if (input.persistence && !input.persistence.canRestore(key)) return null;
44
+ const entry = store.findGenerationCheckpoint(prompt, key, namespace);
45
+ const restored = entry ? input.restore(entry) : null;
46
+ if (!restored) return null;
47
+ const metadata = restored.header.generationCheckpoint;
48
+ if (!metadata || metadata.originalPromptTokens !== prompt.length ||
49
+ metadata.generatedTokens !== restored.tokens.length - prompt.length) {
50
+ disposeResources(restored.caches);
51
+ throw new Error("generation checkpoint has inconsistent continuation metadata");
52
+ }
53
+ seed = metadata.seed;
54
+ return { caches: restored.caches, cacheTokens: restored.tokens,
55
+ generatedTokens: metadata.generatedTokens, pendingToken: metadata.pendingToken, seed };
56
+ },
57
+ async capture(state: Omit<OrdinaryContinuationState, "seed">): Promise<void> {
58
+ const stored = await store.storeGenerationCheckpoint(state.cacheTokens, state.caches, {
59
+ key, cacheNs: namespace, originalPromptTokens: prompt.length,
60
+ generatedTokens: state.generatedTokens, pendingToken: state.pendingToken,
61
+ seed, seedWasExplicit: options.seedWasExplicit === true,
62
+ });
63
+ if (stored) console.log(`[generation-checkpoint] saved ${state.generatedTokens} emitted tokens`);
64
+ },
65
+ captureOwned(state: Omit<OrdinaryContinuationState, "seed">): void {
66
+ if (!input.persistence || !attempt) { disposeResources(state.caches); throw new Error("owned continuation requires persistence coordinator"); }
67
+ input.persistence.enqueue(attempt, state, { key, cacheNs: namespace, originalPromptTokens: prompt.length,
68
+ generatedTokens: state.generatedTokens, pendingToken: state.pendingToken,
69
+ seed, seedWasExplicit: options.seedWasExplicit === true });
70
+ },
71
+ release() { if (input.persistence && attempt) input.persistence.release(attempt); },
72
+ complete() {
73
+ if (input.persistence && attempt) input.persistence.complete(attempt);
74
+ else store.removeGenerationCheckpoints(key);
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,39 @@
1
+ import type { DenoisingGraph } from "../../inference/denoising";
2
+ import type { MlxArray } from "../../mlx/array";
3
+ import type { Cache } from "../../model/gemma4-base";
4
+ import type { DiffusionGemmaModel } from "../../model/diffusion-gemma";
5
+ import type { MlxModelMemory } from "./autoregressive";
6
+ import { disposeResources } from "../../engine/resources";
7
+ import { runtimeConfig, type RuntimeConfig } from "../../runtime-config";
8
+
9
+ export interface MlxDenoisingBinding<State = Cache[]> {
10
+ readonly runtime?: RuntimeConfig;
11
+ readonly graph: DenoisingGraph<MlxArray, State>;
12
+ readonly memory: MlxModelMemory;
13
+ readonly adapters?: { active: string[] };
14
+ }
15
+
16
+ export function bindLegacyDenoisingModel(model: DiffusionGemmaModel): MlxDenoisingBinding {
17
+ return {
18
+ runtime: runtimeConfig(),
19
+ memory: model, adapters: model.loraState,
20
+ graph: {
21
+ descriptor: Object.freeze({ id: "legacy-diffusion-gemma", backend: "mlx",
22
+ graphAbi: "mlx-denoising-v1", stateAbi: "legacy-cache-array-v1", artifact: "legacy-resident-model" }),
23
+ vocabSize: model.config.text.vocabSize, canvasLength: model.canvasLength, embedScale: model.embedScale,
24
+ prefill: (ids, vision) => vision ? model.prefillVision(ids, vision) : model.prefill(ids),
25
+ extendPrefill: model.extendPrefill.bind(model),
26
+ decoderLogits: model.decoderLogits.bind(model),
27
+ dequantEmbedWeight: model.dequantEmbedWeight.bind(model),
28
+ softEmbeddings: model.softEmbeddings.bind(model),
29
+ closeState: disposeResources,
30
+ },
31
+ };
32
+ }
33
+
34
+ export function assertMlxDenoisingGraph<State>(graph: DenoisingGraph<MlxArray, State>): void {
35
+ const descriptor = graph.descriptor;
36
+ if (descriptor.backend !== "mlx" || descriptor.graphAbi !== "mlx-denoising-v1" ||
37
+ !descriptor.stateAbi)
38
+ throw new Error(`denoising graph ${descriptor.id} has an incompatible backend, graph, or state ABI`);
39
+ }
@@ -0,0 +1,32 @@
1
+ import { readAssistantDonors } from "./assistant-target";
2
+ import type { RuntimeModel } from "../../model/factory";
3
+ import { Gemma4Model, type Cache } from "../../model/gemma4";
4
+ import { Qwen35Model } from "../../model/qwen3_5";
5
+ import type { MlxArray } from "../../mlx/array";
6
+ import * as ops from "../../mlx/ops";
7
+ import type { TargetView } from "../../spec/source";
8
+
9
+ /** Class checks and live cache layout stay at the compatibility boundary.
10
+ * A replacement graph supplies the same ports without becoming these classes. */
11
+ export function bindLegacyDraftTarget(model: RuntimeModel, caches: Cache[]): TargetView {
12
+ if (model instanceof Gemma4Model) {
13
+ let sliding = -1, full = -1;
14
+ for (let i = 0; i < model.numDonors; i++) {
15
+ if (model.layers[i]!.layerType === "sliding_attention") sliding = i;
16
+ else full = i;
17
+ }
18
+ return Object.freeze({ identity: model, assistantRows: sliding >= 0 && full >= 0 ? {
19
+ hiddenSize: model.config.text.hiddenSize,
20
+ embed(ids: MlxArray) { using embedded = model.embed.encode(ids); return ops.mulScalar(embedded, model.embedScale); },
21
+ readDonors: () => readAssistantDonors(caches[sliding]!, caches[full]!),
22
+ } : undefined, gemmaTaps: Object.freeze({
23
+ layerCount: model.layers.length,
24
+ projection: Object.freeze({ embed: model.embed, logitsFromHidden: model.logitsFromHidden.bind(model) }),
25
+ }) });
26
+ }
27
+ if (model instanceof Qwen35Model) return Object.freeze({ identity: model, qwenMtp: Object.freeze({
28
+ hiddenSize: model.config.text.hiddenSize, layerCount: model.config.text.numHiddenLayers,
29
+ embed: model.embed.encode.bind(model.embed), logitsFromHidden: model.logitsFromHidden.bind(model),
30
+ }) });
31
+ return Object.freeze({ identity: model });
32
+ }
@@ -0,0 +1,105 @@
1
+ import type { TaskModel, GenOpts } from "../../eval/runner";
2
+ import type { CompletionClient } from "../../contracts/completion";
3
+ import { generate } from "../../generate";
4
+ import { clearCache } from "../../mlx/ffi";
5
+ import * as ops from "../../mlx/ops";
6
+ import { runtimeValue } from "../../runtime-config";
7
+ import { DiffusionGemmaModel } from "../../model/diffusion-gemma";
8
+
9
+ /** Bit-exact greedy decode: raw `model.forward` + argmax loop, which matches mlx-lm
10
+ * token-for-token (gen8k: 8000/8000). The product `generate()` decode wrapper
11
+ * (forwardHidden/logitsFromHidden + pipelined sampler) diverges from the raw forward
12
+ * on near-ties past ~32 tokens, so the EVAL decodes bit-exactly itself rather than
13
+ * alter the serving path. Greedy + full-precision KV only. */
14
+ export function greedyDecodeBitExact(tm: TaskModel, ids: number[], maxTokens: number): string {
15
+ if (tm.activeAdapters) tm.model.loraState.active = tm.activeAdapters; // apply the mounted adapter
16
+ const cache = tm.model.makeCache();
17
+ try {
18
+ // Chunked, LM-head-free prefill. The old single-shot `forward(prompt)`
19
+ // materialized [1, L, vocab] logits just to discard them — at a 248k
20
+ // vocab that's ~1 GB per 1k prompt tokens, and with no allocator-cache
21
+ // clears the Metal buffer cache retained every giant freed tensor
22
+ // (the 27B capability-suite 73 GB swap-thrash). Prefill only needs the
23
+ // cache state, which forwardHidden advances identically (the LM head
24
+ // never touches the cache) — bit-exact by construction. Chunk size and
25
+ // clear cadence mirror mlx-lm generate_step / our generate().
26
+ const PREFILL_CHUNK = 2048;
27
+ const prompt = ids.slice(0, -1);
28
+ for (let s = 0; s < prompt.length; s += PREFILL_CHUNK) {
29
+ const chunk = prompt.slice(s, s + PREFILL_CHUNK);
30
+ const cIds = ops.fromInt32(chunk, [1, chunk.length]);
31
+ tm.model.forwardHidden(cIds, cache).dispose();
32
+ cIds.dispose();
33
+ clearCache();
34
+ }
35
+ let last = ids[ids.length - 1]!;
36
+ const eos = new Set(tm.config.eosTokenIds);
37
+ const out: number[] = [];
38
+ for (let i = 0; i < maxTokens; i++) {
39
+ const lg = tm.model.forward([last], cache);
40
+ const am = ops.argmaxAxis(lg, -1); // GPU argmax (mlx-lm greedy: lowest-index tie-break)
41
+ lg.dispose();
42
+ const best = ops.itemUint32(am);
43
+ am.dispose();
44
+ if (i % 256 === 0) clearCache(); // mlx-lm decode cadence
45
+ if (eos.has(best)) break; // mlx-lm halts on EOS and excludes it from the output
46
+ out.push(best);
47
+ last = best;
48
+ }
49
+ return tm.tokenizer.decode(out, true);
50
+ } finally {
51
+ for (const c of cache) c.dispose();
52
+ }
53
+ }
54
+
55
+ /** Complete `body` via the model's real quantized-KV path. Greedy by default;
56
+ * pass `opts.sampler` to drive temperature/HLG/etc. */
57
+ async function generateTextNative(tm: TaskModel, body: string, opts: GenOpts = {}): Promise<string> {
58
+ const maxTokens = opts.maxTokens ?? 256;
59
+ const enableThinking = opts.enableThinking ?? runtimeValue("MLX_BUN_EVAL_THINK") === "1";
60
+ const templated = opts.useChat !== false && tm.template !== null;
61
+ const text = templated
62
+ ? tm.template!.render([{ role: "user", content: body }], { addGenerationPrompt: true, enableThinking })
63
+ : body;
64
+ // The chat template ALREADY emits the BOS; encoding it with add_special_tokens on
65
+ // would prepend a SECOND BOS (<s><s>…), corrupting generation. Raw bodies (no
66
+ // template) get the BOS added as normal.
67
+ const ids = tm.tokenizer.encode(text, /* addSpecialTokens */ !templated);
68
+
69
+ // optiq's PUBLISHED eval generates with a FULL-PRECISION KV cache — its eval calls
70
+ // plain mlx_lm.generate (kv-quant lives in the serving runtime, not the eval). So
71
+ // for like-for-like parity we default the eval to UNQUANTIZED KV; MMLU's argmax
72
+ // path was already unquantized, which is why it matched while generation didn't.
73
+ // MLX_BUN_EVAL_KV_QUANT=1 generates through the model's quantized KV (serving
74
+ // config); an explicit opts.kvScheme (cli generate's resolved tier route) wins.
75
+ const envKv: GenOpts["kvScheme"] | undefined =
76
+ (runtimeValue("MLX_BUN_EVAL_KV_QUANT") === "1" && tm.config.kvQuant?.length)
77
+ ? { kvConfig: tm.config.kvQuant, quantizedKvStart: 0 } : undefined;
78
+ const kvScheme = opts.kvScheme ?? envKv;
79
+ const kvActive = !!(kvScheme && (kvScheme.kvBits || kvScheme.kvConfig?.length || kvScheme.turboQuant));
80
+
81
+ // Parity default — greedy + full-precision KV + no sampler arm: decode bit-exactly
82
+ // via the raw forward (matches mlx-lm token-for-token). Sampler arms / kv-quant fall
83
+ // through to the product generate(). DiffusionGemma is non-autoregressive (no AR
84
+ // forward) — always go through generate(), which routes it to the denoising engine.
85
+ const isDiffusion = tm.model instanceof DiffusionGemmaModel;
86
+ if (!isDiffusion && !kvActive && !opts.sampler && !tm.samplerOverride) {
87
+ return greedyDecodeBitExact(tm, ids, maxTokens);
88
+ }
89
+ const gen = generate(tm.model, ids, {
90
+ maxTokens,
91
+ temperature: 0, // greedy default — deterministic head-to-head arms
92
+ adapters: tm.activeAdapters ?? [], // per-request activation of the mounted eval-adapter
93
+ ...(kvActive ? kvScheme : {}),
94
+ ...(opts.sampler ?? {}), // overrides temperature when provided
95
+ ...(tm.samplerOverride ?? {}), // arm override wins over the task's own sampler
96
+ });
97
+
98
+ const out: number[] = [];
99
+ for await (const { token } of gen) out.push(token);
100
+ return tm.tokenizer.decode(out, true);
101
+ }
102
+
103
+ export function createEvalCompletionClient(tm: TaskModel): CompletionClient<{ body: string; options?: GenOpts }, string> {
104
+ return { complete: ({ body, options }) => generateTextNative(tm, body, options) };
105
+ }
@@ -0,0 +1,45 @@
1
+ import type { MlxArray } from "../../mlx/array";
2
+ import * as ops from "../../mlx/ops";
3
+ import type { Cache } from "../../model/gemma4";
4
+ import { withResource } from "../../engine/resources";
5
+ import { leaseCacheStates } from "./state-views";
6
+
7
+ /** Append an already accepted span without intermediate vocabulary heads.
8
+ * Chunking changes the graph shape, so callers must qualify its numerics for
9
+ * their model. A verify round must use chunkSize=0: recurrent rollback records
10
+ * one forward per round. The complete append precedes emission/cancellation
11
+ * boundaries, preserving the cache-covered prefix on a consumer break.
12
+ */
13
+ export async function appendFillHidden(
14
+ forward: (ids: MlxArray, cache: Cache[]) => MlxArray | Promise<MlxArray>,
15
+ cache: Cache[],
16
+ ids: number[],
17
+ chunkSize: (cache: readonly Cache[]) => number,
18
+ ): Promise<MlxArray> {
19
+ const input = ops.fromInt32(ids, [1, ids.length]);
20
+ const hidden: MlxArray[] = [];
21
+ try {
22
+ for (let start = 0; start < ids.length;) {
23
+ const limit = chunkSize(cache);
24
+ if (!Number.isSafeInteger(limit) || limit < 1)
25
+ throw new Error("Model append chunk limit must be a positive integer");
26
+ const end = Math.min(ids.length, start + limit);
27
+ if (start === 0 && end === ids.length) return await forward(input, cache);
28
+ const part = input.slice([0, start], [1, end]);
29
+ try {
30
+ const h = await forward(part, cache);
31
+ hidden.push(h);
32
+ // Bound transient graphs while retaining all positions for an optional
33
+ // diagnostic trace. Normal generation projects only the last position.
34
+ withResource(leaseCacheStates(cache), (state) => ops.evalAll([h, ...state]));
35
+ } finally {
36
+ part.dispose();
37
+ }
38
+ start = end;
39
+ }
40
+ return ops.concatAxis(hidden, 1);
41
+ } finally {
42
+ for (const h of hidden) h.dispose();
43
+ input.dispose();
44
+ }
45
+ }
@@ -0,0 +1,32 @@
1
+ import type { MlxArray } from "../../mlx/array";
2
+ import * as ops from "../../mlx/ops";
3
+ import type { Cache } from "../../model/gemma4-base";
4
+ import { fullRowPadding } from "../../model/full-prefill-row";
5
+ import { disposeResources } from "../../engine/resources";
6
+
7
+ /** Assemble full-attention tensors at the model's B while storage retains
8
+ * per-row formats and positions. Returned arrays own their captured state. */
9
+ export function appendFullKvRows(rows: readonly Cache[], leftPad: readonly number[], width: number,
10
+ k: MlxArray, v: MlxArray, deferredValues = false): [MlxArray, MlxArray] {
11
+ const parts: [MlxArray, MlxArray][] = [], padded: MlxArray[] = [];
12
+ try {
13
+ for (const [index, row] of rows.entries()) {
14
+ using kr = k.slice([index, 0, 0, 0], [index + 1, k.shape[1]!, k.shape[2]!, k.shape[3]!]);
15
+ using vr = v.slice([index, 0, 0, 0], [index + 1, v.shape[1]!, v.shape[2]!, v.shape[3]!]);
16
+ parts.push(deferredValues && row.rotatedValueAttention
17
+ ? row.rotatedValueAttention.updateAndFetchDeferredV(kr, vr) : row.updateAndFetch(kr, vr));
18
+ }
19
+ const combine = (field: 0 | 1) => {
20
+ const views = parts.map((part, row) => {
21
+ const a = part[field], before = leftPad[row]! - fullRowPadding(rows[row]!), after = width - before - a.shape[2]!;
22
+ if (!before && !after) return a;
23
+ using prefix = ops.zeros([1, a.shape[1]!, before, a.shape[3]!], a.dtype);
24
+ using suffix = ops.zeros([1, a.shape[1]!, after, a.shape[3]!], a.dtype);
25
+ const result = ops.concatAxis([prefix, a, suffix], 2); padded.push(result); return result;
26
+ });
27
+ return views.length === 1 ? ops.contiguous(views[0]!) : ops.concatAxis(views, 0);
28
+ };
29
+ const keys = combine(0);
30
+ try { return [keys, combine(1)]; } catch (error) { keys.dispose(); throw error; }
31
+ } finally { disposeResources([...parts.flat(), ...padded]); }
32
+ }