mlx-bun 0.0.13 → 0.3.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 (427) hide show
  1. package/README.md +71 -578
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/README.md +91 -0
  4. package/docs/{design → archive}/hlg-sampling.md +3 -3
  5. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  6. package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
  7. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  8. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  9. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  10. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  11. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  12. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  13. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  14. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  15. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  16. package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
  17. package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
  18. package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
  19. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  20. package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
  21. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  22. package/docs/design/batching.md +569 -0
  23. package/docs/design/decode-speed-program.md +3178 -169
  24. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  25. package/docs/design/generic-model-support.md +579 -334
  26. package/docs/design/kv-cache.md +680 -0
  27. package/docs/design/orpo-training.md +673 -1239
  28. package/docs/design/speculative-decoding.md +1627 -0
  29. package/docs/design/turboquant.md +2102 -0
  30. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  31. package/docs/design/web-chat-redesign.md +666 -1083
  32. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  33. package/docs/planning/release-notes-v0.3.0.md +35 -0
  34. package/docs/planning/video-series.md +421 -0
  35. package/docs/reference/benchmarks.md +1162 -0
  36. package/docs/reference/cli.md +395 -149
  37. package/docs/reference/distribution.md +321 -181
  38. package/docs/reference/environment.md +348 -0
  39. package/docs/reference/glossary.md +39 -0
  40. package/docs/reference/library-api.md +580 -142
  41. package/docs/reference/memory.md +227 -64
  42. package/docs/reference/models.md +137 -6
  43. package/docs/reference/server-api.md +938 -422
  44. package/docs/reference/server-config.md +652 -320
  45. package/docs/reference/training.md +393 -254
  46. package/docs/reference/troubleshooting.md +166 -0
  47. package/package.json +10 -5
  48. package/src/admission-plan.ts +18 -0
  49. package/src/anthropic.ts +92 -3
  50. package/src/audio/decode.ts +2 -2
  51. package/src/audio/features.ts +2 -2
  52. package/src/backends/mlx/autoregressive.ts +108 -0
  53. package/src/backends/mlx/batch-group.ts +1646 -0
  54. package/src/backends/mlx/diffusion.ts +39 -0
  55. package/src/backends/mlx/draft-target.ts +47 -0
  56. package/src/backends/mlx/evaluation.ts +105 -0
  57. package/src/backends/mlx/fill-append.ts +45 -0
  58. package/src/backends/mlx/gateway-binding.ts +71 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/memory-client.ts +189 -0
  61. package/src/backends/mlx/memory-guard.ts +40 -0
  62. package/src/backends/mlx/methods.ts +90 -0
  63. package/src/backends/mlx/model-prompt.ts +164 -0
  64. package/src/backends/mlx/model-serving.ts +79 -0
  65. package/src/backends/mlx/prefill.ts +33 -0
  66. package/src/backends/mlx/request-policy.ts +20 -0
  67. package/src/backends/mlx/rollback.ts +51 -0
  68. package/src/backends/mlx/serial-executor.ts +273 -0
  69. package/src/backends/mlx/speculative.ts +114 -0
  70. package/src/backends/mlx/state-views.ts +26 -0
  71. package/src/backends/mlx/text-engine.ts +73 -0
  72. package/src/cli.ts +189 -61
  73. package/src/client.ts +66 -0
  74. package/src/config.ts +21 -4
  75. package/src/contracts/completion.ts +16 -0
  76. package/src/contracts/execution.ts +49 -0
  77. package/src/contracts/generation.ts +103 -0
  78. package/src/contracts/host.ts +7 -0
  79. package/src/contracts/jobs.ts +66 -0
  80. package/src/contracts/pi-provider.ts +30 -0
  81. package/src/contracts/pi.ts +259 -0
  82. package/src/contracts/resources.ts +18 -0
  83. package/src/contracts/scheduling.ts +27 -0
  84. package/src/contracts/tsconfig.json +14 -0
  85. package/src/diffusion/diffusion-generate.ts +218 -153
  86. package/src/embed.ts +1 -1
  87. package/src/engine/admission.ts +59 -0
  88. package/src/engine/cancellation.ts +38 -0
  89. package/src/engine/engine.ts +56 -0
  90. package/src/engine/execution-coordinator.ts +70 -0
  91. package/src/engine/execution-plan.ts +51 -0
  92. package/src/engine/resources.ts +42 -0
  93. package/src/engine/scheduler.ts +67 -0
  94. package/src/engine/session.ts +247 -0
  95. package/src/eval/README.md +44 -1
  96. package/src/eval/kl.ts +263 -23
  97. package/src/eval/reference-logits.ts +207 -0
  98. package/src/eval/runner.ts +10 -79
  99. package/src/eval/tasks/bfcl.ts +2 -1
  100. package/src/eval/tasks/gsm8k.ts +2 -1
  101. package/src/eval/tasks/hashhop.ts +2 -1
  102. package/src/eval/tasks/humaneval.ts +5 -4
  103. package/src/eval/tasks/ifeval.ts +2 -1
  104. package/src/eval/tasks/mmlu.ts +2 -1
  105. package/src/expert-io.ts +3 -1
  106. package/src/expert-offload.ts +3 -1
  107. package/src/fill/echo-index.ts +188 -0
  108. package/src/fill/fill-session.ts +452 -0
  109. package/src/fill/proposal.ts +68 -0
  110. package/src/fill/schema-rows.ts +506 -0
  111. package/src/fill/tool-boundary.ts +103 -0
  112. package/src/fit.ts +24 -43
  113. package/src/generate.ts +740 -337
  114. package/src/grammar.ts +14 -8
  115. package/src/harness-pi.ts +15 -34
  116. package/src/hub-rest.ts +4 -4
  117. package/src/index.ts +27 -0
  118. package/src/inference/denoising.ts +17 -0
  119. package/src/inference/graph.ts +33 -0
  120. package/src/inference/prefill.ts +39 -0
  121. package/src/inference/rollback.ts +7 -0
  122. package/src/jobs/db.ts +4 -0
  123. package/src/jobs/index.ts +2 -0
  124. package/src/jobs/job-entry.ts +15 -14
  125. package/src/jobs/runner.ts +98 -13
  126. package/src/jobs/task-client.ts +19 -0
  127. package/src/jobs/types.ts +2 -58
  128. package/src/kv-scheme.ts +199 -0
  129. package/src/kv-store.ts +459 -234
  130. package/src/lab/README.md +18 -0
  131. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  132. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  133. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +7 -5
  134. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  135. package/src/library.ts +40 -0
  136. package/src/lora.ts +18 -0
  137. package/src/media-fetch.ts +20 -4
  138. package/src/memory/article.ts +1 -1
  139. package/src/memory/chunk.ts +1 -1
  140. package/src/memory/model.ts +32 -170
  141. package/src/memory/prompts.ts +1 -1
  142. package/src/memory/rest.ts +1 -1
  143. package/src/memory/schedule.ts +1 -1
  144. package/src/memory/stages.ts +4 -1
  145. package/src/memory/vault.ts +3 -4
  146. package/src/mlx/array.ts +9 -10
  147. package/src/mlx/compile.ts +12 -2
  148. package/src/mlx/ffi.ts +66 -22
  149. package/src/mlx/materialize.ts +23 -0
  150. package/src/mlx/metal-kernel.ts +10 -11
  151. package/src/mlx/ops.ts +64 -19
  152. package/src/mlx/quantized-rows.ts +36 -0
  153. package/src/mlx/token-bitmask.ts +52 -0
  154. package/src/mlx/turboquant-kv-decode.ts +82 -0
  155. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  156. package/src/mlx/turboquant-ops.ts +33 -3
  157. package/src/mlx/turboquant-tables.ts +2 -2
  158. package/src/model/batched-mask.ts +9 -3
  159. package/src/model/batched-quant.ts +2 -2
  160. package/src/model/batched-rotating-quant.ts +45 -99
  161. package/src/model/batched-rotating-state.ts +73 -0
  162. package/src/model/batched-rotating.ts +57 -106
  163. package/src/model/batched-row-storage.ts +127 -0
  164. package/src/model/compiled-decode.ts +4 -3
  165. package/src/model/diffusion-gemma.ts +8 -8
  166. package/src/model/factory.ts +87 -49
  167. package/src/model/fingerprint.ts +1 -1
  168. package/src/model/flash-attention.ts +3 -3
  169. package/src/model/gemma4-base.ts +187 -17
  170. package/src/model/gemma4.ts +6 -6
  171. package/src/model/generated/gemma4-12b.ts +52 -148
  172. package/src/model/generated/gemma4-26b.ts +34 -94
  173. package/src/model/generated/gemma4-e4b.ts +28 -76
  174. package/src/model/glm52-cache.ts +19 -9
  175. package/src/model/glm52-dsa.ts +13 -9
  176. package/src/model/glm52-memory.ts +28 -1
  177. package/src/model/implementation.ts +69 -0
  178. package/src/model/minicpm5.ts +7 -8
  179. package/src/model/profile.ts +443 -0
  180. package/src/model/qwen-append.ts +12 -0
  181. package/src/model/qwen-conv.ts +52 -0
  182. package/src/model/qwen3-delta.ts +98 -2
  183. package/src/model/qwen3-mrope.ts +252 -0
  184. package/src/model/qwen3_5-checkpoint.ts +164 -0
  185. package/src/model/qwen3_5.ts +336 -60
  186. package/src/model/support.ts +1 -1
  187. package/src/model/trellis-balanced-scatter.ts +51 -0
  188. package/src/model/trellis-linear.ts +672 -0
  189. package/src/model/trellis-shared-m.ts +151 -0
  190. package/src/model/trellis-shared-scatter.ts +144 -0
  191. package/src/model/trellis-splitk-prefill.ts +158 -0
  192. package/src/model/trellis-tiled-prefill.ts +136 -0
  193. package/src/model/trellis-vector-expand.ts +62 -0
  194. package/src/model/trellis-wide-prefill.ts +94 -0
  195. package/src/model/universal/dense.ts +1 -1
  196. package/src/model/universal/modules.ts +8 -1
  197. package/src/native/frame_extract.swift +140 -0
  198. package/src/native-pack.ts +15 -7
  199. package/src/pi-provider.ts +6 -64
  200. package/src/pi-web.ts +14 -320
  201. package/src/prompt-cache.ts +97 -54
  202. package/src/quantize/allocator.ts +1 -1
  203. package/src/quantize/atomic-output.ts +38 -0
  204. package/src/quantize/calibration.ts +2 -1
  205. package/src/quantize/config-writer.ts +16 -3
  206. package/src/quantize/index.ts +28 -1
  207. package/src/quantize/job.ts +11 -0
  208. package/src/quantize/quantizer.ts +265 -100
  209. package/src/quantize/rotate.ts +603 -0
  210. package/src/quantize/safetensors-writer.ts +79 -1
  211. package/src/quantize/sensitivity.ts +41 -59
  212. package/src/quantize/trellis.ts +586 -0
  213. package/src/quantize/weight-transform.ts +292 -0
  214. package/src/responses.ts +130 -3
  215. package/src/runtime-config.ts +81 -0
  216. package/src/sampler.ts +276 -4
  217. package/src/serve/admin-routes.ts +184 -0
  218. package/src/serve/aux-routes.ts +132 -0
  219. package/src/serve/batch-scheduler.ts +8 -1558
  220. package/src/serve/chat-request.ts +391 -0
  221. package/src/serve/chat-stage.ts +195 -0
  222. package/src/serve/checkpoint-identity.ts +64 -0
  223. package/src/serve/completion-executor.ts +354 -0
  224. package/src/serve/completion-sink.ts +157 -0
  225. package/src/serve/discovery-routes.ts +204 -0
  226. package/src/serve/generation-gateway.ts +211 -243
  227. package/src/serve/http.ts +159 -0
  228. package/src/serve/inference-request.ts +79 -0
  229. package/src/serve/isolate.ts +297 -105
  230. package/src/serve/kv-budget.ts +14 -0
  231. package/src/serve/lab-routes.ts +341 -0
  232. package/src/serve/lane-registry.ts +2 -1
  233. package/src/serve/model-admin-routes.ts +155 -0
  234. package/src/serve/model-binding.ts +43 -0
  235. package/src/serve/model-host.ts +512 -0
  236. package/src/serve/openai-wire.ts +103 -0
  237. package/src/serve/parent-application.ts +56 -0
  238. package/src/serve/pipeline.ts +33 -0
  239. package/src/serve/preparation.ts +33 -0
  240. package/src/serve/prompt-response-trace.ts +147 -0
  241. package/src/serve/request-plan.ts +162 -0
  242. package/src/serve/request-prep.ts +346 -0
  243. package/src/serve/response-lease.ts +24 -0
  244. package/src/serve/responses-client.ts +69 -0
  245. package/src/serve/session-completion-engine.ts +110 -0
  246. package/src/serve/session-search.ts +1 -1
  247. package/src/serve/static-routes.ts +101 -0
  248. package/src/serve/text-completion-stage.ts +104 -0
  249. package/src/serve/token-streams.ts +519 -0
  250. package/src/server.ts +523 -3551
  251. package/src/spec/assistant-source.ts +10 -44
  252. package/src/spec/deepspec-source.ts +6 -6
  253. package/src/spec/dflash-source.ts +9 -8
  254. package/src/spec/dspark/ab-stats.ts +3 -3
  255. package/src/spec/dspark/calibration.ts +2 -2
  256. package/src/spec/dspark/deepspec-module.ts +2 -2
  257. package/src/spec/dspark/generate-dflash.ts +2 -2
  258. package/src/spec/dspark/generate.ts +2 -2
  259. package/src/spec/dspark/loader.ts +1 -1
  260. package/src/spec/dspark/module-dflash.ts +5 -4
  261. package/src/spec/dspark/module.ts +3 -3
  262. package/src/spec/dspark/quantize-drafter.ts +1 -1
  263. package/src/spec/generate.ts +1 -1
  264. package/src/spec/glm52-mtp-source.ts +9 -1
  265. package/src/spec/ngram-source.ts +1 -1
  266. package/src/spec/prefix-state.ts +45 -0
  267. package/src/spec/qwen-mtp-source.ts +528 -0
  268. package/src/spec/serve-loop.ts +179 -213
  269. package/src/spec/source.ts +42 -10
  270. package/src/spec/two-model.ts +1 -1
  271. package/src/ssd-cache.ts +95 -24
  272. package/src/ssd-durability.ts +207 -0
  273. package/src/tool-call.ts +51 -14
  274. package/src/train/flash-cce.ts +14 -13
  275. package/src/train/forward.ts +38 -1
  276. package/src/train/loss.ts +3 -2
  277. package/src/train/prefix-shared.ts +5 -1
  278. package/src/train/segmented.ts +9 -6
  279. package/src/train/trainer.ts +9 -8
  280. package/src/vision/preprocess.ts +1 -1
  281. package/src/vision/prompt.ts +48 -2
  282. package/src/vision/qwen3vl-preprocess.ts +409 -0
  283. package/src/vision/qwen3vl-prompt.ts +196 -0
  284. package/src/vision/qwen3vl-tower.ts +555 -0
  285. package/src/vision/video-frames.ts +148 -0
  286. package/src/web/app.html +1 -1
  287. package/src/web/app.js +15 -6
  288. package/src/web/src/adapters-panel.ts +3 -3
  289. package/src/web/src/assistant.ts +2 -36
  290. package/src/web/src/chat.ts +16 -9
  291. package/src/web/src/composer.ts +4 -4
  292. package/src/web/src/hub.ts +1 -1
  293. package/src/web/src/markdown.ts +0 -0
  294. package/src/web/src/memory-panel.ts +1 -1
  295. package/src/web/src/model-picker.ts +1 -1
  296. package/src/web/src/palette.ts +1 -1
  297. package/src/web/src/protocol.ts +3 -39
  298. package/src/web/src/sessions.ts +1 -1
  299. package/src/web/src/ui-catalog.ts +4 -18
  300. package/src/web-assets.ts +47 -0
  301. package/src/weights.ts +134 -33
  302. package/docs/archive/mlx-bun-lab-report.html +0 -275
  303. package/docs/archive/wwdc-mlx-bun.html +0 -848
  304. package/docs/design/adapters-end-to-end.md +0 -205
  305. package/docs/design/batching-perf-path.md +0 -255
  306. package/docs/design/batching-v2-plan.md +0 -529
  307. package/docs/design/bucketing-stage.md +0 -197
  308. package/docs/design/compat-cli-surface-design.md +0 -230
  309. package/docs/design/docs-reorg-plan.md +0 -165
  310. package/docs/design/dspark-serving-program.md +0 -371
  311. package/docs/design/dspark-speculative-decoding.md +0 -268
  312. package/docs/design/grammar-spec-batching-integration.md +0 -301
  313. package/docs/design/memory-inference-path.md +0 -422
  314. package/docs/design/memory-synthesis.md +0 -287
  315. package/docs/design/memory-system.md +0 -237
  316. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  317. package/docs/design/omlx-adoption-map.md +0 -98
  318. package/docs/design/orpo-dynamic-lambda.md +0 -220
  319. package/docs/design/orpo-future-enhancements.md +0 -340
  320. package/docs/design/paged-kv-cache.md +0 -214
  321. package/docs/design/parallel-slots.md +0 -567
  322. package/docs/design/parity-tier-dag.md +0 -161
  323. package/docs/design/repo-cleanup-plan.md +0 -189
  324. package/docs/design/runtime-isolation.md +0 -263
  325. package/docs/design/segmented-backward-training.md +0 -543
  326. package/docs/design/spec-decode-larger-targets.md +0 -229
  327. package/docs/design/ssd-kv-cold-tier.md +0 -288
  328. package/docs/design/structured-output.md +0 -484
  329. package/docs/design/tauri-desktop-app.md +0 -127
  330. package/docs/design/the-dreaming-master-plan.md +0 -445
  331. package/docs/design/turboquant-kv.md +0 -235
  332. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  333. package/docs/design/web-chat-beat-matrix.md +0 -315
  334. package/docs/design/write-pipeline-entity.md +0 -468
  335. package/docs/investigations/curve-runs/bisector-route.json +0 -98
  336. package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
  337. package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
  338. package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
  339. package/docs/investigations/hlg-ab.html +0 -34
  340. package/docs/investigations/hlg-explorer.html +0 -238
  341. package/docs/investigations/hlg-figs/cliff.svg +0 -1
  342. package/docs/investigations/hlg-figs/cross.svg +0 -1
  343. package/docs/investigations/hlg-figs/edges.svg +0 -1
  344. package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
  345. package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
  346. package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
  347. package/docs/investigations/hlg-figs/terrain.svg +0 -1
  348. package/docs/investigations/hlg-report.html +0 -262
  349. package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
  350. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  351. package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
  352. package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
  353. package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
  354. package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
  355. package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
  356. package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
  357. package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
  358. package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
  359. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  360. package/docs/investigations/hlg-runs/hlg-map.json +0 -383
  361. package/docs/investigations/hlg-runs/run0-original.log +0 -159
  362. package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
  363. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  364. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  365. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  366. package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  367. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  368. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  369. package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  370. package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
  371. package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
  372. package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
  373. package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
  374. package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
  375. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  376. package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
  377. package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
  378. package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
  379. package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
  380. package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
  381. package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  382. package/docs/investigations/hlg-runs/runN-verify.log +0 -116
  383. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  384. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  385. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  386. package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  387. package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  388. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  389. package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
  390. package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  391. package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
  392. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  393. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  394. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  395. package/docs/planning/IDEAS.md +0 -69
  396. package/docs/reference/embedding.md +0 -197
  397. package/docs/reference/features-matrix.md +0 -100
  398. package/docs/reference/orpo-quickstart.md +0 -135
  399. package/src/flags.ts +0 -13
  400. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  401. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  402. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  403. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  404. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  405. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  406. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  407. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  408. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  409. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  410. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  411. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  412. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  413. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  414. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  415. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  416. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  417. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  418. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  419. /package/docs/{planning → archive/planning}/journal.md +0 -0
  420. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  421. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  422. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  423. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  424. /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
  425. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
  426. /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
  427. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -1,220 +0,0 @@
