mlx-bun 0.5.0 → 1.0.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 (640) hide show
  1. package/README.md +1449 -109
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +7 -5
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +10 -10
  78. package/src/{serve → server}/chat-stage.ts +14 -14
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +33 -41
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +25 -82
  85. package/src/{serve → server}/http.ts +25 -13
  86. package/src/{serve → server}/inference-request.ts +5 -5
  87. package/src/server/job-routes.ts +20 -0
  88. package/src/server/media-prompt.ts +63 -0
  89. package/src/server/memory-completion-client.ts +46 -0
  90. package/src/server/model-routes.ts +192 -0
  91. package/src/{serve → server}/openai-wire.ts +3 -2
  92. package/src/{serve → server}/pipeline.ts +1 -1
  93. package/src/server/prompt-builder.ts +13 -0
  94. package/src/server/prompt-contracts.ts +17 -0
  95. package/src/server/proxy-routes.ts +187 -0
  96. package/src/server/publishing-routes.ts +41 -0
  97. package/src/{serve → server}/request-plan.ts +6 -8
  98. package/src/{serve → server}/request-prep.ts +47 -50
  99. package/src/{serve → server}/responses-client.ts +30 -13
  100. package/src/{responses.ts → server/responses.ts} +12 -93
  101. package/src/server/routes.ts +205 -0
  102. package/src/server/start.ts +72 -0
  103. package/src/server/status-routes.ts +108 -0
  104. package/src/{serve → server}/text-completion-stage.ts +25 -4
  105. package/src/server/text-prompt.ts +12 -0
  106. package/src/{serve → server}/token-streams.ts +36 -38
  107. package/src/server/worker-routes.ts +311 -0
  108. package/src/storage/paths.ts +61 -0
  109. package/src/web/assets.ts +37 -0
  110. package/src/web/{src → browser}/api.ts +5 -23
  111. package/src/web/browser/chat-host.ts +17 -0
  112. package/src/web/browser/installed-panels.ts +6 -0
  113. package/src/web/browser/main.ts +55 -0
  114. package/src/web/browser/markdown.ts +279 -0
  115. package/src/web/browser/memory-panel.ts +9 -0
  116. package/src/web/browser/palette.ts +64 -0
  117. package/src/web/browser/protocol.ts +13 -0
  118. package/src/web/browser/shell.ts +467 -0
  119. package/src/web/{src → browser}/status.ts +32 -36
  120. package/src/web/build.ts +51 -0
  121. package/src/web/public/app.html +1054 -0
  122. package/src/web/{sw.js → public/sw.js} +4 -3
  123. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  124. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  125. package/THIRD_PARTY_LICENSES.md +0 -51
  126. package/docs/README.md +0 -107
  127. package/docs/archive/hlg-sampling.md +0 -350
  128. package/docs/archive/investigations/audio-input-plan.md +0 -356
  129. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
  174. package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
  175. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  176. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  177. package/docs/design/batching.md +0 -2054
  178. package/docs/design/decode-speed-program.md +0 -2404
  179. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  180. package/docs/design/generic-model-support.md +0 -821
  181. package/docs/design/kv-cache.md +0 -993
  182. package/docs/design/orpo-training.md +0 -680
  183. package/docs/design/speculative-decoding.md +0 -2129
  184. package/docs/design/turboquant.md +0 -2225
  185. package/docs/design/unified-engine-frontier-plan.md +0 -1987
  186. package/docs/design/web-chat-redesign.md +0 -695
  187. package/docs/planning/PRODUCT_ROADMAP.md +0 -593
  188. package/docs/planning/ResearchTopics.md +0 -119
  189. package/docs/planning/public-readiness-handoff.md +0 -244
  190. package/docs/planning/public-readiness.md +0 -149
  191. package/docs/planning/video-series.md +0 -421
  192. package/docs/reference/benchmarks.md +0 -3587
  193. package/docs/reference/cli.md +0 -646
  194. package/docs/reference/distribution.md +0 -380
  195. package/docs/reference/environment.md +0 -494
  196. package/docs/reference/glossary.md +0 -39
  197. package/docs/reference/library-api.md +0 -688
  198. package/docs/reference/memory.md +0 -260
  199. package/docs/reference/models.md +0 -346
  200. package/docs/reference/server-api.md +0 -1945
  201. package/docs/reference/server-config.md +0 -973
  202. package/docs/reference/training.md +0 -462
  203. package/docs/reference/troubleshooting.md +0 -158
  204. package/src/admission-plan.ts +0 -18
  205. package/src/audio/audiotoolbox.ts +0 -112
  206. package/src/audio/conformer.ts +0 -655
  207. package/src/audio/decode.ts +0 -133
  208. package/src/audio/features.ts +0 -202
  209. package/src/audio/mic-capture.ts +0 -98
  210. package/src/audio/silero-vad.ts +0 -379
  211. package/src/audio/transcode.ts +0 -67
  212. package/src/audio/whisper-decode.ts +0 -774
  213. package/src/audio/whisper-fast.ts +0 -732
  214. package/src/audio/whisper-format.ts +0 -73
  215. package/src/audio/whisper-mel.ts +0 -185
  216. package/src/audio/whisper-timing.ts +0 -274
  217. package/src/audio/whisper-tokenizer.ts +0 -239
  218. package/src/audio/whisper-transcribe.ts +0 -410
  219. package/src/backends/mlx/assistant-target.ts +0 -46
  220. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  221. package/src/backends/mlx/autoregressive.ts +0 -129
  222. package/src/backends/mlx/batch-group.ts +0 -1874
  223. package/src/backends/mlx/cache-attachment.ts +0 -67
  224. package/src/backends/mlx/cache-layout.ts +0 -31
  225. package/src/backends/mlx/checkpoint-state.ts +0 -53
  226. package/src/backends/mlx/continuation-persistence.ts +0 -107
  227. package/src/backends/mlx/continuation-request.ts +0 -43
  228. package/src/backends/mlx/continuation.ts +0 -77
  229. package/src/backends/mlx/diffusion.ts +0 -39
  230. package/src/backends/mlx/draft-target.ts +0 -32
  231. package/src/backends/mlx/encoder-cache.ts +0 -25
  232. package/src/backends/mlx/evaluation.ts +0 -105
  233. package/src/backends/mlx/fill-append.ts +0 -58
  234. package/src/backends/mlx/fill-group.ts +0 -497
  235. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  236. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  237. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  238. package/src/backends/mlx/gateway-binding.ts +0 -174
  239. package/src/backends/mlx/graph.ts +0 -38
  240. package/src/backends/mlx/kv-codec.ts +0 -9
  241. package/src/backends/mlx/kv-maintenance.ts +0 -161
  242. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  243. package/src/backends/mlx/memory-client.ts +0 -189
  244. package/src/backends/mlx/methods.ts +0 -90
  245. package/src/backends/mlx/mixed-iteration.ts +0 -62
  246. package/src/backends/mlx/model-prompt.ts +0 -178
  247. package/src/backends/mlx/model-serving.ts +0 -80
  248. package/src/backends/mlx/paged-cache-codec.ts +0 -27
  249. package/src/backends/mlx/prefill-cohort.ts +0 -100
  250. package/src/backends/mlx/prefill-policy.ts +0 -26
  251. package/src/backends/mlx/prefill-rows.ts +0 -244
  252. package/src/backends/mlx/prefill.ts +0 -33
  253. package/src/backends/mlx/projected-context-rows.ts +0 -72
  254. package/src/backends/mlx/prompt-input.ts +0 -29
  255. package/src/backends/mlx/qwen-prompt-input.ts +0 -40
  256. package/src/backends/mlx/request-policy.ts +0 -20
  257. package/src/backends/mlx/request-state-policy.ts +0 -54
  258. package/src/backends/mlx/rollback.ts +0 -92
  259. package/src/backends/mlx/row-sampling.ts +0 -49
  260. package/src/backends/mlx/serial-executor.ts +0 -251
  261. package/src/backends/mlx/speculative-group.ts +0 -438
  262. package/src/backends/mlx/speculative-round.ts +0 -161
  263. package/src/backends/mlx/speculative.ts +0 -131
  264. package/src/backends/mlx/state-rows.ts +0 -104
  265. package/src/backends/mlx/state-views.ts +0 -46
  266. package/src/backends/mlx/target-layout-capability.ts +0 -35
  267. package/src/backends/mlx/text-engine.ts +0 -73
  268. package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
  269. package/src/chat-template.ts +0 -382
  270. package/src/cli.ts +0 -3490
  271. package/src/config.ts +0 -399
  272. package/src/contracts/completion.ts +0 -16
  273. package/src/contracts/execution.ts +0 -70
  274. package/src/contracts/generation.ts +0 -103
  275. package/src/contracts/host.ts +0 -7
  276. package/src/contracts/object-cache.ts +0 -8
  277. package/src/contracts/pi-provider.ts +0 -30
  278. package/src/contracts/pi.ts +0 -261
  279. package/src/contracts/prefix-cache.ts +0 -33
  280. package/src/contracts/resources.ts +0 -25
  281. package/src/contracts/sampling.ts +0 -23
  282. package/src/contracts/scheduling.ts +0 -54
  283. package/src/contracts/tsconfig.json +0 -14
  284. package/src/dataset/generators.ts +0 -1087
  285. package/src/dataset/index.ts +0 -12
  286. package/src/dataset/job.ts +0 -81
  287. package/src/dataset/llm.ts +0 -71
  288. package/src/dataset/registry.ts +0 -528
  289. package/src/diffusion/diffusion-generate.ts +0 -507
  290. package/src/download.ts +0 -505
  291. package/src/embed.ts +0 -77
  292. package/src/engine/admission.ts +0 -59
  293. package/src/engine/cancellation.ts +0 -38
  294. package/src/engine/engine.ts +0 -56
  295. package/src/engine/execution-coordinator.ts +0 -70
  296. package/src/engine/execution-plan.ts +0 -64
  297. package/src/engine/execution-tasks.ts +0 -44
  298. package/src/engine/namespaced-cache.ts +0 -13
  299. package/src/engine/resources.ts +0 -53
  300. package/src/engine/scheduler.ts +0 -92
  301. package/src/engine/session.ts +0 -247
  302. package/src/eval/README.md +0 -130
  303. package/src/eval/ifeval.ts +0 -81
  304. package/src/eval/kl-prompts.ts +0 -29
  305. package/src/eval/kl.ts +0 -590
  306. package/src/eval/perplexity.ts +0 -164
  307. package/src/eval/quality-db.ts +0 -92
  308. package/src/eval/reference-logits.ts +0 -207
  309. package/src/eval/runner.ts +0 -124
  310. package/src/eval/score.ts +0 -49
  311. package/src/eval/tasks/bfcl.ts +0 -535
  312. package/src/eval/tasks/gsm8k.ts +0 -98
  313. package/src/eval/tasks/hashhop.ts +0 -139
  314. package/src/eval/tasks/humaneval.ts +0 -275
  315. package/src/eval/tasks/ifeval.ts +0 -525
  316. package/src/eval/tasks/mmlu.ts +0 -157
  317. package/src/eval/ultrafeedback.ts +0 -60
  318. package/src/evaldb.ts +0 -199
  319. package/src/expert-io.ts +0 -393
  320. package/src/expert-offload-build.ts +0 -77
  321. package/src/expert-offload.ts +0 -76
  322. package/src/expert-residency.ts +0 -1134
  323. package/src/expert-usage.ts +0 -415
  324. package/src/fill/echo-index.ts +0 -188
  325. package/src/fill/fill-session.ts +0 -463
  326. package/src/fill/proposal.ts +0 -68
  327. package/src/fill/schema-rows.ts +0 -506
  328. package/src/fill/tool-boundary.ts +0 -103
  329. package/src/fit.ts +0 -249
  330. package/src/generate.ts +0 -1352
  331. package/src/grammar.ts +0 -604
  332. package/src/harness-pi.ts +0 -184
  333. package/src/hf-push.ts +0 -628
  334. package/src/hub-rest.ts +0 -242
  335. package/src/index.ts +0 -63
  336. package/src/inference/denoising.ts +0 -17
  337. package/src/inference/draft-acceptance.ts +0 -41
  338. package/src/inference/draft-output.ts +0 -49
  339. package/src/inference/graph.ts +0 -33
  340. package/src/inference/prefill.ts +0 -45
  341. package/src/inference/rollback.ts +0 -7
  342. package/src/jobs/index.ts +0 -31
  343. package/src/jobs/job-entry.ts +0 -107
  344. package/src/jobs/task-client.ts +0 -19
  345. package/src/jobs/types.ts +0 -2
  346. package/src/kv-scheme.ts +0 -279
  347. package/src/kv-store.ts +0 -1135
  348. package/src/lab/README.md +0 -22
  349. package/src/lab/curve/curve-designer.html +0 -303
  350. package/src/lab/curve/curve-sampler.ts +0 -122
  351. package/src/lab/expert-trace/expert-trace.ts +0 -69
  352. package/src/lab/paged-kv/paged-attention.ts +0 -117
  353. package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
  354. package/src/lab/paged-kv/paged-kv.ts +0 -336
  355. package/src/library.ts +0 -40
  356. package/src/lora.ts +0 -408
  357. package/src/md-modules.d.ts +0 -54
  358. package/src/media-fetch.ts +0 -442
  359. package/src/memory/article.ts +0 -558
  360. package/src/memory/chunk-validation.ts +0 -150
  361. package/src/memory/chunk.ts +0 -318
  362. package/src/memory/cluster.ts +0 -351
  363. package/src/memory/coverage.ts +0 -134
  364. package/src/memory/crosslink.ts +0 -0
  365. package/src/memory/db.ts +0 -253
  366. package/src/memory/entity.ts +0 -203
  367. package/src/memory/gate.ts +0 -187
  368. package/src/memory/history.ts +0 -152
  369. package/src/memory/ingest.ts +0 -335
  370. package/src/memory/model.ts +0 -183
  371. package/src/memory/normalize.ts +0 -671
  372. package/src/memory/parse.ts +0 -22
  373. package/src/memory/pipeline.ts +0 -275
  374. package/src/memory/prompts.ts +0 -65
  375. package/src/memory/query.ts +0 -334
  376. package/src/memory/reconcile.ts +0 -734
  377. package/src/memory/redirect.ts +0 -108
  378. package/src/memory/reindex.ts +0 -325
  379. package/src/memory/resolve.ts +0 -307
  380. package/src/memory/rest.ts +0 -245
  381. package/src/memory/route.ts +0 -272
  382. package/src/memory/schedule.ts +0 -165
  383. package/src/memory/stages.ts +0 -777
  384. package/src/memory/synthesize.ts +0 -1638
  385. package/src/memory/tools.ts +0 -749
  386. package/src/memory/vault.ts +0 -1194
  387. package/src/memory/wikify.ts +0 -1064
  388. package/src/mlx/array.ts +0 -425
  389. package/src/mlx/autograd.ts +0 -304
  390. package/src/mlx/checkpoint.ts +0 -149
  391. package/src/mlx/compile.ts +0 -168
  392. package/src/mlx/custom-vjp.ts +0 -188
  393. package/src/mlx/ffi.ts +0 -531
  394. package/src/mlx/materialize.ts +0 -23
  395. package/src/mlx/metal-kernel.ts +0 -159
  396. package/src/mlx/normalized-argmax.ts +0 -116
  397. package/src/mlx/ops.ts +0 -1108
  398. package/src/mlx/quantized-rows.ts +0 -36
  399. package/src/mlx/token-bitmask.ts +0 -52
  400. package/src/mlx/turboquant-kv-decode.ts +0 -82
  401. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  402. package/src/mlx/turboquant-ops.ts +0 -861
  403. package/src/mlx/turboquant-tables.ts +0 -237
  404. package/src/mmap.ts +0 -90
  405. package/src/model/artifact-identity.ts +0 -73
  406. package/src/model/batched-kv.ts +0 -235
  407. package/src/model/batched-mask.ts +0 -279
  408. package/src/model/batched-quant.ts +0 -212
  409. package/src/model/batched-quantized-kv.ts +0 -144
  410. package/src/model/batched-rotating-quant.ts +0 -353
  411. package/src/model/batched-rotating-state.ts +0 -149
  412. package/src/model/batched-rotating.ts +0 -338
  413. package/src/model/batched-row-storage.ts +0 -179
  414. package/src/model/batched-ssm.ts +0 -52
  415. package/src/model/batched-turboquant-kv.ts +0 -94
  416. package/src/model/compiled-decode.ts +0 -712
  417. package/src/model/decoded-kv-donor.ts +0 -12
  418. package/src/model/delayed-quantized-kv.ts +0 -72
  419. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  420. package/src/model/delayed-turboquant-kv.ts +0 -57
  421. package/src/model/diffusion-gemma.ts +0 -1026
  422. package/src/model/factory.ts +0 -215
  423. package/src/model/fingerprint.ts +0 -45
  424. package/src/model/flash-attention.ts +0 -694
  425. package/src/model/full-prefill-padding.ts +0 -56
  426. package/src/model/full-prefill-row.ts +0 -105
  427. package/src/model/gemma4-base.ts +0 -2370
  428. package/src/model/gemma4.ts +0 -1395
  429. package/src/model/generated/gemma4-12b.ts +0 -429
  430. package/src/model/generated/gemma4-26b.ts +0 -448
  431. package/src/model/generated/gemma4-e4b.ts +0 -717
  432. package/src/model/generated/index.ts +0 -18
  433. package/src/model/glm52-atlas.ts +0 -511
  434. package/src/model/glm52-cache.ts +0 -792
  435. package/src/model/glm52-config.ts +0 -262
  436. package/src/model/glm52-container.ts +0 -305
  437. package/src/model/glm52-coupling.ts +0 -388
  438. package/src/model/glm52-dsa.ts +0 -606
  439. package/src/model/glm52-expert-layout.ts +0 -253
  440. package/src/model/glm52-layout.ts +0 -152
  441. package/src/model/glm52-memory.ts +0 -500
  442. package/src/model/glm52-mla.ts +0 -792
  443. package/src/model/glm52-moe.ts +0 -223
  444. package/src/model/glm52-pilot.ts +0 -537
  445. package/src/model/glm52-reference.ts +0 -445
  446. package/src/model/glm52-residency.ts +0 -557
  447. package/src/model/glm52-resident-weights.ts +0 -135
  448. package/src/model/glm52-streamed-experts.ts +0 -453
  449. package/src/model/glm52-streamed-metal.ts +0 -610
  450. package/src/model/glm52-weights.ts +0 -503
  451. package/src/model/glm52.ts +0 -1132
  452. package/src/model/implementation.ts +0 -69
  453. package/src/model/kv-attention-view.ts +0 -70
  454. package/src/model/minicpm5.ts +0 -376
  455. package/src/model/profile.ts +0 -468
  456. package/src/model/quantized-append-attention.ts +0 -31
  457. package/src/model/qwen-append.ts +0 -12
  458. package/src/model/qwen-conv.ts +0 -52
  459. package/src/model/qwen3-delta-state.ts +0 -74
  460. package/src/model/qwen3-delta.ts +0 -451
  461. package/src/model/qwen3-moe.ts +0 -404
  462. package/src/model/qwen3-mrope.ts +0 -250
  463. package/src/model/qwen3.ts +0 -277
  464. package/src/model/qwen3_5-checkpoint.ts +0 -164
  465. package/src/model/qwen3_5.ts +0 -881
  466. package/src/model/rotating-kv-donor.ts +0 -35
  467. package/src/model/rotating-kv-layout.ts +0 -262
  468. package/src/model/rotating-row-transaction.ts +0 -35
  469. package/src/model/speculative-rotating-kv.ts +0 -101
  470. package/src/model/ssm-prefill-padding.ts +0 -39
  471. package/src/model/support.ts +0 -110
  472. package/src/model/token-groups.ts +0 -66
  473. package/src/model/trellis-balanced-scatter.ts +0 -51
  474. package/src/model/trellis-linear.ts +0 -672
  475. package/src/model/trellis-shared-m.ts +0 -151
  476. package/src/model/trellis-shared-scatter.ts +0 -144
  477. package/src/model/trellis-splitk-prefill.ts +0 -158
  478. package/src/model/trellis-tiled-prefill.ts +0 -136
  479. package/src/model/trellis-vector-expand.ts +0 -62
  480. package/src/model/trellis-wide-prefill.ts +0 -94
  481. package/src/model/turboquant-codec.ts +0 -89
  482. package/src/model/universal/archs.ts +0 -332
  483. package/src/model/universal/dense.ts +0 -612
  484. package/src/model/universal/modules.ts +0 -170
  485. package/src/model/universal/rope.ts +0 -381
  486. package/src/model/whisper.ts +0 -435
  487. package/src/native/expert_io.c +0 -523
  488. package/src/native/frame_extract.swift +0 -140
  489. package/src/native/mic_capture.swift +0 -92
  490. package/src/native-pack.ts +0 -163
  491. package/src/pi-provider.ts +0 -53
  492. package/src/pi-session.ts +0 -81
  493. package/src/pi-terminal.ts +0 -274
  494. package/src/pi-web.ts +0 -1940
  495. package/src/preflight.ts +0 -158
  496. package/src/prompt-cache.ts +0 -682
  497. package/src/quantize/allocator.ts +0 -627
  498. package/src/quantize/atomic-output.ts +0 -38
  499. package/src/quantize/calibration.ts +0 -181
  500. package/src/quantize/config-writer.ts +0 -145
  501. package/src/quantize/index.ts +0 -84
  502. package/src/quantize/job.ts +0 -162
  503. package/src/quantize/quantizer.ts +0 -666
  504. package/src/quantize/rotate.ts +0 -603
  505. package/src/quantize/safetensors-writer.ts +0 -249
  506. package/src/quantize/sensitivity.ts +0 -216
  507. package/src/quantize/trellis.ts +0 -586
  508. package/src/quantize/weight-transform.ts +0 -292
  509. package/src/registry.ts +0 -602
  510. package/src/runtime-config.ts +0 -77
  511. package/src/safetensors.ts +0 -165
  512. package/src/sampler.ts +0 -1012
  513. package/src/serve/admin-routes.ts +0 -184
  514. package/src/serve/assets/curve-terrain.html +0 -105
  515. package/src/serve/assets/training-inference-map.html +0 -575
  516. package/src/serve/audio-routes.ts +0 -323
  517. package/src/serve/aux-routes.ts +0 -132
  518. package/src/serve/batch-scheduler.ts +0 -8
  519. package/src/serve/checkpoint-identity.ts +0 -64
  520. package/src/serve/generation-gateway.ts +0 -537
  521. package/src/serve/isolate.ts +0 -609
  522. package/src/serve/kv-budget.ts +0 -14
  523. package/src/serve/lab-routes.ts +0 -341
  524. package/src/serve/lane-registry.ts +0 -45
  525. package/src/serve/model-admin-routes.ts +0 -155
  526. package/src/serve/model-binding.ts +0 -49
  527. package/src/serve/model-host.ts +0 -511
  528. package/src/serve/parent-application.ts +0 -56
  529. package/src/serve/prompt-response-trace.ts +0 -161
  530. package/src/serve/response-lease.ts +0 -24
  531. package/src/serve/session-search.ts +0 -326
  532. package/src/serve/static-routes.ts +0 -101
  533. package/src/serve/transcription-server.ts +0 -64
  534. package/src/serve/transcription-service.ts +0 -502
  535. package/src/server.ts +0 -1282
  536. package/src/spec/assistant-rows.ts +0 -93
  537. package/src/spec/assistant-source.ts +0 -126
  538. package/src/spec/cache-identity.ts +0 -22
  539. package/src/spec/deepspec-rows.ts +0 -29
  540. package/src/spec/deepspec-source.ts +0 -145
  541. package/src/spec/dflash-source.ts +0 -149
  542. package/src/spec/drafter.ts +0 -304
  543. package/src/spec/dspark/ab-stats.ts +0 -136
  544. package/src/spec/dspark/calibration.ts +0 -112
  545. package/src/spec/dspark/data-dflash.ts +0 -156
  546. package/src/spec/dspark/data.ts +0 -201
  547. package/src/spec/dspark/deepspec-module.ts +0 -776
  548. package/src/spec/dspark/generate-dflash.ts +0 -245
  549. package/src/spec/dspark/generate.ts +0 -290
  550. package/src/spec/dspark/loader.ts +0 -23
  551. package/src/spec/dspark/loss.ts +0 -213
  552. package/src/spec/dspark/module-dflash.ts +0 -560
  553. package/src/spec/dspark/module.ts +0 -540
  554. package/src/spec/dspark/quantize-drafter.ts +0 -91
  555. package/src/spec/dspark/sample.ts +0 -112
  556. package/src/spec/generate.ts +0 -276
  557. package/src/spec/glm52-mtp-graph.ts +0 -29
  558. package/src/spec/glm52-mtp-rows.ts +0 -132
  559. package/src/spec/glm52-mtp-source.ts +0 -318
  560. package/src/spec/glm52-mtp-state.ts +0 -33
  561. package/src/spec/ngram-source.ts +0 -226
  562. package/src/spec/projected-draft-rows.ts +0 -73
  563. package/src/spec/qwen-mtp-module.ts +0 -171
  564. package/src/spec/qwen-mtp-rows.ts +0 -230
  565. package/src/spec/qwen-mtp-source.ts +0 -386
  566. package/src/spec/qwen-mtp-state.ts +0 -30
  567. package/src/spec/serve-loop.ts +0 -587
  568. package/src/spec/source.ts +0 -228
  569. package/src/spec/two-model-rows.ts +0 -62
  570. package/src/spec/two-model.ts +0 -113
  571. package/src/ssd-cache.ts +0 -431
  572. package/src/ssd-durability.ts +0 -209
  573. package/src/storage/host-buffer.ts +0 -24
  574. package/src/storage/kv-writer.ts +0 -83
  575. package/src/storage/kv-writer.worker.js +0 -264
  576. package/src/storage/retention-policy.ts +0 -37
  577. package/src/tiered-prompt-cache.ts +0 -45
  578. package/src/tokenizer.ts +0 -335
  579. package/src/tool-approvals.ts +0 -131
  580. package/src/tool-call.ts +0 -695
  581. package/src/train/dataset.ts +0 -484
  582. package/src/train/diffusion-lora.ts +0 -216
  583. package/src/train/export.ts +0 -32
  584. package/src/train/flash-cce.ts +0 -1234
  585. package/src/train/forward.ts +0 -266
  586. package/src/train/fuse.ts +0 -243
  587. package/src/train/index.ts +0 -46
  588. package/src/train/job.ts +0 -205
  589. package/src/train/lora-params.ts +0 -254
  590. package/src/train/loss.ts +0 -1591
  591. package/src/train/merge.ts +0 -195
  592. package/src/train/optimizer.ts +0 -183
  593. package/src/train/prefix-shared.ts +0 -490
  594. package/src/train/rank.ts +0 -137
  595. package/src/train/segmented.ts +0 -1828
  596. package/src/train/steel-qmm-header.ts +0 -1461
  597. package/src/train/trainer.ts +0 -1221
  598. package/src/train/watch.ts +0 -542
  599. package/src/vision/diffusion-vision.ts +0 -620
  600. package/src/vision/embedder.ts +0 -176
  601. package/src/vision/preprocess.ts +0 -247
  602. package/src/vision/prompt.ts +0 -409
  603. package/src/vision/qwen3vl-preprocess.ts +0 -409
  604. package/src/vision/qwen3vl-prompt.ts +0 -233
  605. package/src/vision/qwen3vl-tower.ts +0 -566
  606. package/src/vision/siglip.ts +0 -607
  607. package/src/vision/video-frames.ts +0 -148
  608. package/src/web/app.html +0 -2155
  609. package/src/web/app.js +0 -5863
  610. package/src/web/skills/memory/SKILL.md +0 -120
  611. package/src/web/skills/web-research/SKILL.md +0 -46
  612. package/src/web/skills.ts +0 -97
  613. package/src/web/src/adapters-panel.ts +0 -288
  614. package/src/web/src/assistant.ts +0 -451
  615. package/src/web/src/chat.ts +0 -1060
  616. package/src/web/src/composer.ts +0 -962
  617. package/src/web/src/dataset.ts +0 -132
  618. package/src/web/src/finetune.ts +0 -267
  619. package/src/web/src/hub.ts +0 -428
  620. package/src/web/src/main.ts +0 -98
  621. package/src/web/src/markdown.ts +0 -0
  622. package/src/web/src/memory-panel.ts +0 -572
  623. package/src/web/src/model-picker.ts +0 -159
  624. package/src/web/src/palette.ts +0 -346
  625. package/src/web/src/protocol.ts +0 -26
  626. package/src/web/src/quantize.ts +0 -167
  627. package/src/web/src/rag.ts +0 -274
  628. package/src/web/src/sessions.ts +0 -469
  629. package/src/web/src/shell.ts +0 -804
  630. package/src/web/src/ui-catalog.ts +0 -78
  631. package/src/web/src/voice.ts +0 -141
  632. package/src/web/vendor/README.md +0 -61
  633. package/src/web-assets.ts +0 -47
  634. package/src/web-tools.ts +0 -478
  635. package/src/weights.ts +0 -255
  636. /package/src/{serve → server}/completion-sink.ts +0 -0
  637. /package/src/{serve → server}/generated-token-history.ts +0 -0
  638. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  639. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  640. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,680 +0,0 @@
