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
@@ -17,6 +17,7 @@ import { MlxArray } from "../../mlx/array";
17
17
  import { Dtype } from "../../mlx/ffi";
18
18
  import * as ops from "../../mlx/ops";
19
19
  import type { Gemma4Model } from "../../model/gemma4";
20
+ import type { DraftProjection } from "../source";
20
21
  import { loadAdapterTensors } from "../../lora";
21
22
  import { writeShardedSafetensors, type NamedTensor } from "../../quantize/safetensors-writer";
22
23
  import { processLogits, sampleToken, KeyStream, type DSparkSampleConfig } from "./sample";
@@ -25,7 +26,7 @@ import { join } from "node:path";
25
26
 
26
27
  /** STS calibration (§3.2.1) — per-position confidence thresholds fit on real
27
28
  * (confidence, accepted) verify outcomes (src/spec/dspark/calibration.ts,
28
- * scripts/dspark-calibrate.ts). Consumed by the confidence-scheduled
29
+ * scripts/dspark.ts calibrate). Consumed by the confidence-scheduled
29
30
  * draft-length pruning (Alg 1, single-user form) in forwardInfer. Stored
30
31
  * inside DflashConfig so it round-trips through dspark.json for free.
31
32
  * ABSENT ⟹ no pruning — an uncalibrated checkpoint drafts exactly as before. */
@@ -73,6 +74,10 @@ export interface DflashTrainOut { draftLogits: MlxArray; conf: MlxArray }
73
74
  * positions — ABSENT when the caller passed collectLogits:false (the loop
74
75
  * never materialized/concatenated the per-position logits). */
75
76
  export interface DflashDraftBlock { tokens: number[]; conf: number[]; draftLogits?: MlxArray }
77
+ export interface DflashDraftRows { tokens: number[][]; conf: number[][]; draftLogits?: MlxArray; }
78
+ export interface DflashContextAttention {
79
+ attend(layer: number, query: MlxArray, keys: MlxArray, values: MlxArray, scale: number): MlxArray;
80
+ }
76
81
  export interface DflashDraftOpts {
77
82
  sample?: DSparkSampleConfig;
78
83
  keys?: KeyStream;
@@ -91,6 +96,8 @@ export interface DflashDraftOpts {
91
96
  * path runs its own verify lm-head and never reads it — pass false there
92
97
  * to skip the dead concat + a host-unrelated GPU alloc every round. */
93
98
  collectLogits?: boolean;
99
+ /** Return confidence telemetry; pruning still computes it when needed. */
100
+ collectConfidence?: boolean;
94
101
  }
95
102
 
96
103
  const CDT = Dtype.float32;
@@ -244,7 +251,7 @@ export class DflashDrafter {
244
251
  }
245
252
 
246
253
  // --- Eq 3: one KV-injection layer. H_ctx read-only; H_d is queries+updated ---
247
- #layer(i: number, H_d: MlxArray, H_ctx: MlxArray, maskBias: MlxArray | null, A: number, Lctx: number): MlxArray {
254
+ #layer(i: number, H_d: MlxArray, H_ctx: MlxArray, maskBias: MlxArray | null, A: number, Lctx: number, context?: DflashContextAttention): MlxArray {
248
255
  const { dDraft, nHeads, gamma } = this.cfg;
249
256
  const hd = dDraft / nHeads;
250
257
  const eps = this.dims.eps;
@@ -252,24 +259,34 @@ export class DflashDrafter {
252
259
 
253
260
  const residual = H_d;
254
261
  const nd = ops.rmsNorm(H_d, this.get(`bb.${i}.attn_norm`), eps);
255
- const nc = ops.rmsNorm(H_ctx, this.get(`bb.${i}.attn_norm`), eps); // SAME norm as block
262
+ const nc = context ? null : ops.rmsNorm(H_ctx, this.get(`bb.${i}.attn_norm`), eps); // SAME norm as block
256
263
 
257
264
  // Q from block only
258
265
  let q = ops.matmul(nd, this.get(`bb.${i}.q`)); // [A,γ,d]
259
266
  q = disposing(q, ops.reshape(q, [A, gamma, nHeads, hd]));
260
267
  q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3])); // [A,nHeads,γ,hd]
261
268
 
