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,543 +0,0 @@
1
- # Segmented backward: long-context LoRA training that beats the reference
2
-
3
- **Status: Phase A (MiniCPM5) + Phase B (e4b) BOTH WORK — 2026-06-16.** MiniCPM5:
4
- bit-exact, no leak, chunk-eval 95.10 (beats the 91.70 baseline). e4b: forward
5
- bit-exact, grads bf16-class, trains **all 42 layers at 8K (17.5 GB)** where
6
- `mlx_lm.lora --grad-checkpoint` OOMs trying the same (verified — mlx-lm must drop
7
- to its default 16 trainable layers, 25.7 GB, to fit 8K). §1–8 are the original
8
- design dossier; **§9 = Phase A results, §10 = Phase B (e4b) results — read those
9
- first for current state.**
10
-
11
- ## 0. The one-paragraph version
12
-
13
- mlx-bun (and Python mlx) gradient checkpointing does NOT stream the backward —
14
- it holds all 42 layers' recompute activations at once, so e4b's training backward
15
- spikes to **23 GB live @2048** (resting is only 6.95 GB) and **crashes at 4096+**.
16
- We proved a fix — **segmented backward** — that forces per-segment streaming:
17
- forward saves detached boundary activations at segment edges, then backprop runs
18
- segment-by-segment via `mlx_vjp` (the shipped production implementation — see §9.4;
19
- an earlier surrogate-loss `value_and_grad` approach was tried but leaked ~32 MB/seg/step
20
- and was replaced). Only one segment's activations are ever live.
21
- On an activation-dominated toy it beat per-layer
22
- checkpointing (4.26 vs 5.97 GB) with **bit-exact gradients**. Extrapolated to e4b
23
- this lands at **~10 GB @8K** — fitting on the 32 GB M1 Max, where the optiq/mlx-lm
24
- reference cannot (it crashes on e4b @2048+). Next: wire it into the trainer,
25
- MiniCPM5 first (clean), then e4b (handle KV-sharing + per-layer-input + the
26
- O(L²) full-attention layers + mixed-precision quant in the segmentation plan).
27
-
28
- ---
29
-
30
- ## 1. What is already done and CORRECT (do not redo)
31
-
32
- All committed-worthy, validated this session:
33
-
34
- - **Chunk fine-tune experiment (the original goal) — DONE on MiniCPM5-1B.**
35
- Baseline 11.89/100 → fine-tuned **91.70/100** on lucien's 25-case chunk holdout
36
- (valid-JSON rate 12% → 100%). Full mlx-bun loop: train → save adapter (hot-swap
37
- format) → `AdapterManager.mount` → generate. Writeup:
38
- `docs/archive/investigations/chunk-finetune-experiment.md`. Scripts: `scripts/chunk-eval.ts`,
39
- `scripts/chunk-finetune.ts`, `scripts/chunk-filter.ts`. Adapter:
40
- `~/.cache/mlx-bun-finetunes/minicpm5-chunk-final`.
41
-
42
- - **`ops.sdpa` is CORRECT** (NOT the inverse of an earlier wrong claim). Validated
43
- vs autograd ground truth (`scripts/sdpa-vs-manual.ts`): dQ/dK/dV all 0.00%. Use
44
- it for training. It's O(L²) memory in the backward (mlx autographs the fused op).
45
-
46
- - **Flash dK kernel FIXED.** The dKV Metal kernel emitted dK with its (Tkv,D) axes
47
- transposed in the buffer; fix in `flashBackward` (`src/model/flash-attention.ts`):
48
- `reshape(ops.contiguous(transposeAxes(dK,[0,1,3,2])), [B,Hkv,Tkv,D])`. Validated
49
- vs autograd: dK 0.06% non-GQA, 0.12% GQA D=256 (e4b geom). flash is now correct +
50
- O(L) memory. (But flash is ~30× slower per iter and crashes on e4b at 42-layer
51
- scale — see §6. Default training attn is ops.sdpa; flash is opt-in via
52
- `MLX_BUN_TRAIN_ATTN=flash`.)
53
-
54
- - **Combined-eval fix in the trainer.** `trainer.ts` used to eval `value` (forward)
55
- separately via `value.toFloat32()` before `globalNorm(grads)` — that frees the
56
- forward and forces the backward to recompute+hold everything, crippling
57
- checkpointing. Now one `evalAll([value, ...grads])` (Python's `mx.eval(v,g)`
58
- pattern). Verified no MiniCPM5 regression. Toy: separate-eval 0.3% reduction →
59
- combined-eval 16%.
60
-
61
- - **Per-step memory instrumentation.** `MLX_BUN_MEM_LOG=1` logs
62
- `PEAK(live) / active / cache` per step in `trainer.ts`.
63
-
64
- ## 2. The diagnosis (why naive checkpointing fails) — measured, not guessed
65
-
66
- Measurement methodology (the key unlock, courtesy of the active-vs-cache insight):
67
- `mlx_get_peak_memory` = **peak LIVE (active)**, NOT active+cache (proved: toy
68
- active-after 8.66 + cache-after 6.61 = 15.27 > peak 14.90, impossible if peak were
69
- total). MLX holds freed buffers in a **cache** (not returned to OS) so RSS overstates
70
- live memory; use `activeMemory()` / `cacheMemory()` / `clearCache()` to separate
71
- them (`src/mlx/ffi.ts`). Caveat from the MoE expert-offload work
72
- (`docs/archive/investigations/expert-offload-single-user-moe.md`): `clearCache()` does NOT
73
- reliably return device-buffer RAM to the OS — only the mmap clean-page path does.
74
-
75
- Facts established:
76
- - **Forward memory is FLAT** (~8 GB at any seq) — the forward streams fine. The
77
- whole per-token cost is in the BACKWARD.
78
- - **e4b @2048 (ops.sdpa + grad-ckpt): peak-live 23.2 GB, resting 6.95 GB.** The
79
- 16 GB spike is per-layer MLP/hidden activations held across ALL 42 layers — the
80
- checkpointed backward does NOT stream.
81
- - **Checkpointing works and matches Python** (toy: both remove exactly 2.28 GB).
82
- The earlier "checkpoint is 0%/broken" claim was a measurement artifact
83
- (separate-eval + peak-vs-live confusion). It's NOT broken — it just doesn't
84
- stream, and neither does Python's (optiq crashes on e4b @2048+ too).
85
- - **e4b @4096 crashes immediately at ~7 GB RSS** (not gradual OOM). A 28 GB
86
- `set_memory_limit` forcing cache eviction/reuse did NOT help → it's a genuine
87
- backward-eval allocation failure, not cache non-reuse.
88
- - Memory scales LINEARLY in seq: `peak(L) ≈ 7.7 GB + 7.6 MB/token`. @4096 ≈ 38 GB
89
- (> 32 → crash). At the IDEAL streamed rate (~0.2–0.5 MB/tok) e4b@8K ≈ 10 GB.
90
-
91
- ## 3. The fix: segmented backward (PROVEN)
92
-
93
- Reference implementation + proof: **`scripts/ckpt-mem-test.ts`** (`SEG=n` mode).
94
-
95
- Algorithm:
96
- 1. **Forward, saving boundaries.** Run the layer stack; at each segment edge (group
97
- of `n` layers) materialize (`eval`) and **detach** (`stopGradient`) the hidden
98
- state into a boundary leaf. Discard intra-segment interiors. Boundaries are
99
- small (one [B,L,hidden] per edge) and cheap.
100
- 2. **Compute the loss** from the final hidden (LM head + response-only CE) and its
101
- gradient w.r.t. the final hidden — this `dh_out` seeds the last layer segment.
102
- 3. **Backward, reverse over segments.** For each segment k (last→first), call
103
- **`mlx_vjp`** (bound as `Vjp` in `src/mlx/autograd.ts`) over
104
- `[boundary_leaf_k, ...segment_LoRA_params]` with cotangent `dh_out`. It returns
105
- `[dh_in, ...dLoRA_segment]`. `dh_in` becomes `dh_out` for segment k−1; accumulate
106
- `dLoRA`. Only segment k's activations are live during its vjp.
107
- **NOTE:** an earlier design used a surrogate-loss `value_and_grad` here
108
- (`sum(stop_grad(dh)⊙segment_forward(...))`). That proved to leak ~32 MB per
109
- segment per step (linear, no plateau) and was replaced by `mlx_vjp` in §9.4.
110
- The production code uses `Vjp`/`mlx_vjp` throughout — not surrogate-loss
111
- `value_and_grad`.
112
-
113
- Why it's exact: `d/dx sum(stop_grad(dh)⊙y) = (dy/dx)ᵀ dh = dLoss/dx`, and likewise
114
- for params — this is reverse-mode AD done in segments. Verified **0.000%** vs the
115
- full `value_and_grad`.
116
-
117
- Proof numbers (activation-dominated toy, `L=8192 D=1024 LAYERS=24`, the e4b regime):
118
-
119
- | method | peak | grads |
120
- |---|---|---|
121
- | full, no checkpoint | 6.81 GB | — |
122
- | per-layer `Checkpoint` | 5.97 GB | — |
123
- | **segmented (SEG=4)** | **4.26 GB** (active-live 2.52 ≈ resting 1.68 + 1 segment) | **0.000%** |
124
- | segmented (SEG=2) | 4.66 GB | 0.000% |
125
-
126
- Segmented beats per-layer checkpointing and approaches the ideal floor. **Optimal
127
- granularity exists** (SEG=4 < SEG=2): a few layers/segment keeps kernel fusion while
128
- still streaming. NOT all-at-once (spikes), NOT each-tiny-op (loses fusion).
129
-
130
- Recompute cost: each segment's backward recomputes its own layers' forward once →
131
- total recompute overhead ≈ **1× forward, independent of segment count**. So segment
132
- count is a knob for PEAK, with ~fixed time cost (plus minor per-`value_and_grad`
133
- and boundary-materialization overhead). The wall-clock target (finish the chunk
134
- task in budget) is mostly set by this fixed 1× recompute, not the segment count.
135
-
136
- ## 4. Integration plan
137
-
138
- ### Phase A — MiniCPM5-1B first (clean real-model proof)
139
- Plain 24-layer Llama: no KV-sharing, no per-layer-input, no sliding window, uniform-ish
140
- quant. The trivial case to validate the machinery on a real model.
141
-
142
- 1. **Model:** add `Gemma4Model`/`MiniCPM5Model` method `runLayerRange(h, aIdx, bIdx,
143
- cache, ...) -> h'` that runs layers `[aIdx, bIdx)` on hidden `h` (the existing
144
- `forwardLayers` loop body, parameterized by range). Keep `forwardLayers` as the
145
- non-segmented path.
146
- 2. **Trainer:** add a `segmented` training path (flag `TrainConfig.segments` or auto;
147
- gate alongside the existing `gradCheckpoint`). It:
148
- - runs the segmented forward (save detached boundaries),
149
- - computes response-only CE loss and `dFinalHidden` via a `Vjp` of the loss head
150
- with scalar cotangent 1.0 (already memory-bounded),
151
- - runs the backward per-segment using **`mlx_vjp`** (`Vjp` in
152
- `src/mlx/autograd.ts`): for each segment k (last→first), calls
153
- `vjp.apply([boundary_k, ...LoRA_k], [dh_out])` to get `[dh_in, ...dLoRA_k]`,
154
- threads `dh_in` as `dh_out` for segment k−1, and accumulates `dLoRA` — no
155
- surrogate scalar, no `value_and_grad` (see §9.4 for why that approach leaked
156
- ~32 MB/seg/step and was replaced),
157
- - also handles the embedding + (e4b) per-layer-input grads from the first
158
- segment's `dh_in`.
159
- 3. **Validate:** grads bit-match the current trainer (response-only CE, same seed);
160
- measure peak drop with `MLX_BUN_MEM_LOG=1`. Expect a clear reduction even though
161
- MiniCPM5 already fits — it's the template.
162
-
163
- ### Phase B — e4b (the goal). Extra boundary state to thread across segments:
164
- - **KV-shared donor cache.** Layers [0,24) are donors (own caches); [24,42) are
165
- sharers reusing the last same-type donor's K/V. Concretely (verified in
166
- `gemma4.ts`: `numDonors = numHiddenLayers − numKvSharedLayers = 24`,
167
- `previousKvs`/`cacheIndex`): all sliding sharers reuse **donor 22**'s K/V, all
168
- full sharers reuse **donor 23**'s K/V. So when a sharer falls in a later segment
169
- than its donor, **save that donor's K/V (rope-applied) as a detached boundary**
170
- and thread it into the sharer's segment forward AND its recompute. K/V are tiny
171
- ([B,2,L,256] ≈ 1 KB/tok per donor), so this is cheap. (Note: `runCheckpointedLayer`
172
- already threads shared K/V as checkpoint outputs — reuse that plumbing.)
173
- - **Per-layer-input** (`computePerLayerInputs`): [B,L,42,256] computed once from the
174
- embedding, sliced per layer. Compute it in the forward, **detach + save as a
175
- boundary**, slice per layer inside each segment. Its gradient (back to the embed /
176
- projection) accumulates from the per-segment grads.
177
- - **Sliding vs full attention.** 7 full-attention layers (5,11,17,23,29,35,41); the
178
- rest sliding (window 512). With ops.sdpa the **full layers are O(L²)** in the
179
- backward (≈1 GB bf16 @8K for the [1,8,L,L] scores); sliding are O(L·512). The
180
- segmentation must treat a full layer as a heavy unit (see §5).
181
- - **Masks:** one per attention type (sliding/full), built at offset 0; pass into each
182
- segment (cheap; the sliding [L,L] mask is the only quadratic term besides the full
183
- attention — already handled by the current trainer).
184
-
185
- ## 5. The segmentation strategy (the part to get right)
186
-
187
- > **2026-07-02 CORRECTION — this section's cost model is WRONG, measured.**
188
- > The full-attention isolation planner was built and A/B'd @8K on real chunk
189
- > rows (scripts/experiments/seg-isolation-smoke.ts, MLX_BUN_SEG_MEM_LOG=1
190
- > phase probes): ZERO peak win (18.09 vs 18.02 GB seg2). The premise "sliding
191
- > layers are O(L·window) in the backward" is false — mlx's sdpa BACKWARD
192
- > materializes the O(L²) scores for EVERY layer (~3.5 GB/layer @8K e4b; the
193
- > sliding window is only an additive mask; a sliding PAIR costs +7.1 GB ≈
194
- > sliding+full's +7.15). The worst segment is therefore set by LAYER COUNT
195
- > alone and `segment_size` is the whole knob: **seg1 = 14.59 GB @8K, +3% step
196
- > time, loss identical — fits the 24 GB M4 Pro today.** The head vjp adds
197
- > ~3 GB (full-[M,V] responseOnlyCe — kernel-review backlog #8). Going below
198
- > ~14 GB needs that head fix plus an O(L)-memory attention backward (flash
199
- > track), not smarter segmentation. Section kept for the (still-correct)
200
- > donor/boundary constraints; ignore its sliding-vs-full peak arithmetic.
201
-
202
- NOT a naive "divide by N". Compute the split points from the model:
203
-
204
- **Constraints & inputs (all readable from the model/config):**
205
- - Layer attention type per index (`layer_types`): sliding vs full. Full = O(L²) heavy.
206
- - KV donor/sharer boundary at layer 24 (donors below, sharers above); donor→sharer
207
- edges that must become saved boundaries (donors 22, 23).
208
- - Per-layer mixed-precision quant bits (e.g. MiniCPM5's OptiQ config has layer-0 at
209
- 8-bit, others 4/8 mixed; e4b's HF config is default 4-bit but the strategy must
210
- read per-layer bits generically). Bits affect **compute time** per layer (8-bit
211
- matmul slower than 4-bit), slightly the weight residency — NOT the activation size.
212
- - Uniform per-layer activation size (hidden 2560 + MLP gate/up 2×10240, bf16).
213
-
214
- **Memory cost model** (per candidate segmentation):
215
- ```
216
- peak ≈ resting(model + grads + optimizer + saved boundaries)
217
- + max_over_segments( Σ_layers_in_seg uniform_activation(L)
218
- + Σ_full_layers_in_seg attn_OL2(L) ) [+ backward transient]
219
- ```
220
- At 8K: uniform_activation ≈ 0.38 MB/tok·layer; attn_OL2(full, ops.sdpa) ≈ ~1 GB.
221
- So a 6-layer segment with one full layer @8K ≈ 6·0.38·8192 ≈ 2.3 GB + ~1–2 GB full
222
- ≈ 3.3–4.3 GB; with resting ~6.95 GB that's ~10–11 GB. **To stay ≤10 GB, isolate
223
- full-attention layers** (put each in a short segment) and group sliding layers.
224
-
225
- **Time cost model:** total ≈ base(fwd+bwd) + 1×fwd recompute (≈fixed) +
226
- per-segment `value_and_grad`/boundary overhead (∝ segment count, small). Weight
227
- per-layer forward time by quant bits + attention type. Target: ≤ a chosen
228
- wall-clock for the chunk task (e.g. N iters × per-iter ≤ budget).
229
-
230
- **Natural alignment:** the 6-layer period (`sssssF`) makes 7 segments of 6 (one full
231
- layer each) the obvious first cut, with the donor/sharer boundary at 24 respected
232
- (segments 0–3 = donors, 4–6 = sharers). If peak still > target at 8K, split each
233
- 6-group around its full layer (e.g. [0–4 sliding][5 full] …) so no segment carries a
234
- full layer plus many sliding layers. The optimizer picks split points to **minimize
235
- peak subject to the time budget**, respecting: (a) KV donor→sharer boundaries become
236
- saved K/V, (b) don't split mid-attention-pair unnecessarily, (c) keep ≥~3 layers per
237
- segment for fusion.
238
-
239
- **Deliverable:** a `planSegments(model, {L, peakTargetGB, timeBudget})` function that
240
- returns the split indices + which donor K/V to save, computed from the above.
241
-
242
- ## 6. Notes, risks, alternatives
243
- - **Flash vs ops.sdpa for the full layers.** Flash (now dK-correct) is O(L) and would
244
- remove the full-attention O(L²) term entirely — but flash is ~30× slower per iter
245
- and currently crashes on e4b at 42-layer scale (immediate C++ exception; separate
246
- bug). With segmented backward, the full-attention O(L²) is only transient within
247
- one segment, so ops.sdpa may suffice; revisit flash only if the full term blows the
248
- budget at 8K AND its e4b crash is fixed.
249
- - The chunk data needs ≥4096 (median 4092; 0% ≤2048; 98% ≤8192). Success = e4b
250
- trains at 8192 ≤ ~10 GB AND the chunk eval (`scripts/chunk-eval.ts`) shows a
251
- quality delta on e4b like MiniCPM5's.
252
- - Training needs no env-flag sanitization (the no-vjp fused CustomKernels that
253
- once required `MLX_BUN_PERF_KERNEL=0 MLX_BUN_FUSED_GELU=0` were deleted
254
- 2026-07-05; see docs/design/unified-engine-frontier-plan.md §6).
255
- - MLX `Date.now()`/random caveats don't apply here; standard determinism via seed.
256
-
257
- ## 7. Key files
258
- - `scripts/ckpt-mem-test.ts` — segmented-backward proof + memory harness (SEG/CKPT/REUSE, active/cache/peak).
259
- - `scripts/ft-chunk-smoke.ts` — e4b train/fwd/infer smoke (GRAD_CKPT, MEM_LIMIT_GB, MLX_BUN_MEM_LOG).
260
- - `scripts/sdpa-vs-manual.ts`, `scripts/flash-dk-debug.ts`, `scripts/flash-grad-test.ts` — gradient correctness.
261
- - `scripts/chunk-{eval,finetune,filter}.ts` — the chunk experiment pipeline.
262
- - `src/train/trainer.ts` — training loop (combined-eval, mem-log, gradCkpt ctx); contains the segmented path (`TrainConfig.segmentSize`).
263
- - `src/train/forward.ts`, `src/train/loss.ts` — training forward + response-only CE.
264
- - `src/model/gemma4.ts` — e4b model: `forwardLayers`, `runCheckpointedLayer`, `computePerLayerInputs`, KV-sharing (`numDonors`, `previousKvs`, `cacheIndex`), `layer_types`, `runLayerRange` (added for segmented backward).
265
- - `src/model/minicpm5.ts` — MiniCPM5 (Phase A).
266
- - `src/mlx/autograd.ts` (`ValueAndGrad`, `Vjp`), `src/mlx/checkpoint.ts` (`Checkpoint`), `src/mlx/ffi.ts` (memory fns, `stopGradient`, `evalAll`, `mlx_vjp`).
267
- - Memory: `[[e4b-lora-training-seqlen-ceiling]]` (full findings), `[[opssdpa-dk-vjp-bug]]`, `[[three-level-fidelity-tree-model]]`.
268
-
269
- ## 8. Original first action (COMPLETED)
270
- ~~Phase A step 1: add `runLayerRange` to `MiniCPM5Model`, then a segmented training
271
- path in `trainer.ts`, validate bit-exact grads + measure peak on
272
- `scripts/ft-chunk-smoke.ts`-style MiniCPM5 run. Then Phase B (e4b) with the §5 plan.~~
273
-
274
- **Both Phase A and Phase B are done — see §9 and §10.** The genuine remaining work
275
- is `planSegments` full-attention isolation for the ~10 GB @8K e4b target: put each
276
- O(L²) full-attn layer (5,11,17,23,29,35,41) in its own short segment so it isn't
277
- materialized alongside sliding-layer activations. Currently 17.5 GB @8K (seg2);
278
- resting is ~6.6 GB, so ~3.4 GB of transient budget remains. The mechanism and tunable
279
- segmentation knob are proven; this is a peak-optimization step. See §10 close.
280
-
281
- ---
282
-
283
- ## 9. Phase A IMPLEMENTED + validated (2026-06-16, session 2)
284
-
285
- Phase A is built and the mechanism is **proven on the real MiniCPM5 model**.
286
- Two findings landed (one a win, one a blocker). Code:
287
- - `src/model/minicpm5.ts` — `runLayerRange(h, aIdx, bIdx, cache)` (added;
288
- `forwardLayers` now delegates to it). NOTE it does **not** dispose its input
289
- `h` (the caller / autograd owns the boundary leaf).
290
- - `src/train/segmented.ts` — `SegmentedBackward` class (build once, `.step(batch)`
291
- per iter) + `planSegmentsBySize`. Reusable per-segment value_and_grads.
292
- - `src/train/trainer.ts` — `TrainConfig.segmentSize` (0 = off; >0 = layers per
293
- segment). Gated in `sftLoop`, mutually exclusive with `gradCheckpoint`,
294
- MiniCPM5-only for now. `src/train/job.ts` threads `segment_size`.
295
- - `src/mlx/array.ts` — `MlxArray.fromBytesCopy(bytes, shape, dtype)` (a COPYING
296
- leaf constructor via `mlx_array_new_data`, page-aligned, unlike `fromView`).
297
- - `src/train/loss.ts` — `responseOnlyCe` is now exported.
298
- - Harness: `scripts/segmented-grad-test.ts` (correctness + peak + `LEAK_LOOP`),
299
- `scripts/seg-debug.ts` (forward-fidelity bisect), `scripts/vag-leak-test.ts`
300
- (minimal value_and_grad leak repro). `scripts/chunk-finetune.ts` takes `SEG=n`.
301
-
302
- ### 9.1 Correctness — the mechanism is exact
303
- `scripts/segmented-grad-test.ts` (MiniCPM5, synthetic B=1 batch):
304
- - **Under flash attention: grads bit-match the full value_and_grad — relNorm
305
- 0.0000%, maxAbs 0.0, loss identical.** The segmentation algorithm (forward
306
- boundaries → loss-head dh → reverse per-segment surrogate) is reverse-mode AD
307
- done in segments, exactly equal to one big backward. PROVEN.
308
- - Run training with `MLX_BUN_TRAIN_ATTN=flash` for bit-exact segmented grads.
309
-
310
- ### 9.2 Forward-fidelity finding: `ops.sdpa` fused-eager ≠ autograd forward
311
- Under the default `ops.sdpa`, the segmented grads diverge ~6% (relNorm) from the
312
- full value_and_grad — **not** a segmentation bug. Root cause (bisected in
313
- `scripts/seg-debug.ts`): mlx's fused `ops.sdpa` computes a **different bf16 result
314
- in its eager forward (12.0712) than in its autograd-decomposed forward (12.0568)**
315
- — a 0.12% gap, independent of LoRA (holds at lora scale 0). Flash uses one forward
316
- for both → no gap. Consequences:
317
- - The **existing non-segmented trainer already trains against the autograd forward
318
- (12.0568) while inference uses the eager forward (12.0712)** — pre-existing, not
319
- introduced here.
320
- - Segmented saves boundaries with the eager forward but recomputes/backprops with
321
- the autograd forward, so the dh seed is inconsistent under `ops.sdpa` → ~6% grad
322
- perturbation (bf16-class; segmented's *loss* still matches the true eager forward
323
- exactly). Fine for LoRA fine-tuning; use flash if you want exactness.
324
-
325
- ### 9.3 Memory — the win is real and large
326
- `MLX_BUN_MEM_LOG=1`, MiniCPM5 (24 layers, hidden 1536), peak LIVE:
327
-
328
- | seq | full backward | segmented (SEG=4) | saved |
329
- |---|---|---|---|
330
- | 512 | 3.15 GB | 1.87 GB | 1.3 GB |
331
- | 2048 | 10.91 GB | 3.44 GB | **7.5 GB (3.2×)** |
332
- | 4096 | **21–26 GB** (spikes) | 6–8 GB | ~3× |
333
-
334
- This is the thesis confirmed: at 4096 the full backward spikes to 21–26 GB (would
335
- crash e4b); segmented stays at 6–8 GB. Segmented makes long-context training FIT.
336
-
337
- ### 9.4 A per-segment memory leak — FOUND and FIXED (use mlx_vjp, not value_and_grad)
338
- The first segmented implementation used the optiq surrogate-loss trick — a
339
- `value_and_grad` of `sum(stop_grad(dh) (.) segment_forward(...))` per segment. It
340
- **leaked ~32 MB of genuinely-live memory per segment per step** (linear, no
341
- plateau: `LEAK_LOOP` 1.12 → 6.61 GB over 30 iters at SEG=4/2048; ~0.19 GB/step;
342
- would OOM well before a 300-iter run). The leak survived `clearCache`,
343
- `Bun.gc(true)`, `synchronize`, disposing every returned array, and reusing the
344
- value_and_grads across steps. It was segmented-specific (the non-segmented trainer
345
- is flat). Minimal repro that pins it to `value_and_grad`: `vag-leak-test`
346
- **MODE=layers** (runLayerRange under value_and_grad) leaks ~6 MB/iter; **MODE=vjp**
347
- (the same forward via mlx_vjp) is **flat**.
348
-
349
- **Fix:** drive each segment's backward with **`mlx_vjp`** (the natural primitive:
350
- forward + an explicit cotangent `dh`) instead of a surrogate-loss `value_and_grad`.
351
- New binding `Vjp` in `src/mlx/autograd.ts` (+ `mlx_vjp` in `ffi.ts`). Per segment:
352
- `vjp.apply([boundary, ...LoRA], [dh])` returns outputs (the segment output, unused)
353
- and vjps `[dh_in, ...dLoRA]` — exactly what the chain needs, no surrogate scalar.
354
- The loss head is likewise a vjp of `responseOnlyCe(finalNorm(boundary))` with a
355
- scalar cotangent 1.0, returning the loss and `dh`. Result: **active is flat**
356
- (0.93 GB × 15 iters in `LEAK_LOOP`; 1.04 GB × 12 iters end-to-end through the
357
- trainer at SEQ=4096), with the memory win intact (peak 10.91 → 3.29 GB @2048).
358
-
359
- NOTE on the OTHER memory fix that is also in place: mlx `eval` does **not** detach —
360
- an eval'd array keeps its upstream graph. Each forward boundary is therefore copied
361
- into a fresh leaf via `detachLeaf` (`MlxArray.fromBytesCopy`), otherwise each
362
- boundary drags a whole layer-stack's activations (~0.1 GB per 4-layer segment of
363
- *within-step* retention before this fix). This is independent of the vjp fix.
364
-
365
- ### 9.5 STATUS: Phase A complete. Next actions
366
- Phase A (MiniCPM5) is done and production-ready: mechanism bit-exact (flash),
367
- memory win confirmed, no leak, trains end-to-end through `scripts/chunk-finetune.ts`
368
- (`SEG=n`). Remaining:
369
- 1. ✅ **DONE** — real 300-iter segmented fine-tune (SEG=4, SEQ=4096, ops.sdpa):
370
- **peak 6.51 GB** (non-seg baseline was 25.47 GB), 35.6 min, val loss 0.231→0.189,
371
- no leak (active flat at 1.04 GB the whole run). `chunk-eval` on the holdout:
372
- **95.10/100** — EXCEEDS the non-segmented baseline (91.70) and the pre-tune
373
- baseline (11.89). json/schema/labels/nonempty 100%, anchors 86%. Segmented
374
- training produces a quality adapter. Adapter:
375
- `~/.cache/mlx-bun-finetunes/minicpm5-chunk-segmented`.
376
- 2. ✅ **DONE — see §10.** **Phase B (e4b)** with the §5 plan (KV-sharing,
377
- per-layer-input, full-attn O(L²) isolation). e4b `runLayerRange` added to
378
- `Gemma4Model` with donor-KV / per-layer-input boundary threading;
379
- `SegmentedBackwardGemma4` + vjp machinery wired into trainer, validated on real
380
- e4b. Full details in §10.
381
-
382
- ---
383
-
384
- ## 10. Phase B (e4b) — BUILT + VALIDATED (2026-06-16)
385
-
386
- **Status: e4b segmented backward works.** `SegmentedBackwardGemma4`
387
- (`src/train/segmented.ts`) + the model-side `runLayerRange` are done, wired into
388
- the trainer (Gemma4 path), and validated on the real e4b
389
- (`scripts/segmented-grad-test-e4b.ts`):
390
- - **Forward bit-exact** vs the full value_and_grad (loss identical at every L).
391
- - **Grads: single-consumer donor reuse is BIT-EXACT** (2-segment split at 24:
392
- 0/258 targets off). With the natural 7-segment cut the donor K/V grad is ~0.97%
393
- (relNorm) off the full backward — **bf16 non-associativity, established by
394
- discriminating tests (NOT a logic bug, and NOT "irreducible because I must match
395
- bf16" — that earlier inference was wrong):**
396
- - single-consumer reuse is bit-exact (no sum to reorder);
397
- - the error is FLAT in consumer-segment count (1→0%, 2→0.98%, 3→0.97%, 6→0.96%,
398
- 9→1.16% — jumps once, doesn't compound);
399
- - it tracks SUMMAND count, not segment count: donor 22 (sliding, 15 sharers)
400
- ≈0.46%, donor 23 (full, 3 sharers) ≈0.000%;
401
- - the donor-22 grad is grouping-DEPENDENT (~0.5% moving from a 2- vs 9-consumer
402
- split) for BOTH bf16 AND fp32 accumulation — neither is grouping-invariant.
403
- fp32 cross-segment accumulation does NOT help (identical at the natural cut,
404
- marginally worse at fine cuts): the dominant non-associativity is mlx's
405
- WITHIN-vjp bf16 cotangent sum per consumer, which regrouping the sharers
406
- changes; only an fp32 forward could remove it. So bf16 is the right (simplest)
407
- choice; ~1% is bf16-class and fine for LoRA. (The producer's `dh_in` depends on
408
- `dKV`, so the donor-22 ~0.46% smears thinly across earlier layers — broad but
409
- small, ~1.4% worst-target.)
410
- - **The error is CONTROLLABLE BY GROUPING, not fixed by nature.** The lever is
411
- the SEGMENT CUT — how a donor's sharers distribute across consumer segments —
412
- NOT the accumulation dtype. The floor is bit-exact: with all of a donor's
413
- sharers in ONE consumer segment (the 1-consumer cut) there is no sum to
414
- reorder, 0/258 off. Fewer/larger consumer segments → tighter grad (toward
415
- bit-exact) but higher peak; more/smaller → ~1% but lower peak. So if you ever
416
- needed it tighter, keep a donor's sharers together (coarser sharer-side cut),
417
- trading some peak for fidelity. You almost certainly won't — ~0.5–1% bf16-class
418
- is fine for LoRA (CPM5 converged healthily to 95.10) — but the lever exists and
419
- it is the cut, not the dtype.
420
- - **Memory — VERIFIED vs the real reference (`mlx_lm.lora --train --grad-checkpoint`,
421
- the way a user trains, oracle venv, freshly measured).** An earlier draft of
422
- this doc compared against *mlx-bun's own* checkpointed full backward (22.96 GB
423
- @2048, crash @4096) and claimed "the reference crashes at 4K" — that was WRONG:
424
- mlx-bun's gradient checkpointing is itself ineffective
425
- (`[[e4b-lora-training-seqlen-ceiling]]` pt 9), so it is NOT the honest baseline.
426
- **GROUPING — they differ, and the first table I drew did not match them.**
427
- mlx-lm checkpoints PER-LAYER: `grad_checkpoint(model.layers[0])` patches
428
- `type(layer).__call__` to wrap every layer in `mx.checkpoint`, i.e. 42 groups of
429
- 1, fixed (no coarser/finer knob). Ours groups `segmentSize` layers per segment
430
- (tunable). The honest, MATCHED-grouping comparison (both per-layer = group of 1,
431
- all 42 layers, batch 1):
432
-
433
- | L | mlx-lm (per-layer) | ours segSize=1 (per-layer) | ours segSize=6 |
434
- |---|---|---|---|
435
- | 2048 | 12.84 GB | **8.76 GB** | 11.05 |
436
- | 4096 | 20.87 GB | **10.93 GB** | 16.06 |
437
- | 8192 | **OOM** (25.72 @16 layers) | **15.29 GB** | 17.5 (seg2) |
438
-
439
- So at matched grouping ours is lower at every length, AND at 8K mlx-lm OOMs
440
- training all 42 layers (must drop to 16, 25.7 GB) while ours trains all 42.
441
- **Two confounds make this not a pure segmentation-vs-checkpoint number, and I
442
- have not isolated them:** (1) mlx-lm's `default_loss` materializes the full
443
- `[1,L,262144]` logits + their grad (~4 GB @4K), while ours uses response-only CE;
444
- (2) ours fully streams the backward (detached per-segment value_and_grads) vs
445
- mlx-lm's per-layer checkpoint inside one backward. Both are legitimate mlx-bun
446
- choices, but the headline gap is part CE-optimization, part mechanism — not all
447
- "segmentation beats checkpointing." (segSize is the peak↔grad-fidelity knob:
448
- finer = lower peak but more donor-KV bf16 deviation; §10 grad note.) Separately,
449
- mlx-bun's OWN checkpoint is the broken one (23 GB @2048 vs mlx-lm's 12.8) — that's
450
- why segmented matters *within* mlx-bun, and fixing it is orthogonal.
451
- - **Trainer end-to-end** (e4b, SEQ=2048, SEG=4, real chunk data): peak 10 GB,
452
- active flat (no leak), adapter saved.
453
- - TWO contiguity fixes were essential: `detachLeaf` must force a row-major copy
454
- (`ops.contiguous`) — the donor K/V are TRANSPOSED views and `rawBytes` reads
455
- the buffer linearly (without this the forward was 4% wrong); and the producer
456
- segment outputs contiguous donor K/V to match the row-major cotangent.
457
-
458
- **To reach the ~10 GB @8K target** (currently 17.5 GB @8K, seg2): the resting is
459
- ~6.6 GB (e4b weights), so ~3.4 GB transient is the budget. Next is the §5
460
- full-attention ISOLATION `planSegments` — put each O(L²) full-attn layer
461
- (5,11,17,23,29,35,41) in its own short segment so it isn't materialized alongside
462
- sliding-layer activations — plus possibly flash for the full layers (O(L); flash
463
- crashes on e4b at multi-K today — a separate bug). The mechanism + tunable knob
464
- are proven; this is a peak-optimization, not a blocker.
465
-
466
- **Model side** (`src/model/gemma4.ts`, additive — `forwardLayers` untouched):
467
- - `runLayerRange(h, aIdx, bIdx, cache, masks, perLayer, donorKvIn) -> {h, donorKvOut}`
468
- — runs layers `[aIdx,bIdx)`, slices `perLayer` per layer, threads KV-shared
469
- donors: a sharer reads its donor's K/V from `donorKvIn` (earlier segment) or
470
- `donorKvOut` (same range); reused donors' K/V (e4b: {22,23}) are returned for
471
- later segments, non-reused donors' K/V disposed. Does NOT dispose `h`.
472
- - `makeTrainingMasks(cache, L)` — sliding + full masks, built once.
473
- - `embedForSegmented(ids) -> {hScaled, perLayer}` — scaled input embedding (first
474
- boundary) + the `[B,L,nLayers,width]` per-layer-input tensor.
475
- - `reusedDonors` getter ({22,23} for e4b, ∅ for dense).
476
- - **Validate first** (when GPU free): running the full range through `runLayerRange`
477
- must bit-match `forwardLayers` (a forward-equivalence check on the 12B/e4b parity
478
- harness) before trusting the backward.
479
-
480
- - **PARITY vs mlx-lm — VERIFIED bit-exact (the correctness oracle).** Ran
481
- `mlx_lm.lora`'s actual `default_loss` and `nn.value_and_grad` against ours on the
482
- SAME fixed 2048-token input (`scripts/parity-vs-mlxlm.ts`,
483
- `scripts/grad-parity-vs-mlxlm.ts`; mlx-lm side in `/tmp/mlxlm-*-parity.py`):
484
- - **forward + loss:** verified across 6 mask spans (varying promptLen incl. the
485
- edges ntoks=2047 and ntoks=1), checking the SUMMATION not just one number:
486
- the **denominator** (our `M = (len-1)-max(0,promptLen-1)`) == mlx-lm's
487
- `ntoks = mask.sum()` BIT-EXACT on every span; **response-only CE == full-masked
488
- CE** on every span (so response-only is a pure memory optimization, not a
489
- parity risk); the loss is BIT-EXACT vs mlx-lm on 4/6 spans and **0.5 float32-ulp**
490
- (~1e-6, ~5e-5%) on 2 — a logsumexp/reduction f32 non-associativity (full-masked
491
- shows the same residual, so it's NOT the response-only summation), far below
492
- bf16 and harmless;
493
- - **gradients (FULL value_and_grad = our non-segmented trainer): BIT-EXACT** — dB
494
- over all 42 q_proj layers (identical injected A, B=0, rank 8, scale 20) matches
495
- mlx-lm 0.000e+0. So mlx-bun's non-segmented training equals mlx-lm's bit-for-bit
496
- (loss AND grad), extending the inference logit-parity oracle to training;
497
- - **segmented gradients: ~2.3% (bf16-class)** off mlx-lm (q_proj dB) — the
498
- donor-KV bf16 non-associativity (above), not a logic bug. So the SEGMENTED path
499
- is NOT bit-exact with mlx-lm; it's bf16-class. Both `ops.sdpa` value_and_grads
500
- (ours and mlx-lm's) use the same autograd forward, which is why FULL is exact.
501
- (Config note: mlx-lm's default LoRA scale is 20.0; mlx-bun's `DEFAULT_TRAIN_CONFIG`
502
- is 1.0 — a config difference, not correctness. The chunk runs used 1.0.)
503
-
504
- **Trainer side — `SegmentedBackwardGemma4` (BUILT + VALIDATED, `src/train/segmented.ts`):**
505
- same skeleton as the MiniCPM5 `SegmentedBackward`, with TWO additions — both are
506
- implemented and verified in the Phase B results above.
507
-
508
- (a) **per-layer-input is a pure constant boundary — NO gradient threading.** The
509
- `per_layer_input_gate` / `per_layer_projection` LoRA live INSIDE the layer (applied
510
- to the sliced `pls`), so they're differentiated within each segment's vjp normally.
511
- The `perLayer` tensor itself feeds in as a constant (its grad would flow to the
512
- embed / `per_layer_model_projection`, neither a LoRA target) — detached once and
513
- sliced per segment; its cotangent is discarded.
514
-
515
- (b) **the KV-shared donor K/V is a SECOND boundary stream WITH its own cotangent.**
516
- The donor's K/V depend on the donor layer's q/k/v LoRA and are attended by every
517
- sharer, so `dLoss/d(donor LoRA)` gets contributions from the donor's own attention
518
- AND all sharers. Threading:
519
- - **Forward (boundary-saving):** when a segment produces a reused donor's K/V
520
- (`donorKvOut`), it is detached (rope-applied K + V, tiny — ~1 KB/tok) and saved as
521
- a boundary; later sharer segments pass it in via `donorKvIn`.
522
- - **Backward (reverse), two extra pieces:**
523
- - a cotangent accumulator `dKV[d]` per reused donor d (init zero).
524
- - a SHARER segment's vjp differentiates `[boundary_h, donorK, donorV, ...LoRA]`
525
- with cotangent `[dh]`; its vjps for `donorK/donorV` are ACCUMULATED into `dKV[d]`.
526
- - a DONOR segment (produces donor d's K/V) has a MULTI-OUTPUT forward
527
- `[h_out, donorK, donorV]`; its vjp takes cotangents `[dh, dKV[d].k, dKV[d].v]`
528
- (the `Vjp` class supports nOut>1 + multiple cotangents) and returns
529
- `[dh_in, ...donor LoRA]`. This folds the sharers' gradient back into the donor.
530
- - donors 22 (sliding) and 23 (full) are each reused by all sharers of their type;
531
- if both live in one segment, that segment's forward outputs 5 arrays
532
- `[h, k22, v22, k23, v23]` with 5 cotangents.
533
- - **Segmentation (the §5 plan):** the donor/sharer split at layer 24 is respected and
534
- the 7 full-attention layers (5,11,17,23,29,35,41) — each O(L²) in the backward
535
- under ops.sdpa (~1 GB bf16 @8K) — are candidates for isolation into short segments;
536
- the natural first cut is the 6-layer `sssssF` period → 7 segments of 6 (one full
537
- layer each). `planSegments` from §5 returns the split indices + which donor K/V to
538
- save. Full-attention ISOLATION for the ~10 GB @8K target remains the next
539
- optimization step (currently 17.5 GB @8K, seg2 — see §10 close above).
540
-
541
- Everything else (forward boundaries via `detachLeaf`, head vjp with cotangent 1.0,
542
- reverse `dh` chain, `mlx_vjp` not surrogate) is identical to the MiniCPM5 path and
543
- is verified end-to-end: peak 10 GB @2048, active flat (no leak), adapter saved.