1
- ---
2
- status: landed
3
- axis: USING
4
- canonical-for: training
5
- plan-anchor: "Phase: Steel flash-CCE ORPO head + full ORPO training stack"
6
- last-verified: 2026-08-23
7
- ---
8
-
9
- # Training — design (ORPO, flash-CCE head, prefix sharing, segmented backward)
10
-
11
- The canonical design record for mlx-bun's LoRA trainer: the **ORPO** objective
12
- (Hong et al. 2024) as a third `method` beside `sft`/`dpo`, and the long-context
13
- machinery it runs on — the `[M,V]`-free **flash-CCE** head, the **prefix-shared**
14
- two-branch forward, and the **segmented backward**. All of it is landed and
15
- default-on in `scripts/train-orpo.ts` and `mlx-bun train`.
16
-
17
- - Usage, flags, measured run tables: [training.md](../reference/training.md),
18
- [docs/reference/training.md](../reference/training.md).
19
- - Build narratives (frozen): [steel-flash-cce-handoff.md](../archive/investigations/steel-flash-cce-handoff.md),
20
- [segmented-backward-handoff.md](../archive/investigations/segmented-backward-handoff.md),
21
- [orpo-flash-cce-pin-leak.md](../archive/investigations/orpo-flash-cce-pin-leak.md),
22
- [orpo-base-uf-experiment-and-directions.md](../archive/investigations/orpo-base-uf-experiment-and-directions.md),
23
- [orpo-uf-testing-handoff.md](../archive/investigations/orpo-uf-testing-handoff.md).
24
- - Open training work is tracked in PLAN.md under the plan-anchor above; status
25
- prose lives there, not here.
26
-
27
- Code: [`src/train/loss.ts`](../../src/train/loss.ts) (objective + heads),
28
- [`trainer.ts`](../../src/train/trainer.ts) (loops, config), [`segmented.ts`](../../src/train/segmented.ts),
29
- [`prefix-shared.ts`](../../src/train/prefix-shared.ts), [`flash-cce.ts`](../../src/train/flash-cce.ts)
30
- (+ `steel-qmm-header.ts`), [`job.ts`](../../src/train/job.ts) (submit surface),
31
- [`lora-params.ts`](../../src/train/lora-params.ts), [`rank.ts`](../../src/train/rank.ts).
32
- Entry points: [`scripts/train-orpo.ts`](../../scripts/train-orpo.ts) (env launcher),
33
- `mlx-bun train` (`src/cli.ts`), the `/finetune` job runner. SFT examples:
34
- `scripts/examples/chunk-finetune.ts`, `scripts/examples/ft-e4b-v2.sh`,
35
- `scripts/examples/chunk-eval.ts`.
36
-
37
- ## 1. The objective
38
-
39
- $$\mathcal{L}_{ORPO} = \mathcal{L}_{NLL}(y_w) + \lambda \cdot \mathcal{L}_{OR}$$
40
-
41
- - $\mathcal{L}_{NLL}(y_w)$ — cross-entropy on the **chosen** sequence. Scope is
42
- `sft_scope` (`TrainConfig.sftScope`, default **`full`**):
43
- - `full` (paper/TRL-faithful): token-mean CE over all non-pad positions,
44
- prompt AND response, from the **same chosen forward** — the head runs once
45
- over the prompt span and once over the response span (disjoint), combined
46
- as $\text{NLL}_{full} = -\frac{(P{-}1)\,\text{pm} + M\,\ell_w}{(P{-}1)+M}$
47
- (`combineFullNll`). Matches TRL `ORPOTrainer.chosen_nll_loss` and
48
- xfactlab/orpo `outputs_pos.loss`.
49
- - `response`: $-\ell_w$, the pre-2026-07 house convention (prompt masked),
50
- kept bit-exact for reproducing older runs.
51
- - $\mathcal{L}_{OR} = -\log\sigma(\text{log\_odds})$ with
52
- $\text{log\_odds} = (\ell_w - \ell_r) - (\text{log1mexp}(\ell_w) - \text{log1mexp}(\ell_r))$,
53
- $\text{log1mexp}(x) = \log(1-e^x)$. $\ell_w,\ell_r$ are **length-normalized
54
- means** over response tokens in BOTH scope modes (also TRL's convention —
55
- its `get_batch_logps` masks the prompt for the odds ratio; only the NLL term
56
- is full-sequence). $\lambda$ weights only $\mathcal{L}_{OR}$; the NLL term is
57
- unweighted.
58
-
59
- Implementation (`loss.ts`): `log1mexp` (stable `log(clip(1 − exp(x), eps, 1))`
60
- — mlx-bun has no `expm1`/`log1p`; per-token mean logp is comfortably negative
61
- so the clip is safe), `orpoLogOdds`, `orpoLossFromLogps(lw, lr, λ, nllFull?)`
62
- (model-free, unit-testable against a hand reference), `orpoLoss(model, batch,
63
- λ, chunk?, sftScope)` (runs the two forwards), `orpoMetrics` (no-grad
64
- `loss`/`nll`/`or`/`accuracy`/`margin`).
65
-
66
- Two facts that shape everything downstream:
67
-
68
- 1. **Mean, not sum.** The one substantive difference from DPO's `seqLogp`
69
- (per-row sum). Summing biases toward short responses and makes the OR
70
- gradient length-dependent.
71
- 2. **The chosen forward does double duty.** ORPO is **2 forwards/step**
72
- (chosen + rejected, both with grad) vs DPO's 4 (2 reference + 2 policy). No
73
- reference model, no `setLoraScale(0)` dance, no detached reference tensors.
74
- Never forward chosen twice; never compute a rejected NLL.
75
-
76
- Two unpadded sequential B=1 forwards are the *right* shape, not a limitation:
77
- TRL's padded `concatenated_forward` exists to amortize FSDP all-gathers we
78
- don't have; on unified memory the padding is pure waste.
79
-
80
- ## 2. Oracles and the parity ladder
81
-
82
- Neither `mlx_lm` nor `optiq` ships ORPO, so — unlike DPO (`loss.ts` ported
83
- `optiq/lora/dpo.py`) — ORPO is a **no-oracle mlx-bun original (Lab-class)**.
84
- The current MLX SOTA (`mlx-lm-lora`) ships a non-canonical ORPO
85
- (`log_sigmoid(chosen − rejected)`, no SFT-NLL term); do not copy it.
86
-
87
- What IS oracle-backed, and where each tier bottoms out:
88
-
89
- | Tier | Path | Oracle / gate |
90
- |---|---|---|
91
- | L1 | Non-segmented SFT `value_and_grad` through `ops.sdpa` | **Bit-exact vs `mlx_lm.lora`** — loss on 4/6 mask spans, 0.5 f32-ulp on 2; `dB` over all 42 e4b q_proj layers `0.000e+0` (2026-06-16). Response-only CE == full-masked CE on every span. |
92
- | L2 | `MLX_BUN_TRAIN_ATTN=flash` (port of optiq's `flash_attention_metal`) | optiq flash dK == `ops.sdpa` to f16 (`scripts/oracle/flash-optiq-check.py`); ours == `ops.sdpa` (`tests/flash-attention.test.ts`). |
93
- | Lab | ORPO loss math | Standalone JS/numpy reference + finite differences (`tests/train-orpo.test.ts`; the deleted `parity-orpo.ts` passed 10/10 incl. FD grads at B=1/B=3). |
94
- | Lab | Each memory tier (chunked → fused → flash head; segmented; prefix) | Validated against the tier below: value bit-exact or bf16-class, grads bf16-class, bottoming out at the naive full-logits two-forward path. |
95
-
96
- Rules learned the hard way:
97
-
98
- - A parity reference is trustworthy only if read **contiguously**
99
- (`ops.contiguous` before `toFloat32`) with a **non-uniform cotangent** — both
100
- pitfalls produced false "kernel is wrong" signals; finite differences settled it.
101
- - "0.28% dh" for the flash-CCE head is fp-reassociation vs a full-logits
102
- *proxy*, not error (two correct reductions in different orders aren't
103
- bit-identical). Correctness is FD + the math audit; fidelity is
104
- **teacher-forced on real hiddens**, not synthetic random logits (whose flat
105
- softmax is the coeff filter's worst case). Standing regression:
106
- `tests/train-orpo-fused-ce.test.ts` "teacher-forced fidelity" (cos > 0.999,
107
- relnorm < 5% vs a `Vjp` full-logits reference).
108
- - Validate every custom `.vjp` against `mx.vjp`/autograd, never a hand formula.
109
-
110
- ## 3. Trainer architecture
111
-
112
- `trainLora` dispatches on `cfg.method`: `sftLoop` / `dpoLoop` / `orpoLoop`. All
113
- share `resolveRanks` → `buildTrainableLora` → `AdamW` → a closure that swaps the
114
- LoRA A/B primals so only those leaves differentiate. ORPO consumes the
115
- **preference** dataset format `{prompt, chosen, rejected}` (`"dpo"` kept as an
116
- alias) via `loadDpoDataset` / `iterateDpoBatches`.
117
-
118
- `orpoLoop` path selection (verified in `trainer.ts`):
119
-
120
- - `segmentSize > 0` → segmented classes (MiniCPM5 or Gemma4 only; else throws).
121
- With `orpoPrefixShared` the composed `SegmentedBackwardOrpoPrefix[Gemma4]` is
122
- built alongside a plain `SegmentedBackwardOrpo[Gemma4]` for per-row fallback.
123
- The segmented head is `boundedHeadFromHidden` (Checkpoint-chunked or fused)
124
- when `orpoFusedCe || orpoFlashCe`, else full `responseOnlyLogpMean`.
125
- - Otherwise a `ValueAndGrad` over `orpoLossPrefixShared[Gemma]` (when the row's
126
- prompts are byte-identical; `splitPrefixBatch`) or `orpoLoss`, with a
127
- `ChunkCtx` selecting the head: `{fused, flash}` (chunk = `orpoChunkSize` or
128
- 512) / Checkpoint token-chunked (`orpoChunkSize > 0`) / full logits.
129
- - Every micro-step goes through `accumulateStep(gradAccumSteps, …)`; the
130
- per-chunk Checkpoint sink is disposed in its `afterMicroEval` hook.
131
- - Dropout: `model.loraState.dropoutSeed` is set once per micro-step so the
132
- segmented/checkpoint recompute replays the same mask; cleared before
133
- metrics/val.
134
- - `MLX_BUN_TRAIN_ATTN=flash` is **refused for Gemma models** (e4b SIGTRAPed at
135
- ≥2K and has not been re-validated since the kernel fixes); MiniCPM5 allowed.
136
-
137
- ### Config surface (TrainConfig ↔ job submit ↔ CLI/launcher)
138
-
139
- | TrainConfig | `FinetuneSubmit` | `mlx-bun train` / `train-orpo.ts` env | Default | Notes |
140
- |---|---|---|---|---|
141
- | `orpoLambda` | `orpo_lambda` | `LAMBDA` | 0.1 | weights only L_OR |
142
- | `orpoWarmupIters` / `orpoLrSchedule` | `orpo_warmup_iters` / `orpo_lr_schedule` | — | 0 / `cosine` | launcher sets warmup = min(10, iters/10) |
143
- | `orpoChunkSize` | `orpo_chunk_size` | — | 0 (job: 512 for orpo) | token-chunk for all head variants |
144
- | `orpoFusedCe` | `orpo_fused_ce` | `FLASH=0` selects it | false | analytic MLX fused head |
145
- | `orpoFlashCe` | `orpo_flash_ce` | `FLASH` (default on) | false (job: true for orpo) | implies fused |
146
- | `orpoPrefixShared` | `orpo_prefix_shared` | `PREFIX` (default on) | false (job: true for orpo) | MiniCPM5 + Gemma4, B=1 |
147
- | `sftScope` | `sft_scope` | `SFT_SCOPE` | `full` | |
148
- | `segmentSize` | `segment_size` | `SEG` / `SEGOFF=1`; `--seg` / `--no-segment` | 0 (job/CLI: 2 for orpo) | |
149
- | `gradAccumSteps` | `grad_accumulation_steps` | `--grad-accum` (CLI only; no env knob in `train-orpo.ts`) | 1 | |
150
- | `loraDropout` / `rsLora` / `loraPlusRatio` | `lora_dropout` / `rs_lora` / `lora_plus_ratio` | — | 0 / false / 1 | |
151
- | `warmStartAdapter` | `warm_start_adapter` | `RESUME` / `--resume` | "" | weights only; optimizer + schedule restart |
152
- | `gradCheckpoint` + `mlpSplit` | `grad_checkpoint` / `mlp_split` | — | false | mutually exclusive with `segmentSize`; `mlpSplit` Gemma4-only |
153
- | `learningRate` | `learning_rate` | `LR` | 1e-5 for orpo (job/CLI/launcher) | `DEFAULT_TRAIN_CONFIG` is 2e-4 (SFT) |
154
- | `rank` / `scale` | `rank` / `scale` | `RANK` / `SCALE` | orpo: 16 / **2.0** (CLI + launcher) | `DEFAULT_TRAIN_CONFIG`: 8 / 1.0; SFT example scripts use 20 |
155
-
156
- Method-prefixed knobs (`orpo_*` / `dpo_*`) are deliberate: no shared or renamed
157
- fields.
158
-
159
- ## 4. Recommended defaults and regularization geometry
160
-
161
- From the hyperparameter spike (paper, TRL `ORPOConfig`, Labonne's Llama-3
162
- ORPO-LoRA). The trap: the paper's `8e-6` is for *full* fine-tuning and
163
- DPO-LoRA's `5e-5` is too hot because ORPO carries a full SFT-NLL term.
164
-
165
- | Knob | Default | Why |
166
- |---|---|---|
167
- | `learning_rate` | 1e-5 | matches our SFT recipe; sweep 5e-6–2e-5 |
168
- | `orpo_lambda` | 0.1 | paper's primary value + TRL default; stable 0.1–0.5 (λ=1.0 depresses chosen logp) |
169
- | schedule / warmup | cosine, ≈3% of iters | the OR gradient's `1/(1−P)` factor amplifies low-likelihood updates, so cold high LR hurts more than in SFT |
170
- | `rank` | 16 | |
171
- | epochs | 1 to start | eval loss rises fast past epoch 1–2; early-stop on val margin |
172
-
173
- Sources: [ORPO paper](https://aclanthology.org/2024.emnlp-main.626/),
174
- [TRL `orpo_config.py`](https://github.com/huggingface/trl/blob/main/trl/trainer/orpo_config.py),
175
- [Labonne](https://huggingface.co/blog/mlabonne/orpo-llama-3).
176
-
177
- **Regularization, ordered by leverage** (ORPO is ~1 epoch on small sets, so
178
- overfitting and step geometry matter more than capacity):
179
-
180
- - **Rank is the primary regularizer**; `rank_scaling` (`by_bits`/`by_kl`) is how
181
- capacity is distributed. `bitsMapFromModel` (reads `linear.spec.bits`) and
182
- `readPerLayerKl` (`optiq_metadata.json`) feed `resolveRanks` — before this was
183
- wired the policy silently went uniform. `by_bits`: `rank·(bits/4)`; `by_kl`:
184
- `rank·clip(kl/median, 0.5, 2)` — optiq's "one signal, two optimizations"
185
- (`tests/train-rank.test.ts`).
186
- - **Rank ↔ scale ↔ init.** Update is `α·BA` with B zero-init, A uniform(±1/√in).
187
- At step 0 `dA ∝ dy·Bᵀ = 0`: B does all the early learning → **LoRA+**
188
- (`lora_plus_ratio`, higher LR on B leaves) helps. A fixed α makes the step
189
- size scale with per-layer rank, defeating rank scaling → **rsLoRA**
190
- (`rs_lora`, α/√r; saved into the adapter config so inference applies the same
191
- per-layer scale).
192
- - **Weight decay** on A and B independently is a nuclear-norm-style penalty on
193
- `ΔW`, not Frobenius; the SFT-NLL term already anchors, so default lower than
194
- SFT's 0.01 and tune.
195
- - **LoRA-input dropout** (`lora_dropout`, PEFT-style, default 0, ~0.05 for small
196
- sets). The base has no dropout modules (TRL's `disable_dropout` is moot).
197
- Recompute determinism is mandatory: mask keyed by (step, layer) so segmented
198
- / checkpoint / chunked recompute reproduces it (`tests/train-regularization*.test.ts`).
199
-
200
- The remaining work here is empirical: sweep `rs_lora` / `lora_plus_ratio` /
201
- `lora_dropout` / `weight_decay` on a real run and judge by held-out val margin
202
- (`bestMargin` in `metrics.json`).
203
-
204
- ## 5. Memory and speed
205
-
206
- Two terms dominate at long context, and neither is the LoRA grads: (1) the LM
207
- head logits `[1,T,V]` + gradient (~4.3/8.6 GB bf16 at T=8192, V=262k), doubled
208
- by ORPO's two branches; (2) per-layer activations held for the backward. The
209
- ordering principle for Apple Silicon: **only reducing total live footprint
210
- moves the constraint — relocation does not.** CPU and GPU share one pool, so
211
- activation offloading frees zero bytes, risks double residency, and burns the
212
- scarce bandwidth. We do not implement it.
213
-
214
- ### 5.1 The head: from token-chunking to the flash-CCE kernel
215
-
216
- CE and the per-token target logp ORPO needs are the same quantity
217
- (`logp_t = h_t·W_{y_t} − logsumexp_v(h_t·W_v)`), so one memory-efficient engine
218
- serves the NLL and the odds-ratio terms. Four tiers were built; each is
219
- validated against the one below.
220
-
221
- 1. **Token-chunked Checkpoint head** (`chunkedLogpMeanB1`, `orpoChunkSize`).
222
- Each `[chunk,V]` head is wrapped in a `Checkpoint` and recomputed in the
223
- backward. A Checkpoint *drops* its input activations, so the hidden slices
224
- it reads must live in the `sink` until grads are eval'd (found during
225
- bring-up). Forward parity within bf16 (single-ULP head-matmul tiling
226
- differences), exact for one chunk (`tests/train-orpo-chunked.test.ts`).
227
- 2. **Fused linear-CE head** (`fusedLogpMeanB1`, `orpo_fused_ce`; Liger
228
- `FusedLinearCrossEntropy` on MLX). One `CustomVjp`: forward materializes
229
- `[chunk,V]` per chunk (`quantizedMatmul(h, W, transpose=true)` + Gemma
230
- `logitSoftcap`), reduces to `target − lse`, drops the logits; the analytic
231
- backward recomputes and forms `g·(onehot − softmax)·sech²` directly, then
232
- `dh = quantizedMatmul(grad, W, transpose=false)` — mlx's own x-vjp,
233
- contracting vocab without dequantizing the head. Value **bit-exact** vs full
234
- logits on MiniCPM5 and e4b (softcap=30 matched by reusing `logitSoftcap`'s
235
- div-by-bf16-cap formula); grads 0.51% / 1.97% bf16-class
236
- (`tests/train-orpo-fused-ce.test.ts`). The head is frozen (not a LoRA
237
- target), so only `dh` flows back.
238
- 3. **Vocab-blocked online-softmax in pure MLX** — implemented, numerically
239
- correct, **does not bound memory**, gated off. The `(m,d)` recurrence and
240
- the cross-block `dh` accumulation must be f32 (bf16 gave 17→45% grad error
241
- scaling with block count). But the online softmax is a sequentially
242
- dependent chain and MLX's lazy eval retains the whole chain: head-isolated
243
- peak went 3.4 GB → 14.1 GB at 512-wide blocks (MiniCPM5 M=2048, 2026-06-18).
244
- Forcing an eval mid-chain breaks autodiff. Conclusion: the CCE residency
245
- bound needs the vocab loop INSIDE a kernel.
246
- 4. **flash-CCE Metal kernel** (`flash-cce.ts`, `orpo_flash_ce`) — Apple
247
- Cut-Cross-Entropy + Liger transpiled Triton → Metal with three divergences:
248
- the classifier is the **quantized** head (in-Metal 4/8-bit affine dequant,
249
- mlx `quantized.h` qdot pattern); only `dE`/`dh` is needed (frozen head, no
250
- `dC`); the cross-block LSE merge is cheap MLX ops on per-block partials, not
251
- atomic logaddexp. Evolution, all 2026-06-18/19 on the M1 Max 32 GB dev box:
252
- - v1 (one threadgroup per token, whole vocab serially): memory bound
253
- achieved (MiniCPM5 M=8192 fwd 2.45 → 0.34 GB; e4b M=512 bwd 2.06 → 1.17
254
- GB) but e4b ≥1024 tripped the GPU watchdog.
255
- - Vocab-parallel grid `[TG, nVocabBlocks, M]`, `VOCAB_BLOCK=8192`; backward
256
- atomic-adds partial `dh` into `[M,H]`. e4b M=1024 deterministic, logp
257
- 0.20% / dh 0.40%, bwd peak 3.41 → 1.46 GB.
258
- - `BLOCK_B` token tiling (dequant a vocab row once, reuse across 8 tokens
259
- fwd / 4 bwd — the backward is register-bound by `dacc`): 2.5× total.
260
- - **steel GEMM** (`steel-qmm-header.ts`, MLX's verbatim `BlockMMA`) for
261
- forward and backward, with a temp-BlockMMA-per-H-tile + lane-local
262
- fragment accumulation: e4b bwd **3687 → 754 ms**, fwd 180 ms, peak
263
- **0.93 GB flat @ M=8192**; dh 0.40% e4b / 0.28% cpm.
264
- `MLX_BUN_CCE_NOSTEEL` / `MLX_BUN_CCE_BWD_NOSTEEL` fall back to the
265
- simdgroup / scalar kernels (also the non-32-divisible-shape fallback).
266
- - Apple-CCE backward skips: coeff filter (`MLX_BUN_CCE_BWD_FILTER_EPS`) and
267
- blockMax skip (`MLX_BUN_CCE_BWD_BLOCK_EPS`), **both default `1e-5` since
268
- 2026-07-02** (`=0` compiles them out). Real-data teacher-forced
269
- measurement (M1 Max, chunk-ORPO data): filter 0.343%/1.41× (CPM5),
270
- 0.158%/1.70× (e4b); block skip ≤0.004% dh; combined **1.71× / 3.16×**
271
- backward vs exact. Run-to-run dh is not byte-stable at any eps (atomic-add
272
- reassociation). Note: docs/reference/training.md still describes the skips as
273
- off by default; `flash-cce.ts` is the truth.
274
- - Operand precision today is **W4A32**: 4-bit weights dequantized into fp32
275
- tiles and multiplied with fp32 simdgroup matrices; the `dh` accumulator is
276
- already fp32. See §8.1 (W4A16 spike).
277
-
278
- Composition finding: the fused `CustomVjp` **does not bound memory when nested
279
- inside the segmented `mlx_vjp` head** (its per-chunk graph is not freed
280
- incrementally there; +0.8–1.1 GB at L=3072–4096 on MiniCPM5), so the segmented
281
- bounded head uses the Checkpoint variant `chunkedLogpMeanFromHidden` (bit-exact
282
- recompute). And bounding the head did not reduce the segmented peak at any
283
- reachable scale (MiniCPM5 L=2048–4096 × SEG 2/6; e4b L=2048: 11.95 GB both) —
284
- the head `[M,V]` is transient and never stacks with the per-segment backward
285
- peak. The trainer still routes flash/fused into the segmented head so e4b never
286
- reintroduces `[M,V]`.
287
-
288
- Host-buffer lesson (2026-06-21): `u32()` kernel-arg arrays built with the
289
- zero-copy `fromView` and disposed before the lazy kernel evaluated leaked pins
290
- (+32/step) → use-after-free that ran clean on the 32 GB M1 Max and crashed on
291
- the 24 GB M4 Pro. Fix: `fromBytesCopy`; `MetalKernel.apply` `ptr()` lifetimes
292
- hardened; a `pinned` canary on the train metric. `fromView` is only for
293
- process-lifetime memory. A related UAF in the segmented ORPO classes (the flash
294
- head's `headSink` freed before the lazy backward read `lse`/`blockMax`) is fixed
295
- by `ops.evalAll` on the head-VJP roots before dispose.
296
-
297
- ### 5.2 Attention backward: `ops.sdpa` vs the flash kernel
298
-
299
- Default training attention is `ops.sdpa` (`mx.fast.scaled_dot_product_attention`)
300
- — exact (dQ/dK/dV FD-verified 0.00%) and fast, but its Metal backward is NYI
301
- (`ScaledDotProductAttentionVJP::use_fallback()` returns true), so MLX
302
- decomposes it and **materializes the L×L scores for every layer**: O(L²)
303
- backward memory (0.02 → 0.33 → 1.40 GB at T=512/2048/4096 in the isolated
304
- attention-backward bench; ~3.5 GB/layer @8K on e4b). The hand-rolled flash
305
- kernel (`src/model/flash-attention.ts`, `MLX_BUN_TRAIN_ATTN=flash`) is O(L)
306
- (0.03 → 0.23 → 0.90 GB) but ~30× slower — a naive scalar kernel at ~0.5% of GPU
307
- peak. Two genuine port bugs were fixed rather than deleting it (a spurious dK
308
- transpose for Tkv≠D; a divergent `threadgroup_barrier` in the dQ causal
309
- tile-skip), restoring L2 parity with optiq. It remains the dormant long-context
310
- fallback for MiniCPM5; the trainer refuses it for Gemma.
311
-
312
- The sliding-window layers are NOT cheaper in the backward — the window is only
313
- an additive mask on the materialized scores (measured 2026-07-02: a sliding
314
- pair +7.1 GB ≈ sliding+full +7.15 GB @8K). This falsified the earlier
315
- full-attention-isolation planner premise (§5.3).
316
-
317
- ### 5.3 Segmented backward
318
-
319
- Naive gradient checkpointing (ours and Python mlx's) does not stream: the
320
- checkpointed backward holds every layer's recompute activations at once, so
321
- e4b spiked to 23 GB live @2048 (resting 6.95 GB) and crashed at 4096
322
- (`peak(L) ≈ 7.7 GB + 7.6 MB/token`). Measurement note that unlocked this:
323
- `mlx_get_peak_memory` is peak **live**, not live+cache; use
324
- `activeMemory()`/`cacheMemory()`/`clearCache()` to separate them, and eval the
325
- loss and grads together (`evalAll([value, ...grads])`) — evaluating `value`
326
- separately frees the forward and forces the backward to recompute-and-hold
327
- everything.
328
-
329
- **Mechanism** (`SegmentedBackward*`, `planSegmentsBySize`):
330
-
331
- 1. Forward, saving boundaries: run the layer stack; at each segment edge
332
- (`segmentSize` layers) eval and **detach** the residual hidden into a leaf
333
- (`detachLeaf` = `ops.contiguous` + `detachCopy`; `detachLeaves` batches the
334
- eval barrier). mlx `eval` does not detach — without the copy each boundary
335
- drags a layer-stack of activations.
336
- 2. Head: `finalNorm` + LM head + loss over the last boundary as a `Vjp` with
337
- scalar cotangent 1.0 → loss value and `dh_out`.
338
- 3. Backward, reverse over segments: `vjp.apply([boundary_k, ...LoRA_k], [dh_out])`
339
- → `[dh_in, ...dLoRA_k]`; `dh_in` seeds segment k−1; LoRA grads scatter into
340
- the flat grad vector. Only one segment's activations are live. Recompute
341
- overhead ≈ 1× forward regardless of segment count, so `segmentSize` is a
342
- peak knob at ~fixed time.
343
-
344
- **The `mlx_vjp` finding.** The first implementation used optiq's
345
- surrogate-loss trick — `value_and_grad` of
346
- `sum(stop_grad(dh) ⊙ segment_forward(...))` per segment. Numerically identical,
347
- but it **leaked ~32 MB of live memory per segment per step** (linear, no
348
- plateau; 1.12 → 6.61 GB over 30 iters at SEG=4/2048), unreclaimable by
349
- `clearCache`/GC/`synchronize`/disposal. A minimal repro pinned it to
350
- `value_and_grad` (`MODE=layers` leaks ~6 MB/iter; `MODE=vjp` flat). Driving
351
- each segment with **`mlx_vjp`** (`Vjp` in `src/mlx/autograd.ts`) is the
352
- production path: active flat (1.04 GB × 12 iters end-to-end at SEQ=4096). Vjp
353
- objects are built once per run and reused; rebuilding per step leaks.
354
-
355
- **Numbers.** MiniCPM5-1B, SEG=4, M1 Max 32 GB, 2026-06-16, peak live:
356
-
357
- | seq | full backward | segmented | note |
358
- |---|---|---|---|
359
- | 512 | 3.15 GB | 1.87 GB | |
360
- | 2048 | 10.91 GB | **3.29 GB** | post-vjp fix (3.44 pre) |
361
- | 4096 | 21–26 GB (spikes) | 6–8 GB | |
362
-
363
- Under flash attention the segmented grads bit-match the full `value_and_grad`
364
- (relNorm 0.0000%, maxAbs 0.0). Under `ops.sdpa` they differ ~6% relNorm — not a
365
- segmentation bug: mlx's fused sdpa gives a different bf16 result in its eager
366
- forward (12.0712) than its autograd-decomposed forward (12.0568), so boundaries
367
- saved eagerly and recomputed under autograd disagree. The non-segmented trainer
368
- already trained against the autograd forward while inference used the eager
369
- one. A real 300-iter run (SEG=4, SEQ=4096) peaked 6.51 GB (non-seg 25.47 GB),
370
- no leak, and its adapter scored 95.10 on the chunk holdout vs 91.70
371
- non-segmented.
372
-
373
- **e4b (Gemma4) additions** (`SegmentedBackwardGemma4`, `Gemma4Model.runLayerRange`
374
- / `makeTrainingMasks` / `embedForSegmented` / `reusedDonors`):
375
-
376
- - Per-layer-input is a pure constant boundary (its grad would flow to the
377
- embed / `per_layer_model_projection`, neither a LoRA target): detached once,
378
- sliced per segment, cotangent discarded.
379
- - KV-shared donor K/V (donors 22 sliding / 23 full feed all sharers in
380
- [24,42)) is a **second boundary stream with its own cotangent**: sharer
381
- segments differentiate `[h, donorK, donorV, ...LoRA]` and accumulate the
382
- donor vjps into `dKV[d]`; the donor segment's multi-output forward
383
- `[h_out, k, v]` takes `[dh, dKV.k, dKV.v]`. Two contiguity fixes were
384
- essential: `detachLeaf` must force row-major (donor K/V are transposed
385
- views) and the producer must output contiguous donor K/V.
386
- - Grads: single-consumer donor reuse is bit-exact (2-segment split at 24: 0/258
387
- off); the natural 7-segment cut is ~0.97% relNorm from bf16
388
- non-associativity of the within-vjp cotangent sums — flat in consumer count,
389
- tracks summand count (donor 22 ≈0.46%, donor 23 ≈0.000%), not helped by fp32
390
- cross-segment accumulation. **The lever is the segment cut, not the dtype**:
391
- keep a donor's sharers in one consumer segment for tighter grads at higher
392
- peak. Vs the `mlx_lm.lora` bit-exact reference: segmented q_proj dB ~2.3%
393
- (bf16-class).
394
- - Memory vs the honest reference (`mlx_lm.lora --train --grad-checkpoint`,
395
- which checkpoints per layer, 42 groups of 1; oracle venv, M1 Max 32 GB,
396
- 2026-06-16), matched grouping:
397
-
398
- | L | mlx-lm (per-layer) | ours segSize=1 | ours segSize=6 |
399
- |---|---|---|---|
400
- | 2048 | 12.84 GB | **8.76 GB** | 11.05 |
401
- | 4096 | 20.87 GB | **10.93 GB** | 16.06 |
402
- | 8192 | OOM (25.72 @16 layers) | **15.29 GB** | 17.5 (seg2) |
403
-
404
- Two unisolated confounds: mlx-lm's `default_loss` materializes full
405
- `[1,L,262144]` logits (~4 GB @4K) vs our response-only CE; and full streaming
406
- vs per-layer checkpoint inside one backward. The headline is part
407
- CE-optimization, part mechanism.
408
-
409
- **Segmentation strategy — corrected 2026-07-02.** The original plan (isolate the
410
- 7 O(L²) full-attention layers in singleton segments, `planSegments` with a
411
- memory cost model) was built and A/B'd @8K: **zero peak win** (18.09 vs 18.02
412
- GB seg2), because every layer's sdpa backward is O(L²) (§5.2). The worst
413
- segment is set by **layer count alone** — `segment_size` is the whole knob:
414
- seg1 = 14.59 GB @8K on e4b, +3% step time, loss identical. Going lower needs an
415
- O(L)-memory attention backward, not smarter segmentation. `planSegmentsBySize`
416
- stays; the donor/boundary constraints from the plan still hold. Rotor/min-cut
417
- planners (§8.1) would need a corrected cost model.
418
-
419
- **ORPO variants.** `SegmentedBackwardOrpo[Gemma4]` run chosen and rejected
420
- forwards segment-by-segment, a two-input head `Vjp` over `[hC, hR]` yields
421
- `[dh_c, dh_r]`, each branch walks backward over its segments (donor cotangents
422
- in a per-branch `dKV`), LoRA grads summed. e4b at L=256: full two-branch 12.67
423
- GB → segmented 7.66 GB; 8.71 GB at L=1024 where the full backward doesn't fit
424
- (2026-06-18, M1 Max 32 GB). At λ=0 the e4b path is byte-identical to SFT
425
- `SegmentedBackwardGemma4` (relNorm 1.1341%, maxAbs 3.79e-3 — the same numbers),
426
- proving zero added error; donor recipients (layers 22/23 k/v_proj) stay <0.5%.
427
-
428
- **Intra-layer MLP split** (`mlpSplit`, gradCheckpoint path, Gemma4 only):
429
- `forwardAttn` / `forwardMlp` checkpointed separately with the post-attn
430
- residual `hMid` as the boundary. Bit-exact (0.000000%). Honest peak: L=2048
431
- no-ckpt 28.60 → single 23.40 → split 23.35 GB — the per-layer checkpoint
432
- already serializes layers, so the split saves one layer's attn-sized window;
433
- under `ops.sdpa` the MLP intermediate dominates. Off by default.
434
-
435
- ### 5.4 Shared prompt-prefix forward
436
-
437
- Chosen and rejected share the prompt; the causal mask makes the prefix's
438
- hidden states independent of the continuation. One forward over
439
- `[prompt(P); chosen(Rc); rejected(Rr)]` with (a) a **block-sparse mask**
440
- (causal AND NOT rejected→chosen, `blockSparseMask`, carried by
441
- `PrefixSharedCache`), (b) **block-wise RoPE** resetting each response to offset
442
- P (`ropeBlocks`, `setMiniCpmPrefixPlan` / `setGemmaPrefixPlan`), and the head
443
- gathering `chosen[k] ← H[P−1+k]`, `rejected[0] ← H[P−1]`, `rejected[k≥1] ←
444
- H[P+Rc+k−1]` (`prefixGatherIdx`). Token cost `2(P+R) → P+2R`; reverse-mode AD
445
- sums the two branches' cotangents into the prefix for free. Published for DPO
446
- (Wang & Hegde, arXiv 2410.20305); no mainstream trainer does it at training
447
- time. `splitPrefixBatch` falls back to two-forward for rows whose prompts
448
- differ (logged once, counted).
449
-
450
- - **MiniCPM5**: forward **bit-exact** vs two-forward (P=512/Rc=64/Rr=80), grads
451
- ~1.05% (f32-over-bf16 reassociation in LoRA grad matmuls over the merged
452
- length; holds at λ=0), 1.78× fewer token-passes, peak 5.40 → 3.80 GB
453
- (2026-06-18).
454
- - **e4b** (`blockSparsePrefixMaskGemma` with a LOGICAL-position sliding-window
455
- cut, `Gemma4PrefixSharedCache`, `orpoLossPrefixSharedGemma`): the
456
- construction is proven — block-RoPE+mask == a plain forward over the same
457
- concat to 0.00% at every chosen position — but it does NOT reproduce the
458
- two-forward numbers (loss ~0.08%, hiddens 3–14%) because **the e4b forward is
459
- itself length-sensitive**: `forward([prompt])` vs `forward([prompt; extra])`
460
- differ 1.7% at position 0 up to ~14% later, through the production
461
- `TrainingCache`. Root cause is e4b's scale=1.0 attention (headDim 256, normed
462
- q/k → sharply peaked softmax) amplifying sub-bf16 matmul-tiling roundings
463
- that vary with row count — the same class as the SigLIP tower. MiniCPM5
464
- (1/√d) shows 0.00%. Accepted as a numerical caveat for a fine-tune; the
465
- single-concat forward is its oracle. Composed with the segmented backward
466
- (`SegmentedBackwardOrpoPrefixGemma4`, donor-KV + logical-window mask threaded
467
- through segments): seg-vs-non-seg grads 1.7–2.3%, peak 30–39% lower
468
- (2026-06-19).
469
- - Measured effect on e4b @8192 (M1 Max 32 GB, prompt-dominant data, short
470
- probe): 16.1 GB / ~175 s/step → **13.3 GB / ~70 s/step** with prefix-share.
471
-
472
- ### 5.5 What composes with what
473
-
474
- | | flash/fused head | segmented | prefix-shared | grad-accum | B>1 |
475
- |---|---|---|---|---|---|
476
- | flash/fused head | — | yes (Checkpoint-bounded head inside segments) | yes (per branch) | yes | no (B=1) |
477
- | segmented | | — | yes (MiniCPM5 + Gemma4) | yes | no |
478
- | prefix-shared | | | — | yes | no |
479
- | grad-accum | | | | — | n/a |
480
-
481
- The memory-optimized paths are **B=1 only**; `grad_accumulation_steps` is the
482
- effective-batch lever at B=1 memory (`accumulateStep` evals and frees each
483
- micro-graph; measured ~1.02× peak at N=3 on MiniCPM5, both SFT and ORPO;
484
- `tests/train-gradaccum.test.ts`). B>1 falls out on the monolithic path via
485
- `iterateDpoBatches` padding + the batched pad mask (`forward.ts`). True batched
486
- segmented backward is not built. Qwen has no segmented backward (needs
487
- `SSMCache` + a differentiable gated-delta kernel) and would land on the full
488
- backward first.
489
-
490
- Explicitly dropped: activation offloading (no-op on unified memory), TRL's
491
- padded concatenated forward, sequence packing / length-grouped batching
492
- (SFT-only wins, moot at B=1).
493
-
494
- ### 5.6 Quantized base
495
-
496
- Each `QuantizedLinear` carries its own `QuantSpec`; `quantized_matmul` has a
497
- working x-vjp and the base is frozen, so mixed precision costs nothing in the
498
- layer stack. The only quant-aware training code is the head: a vocab chunk is
499
- an axis-0 row slice of the quantized `w`/`scales`/`biases` (one spec per head),
500
- and the flash-CCE kernel dequantizes in-kernel.
501
-
502
- ## 6. Kernel-fusion notes (the flat-DAG payoff)
503
-
504
- Verified MLX facts (re-grep against the pinned 0.31.2 before relying):
505
- `mx.compile` fuses contiguous elementwise chains in fwd AND bwd, bounded by
506
- matmul/SDPA/reductions/reshape — compile the *outermost* `value_and_grad` or
507
- the backward lands outside the region; `mx.fast` ships fused fwd+bwd for
508
- `rms_norm`/`layer_norm`/`rope` (use them); SDPA backward is the Metal
509
- exception (§5.2); `mx.fast.metal_kernel` inside `mx.custom_function` with a
510
- `.vjp` is fully differentiable (the mechanism every custom head uses).
511
-
512
- Remaining custom-kernel targets, ranked: fused SDPA backward (FA2-style,
513
- store per-row LSE, recompute `P` — removes the O(L²) term segmentation
514
- compensates for); fused dequant-GEMM + LoRA epilogue with the Unsloth-style
515
- manual backward (`h=x@A; dB=s·hᵀ@dy; dA=s·xᵀ@(dy@Bᵀ); dx=dy@Wᵀ + s·(dy@Bᵀ)@Aᵀ`);
516
- recompute↔backward fusion in the segmented path; min-cut rematerialization
517
- (AOTAutograd's partitioner) as a fusion-aware replacement for hand-chosen cuts.
518
- The GeGLU/SwiGLU `CustomVjp` (grad_b = dc·gelu(a), grad_a = dc·b·gelu'(a)) was
519
- written and validated, then deleted 2026-07-05 with the fused kernels —
520
- compiled activations own that fusion bit-exactly
521
- ([unified-engine-frontier-plan.md §6](unified-engine-frontier-plan.md)), and
522
- training needs no env-flag sanitization.
523
-
524
- Per-model specialized training paths would mirror `scripts/gen-model.ts`'s
525
- fingerprint-dispatched generated forwards with the monolith as fallback; not
526
- built.
527
-
528
- ## 7. Quality experiment pipeline
529
-
530
- Pieces (judge-free, unit-tested): `src/eval/ifeval.ts` (verifiable-instruction
531
- scorer), `src/eval/ultrafeedback.ts` + `scripts/curate-ultrafeedback.ts`
532
- (binarized UF → preference JSONL with length filtering), `scripts/run-ifeval.ts`
533
- (before/after runner), `scripts/bench-orpo.ts` (ms/step + peak across head /
534
- segment configs). Prompt masking is verified (`tests/dpo-masking.test.ts`):
535
- left-truncation preserves the prompt/response boundary; the only leak is a
536
- completion ≥ `max_seq_length`, which `loadDpoDataset` warns on.
537
-
538
- Result so far: an 800-step MiniCPM5 UltraFeedback run left IFEval flat
539
- (22.5%) — general data + a tiny run on a 1B is the wrong lever. The
540
- load-bearing target is the **chunk segmenter** (distilled segmentation with
541
- boundary/label accuracy vs gold). Small sets (~450 pairs) overfit fast: keep λ
542
- modest, carve a ~50-pair `valid.jsonl`, watch val margin, `save_checkpoints`
543
- and mount the best-margin checkpoint.
544
-
545
- ## 8. Future
546
-
547
- ### 8.1 Deferred performance levers
548
-
549
- - **Grad-accumulation A/B.** `--grad-accum` is exposed in `mlx-bun train`
550
- (`scripts/train-orpo.ts` has no env knob for it). What's open is the
551
- measurement: at hard B=1 every update is one pair (the 0.4↔3.3 per-step loss
552
- swing is that noise); A/B G=4/8 on val margin. It is not a speedup and not a
553
- guaranteed win (the prior run converged at B=1, val 1.66→1.50). Generic
554
- N-way batching is parked — prefix-share is already a hand-built
555
- batch-of-2 (1F+1B over `[P; Rc; Rr]`), strictly cheaper than a generic
556
- 2-batch's 2F+2B.
557
- - **W4A16 head GEMM (compute precision) — proposed 2026-06-19, not started.**
558
- The steel head runs W4A32 (`BlockMMA<float,float>` fwd and both bwd phases;
559
- fp32 staging tiles; dequant into fp32). Apple GPUs run fp16 FMA at ~2× fp32,
560
- and the kernel is **ALU-bound, measured live** (M1 Max, MiniCPM5 ORPO
561
- `--no-segment` seq 4096: GPU 100% @1296 MHz, memory BW 129 GB/s ≈ ⅓ of ~400,
562
- 17.4/32 GB) — the 4-bit storage already did its job and the GPU spends
563
- cycles on fp32 FMAs. Plan: (0) confirm which `BlockMMA`/`BlockLoader`
564
- template params are operand vs accumulator; (1b) profile the dequant-ALU vs
565
- FMA split — if FMA < ~40% of the kernel, reset expectations; (2) add a gate
566
- (`CCE_A_HALF` define + env flag, fp32 stays the exact parity mode for the
567
- 1e-5 gate, half graded by greedy-prefix logit parity); (3) forward to half
568
- operands first, land alone; (4) backward range probe — the survivors after
569
- the skips are near-zero coeff tails where fp16 underflows, and `h` can carry
570
- residual outliers (M1 Max has no bf16 simdgroup_matrix, so fp16 is the only
571
- half); (5) backward to half only on a go; (6) re-sweep `BLOCK_B`/tiles —
572
- half staging halves threadgroup memory, the constraint that capped the
573
- current tiling. fp32 accumulate is non-negotiable; validate grads vs
574
- autograd. Context measurement (M1 Max, MiniCPM5 seq 4096): `--no-segment`
575
- ~1.25 s/step @9.26 GB vs `--seg 2` ~1.58 s/step @2.62 GB — bit-identical
576
- output, ~20% faster for 3.5× peak.
577
- - **Sequence-chunk the MLP sub-block** inside segments: MLP is per-token
578
- independent, so its `d_ff` intermediate can drop from `O(T·d_ff)` to
579
- `O(chunk·d_ff)` at ~zero recompute — the bigger MLP lever than `mlpSplit`.
580
- - **Budget-aware segment planner** (rotor DP / min-cut): only worth building
581
- with the corrected cost model (every layer O(L²) under `ops.sdpa`); today
582
- `segment_size` is the whole knob.
583
- - **Faster flash-attention backward**: simdgroup tiling, larger threadgroups,
584
- FA2-style LSE-only storage — matters only where `ops.sdpa`'s O(L²) is the
585
- blocker (≳16K). MLX's own `mx.fast.sdpa` source is the blueprint.
586
- - **Fused LoRA epilogue** (§6) — benefits SFT/DPO/ORPO and adapter serving.
587
- - **Shape-specialized compiled training graphs**: each row has concrete
588
- `(P, Rc, Rr)`, so ORPO can compile exact or bucketed shapes keyed on model
589
- checksum + flags + segment plan + chunk sizes, eager fallback for misses.
590
- Custom Metal kernels stay outside compiled regions.
591
- - **ORPO-grad folding inside the head kernel** (speed-only; autograd composes
592
- the odds-ratio grad outside it today) and a head-LoRA `∇A/∇B` path if the
593
- head ever becomes a target.
594
- - Optimize the flash SDPA kernel with simdgroup_matrix, or beat `mx.fast.sdpa`
595
- for our fixed head-dims / training-only backward / O(L) constraint —
596
- research spike, not scheduled.
597
-
598
- ### 8.2 Dynamic λ controller (proposed 2026-06-20, reframed 2026-07-01; not built)
599
-
600
- Josh's framing: *λ is not a hyperparameter to tune but a process variable to
601
- control* — push preference pressure to the maximum the model can bear
602
- (self-discovering), retreat on confirmed coherence damage (self-stabilizing),
603
- hold inside the healthy band. Motivation: the SFT/NLL half learns easily (val
604
- loss falls while accuracy/margin barely move); over-weighting OR triggers
605
- **likelihood displacement** (margin widened by crushing `logp(rejected)` and
606
- sometimes `logp(chosen)`, cf. arXiv 2410.08847). Closest prior is β-DPO
607
- (arXiv 2407.08639, per-batch calibration); the claim here would be closed-loop
608
- trajectory control with a displacement-aware signal — the controller must beat
609
- the *best constant* λ from a sweep, not λ=0.1.
610
-
611
- - **Signals**: `lw` and `lr` separately (today `orpoMetrics` returns only
612
- `loss/nll/or/accuracy/margin` — surfacing `lw`/`lr` is the load-bearing
613
- instrumentation), on **val** each `stepsPerEval` (per-step B=1 train metrics
614
- are too noisy). Control on the **response-only** `lw` — full-scope NLL mixes
615
- prompt modeling into the level. Margin↑ via `lw`↑ is healthy; margin↑ via
616
- `lr`↓ with `lw` flat/falling is displacement.
617
- - **Law**: phase machine (SFT warmup at `LAMBDA_MIN` until `dLw` plateaus →
618
- ramp `+RAMP` per eval while `dLw ≥ −EPS_DISP` → back off on displacement,
619
- hysteresis over K evals; hard stop if `lw` falls below its start by a
620
- threshold) — i.e. AIMD; generalize to P → PD → PID incrementally, each term
621
- earning its place (D on a robust EMA slope for the leading indicator; I with
622
- anti-windup for chronic offset). Bias toward pushing with a safety margin
623
- below the cliff; the degenerate failure is collapsing to pure SFT.
624
- - **Wiring**: λ becomes a mutable ref read per step (today it is a constant
625
- threaded into `orpoLoss` and baked into the segmented classes' constructors
626
- → add `setLambda`); controller lives in `orpoLoop` after each val eval;
627
- gated `orpoLambdaSchedule: "static" | "adaptive"`, static = today exactly.
628
- - **Validation**: same data/seed, Arm A static 0.1 vs Arm B adaptive; compare
629
- val accuracy/margin and `lw` trajectories plus downstream win-rate; validate
630
- the controller on the prefer-uppercase positive control first; set EPS from
631
- observed val-`lw` noise, not hand-fit. Report both arms (adaptive is our
632
- method, not "standard ORPO").
633
-
634
- ### 8.3 Objective variants and data
635
-
636
- Once the shared logp engine exists these are cheap siblings: **SimPO**
637
- (`softplus(−β((lw − lr) − γ))`), **IPO**, **hinge/SLiC** margin loss, label
638
- smoothing / per-example weights / loss clipping / ambiguity filtering. Data
639
- quality likely beats objective cleverness on small runs: filter UltraFeedback
640
- by score gap, bounded length ratio, semantic contrast, and instruction-following
641
- categories if IFEval is the metric.
642
-
643
- ## History
644
-
645
- - 2026-06-15 — e4b LoRA enablement diagnosis: the wall is backward memory
646
- (full-vocab logits + un-streamed activations), not attention; response-only
647
- CE landed.
648
- - 2026-06-16 — Segmented backward Phase A (MiniCPM5, bit-exact under flash,
649
- 10.91 → 3.29 GB @2048) and Phase B (e4b, donor-KV + per-layer-input
650
- threading, all 42 layers @8K where mlx-lm OOMs); surrogate `value_and_grad`
651
- leak found, replaced by `mlx_vjp`; non-segmented SFT proven bit-exact vs
652
- `mlx_lm.lora`.
653
- - 2026-06-17 — ORPO landed as a method: loss math + tests, `orpoLoop`, job
654
- submit, `/finetune` picker, e2e on MiniCPM5; `bitsMap` rank scaling wired;
655
- dataset format renamed `preference`; flash SDPA kernel fixed to L2 parity.
656
- - 2026-06-18 — Token-chunked head; fused linear-CE head; vocab-blocked pure-MLX
657
- CCE (correct, unbounded, gated off); flash-CCE kernel v1 → vocab-parallel →
658
- `BLOCK_B`; `SegmentedBackwardOrpo` + `SegmentedBackwardOrpoGemma4`; MiniCPM5
659
- prefix-shared reference (forward bit-exact); LoRA dropout / rsLoRA / LoRA+;
660
- `mlpSplit`.
661
- - 2026-06-19 — Steel GEMM flash head fwd+bwd in production (e4b bwd 3687 → 754
662
- ms, 0.93 GB flat); prefix-sharing wired and composed with the flash head and
663
- segmented backward for MiniCPM5 and e4b; warm-start (`RESUME`);
664
- `scripts/train-orpo.ts` launcher; W4A16 spike proposed; grad-accum A/B
665
- deferred.
666
- - 2026-06-20/23 — Dynamic λ controller proposed, PID/AIMD framing added.
667
- - 2026-06-21/22 — Host-buffer pin-leak post-mortem (`fromView` → `fromBytesCopy`);
668
- Lab gates: FD + teacher-forced fidelity replace "parity" for the no-oracle
669
- head; coeff filter measured near-free on real data.
670
- - 2026-07-01 — `sft_scope: full | response` (default `full`), implemented on
671
- every ORPO path; dynamic-λ reframed as band control.
672
- - 2026-07-02 — Apple-CCE skips default 1e-5 (1.71× / 3.16× backward);
673
- teacher-forced fidelity regression test; full-attention isolation planner
674
- refuted (every layer O(L²)); `segment_size` 1 = 14.59 GB @8K on e4b.
675
- - 2026-07-05 — Fused GeGLU/SwiGLU and perf kernels deleted; no training flag
676
- sanitization remains.
677
- - 2026-08-23 — This doc consolidated `docs/design/orpo-training.md`,
678
- `docs/design/orpo-training.md`, `docs/design/orpo-training.md`, and
679
- `docs/design/orpo-training.md`; examples scripts moved to
680
- `scripts/examples/`.