262
- // K/V = [Wk·H_ctx ; Wk·H_d] (context first, then block)
263
- const kc = ops.matmul(nc, wk), kd = ops.matmul(nd, wk);
264
- const vc = ops.matmul(nc, wv), vd = ops.matmul(nd, wv);
265
- nd.dispose(); nc.dispose();
266
- const shape4 = (x: MlxArray) => { const r = ops.reshape(x, [A, Lctx + gamma, nHeads, hd]); x.dispose(); const t = ops.transposeAxes(r, [0, 2, 1, 3]); r.dispose(); return t; };
267
- let K = ops.concatAxis([kc, kd], 1); kc.dispose(); kd.dispose(); // [A,Lctx+γ,d]
268
- let V = ops.concatAxis([vc, vd], 1); vc.dispose(); vd.dispose();
269
- K = shape4(K); V = shape4(V); // [A,nHeads,Lctx+γ,hd]
270
-
271
- let attn = ops.sdpa(q, K, V, Math.pow(hd, -0.5), maskBias ? "array" : "", maskBias);
272
- q.dispose(); K.dispose(); V.dispose();
269
+ const kd = ops.matmul(nd, wk), vd = ops.matmul(nd, wv); nd.dispose();
270
+ let attn: MlxArray;
271
+ if (context) {
272
+ const shape = (array: MlxArray) => {
273
+ using rows = ops.reshape(array, [A, gamma, nHeads, hd]);
274
+ return ops.transposeAxes(rows, [0, 2, 1, 3]);
275
+ };
276
+ using keys = shape(kd), values = shape(vd); kd.dispose(); vd.dispose();
277
+ attn = context.attend(i, q, keys, values, Math.pow(hd, -0.5));
278
+ } else {
279
+ const kc = ops.matmul(nc!, wk), vc = ops.matmul(nc!, wv); nc!.dispose();
280
+ const shape = (array: MlxArray) => {
281
+ using rows = ops.reshape(array, [A, Lctx + gamma, nHeads, hd]);
282
+ return ops.transposeAxes(rows, [0, 2, 1, 3]);
283
+ };
284
+ using k = ops.concatAxis([kc, kd], 1), v = ops.concatAxis([vc, vd], 1);
285
+ kc.dispose(); kd.dispose(); vc.dispose(); vd.dispose();
286
+ using keys = shape(k), values = shape(v);
287
+ attn = ops.sdpa(q, keys, values, Math.pow(hd, -0.5), maskBias ? "array" : "", maskBias);
288
+ }
289
+ q.dispose();
273
290
  attn = disposing(attn, ops.transposeAxes(attn, [0, 2, 1, 3])); // [A,γ,nHeads,hd]
274
291
  attn = disposing(attn, ops.reshape(attn, [A, gamma, dDraft]));
275
292
  attn = disposing(attn, ops.matmul(attn, this.get(`bb.${i}.o`)));
@@ -289,18 +306,36 @@ export class DflashDrafter {
289
306
  }
290
307
 
291
308
  /** hCtx [A,Lctx,m*H], anchorEmb [A,H], ctxMask [A,Lctx]|null → block [A,γ,d]. */
292
- #backbone(hCtx: MlxArray, anchorEmb: MlxArray, ctxMask: MlxArray | null, A: number): MlxArray {
293
- const Lctx = hCtx.shape[1]!;
294
- const H_ctx = this.#buildContext(hCtx);
309
+ #backbone(hCtx: MlxArray | null, anchorEmb: MlxArray, ctxMask: MlxArray | null, A: number, context?: DflashContextAttention): MlxArray {
310
+ const Lctx = hCtx?.shape[1] ?? 0;
311
+ const H_ctx = context ? null : this.#buildContext(hCtx!);
295
312
  let H_d = this.#buildBlock(anchorEmb, A);
296
313
  const maskBias = this.#maskBias(ctxMask, A, Lctx);
297
- for (let i = 0; i < this.cfg.nLayers; i++) H_d = disposing(H_d, this.#layer(i, H_d, H_ctx, maskBias, A, Lctx));
298
- H_ctx.dispose(); maskBias?.dispose();
314
+ for (let i = 0; i < this.cfg.nLayers; i++) H_d = disposing(H_d, this.#layer(i, H_d, H_ctx!, maskBias, A, Lctx, context));
315
+ H_ctx?.dispose(); maskBias?.dispose();
299
316
  H_d = disposing(H_d, ops.rmsNorm(H_d, this.get("out_norm"), this.dims.eps));
300
317
  return H_d;
301
318
  }
302
319
 
303
- #baseLogits(model: Gemma4Model, block: MlxArray): MlxArray {
320
+ /** Context projection is independent of the block and can be retained. */
321
+ projectContextRows(hidden: MlxArray): { k: MlxArray; v: MlxArray }[] {
322
+ const { nHeads, dDraft } = this.cfg, B = hidden.shape[0]!, N = hidden.shape[1]!;
323
+ using projected = this.#buildContext(hidden);
324
+ using pending = new DisposableStack();
325
+ const pairs = Array.from({ length: this.cfg.nLayers }, (_, layer) => {
326
+ using normalized = ops.rmsNorm(projected, this.get(`bb.${layer}.attn_norm`), this.dims.eps);
327
+ const plane = (name: string) => {
328
+ using flat = ops.matmul(normalized, this.get(name));
329
+ using rows = ops.reshape(flat, [B, N, nHeads, dDraft / nHeads]);
330
+ return pending.use(ops.transposeAxes(rows, [0, 2, 1, 3]));
331
+ };
332
+ return { k: plane(`bb.${layer}.k`), v: plane(`bb.${layer}.v`) };
333
+ });
334
+ pending.move();
335
+ return pairs;
336
+ }
337
+
338
+ #baseLogits(model: DraftProjection, block: MlxArray): MlxArray {
304
339
  const hOut = ops.matmul(block, this.get("out_proj")); // [A,γ,H]
305
340
  const hBf = hOut.astype(model.embed.scales.dtype); hOut.dispose();
306
341
  const logits = model.logitsFromHidden(hBf); hBf.dispose();
@@ -400,167 +435,89 @@ export class DflashDrafter {
400
435
  }
401
436
  }
