mlx-bun 0.4.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 (601) hide show
  1. package/README.md +1453 -88
  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 +10 -8
  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 +12 -10
  78. package/src/{serve → server}/chat-stage.ts +27 -22
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +42 -42
  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 +42 -83
  85. package/src/server/generated-token-history.ts +57 -0
  86. package/src/{serve → server}/http.ts +25 -13
  87. package/src/{serve → server}/inference-request.ts +5 -5
  88. package/src/server/job-routes.ts +20 -0
  89. package/src/server/media-prompt.ts +63 -0
  90. package/src/server/memory-completion-client.ts +46 -0
  91. package/src/server/model-routes.ts +192 -0
  92. package/src/{serve → server}/openai-wire.ts +3 -2
  93. package/src/{serve → server}/pipeline.ts +1 -1
  94. package/src/server/prompt-builder.ts +13 -0
  95. package/src/server/prompt-contracts.ts +17 -0
  96. package/src/server/proxy-routes.ts +187 -0
  97. package/src/server/publishing-routes.ts +41 -0
  98. package/src/{serve → server}/request-plan.ts +8 -8
  99. package/src/{serve → server}/request-prep.ts +82 -68
  100. package/src/{serve → server}/responses-client.ts +30 -13
  101. package/src/{responses.ts → server/responses.ts} +12 -93
  102. package/src/server/routes.ts +205 -0
  103. package/src/server/start.ts +72 -0
  104. package/src/server/status-routes.ts +108 -0
  105. package/src/{serve → server}/text-completion-stage.ts +25 -4
  106. package/src/server/text-prompt.ts +12 -0
  107. package/src/{serve → server}/token-streams.ts +36 -38
  108. package/src/server/worker-routes.ts +311 -0
  109. package/src/storage/paths.ts +61 -0
  110. package/src/web/assets.ts +37 -0
  111. package/src/web/{src → browser}/api.ts +5 -23
  112. package/src/web/browser/chat-host.ts +17 -0
  113. package/src/web/browser/installed-panels.ts +6 -0
  114. package/src/web/browser/main.ts +55 -0
  115. package/src/web/browser/markdown.ts +279 -0
  116. package/src/web/browser/memory-panel.ts +9 -0
  117. package/src/web/browser/palette.ts +64 -0
  118. package/src/web/browser/protocol.ts +13 -0
  119. package/src/web/browser/shell.ts +467 -0
  120. package/src/web/{src → browser}/status.ts +32 -36
  121. package/src/web/build.ts +51 -0
  122. package/src/web/public/app.html +1054 -0
  123. package/src/web/{sw.js → public/sw.js} +4 -3
  124. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  125. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  126. package/THIRD_PARTY_LICENSES.md +0 -51
  127. package/docs/README.md +0 -94
  128. package/docs/archive/hlg-sampling.md +0 -350
  129. package/docs/archive/investigations/audio-input-plan.md +0 -356
  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/web-ui-pass-plan.md +0 -213
  174. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  175. package/docs/design/batching.md +0 -1841
  176. package/docs/design/decode-speed-program.md +0 -2349
  177. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  178. package/docs/design/generic-model-support.md +0 -705
  179. package/docs/design/kv-cache.md +0 -824
  180. package/docs/design/orpo-training.md +0 -680
  181. package/docs/design/speculative-decoding.md +0 -1990
  182. package/docs/design/turboquant.md +0 -2212
  183. package/docs/design/unified-engine-frontier-plan.md +0 -1880
  184. package/docs/design/web-chat-redesign.md +0 -695
  185. package/docs/planning/PRODUCT_ROADMAP.md +0 -594
  186. package/docs/planning/ResearchTopics.md +0 -119
  187. package/docs/planning/release-notes-v0.4.0.md +0 -45
  188. package/docs/planning/video-series.md +0 -421
  189. package/docs/reference/benchmarks.md +0 -3613
  190. package/docs/reference/cli.md +0 -585
  191. package/docs/reference/distribution.md +0 -355
  192. package/docs/reference/environment.md +0 -398
  193. package/docs/reference/glossary.md +0 -39
  194. package/docs/reference/library-api.md +0 -674
  195. package/docs/reference/memory.md +0 -260
  196. package/docs/reference/models.md +0 -334
  197. package/docs/reference/server-api.md +0 -1775
  198. package/docs/reference/server-config.md +0 -846
  199. package/docs/reference/training.md +0 -462
  200. package/docs/reference/troubleshooting.md +0 -158
  201. package/src/admission-plan.ts +0 -18
  202. package/src/audio/conformer.ts +0 -647
  203. package/src/audio/decode.ts +0 -133
  204. package/src/audio/features.ts +0 -202
  205. package/src/audio/transcode.ts +0 -67
  206. package/src/backends/mlx/assistant-target.ts +0 -46
  207. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  208. package/src/backends/mlx/autoregressive.ts +0 -108
  209. package/src/backends/mlx/batch-group.ts +0 -1790
  210. package/src/backends/mlx/cache-attachment.ts +0 -67
  211. package/src/backends/mlx/cache-layout.ts +0 -31
  212. package/src/backends/mlx/checkpoint-state.ts +0 -53
  213. package/src/backends/mlx/continuation-persistence.ts +0 -106
  214. package/src/backends/mlx/continuation-request.ts +0 -43
  215. package/src/backends/mlx/continuation.ts +0 -77
  216. package/src/backends/mlx/diffusion.ts +0 -39
  217. package/src/backends/mlx/draft-target.ts +0 -32
  218. package/src/backends/mlx/evaluation.ts +0 -105
  219. package/src/backends/mlx/fill-append.ts +0 -45
  220. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  221. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  222. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  223. package/src/backends/mlx/gateway-binding.ts +0 -130
  224. package/src/backends/mlx/graph.ts +0 -38
  225. package/src/backends/mlx/kv-codec.ts +0 -9
  226. package/src/backends/mlx/kv-maintenance.ts +0 -133
  227. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  228. package/src/backends/mlx/memory-client.ts +0 -189
  229. package/src/backends/mlx/methods.ts +0 -90
  230. package/src/backends/mlx/model-prompt.ts +0 -164
  231. package/src/backends/mlx/model-serving.ts +0 -79
  232. package/src/backends/mlx/prefill-cohort.ts +0 -89
  233. package/src/backends/mlx/prefill-rows.ts +0 -200
  234. package/src/backends/mlx/prefill.ts +0 -33
  235. package/src/backends/mlx/projected-context-rows.ts +0 -72
  236. package/src/backends/mlx/request-policy.ts +0 -20
  237. package/src/backends/mlx/request-state-policy.ts +0 -41
  238. package/src/backends/mlx/rollback.ts +0 -89
  239. package/src/backends/mlx/row-sampling.ts +0 -49
  240. package/src/backends/mlx/serial-executor.ts +0 -242
  241. package/src/backends/mlx/speculative-group.ts +0 -359
  242. package/src/backends/mlx/speculative-round.ts +0 -161
  243. package/src/backends/mlx/speculative.ts +0 -114
  244. package/src/backends/mlx/state-rows.ts +0 -104
  245. package/src/backends/mlx/state-views.ts +0 -46
  246. package/src/backends/mlx/target-layout-capability.ts +0 -35
  247. package/src/backends/mlx/text-engine.ts +0 -73
  248. package/src/backends/mlx/transitioning-kv-rows.ts +0 -165
  249. package/src/chat-template.ts +0 -382
  250. package/src/cli.ts +0 -3168
  251. package/src/config.ts +0 -399
  252. package/src/contracts/completion.ts +0 -16
  253. package/src/contracts/execution.ts +0 -60
  254. package/src/contracts/generation.ts +0 -103
  255. package/src/contracts/host.ts +0 -7
  256. package/src/contracts/pi-provider.ts +0 -30
  257. package/src/contracts/pi.ts +0 -259
  258. package/src/contracts/prefix-cache.ts +0 -23
  259. package/src/contracts/resources.ts +0 -25
  260. package/src/contracts/sampling.ts +0 -23
  261. package/src/contracts/scheduling.ts +0 -45
  262. package/src/contracts/tsconfig.json +0 -14
  263. package/src/dataset/generators.ts +0 -1087
  264. package/src/dataset/index.ts +0 -12
  265. package/src/dataset/job.ts +0 -81
  266. package/src/dataset/llm.ts +0 -71
  267. package/src/dataset/registry.ts +0 -528
  268. package/src/diffusion/diffusion-generate.ts +0 -507
  269. package/src/download.ts +0 -505
  270. package/src/embed.ts +0 -77
  271. package/src/engine/admission.ts +0 -59
  272. package/src/engine/cancellation.ts +0 -38
  273. package/src/engine/engine.ts +0 -56
  274. package/src/engine/execution-coordinator.ts +0 -70
  275. package/src/engine/execution-plan.ts +0 -53
  276. package/src/engine/resources.ts +0 -53
  277. package/src/engine/scheduler.ts +0 -72
  278. package/src/engine/session.ts +0 -247
  279. package/src/eval/README.md +0 -130
  280. package/src/eval/ifeval.ts +0 -81
  281. package/src/eval/kl-prompts.ts +0 -29
  282. package/src/eval/kl.ts +0 -590
  283. package/src/eval/perplexity.ts +0 -164
  284. package/src/eval/quality-db.ts +0 -92
  285. package/src/eval/reference-logits.ts +0 -207
  286. package/src/eval/runner.ts +0 -124
  287. package/src/eval/score.ts +0 -49
  288. package/src/eval/tasks/bfcl.ts +0 -535
  289. package/src/eval/tasks/gsm8k.ts +0 -98
  290. package/src/eval/tasks/hashhop.ts +0 -139
  291. package/src/eval/tasks/humaneval.ts +0 -275
  292. package/src/eval/tasks/ifeval.ts +0 -525
  293. package/src/eval/tasks/mmlu.ts +0 -157
  294. package/src/eval/ultrafeedback.ts +0 -60
  295. package/src/evaldb.ts +0 -199
  296. package/src/expert-io.ts +0 -393
  297. package/src/expert-offload-build.ts +0 -77
  298. package/src/expert-offload.ts +0 -76
  299. package/src/expert-residency.ts +0 -1134
  300. package/src/expert-usage.ts +0 -415
  301. package/src/fill/echo-index.ts +0 -188
  302. package/src/fill/fill-session.ts +0 -452
  303. package/src/fill/proposal.ts +0 -68
  304. package/src/fill/schema-rows.ts +0 -506
  305. package/src/fill/tool-boundary.ts +0 -103
  306. package/src/fit.ts +0 -249
  307. package/src/generate.ts +0 -1346
  308. package/src/grammar.ts +0 -591
  309. package/src/harness-pi.ts +0 -184
  310. package/src/hf-push.ts +0 -628
  311. package/src/hub-rest.ts +0 -242
  312. package/src/index.ts +0 -63
  313. package/src/inference/denoising.ts +0 -17
  314. package/src/inference/draft-acceptance.ts +0 -41
  315. package/src/inference/draft-output.ts +0 -49
  316. package/src/inference/graph.ts +0 -33
  317. package/src/inference/prefill.ts +0 -39
  318. package/src/inference/rollback.ts +0 -7
  319. package/src/jobs/index.ts +0 -31
  320. package/src/jobs/job-entry.ts +0 -107
  321. package/src/jobs/task-client.ts +0 -19
  322. package/src/jobs/types.ts +0 -2
  323. package/src/kv-scheme.ts +0 -257
  324. package/src/kv-store.ts +0 -1099
  325. package/src/lab/README.md +0 -18
  326. package/src/lab/curve/curve-designer.html +0 -303
  327. package/src/lab/curve/curve-sampler.ts +0 -122
  328. package/src/lab/expert-trace/expert-trace.ts +0 -69
  329. package/src/lab/paged-kv/paged-kv-rows.ts +0 -83
  330. package/src/lab/paged-kv/paged-kv.ts +0 -270
  331. package/src/library.ts +0 -40
  332. package/src/lora.ts +0 -408
  333. package/src/md-modules.d.ts +0 -49
  334. package/src/media-fetch.ts +0 -442
  335. package/src/memory/article.ts +0 -558
  336. package/src/memory/chunk-validation.ts +0 -150
  337. package/src/memory/chunk.ts +0 -318
  338. package/src/memory/cluster.ts +0 -351
  339. package/src/memory/coverage.ts +0 -134
  340. package/src/memory/crosslink.ts +0 -0
  341. package/src/memory/db.ts +0 -253
  342. package/src/memory/entity.ts +0 -203
  343. package/src/memory/gate.ts +0 -187
  344. package/src/memory/history.ts +0 -152
  345. package/src/memory/ingest.ts +0 -335
  346. package/src/memory/model.ts +0 -183
  347. package/src/memory/normalize.ts +0 -671
  348. package/src/memory/parse.ts +0 -22
  349. package/src/memory/pipeline.ts +0 -275
  350. package/src/memory/prompts.ts +0 -65
  351. package/src/memory/query.ts +0 -334
  352. package/src/memory/reconcile.ts +0 -734
  353. package/src/memory/redirect.ts +0 -108
  354. package/src/memory/reindex.ts +0 -325
  355. package/src/memory/resolve.ts +0 -307
  356. package/src/memory/rest.ts +0 -245
  357. package/src/memory/route.ts +0 -272
  358. package/src/memory/schedule.ts +0 -165
  359. package/src/memory/stages.ts +0 -777
  360. package/src/memory/synthesize.ts +0 -1638
  361. package/src/memory/tools.ts +0 -749
  362. package/src/memory/vault.ts +0 -1194
  363. package/src/memory/wikify.ts +0 -1064
  364. package/src/mlx/array.ts +0 -388
  365. package/src/mlx/autograd.ts +0 -304
  366. package/src/mlx/checkpoint.ts +0 -149
  367. package/src/mlx/compile.ts +0 -168
  368. package/src/mlx/custom-vjp.ts +0 -188
  369. package/src/mlx/ffi.ts +0 -512
  370. package/src/mlx/materialize.ts +0 -23
  371. package/src/mlx/metal-kernel.ts +0 -159
  372. package/src/mlx/ops.ts +0 -1021
  373. package/src/mlx/quantized-rows.ts +0 -36
  374. package/src/mlx/token-bitmask.ts +0 -52
  375. package/src/mlx/turboquant-kv-decode.ts +0 -82
  376. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  377. package/src/mlx/turboquant-ops.ts +0 -861
  378. package/src/mlx/turboquant-tables.ts +0 -237
  379. package/src/mmap.ts +0 -90
  380. package/src/model/artifact-identity.ts +0 -73
  381. package/src/model/batched-kv.ts +0 -235
  382. package/src/model/batched-mask.ts +0 -279
  383. package/src/model/batched-quant.ts +0 -212
  384. package/src/model/batched-quantized-kv.ts +0 -144
  385. package/src/model/batched-rotating-quant.ts +0 -353
  386. package/src/model/batched-rotating-state.ts +0 -149
  387. package/src/model/batched-rotating.ts +0 -338
  388. package/src/model/batched-row-storage.ts +0 -179
  389. package/src/model/batched-ssm.ts +0 -52
  390. package/src/model/batched-turboquant-kv.ts +0 -93
  391. package/src/model/compiled-decode.ts +0 -712
  392. package/src/model/decoded-kv-donor.ts +0 -12
  393. package/src/model/delayed-quantized-kv.ts +0 -72
  394. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  395. package/src/model/delayed-turboquant-kv.ts +0 -55
  396. package/src/model/diffusion-gemma.ts +0 -1026
  397. package/src/model/factory.ts +0 -189
  398. package/src/model/fingerprint.ts +0 -33
  399. package/src/model/flash-attention.ts +0 -694
  400. package/src/model/full-prefill-padding.ts +0 -56
  401. package/src/model/full-prefill-row.ts +0 -105
  402. package/src/model/gemma4-base.ts +0 -2351
  403. package/src/model/gemma4.ts +0 -1315
  404. package/src/model/generated/gemma4-12b.ts +0 -429
  405. package/src/model/generated/gemma4-26b.ts +0 -448
  406. package/src/model/generated/gemma4-e4b.ts +0 -717
  407. package/src/model/generated/index.ts +0 -18
  408. package/src/model/glm52-atlas.ts +0 -511
  409. package/src/model/glm52-cache.ts +0 -792
  410. package/src/model/glm52-config.ts +0 -262
  411. package/src/model/glm52-container.ts +0 -305
  412. package/src/model/glm52-coupling.ts +0 -388
  413. package/src/model/glm52-dsa.ts +0 -606
  414. package/src/model/glm52-expert-layout.ts +0 -253
  415. package/src/model/glm52-layout.ts +0 -152
  416. package/src/model/glm52-memory.ts +0 -500
  417. package/src/model/glm52-mla.ts +0 -792
  418. package/src/model/glm52-moe.ts +0 -223
  419. package/src/model/glm52-pilot.ts +0 -537
  420. package/src/model/glm52-reference.ts +0 -445
  421. package/src/model/glm52-residency.ts +0 -557
  422. package/src/model/glm52-resident-weights.ts +0 -135
  423. package/src/model/glm52-streamed-experts.ts +0 -453
  424. package/src/model/glm52-streamed-metal.ts +0 -610
  425. package/src/model/glm52-weights.ts +0 -503
  426. package/src/model/glm52.ts +0 -1132
  427. package/src/model/implementation.ts +0 -69
  428. package/src/model/kv-attention-view.ts +0 -67
  429. package/src/model/minicpm5.ts +0 -376
  430. package/src/model/profile.ts +0 -443
  431. package/src/model/qwen-append.ts +0 -12
  432. package/src/model/qwen-conv.ts +0 -52
  433. package/src/model/qwen3-delta-state.ts +0 -74
  434. package/src/model/qwen3-delta.ts +0 -451
  435. package/src/model/qwen3-moe.ts +0 -404
  436. package/src/model/qwen3-mrope.ts +0 -252
  437. package/src/model/qwen3.ts +0 -277
  438. package/src/model/qwen3_5-checkpoint.ts +0 -164
  439. package/src/model/qwen3_5.ts +0 -819
  440. package/src/model/rotating-kv-donor.ts +0 -35
  441. package/src/model/rotating-kv-layout.ts +0 -262
  442. package/src/model/rotating-row-transaction.ts +0 -35
  443. package/src/model/speculative-rotating-kv.ts +0 -101
  444. package/src/model/ssm-prefill-padding.ts +0 -39
  445. package/src/model/support.ts +0 -102
  446. package/src/model/trellis-balanced-scatter.ts +0 -51
  447. package/src/model/trellis-linear.ts +0 -672
  448. package/src/model/trellis-shared-m.ts +0 -151
  449. package/src/model/trellis-shared-scatter.ts +0 -144
  450. package/src/model/trellis-splitk-prefill.ts +0 -158
  451. package/src/model/trellis-tiled-prefill.ts +0 -136
  452. package/src/model/trellis-vector-expand.ts +0 -62
  453. package/src/model/trellis-wide-prefill.ts +0 -94
  454. package/src/model/turboquant-codec.ts +0 -83
  455. package/src/model/universal/archs.ts +0 -332
  456. package/src/model/universal/dense.ts +0 -612
  457. package/src/model/universal/modules.ts +0 -170
  458. package/src/model/universal/rope.ts +0 -381
  459. package/src/native/expert_io.c +0 -523
  460. package/src/native/frame_extract.swift +0 -140
  461. package/src/native-pack.ts +0 -162
  462. package/src/pi-provider.ts +0 -53
  463. package/src/pi-session.ts +0 -81
  464. package/src/pi-terminal.ts +0 -274
  465. package/src/pi-web.ts +0 -1932
  466. package/src/preflight.ts +0 -158
  467. package/src/prompt-cache.ts +0 -460
  468. package/src/quantize/allocator.ts +0 -627
  469. package/src/quantize/atomic-output.ts +0 -38
  470. package/src/quantize/calibration.ts +0 -181
  471. package/src/quantize/config-writer.ts +0 -145
  472. package/src/quantize/index.ts +0 -84
  473. package/src/quantize/job.ts +0 -162
  474. package/src/quantize/quantizer.ts +0 -666
  475. package/src/quantize/rotate.ts +0 -603
  476. package/src/quantize/safetensors-writer.ts +0 -249
  477. package/src/quantize/sensitivity.ts +0 -216
  478. package/src/quantize/trellis.ts +0 -586
  479. package/src/quantize/weight-transform.ts +0 -292
  480. package/src/registry.ts +0 -602
  481. package/src/runtime-config.ts +0 -81
  482. package/src/safetensors.ts +0 -158
  483. package/src/sampler.ts +0 -1011
  484. package/src/serve/admin-routes.ts +0 -184
  485. package/src/serve/assets/curve-terrain.html +0 -105
  486. package/src/serve/assets/training-inference-map.html +0 -575
  487. package/src/serve/aux-routes.ts +0 -132
  488. package/src/serve/batch-scheduler.ts +0 -8
  489. package/src/serve/checkpoint-identity.ts +0 -64
  490. package/src/serve/generation-gateway.ts +0 -496
  491. package/src/serve/isolate.ts +0 -609
  492. package/src/serve/kv-budget.ts +0 -14
  493. package/src/serve/lab-routes.ts +0 -341
  494. package/src/serve/lane-registry.ts +0 -45
  495. package/src/serve/model-admin-routes.ts +0 -155
  496. package/src/serve/model-binding.ts +0 -43
  497. package/src/serve/model-host.ts +0 -511
  498. package/src/serve/parent-application.ts +0 -56
  499. package/src/serve/prompt-response-trace.ts +0 -147
  500. package/src/serve/response-lease.ts +0 -24
  501. package/src/serve/session-search.ts +0 -326
  502. package/src/serve/static-routes.ts +0 -101
  503. package/src/server.ts +0 -1314
  504. package/src/spec/assistant-rows.ts +0 -92
  505. package/src/spec/assistant-source.ts +0 -126
  506. package/src/spec/cache-identity.ts +0 -22
  507. package/src/spec/deepspec-rows.ts +0 -29
  508. package/src/spec/deepspec-source.ts +0 -145
  509. package/src/spec/dflash-source.ts +0 -149
  510. package/src/spec/drafter.ts +0 -304
  511. package/src/spec/dspark/ab-stats.ts +0 -136
  512. package/src/spec/dspark/calibration.ts +0 -112
  513. package/src/spec/dspark/data-dflash.ts +0 -156
  514. package/src/spec/dspark/data.ts +0 -201
  515. package/src/spec/dspark/deepspec-module.ts +0 -776
  516. package/src/spec/dspark/generate-dflash.ts +0 -245
  517. package/src/spec/dspark/generate.ts +0 -290
  518. package/src/spec/dspark/loader.ts +0 -23
  519. package/src/spec/dspark/loss.ts +0 -213
  520. package/src/spec/dspark/module-dflash.ts +0 -560
  521. package/src/spec/dspark/module.ts +0 -540
  522. package/src/spec/dspark/quantize-drafter.ts +0 -91
  523. package/src/spec/dspark/sample.ts +0 -112
  524. package/src/spec/generate.ts +0 -276
  525. package/src/spec/glm52-mtp-graph.ts +0 -29
  526. package/src/spec/glm52-mtp-rows.ts +0 -132
  527. package/src/spec/glm52-mtp-source.ts +0 -317
  528. package/src/spec/glm52-mtp-state.ts +0 -33
  529. package/src/spec/ngram-source.ts +0 -204
  530. package/src/spec/projected-draft-rows.ts +0 -73
  531. package/src/spec/qwen-mtp-module.ts +0 -171
  532. package/src/spec/qwen-mtp-rows.ts +0 -219
  533. package/src/spec/qwen-mtp-source.ts +0 -383
  534. package/src/spec/qwen-mtp-state.ts +0 -30
  535. package/src/spec/serve-loop.ts +0 -585
  536. package/src/spec/source.ts +0 -215
  537. package/src/spec/two-model-rows.ts +0 -61
  538. package/src/spec/two-model.ts +0 -113
  539. package/src/ssd-cache.ts +0 -372
  540. package/src/ssd-durability.ts +0 -211
  541. package/src/tokenizer.ts +0 -335
  542. package/src/tool-approvals.ts +0 -131
  543. package/src/tool-call.ts +0 -695
  544. package/src/train/dataset.ts +0 -484
  545. package/src/train/diffusion-lora.ts +0 -216
  546. package/src/train/export.ts +0 -32
  547. package/src/train/flash-cce.ts +0 -1234
  548. package/src/train/forward.ts +0 -266
  549. package/src/train/fuse.ts +0 -243
  550. package/src/train/index.ts +0 -46
  551. package/src/train/job.ts +0 -205
  552. package/src/train/lora-params.ts +0 -254
  553. package/src/train/loss.ts +0 -1591
  554. package/src/train/merge.ts +0 -195
  555. package/src/train/optimizer.ts +0 -183
  556. package/src/train/prefix-shared.ts +0 -490
  557. package/src/train/rank.ts +0 -137
  558. package/src/train/segmented.ts +0 -1828
  559. package/src/train/steel-qmm-header.ts +0 -1461
  560. package/src/train/trainer.ts +0 -1221
  561. package/src/train/watch.ts +0 -542
  562. package/src/vision/diffusion-vision.ts +0 -620
  563. package/src/vision/embedder.ts +0 -168
  564. package/src/vision/preprocess.ts +0 -247
  565. package/src/vision/prompt.ts +0 -373
  566. package/src/vision/qwen3vl-preprocess.ts +0 -409
  567. package/src/vision/qwen3vl-prompt.ts +0 -196
  568. package/src/vision/qwen3vl-tower.ts +0 -555
  569. package/src/vision/siglip.ts +0 -599
  570. package/src/vision/video-frames.ts +0 -148
  571. package/src/web/app.html +0 -2143
  572. package/src/web/app.js +0 -5703
  573. package/src/web/skills/memory/SKILL.md +0 -120
  574. package/src/web/skills/web-research/SKILL.md +0 -46
  575. package/src/web/skills.ts +0 -97
  576. package/src/web/src/adapters-panel.ts +0 -288
  577. package/src/web/src/assistant.ts +0 -451
  578. package/src/web/src/chat.ts +0 -1057
  579. package/src/web/src/composer.ts +0 -959
  580. package/src/web/src/dataset.ts +0 -132
  581. package/src/web/src/finetune.ts +0 -267
  582. package/src/web/src/hub.ts +0 -428
  583. package/src/web/src/main.ts +0 -98
  584. package/src/web/src/markdown.ts +0 -0
  585. package/src/web/src/memory-panel.ts +0 -572
  586. package/src/web/src/model-picker.ts +0 -159
  587. package/src/web/src/palette.ts +0 -346
  588. package/src/web/src/protocol.ts +0 -26
  589. package/src/web/src/quantize.ts +0 -167
  590. package/src/web/src/rag.ts +0 -274
  591. package/src/web/src/sessions.ts +0 -469
  592. package/src/web/src/shell.ts +0 -804
  593. package/src/web/src/ui-catalog.ts +0 -78
  594. package/src/web/vendor/README.md +0 -61
  595. package/src/web-assets.ts +0 -47
  596. package/src/web-tools.ts +0 -478
  597. package/src/weights.ts +0 -255
  598. /package/src/{serve → server}/completion-sink.ts +0 -0
  599. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  600. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  601. /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/`.