1
- # Dynamic λ controller for ORPO (adaptive preference pressure)
2
-
3
- **Status:** proposed (design only — not built). **Opened:** 2026-06-20.
4
- **Reaffirmed + reframed by Josh 2026-07-01** (see "The band framing" below).
5
- **Surface:** `src/train/loss.ts` (orpoMetrics/orpoLoss), `src/train/trainer.ts`
6
- (orpoLoop, the λ config), the segmented ORPO classes in `src/train/segmented.ts`.
7
-
8
- ## The band framing (Josh, 2026-07-01 — the product statement)
9
-
10
- > "Vary the lambda term based on the loss or another signal — maintain the
11
- > interest inside a band: learn preference HARDER while you are still able to
12
- > stay coherent. A self-stabilizing AND self-discovering hyperparameter, which
13
- > optimizes the training to get as much preference as possible while not
14
- > resulting in degeneracy."
15
-
16
- That is the controller's contract in one sentence: **λ is not a hyperparameter
17
- to tune, it is a process variable to control** — push preference pressure to
18
- the maximum the model can currently bear (self-discovering), retreat on
19
- confirmed coherence damage (self-stabilizing), and hold the system inside the
20
- healthy band rather than at a fixed point. Everything below (phases, AIMD/PID,
21
- displacement decomposition) is the mechanism for that contract.
22
-
23
- **Interplay with `sft_scope` (landed 2026-07-01):** the default chosen-NLL is
24
- now full prompt+response. The controller's displacement signal must key on the
25
- **response-only `lw`** (which `orpoMetrics` computes for the odds ratio in both
26
- modes) — full-scope NLL mixes prompt modeling into the level and would mask a
27
- crushed `logp(chosen-response)`. Instrument both, control on response-only.
28
-
29
- ## Paper lens (2026-07-01): is this citable?
30
-
31
- Closest prior: **β-DPO (arXiv 2407.08639)** — dynamic β for DPO, calibrated
32
- per-batch from the reward gap. Ours differs on three axes worth claiming:
33
- (1) **closed-loop trajectory control** (a controller tracking a non-stationary
34
- optimum across the run) vs per-batch heuristic calibration; (2) the control
35
- signal is **displacement-aware** — the (dLw, dLr) decomposition targets the
36
- known ORPO/DPO failure mode directly (cf. likelihood displacement,
37
- arXiv 2410.08847 "Unintentional Unalignment"); (3) **objective-agnostic**
38
- actuator (ORPO λ, SimPO β/γ). The incremental P→PD→PID build IS the ablation
39
- section. The claim to stake: *"the optimal preference pressure is
40
- non-stationary, so a controller strictly dominates any fixed λ a sweep can
41
- find — and a displacement-aware control law finds the coherence cliff without
42
- a priori knowledge of it."* Referees will demand: ≥2 datasets, ≥2 model sizes,
43
- the static-λ sweep as the baseline family (the controller must beat the BEST
44
- constant, not λ=0.1), seeds, and the positive-control sanity run this doc
45
- already specifies. Workshop/arXiv-preprint tier as a single-model study;
46
- main-track needs the multi-model sweep.
47
-
48
- ## Motivation
49
-
50
- ORPO's loss is `L = NLL(chosen) + λ·OR`. The two terms learn on different
51
- timescales, and a **static λ** can't serve both:
52
-
53
- - **The SFT/NLL half learns easily** — the model quickly raises `logp(chosen)`
54
- (fits the response *style*). Observed live: val loss falls steadily while
55
- preference accuracy/margin barely move.
56
- - **The preference/OR half is hard** — on subtle data (UltraFeedback on a 1B) the
57
- model doesn't generalize "which of two plausible answers is better."
58
- - **Over-weighting OR backfires** via **likelihood displacement**: the model widens
59
- the *relative* margin by **crushing `logp(rejected)`** (and sometimes dropping
60
- `logp(chosen)` too) rather than genuinely preferring chosen — degrading fluency.
61
-
62
- A fixed λ either under-pressures the preference (λ too low → never leans on OR) or
63
- over-pressures it (λ too high → displacement/degeneration). The hypothesis: an
64
- **adaptive, displacement-aware λ** — low while SFT learns, ramped once SFT
65
- saturates, backed off the moment displacement appears — can find a generalization
66
- regime a static λ sails past. (Static λ tuning was the blunt version we rejected;
67
- this is the targeted version.)
68
-
69
- ## Control signals (what we measure)
70
-
71
- `orpoMetrics` (`loss.ts:1249`) already computes everything; we just don't surface it:
72
-
73
- - **`lw`** = mean `logp(chosen)` (currently folded into `nll = mean(−lw)`).
74
- - **`lr`** = mean `logp(rejected)` (currently only used inside the odds ratio).
75
- - **`margin`** = mean log-odds (chosen vs rejected) — already emitted.
76
- - **`accuracy`** = fraction `lw > lr` — already emitted.
77
-
78
- The controller keys on the **decomposition of margin growth**:
79
- - margin ↑ because **`lw` ↑** → *healthy* (genuinely preferring chosen).
80
- - margin ↑ because **`lr` ↓** while `lw` flat/falling → *displacement* (the cheat).
81
-
82
- So the load-bearing new instrumentation is **surfacing `lw` and `lr` separately**
83
- per eval (not just `nll`/`margin`).
84
-
85
- ## The controller (state machine)
86
-
87
- Runs at **each val eval** (every `stepsPerEval` steps) — val (`lw`, `lr`) is the
88
- stable signal; per-step B=1 train metrics are too noisy to control on. Maintains a
89
- short window of recent val (`lw`, `lr`, margin, accuracy).
90
-
91
- ```
92
- state: { phase, lambda, window[] } # lambda starts at LAMBDA_MIN
93
- on each val eval (lw, lr, margin, acc):
94
- dLw = lw - lw_prev ; dLr = lr - lr_prev
95
-
96
- PHASE 1 — SFT warmup (lambda = LAMBDA_MIN, low):
97
- stay while the NLL is still improving (dLw > EPS_SFT, i.e. chosen logp rising).
98
- when dLw plateaus (|dLw| < EPS_SFT over K evals) → SFT saturated → PHASE 2.
99
-
100
- PHASE 2 — preference ramp:
101
- each eval, lambda += RAMP (cap LAMBDA_MAX), applying OR pressure —
102
- AS LONG AS chosen holds: dLw >= -EPS_DISP.
103
- if accuracy/margin respond (margin widening with dLw >= 0) → keep ramping.
104
- if displacement detected (dLw < -EPS_DISP, i.e. chosen logp dropping, or
105
- dLr << 0 with dLw <= 0) → PHASE 3.
106
-
107
- PHASE 3 — displacement backoff:
108
- lambda -= BACKOFF (floor LAMBDA_MIN) until chosen recovers (dLw >= 0),
109
- then hold (hysteresis) before considering another ramp.
110
- ```
111
-
112
- Knobs (all tunable): `LAMBDA_MIN≈0.05`, `LAMBDA_MAX≈0.5`, `RAMP/BACKOFF≈0.05`,
113
- window `K≈3` evals, `EPS_SFT`/`EPS_DISP` set from the observed val-`lw` noise scale.
114
- Hysteresis (require K consecutive evals before a phase flip) prevents oscillation.
115
-
116
- **Degeneration guard (hard stop):** if `lw` falls below its phase-1 starting value
117
- by more than a threshold, freeze λ at `LAMBDA_MIN` and log — the model is paying for
118
- margin with chosen's likelihood, which a backoff alone isn't recovering.
119
-
120
- ## Control-theory framing (PID / AIMD) — added 2026-06-23
121
-
122
- The phase state-machine above is one instance of a more general and more honest
123
- frame: **this is closed-loop control.** The **process variable** is the SFT signal
124
- (NLL / `lw`), the **actuator** is the preference weight (ORPO's λ, *or* SimPO's
125
- β/γ — the controller is objective-agnostic), and the **control law** is what maps the
126
- measured degradation back to the actuator. Two laws worth naming:
127
-
128
- - **AIMD** (additive-increase / multiplicative-decrease — TCP congestion control):
129
- ramp the preference up *gently* while the SFT is healthy, **slam it down hard** the
130
- moment degradation ("congestion") appears. Famously robust and forgiving of its own
131
- knobs. The PHASE-2 ramp / PHASE-3 backoff is essentially AIMD.
132
- - **PID** decomposes the response into three terms, each fixing a failure the others
133
- can't:
134
- - **P (proportional · the level):** how degraded is `lw` *right now* → back off in proportion. (Leaves a steady-state offset.)
135
- - **D (derivative · the slope):** is degradation *accelerating* → a **leading indicator** that catches the helping→hurting **inflection before the level rises**, so you pull back *earlier* and leave less damage to heal. **Gotcha:** differentiating a noisy signal amplifies noise → use a *robust* slope (slope of the EMA, or a windowed regression), never raw step deltas. The slope is where most of the value is.
136
- - **I (integral · accumulated error):** the "missing letter." P leaves a chronic offset and D ignores it (flat slope at steady-state) → the model can settle hovering with a *small persistent* NLL elevation = quiet permanent SFT damage. I accumulates that residual and drives `lw` **back to its floor**. **Gotcha — integral windup:** if the actuator is pinned (λ at floor) while error persists, the integral piles up and **overshoots** on recovery → needs **anti-windup** (clamp/stop accumulating when saturated).
137
-
138
- **Build it incrementally — P → PD → PID** — each term earning its place against an
139
- *observed* failure of the simpler controller (P oscillates/offsets → add D; chronic
140
- offset remains → add I with anti-windup). That's both good control practice and an
141
- **ablation that writes the experimental section**, and it stops us from tuning three
142
- gains blind.
143
-
144
- **Why a controller beats a sweep (the real argument):** the optimal preference
145
- pressure is **non-stationary** — fragile early (push less), robust late (push more) —
146
- so **no fixed λ is optimal.** A sweep finds the best *constant*; the controller tracks
147
- the moving target and finds the best *trajectory*, which strictly dominates. The pitch
148
- isn't just "one run instead of six," it's "**beats any fixed value** a sweep could
149
- find." (The displacement backoff is also why this is *more* than auto-tuning — it
150
- targets the degradation signal directly.)
151
-
152
- **Degenerate failure to guard (beyond the §degeneration hard-stop):** if the law backs
153
- off too eagerly it **collapses the preference term to ~0 → you've trained pure SFT.**
154
- The AIMD/PID balance must **favor pushing** (aggressive-ish increase, retreat only on
155
- *confirmed* degradation), and target a small **safety margin below the cliff** rather
156
- than the absolute max-bearable (the edge is fragile).
157
-
158
- **Synergy with the diffusion frontier:** all of this is noise-robustness machinery for
159
- controlling off a noisy process variable — so it transfers *especially* well to a
160
- diffusion LM, where the likelihood is a stochastic ELBO (even noisier). See
161
- [orpo-base-uf-experiment-and-directions.md](../investigations/orpo-base-uf-experiment-and-directions.md) §6.7.
162
-
163
- ## Wiring
164
-
165
- 1. **Instrument (small):** add `lw`, `lr` to `orpoMetrics`'s return + the trainer's
166
- `metric` emit (train and val). `pref-control.ts` / `metrics.json` log them so the
167
- controller (and our plots) can see the decomposition.
168
- 2. **Make λ dynamic:** today `cfg.orpoLambda` is a constant threaded into
169
- `orpoLoss(model, batch, cfg.orpoLambda, …)` and **baked into the segmented ORPO
170
- classes' constructors** (`new SegmentedBackwardOrpo(model, lora, ranges,
171
- cfg.orpoLambda, …)`). Change to a **mutable λ source**:
172
- - simplest: a `{ value: number }` box (or a getter) passed instead of the scalar;
173
- the loss / segmented step reads `lambdaRef.value` each step.
174
- - the segmented classes expose a `setLambda(x)` (or read the ref) so the
175
- constructor-baked constant becomes a live field.
176
- 3. **The controller** lives in `orpoLoop` (`trainer.ts`): after each val eval, feed
177
- the val (`lw`, `lr`, margin, acc) to `updateLambda(state)` and write the new λ
178
- into the ref. Log every λ change (`· λ 0.10 → 0.15 (SFT saturated, ramping)`).
179
- 4. **Off by default:** gate behind a config flag (`orpoLambdaSchedule: "static" |
180
- "adaptive"`); static = today's behavior, exact.
181
-
182
- ## Experiment design (how we validate it)
183
-
184
- Head-to-head, **same data / seed / everything else**, on the from-base UF setup:
185
- - **Arm A:** static λ=0.1 (the current run — the baseline).
186
- - **Arm B:** adaptive λ (this controller).
187
-
188
- Compare:
189
- - **val accuracy + margin** trajectories (does B clear the noise floor / widen
190
- margin where A stalls?);
191
- - **`lw` trajectory** (does B avoid displacement — `lw` non-decreasing — where a
192
- high static λ would crater it?);
193
- - the **downstream** numbers: Exp-1 capability x2 and Exp-2 win-rate (does B's model
194
- actually win more head-to-head?).
195
-
196
- Success = B generalizes measurably better (val acc clears ~0.55 with margin lifting,
197
- and/or Exp-2 win-rate > A's) **without** degeneration (`lw` holds). Null result is
198
- also informative: "even adaptive λ can't beat the 1B capacity ceiling on UF."
199
-
200
- ## Open questions / risks
201
-
202
- - **Cadence:** controlling on val every `stepsPerEval` may be too coarse (few control
203
- steps per run). Option: a cheaper, more frequent "control eval" on a small fixed
204
- probe set distinct from the reported val set.
205
- - **Confounds the replication claim:** an adaptive-λ result is no longer "standard
206
- ORPO" — it's our method. Keep Arm A (static 0.1) as the honest paper-replication;
207
- Arm B is the "can we do better" research arm. Report both.
208
- - **Tuning the controller's own knobs** risks the same trap (we'd be tuning a tuner).
209
- Mitigate: set EPS thresholds from observed noise scale, not by hand-fitting to one
210
- run; validate the controller on the *positive control* (prefer-uppercase) first —
211
- it should ramp λ early (SFT saturates fast) and never trip displacement.
212
-
213
- ## References
214
-
215
- - `src/train/loss.ts:1249` `orpoMetrics` (computes `lw`,`lr`,`nll`,`or`,`accuracy`,`margin`).
216
- - `src/train/trainer.ts` `orpoLoop` (λ config, val emit), `DEFAULT_TRAIN_CONFIG.orpoLambda`.
217
- - `src/train/segmented.ts` segmented ORPO classes (λ baked in constructor).
218
- - `scripts/experiments/pref-control.ts` (trajectory logging — extend with `lw`,`lr`).
219
- - Context: [[opssdpa-dk-vjp-bug]] (validate grads vs autograd), the dynamic-λ
220
- discussion + the live from-base UF val trajectory (2026-06-20).
@@ -1,340 +0,0 @@
1
- # ORPO future enhancements
2
-
3
- Performance-first follow-on ideas for mlx-bun's ORPO trainer, plus a short
4
- appendix of objective variants that may be useful once the runtime path is
5
- comfortable.
6
-
7
- > **LANDED since this was written (2026-06-19):** the top items here are now in
8
- > production — **Priority 1 (Cut Cross Entropy / fused linear CE)** and **§1
9
- > (vocab-axis chunked CE)** shipped as the `[M,vocab]`-free **flash-CCE Metal head**
10
- > (verbatim MLX steel GEMM, fwd+bwd; `orpo_flash_ce`), and **§3 (prefix sharing in the
11
- > trainer)** shipped as `orpo_prefix_shared`, composed with the flash head AND the
12
- > segmented backward for MiniCPM5 **and** e4b. See [training.md](../reference/training.md),
13
- > [orpo-quickstart.md](../reference/orpo-quickstart.md), and the live record
14
- > [steel-flash-cce-handoff.md](../investigations/steel-flash-cce-handoff.md). The
15
- > remaining sections (§2, §4–§9, the objective variants) are still genuinely future.
16
-
17
- ## Deferred levers — queued, do NOT start mid-run (2026-06-19)
18
-
19
- Two optimizations identified during the MiniCPM5 UltraFeedback ORPO run. **Both are
20
- deferred until the active run finishes** — the GPU is fully occupied, so these get
21
- *measured* later, not "quickly tested" now (a quick probe steals the training GPU).
22
-
23
- 1. **Expose `--grad-accum` (gradient accumulation).** The machinery already exists —
24
- `accumulateStep` (`src/train/trainer.ts:975`) loops `gradAccumSteps` B=1 micro-steps,
25
- **averages** their grads (`scale = 1/accumSteps`, line 989), and evals+frees each
26
- micro-graph so **peak memory stays flat** (line 1015). It's wired into the ORPO loop
27
- (`trainer.ts:837`) but `gradAccumSteps` defaults to 1 and has **no CLI flag**.
28
- - **Why it helps us:** ORPO here is hard **B=1** (segmented + prefix-share assume a
29
- single stream — `segmented.ts:636` throws on B>1), so every optimizer update is
30
- computed from exactly **one** preference pair — the noisiest possible gradient (the
31
- per-step loss swinging 0.4↔3.3 is that noise). Grad-accum gives an effective batch of
32
- G — averaging G pairs/update, variance ↓ ~1/G, steadier descent, tolerates a higher
33
- LR — at **B=1 memory**. It's the *only* way to a bigger effective batch when B>1 is
34
- impossible.
35
- - **What it is NOT:** not a speedup (G× passes per optimizer update → same total
36
- passes, same wall-clock, same data seen); and not a guaranteed win (the prior run
37
- converged fine at B=1, val 1.66→1.50). It's a **try-and-measure** lever — expose the
38
- flag, A/B G=4/8 judged on **val-margin**, don't assume.
39
- - **Note on prefix-share:** the existing `orpo_prefix_shared` is *already* a hand-built
40
- batch-of-2-with-shared-prefix (1F + 1B over `[P; Rc; Rr]`, prompt deduped in **both**
41
- directions — strictly cheaper than a generic 2-batch's `2F + 2B`). Generic N-way
42
- batching would re-duplicate per-pair prefixes and need a block-diagonal mask layer
43
- *on top of* the prefix-share mask — high friction, and the payoff is dominated by
44
- grad-accum (effective batch, free) + W4A16 (the real GPU bottleneck). Park batching
45
- unless a profile ever shows real short-row GPU idle.
46
-
47
- 2. **W4A16 compute-precision spike.** The head GEMM dequants 4-bit weights into **fp32**
48
- and matmuls in fp32 (W4A32); dropping the operands to `half` (fp32 accumulate) attacks
49
- the actual bottleneck. Confirmed **compute-bound live** (GPU 100% / BW ~⅓ of 400 on
50
- M1 Max). Full plan + parity gating: [w4a16-compute-precision-spike.md](w4a16-compute-precision-spike.md).
51
-
52
- **Context — seg vs no-segment A/B (MiniCPM5, seq 4096, same-machine M1 Max):**
53
- `--no-segment` measured **~1.25 s/step steady @ peak 9.26 GB** vs `--seg 2`
54
- **~1.58 s/step @ 2.62 GB** — ~20% faster for 3.5× peak memory, **bit-identical output**
55
- (segmenting is just gradient checkpointing). Dropping the segs removed the recompute (real
56
- GPU work) → GPU busy but no longer pinned. Fine when memory is spare; the current 3-epoch
57
- run uses `--no-segment`.
58
-
59
- Product relevance: ORPO is not only a researcher feature. It is the path to
60
- small, downloadable task adapters that make the default local assistant better
61
- without forcing every user onto a larger base model. The performance work below
62
- matters when it turns adapter training from a fragile lab run into a repeatable
63
- part of the mlx-bun product loop.
64
-
65
- ## Priority 1: Cut Cross Entropy / Liger Fused Linear CE
66
-
67
- The top ORPO performance priority is a Cut Cross Entropy / Liger-style Fused
68
- Linear Cross Entropy path for the LM head. This should be treated as the next
69
- major kernel project before lower-impact objective variants.
70
-
71
- Why it is first:
72
-
73
- - Gemma's vocab is huge, so materializing `[tokens, vocab]` logits dominates
74
- peak memory in ORPO.
75
- - ORPO needs chosen and rejected response log-probs, so the head cost is paid
76
- twice unless the trainer can share or fuse more work.
77
- - Token-axis chunking already helps fit the run, but vocab-axis chunking cuts
78
- the largest remaining logits allocation at the source.
79
- - The same fused engine can support SFT, DPO, ORPO, SimPO, IPO, and hinge-style
80
- preference losses once it exposes mean log-prob with a custom upstream
81
- cotangent.
82
- - A Metal fused version can avoid extra MLX dispatches and intermediate memory
83
- traffic in the hot training path.
84
-
85
- The implementation target is not just "chunk logits"; it is a fused log-prob
86
- head that computes the linear projection, online vocab logsumexp, target-logit
87
- gather, loss reduction, and backward replay without ever storing full logits.
88
-
89
- ## Performance focus
90
-
91
- ### 1. Vocab-axis chunked cross entropy
92
-
93
- The current ORPO head optimization is **token-axis chunking**: split the response
94
- positions into chunks, run the LM head on `[chunk, vocab]`, and rematerialize
95
- those chunks in the backward. That bounds peak logits by `chunk_tokens * vocab`.
96
-
97
- The complementary trick is **vocab-axis chunking**: split the vocabulary instead,
98
- run the head on `[tokens, vocab_chunk]`, and combine chunks with an
99
- online-logsumexp recurrence. This is the idea behind **Cut Cross Entropy** and
100
- Liger-style **Fused Linear Cross Entropy**. This is priority 1 for the ORPO
101
- performance roadmap.
102
-
103
- Exact forward sketch:
104
-
105
- 1. For each vocab chunk, compute partial logits `h @ W_chunk`.
106
- 2. Gather the target logit only if the target id falls inside this chunk.
107
- 3. Maintain per-token online `logsumexp` across vocab chunks.
108
- 4. At the end, `logp = target_logit - lse`.
109
-
110
- Backward reconstructs the softmax chunk-by-chunk from the saved per-token LSE
111
- and never materializes full `[tokens, vocab]` logits. For Gemma's 262k vocab,
112
- this is likely the next real moat after segmented backward.
113
-
114
- Two implementation levels:
115
-
116
- - Pure MLX chunked VJP first: exact, easier to validate, probably enough to fit
117
- longer contexts.
118
- - Metal fused kernel later: fuse quantized head matmul + online LSE + target
119
- gather + backward. This is the high-performance version.
120
-
121
- ### 2. Fold ORPO gradients into the fused CE path
122
-
123
- ORPO needs two mean log-probs, `lw` and `lr`, then applies the odds-ratio loss.
124
- For a fused CE/log-prob kernel, the clean design is to feed the upstream scalar
125
- gradient for each branch back into the chunked head backward. In other words,
126
- the fused head should expose "mean logp with custom upstream cotangent", not
127
- only cross-entropy.
128
-
129
- This avoids materializing logits while still letting ORPO, SimPO, and future
130
- preference losses share the same head engine.
131
-
132
- ### 3. Prefix sharing in the trainer
133
-
134
- `src/train/prefix-shared.ts` already proves the MiniCPM5 shared-prefix idea:
135
- one forward over `[prompt; chosen; rejected]` with block-sparse masking and
136
- block-wise RoPE. It is not yet wired into `orpoLoop`.
137
-
138
- Next useful path:
139
-
140
- - Add an opt-in flag, for example `orpo_prefix_sharing`.
141
- - Gate it by prompt/response ratio so short-prompt data stays on the simple
142
- two-forward path.
143
- - Wire MiniCPM5 first.
144
- - Port e4b later, where sliding-window masks, donor-KV reuse, and per-layer
145
- input make it trickier.
146
-
147
- ### 4. Sequence-chunk the MLP sub-block
148
-
149
- The MLP is per-token independent. Inside segmented backward, run MLP blocks over
150
- sequence chunks so the GeGLU/SwiGLU intermediate is `chunk_tokens * d_ff` instead
151
- of `seq_len * d_ff`.
152
-
153
- This stacks with depth segmentation and vocab-axis CE:
154
-
155
- `layer segment -> layer -> MLP seq chunk -> vocab chunk`
156
-
157
- Attention cannot be chunked this naively, but the MLP can.
158
-
159
- ### 5. Rotor / budget-aware segment planner
160
-
161
- Replace fixed `segment_size` with a planner that chooses segment boundaries from
162
- estimated activation bytes and a target memory budget. The planner should isolate
163
- expensive full-attention layers and larger MLP regions instead of treating every
164
- layer equally.
165
-
166
- This is the practical version of rotor/checkpoint scheduling for this codebase:
167
- less hand tuning, better fit at 8K+.
168
-
169
- ### 6. Faster flash-attention backward
170
-
171
- The custom flash backward is now correctness-useful but too slow. A serious
172
- performance version should mimic MLX's tuned SDPA style: simdgroup tiling,
173
- larger threadgroups, and FA2-style backward that stores only per-row LSE and
174
- recomputes attention probabilities.
175
-
176
- This matters most when `ops.sdpa` backward's O(L^2) memory is the blocker.
177
-
178
- ### 7. Fused LoRA epilogue
179
-
180
- Today LoRA adds multiple ops after the quantized base matmul:
181
-
182
- `base(x) + scale * ((x @ A) @ B)`
183
-
184
- A fused dequant-GEMM + LoRA epilogue would reduce dispatches and intermediate
185
- traffic. Backward can recompute the cheap rank projection:
186
-
187
- - `h = x @ A`
188
- - `dB = scale * h.T @ dy`
189
- - `dA = scale * x.T @ (dy @ B.T)`
190
- - `dx = dy @ W.T + scale * (dy @ B.T) @ A.T`
191
-
192
- This is more kernel work, but it benefits SFT, DPO, ORPO, and serving with
193
- adapters.
194
-
195
- ### 8. Shape-specialized compile for training graphs
196
-
197
- Shapeless compile is mainly required when a dimension changes every replay, such
198
- as autoregressive decode with a growing KV cache:
199
-
200
- `active_kv_len = prompt_len + generated_tokens`
201
-
202
- ORPO training is different. Each dataset row already has concrete lengths:
203
-
204
- - `P`: prompt length
205
- - `Rc`: chosen response length
206
- - `Rr`: rejected response length
207
- - `T`: prefix-shared length, `P + Rc + Rr`
208
-
209
- That means ORPO does not need shapeless compile in the same way decode does. A
210
- training graph can be compiled for an exact `(P, Rc, Rr)` shape, or for a padded
211
- length bucket, and cached under a key that includes the model, flags, segment
212
- plan, token/vocab chunk sizes, and objective mode.
213
-
214
- This is especially relevant for e4b prefix-sharing: the proof path should stay
215
- eager first, but a later L3 speed tier can use shape-specialized compiled graphs
216
- for known training shapes instead of requiring every primitive to support
217
- shapeless output-shape inference.
218
-
219
- The deeper L3 framing is **model-checksum-specific specialization**. For a fixed
220
- model snapshot, we know the architecture, layer count, hidden sizes, quantization
221
- layout, RoPE geometry, vocab, layer types, KV-sharing pattern, and supported
222
- context budget in advance. It is acceptable to spend much more work up front if
223
- that buys a faster hot path later.
224
-
225
- Practical version:
226
-
227
- - Treat the model snapshot checksum as part of the compilation key.
228
- - Generate/compile/cache a table of exact graph variants for the shapes and
229
- modes we care about.
230
- - Bucket or pad examples into those graph variants when helpful.
231
- - Fall back to eager only for shapes/modes not in the table.
232
-
233
- This is effectively a lookup table of specialized execution plans: more compile
234
- and analysis cost up front, less branching, shape handling, and dispatch overhead
235
- at runtime.
236
-
237
- ### 9. Fixed-shape decode cache as a compile route
238
-
239
- Decode is harder because the active KV length grows by one token per step. The
240
- current shapeless-compile path exists to avoid recompiling for:
241
-
242
- `L = 17, 18, 19, 20, ...`
243
-
244
- A separate route is to make decode shapes fixed:
245
-
246
- 1. Preallocate K/V buffers to a known maximum context length.
247
- 2. Write the new token into a dynamic position.
248
- 3. Carry `active_length` / `write_position` as scalar array inputs.
249
- 4. Mask out inactive cache positions, or preferably use an
250
- active-length-aware attention kernel that simply does not scan them.
251
-
252
- The correctness version is "fixed cache + mask". The performance version is
253
- "fixed cache + active-length-aware attention"; otherwise early decode steps may
254
- pay for the full max context even when only a short prefix is active.
255
-
256
- There is also a more aggressive specialization route: prebuild a graph table for
257
- specific active lengths or length buckets. Even if that means many compiled graph
258
- variants, the trade can be valid for a fixed model/checksum if compile cost is
259
- paid once and reused. In that regime, the important design question is not "can
260
- one shapeless graph infer every shape?" but "which finite set of exact shapes and
261
- runtime states should we specialize for?"
262
-
263
- If every op in a chosen route is compile-safe, the whole decode graph can become
264
- a stable compiled graph. Custom Metal kernels still need to stay outside compiled
265
- regions unless the route is intentionally eager/custom-kernel-only or MLX compile
266
- can accept the primitive's output contract.
267
-
268
- ## Quality and robustness ideas
269
-
270
- These are lower priority if the current goal is performance, but they are useful
271
- once ORPO can run comfortably.
272
-
273
- ### SimPO
274
-
275
- SimPO is a reference-free preference loss that uses the **average log-probability
276
- of each response** as the reward and adds a target reward margin. It reuses the
277
- same `lw` / `lr` values ORPO already computes.
278
-
279
- Rough shape:
280
-
281
- `loss = softplus(-beta * ((lw - lr) - gamma))`
282
-
283
- where `gamma` is the desired chosen-vs-rejected margin. It should be cheap to add
284
- as a sibling objective because the expensive part, response mean log-probs, is
285
- already implemented.
286
-
287
- ### IPO
288
-
289
- IPO means **Identity Preference Optimization**. It was introduced as a more
290
- overfit-resistant alternative to the original DPO logistic objective. Instead of
291
- driving preference probabilities toward hard 0/1 labels indefinitely, IPO uses a
292
- smoother identity-style objective over preference gaps. The practical point:
293
- when preference labels are noisy, IPO is often less eager to push margins to
294
- extremes.
295
-
296
- For mlx-bun, IPO is interesting mainly as a loss-family option after the shared
297
- logp engine exists.
298
-
299
- ### Hinge / SLiC-style loss
300
-
301
- The hinge loss is a margin-ranking objective:
302
-
303
- `loss = max(0, margin - reward_gap)`
304
-
305
- For preference training, `reward_gap` is usually the chosen score minus rejected
306
- score. Once the gap exceeds the margin, that pair contributes no gradient. This
307
- can be useful when you want to stop over-optimizing already-solved pairs and
308
- focus compute on pairs whose preference margin is still too small.
309
-
310
- ### Label smoothing and robust weighting
311
-
312
- Preference pairs are noisy. Cheap robustness knobs:
313
-
314
- - `label_smoothing`: treat a pair as, for example, 95% chosen / 5% rejected
315
- rather than a hard label.
316
- - Per-example `weight`: let curated data carry confidence or score gap.
317
- - Loss clipping / winsorization: cap extreme high-loss pairs so noisy examples
318
- do not dominate gradients.
319
- - Ambiguity filtering: skip pairs with tiny initial margin or near-identical
320
- chosen/rejected responses.
321
-
322
- ### Better curation
323
-
324
- The current UltraFeedback curation mostly extracts rows and filters by length.
325
- Quality can likely improve by filtering for:
326
-
327
- - Large enough chosen-vs-rejected score gap when available.
328
- - Similar response length, or at least bounded length ratio.
329
- - High semantic contrast but not unrelated answers.
330
- - Instruction-following categories if IFEval is the target metric.
331
-
332
- Data quality may beat objective cleverness on small ORPO runs.
333
-
334
- ## Short answer to "token vs vocab"
335
-
336
- The thing to look for is **vocab-axis chunked / fused cross entropy**, especially
337
- under the names **Cut Cross Entropy** or **Liger Fused Linear Cross Entropy**.
338
- Token chunking slices `[tokens, vocab]` by rows. Vocab chunking slices it by
339
- columns and combines chunks with online logsumexp so the full logits matrix is
340
- never live.