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,10 +1,12 @@
1
+ import { cloneAttachments } from "./backends/mlx/checkpoint-state";
2
+ import { cleanupFailure, disposeResources } from "./engine/resources";
1
3
  import type { Cache } from "./model/gemma4";
2
4
  import type { PromptCacheEntry } from "./prompt-cache";
3
5
  import type { SpillItem, SpillQueue } from "./kv-store";
4
6
 
5
7
  export interface DurabilityGateway {
6
8
  readonly busy: boolean;
7
- runExclusive<T>(fn: () => Promise<T>): Promise<T>;
9
+ runWhenIdle<T>(fn: () => Promise<T>): Promise<T>;
8
10
  }
9
11
 
10
12
  export interface DurabilityPromptCache {
@@ -115,9 +117,9 @@ export class SsdDurabilityCoordinator {
115
117
  this.#attempts.set(key, task);
116
118
  try {
117
119
  const outcome = await task;
118
- if (outcome !== "failed" && this.#dirty.get(key) === rec)
120
+ if (outcome === "stored" && this.#dirty.get(key) === rec)
119
121
  this.#dirty.delete(key);
120
- if (outcome === "failed" && !force && this.#dirty.get(key) === rec)
122
+ if (outcome !== "stored" && !force && this.#dirty.get(key) === rec)
121
123
  this.#arm(key, this.busyRetryMs);
122
124
  return outcome;
123
125
  } finally {
@@ -128,14 +130,17 @@ export class SsdDurabilityCoordinator {
128
130
  async #store(rec: DirtySnapshot): Promise<StoreOutcome> {
129
131
  let snap: SpillItem | null = null;
130
132
  try {
131
- snap = await this.gateway.runExclusive(async () => {
133
+ snap = await this.gateway.runWhenIdle(async () => {
134
+ if (this.isAlreadyDurable(rec.tokens, rec.ns)) return null;
132
135
  const entry = this.promptCache.findExact(rec.tokens, rec.ns);
133
136
  if (!entry) return null;
134
- return {
135
- tokens: [...entry.tokens],
136
- caches: this.cloneCaches(entry.caches),
137
- ns: rec.ns,
138
- };
137
+ const caches = this.cloneCaches(entry.caches);
138
+ try {
139
+ return { tokens: [...entry.tokens], caches, ns: rec.ns,
140
+ attachments: cloneAttachments(entry.attachments) };
141
+ } catch (error) {
142
+ return cleanupFailure(error, () => disposeResources(caches));
143
+ }
139
144
  });
140
145
  } catch {
141
146
  return "failed";
@@ -150,7 +155,7 @@ export class SsdDurabilityCoordinator {
150
155
  this.#timers.clear();
151
156
 
152
157
  let flushedSnapshots = 0;
153
- let missingSnapshots = 0;
158
+ const missing = new Map<string, DirtySnapshot>();
154
159
  const droppedBefore = this.spillQueue.droppedCount;
155
160
  const failedBefore = this.spillQueue.failedCount;
156
161
 
@@ -160,22 +165,33 @@ export class SsdDurabilityCoordinator {
160
165
 
161
166
  // Flush one entry at a time. This prevents the queue cap from dropping a
162
167
  // boundary snapshot while a large final snapshot is already in flight.
168
+ // Attempt each record version once. Missing state stays dirty, so a
169
+ // second flush cannot report success without a real durable snapshot.
170
+ const attempted = new Set<DirtySnapshot>();
163
171
  while (this.#dirty.size > 0) {
164
- const keys = [...this.#dirty.keys()];
165
- let progressed = false;
166
- for (const key of keys) {
167
- if (!this.#dirty.has(key)) continue;
172
+ const records = [...this.#dirty.entries()].filter(([, rec]) => !attempted.has(rec));
173
+ if (records.length === 0) break;
174
+ for (const [key, rec] of records) {
175
+ if (this.#dirty.get(key) !== rec) continue;
176
+ attempted.add(rec);
168
177
  const outcome = await this.#attempt(key, true);
169
178
  await this.spillQueue.drain();
170
179
  if (outcome === "stored") {
171
180
  flushedSnapshots++;
172
- progressed = true;
173
181
  } else if (outcome === "missing") {
174
- missingSnapshots++;
175
- progressed = true;
182
+ missing.set(key, rec);
176
183
  }
177
184
  }
178
- if (!progressed) break;
185
+ }
186
+
187
+ // A later trimmable snapshot may now cover a superseded RAM ancestor.
188
+ // Reconcile against committed SSD coverage after all writes settle;
189
+ // never clear a missing prefix merely because another write succeeded.
190
+ for (const [key, rec] of missing) {
191
+ if (!this.isAlreadyDurable(rec.tokens, rec.ns)) continue;
192
+ if (this.#dirty.get(key) === rec) this.#dirty.delete(key);
193
+ missing.delete(key);
194
+ flushedSnapshots++;
179
195
  }
180
196
 
181
197
  const stats = this.stats;
@@ -186,9 +202,9 @@ export class SsdDurabilityCoordinator {
186
202
  stats.pendingSpills === 0 &&
187
203
  stats.droppedSpills === droppedBefore &&
188
204
  stats.failedSpills === failedBefore &&
189
- missingSnapshots === 0,
205
+ missing.size === 0,
190
206
  flushedSnapshots,
191
- missingSnapshots,
207
+ missingSnapshots: missing.size,
192
208
  elapsedMs: performance.now() - started,
193
209
  };
194
210
  }
package/src/tool-call.ts CHANGED
@@ -161,7 +161,7 @@ export function parseToolCalls(text: string): ParsedToolCall[] {
161
161
 
162
162
  const TOOL_CALL_BLOCK_RE = /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/gi;
163
163
  const XML_FUNCTION_EQUALS_RE = /^\s*<function=([^>\s]+)>\s*([\s\S]*?)\s*<\/function>\s*$/i;
164
- const XML_PARAMETER_EQUALS_RE = /<parameter=([^>\s]+)>\s*([\s\S]*?)\s*<\/parameter>/gi;
164
+ const XML_PARAMETER_EQUALS_RE = /<parameter=([^>\s]+)>([\s\S]*?)<\/parameter>/gi;
165
165
  const GLM52_ARG_KEY_RE = /<arg_key>/i;
166
166
  const GLM52_ARG_PAIR_RE = /<arg_key>\s*([\s\S]*?)\s*<\/arg_key>\s*<arg_value>\s*([\s\S]*?)\s*<\/arg_value>/gi;
167
167
  const XML_FUNCTION_ATTR_RE = /<function\s+name=["']([^"']+)["']\s*>\s*([\s\S]*?)\s*<\/function>/gi;
@@ -208,11 +208,9 @@ function toolParameterSchema(
208
208
  return null;
209
209
  }
210
210
 
211
- /** Oracle `_decode_tool_parameter_value`: string-typed params stay raw
212
- * text (a path like "2025" must not become a number); everything else
213
- * is JSON-decoded when possible. */
214
- function decodeToolValue(value: string, schema: Record<string, unknown> | null): unknown {
215
- const text = decodeXml(value);
211
+ /** Decode a value after its caller removes protocol-specific delimiters.
212
+ * String-typed params stay raw; everything else is JSON-decoded when possible. */
213
+ function decodeToolValue(text: string, schema: Record<string, unknown> | null): unknown {
216
214
  if (!text) return "";
217
215
  const types = schemaTypeNames(schema);
218
216
  if (types.size && [...types].every((t) => t === "string" || t === "null")) return text;
@@ -546,23 +544,59 @@ function parseJsonToolCall(block: string): ParsedToolCall | null {
546
544
  return repairs.length ? { ...call, repaired: true, repairs } : call;
547
545
  }
548
546
 
549
- function parseXmlEqualsToolCall(block: string, tools: ToolSpec[]): ParsedToolCall | null {
547
+ function parseXmlEqualsToolCall(block: string, tools: ToolSpec[], strict = false): ParsedToolCall | null {
550
548
  const match = XML_FUNCTION_EQUALS_RE.exec(block);
551
549
  XML_FUNCTION_EQUALS_RE.lastIndex = 0;
552
550
  if (!match) return null;
551
+ if (strict && (match.index !== 0 || match[0].length !== block.length)) return null;
553
552
  const name = match[1]!.trim();
554
553
  const body = match[2]!;
555
554
  const args: Record<string, unknown> = {};
555
+ let cursor = 0;
556
556
  for (const param of body.matchAll(XML_PARAMETER_EQUALS_RE)) {
557
+ if (strict && body.slice(cursor, param.index).trim()) return null;
557
558
  const key = param[1]!.trim();
558
559
  if (!key) throw new Error(`tool '${name}' contains an empty parameter name`);
559
- args[key] = decodeToolValue(param[2]!, toolParameterSchema(tools, name, key));
560
+ if (strict && Object.hasOwn(args, key)) return null;
561
+ // Qwen's format carries raw text, not XML entity-encoded text. Match
562
+ // mlx_lm.tool_parsers.qwen3_coder: remove one framing newline at each
563
+ // end, preserving indentation, literal entities and JSON string escapes.
564
+ const value = param[2]!.replace(/^\n|\n$/g, "");
565
+ args[key] = decodeToolValue(value, toolParameterSchema(tools, name, key));
566
+ cursor = param.index + param[0].length;
560
567
  }
561
568
  XML_PARAMETER_EQUALS_RE.lastIndex = 0;
569
+ if (strict && body.slice(cursor).trim()) return null;
562
570
  return { name, arguments: args };
563
571
  }
564
572
 
565
- function parseGlm52ToolCall(block: string, tools: ToolSpec[]): ParsedToolCall | null {
573
+ /** One complete envelope, with full consumption and no syntax repair. Used
574
+ * to establish structural fill boundaries, never to verify model predictions. */
575
+ export function parseStrictToolCall(text: string, tools: ToolSpec[]): ParsedToolCall | null {
576
+ const open = "<tool_call>", close = "</tool_call>";
577
+ if (!text.startsWith(open) || !text.endsWith(close)) return null;
578
+ const body = text.slice(open.length, -close.length).trim();
579
+ if (body.includes(open) || body.includes(close)) return null;
580
+ try {
581
+ let call: ParsedToolCall | null;
582
+ if (body.startsWith("{")) {
583
+ // JSON.parse must succeed before the existing payload interpretation.
584
+ JSON.parse(body);
585
+ call = parseJsonToolCall(body);
586
+ } else {
587
+ call = parseXmlEqualsToolCall(body, tools, true) ?? parseGlm52ToolCall(body, tools, true);
588
+ }
589
+ if (!call || call.repaired || !tools.some(tool => toolSpecName(tool) === call.name)) return null;
590
+ return call;
591
+ } catch { return null; }
592
+ }
593
+
594
+ /** A textual argument delimiter is complete only after its closing bracket. */
595
+ export function hasCompleteToolValueDelimiter(text: string): boolean {
596
+ return /^(?:<\/parameter>|<\/arg_value>)/.test(text.trimStart());
597
+ }
598
+
599
+ function parseGlm52ToolCall(block: string, tools: ToolSpec[], strict = false): ParsedToolCall | null {
566
600
  const firstArg = block.search(GLM52_ARG_KEY_RE);
567
601
  const name = (firstArg < 0 ? block : block.slice(0, firstArg)).trim();
568
602
  if (!name || !/^[^\s<>]+$/.test(name)) return null;
@@ -576,7 +610,8 @@ function parseGlm52ToolCall(block: string, tools: ToolSpec[]): ParsedToolCall |
576
610
  if (body.slice(cursor, pair.index).trim()) return null;
577
611
  const key = pair[1]!.trim();
578
612
  if (!key) throw new Error(`tool '${name}' contains an empty argument name`);
579
- args[key] = decodeToolValue(pair[2]!, toolParameterSchema(tools, name, key));
613
+ if (strict && Object.hasOwn(args, key)) return null;
614
+ args[key] = decodeToolValue(decodeXml(pair[2]!), toolParameterSchema(tools, name, key));
580
615
  cursor = pair.index + pair[0].length;
581
616
  matched = true;
582
617
  }
@@ -594,7 +629,7 @@ function parseXmlAttrToolCalls(text: string, tools: ToolSpec[]): ParsedToolCall[
594
629
  for (const param of body.matchAll(XML_PARAM_ATTR_RE)) {
595
630
  const key = param[1]!.trim();
596
631
  if (!key) throw new Error(`tool '${name}' contains an empty parameter name`);
597
- args[key] = decodeToolValue(param[2]!, toolParameterSchema(tools, name, key));
632
+ args[key] = decodeToolValue(decodeXml(param[2]!), toolParameterSchema(tools, name, key));
598
633
  }
599
634
  XML_PARAM_ATTR_RE.lastIndex = 0;
600
635
  calls.push({ name, arguments: args });
@@ -616,9 +651,11 @@ export function parseGeneratedToolCalls(text: string, tools: ToolSpec[]): Parsed
616
651
  const calls: ParsedToolCall[] = [];
617
652
  for (const block of text.matchAll(TOOL_CALL_BLOCK_RE)) {
618
653
  const body = block[1]!.trim();
619
- const parsed = parseJsonToolCall(body) ??
620
- parseXmlEqualsToolCall(body, tools) ??
621
- parseGlm52ToolCall(body, tools);
654
+ // A valid Qwen parameter may itself contain JSON. Dispatch by the outer
655
+ // format before JSON repair can mistake that value for the tool envelope.
656
+ const parsed = /^<function=/i.test(body)
657
+ ? parseXmlEqualsToolCall(body, tools)
658
+ : parseJsonToolCall(body) ?? parseGlm52ToolCall(body, tools);
622
659
  if (!parsed) throw new Error("unsupported tool_call payload format");
623
660
  calls.push(parsed);
624
661
  }
@@ -728,7 +728,7 @@ const BWD_SG_SOURCE = String.raw`
728
728
  // (flipped 2026-07-02 — the kernel-perf-review backlog #1 flip). The old
729
729
  // 0.66%→2.7% dh-accuracy scare was on RANDOM targets (flat softmax, the filter's
730
730
  // worst case — see PLAN.md "filter-on-real-data"); the real-data gate
731
- // (scripts/experiments/flash-cce-filter-realdata.ts) measured, on REAL chunk-ORPO
731
+ // (flash-cce-filter-realdata.ts (deleted 2026-08-23; git history)) measured, on REAL chunk-ORPO
732
732
  // data (M1 Max, 2026-07-02): CPM5 eps 1e-5 = 0.343% dh for 1.41×; e4b = 0.158% dh
733
733
  // for 1.70× — and the teacher-forced full-logits fidelity shows the filter's added
734
734
  // error is far below the flash-vs-full fp-reassociation scatter that already
@@ -781,7 +781,7 @@ function bwdSgKernel(dimTiles: number, filterEps: string, blockEps: string): Met
781
781
  // accumulated into D's frags LANE-LOCALLY (frag += frag — no get_coord math, the
782
782
  // per-lane element mapping is identical between temp and D). dh atomic-added
783
783
  // across the NBLK vocab-block programs. Validated dh ~1e-4 rel vs autograd.
784
- // (Design + parity: scripts/experiments/steel-bwd-htile-test.ts.)
784
+ // (Design + parity: steel-bwd-htile-test.ts (deleted 2026-08-23; git history).)
785
785
  // PHASE-2 DEQUANT: the Wd[32,HTw] staging uses MLX's vectorized+fused
786
786
  // QuantizedBlockLoader (the SAME loader the forward uses), NOT a manual scalar
787
787
  // dequant. The wrinkle: phase-2's GEMM contracts over vocab (K=32) and outputs H
@@ -31,6 +31,8 @@ import type { RuntimeModel } from "../model/factory";
31
31
  * backward recompute, and a stateful (appending) cache would corrupt on the
32
32
  * second run. */
33
33
  export class TrainingCache implements Cache {
34
+ /** Training-only adapter; never admitted, merged, or persisted. */
35
+ signature(): string { return "train:training"; }
34
36
  offset = 0;
35
37
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
36
38
  // Return fresh views: the caller disposes its input k/v right after (its
@@ -67,6 +69,8 @@ export class TrainingCache implements Cache {
67
69
  * threw on the missing conv/recurrent/advance surface (the recorded
68
70
  * "mlx-bun perplexity cannot score qwen3_5" gap). */
69
71
  export class TrainingSSMCache implements Cache {
72
+ /** Training-only adapter; never admitted, merged, or persisted. */
73
+ signature(): string { return "train:training-ssm"; }
70
74
  offset = 0;
71
75
  specRound: null = null;
72
76
  get conv(): MlxArray | null { return null; }
@@ -192,6 +196,8 @@ export function buildBatchedPadMask(
192
196
  * forwardLayers only disposes the mask it pulled from makeMask, so makeMask
193
197
  * hands back a fresh view each call and keeps the canonical copy itself. */
194
198
  class BatchedMaskCache implements Cache {
199
+ /** Training-only adapter; never admitted, merged, or persisted. */
200
+ signature(): string { return "train:batched-mask"; }
195
201
  constructor(
196
202
  private readonly inner: Cache,
197
203
  private readonly window: number | null,
package/src/train/loss.ts CHANGED
@@ -1190,7 +1190,7 @@ export function fusedRespLogpMean(
1190
1190
  // [chunk,vocabBlock]. Otherwise the whole-vocab analytic head below.
1191
1191
  const vb = vocabBlock > 0 && vocabBlock < V ? vocabBlock : 0;
1192
1192
  // Auto-dispatch by M (kernel backlog #3; measured 2026-07-02 on the M1 Max,
1193
- // scripts/experiments/head-dispatch-sweep.ts): the fused QM head is FASTER
1193
+ // head-dispatch-sweep.ts (deleted 2026-08-23; git history)): the fused QM head is FASTER
1194
1194
  // at short M (e4b M=64 1.6×, M=512 1.13×; CPM5 faster at EVERY M) — the
1195
1195
  // flash head's win is residency, plus time at long M on e4b (M≥2048 after
1196
1196
  // the 2026-07-02 filter/blockMax flip; 8K: 10.7 vs 13.2 s). So a
@@ -11,7 +11,7 @@
11
11
  // prompt dominates (our chunk/document fine-tunes), ~0 when the response does.
12
12
  //
13
13
  // Bit-exact with the two-forward path (validated in
14
- // scripts/experiments/prefix-shared-parity.ts): each predicted token uses a hidden
14
+ // prefix-shared-parity.ts (deleted 2026-08-23; git history)): each predicted token uses a hidden
15
15
  // conditioned on exactly the same context + RoPE positions —
16
16
  // chosen[k] from H[P-1+k] (k=0..Rc-1; H[P-1] = prompt's last)
17
17
  // rejected[k] from H[P-1] (k=0) / H[P+Rc+k-1] (k≥1)
@@ -62,6 +62,8 @@ export function blockSparseMask(P: number, Rc: number, Rr: number): MlxArray {
62
62
  * TrainingCache) but `makeMask` returns the block-sparse mask. One per layer;
63
63
  * MiniCPM5's runLayerRange calls makeMask once and disposes the returned arr. */
64
64
  export class PrefixSharedCache implements Cache {
65
+ /** Training-only adapter; never admitted, merged, or persisted. */
66
+ signature(): string { return "train:prefix-shared"; }
65
67
  offset = 0;
66
68
  constructor(
67
69
  private readonly P: number, private readonly Rc: number, private readonly Rr: number,
@@ -371,6 +373,8 @@ export function blockSparsePrefixMaskGemma(P: number, Rc: number, Rr: number, wi
371
373
  * type serves both (the window arg selects the sliding term). One per DONOR
372
374
  * layer (sharers reuse donors' fetched KV). */
373
375
  class Gemma4PrefixSharedCache implements Cache {
376
+ /** Training-only adapter; never admitted, merged, or persisted. */
377
+ signature(): string { return "train:gemma4prefix-shared"; }
374
378
  offset = 0;
375
379
  constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
376
380
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
@@ -2,7 +2,7 @@
2
2
  // segment-by-segment so only ONE segment's activations are ever live, instead
3
3
  // of holding every layer's recompute activations at once (the spike that makes
4
4
  // naive gradient checkpointing crash at long context). Design + proof:
5
- // docs/design/segmented-backward-training.md (mechanism validated on a toy in
5
+ // docs/design/orpo-training.md (mechanism validated on a toy in
6
6
  // scripts/ckpt-mem-test.ts, SEG mode: 0.000% grad error, peak below per-layer
7
7
  // checkpointing; bit-exact on MiniCPM5 under flash attention).
8
8
  //
@@ -24,7 +24,7 @@
24
24
  // `value_and_grad(sum(stop_grad(dh) (.) segment_forward(...)))` is numerically
25
25
  // identical but LEAKS ~one activation buffer per segment per step at the mlx
26
26
  // level (measured; not GC/cache/synchronize/reuse reclaimable — see
27
- // docs/design/segmented-backward-training.md §9.4). mlx_vjp takes the cotangent
27
+ // docs/design/orpo-training.md §9.4). mlx_vjp takes the cotangent
28
28
  // directly, needs no surrogate, and does not leak.
29
29
  //
30
30
  // IMPORTANT lifetime fact: mlx `eval` does NOT detach — an eval'd array retains
@@ -130,7 +130,7 @@ export function planSegmentsBySize(nLayers: number, segSize: number): [number, n
130
130
  // segment_size 1 measures 14.59 GB @8K (+3% step time) vs 17.5 @seg2. Below
131
131
  // that needs the [M,V]-bounded SFT head (backlog #8, ~3 GB head-vjp spike)
132
132
  // and/or an O(L)-memory attention backward. Evidence:
133
- // scripts/experiments/seg-isolation-smoke.ts + MLX_BUN_SEG_MEM_LOG=1 probes.
133
+ // seg-isolation-smoke.ts (deleted 2026-08-23; git history) + MLX_BUN_SEG_MEM_LOG=1 probes.
134
134
 
135
135
  /** Detach `a` into a graph-free LEAF holding its exact (evaluated) bytes, then
136
136
  * dispose `a`. mlx retains the upstream graph after `eval`, so a segment
@@ -1517,6 +1517,8 @@ export class SegmentedBackwardOrpoGemma4 {
1517
1517
  * and null for full layers; the window arg selects the sliding term. One per DONOR
1518
1518
  * layer (sharers reuse donors' fetched KV inside runLayerRange). */
1519
1519
  class Gemma4PrefixSharedSegCache implements Cache {
1520
+ /** Training-only adapter; never admitted, merged, or persisted. */
1521
+ signature(): string { return "train:gemma4-prefix-shared-seg"; }
1520
1522
  offset = 0;
1521
1523
  constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
1522
1524
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
@@ -82,7 +82,7 @@ export interface TrainConfig {
82
82
  * beats per-layer checkpointing at long context, where naive checkpointing
83
83
  * holds every layer's recompute activations at once and crashes. B=1 SFT
84
84
  * only (the responseOnlyCe path); mutually exclusive with gradCheckpoint.
85
- * Numerically identical to off. See docs/design/segmented-backward-training.md. */
85
+ * Numerically identical to off. See docs/design/orpo-training.md. */
86
86
  segmentSize: number;
87
87
  /** Keep ALL eval-step checkpoints (full mountable adapters under
88
88
  * checkpoints/step-<NNNNN>-val<loss>/) + write metrics.json. Off by default
@@ -440,12 +440,12 @@ export async function trainLora(
440
440
 
441
441
  // ---------------------------------------------------------------------------
442
442
  // Segment planning: uniform planSegmentsBySize only. A full-attention
443
- // ISOLATION planner (kernel-review backlog #2 / segmented-backward-training.md
443
+ // ISOLATION planner (kernel-review backlog #2 / docs/design/orpo-training.md
444
444
  // §5) was built and A/B-measured 2026-07-02: ZERO peak win — mlx's sdpa
445
445
  // backward is O(L²) for EVERY layer (~3.5 GB/layer @8K e4b), sliding included,
446
446
  // so the worst segment is set by layer count alone and segment_size is the
447
447
  // whole knob (seg1 = 14.59 GB @8K, +3% step time — fits the 24 GB M4 Pro).
448
- // Evidence: scripts/experiments/seg-isolation-smoke.ts; note in segmented.ts.
448
+ // Evidence: seg-isolation-smoke.ts (deleted 2026-08-23; git history); note in segmented.ts.
449
449
  // ---------------------------------------------------------------------------
450
450
 
451
451
  // ---------------------------------------------------------------------------
@@ -473,7 +473,7 @@ async function sftLoop(
473
473
  // Segmented backward: stream the SFT backward segment-by-segment (only one
474
474
  // segment's activations live at a time). Phase A is MiniCPM5 SFT B=1 only;
475
475
  // it replaces the single value_and_grad below and is mutually exclusive with
476
- // gradient checkpointing (see docs/design/segmented-backward-training.md).
476
+ // gradient checkpointing (see docs/design/orpo-training.md).
477
477
  const useSegmented = cfg.segmentSize > 0;
478
478
  let segmented: SegmentedBackward | SegmentedBackwardGemma4 | null = null;
479
479
  if (useSegmented) {
@@ -532,13 +532,13 @@ async function sftLoop(
532
532
  // hand-rolled flash kernel is an opt-in (MLX_BUN_TRAIN_ATTN=flash) O(L)-memory
533
533
  // path for memory-bound long context — now correct (its dK-transpose and
534
534
  // dQ-causal-barrier bugs were fixed; FD-validated in
535
- // scripts/experiments/flash-fd-check.ts), though ~30× slower than ops.sdpa.
535
+ // flash-fd-check.ts (deleted 2026-08-23; git history)), though ~30× slower than ops.sdpa.
536
536
  // Cleared in finally.
537
537
  //
538
538
  // GEMMA GUARD (same convention as the launcher's env sanitization in
539
539
  // cli.ts / job.ts, but enforced at the trainer): e4b on this path SIGTRAPed
540
540
  // (uncatchable native crash) at multi-K sequence lengths (>=2K, reproduced in
541
- // scripts/experiments/segmented-grad-test-e4b.ts; docs/reference/training.md)
541
+ // segmented-grad-test-e4b.ts (deleted 2026-08-23; git history); docs/reference/training.md)
542
542
  // and has NOT been re-validated at that scale since the two kernel fixes —
543
543
  // the regression tests stop at T<=256. cli.ts even defaults e4b seq to 8192,
544
544
  // so a stale `export MLX_BUN_TRAIN_ATTN=flash` from a MiniCPM5 experiment
@@ -8,7 +8,7 @@
8
8
  // n = min(ceil(duration_ms/40), 750) (processing_gemma4.py) — embed the full
9
9
  // sequence, then overwrite the soft-token rows with the towers' features.
10
10
  //
11
- // Attention semantics (audio-input-plan.md §3.3, resolved vs the oracle):
11
+ // Attention semantics (docs/design/generic-model-support.md §3.3, resolved vs the oracle):
12
12
  // - image runs attend bidirectionally among themselves (bidirMask), BUT
13
13
  // any audio token in the prompt disables the vision overlay entirely —
14
14
  // mixed image+audio prompts run fully causal (bidirMask = null);
@@ -115,9 +115,42 @@ export class Qwen3VLVisionTower {
115
115
  return names;
116
116
  }
117
117
 
118
+ /** Checkpoint-generation naming for the tower. Pre-5.8 artifacts store
119
+ * `vision_tower.*`; the transformers-5.8 export stores `model.visual.*`
120
+ * (mlx-lm's text-only qwen3_5 drops BOTH prefixes in `Model.sanitize`, so
121
+ * the mapping between them is ours). Verified 2026-09-01 across
122
+ * Qwen3.8-27B-tqalloc-norot vs the staged pre-5.8 artifact: 331/333
123
+ * tensors byte-identical under the rename, patch_embed.proj.weight
124
+ * identical under the layout permutation below, and merger.linear_fc2
125
+ * differs only because the staged artifact is rotation-folded. */
126
+ static #sourceName(canonical: string, prefix: string): string {
127
+ return prefix + canonical.slice("vision_tower.".length);
128
+ }
129
+
130
+ /** HF stores the Conv3d patch embed as (out, in, kT, kH, kW); MLX (and this
131
+ * tower) want (out, kT, kH, kW, in). Proven a pure permutation on the
132
+ * artifacts above — transpose(0,2,3,4,1) of the 5.8 tensor is bit-equal to
133
+ * the pre-5.8 one. */
134
+ static #fixPatchEmbed(w: Map<string, MlxArray>): void {
135
+ const key = "vision_tower.patch_embed.proj.weight";
136
+ const a = w.get(key);
137
+ if (!a || a.shape.length !== 5) return;
138
+ if (a.shape[4] === 3) return; // already MLX layout
139
+ const t = ops.transposeAxes(a, [0, 2, 3, 4, 1]);
140
+ const c = ops.contiguous(t);
141
+ t.dispose();
142
+ a.dispose();
143
+ w.set(key, c);
144
+ }
145
+
118
146
  /** Load one safetensors file's map and move `wanted` tensors into `out`
119
- * (missing names are skipped; the map is always freed). */
120
- static #takeFromFile(path: string, wanted: string[], out: Map<string, MlxArray>): void {
147
+ * under their CANONICAL (`vision_tower.*`) names; `source` maps each
148
+ * canonical name to the stored one. Missing names are skipped; the map is
149
+ * always freed. */
150
+ static #takeFromFile(
151
+ path: string, wanted: string[], out: Map<string, MlxArray>,
152
+ source: (canonical: string) => string = (n) => n,
153
+ ): void {
121
154
  const arrMap = new BigUint64Array([C.mlx_map_string_to_array_new()]);
122
155
  const metaMap = new BigUint64Array([C.mlx_map_string_to_string_new()]);
123
156
  const arrMapPtr = ptr(arrMap);
@@ -136,7 +169,7 @@ export class Qwen3VLVisionTower {
136
169
  if (out.has(name)) continue;
137
170
  const slot = new BigUint64Array([C.mlx_array_new()]);
138
171
  const slotPtr = ptr(slot);
139
- if (C.mlx_map_string_to_array_get(slotPtr, handle, ptr(cstr(name))) !== 0) {
172
+ if (C.mlx_map_string_to_array_get(slotPtr, handle, ptr(cstr(source(name)))) !== 0) {
140
173
  C.mlx_array_free(read.u64(slotPtr, 0));
141
174
  continue;
142
175
  }
@@ -156,28 +189,37 @@ export class Qwen3VLVisionTower {
156
189
  // OptiQ-convention sidecar (their artifacts + ours pre-2026-08-18).
157
190
  Qwen3VLVisionTower.#takeFromFile(sidecar, names, weights);
158
191
  } else {
159
- // In-main fallback: the bf16 vision_tower.* tensors live in the main
192
+ // In-main fallback: the bf16 tower tensors live in the main
160
193
  // safetensors shards (the mlx-vlm convention — one copy serves both
161
- // ecosystems; sidecar duplication retired 2026-08-18).
194
+ // ecosystems; sidecar duplication retired 2026-08-18). The 5.8-family
195
+ // export names them `model.visual.*` instead of `vision_tower.*`.
162
196
  const idxPath = `${modelDir}/model.safetensors.index.json`;
163
197
  let files: string[];
198
+ let prefixes = ["vision_tower.", "model.visual."];
164
199
  if (existsSync(idxPath)) {
165
200
  const idx = JSON.parse(readFileSync(idxPath, "utf8")) as
166
201
  { weight_map: Record<string, string> };
167
- files = [...new Set(
168
- names.map((n) => idx.weight_map[n]).filter((f): f is string => !!f),
169
- )];
202
+ prefixes = prefixes.filter((p) =>
203
+ Qwen3VLVisionTower.#sourceName(names[0]!, p) in idx.weight_map);
204
+ files = [...new Set(prefixes.flatMap((p) => names
205
+ .map((n) => idx.weight_map[Qwen3VLVisionTower.#sourceName(n, p)])
206
+ .filter((f): f is string => !!f)))];
170
207
  if (files.length === 0)
171
- throw new Error(`no vision_tower tensors in ${modelDir} (index has none)`);
208
+ throw new Error(`no vision tower tensors in ${modelDir} (index has none)`);
172
209
  } else {
173
210
  files = ["model.safetensors"];
174
211
  }
175
212
  for (const f of files)
176
- Qwen3VLVisionTower.#takeFromFile(`${modelDir}/${f}`, names, weights);
213
+ for (const p of prefixes)
214
+ Qwen3VLVisionTower.#takeFromFile(
215
+ `${modelDir}/${f}`, names, weights,
216
+ (n) => Qwen3VLVisionTower.#sourceName(n, p),
217
+ );
177
218
  }
178
219
  for (const name of names)
179
220
  if (!weights.has(name))
180
221
  throw new Error(`qwen vision weights missing tensor ${name} (sidecar or in-main)`);
222
+ Qwen3VLVisionTower.#fixPatchEmbed(weights);
181
223
  } catch (e) {
182
224
  for (const [, a] of weights) a.dispose();
183
225
  throw e;
package/src/web/app.html CHANGED
@@ -1216,7 +1216,7 @@
1216
1216
  <!-- PWA installability (plan §9 Phase 3, beat-matrix Axis 10): manifest +
1217
1217
  inline SVG icon (src/web/manifest.webmanifest, src/web/icon.svg — no
1218
1218
  binary PNGs, per the hygiene gate; some browsers won't render an SVG
1219
- app icon and that's an accepted tradeoff, see features-matrix.md).
1219
+ app icon and that's an accepted tradeoff, see docs/reference/server-config.md).
1220
1220
  theme-color matches the app's --bg token so the OS chrome doesn't
1221
1221
  flash a mismatched color on launch. -->
1222
1222
  <link rel="manifest" href="/manifest.webmanifest">