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,242 @@
1
+ import type { SpecRunServices } from "../../spec/serve-loop";
2
+ import { cleanupFailure, disposeResources, ownResource } from "../../engine/resources";
3
+ import { bindGeneration } from "../../generate";
4
+ import type { RuntimeModel } from "../../model/factory";
5
+ import { Qwen35Model } from "../../model/qwen3_5";
6
+ import type { Cache } from "../../model/gemma4";
7
+ import type { MlxPrefixCache } from "./checkpoint-state";
8
+ import type { SsdCacheStore } from "../../ssd-cache";
9
+ import type { DraftProvider } from "../../spec/source";
10
+ import type { SerialRun, Vision } from "../../serve/generation-gateway";
11
+ import { bindContinuationPolicy } from "./continuation";
12
+ import { runtimeConfig, withRuntimeConfig, type RuntimeConfig } from "../../runtime-config";
13
+ import { bindLegacySpeculativeModel } from "./speculative";
14
+
15
+ /** Native serial execution depends on this bound port, never a model union.
16
+ * Weights remain borrowed. The gateway supplies the exclusive runtime lease. */
17
+ export interface MlxSerialBinding {
18
+ readonly runtime: RuntimeConfig;
19
+ readonly generate: ReturnType<typeof bindGeneration>;
20
+ readonly speculate?: (
21
+ promptIds: Parameters<SerialRun>[0], options: Parameters<SerialRun>[1],
22
+ onToken: Parameters<SerialRun>[2], services?: SpecRunServices,
23
+ ) => ReturnType<SerialRun>;
24
+ makeCache(): Cache[];
25
+ enterMedia?(vision?: Vision): () => void;
26
+ }
27
+
28
+ /** Family-specific context is bound once at the compatibility boundary. */
29
+ export function bindLegacySerialModel(
30
+ model: RuntimeModel,
31
+ draft?: { provider: DraftProvider; numDraftTokens: number },
32
+ ): MlxSerialBinding {
33
+ const speculative = draft ? bindLegacySpeculativeModel(model, draft.provider) : undefined;
34
+ return {
35
+ runtime: runtimeConfig(),
36
+ generate: bindGeneration(model),
37
+ makeCache: model.makeCache.bind(model),
38
+ ...(speculative && draft ? { speculate: (async (prompt, options, onToken, services) => {
39
+ const { specRun } = await import("../../spec/serve-loop");
40
+ return specRun(speculative, draft.numDraftTokens, prompt, options, onToken, services);
41
+ }) satisfies NonNullable<MlxSerialBinding["speculate"]> } : {}),
42
+ ...(model instanceof Qwen35Model ? { enterMedia(vision?: Vision) {
43
+ const previous = model.mrope;
44
+ if (vision?.mrope) model.mrope = vision.mrope;
45
+ return () => { model.mrope = previous; };
46
+ } } : {}),
47
+ };
48
+ }
49
+
50
+ export interface MlxSerialServices {
51
+ readonly promptCache: MlxPrefixCache & { readonly maxBytes?: number };
52
+ readonly checkpoints: Pick<SsdCacheStore, "findGenerationCheckpoint" | "restore" |
53
+ "storeGenerationCheckpoint" | "removeGenerationCheckpoints"> | null;
54
+ readonly checkpointEveryTokens?: number;
55
+ readonly checkpointPersistence?: import("./continuation-persistence").ContinuationPersistence;
56
+ /** Artifact, implementation, state ABI and codec identity captured at load. */
57
+ readonly identity: unknown;
58
+ adapterNamespace(adapters: string[]): string;
59
+ cloneState(caches: Cache[]): Cache[];
60
+ }
61
+
62
+ /** Cache lookup, replay, prompt snapshots and checkpoint persistence belong to
63
+ * native execution. HTTP only composes this service and frames its output.
64
+ * The caller transfers prepared native inputs; caches created/taken here are
65
+ * owned until returned to the prefix store or disposed after execution. */
66
+ export function createMlxSerialExecutor(binding: MlxSerialBinding, services: MlxSerialServices): SerialRun {
67
+ const { promptCache, checkpoints: ssdStore } = services;
68
+ return (promptIds, options, onToken, vision, trace, execution) => withRuntimeConfig(binding.runtime, async () => {
69
+ let releaseContinuation: (() => void) | undefined;
70
+ let caches: Cache[] = [];
71
+ let retain: (() => void) | undefined;
72
+ let closeMedia: (() => void) | undefined;
73
+ const cleanup = ownResource(null, () => disposeResources([
74
+ { dispose() {
75
+ disposeResources(caches);
76
+ retain?.(); // backing release requires successful cache disposal
77
+ } },
78
+ { dispose: () => closeMedia?.() },
79
+ { dispose: () => releaseContinuation?.() },
80
+ ...[options.grammar, vision?.embeddings, vision?.imageMask,
81
+ vision?.multimodalMask, options.visionPixels].filter((value) => value != null),
82
+ ]));
83
+ try {
84
+ options.signal?.throwIfAborted();
85
+ if (!execution) throw new Error("serial execution requires a resolved plan");
86
+ if (execution.method === "speculative") {
87
+ if (!binding.speculate) throw new Error("resolved speculation requires a bound verifier");
88
+ return await binding.speculate(promptIds, options, onToken, {
89
+ ...(binding.runtime.flag("MLX_BUN_MTP_PROMPT_CACHE", true) && services.promptCache.maxBytes !== 0
90
+ ? { prefixCache: services.promptCache } : {}),
91
+ cloneState: services.cloneState,
92
+ cacheNamespace: options.adapters?.length ? services.adapterNamespace(options.adapters) : "",
93
+ });
94
+ }
95
+ // Cache entries are adapter-specific: KV computed under one adapter
96
+ // must never seed another's (or the base's) prefill.
97
+ const cacheNs = options.adapters?.length ? services.adapterNamespace(options.adapters) : "";
98
+ // Paged-KV request scope (docs/design/kv-cache.md): media
99
+ // prompts (bidir overlay) and LoRA-adapter requests are v1 non-goals —
100
+ // they run the PLAIN cache path even under --paged-kv (scope the flag
101
+ // per request, never 400). Effective value computed ONCE so the
102
+ // prompt-cache bypass below and the generate() options can't disagree.
103
+ const pagedKv = execution.pagedKv ? options.pagedKv : undefined;
104
+ // Paged requests bypass the prompt cache entirely (v1 non-goal:
105
+ // PagedKVCache has no cloneKvCaches/restore path — the vision
106
+ // precedent). Fresh caches per request, disposed on completion.
107
+ const skipPromptCache = !execution.promptCache;
108
+ const checkpointEvery = services.checkpointEveryTokens;
109
+ const checkpointEligible = execution.checkpoint;
110
+ const continuation = checkpointEligible ? bindContinuationPolicy({ store: ssdStore!,
111
+ restore: entry => ssdStore!.restore(entry, binding), prompt: promptIds,
112
+ options, execution, identity: services.identity, namespace: cacheNs, persistence: services.checkpointPersistence }) : null;
113
+ releaseContinuation = continuation?.release;
114
+ const checkpointKey = continuation?.key;
115
+ // Both tiers in one call (Layer 0): take() prefers a strictly-longer
116
+ // SSD prefix, restores it zero-copy, and trims — see PromptCache.take.
117
+ const closeCacheLookup = trace?.begin("cache.lookup_restore", {
118
+ mechanism: "serial",
119
+ bypassed: skipPromptCache,
120
+ });
121
+ const checkpoint = continuation?.restore();
122
+ caches = checkpoint?.caches ?? [];
123
+ const resuming = Boolean(checkpoint);
124
+ const generationPromptIds = checkpoint?.cacheTokens ?? promptIds;
125
+ const entry = skipPromptCache || resuming
126
+ ? null
127
+ : promptCache.take(promptIds, cacheNs);
128
+ if (entry) { caches = entry.caches; retain = entry.retain; }
129
+ if (!checkpoint && !entry) caches = binding.makeCache();
130
+ closeCacheLookup?.();
131
+ // Prompt-boundary snapshot (the multi-turn agent fix, 2026-07-04): the
132
+ // prompt+gen entry put() below is UNTRIMMABLE at context > sliding
133
+ // window (wrapped rings) and under quantized KV (mid-group), so any
134
+ // decode→encode roundtrip drift in the reply the client sends back
135
+ // turns the next turn into a total miss (measured: 12B turn-2 TTFT
136
+ // 8.9 s instead of ~0.2 s). A prompt-ONLY entry is always an exact
137
+ // prefix of the next turn's rendering regardless of reply drift.
138
+ // Zero-copy (cloneKvCaches = slice views); only for substantial cold
139
+ // prefills, where the re-prefill it saves is worth an extra entry.
140
+ // The oracle invariant (mlx-lm insert_segments): a trim-free STRICT
141
+ // prefix of the prompt exists for EVERY substantial request — cap the
142
+ // boundary at len-1 so even a stableLen == len prompt (e4b: the
143
+ // template tail survives the probe render) snapshots prompt[:-1]. An
144
+ // exact repeat then matches with trimNeeded == 0, bypassing
145
+ // isTrimmable() entirely — the only reuse path a wrapped ring has.
146
+ const boundary = Math.min(options.snapshotAt ?? promptIds.length, promptIds.length - 1);
147
+ // Re-snapshot on EVERY substantial request whose stable boundary extends
148
+ // past the cached prefix; the clone is zero-copy views, so re-putting
149
+ // is ~free.
150
+ const snapshotBoundary =
151
+ !skipPromptCache && !resuming && boundary >= 256 &&
152
+ boundary > (entry?.tokens.length ?? 0);
153
+ closeMedia = binding.enterMedia?.(vision);
154
+ if (resuming) {
155
+ const replay = generationPromptIds.slice(promptIds.length);
156
+ console.log(
157
+ `[generation-checkpoint] resuming ${checkpointKey} at ` +
158
+ `${replay.length} emitted tokens`,
159
+ );
160
+ for (const token of replay) {
161
+ options.signal?.throwIfAborted();
162
+ if ((await onToken(token)) === false)
163
+ throw new Error("saved generation prefix triggered a terminal stop while replaying");
164
+ }
165
+ }
166
+ const gen = binding.generate(generationPromptIds, {
167
+ ...options,
168
+ decodePolicy: execution,
169
+ ...(resuming ? { seed: checkpoint!.seed } : {}),
170
+ fill: execution.fill ? options.fill : undefined,
171
+ pagedKv, // request-scoped (undefined strips the server-wide flag)
172
+ cache: caches,
173
+ ...(resuming
174
+ ? {
175
+ initialPendingToken: checkpoint!.pendingToken,
176
+ initialGeneratedTokens: checkpoint!.generatedTokens,
177
+ originalPromptTokens: promptIds.length,
178
+ }
179
+ : {}),
180
+ ...(checkpointEligible
181
+ ? {
182
+ checkpointEveryTokens: checkpointEvery,
183
+ onDecodeCheckpoint: async (state: {
184
+ cacheTokens: number[];
185
+ caches: Cache[];
186
+ generatedTokens: number;
187
+ pendingToken: number;
188
+ }) => {
189
+ if (services.checkpointPersistence)
190
+ continuation!.captureOwned({ ...state, caches: services.cloneState(state.caches) });
191
+ else await continuation!.capture(state);
192
+ },
193
+ }
194
+ : {}),
195
+ ...(snapshotBoundary
196
+ ? {
197
+ // snapshotAt MUST travel with the hook: generate() splits the
198
+ // prefill at exactly this many tokens and fires the hook while
199
+ // the caches hold exactly that prefix — putting boundary
200
+ // tokens against caches at any other offset is silent KV
201
+ // corruption.
202
+ snapshotAt: boundary,
203
+ onPrefillDone: () => {
204
+ try {
205
+ const snapshot = ownResource(services.cloneState(caches), disposeResources);
206
+ try {
207
+ promptCache.put(promptIds.slice(0, boundary), snapshot.borrow(), cacheNs);
208
+ snapshot.transfer();
209
+ } finally { snapshot.close(); }
210
+ } catch (e) {
211
+ console.warn(`prompt-boundary snapshot skipped: ${(e as Error).message}`);
212
+ }
213
+ },
214
+ }
215
+ : {}),
216
+ ...(vision
217
+ ? {
218
+ promptEmbeddings: vision.embeddings,
219
+ ...(vision.imageMask ? { imageMask: vision.imageMask } : {}),
220
+ ...(vision.multimodalMask ? { multimodalMask: vision.multimodalMask } : {}),
221
+ }
222
+ : {}),
223
+ }, { trace, mechanism: "serial" });
224
+ for await (const t of gen) {
225
+ if ((await onToken(t.token, t.logprobs)) === false) break;
226
+ }
227
+ const s = gen.stats!; // set on completion AND on early break
228
+ continuation?.complete();
229
+ if (!skipPromptCache) {
230
+ // put() fires onPut → the debounced write-behind SSD snapshot
231
+ // (wired below), covering the batch lane's puts too.
232
+ promptCache.put(s.cacheTokens, caches, cacheNs, retain);
233
+ caches = []; retain = undefined; // ownership returned to the prefix store
234
+ }
235
+ return s;
236
+ } catch (error) {
237
+ cleanupFailure(error, () => cleanup.close());
238
+ } finally {
239
+ cleanup.close();
240
+ }
241
+ });
242
+ }
@@ -0,0 +1,359 @@
1
+ import {captureSpeculativeOptions, speculativePrefixNamespace} from "../../spec/cache-identity";
2
+ import type { RuntimeModel } from "../../model/factory";
3
+ import type { GenerateOptions } from "../../generate";
4
+ import type { DraftProvider, DraftRowGroup, DraftPrefillGroup, DraftRowCheckpoint } from "../../spec/source";
5
+ import { makeSampler, makeStepSampler, readStepExtras, type NumberStepSampler, type Sampler } from "../../sampler";
6
+ import { MlxArray } from "../../mlx/array";
7
+ import * as ops from "../../mlx/ops";
8
+ import { clearCache } from "../../mlx/ffi";
9
+ import { applyStateChanges, cleanupFailure, disposeResources } from "../../engine/resources";
10
+ import { cloneKvCaches } from "../../kv-store";
11
+ import type { Cache } from "../../model/gemma4-base";
12
+ import { targetCacheLayout } from "./cache-layout";
13
+ import { MlxStateRows } from "./state-rows";
14
+ import { bindLegacySpeculativeModel, type MlxSpeculativeBinding } from "./speculative";
15
+ import { bindLegacyDraftTarget } from "./draft-target";
16
+ import { bindRowCacheRollback } from "./rollback";
17
+ import { createKvMaintenance } from "./kv-maintenance";
18
+ import { disposeAttachments, type CheckpointAttachment } from "./checkpoint-state";
19
+ import { advanceSpeculativeOutputs } from "./speculative-round";
20
+ import { MlxPrefillRows, type MlxPrefillState } from "./prefill-rows";
21
+ import type { MlxGroupMethodHost, MlxGroupMethodRequest, MlxGroupPreparation, MlxGroupedMethod, Row } from "./batch-group";
22
+
23
+ interface RequestState {
24
+ sampling: NumberStepSampler;
25
+ draftSampling: Sampler;
26
+ retain?: () => void;
27
+ namespace: string;
28
+ prefixLength: number;
29
+ /** Generated inputs retained by committed target/draft rounds. The pending
30
+ * correction is excluded until a later round actually processes it. */
31
+ processed?: number[];
32
+ }
33
+
34
+ /** Binding owns graph/layout selection. The executor receives only the method
35
+ * key and lifecycle; sampling, checkpoints and numerical state remain ports. */
36
+ export function bindSpeculativeGroupRequests(model: RuntimeModel, provider: DraftProvider, depth: number) {
37
+ const binding = bindLegacySpeculativeModel(model, provider);
38
+ return (input: GenerateOptions): MlxGroupMethodRequest => {
39
+ const options = captureSpeculativeOptions(input);
40
+ return {
41
+ key: JSON.stringify(["speculative", provider.id, depth, options.kvBits ?? null,
42
+ options.kvGroupSize ?? 64, options.quantizedKvStart ?? null, options.turboQuant ?? null,
43
+ ...(options.kvConfig?.length ? [options.kvConfig] : [])]),
44
+ data: options,
45
+ open: host => new SpeculativeGroup(host, model, provider, binding, depth),
46
+ };
47
+ };
48
+ }
49
+
50
+ class SpeculativeGroup implements MlxGroupedMethod {
51
+ #target: MlxStateRows<ReturnType<typeof targetCacheLayout>> | null = null;
52
+ #draft: DraftRowGroup | null = null;
53
+ readonly #requests = new Map<Row, RequestState>();
54
+ #steps = 0;
55
+
56
+ constructor(readonly host: MlxGroupMethodHost, readonly model: RuntimeModel,
57
+ readonly provider: DraftProvider, readonly binding: MlxSpeculativeBinding, readonly depth: number) {}
58
+
59
+ prepare(first: Row): MlxGroupPreparation {
60
+ const method = this;
61
+ const options = first.req.method!.data as GenerateOptions;
62
+ const maintain = createKvMaintenance(options);
63
+ interface Preparation extends MlxPrefillState {
64
+ request: RequestState;
65
+ end: number;
66
+ chunkSize: number;
67
+ pendingPrompt: boolean;
68
+ boundary: number;
69
+ transferred: boolean;
70
+ closed: boolean;
71
+ }
72
+ interface Ready {
73
+ row: Row;
74
+ request: RequestState;
75
+ caches: Cache[];
76
+ draft: DraftRowCheckpoint;
77
+ retain?: () => void;
78
+ transferred: boolean;
79
+ }
80
+ let prefix: DraftPrefillGroup | undefined;
81
+ let context: MlxArray | null = null;
82
+ const ready: Ready[] = [];
83
+ const closeReady = (state: Ready) => {
84
+ const caches = state.caches.splice(0), attachments = [state.draft.attachment];
85
+ const release = state.retain; state.retain = undefined;
86
+ disposeResources([...caches, { dispose: () => disposeAttachments(attachments) },
87
+ { dispose: () => release?.() }, ...(!state.transferred ? [state.request.sampling] : [])]);
88
+ };
89
+ const capture = (state: Preparation, snapshot: () => Cache[], index: number) => {
90
+ let caches: Cache[] = [], attachments: CheckpointAttachment[] = [];
91
+ try {
92
+ caches = snapshot(); attachments.push(prefix!.capture(index).attachment);
93
+ method.host.promptCache!.put(state.row.req.promptIds.slice(0, state.pos), caches,
94
+ state.request.namespace, undefined, attachments);
95
+ caches = []; attachments = [];
96
+ } finally { disposeResources([...caches, { dispose: () => disposeAttachments(attachments) }]); }
97
+ };
98
+ const target = new MlxPrefillRows<Preparation>({
99
+ maintain,
100
+ open(row) {
101
+ const options = row.req.method!.data as GenerateOptions;
102
+ row.spec = { drafted: 0, accepted: 0, targetCalls: 0, rejected: 0, rounds: 0,
103
+ acceptanceLengths: [], tokensPerForward: 0, forwardsSaved: 0, draftedByPos: [], acceptedByPos: [] };
104
+ const sampler = makeSampler(options);
105
+ const request: RequestState = { draftSampling: sampler, namespace: "", prefixLength: 0,
106
+ processed: method.host.promptCache ? [] : undefined,
107
+ sampling: makeStepSampler(options, { tokenRepresentation: "number", grammarWait: "before-sample",
108
+ historyUpdate: "after-sample", initialHistory: row.req.promptIds, acceptGrammar: true,
109
+ eosTokenIds: row.req.eosTokenIds, captureSelectedLogprob: options.logprobs === true,
110
+ captureTopLogprobs: options.topLogprobs }) };
111
+ let caches: Cache[] = [], retained: (() => void) | undefined;
112
+ let appended = false;
113
+ const previous = prefix?.rowCount ?? 0;
114
+ try {
115
+ caches = method.binding.makeCache();
116
+ prefix ??= method.provider.grouped!.openPrefill({ target: bindLegacyDraftTarget(method.model, caches), checkpoints: [] });
117
+ const prompt = row.req.promptIds;
118
+ const pendingPrompt = prefix.prefillMode !== "full" && method.binding.prefillTailSplit && prompt.length > 1;
119
+ request.prefixLength = prompt.length - Number(pendingPrompt);
120
+ request.namespace = speculativePrefixNamespace(prefix.namespace, row.cacheNamespace ?? "", options);
121
+ const hit = method.host.promptCache?.take(prompt, request.namespace);
122
+ if (hit) {
123
+ try {
124
+ disposeResources(caches); caches = hit.caches.splice(0);
125
+ retained = hit.retain; hit.retain = undefined;
126
+ prefix.append([{ processedTokens: hit.tokens.length, attachment: hit.attachments![0]! }]);
127
+ appended = true;
128
+ // Target membership later releases backing leases after its own
129
+ // copies resolve. Settle companion copies under the same lease.
130
+ if (retained) prefix.materialize();
131
+ row.cachedTokens = hit.tokens.length;
132
+ } finally { disposeResources([...hit.caches, { dispose: () => disposeAttachments(hit.attachments) },
133
+ { dispose: () => hit.retain?.() }]); }
134
+ } else { prefix.append([null]); appended = true; }
135
+ return { row, request, solo: caches, retain: retained, pos: row.cachedTokens,
136
+ end: request.prefixLength, pendingPrompt, transferred: false, closed: false,
137
+ chunkSize: options.prefillChunkSize ?? method.host.prefillChunkSize,
138
+ boundary: method.host.promptCache ? Math.min(row.req.snapshotAt ?? prompt.length, prompt.length - 1) : -1 };
139
+ } catch (error) {
140
+ return cleanupFailure(error, () => disposeResources([
141
+ { dispose: () => { if (appended) prefix!.filterRows(Array.from({ length: previous }, (_, i) => i)); } },
142
+ ...caches, request.sampling, { dispose: () => retained?.() },
143
+ ]));
144
+ }
145
+ },
146
+ plan(state) {
147
+ const end = Math.min(state.pos + state.chunkSize, state.end, state.boundary > state.pos ? state.boundary : Infinity);
148
+ return { start: state.pos, end, kind: end === state.end ? "final" : "drain",
149
+ snapshot: end === state.boundary && end > state.row.cachedTokens, batchYield: true, maintain: true };
150
+ },
151
+ async forward(ids, caches) {
152
+ const taps = prefix!.tapLayers;
153
+ const output = await method.binding.forward(ids, caches, taps.length ? [...taps] : undefined);
154
+ context = output.ctxML;
155
+ return output.hidden;
156
+ },
157
+ afterForward(ids, _caches, _states, hidden) {
158
+ // Tapped providers consume their captured layers; assistant drafting
159
+ // consumes the true target hidden. Other providers ignore context.
160
+ const captured = context; context = null;
161
+ try {
162
+ const pending = prefix!.prefill(ids, captured ?? hidden);
163
+ if (pending) return pending.finally(() => captured?.dispose());
164
+ } catch (error) { captured?.dispose(); throw error; }
165
+ captured?.dispose();
166
+ },
167
+ project(hidden, _caches, completed) {
168
+ if (completed.every(state => state.pendingPrompt)) return null;
169
+ using last = hidden.slice([0, hidden.shape[1]! - 1, 0], [hidden.shape[0]!, hidden.shape[1]!, hidden.shape[2]!]);
170
+ return method.binding.projectLogits(last);
171
+ },
172
+ checkpoint: capture,
173
+ async complete(state, logits, index) {
174
+ const row = state.row;
175
+ row.spec!.targetCalls++;
176
+ if (state.pendingPrompt) { row.current = row.req.promptIds[state.end]!; row.generated = row.sampled = 0; }
177
+ else {
178
+ using scores = ops.reshape(logits!, [1, logits!.shape.at(-1)!]);
179
+ const sampled = await state.request.sampling.sample(scores, 0);
180
+ row.current = sampled.token; row.generated = row.sampled = 1;
181
+ if (row.req.eosTokenIds.includes(row.current)) {
182
+ if (method.host.promptCache) capture(state, () => cloneKvCaches(state.solo), index);
183
+ disposeResources(state.solo.splice(0)); state.retain?.(); state.retain = undefined;
184
+ method.host.finish(row, "stop"); return;
185
+ }
186
+ const more = await method.host.publish(row, row.current, readStepExtras(sampled.extras));
187
+ if (more === false || row.req.grammar?.isTerminated || row.generated >= row.req.maxTokens) {
188
+ if (method.host.promptCache && !row.req.signal?.aborted) capture(state, () => cloneKvCaches(state.solo), index);
189
+ disposeResources(state.solo.splice(0)); state.retain?.(); state.retain = undefined;
190
+ method.host.finish(row, more === false || row.req.grammar?.isTerminated ? "stop" : "length"); return;
191
+ }
192
+ }
193
+ ready.push({ row, request: state.request, caches: state.solo, retain: state.retain,
194
+ draft: prefix!.capture(index), transferred: false });
195
+ state.transferred = true;
196
+ },
197
+ reject(row, error) { row.reject(error); },
198
+ close(state) {
199
+ if (state.closed) return;
200
+ state.closed = true;
201
+ if (!state.transferred) state.request.sampling.dispose();
202
+ },
203
+ filterRows(keep) { prefix!.filterRows(keep); },
204
+ dispose() {
205
+ const held = [prefix, context].filter(value => value != null);
206
+ prefix = undefined; context = null; disposeResources(held);
207
+ },
208
+ });
209
+ const preparation: MlxGroupPreparation = {
210
+ get rows() { return [...target.rows, ...ready.map(state => state.row)]; },
211
+ get canAdmit() { return target.canAdmit; },
212
+ get tokenWeight() { return target.tokenWeight + ready.reduce((sum, state) => sum + state.row.promptTokens - state.row.cachedTokens, 0); },
213
+ admit: row => target.admit(row),
214
+ advance: async () => {
215
+ // Publication returns to scheduling before prepared state joins active
216
+ // decode. This preserves the first-output flush boundary at every B.
217
+ if (ready.length) {
218
+ while (ready.length) {
219
+ const state = ready[0]!;
220
+ try {
221
+ if (state.row.req.signal?.aborted) {
222
+ state.row.reject(state.row.req.signal.reason);
223
+ continue;
224
+ }
225
+ maintain.prepareBatch?.(state.caches);
226
+ this.#target ??= new MlxStateRows(state.caches.map(targetCacheLayout));
227
+ this.#draft ??= this.provider.grouped!.open({ target: bindLegacyDraftTarget(this.model, this.#target.caches),
228
+ checkpoints: [], sampling: { sample: (lp, steps) => this.#sampleDraftRows(lp, steps) } });
229
+ applyStateChanges([() => this.#target!.prepareAppend(state.caches),
230
+ () => this.#draft!.prepareAppend([state.draft]), () => ({ commit: () => {
231
+ state.request.retain = state.retain; state.retain = undefined;
232
+ this.#requests.set(state.row, state.request); state.transferred = true;
233
+ this.host.join(state.row);
234
+ }, dispose() {} })]);
235
+ } finally { ready.shift(); closeReady(state); }
236
+ }
237
+ return target.rows.length === 0;
238
+ }
239
+ const done = await target.advance();
240
+ return done && ready.length === 0;
241
+ },
242
+ dispose() {
243
+ const held = ready.splice(0);
244
+ disposeResources([target, ...held.map(state => ({ dispose: () => closeReady(state) }))]);
245
+ },
246
+ };
247
+ try { target.admit(first); return preparation; }
248
+ catch (error) { return cleanupFailure(error, () => preparation.dispose()); }
249
+ }
250
+
251
+ #sampleDraftRows(logprobs: MlxArray, steps: readonly number[]): MlxArray {
252
+ const tokens: MlxArray[] = [];
253
+ try {
254
+ for (let row = 0; row < this.host.rows.length; row++) {
255
+ using scores = logprobs.slice([row, 0], [row + 1, logprobs.shape[1]!]);
256
+ tokens.push(this.#requests.get(this.host.rows[row]!)!.draftSampling(scores, steps[row]!));
257
+ }
258
+ return ops.concatAxis(tokens, 0);
259
+ } finally { disposeResources(tokens); }
260
+ }
261
+
262
+ async advance(): Promise<void> {
263
+ const active = [...this.host.rows];
264
+ const live = active.flatMap((row, index) => row.req.signal?.aborted ? [] : [index]);
265
+ if (live.length !== active.length) {
266
+ for (const row of active) if (row.req.signal?.aborted) row.reject(row.req.signal.reason);
267
+ this.host.filterRows(live);
268
+ }
269
+ const rows = [...this.host.rows];
270
+ if (!rows.length) return;
271
+ const depth = Math.min(this.depth, Math.max(...rows.map(row => row.req.maxTokens - row.generated)));
272
+ const halted = new Set<Row>();
273
+ const pin = this.binding.pinVerify?.();
274
+ try {
275
+ const completed = await advanceSpeculativeOutputs(rows.map(row => ({ pending: row.current,
276
+ step: row.sampled, remaining: row.req.maxTokens - row.generated, eosTokenIds: row.req.eosTokenIds,
277
+ sampling: this.#requests.get(row)!.sampling, grammarDone: () => row.req.grammar?.isTerminated === true,
278
+ output: { commit: async (ids, metadata) => {
279
+ row.generated++;
280
+ const more = await this.host.publish(row, ids[0]!, metadata?.[0]);
281
+ if (more === false) { halted.add(row); return false; }
282
+ } },
283
+ })), depth, this.#draft!, {
284
+ transaction: bindRowCacheRollback(this.#target!.caches, rows.length),
285
+ forward: async ids => {
286
+ const taps = this.#draft!.tapLayers;
287
+ const result = await this.binding.forward(ids, this.#target!.caches, taps.length ? [...taps] : undefined);
288
+ const context = result.ctxML ?? result.hidden;
289
+ try { return { logits: this.binding.projectLogits(result.hidden), context }; }
290
+ catch (error) { context.dispose(); throw error; }
291
+ finally { if (result.ctxML) result.hidden.dispose(); }
292
+ },
293
+ });
294
+ const keep: number[] = [];
295
+ for (let index = 0; index < rows.length; index++) {
296
+ const row = rows[index]!, output = completed.outputs[index]!, round = completed.rounds[index]!;
297
+ const request = this.#requests.get(row)!;
298
+ if (output.kind !== "failed" && output.kind !== "cancelled")
299
+ request.processed?.push(row.current, ...round.drafts.slice(0, output.accepted));
300
+ const stats = row.spec!;
301
+ stats.drafted += round.drafts.length;
302
+ stats.accepted += round.acceptance.accepted;
303
+ stats.rejected = stats.drafted - stats.accepted;
304
+ stats.rounds!++; stats.targetCalls++;
305
+ stats.acceptanceLengths!.push(round.acceptance.accepted);
306
+ for (let p = 0; p < round.drafts.length; p++) stats.draftedByPos![p] = (stats.draftedByPos![p] ?? 0) + 1;
307
+ for (let p = 0; p < round.acceptance.accepted; p++) stats.acceptedByPos![p] = (stats.acceptedByPos![p] ?? 0) + 1;
308
+ row.sampled += round.acceptance.emitted.length + Number(round.acceptance.sawEos);
309
+ // A consumer may stop inside a verified burst before its terminal EOS.
310
+ if (output.kind === "stop" && !halted.has(row) && round.acceptance.sawEos &&
311
+ output.generated === round.acceptance.emitted.length && row.generated < row.req.maxTokens)
312
+ row.generated++;
313
+ stats.tokensPerForward = row.generated / stats.rounds!;
314
+ stats.forwardsSaved = Math.max(0, row.generated - 1 - stats.rounds!);
315
+ if (output.kind === "continue") { row.current = output.pending; keep.push(index); }
316
+ else if (output.kind === "failed") row.reject(output.error);
317
+ else if (output.kind === "cancelled") row.reject(row.req.signal?.reason ?? new Error(output.reason));
318
+ else {
319
+ if (!row.req.signal?.aborted) this.#publishCheckpoint(row, index, request);
320
+ this.host.finish(row, output.kind);
321
+ }
322
+ }
323
+ if (keep.length !== rows.length) this.host.filterRows(keep);
324
+ if (++this.#steps % 256 === 0) clearCache();
325
+ } finally { pin?.close(); }
326
+ }
327
+
328
+ /** The method aligns token coverage and captures its state. The cache owns
329
+ * RAM retention and eventual SSD persistence; no storage work is awaited. */
330
+ #publishCheckpoint(row: Row, index: number, request: RequestState): void {
331
+ if (!this.host.promptCache || !request.processed) return;
332
+ let target: Cache[] = [], attachments: CheckpointAttachment[] = [];
333
+ try {
334
+ target = this.#target!.extractRow(index);
335
+ attachments.push(this.#draft!.capture(index).attachment);
336
+ this.host.promptCache.put([...row.req.promptIds.slice(0, request.prefixLength), ...request.processed], target,
337
+ request.namespace, undefined, attachments);
338
+ target = []; attachments = [];
339
+ } finally { disposeResources([...target, { dispose: () => disposeAttachments(attachments) }]); }
340
+ }
341
+
342
+ filterRows(keep: readonly number[], _discard: boolean): void {
343
+ this.#target?.filterRows(keep); this.#draft?.filterRows(keep);
344
+ const retained = new Set(keep.map(index => this.host.rows[index]!));
345
+ const releases: { dispose(): void }[] = [];
346
+ for (const [row, request] of this.#requests) if (!retained.has(row)) {
347
+ this.#requests.delete(row);
348
+ releases.push(request.sampling, { dispose: () => request.retain?.() });
349
+ }
350
+ disposeResources(releases);
351
+ }
352
+
353
+ dispose(): void {
354
+ const resources = [...(this.#target ? [this.#target] : []), ...(this.#draft ? [this.#draft] : []),
355
+ ...[...this.#requests.values()].flatMap(request => [request.sampling, { dispose: () => request.retain?.() }])];
356
+ this.#target = null; this.#draft = null; this.#requests.clear();
357
+ disposeResources(resources);
358
+ }
359
+ }