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,165 @@
1
+ import type { MlxArray } from "../../mlx/array";
2
+ import * as ops from "../../mlx/ops";
3
+ import type { BatchableCache, Cache, Mask } from "../../model/gemma4-base";
4
+ import { KvTensorRows } from "./kv-tensor-rows";
5
+ import { leaseCacheStates, minimumReusableOffset } from "./state-views";
6
+ import { withResource, disposeResources } from "../../engine/resources";
7
+ import { cloneKvCaches } from "../../kv-store";
8
+
9
+ /** Encoded layouts retain physical row alignment across precision changes. */
10
+ export interface TransitionedKvLayout extends BatchableCache {
11
+ alignRows?(leftPad: readonly number[]): void;
12
+ }
13
+ export interface SpeculativeTransitionedKvLayout extends TransitionedKvLayout {
14
+ specRoundBegin(): void;
15
+ specRoundCommit(): void;
16
+ specRoundRollback(keep: number | readonly number[]): void;
17
+ }
18
+ export interface KvRowTransition<Layout extends TransitionedKvLayout> {
19
+ readonly signature: string;
20
+ maintain(rows: Cache[]): void;
21
+ converted(row: Cache): boolean;
22
+ makeLayout(): Layout;
23
+ packRows?(layout: Layout, rows: readonly Cache[]): void;
24
+ prepareRows?(rows: Cache[]): Cache[];
25
+ extractRow?(row: Cache): Cache;
26
+ rollbackRow?(row: Cache, before: number, keep: number, preserveColumns: boolean): void;
27
+ }
28
+
29
+
30
+ export interface TransitioningKvPositions {
31
+ readonly rowOffsets: readonly number[];
32
+ readonly leftPad: readonly number[];
33
+ readonly offset: number;
34
+ readonly batchSize: number | null;
35
+ readonly ropeOffsetArr: MlxArray | undefined;
36
+ sync(rows: readonly Cache[], preserve: boolean): void;
37
+ filterRows(keep: readonly number[]): void;
38
+ makeMask(tokens: number, window: number | null): Mask;
39
+ dispose(): void;
40
+ }
41
+ export class FullKvPositions extends KvTensorRows implements TransitioningKvPositions {
42
+ sync(rows: readonly Cache[], preserve: boolean): void {
43
+ this.mergeRows(preserve
44
+ ? [{ planes: [], rowOffsets: rows.map(row => row.offset), leftPad: [...this.leftPad] }]
45
+ : rows.map(row => ({ planes: [], rowOffsets: [row.offset], leftPad: [0] })));
46
+ }
47
+ }
48
+
49
+ /** Owns membership, committed positions and precision transitions. Codec and
50
+ * attention adapters own tensor access; scheduling and persistence use the
51
+ * existing batch and checkpoint contracts. Row conversion never examines an
52
+ * uncommitted speculative suffix. */
53
+ export abstract class TransitioningKvRows<Layout extends TransitionedKvLayout> implements BatchableCache {
54
+ protected readonly positions: TransitioningKvPositions;
55
+ protected rows: Cache[] = [];
56
+ protected packed?: Layout;
57
+ #prefilling = false;
58
+
59
+ readonly stateNeedsDispose = true;
60
+ constructor(readonly transition: KvRowTransition<Layout>, row?: Cache, positions: TransitioningKvPositions = new FullKvPositions()) {
61
+ this.positions = positions;
62
+ if (row) { this.rows = transition.prepareRows?.([row]) ?? [row]; this.syncPositions(); }
63
+ }
64
+ signature(): string { return this.transition.signature; }
65
+ get prefillMaintenance() { return this; }
66
+ beginPrefill(): void { this.#prefilling = true; }
67
+ commitPrefill(indices: readonly number[]): void {
68
+ if (this.packed) return;
69
+ for (const index of indices) {
70
+ const row = [this.rows[index]!];
71
+ try { this.transition.maintain(row); }
72
+ finally { this.rows[index] = row[0]!; }
73
+ }
74
+ }
75
+ endPrefill(): void { this.#prefilling = false; }
76
+ get minimumReusableOffset(): number { return this.packed?.minimumReusableOffset ?? minimumReusableOffset(this.rows); }
77
+ get rowOffsets(): readonly number[] { return this.packed?.rowOffsets ?? this.positions.rowOffsets; }
78
+ get leftPad(): readonly number[] { return this.packed?.leftPad ?? this.positions.leftPad; }
79
+ get offset(): number { return this.packed?.offset ?? this.positions.offset; }
80
+ get batchSize(): number | null { return this.packed?.batchSize ?? this.positions.batchSize; }
81
+ get ropeOffsetArr(): MlxArray | undefined { return this.packed ? this.packed.ropeOffsetArr : this.positions.ropeOffsetArr; }
82
+ bytesPerToken(): number { return this.packed?.bytesPerToken?.() ?? Math.max(0, ...this.rows.map(row => row.bytesPerToken?.() ?? 0)); }
83
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * tokens; }
84
+ makeMask(tokens: number, window: number | null): Mask { return (this.packed ?? this.positions).makeMask(tokens, window); }
85
+ isTrimmable(): boolean { return this.packed?.isTrimmable() ?? this.rows.every(row => row.isTrimmable()); }
86
+ trim(count: number): void {
87
+ if (this.packed) this.packed.trim(count);
88
+ else { for (const row of this.rows) row.trim(count); this.syncPositions(true); }
89
+ }
90
+ protected syncPositions(preserve = false): void { this.positions.sync(this.rows, preserve); }
91
+ protected advance(): void {
92
+ if (this.packed) return;
93
+ if (!this.#prefilling) this.transition.maintain(this.rows);
94
+ if (this.rows.length && this.rows.every(row => this.transition.converted(row))) {
95
+ const packed = this.transition.makeLayout();
96
+ try {
97
+ if (this.transition.packRows) this.transition.packRows(packed, this.rows);
98
+ else packed.mergeRows(this.rows);
99
+ packed.alignRows?.(this.positions.leftPad);
100
+ }
101
+ catch (error) { packed.dispose(); throw error; }
102
+ const previous = this.rows; this.rows = []; this.packed = packed;
103
+ this.positions.dispose(); disposeResources(previous);
104
+ }
105
+ }
106
+ abstract updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
107
+ abstract makeEmptyBatch(): TransitioningKvRows<Layout>;
108
+ mergeRows(rows: readonly Cache[]): void {
109
+ const owned: Cache[] = [];
110
+ try {
111
+ for (const row of rows) {
112
+ if (row instanceof TransitioningKvRows) {
113
+ for (let index = 0; index < row.batchSize!; index++) owned.push(row.extractRow(index));
114
+ } else owned.push(...cloneKvCaches([row]));
115
+ }
116
+ } catch (error) { disposeResources(owned); throw error; }
117
+ let prepared: Cache[];
118
+ try { prepared = this.transition.prepareRows?.(owned) ?? owned; }
119
+ catch (error) { disposeResources(owned); throw error; }
120
+ this.dispose(); this.rows = prepared; this.syncPositions();
121
+ }
122
+ filterRows(keep: readonly number[]): void {
123
+ if (this.packed) { this.packed.filterRows(keep); return; }
124
+ const previous = this.rows; this.rows = keep.map(index => previous[index]!);
125
+ disposeResources(previous.filter((_, index) => !keep.includes(index))); this.positions.filterRows(keep);
126
+ }
127
+ extractRow(row: number): Cache { return this.packed?.extractRow(row) ?? (this.transition.extractRow?.(this.rows[row]!) ?? cloneKvCaches([this.rows[row]!])[0]!); }
128
+ state(): MlxArray[] {
129
+ return withResource(leaseCacheStates(this.packed ? [this.packed] : this.rows), arrays => {
130
+ const views: MlxArray[] = [];
131
+ try { for (const array of arrays) views.push(ops.contiguous(array)); return views; }
132
+ catch (error) { disposeResources(views); throw error; }
133
+ });
134
+ }
135
+ dispose(): void {
136
+ this.#prefilling = false;
137
+ const previous = this.rows; this.rows = [];
138
+ this.packed?.dispose(); this.packed = undefined; this.positions.dispose(); disposeResources(previous);
139
+ }
140
+ }
141
+
142
+ /** Rollback is a method capability, separate from ordinary cache membership. */
143
+ export abstract class SpeculativeTransitioningKvRows<Layout extends SpeculativeTransitionedKvLayout> extends TransitioningKvRows<Layout> {
144
+ #beforeRound?: number[];
145
+ specRoundBegin(): void {
146
+ this.advance(); this.#beforeRound = [...this.rowOffsets]; this.packed?.specRoundBegin();
147
+ }
148
+ specRoundCommit(): void {
149
+ this.packed?.specRoundCommit(); this.#beforeRound = undefined; this.advance();
150
+ }
151
+ specRoundRollback(keep: number | readonly number[]): void {
152
+ if (this.packed) this.packed.specRoundRollback(keep);
153
+ else {
154
+ for (const [index, row] of this.rows.entries()) {
155
+ const count = typeof keep === "number" ? keep : keep[index]!;
156
+ const before = this.#beforeRound![index]!;
157
+ if (this.transition.rollbackRow) this.transition.rollbackRow(row, before, count, this.rows.length > 1);
158
+ else row.trim(row.offset - before - count);
159
+ }
160
+ this.syncPositions(true);
161
+ }
162
+ this.#beforeRound = undefined; this.advance();
163
+ }
164
+ override dispose(): void { this.#beforeRound = undefined; super.dispose(); }
165
+ }
package/src/cli.ts CHANGED
@@ -35,6 +35,14 @@ import {
35
35
  } from "./runtime-config";
36
36
 
37
37
  const argv = process.argv.slice(2);
38
+ // Private self-exec protocol for managed jobs in the single-file binary.
39
+ // Literal imports in job-entry keep the numerical runners inside the bundle.
40
+ if (argv[0] === "__job") {
41
+ const { runJobEntry } = await import("./jobs/job-entry");
42
+ await runJobEntry(argv[1]);
43
+ process.exit(0);
44
+ }
45
+
38
46
  // The appliance path: naked `mlx-bun` (or only options, e.g.
39
47
  // `mlx-bun --port 9000`) runs `serve` — first run downloads a model and
40
48
  // opens the chat UI. `--help`/`--version` and explicit subcommands still win.
@@ -103,13 +111,20 @@ const SERVER_FLAGS = `Server options:
103
111
  restarts (long-context agent TTFT drops from a
104
112
  full re-prefill to a zero-copy mmap restore).
105
113
  Off unless set.
106
- --ssd-cache-max <GB> SSD tier byte cap [default: 32 GB]
114
+ --ssd-cache-max <GB> Optional SSD tier byte cap; 0 = unlimited
115
+ [default: unlimited]
107
116
  --ssd-demote-idle <sec> Demote prompt-cache entries idle this long to the
108
117
  SSD tier, freeing their GPU memory (prefixes stay
109
118
  reachable via zero-copy restore). 0 disables.
110
119
  [default: 300 when --ssd-cache is on]
111
120
  --ssd-cache-verify Verify tensor hashes on every restore (reads all
112
121
  bytes eagerly — integrity paranoia only)
122
+ --generation-checkpoint <tokens>
123
+ Queue an in-flight generation snapshot to the SSD
124
+ cache every N emitted tokens. Repeating the same
125
+ request after restart replays and resumes it.
126
+ Requires --ssd-cache; supports ordinary requests
127
+ with shared cache layouts.
113
128
  --isolate Run the inference engine as a CHILD process and
114
129
  keep this process a pure UI/API proxy — the UI
115
130
  stays instant under GPU load and survives engine
@@ -155,8 +170,8 @@ Model & quality:
155
170
  auto-detected — a full same-tokenizer model
156
171
  (mlx_lm.server parity), a Gemma "-assistant"
157
172
  KV-borrowing drafter, or a DSpark checkpoint.
158
- Serial lane only — with --batch N a mounted draft
159
- routes every request serial, like mlx_lm.server.
173
+ Supported draft providers use shared batched
174
+ execution; ordinary requests can batch too.
160
175
  --draft-kind <kind> Override draft-artifact detection:
161
176
  two-model | assistant | dspark | deepspec | mtp | ngram
162
177
  (deepspec = DeepSeek's released Gemma4DSparkModel
@@ -181,7 +196,7 @@ Model & quality:
181
196
  (bf16), 4 / 8 (uniform bits), or turbo[:k<bits>v
182
197
  <bits>] (rotation-based TurboQuant — default
183
198
  k8v3; kBits in {2,4,5,8}, vBits in {2,3,4,5,8};
184
- docs/design/turboquant-kv.md)
199
+ docs/design/turboquant.md)
185
200
  [default: off — quantized KV measured 5-20% slower
186
201
  decode than bf16 at ≤16k (on mlx-lm too); it buys
187
202
  memory headroom, so opt in when context is tight.
@@ -191,12 +206,11 @@ Model & quality:
191
206
  only; sliding-window layers stay bf16)]
192
207
  --paged-kv OPTIONAL vLLM-style paged KV storage (block pool +
193
208
  gather before the stock SDPA; env MLX_BUN_PAGED_KV=1).
194
- v1: serial only (pins --batch 1 unless --batch is
195
- given), Gemma4-family, bf16 — refuses --batch N>1,
196
- --kv-quant, --draft-model; bypasses the prompt
209
+ Gemma4-family, bf16; supports --batch N.
210
+ Refuses --kv-quant and --draft-model; bypasses the prompt
197
211
  cache; runs uncompiled decode. Bit-exact with the
198
212
  plain path; expect a small decode cost at batch=1
199
- (the gather copy). docs/design/paged-kv-cache.md
213
+ (the gather copy). docs/design/kv-cache.md
200
214
  [default: off]
201
215
  --paged-kv-block-size <n> Tokens per KV block [default: 256 = the plain
202
216
  cache's growth step]
@@ -212,13 +226,14 @@ Model & quality:
212
226
  pass --temp 0 for its unset-request behavior)
213
227
  [default: the model's generation_config.json]
214
228
  --max-tokens <n> Completion cap when a request omits max_tokens
215
- (mlx_lm.server flag; its default is 512 there)
216
- [default: 128 GLM-5.2; otherwise 65536 chat /
217
- 512 raw completion]
229
+ (mlx_lm.server flag; --max-tokens 512 reproduces
230
+ its default) [default: 128 GLM-5.2 (memory-plan
231
+ reservation); otherwise none — a defaulted
232
+ request runs to EOS or the admitted context]
218
233
  --hlg-sampling on|off Piecewise tone-curve sampling (HLG): rolls off the
219
234
  top, boosts the mids, gentles the tail. Gain folds
220
235
  from --temperature. [default: off]
221
- See docs/design/hlg-sampling.md
236
+ See docs/archive/hlg-sampling.md
222
237
  --hlg-width <nats> HLG mid-region half-width [default: 4]
223
238
  --hlg-shoulder <nats> HLG highlight rolloff scale [default: 4]
224
239
  --hlg-toe <nats> HLG shadow rolloff scale [default: 6]
@@ -391,7 +406,7 @@ Usage: mlx-bun benchmark [query] [options]
391
406
 
392
407
  Runs mlx-bun by itself (no other systems required) and records the
393
408
  result to the eval DB. For quotable, cross-stack numbers use
394
- ./benchmark.sh from the repo — it preflight-gates on an idle machine
409
+ bun scripts/bench-serve.ts all from the repo — it preflight-gates on an idle machine
395
410
  and runs the mlx-lm/optiq comparison legs.
396
411
 
397
412
  [query] Model to benchmark (--query <q> is accepted for the
@@ -475,7 +490,7 @@ Options:
475
490
  --clear Archive the eval DB (timestamped backup) and start fresh
476
491
 
477
492
  Table view: when, model, bench kind, KV mode, decode tok/s, TTFT,
478
- peak memory, commit. Runs are written by ./benchmark.sh.`,
493
+ peak memory, commit. Runs are written by bun scripts/bench-serve.ts all.`,
479
494
 
480
495
  train: `mlx-bun train — fine-tune a LoRA adapter on your data
481
496
 
@@ -825,14 +840,14 @@ function openChatUi(url: string, hostPort: string): void {
825
840
  /** Resolve the decode ROUTE: a tier alias (--l1/--l2) sets the whole route,
826
841
  * and an explicit per-fork flag (--kv-quant/--fused-sdpa/…) overrides the
827
842
  * alias. Installs decode runtime options and returns the kv-quant mode. See
828
- * docs/design/parity-tier-dag.md + unified-engine-frontier-plan.md.
843
+ * docs/design/unified-engine-frontier-plan.md + unified-engine-frontier-plan.md.
829
844
  * Each tier is a GUARANTEE about which reference you reproduce bit-for-bit:
830
845
  * --l1 bit-for-bit IDENTICAL to mlx-lm — drop-in replacement for mlx-lm
831
846
  * (bf16 KV; THE naked default since 2026-07-05)
832
847
  * --l2 bit-for-bit IDENTICAL to mlx-optiq — drop-in replacement for
833
848
  * mlx-optiq: quantized KV per kv_config.json + fused N-tiled SDPA for
834
849
  * L>1 + STOCK unfused L=1 decode (the optiq-golden composition,
835
- * scripts/regen-kvq-goldens.ts + regen-mixed-kv-goldens.ts).
850
+ * scripts/regen.ts kvq + regen-mixed-kv-goldens.ts).
836
851
  * There is no --l3: output-changing experiments live in the Lab (env flags
837
852
  * with a bench + expiry) until one beats the L1 baseline in a paired A/B —
838
853
  * docs/design/unified-engine-frontier-plan.md §6-7. */
@@ -897,7 +912,7 @@ function applyDecodeRoute(): { kvQuant?: "off" | "config" | number; turboQuant?:
897
912
  if (kv === "off") return { kvQuant: "off" };
898
913
  if (kv === "config") return { kvQuant: "config" };
899
914
  if (kv) {
900
- // TurboQuant (docs/design/turboquant-kv.md): a distinct axis from the
915
+ // TurboQuant (docs/design/turboquant.md): a distinct axis from the
901
916
  // affine kvQuant above — dequantize-on-fetch, stock unfused ops.sdpa,
902
917
  // no kvQuant value at all (mutually exclusive with kvBits/kvConfig).
903
918
  let turboQuant: TurboQuantScheme | null;
@@ -940,23 +955,41 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
940
955
  if (kvBudgetGB > 0) serverOptions.kvBudgetBytes = kvBudgetGB * 1e9;
941
956
  const pcRaw = opt("prompt-cache"); // null if absent; an explicit value (incl. 0) wins → `--prompt-cache 0` DISABLES the cache
942
957
  if (pcRaw !== null) serverOptions.promptCacheBytes = Math.max(0, Number(pcRaw)) * 2 ** 30;
943
- // Engine-child mode (runtime-isolation.md): listen on a unix socket
958
+ // Engine-child mode (docs/reference/server-config.md): listen on a unix socket
944
959
  // instead of TCP — the isolated parent proxies here. Internal flag,
945
960
  // spawned by --isolate; usable directly for socket-level integration.
946
961
  const unixRaw = opt("unix");
947
962
  if (unixRaw !== null) serverOptions.unixSocket = unixRaw;
948
- // SSD cold tier (docs/design/ssd-kv-cold-tier.md) — off unless a dir is given.
963
+ // SSD cold tier (docs/design/kv-cache.md) — off unless a dir is given.
949
964
  const ssdDir = opt("ssd-cache");
950
965
  if (ssdDir !== null) {
951
966
  serverOptions.ssdCacheDir = ssdDir;
952
967
  const maxRaw = opt("ssd-cache-max");
953
- if (maxRaw !== null) serverOptions.ssdCacheMaxBytes = Math.max(1, Number(maxRaw)) * 2 ** 30;
968
+ if (maxRaw !== null) {
969
+ const maxGb = Number(maxRaw);
970
+ if (!Number.isFinite(maxGb) || maxGb < 0) {
971
+ console.error(`--ssd-cache-max expects a number >= 0 (got "${maxRaw}")`);
972
+ process.exit(1);
973
+ }
974
+ serverOptions.ssdCacheMaxBytes = maxGb === 0
975
+ ? Number.POSITIVE_INFINITY
976
+ : maxGb * 2 ** 30;
977
+ }
954
978
  if (flag("ssd-cache-verify")) serverOptions.ssdCacheVerify = true;
955
979
  const demoteRaw = opt("ssd-demote-idle");
956
980
  if (demoteRaw !== null) serverOptions.ssdDemoteIdleSec = Math.max(0, Number(demoteRaw));
981
+ const checkpointRaw = opt("generation-checkpoint");
982
+ if (checkpointRaw !== null) {
983
+ const n = Number(checkpointRaw);
984
+ if (!Number.isInteger(n) || n < 1) {
985
+ console.error(`--generation-checkpoint expects an integer >= 1 (got "${checkpointRaw}")`);
986
+ process.exit(1);
987
+ }
988
+ serverOptions.generationCheckpointTokens = n;
989
+ }
957
990
  } else {
958
991
  // Refuse-loudly house rule: sub-flags of an absent tier must not vanish.
959
- for (const f of ["ssd-cache-max", "ssd-demote-idle"] as const)
992
+ for (const f of ["ssd-cache-max", "ssd-demote-idle", "generation-checkpoint"] as const)
960
993
  if (opt(f) !== null) console.warn(`--${f} has no effect without --ssd-cache — ignored`);
961
994
  if (flag("ssd-cache-verify"))
962
995
  console.warn("--ssd-cache-verify has no effect without --ssd-cache — ignored");
@@ -979,16 +1012,11 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
979
1012
  if (route.kvQuant !== undefined) serverOptions.kvQuant = route.kvQuant;
980
1013
  if (route.turboQuant !== undefined) serverOptions.turboQuant = route.turboQuant;
981
1014
  // --paged-kv (env: MLX_BUN_PAGED_KV=1): OPTIONAL vLLM-style block-pool KV
982
- // storage, default off (docs/design/paged-kv-cache.md). Serial-only in v1:
983
- // with no explicit --batch, pin --batch 1 (the default is 8); an explicit
984
- // --batch N>1 is refused loudly by createServer rather than downgraded.
1015
+ // storage, default off (docs/design/kv-cache.md). The state policy owns
1016
+ // block pools and cache reuse; paging preserves the selected batch size.
985
1017
  if (flag("paged-kv") || runtimeValue("MLX_BUN_PAGED_KV") === "1") {
986
1018
  const bsRaw = opt("paged-kv-block-size");
987
1019
  serverOptions.pagedKv = bsRaw !== null ? { blockSize: Number(bsRaw) } : {};
988
- if (batchRaw === null) {
989
- serverOptions.batch = 1;
990
- console.log("[paged-kv] serial-only in v1 — pinning --batch 1");
991
- }
992
1020
  }
993
1021
  // Bind loopback unless asked otherwise (mlx_lm.server parity); --host
994
1022
  // 0.0.0.0 is the explicit opt-in for LAN exposure. The chat-UI open and
@@ -1026,7 +1054,7 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
1026
1054
  const maxTok = numFlag("max-tokens", 1, 10_000_000);
1027
1055
  if (maxTok !== null) serverOptions.defaultMaxTokens = Math.floor(maxTok);
1028
1056
  // HLG sampling — piecewise tone curve on the logprobs (default off). Knobs in
1029
- // nats; the mid gain folds from --temperature. docs/design/hlg-sampling.md.
1057
+ // nats; the mid gain folds from --temperature. docs/archive/hlg-sampling.md.
1030
1058
  if (onOff("hlg-sampling") === true) {
1031
1059
  serverOptions.hlg = {
1032
1060
  enabled: true,
@@ -1538,7 +1566,7 @@ switch (cmd) {
1538
1566
  process.exit(1);
1539
1567
  }
1540
1568
  }
1541
- // Runtime isolation (--isolate, runtime-isolation.md): THIS process
1569
+ // Runtime isolation (--isolate, docs/reference/server-config.md): THIS process
1542
1570
  // becomes a pure reactor — it binds the TCP port and reverse-proxies to
1543
1571
  // an engine CHILD (the full server on a unix socket, spawned from the
1544
1572
  // same argv minus parent-only flags). Zero MLX calls in the parent: the
@@ -1580,7 +1608,7 @@ switch (cmd) {
1580
1608
  };
1581
1609
  const poolMaxRaw = opt("model-pool");
1582
1610
  const poolMax = Math.max(1, Number(poolMaxRaw ?? 1) || 1);
1583
- const { engine, pool } = startProxyServer({
1611
+ const { engine, close } = startProxyServer({
1584
1612
  port: rt.port,
1585
1613
  ...(rt.serverOptions.hostname ? { hostname: rt.serverOptions.hostname } : {}),
1586
1614
  engine: {
@@ -1596,7 +1624,7 @@ switch (cmd) {
1596
1624
  const shownHost0 = rt.serverOptions.hostname ?? "localhost";
1597
1625
  console.log(style.dim(` isolated mode: engine child starting (socket ${sock})`));
1598
1626
  console.log(` ${style.green("●")} ${style.bold("proxy up")} http://${shownHost0}:${rt.port} ${style.dim(`· ${m0.repoId} loading behind it · model pool ${poolMax}`)}`);
1599
- const onExit = () => { pool?.stopAll(); engine.stop(); process.exit(0); };
1627
+ const onExit = () => { void close().finally(() => process.exit(0)); };
1600
1628
  process.on("SIGINT", onExit);
1601
1629
  process.on("SIGTERM", onExit);
1602
1630
  await engine.ready;
@@ -1613,25 +1641,6 @@ switch (cmd) {
1613
1641
  const config = await loadModelConfig(m.path);
1614
1642
  const report = fit(config, m.sizeBytes, 8192, undefined, undefined, m.expertsBytes);
1615
1643
  sFit.done(`${style.bold(m.repoId)} ${style.dim(`· ${gb(m.sizeBytes)} · ~${report.predictedDecodeTps.toFixed(0)} tok/s predicted`)}${picked ? style.dim(" · auto-picked (override: mlx-bun serve <query>)") : ""}`);
1616
- // Near-ceiling advisory (admission doctrine: advise, never refuse). A
1617
- // model near the DEFAULT Metal wired ceiling (~75% of RAM) dies with an
1618
- // UNCATCHABLE async-GPU-OOM on long prefills (M4 24 GB × 17 GB model,
1619
- // 2026-08-20) — surface the standard remedy up front instead.
1620
- {
1621
- const { maxRecommendedWorkingSetSize } = await import("./mlx/ffi");
1622
- const ceiling = maxRecommendedWorkingSetSize();
1623
- const sysctlRaw = Bun.spawnSync(["sysctl", "-n", "iogpu.wired_limit_mb"]).stdout.toString().trim();
1624
- const limitIsDefault = sysctlRaw === "0" || sysctlRaw === "";
1625
- if (limitIsDefault && ceiling > 0 && m.sizeBytes > 0.8 * ceiling) {
1626
- const ramGb = Number(Bun.spawnSync(["sysctl", "-n", "hw.memsize"]).stdout.toString().trim()) / 2 ** 30;
1627
- const suggestMb = Math.floor((ramGb - 2.5) * 1024);
1628
- console.error(style.dim(
1629
- ` ! ${gb(m.sizeBytes)} model vs ~${gb(ceiling)} default GPU ceiling — long prompts can\n` +
1630
- ` hit an uncatchable Metal OOM. If that happens, raise the limit first:\n` +
1631
- ` sudo sysctl iogpu.wired_limit_mb=${suggestMb} (resets on reboot)`,
1632
- ));
1633
- }
1634
- }
1635
1644
  const sNative = step("native runtime");
1636
1645
  await ensureNative(sNative);
1637
1646
  sNative.done("native runtime ready");
@@ -1651,9 +1660,8 @@ switch (cmd) {
1651
1660
  }
1652
1661
  // Speculative decoding (mlx_lm.server parity): --draft-model <path|query>
1653
1662
  // resolves like the main model; --num-draft-tokens defaults to 3 (the
1654
- // server's default upstream — mlx_lm.generate's is 2). Serial-lane-only:
1655
- // with --batch N, a mounted draft routes every request serial (upstream
1656
- // is_batchable = draft is None).
1663
+ // server's default upstream — mlx_lm.generate's is 2). Qualified providers
1664
+ // use shared execution; ordinary requests retain their own placement.
1657
1665
  const draftQuery = opt("draft-model");
1658
1666
  let draftModelDir: string | undefined;
1659
1667
  if (draftQuery !== null) {
@@ -1886,7 +1894,7 @@ switch (cmd) {
1886
1894
  const promptTokens = Number(opt("prompt-tokens", "0"));
1887
1895
  const kvMode = opt("kv-quant", "off")!;
1888
1896
  console.log(` ${style.dim("measures THIS machine, OUR stack only — no other systems needed.")}`);
1889
- console.log(` ${style.dim("numbers on a loaded machine are not quotable; ./benchmark.sh is the preflight-gated harness.")}`);
1897
+ console.log(` ${style.dim("numbers on a loaded machine are not quotable; bun scripts/bench-serve.ts all is the preflight-gated harness.")}`);
1890
1898
  const sNative = step("native runtime");
1891
1899
  await ensureNative(sNative);
1892
1900
  sNative.done("native runtime ready");
@@ -1931,7 +1939,7 @@ switch (cmd) {
1931
1939
  : kvMode !== "off" ? { kvBits: Number(kvMode), quantizedKvStart: 0 } : {};
1932
1940
 
1933
1941
  // Warmup materializes weights so prefill timing measures prefill,
1934
- // not lazy page-in (same protocol as scripts/bench.ts).
1942
+ // not lazy page-in (same protocol as bench.ts (deleted 2026-08-23; git history)).
1935
1943
  const sWarm = step("warmup");
1936
1944
  {
1937
1945
  const wCache = model.makeCache();
@@ -2006,7 +2014,7 @@ switch (cmd) {
2006
2014
  }
2007
2015
  const { style } = await import("./tui");
2008
2016
  if (rows.length === 0) {
2009
- console.log("no eval runs recorded yet — ./benchmark.sh writes them");
2017
+ console.log("no eval runs recorded yet — bun scripts/bench-serve.ts all writes them");
2010
2018
  break;
2011
2019
  }
2012
2020
  const ago = (ts: number) => {
@@ -2078,7 +2086,7 @@ switch (cmd) {
2078
2086
  // already run their own pi connect it with `mlx-bun harness pi`.
2079
2087
  const OURS_VAL = new Set([
2080
2088
  "--query", "-q", "--model", "--port", "--host", "--memory-budget", "--kv-budget", "--prompt-cache",
2081
- "--ssd-cache", "--ssd-cache-max", "--ssd-demote-idle", "--kv-quant", "--unix",
2089
+ "--ssd-cache", "--ssd-cache-max", "--ssd-demote-idle", "--generation-checkpoint", "--kv-quant", "--unix",
2082
2090
  "--batch", "--decode-concurrency", "--adapter", "--adapter-path",
2083
2091
  "--draft-model", "--num-draft-tokens", "--draft-kind", "--ngram-max", "--ngram-min",
2084
2092
  "--compiled-decode", "--compiled-activations", "--fused-sdpa", "--thinking",
package/src/client.ts ADDED
@@ -0,0 +1,66 @@
1
+ import type { CompletionClient } from "./contracts/completion";
2
+ import type { EngineHost } from "./contracts/host";
3
+
4
+ export interface CompletionCall {
5
+ readonly body: Readonly<Record<string, unknown>>;
6
+ readonly route?: "chat/completions" | "completions";
7
+ readonly signal?: AbortSignal;
8
+ }
9
+
10
+ export interface CompletionResponse {
11
+ choices: Array<{ text?: string; message?: { content?: string | null; [key: string]: unknown }; [key: string]: unknown }>;
12
+ usage?: Record<string, unknown>;
13
+ [key: string]: unknown;
14
+ }
15
+
16
+ /** A borrowed transport. Direct services, HTTP and isolated workers receive
17
+ * the same request and return the same result; POSTs are never retried here. */
18
+ export function createCompletionClient(options: {
19
+ baseUrl: string;
20
+ headers?: Readonly<Record<string, string>>;
21
+ host?: Pick<EngineHost<Request, Response>, "forward">;
22
+ }): CompletionClient<CompletionCall, CompletionResponse> {
23
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
24
+ const headers = { ...options.headers, "content-type": "application/json" };
25
+ const send = options.host ? options.host.forward.bind(options.host) : fetch;
26
+ return {
27
+ async complete({ body, route = "chat/completions", signal }) {
28
+ signal?.throwIfAborted();
29
+ const response = await send(new Request(`${baseUrl}/${route}`, {
30
+ method: "POST", headers, body: JSON.stringify({ ...body, stream: false }), signal,
31
+ }));
32
+ if (!response.ok) {
33
+ const detail = await response.text();
34
+ throw new Error(`completion failed (${response.status}): ${detail}`);
35
+ }
36
+ const result = await response.json() as CompletionResponse;
37
+ if (!Array.isArray(result.choices)) throw new Error("completion response has no choices");
38
+ return result;
39
+ },
40
+ };
41
+ }
42
+
43
+ /** Adapts an in-process request handler. Closing waits for active handlers;
44
+ * a handler owns any response stream it returns. */
45
+ export function createDirectHost(
46
+ handler: (request: Request) => Promise<Response>, shutdown: () => Promise<void> = async () => {},
47
+ ): EngineHost<Request, Response> {
48
+ let closed = false;
49
+ let closing: Promise<void> | undefined;
50
+ const active = new Set<Promise<Response>>();
51
+ return {
52
+ ready: Promise.resolve(),
53
+ forward(request) {
54
+ if (closed) return Promise.reject(new Error("engine host is closed"));
55
+ if (request.signal.aborted) return Promise.reject(request.signal.reason);
56
+ const work = Promise.resolve().then(() => handler(request));
57
+ active.add(work);
58
+ void work.then(() => active.delete(work), () => active.delete(work));
59
+ return work;
60
+ },
61
+ close() {
62
+ closed = true;
63
+ return closing ??= (async () => { await Promise.allSettled(active); await shutdown(); })();
64
+ },
65
+ };
66
+ }
package/src/config.ts CHANGED
@@ -75,6 +75,17 @@ export interface QuantSpec {
75
75
  bits: number;
76
76
  groupSize: number;
77
77
  mode: string;
78
+ /** `mode: "trellis"` (packed trellis-coded weights, src/model/trellis-linear.ts):
79
+ * bits = k per weight, groupSize = block T, plus the codec geometry. */
80
+ trellis?: TrellisSpec;
81
+ }
82
+
83
+ export interface TrellisSpec {
84
+ L: number;
85
+ code: string;
86
+ /** 1 = coded along the input dim (stored [out, in·k/32]); 0 = along the
87
+ * output dim (stored [in, out·k/32], i.e. Wᵀ coded along its last axis). */
88
+ axis: 0 | 1;
78
89
  }
79
90
 
80
91
  export interface QuantizationConfig {
@@ -89,12 +100,12 @@ export interface KvQuantSpec {
89
100
  groupSize: number;
90
101
  }
91
102
 
92
- /** TurboQuant scheme (docs/design/turboquant-kv.md): rotation-based KV
103
+ /** TurboQuant scheme (docs/design/turboquant.md): rotation-based KV
93
104
  * quantization, a distinct axis from the uniform/per-layer affine kvQuant
94
105
  * above (composes with it in principle; v1 ships as a standalone CLI-only
95
106
  * runtime lever, same class as uniform kvBits — see
96
107
  * src/generate.ts maybeQuantizeKv). Valid kBits: {2,4,5,8}; vBits:
97
- * {2,3,4,5,8} (docs/design/turboquant-kv.md's supported Lloyd-Max tables). */
108
+ * {2,3,4,5,8} (docs/design/turboquant.md's supported Lloyd-Max tables). */
98
109
  export interface TurboQuantScheme {
99
110
  kBits: number;
100
111
  vBits: number;
@@ -162,11 +173,17 @@ export function parseQuantization(raw: Record<string, any> | undefined): Quantiz
162
173
  for (const [key, value] of Object.entries(raw)) {
163
174
  if (key === "bits" || key === "group_size" || key === "mode") continue;
164
175
  if (typeof value === "object" && value !== null) {
165
- perLayer.set(key, {
176
+ const spec: QuantSpec = {
166
177
  bits: (value as any).bits ?? def.bits,
167
178
  groupSize: (value as any).group_size ?? def.groupSize,
168
179
  mode: (value as any).mode ?? def.mode,
169
- });
180
+ };
181
+ const tr = (value as any).trellis;
182
+ if (spec.mode === "trellis") {
183
+ if (!tr || typeof tr !== "object") throw new Error(`quantization.${key}: mode "trellis" needs a trellis {L, code, axis} block`);
184
+ spec.trellis = { L: Number(tr.L), code: String(tr.code ?? "1mad"), axis: tr.axis === 0 ? 0 : 1 };
185
+ }
186
+ perLayer.set(key, spec);
170
187
  } else if (value === false) {
171
188
  // mlx convention: `"layer": false` means not quantized
172
189
  perLayer.set(key, { bits: 0, groupSize: 0, mode: "none" });
@@ -0,0 +1,16 @@
1
+ import type { Cancellation } from "./generation";
2
+
3
+ /** Application-facing calls. The implementation owns tokenization, numerical
4
+ * method and transport; callers provide request data and consume results. */
5
+ export interface CompletionClient<Request, Result> {
6
+ complete(request: Request): Promise<Result>;
7
+ }
8
+
9
+ export interface BatchCompletionClient<Request, Result> extends CompletionClient<Request, Result> {
10
+ /** Results preserve input order; the implementation chooses execution groups. */
11
+ completeBatch(requests: readonly Request[]): Promise<Result[]>;
12
+ }
13
+
14
+ export interface TaskClient<Request, Progress, Result> {
15
+ run(request: Request, report: (progress: Progress) => void, cancellation?: Cancellation): Promise<Result>;
16
+ }