402
437
 
403
- /** Inference: parallel backbone once, then sequential Markov sampling.
404
- * hCtx [1,Lctx,m*H] (full current context), anchorTok the bonus token.
405
- *
406
- * Host-sync discipline (docs/archive/investigations/dspark-handoff.md item 3): the
407
- * greedy path's token recurrence stays ON-DEVICE across positions — the
408
- * argmax result [1] uint32 feeds directly into the next position's
409
- * takeAxis, no itemUint32 in the loop. Per-position token arrays are
410
- * collected and resolved to host numbers with exactly ONE concat +
411
- * toIntTokens() after the loop. Confidence is the one read the pruning
412
- * ALGORITHM inherently needs per position (Alg 1 must decide whether to
413
- * stop NOW) — so it stays per-position whenever thresholds/minConf are
414
- * actually active; when pruning is inactive there is no such need, so
415
- * confidence is deferred the same way (collect [1] arrays, one concat +
416
- * one toFloat32 after the loop). The sampling path already forces a host
417
- * read every position (sampleToken draws from a device RNG key) — that
418
- * path is unchanged. */
419
- forwardInfer(model: Gemma4Model, hCtx: MlxArray, anchorTok: number, gamma: number, opts: DflashDraftOpts = {}): DflashDraftBlock {
420
- const { dDraft, markovRank: r } = this.cfg;
421
- const V = this.dims.vocabSize;
422
- const sample = opts.sample && opts.sample.temperature > 0 ? opts.sample : null;
423
- const keys = opts.keys ?? new KeyStream(opts.sample?.seed ?? 0);
424
- const collectLogits = opts.collectLogits ?? true;
425
- const pruningActive = opts.thresholds !== undefined || opts.minConf !== undefined;
426
-
427
- const ids = ops.fromInt32([anchorTok], [1, 1]);
428
- const ae3 = model.embed.encode(ids); ids.dispose();
429
- const anchorEmb = ops.reshape(ae3, [1, this.dims.hiddenSize]); ae3.dispose();
430
- const block = this.#backbone(hCtx, anchorEmb, null, 1); // [1,γ,d]
431
- anchorEmb.dispose();
432
- const U = this.#baseLogits(model, block);
433
- const Uf = U.astype(CDT); U.dispose();
438
+ /** Single-request callers use the same graph and proposal head at B1. */
439
+ forwardInfer(model: DraftProjection, hCtx: MlxArray, anchor: number, gamma: number, opts: DflashDraftOpts = {}): DflashDraftBlock {
440
+ const result = this.forwardRows(model, hCtx, [anchor], gamma, opts);
441
+ return { tokens: result.tokens[0]!, conf: result.conf[0]!, draftLogits: result.draftLogits };
442
+ }
434
443
 
435
- const w1 = this.get("markov.w1"), w2 = this.get("markov.w2");
444
+ forwardRows(model: DraftProjection, hCtx: MlxArray | null, anchors: readonly number[], gamma: number,
445
+ opts: DflashDraftOpts = {}, context?: DflashContextAttention): DflashDraftRows {
446
+ const B = anchors.length, { dDraft, markovRank: r } = this.cfg, V = this.dims.vocabSize;
447
+ const sample = opts.sample && opts.sample.temperature > 0 ? opts.sample : null;
448
+ const keys = sample ? anchors.map((_, row) => row === 0 && opts.keys ? opts.keys : new KeyStream(opts.sample?.seed ?? 0)) : [];
449
+ const collect = opts.collectLogits ?? true, pruning = opts.thresholds !== undefined || opts.minConf !== undefined;
450
+ using ids = ops.fromInt32([...anchors], [B, 1]);
451
+ using embeddings = model.embed.encode(ids);
452
+ using anchorEmb = ops.reshape(embeddings, [B, this.dims.hiddenSize]);
453
+ using block = this.#backbone(hCtx, anchorEmb, null, B, context);
454
+ using raw = this.#baseLogits(model, block);
455
+ using base = raw.astype(CDT);
436
456
  const isRnn = this.cfg.seqHead === "rnn";
437
- const wH = isRnn ? this.get("rnn.wH") : null;
438
- const bH = isRnn ? this.get("rnn.bH") : null;
439
- const wO = isRnn ? this.get("rnn.wO") : null;
440
- let s: MlxArray | null = isRnn ? zerosArray([1, r]) : null; // s_{-1} = 0, carried across k
441
- const conf: number[] = [];
442
- const perPos: MlxArray[] = []; // per-position [1,1,V] draft logits (collectLogits only)
443
- const tokArrs: MlxArray[] = []; // per-position [1] device token ids, greedy path only
444
- const confArrs: MlxArray[] = []; // per-position [1] device confidences, deferred (no pruning) only
445
- let tokens: number[] = [];
446
- let prevIdx: MlxArray = ops.fromInt32([anchorTok], [1]); // device token id fed to takeAxis
447
- let ownPrevIdx = true; // false once prevIdx aliases a tokArrs/argmax entry we must not double-dispose
448
- // Iteration-local temps register here at creation so the catch can reach
449
- // them on a mid-flight throw; drained (length=0) at the bottom of each
450
- // iteration once everything in it was disposed on the normal path.
451
- // dispose() idempotence makes catch-side re-disposal of the already-freed
452
- // ones inert.
453
- const pending: MlxArray[] = [];
457
+ let state: MlxArray | null = isRnn ? zerosArray([B, r]) : null;
458
+ const tokens: MlxArray[] = [], confidence: MlxArray[] = [], logits: MlxArray[] = [];
459
+ const lengths: Array<number | undefined> = anchors.map(() => undefined);
460
+ const hostConfidence: number[][] = [];
461
+ const needConfidence = pruning || opts.collectConfidence !== false;
462
+ using first = ops.fromInt32([...anchors], [B]); let previous = first;
454
463
  try {
455
- for (let k = 0; k < gamma; k++) {
456
- const UkS = Uf.slice([0, k, 0], [1, k + 1, V]); pending.push(UkS);
457
- const Uk = ops.reshape(UkS, [1, V]); pending.push(Uk);
458
- UkS.dispose();
459
- const e1 = ops.takeAxis(w1, prevIdx, 0); pending.push(e1); // [1,r], shared embedding either way
460
- if (ownPrevIdx) { prevIdx.dispose(); ownPrevIdx = false; } // consumed; prevIdx reassigned below
461
- let Bk: MlxArray;
464
+ for (let step = 0; step < gamma; step++) {
465
+ using row = base.slice([0, step, 0], [B, step + 1, V]);
466
+ using flat = ops.reshape(row, [B, V]);
467
+ using e1 = ops.takeAxis(this.get("markov.w1"), previous, 0);
468
+ let bias: MlxArray;
462
469
  if (isRnn) {
463
- const sh = ops.matmul(s!, wH!); pending.push(sh);
464
- s!.dispose(); s = null;
465
- let sNext = ops.add(sh, e1); pending.push(sNext);
466
- sh.dispose();
467
- sNext = disposing(sNext, ops.add(sNext, bH!)); pending.push(sNext);
468
- sNext = disposing(sNext, ops.tanh(sNext)); pending.push(sNext);
469
- s = sNext; // s_k, survives to the next iteration (covered by the s slot from here)
470
- Bk = ops.matmul(s, wO!); pending.push(Bk);
471
- } else {
472
- Bk = ops.matmul(e1, w2); pending.push(Bk);
473
- }
474
- const logitsK = ops.add(Uk, Bk); pending.push(logitsK);
475
- Uk.dispose(); Bk.dispose();
476
- // prevIdx becomes the SINGLE tracked owner of the next token id the
477
- // moment it exists — no separate local for the catch to miss.
470
+ using recurrent = ops.matmul(state!, this.get("rnn.wH"));
471
+ using sum = ops.add(recurrent, e1);
472
+ using shifted = ops.add(sum, this.get("rnn.bH"));
473
+ const next = ops.tanh(shifted); state!.dispose(); state = next;
474
+ bias = ops.matmul(state, this.get("rnn.wO"));
475
+ } else bias = ops.matmul(e1, this.get("markov.w2"));
476
+ using correction = bias;
477
+ using scores = ops.add(flat, correction);
478
+ let token: MlxArray;
478
479
  if (sample) {
479
- const sc = processLogits(logitsK, sample); pending.push(sc);
480
- const tok = sampleToken(sc, keys.next()); sc.dispose();
481
- tokens.push(tok);
482
- prevIdx = ops.fromInt32([tok], [1]); ownPrevIdx = true;
483
- } else {
484
- const am = ops.argmaxAxis(logitsK, -1); // [1] uint32, ON-DEVICE — no itemUint32 here
485
- tokArrs.push(am);
486
- prevIdx = am; ownPrevIdx = false; // aliases tokArrs[k] — that collection owns it
487
- }
488
- if (collectLogits) perPos.push(ops.reshape(logitsK, [1, 1, V]));
489
- logitsK.dispose();
490
- const hkS = block.slice([0, k, 0], [1, k + 1, dDraft]); pending.push(hkS);
491
- const hk = ops.reshape(hkS, [1, dDraft]); pending.push(hk);
492
- hkS.dispose();
493
- const ci = ops.concatAxis([hk, e1], 1); pending.push(ci);
494
- hk.dispose(); e1.dispose();
495
- let cz = ops.matmul(ci, this.get("conf.w")); pending.push(cz);
496
- ci.dispose();
497
- cz = disposing(cz, ops.add(cz, this.get("conf.b"))); pending.push(cz);
498
- cz = disposing(cz, ops.sigmoid(cz)); pending.push(cz);
499
-
500
- // Alg 1 (single-user): drop position k when its predicted acceptance
501
- // is below the calibrated/manual threshold — the target then
502
- // verifies a shorter window. Position 0 always survives (sources
503
- // return ≥1 token). Pruning inherently needs the confidence value
504
- // THIS position, on the host, to decide whether to stop — no way
505
- // around that read when active. When inactive, defer it (device
506
- // array, resolved once after the loop) since nothing this iteration
507
- // depends on its value.
508
- const thr = opts.thresholds?.[k] ?? opts.minConf;
509
- if (pruningActive) {
510
- const cVal = cz.toFloat32()[0]!; cz.dispose();
511
- if (thr !== undefined && k > 0 && cVal < thr) {
512
- // Drop position k SYMMETRICALLY on both paths so tokens/conf/
513
- // draftLogits stay aligned (the DflashDraftBlock contract —
514
- // verifySampling slices draftLogits per token). Greedy: pop the
515
- // device array (prevIdx aliases it — one dispose via the owning
516
- // collection). Sample: pop the host token + free the standalone
517
- // fromInt32.
518
- if (!sample) tokArrs.pop()!.dispose(); // also frees prevIdx (same array)
519
- else { prevIdx.dispose(); tokens.pop(); }
520
- ownPrevIdx = false; // dead either way — post-loop/catch must not re-dispose
521
- if (collectLogits) perPos.pop()!.dispose(); // logits row for the dropped position
522
- break;
480
+ const sampled = anchors.map((_, index) => {
481
+ if (lengths[index] !== undefined) return 0;
482
+ using one = scores.slice([index, 0], [index + 1, V]);
483
+ using processed = processLogits(one, sample);
484
+ return sampleToken(processed, keys[index]!.next());
485
+ });
486
+ token = ops.fromInt32(sampled, [B]);
487
+ } else token = ops.argmaxAxis(scores, -1);
488
+ tokens.push(token); previous = token;
489
+ if (collect) logits.push(ops.reshape(scores, [B, 1, V]));
490
+ if (needConfidence) {
491
+ using h = block.slice([0, step, 0], [B, step + 1, dDraft]);
492
+ using hFlat = ops.reshape(h, [B, dDraft]);
493
+ using joined = ops.concatAxis([hFlat, e1], 1);
494
+ using product = ops.matmul(joined, this.get("conf.w"));
495
+ using shifted = ops.add(product, this.get("conf.b"));
496
+ const conf = ops.sigmoid(shifted); confidence.push(conf);
497
+ if (pruning) {
498
+ const values = conf.toFloat32(), threshold = opts.thresholds?.[step] ?? opts.minConf;
499
+ hostConfidence.push(Array.from(values));
500
+ if (step > 0 && threshold !== undefined) values.forEach((value, row) => {
501
+ if (lengths[row] === undefined && value < threshold) lengths[row] = step;
502
+ });
503
+ if (lengths.every(length => length !== undefined)) break;
523
504
  }
524
- conf.push(cVal);
525
- } else {
526
- confArrs.push(cz);
527
505
  }
528
- pending.length = 0; // everything above was disposed (or handed to a collection)
529
506
  }
530
- pending.length = 0; // early-break path: the broken iteration's temps are all freed too
531
- if (ownPrevIdx) prevIdx.dispose(); // last iteration's fresh sample-path array, unused past the loop
532
- s?.dispose(); // final on-device state (loop end or early break — both leave s live)
533
- Uf.dispose(); block.dispose();
534
-
535
- if (!sample) {
536
- const allToks = ops.concatAxis(tokArrs, 0); // [nKept] uint32, ONE host read below
537
- for (const a of tokArrs) a.dispose();
538
- tokens = allToks.toIntTokens(); allToks.dispose();
507
+ using packed = ops.concatAxis(tokens, 0); const values = packed.toIntTokens();
508
+ using packedConfidence = !pruning && confidence.length ? ops.concatAxis(confidence, 0) : null;
509
+ const conf = packedConfidence?.toFloat32();
510
+ const sizes = lengths.map(length => length ?? tokens.length);
511
+ let draftLogits: MlxArray | undefined;
512
+ if (collect) {
513
+ using full = ops.concatAxis(logits, 1);
514
+ draftLogits = full.slice([0, 0, 0], [B, Math.max(...sizes), V]);
539
515
  }
540
- if (!pruningActive && confArrs.length) {
541
- const allConf = ops.concatAxis(confArrs, 0); // [nKept,1]
542
- for (const a of confArrs) a.dispose();
543
- const flat = allConf.toFloat32(); allConf.dispose();
544
- for (const v of flat) conf.push(v);
545
- }
546
- const draftLogits = collectLogits ? ops.concatAxis(perPos, 1) : undefined;
547
- if (collectLogits) for (const a of perPos) a.dispose();
548
- return { tokens, conf, draftLogits };
549
- } catch (err) {
550
- // Cleanup on a mid-flight throw — the collections, the carried state,
551
- // AND the current iteration's registered temps (`pending`). dispose()
552
- // is idempotent (array.ts #disposed guard) so re-disposing an array the
553
- // try block already freed (or that aliases one of these collections,
554
- // e.g. prevIdx === tokArrs[k]) is inert, never a double-free bug.
555
- for (const a of pending) a.dispose();
556
- if (ownPrevIdx) prevIdx.dispose();
557
- for (const a of tokArrs) a.dispose();
558
- for (const a of confArrs) a.dispose();
559
- for (const a of perPos) a.dispose();
560
- s?.dispose();
561
- Uf.dispose(); block.dispose();
562
- throw err;
563
- }
516
+ return {
517
+ tokens: sizes.map((length, row) => Array.from({ length }, (_, step) => values[step * B + row]!)),
518
+ conf: sizes.map((length, row) => opts.collectConfidence === false ? [] : Array.from({ length }, (_, step) => pruning ? hostConfidence[step]![row]! : conf![step * B + row]!)), draftLogits
519
+ };
520
+ } finally { state?.dispose(); for (const array of [...tokens, ...confidence, ...logits]) array.dispose(); }
564
521
  }
565
522
 
566
523
  save(dir: string): void {
@@ -31,6 +31,7 @@ import { MlxArray } from "../../mlx/array";
31
31
  import { Dtype } from "../../mlx/ffi";
32
32
  import * as ops from "../../mlx/ops";
33
33
  import type { Gemma4Model } from "../../model/gemma4";
34
+ import type { DraftProjection } from "../source";
34
35
  import { loadAdapterTensors } from "../../lora";
35
36
  import { writeShardedSafetensors, type NamedTensor } from "../../quantize/safetensors-writer";
36
37
  import { processLogits, sampleToken, KeyStream, type DSparkSampleConfig } from "./sample";
@@ -343,7 +344,7 @@ export class DSparkDrafter {
343
344
 
344
345
  /** Base logits U from the SHARED frozen LM head over the backbone block
345
346
  * hidden. [A,γ,V] (model dtype). */
346
- #baseLogits(model: Gemma4Model, block: MlxArray): MlxArray {
347
+ #baseLogits(model: DraftProjection, block: MlxArray): MlxArray {
347
348
  const hOut = ops.matmul(block, this.get("out_proj")); // [A,γ,H] (f32)
348
349
  const hBf = hOut.astype(model.embed.scales.dtype); // head's activation dtype
349
350
  hOut.dispose();
@@ -404,7 +405,7 @@ export class DSparkDrafter {
404
405
  * reconstructs (so q is consistent). hCtx [1,H], anchorTok the bonus token.
405
406
  * Returns the raw per-position logits so the verifier can recover q.
406
407
  */
407
- forwardInfer(model: Gemma4Model, hCtx: MlxArray, anchorTok: number, gamma: number, opts: DSparkDraftOpts = {}): DSparkDraftBlock {
408
+ forwardInfer(model: DraftProjection, hCtx: MlxArray, anchorTok: number, gamma: number, opts: DSparkDraftOpts = {}): DSparkDraftBlock {
408
409
  const { dDraft, markovRank: r } = this.cfg;
409
410
  const V = this.dims.vocabSize;
410
411
  const A = 1;
@@ -537,4 +538,3 @@ function buildNames(cfg: DSparkConfig): string[] {
537
538
  "markov.w1", "markov.w2", "conf.w", "conf.b");
538
539
  return names;
539
540
  }
540
-
@@ -1,5 +1,5 @@
1
1
  // Quantize a DeepSpec-format drafter checkpoint (Phase 1a of the DSpark
2
- // serving program, docs/design/dspark-serving-program.md).
2
+ // serving program, docs/design/speculative-decoding.md).
3
3
  //
4
4
  // Thin policy layer over the house quantizer (src/quantize): the generic
5
5
  // shape-eligibility rule already captures every 2-D matmul weight — the
@@ -11,7 +11,7 @@
11
11
  // with stock only on tie-free prompts (where batched == per-position).
12
12
  // An earlier version verified per-position to stay bit-exact to STOCK —
13
13
  // that matched no real oracle and cost an extra γ× read of the lm-head
14
- // weight per step; superseded. See docs/design/spec-decode-larger-targets.md.
14
+ // weight per step; superseded. See docs/design/speculative-decoding.md.
15
15
  //
16
16
  // Limitation (same as the reference): partial-accept rollback requires
17
17
  // trimmable caches; RotatingKVCache loses trimability once its ring
@@ -0,0 +1,29 @@
1
+ import type { MlxArray } from "../mlx/array";
2
+ import * as ops from "../mlx/ops";
3
+ import type { Glm52DecoderLayer, Glm52Model } from "../model/glm52";
4
+ import type { MLACache } from "../model/glm52-cache";
5
+ import { rmsNormF32Mlx } from "../model/glm52-mla";
6
+
7
+ /** Native MTP numerical graph. Tokens, anchor hidden and compressed state may
8
+ * have one or several rows; sampling and state retention belong to callers. */
9
+ export class Glm52MtpGraph {
10
+ constructor(readonly model: Glm52Model, readonly layer: Glm52DecoderLayer) {}
11
+
12
+ async forward(ids: MlxArray, hidden: MlxArray, cache: MLACache): Promise<MlxArray> {
13
+ const { glmConfig: config, weights } = this.model;
14
+ const prefix = `model.layers.${config.numHiddenLayers}`;
15
+ using embedded = weights.embedding(ids, "model.embed_tokens.weight", config.vocabSize, config.hiddenSize);
16
+ using embeddedNorm = rmsNormF32Mlx(embedded, weights.tensor(`${prefix}.enorm.weight`), config.rmsNormEps);
17
+ using hiddenNorm = rmsNormF32Mlx(hidden, weights.tensor(`${prefix}.hnorm.weight`), config.rmsNormEps);
18
+ using joined = ops.concatAxis([embeddedNorm, hiddenNorm], 2);
19
+ using projected = weights.linear(joined, `${prefix}.eh_proj.weight`, config.hiddenSize, 2 * config.hiddenSize);
20
+ return await this.layer.forwardAsync(projected, cache, null);
21
+ }
22
+
23
+ project(hidden: MlxArray): MlxArray {
24
+ const { glmConfig: config, weights } = this.model;
25
+ using normalized = rmsNormF32Mlx(hidden,
26
+ weights.tensor(`model.layers.${config.numHiddenLayers}.shared_head.norm.weight`), config.rmsNormEps);
27
+ return this.model.logitsFromHidden(normalized);
28
+ }
29
+ }
@@ -0,0 +1,132 @@
1
+ import type { MlxArray } from "../mlx/array";
2
+ import * as ops from "../mlx/ops";
3
+ import { Dtype } from "../mlx/ffi";
4
+ import { materializeCopy } from "../mlx/materialize";
5
+ import { MLACache } from "../model/glm52-cache";
6
+ import { toLogprobs } from "../sampler";
7
+ import { applyStateChanges, disposeResources } from "../engine/resources";
8
+ import type { PreparedStateChange } from "../contracts/resources";
9
+ import type { DraftRowSampling } from "./source";
10
+ import type { Glm52MtpGraph } from "./glm52-mtp-graph";
11
+
12
+ export interface Glm52MtpRowState {
13
+ readonly cache: MLACache;
14
+ readonly hidden: MlxArray;
15
+ readonly processedTokens: number;
16
+ }
17
+
18
+ /** Native Colibri draft history starts empty, independently of target prompt
19
+ * length. Accepted draft rows are rebuilt from true target hidden states. */
20
+ export class Glm52MtpRows {
21
+ #cache: MLACache;
22
+ #hidden: MlxArray | null = null;
23
+ #processed: number[] = [];
24
+ #drafts: number[][] = [];
25
+ #depth = 0;
26
+
27
+ constructor(readonly graph: Glm52MtpGraph, readonly sampling: DraftRowSampling | null) {
28
+ this.#cache = this.makeCache();
29
+ }
30
+ makeCache(): MLACache {
31
+ const c = this.graph.model.glmConfig;
32
+ return new MLACache({ kvLoraRank: c.kvLoraRank, ropeHeadDim: c.qkRopeHeadDim,
33
+ maxTokens: c.maxPositionEmbeddings, role: "mtp" });
34
+ }
35
+ get rowCount(): number { return this.#processed.length; }
36
+ append(states: readonly (Glm52MtpRowState | null)[]): void {
37
+ applyStateChanges([() => this.prepareAppend(states)]);
38
+ }
39
+ prepareAppend(states: readonly (Glm52MtpRowState | null)[]): PreparedStateChange {
40
+ if (!states.length) return { commit() {}, dispose() {} };
41
+ let cache: MLACache | null = this.makeCache(), hidden: MlxArray | null = null;
42
+ const held: { dispose(): void }[] = [];
43
+ const processed = [...this.#processed, ...states.map(state => state?.processedTokens ?? 0)];
44
+ try {
45
+ cache.mergeRows([...(this.rowCount ? [this.#cache] : []), ...states.map(state => {
46
+ if (state) return state.cache;
47
+ const empty = this.makeCache(); held.push(empty); return empty;
48
+ })]);
49
+ hidden = ops.concatAxis([...(this.#hidden ? [this.#hidden] : []), ...states.map(state => {
50
+ if (state) return state.hidden;
51
+ const zero = ops.zeros([1, 1, this.graph.model.glmConfig.hiddenSize], Dtype.float32);
52
+ held.push(zero); return zero;
53
+ })], 0);
54
+ } catch (error) { cache.dispose(); hidden?.dispose(); throw error; }
55
+ finally { disposeResources(held); }
56
+ return {
57
+ commit: () => {
58
+ const priorCache = this.#cache, priorHidden = this.#hidden;
59
+ this.#cache = cache!; this.#hidden = hidden; this.#processed = processed;
60
+ cache = priorCache; hidden = priorHidden;
61
+ },
62
+ dispose: () => { cache?.dispose(); hidden?.dispose(); cache = null; hidden = null; },
63
+ };
64
+ }
65
+ prefill(tokens: MlxArray, context: MlxArray): void {
66
+ const [batch, length] = tokens.shape as [number, number];
67
+ using tail = context.slice([0, length - 1, 0], [batch, length, context.shape[2]!]);
68
+ const hidden = materializeCopy(tail);
69
+ this.#hidden?.dispose(); this.#hidden = hidden;
70
+ this.#processed = this.#processed.map(count => count + length);
71
+ }
72
+ materialize(): void {
73
+ ops.evalAll([...this.#cache.state(), ...(this.#hidden ? [this.#hidden] : [])]);
74
+ }
75
+ filterRows(keep: readonly number[]): void {
76
+ if (!keep.length) { this.dispose(); return; }
77
+ using indices = ops.fromInt32([...keep], [keep.length]);
78
+ const hidden = ops.takeAxis(this.#hidden!, indices, 0);
79
+ try { this.#cache.filterRows(keep); } catch (error) { hidden.dispose(); throw error; }
80
+ this.#hidden!.dispose(); this.#hidden = hidden;
81
+ this.#processed = keep.map(row => this.#processed[row]!);
82
+ }
83
+ async draft(pending: readonly number[], depth: number, steps: readonly number[]): Promise<number[][]> {
84
+ const B = pending.length, tokens: MlxArray[] = [];
85
+ let ids: MlxArray | null = ops.fromInt32([...pending], [B, 1]), chained: MlxArray | null = null;
86
+ this.#cache.specRoundBegin();
87
+ try {
88
+ for (let position = 0; position < depth; position++) {
89
+ const hidden = await this.graph.forward(ids!, chained ?? this.#hidden!, this.#cache);
90
+ chained?.dispose(); chained = hidden;
91
+ using logits = this.graph.project(hidden);
92
+ using flat = ops.reshape(logits, [B, logits.shape.at(-1)!]);
93
+ using logprobs = toLogprobs(flat);
94
+ const next = this.sampling!.sample(logprobs, steps.map(step => step + position));
95
+ tokens.push(next); ids!.dispose(); ids = ops.reshape(next, [B, 1]);
96
+ }
97
+ using packed = depth ? ops.concatAxis(tokens, 0) : null;
98
+ const read = packed?.toIntTokens() ?? [];
99
+ this.#drafts = Array.from({ length: B }, (_, row) =>
100
+ Array.from({ length: depth }, (_, position) => read[position * B + row]!));
101
+ this.#depth = depth; return this.#drafts;
102
+ } finally { ids?.dispose(); chained?.dispose(); disposeResources(tokens); }
103
+ }
104
+ async commit(accepted: readonly number[], context: MlxArray): Promise<void> {
105
+ const B = accepted.length, width = Math.max(...accepted);
106
+ if (this.#depth) this.#cache.specRoundRollback(1);
107
+ else this.#cache.specRoundCommit();
108
+ if (width) {
109
+ using ids = ops.fromInt32(this.#drafts.flatMap(tokens => Array.from({ length: width }, (_, i) => tokens[i] ?? 0)), [B, width]);
110
+ using hidden = context.slice([0, 0, 0], [B, width, context.shape[2]!]);
111
+ this.#cache.specRoundBegin();
112
+ using output = await this.graph.forward(ids, hidden, this.#cache);
113
+ output.eval(); this.#cache.specRoundRollback(accepted);
114
+ }
115
+ using positions = ops.fromInt32([...accepted], [B, 1, 1]);
116
+ const hidden = ops.takeAlongAxis(context, positions, 1);
117
+ this.#hidden!.dispose(); this.#hidden = hidden;
118
+ this.#processed = this.#processed.map((count, row) => count + 1 + accepted[row]!);
119
+ this.#drafts = []; this.#depth = 0;
120
+ }
121
+ extractRow(row: number): Glm52MtpRowState {
122
+ const cache = this.#cache.extractRow(row);
123
+ try {
124
+ using hidden = this.#hidden!.slice([row, 0, 0], [row + 1, 1, this.#hidden!.shape[2]!]);
125
+ return { cache, hidden: materializeCopy(hidden), processedTokens: this.#processed[row]! };
126
+ } catch (error) { cache.dispose(); throw error; }
127
+ }
128
+ dispose(): void {
129
+ this.#cache.dispose(); this.#hidden?.dispose(); this.#hidden = null;
130
+ this.#processed = []; this.#drafts = []; this.#depth = 0;
131
+ }
132
+ }