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
@@ -1,3 +1,4 @@
1
+ import { CancellationSource } from "../engine/cancellation";
1
2
  // Job runner: turns a registered `JobRunner` into a tracked, logged job.
2
3
  // Two execution modes — both create a row, both stream NDJSON events to the
3
4
  // log, both own terminal status:
@@ -14,6 +15,8 @@
14
15
  // Port of optiq lab jobs.py `submit` + `_job_main`, split across this file
15
16
  // and job-entry.ts.
16
17
 
18
+ import type { DisposableResource } from "../contracts/resources";
19
+ import { createJobTaskClient } from "./task-client";
17
20
  import { appendFileSync } from "node:fs";
18
21
  import { fileURLToPath } from "node:url";
19
22
  import type { Emit, JobEvent, JobRunner } from "./types";
@@ -104,18 +107,28 @@ export interface SubmitResult {
104
107
  * on success sets 'done'/progress 1/ended_at + emits 'done', on throw sets
105
108
  * 'failed'/error/ended_at + emits 'failed'. Returns once the task is
106
109
  * scheduled (does not await completion — poll the row / tail the log). */
110
+ interface InProcessWork { cancellation: CancellationSource; done: Promise<void>; }
111
+ const inProcess = new WeakMap<JobStore, Set<InProcessWork>>();
112
+
107
113
  export function submitInProcess(
108
114
  store: JobStore,
109
115
  kind: string,
110
116
  config: Record<string, unknown>,
111
117
  outputPath?: string,
112
118
  ): SubmitResult {
119
+ if (closedStores.has(store)) throw new Error("job host is closed");
113
120
  const row = store.create(kind, config, outputPath);
114
121
  const emit = makeEmit(store, row.id, row.log_path);
115
122
  store.setStatus(row.id, "running");
116
123
  emit({ type: "started", ts: Date.now() });
117
124
 
118
125
  const runner = getRunner(kind);
126
+ const cancellation = new CancellationSource();
127
+ const running = inProcess.get(store) ?? new Set<InProcessWork>();
128
+ inProcess.set(store, running);
129
+ let finish!: () => void;
130
+ const work = { cancellation, done: new Promise<void>((resolve) => { finish = resolve; }) };
131
+ running.add(work);
119
132
  void (async () => {
120
133
  if (!runner) {
121
134
  const error = `no runner registered for kind "${kind}"`;
@@ -124,7 +137,7 @@ export function submitInProcess(
124
137
  return;
125
138
  }
126
139
  try {
127
- const result = await runner(emit, JSON.parse(row.config_json));
140
+ const result = await createJobTaskClient(runner).run(JSON.parse(row.config_json), emit, cancellation);
128
141
  const out = result?.outputPath;
129
142
  if (out) store.setOutputPath(row.id, out);
130
143
  store.setProgress(row.id, 1);
@@ -135,7 +148,7 @@ export function submitInProcess(
135
148
  store.setStatus(row.id, "failed", { error, endedAt: nowIso() });
136
149
  emit({ type: "failed", error, ts: Date.now() });
137
150
  }
138
- })();
151
+ })().finally(() => { running.delete(work); finish(); });
139
152
 
140
153
  return { jobId: row.id, outputPath };
141
154
  }
@@ -145,16 +158,25 @@ export function submitInProcess(
145
158
  // ---------------------------------------------------------------------------
146
159
 
147
160
  interface QueuedSpawn {
161
+ acquire?: (signal: AbortSignal) => Promise<DisposableResource>;
162
+ lease?: DisposableResource;
163
+ abort: AbortController;
164
+ proc?: Bun.Subprocess;
165
+ finished: Promise<void>;
166
+ finish(): void;
148
167
  store: JobStore;
149
168
  jobId: string;
150
169
  entry: string;
151
170
  bin: string;
171
+ compiled: boolean;
152
172
  spawn: typeof Bun.spawn;
153
173
  onComplete?: (jobId: string, code: number) => void;
154
174
  }
155
175
 
156
176
  let gpuLeaseHolder: string | null = null;
157
177
  const spawnQueue: QueuedSpawn[] = [];
178
+ let activeSpawn: QueuedSpawn | undefined;
179
+ const closedStores = new WeakSet<JobStore>();
158
180
 
159
181
  /** True while a GPU (subprocess) job is running — the server gates token
160
182
  * generation on this so quantize/finetune don't fight live inference. */
@@ -168,6 +190,8 @@ export function currentGpuJob(): string | null {
168
190
  }
169
191
 
170
192
  export interface SubprocessOpts {
193
+ /** Host execution lease, held until child exit (including crashes). */
194
+ acquire?: (signal: AbortSignal) => Promise<DisposableResource>;
171
195
  /** Override the entry script (tests). Defaults to JOB_ENTRY_PATH. */
172
196
  entry?: string;
173
197
  /** Override the runtime binary (tests). Defaults to "bun". */
@@ -189,14 +213,20 @@ export function submitSubprocess(
189
213
  outputPath?: string,
190
214
  opts: SubprocessOpts = {},
191
215
  ): SubmitResult {
216
+ if (closedStores.has(store)) throw new Error("job host is closed");
192
217
  const row = store.create(kind, config, outputPath);
218
+ let finish!: () => void;
219
+ const finished = new Promise<void>((resolve) => { finish = resolve; });
193
220
  const item: QueuedSpawn = {
221
+ abort: new AbortController(), finished, finish,
194
222
  store,
195
223
  jobId: row.id,
196
224
  entry: opts.entry ?? JOB_ENTRY_PATH,
225
+ compiled: opts.entry === undefined && opts.bin === undefined && JOB_ENTRY_PATH.includes("$bunfs"),
197
226
  bin: opts.bin ?? "bun",
198
227
  spawn: opts.spawn ?? Bun.spawn,
199
228
  onComplete: opts.onComplete,
229
+ acquire: opts.acquire,
200
230
  };
201
231
  spawnQueue.push(item);
202
232
  drainQueue();
@@ -215,7 +245,7 @@ function spawnNow(item: QueuedSpawn): void {
215
245
  // a file-backed DB.
216
246
  let proc: Bun.Subprocess<"ignore", "pipe", "pipe">;
217
247
  try {
218
- proc = spawn([bin, entry, jobId], {
248
+ proc = spawn(item.compiled ? [process.execPath, "__job", jobId] : [bin, entry, jobId], {
219
249
  stdout: "pipe",
220
250
  stderr: "pipe",
221
251
  env: {
@@ -230,14 +260,16 @@ function spawnNow(item: QueuedSpawn): void {
230
260
  error: errString(e),
231
261
  endedAt: nowIso(),
232
262
  });
263
+ } catch (error) {
264
+ console.error(`[jobs] failed to record spawn failure: ${errString(error)}`);
233
265
  } finally {
234
- releaseLease(jobId);
266
+ releaseLease(item);
235
267
  }
236
268
  return;
237
269
  }
238
270
 
239
- const row = store.get(jobId);
240
- const logPath = row?.log_path;
271
+ item.proc = proc;
272
+ const logPath = store.get(jobId)?.log_path;
241
273
  const logLine = (line: string) => {
242
274
  if (!line || !logPath) return;
243
275
  const ev: JobEvent = { type: "log", line };
@@ -252,7 +284,7 @@ function spawnNow(item: QueuedSpawn): void {
252
284
  // code 0 ⇒ trust the child's terminal status (it set done/failed itself).
253
285
  // non-zero ⇒ if the row never reached terminal (crash before the wrapper
254
286
  // could write), force it failed.
255
- if (code !== 0) {
287
+ try { if (code !== 0) {
256
288
  const cur = store.get(jobId);
257
289
  if (cur && (cur.status === "queued" || cur.status === "running")) {
258
290
  store.setStatus(jobId, "failed", {
@@ -260,15 +292,25 @@ function spawnNow(item: QueuedSpawn): void {
260
292
  endedAt: nowIso(),
261
293
  });
262
294
  }
295
+ } } catch (error) {
296
+ console.error(`[jobs] failed to record child exit: ${errString(error)}`);
297
+ } finally {
298
+ releaseLease(item);
299
+ try { item.onComplete?.(jobId, code); } catch {}
300
+ drainQueue();
263
301
  }
264
- releaseLease(jobId);
265
- try { item.onComplete?.(jobId, code); } catch {}
266
- drainQueue();
267
302
  })();
268
303
  }
269
304
 
270
- function releaseLease(jobId: string): void {
271
- if (gpuLeaseHolder === jobId) gpuLeaseHolder = null;
305
+ function releaseLease(item: QueuedSpawn): void {
306
+ try { item.lease?.dispose(); }
307
+ catch (error) { console.error(`[jobs] execution lease cleanup failed: ${errString(error)}`); }
308
+ finally {
309
+ item.lease = undefined;
310
+ if (gpuLeaseHolder === item.jobId) gpuLeaseHolder = null;
311
+ if (activeSpawn === item) activeSpawn = undefined;
312
+ item.finish();
313
+ }
272
314
  }
273
315
 
274
316
  /** Run the next queued subprocess job if the lease is free. */
@@ -279,8 +321,51 @@ export function drainQueue(): void {
279
321
  // A synchronous spawn failure releases the lease. Keep draining
280
322
  // iteratively so a run of bad queue entries cannot recurse or wedge the
281
323
  // first startable job behind them.
282
- spawnNow(next);
324
+ activeSpawn = next;
325
+ if (next.acquire) {
326
+ gpuLeaseHolder = next.jobId; // reserve FIFO order while inference drains
327
+ void Promise.resolve().then(() => next.acquire!(next.abort.signal)).then((lease) => {
328
+ next.lease = lease;
329
+ if (next.abort.signal.aborted) throw next.abort.signal.reason;
330
+ spawnNow(next);
331
+ }).catch((error) => {
332
+ try {
333
+ next.store.setStatus(next.jobId, "failed", { error: errString(error), endedAt: nowIso() });
334
+ } catch (recordError) {
335
+ console.error(`[jobs] failed to record admission failure: ${errString(recordError)}`);
336
+ } finally { releaseLease(next); }
337
+ }).finally(drainQueue);
338
+ } else spawnNow(next);
339
+ }
340
+ }
341
+
342
+ /** Dataset work stays in-process, but belongs to the application host too. */
343
+ export async function closeInProcessJobs(store: JobStore): Promise<void> {
344
+ closedStores.add(store);
345
+ const active = [...inProcess.get(store) ?? []];
346
+ for (const work of active) work.cancellation.cancel("engine_closed");
347
+ await Promise.all(active.map((work) => work.done));
348
+ }
349
+
350
+ /** Stop only this host's managed GPU jobs. Await process death before releasing
351
+ * the execution lease; queued and admission-waiting jobs never spawn. */
352
+ export async function closeSubprocessJobs(store: JobStore): Promise<void> {
353
+ closedStores.add(store);
354
+ for (let i = spawnQueue.length - 1; i >= 0; i--) {
355
+ const item = spawnQueue[i]!;
356
+ if (item.store !== store) continue;
357
+ spawnQueue.splice(i, 1);
358
+ store.setStatus(item.jobId, "failed", { error: "job host closed", endedAt: nowIso() });
359
+ item.finish();
283
360
  }
361
+ const active = activeSpawn;
362
+ if (!active || active.store !== store) return;
363
+ active.abort.abort(new Error("job host closed"));
364
+ const proc = active.proc;
365
+ if (proc) proc.kill("SIGTERM");
366
+ const force = proc ? setTimeout(() => { if (proc.exitCode === null) proc.kill("SIGKILL"); }, 3000) : undefined;
367
+ try { await active.finished; }
368
+ finally { if (force) clearTimeout(force); }
284
369
  }
285
370
 
286
371
  /** Read a child stream line-by-line, buffering partial trailing lines, and
@@ -0,0 +1,19 @@
1
+ import type { TaskClient } from "../contracts/completion";
2
+ import { throwIfCancelled } from "../engine/cancellation";
3
+ import type { JobEvent, JobRunner } from "./types";
4
+
5
+ /** Numerical runners retain their artifact policy; the client owns delivery
6
+ * and cooperative cancellation at progress boundaries. No event buffering. */
7
+ export function createJobTaskClient(runner: JobRunner): TaskClient<
8
+ Record<string, unknown>, JobEvent, { outputPath?: string } | void
9
+ > {
10
+ return {
11
+ async run(request, report, cancellation) {
12
+ const check = () => { if (cancellation) throwIfCancelled(cancellation); };
13
+ check();
14
+ const result = await runner((event) => { check(); report(event); check(); }, request);
15
+ check();
16
+ return result;
17
+ },
18
+ };
19
+ }
package/src/jobs/types.ts CHANGED
@@ -1,58 +1,2 @@
1
- // Shared job-system contract. Both the job runner (src/jobs/*) and every
2
- // job producer (src/dataset/*, src/quantize/*, src/train/*) speak this
3
- // vocabulary. Mirrors the optiq lab SSE event grammar so the web wizards
4
- // port over mechanically (started → log/stage/metric → done|failed).
5
-
6
- /** A single streamed job event, serialized one-per-line into the job's
7
- * NDJSON log and re-emitted to the browser as SSE `data:` frames. */
8
- export type JobEvent =
9
- | { type: "started"; ts: number }
10
- | { type: "log"; line: string }
11
- | {
12
- type: "stage";
13
- stage: string;
14
- progress?: number; // 0..1
15
- message?: string;
16
- [k: string]: unknown;
17
- }
18
- | {
19
- type: "metric";
20
- kind: "train" | "val";
21
- step: number;
22
- loss: number;
23
- [k: string]: unknown; // grad_norm, learning_rate, tokens_per_sec, accuracy, margin, progress, message
24
- }
25
- | { type: "done"; ts: number; output_dir?: string; summary?: unknown; [k: string]: unknown }
26
- | { type: "failed"; error: string; ts: number };
27
-
28
- /** Sink a job calls to report progress. Implementations append to the log
29
- * file and (for stage/metric events carrying `progress`/`message`) update
30
- * the SQLite row. Never throws — a logging failure must not kill a job. */
31
- export type Emit = (e: JobEvent) => void;
32
-
33
- /** The unit of work. Returns an optional output path recorded on the row.
34
- * Receives the parsed `config` from the submit request. */
35
- export type JobRunner = (
36
- emit: Emit,
37
- config: Record<string, unknown>,
38
- ) => Promise<{ outputPath?: string } | void>;
39
-
40
- /** Job kinds in the system. */
41
- export type JobKind = "quantize" | "finetune" | "dataset" | "noop" | "crash";
42
-
43
- export type JobStatus = "queued" | "running" | "done" | "failed" | "zombie";
44
-
45
- /** A persisted job row (shape returned by the HTTP `GET /api/jobs/:id`). */
46
- export interface JobRow {
47
- id: string;
48
- kind: string;
49
- status: JobStatus;
50
- config_json: string;
51
- progress: number;
52
- message: string | null;
53
- log_path: string;
54
- output_path: string | null;
55
- error: string | null;
56
- started_at: string;
57
- ended_at: string | null;
58
- }
1
+ /** Compatibility exports; producer and browser share the portable wire contract. */
2
+ export type * from "../contracts/jobs";
package/src/kv-scheme.ts CHANGED
@@ -86,6 +86,42 @@ export function kvBytesAt(
86
86
  value.linearStateBytes;
87
87
  }
88
88
 
89
+ /** Head dims MLX 0.32.2's fused multi-query SDPA kernel serves for L > 8
90
+ * queries on M1–M4 GPUs (mlx/backend/metal/scaled_dot_product_attention.cpp
91
+ * `use_fallback`: 192 and 256 always fall back off-NAX; `has_fused_kernel`
92
+ * lists 64/72/80/96/128 for the full kernel). Every other head dim runs
93
+ * mlx/fast.cpp's composed fallback, which materializes the bf16 scores
94
+ * tensor [heads, L, S] plus a bool mask per full-attention layer. The
95
+ * decode kernel (L ≤ 8) fuses 256, so this term is a prefill-only cost. */
96
+ export const FUSED_SDPA_HEAD_DIMS: ReadonlySet<number> = new Set([64, 72, 80, 96, 128]);
97
+ const SDPA_SCORES_BYTES = 2; // bf16 scores in q's dtype
98
+ const SDPA_FUSED_MAX_QUERIES = 8;
99
+
100
+ /** Peak bytes of the SDPA-fallback scores tensor live during one prefill
101
+ * chunk of `chunk` queries against a `ctx`-token context: the last chunk
102
+ * sees the longest keys, and the lazy graph keeps one layer's scores live
103
+ * at a time. Zero when every attention layer's head dim fuses. Sliding
104
+ * layers see at most `window + L` keys. Admission-only (fit and the
105
+ * in-flight memory guard); nothing here changes what is computed. */
106
+ export function sdpaFallbackBytes(config: ModelConfig, chunk: number, ctx: number): number {
107
+ const text = config.text;
108
+ const heads = text.numAttentionHeads || 0;
109
+ const queries = Math.min(chunk, ctx);
110
+ if (heads <= 0 || queries <= SDPA_FUSED_MAX_QUERIES) return 0;
111
+ let peak = 0;
112
+ const layers = text.numHiddenLayers || text.layerTypes.length;
113
+ for (let layer = 0; layer < layers; layer++) {
114
+ const type = text.layerTypes[layer] ?? "full_attention";
115
+ if (type === "linear_attention") continue;
116
+ const sliding = type === "sliding_attention" && text.slidingWindow > 0;
117
+ const headDim = sliding ? text.headDim : text.globalHeadDim;
118
+ if (FUSED_SDPA_HEAD_DIMS.has(headDim)) continue;
119
+ const keys = sliding ? Math.min(ctx, text.slidingWindow + queries) : ctx;
120
+ peak = Math.max(peak, heads * queries * keys * SDPA_SCORES_BYTES);
121
+ }
122
+ return peak;
123
+ }
124
+
89
125
  export class KvScheme {
90
126
  readonly kind: KvSchemeKind;
91
127
  readonly options: Readonly<ResolvedKvSchemeOptions>;
@@ -112,10 +148,17 @@ export class KvScheme {
112
148
  get cacheKey(): string {
113
149
  if (this.kind === "turbo") {
114
150
  const turbo = this.options.turboQuant!;
115
- return `turbo-k${turbo.kBits}v${turbo.vBits}`;
151
+ const start = this.options.quantizedKvStart ?? 0;
152
+ return `turbo-k${turbo.kBits}v${turbo.vBits}${start > 0 ? `-start${start}` : ""}`;
153
+ }
154
+ if (this.kind === "affine-uniform") {
155
+ const start = this.options.quantizedKvStart ?? 5000, group = this.options.kvGroupSize ?? 64;
156
+ return `kv${this.options.kvBits}${group !== 64 ? `-g${group}` : ""}${start > 0 ? `-start${start}` : ""}`;
157
+ }
158
+ if (this.kind === "affine-config") {
159
+ const start = this.options.quantizedKvStart ?? 0;
160
+ return `config${start > 0 ? `-start${start}` : ""}`;
116
161
  }
117
- if (this.kind === "affine-uniform") return `kv${this.options.kvBits}`;
118
- if (this.kind === "affine-config") return "config";
119
162
  return "bf16";
120
163
  }
121
164
 
@@ -154,14 +197,24 @@ export class KvScheme {
154
197
  return this.kind === "turbo" ? {} : this.generationOptions;
155
198
  }
156
199
 
157
- /** The current batch engine accepts only per-layer affine schemes whose
158
- * named cache instances can perform the required conversion. The capability
159
- * probe is mandatory for quantized schemes so config shape alone can never
160
- * authorize placement. */
161
- batchable(config: ModelConfig, canConvert?: (layerIdx: number) => boolean): boolean {
200
+ /** Affine layouts convert during solo prefill. TQ also has a transitional
201
+ * row layout that preserves each request's delayed conversion boundary. */
202
+ batchable(config: ModelConfig, canConvert?: (layerIdx: number) => boolean,
203
+ cacheLayerCount = config.text.numHiddenLayers, capabilities: { delayedAffine?: boolean } = {}): boolean {
162
204
  if (this.kind === "bf16") return true;
163
- if (this.kind !== "affine-config") return false;
205
+ if (this.kind !== "affine-config" && this.kind !== "affine-uniform" && this.kind !== "turbo") return false;
164
206
  if (!canConvert) return false;
207
+ const start = this.options.quantizedKvStart ?? (this.kind === "affine-uniform" ? 5000 : 0);
208
+ if (start > 0 && this.kind !== "turbo" && !capabilities.delayedAffine) return false;
209
+ if (this.kind === "affine-uniform" || this.kind === "turbo") {
210
+ // Some models share the donor prefix's KV in later layers. Probe the
211
+ // actual cache list, just as uniform conversion does.
212
+ for (let layer = 0; layer < cacheLayerCount; layer++) {
213
+ if (config.text.layerTypes[layer] === "linear_attention") continue;
214
+ if (!canConvert(layer)) return false;
215
+ }
216
+ return true;
217
+ }
165
218
  return this.options.kvConfig!.every((entry) => {
166
219
  if (entry.layerIdx < 0 || entry.layerIdx >= config.text.numHiddenLayers) return false;
167
220
  return (config.text.layerTypes[entry.layerIdx] ?? "full_attention") !== "linear_attention" &&
@@ -175,11 +228,15 @@ export function resolveKvScheme(input: {
175
228
  turboQuant?: TurboQuantScheme;
176
229
  config?: readonly KvQuantSpec[] | null;
177
230
  missingConfig?: "bf16" | "error";
231
+ quantizedKvStart?: number;
178
232
  }): KvScheme {
233
+ const start = input.quantizedKvStart;
234
+ if (start !== undefined && (!Number.isSafeInteger(start) || start < 0))
235
+ throw new Error("quantizedKvStart must be a nonnegative integer");
179
236
  if (input.turboQuant) {
180
237
  return new KvScheme("turbo", {
181
238
  turboQuant: input.turboQuant,
182
- quantizedKvStart: 0,
239
+ quantizedKvStart: start ?? 0,
183
240
  });
184
241
  }
185
242
  if (input.override === "off" || input.override === undefined)
@@ -190,10 +247,11 @@ export function resolveKvScheme(input: {
190
247
  throw new Error("model has no kv_config.json (--kv-quant config)");
191
248
  return new KvScheme("bf16", {});
192
249
  }
193
- return new KvScheme("affine-config", { kvConfig: [...input.config] });
250
+ return new KvScheme("affine-config", { kvConfig: [...input.config],
251
+ ...(start === undefined ? {} : { quantizedKvStart: start }) });
194
252
  }
195
253
  return new KvScheme("affine-uniform", {
196
254
  kvBits: input.override,
197
- quantizedKvStart: 0,
255
+ quantizedKvStart: start ?? 0,
198
256
  });
199
257
  }