mlx-bun 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (640) hide show
  1. package/README.md +1449 -109
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +7 -5
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +10 -10
  78. package/src/{serve → server}/chat-stage.ts +14 -14
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +33 -41
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +25 -82
  85. package/src/{serve → server}/http.ts +25 -13
  86. package/src/{serve → server}/inference-request.ts +5 -5
  87. package/src/server/job-routes.ts +20 -0
  88. package/src/server/media-prompt.ts +63 -0
  89. package/src/server/memory-completion-client.ts +46 -0
  90. package/src/server/model-routes.ts +192 -0
  91. package/src/{serve → server}/openai-wire.ts +3 -2
  92. package/src/{serve → server}/pipeline.ts +1 -1
  93. package/src/server/prompt-builder.ts +13 -0
  94. package/src/server/prompt-contracts.ts +17 -0
  95. package/src/server/proxy-routes.ts +187 -0
  96. package/src/server/publishing-routes.ts +41 -0
  97. package/src/{serve → server}/request-plan.ts +6 -8
  98. package/src/{serve → server}/request-prep.ts +47 -50
  99. package/src/{serve → server}/responses-client.ts +30 -13
  100. package/src/{responses.ts → server/responses.ts} +12 -93
  101. package/src/server/routes.ts +205 -0
  102. package/src/server/start.ts +72 -0
  103. package/src/server/status-routes.ts +108 -0
  104. package/src/{serve → server}/text-completion-stage.ts +25 -4
  105. package/src/server/text-prompt.ts +12 -0
  106. package/src/{serve → server}/token-streams.ts +36 -38
  107. package/src/server/worker-routes.ts +311 -0
  108. package/src/storage/paths.ts +61 -0
  109. package/src/web/assets.ts +37 -0
  110. package/src/web/{src → browser}/api.ts +5 -23
  111. package/src/web/browser/chat-host.ts +17 -0
  112. package/src/web/browser/installed-panels.ts +6 -0
  113. package/src/web/browser/main.ts +55 -0
  114. package/src/web/browser/markdown.ts +279 -0
  115. package/src/web/browser/memory-panel.ts +9 -0
  116. package/src/web/browser/palette.ts +64 -0
  117. package/src/web/browser/protocol.ts +13 -0
  118. package/src/web/browser/shell.ts +467 -0
  119. package/src/web/{src → browser}/status.ts +32 -36
  120. package/src/web/build.ts +51 -0
  121. package/src/web/public/app.html +1054 -0
  122. package/src/web/{sw.js → public/sw.js} +4 -3
  123. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  124. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  125. package/THIRD_PARTY_LICENSES.md +0 -51
  126. package/docs/README.md +0 -107
  127. package/docs/archive/hlg-sampling.md +0 -350
  128. package/docs/archive/investigations/audio-input-plan.md +0 -356
  129. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
  174. package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
  175. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  176. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  177. package/docs/design/batching.md +0 -2054
  178. package/docs/design/decode-speed-program.md +0 -2404
  179. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  180. package/docs/design/generic-model-support.md +0 -821
  181. package/docs/design/kv-cache.md +0 -993
  182. package/docs/design/orpo-training.md +0 -680
  183. package/docs/design/speculative-decoding.md +0 -2129
  184. package/docs/design/turboquant.md +0 -2225
  185. package/docs/design/unified-engine-frontier-plan.md +0 -1987
  186. package/docs/design/web-chat-redesign.md +0 -695
  187. package/docs/planning/PRODUCT_ROADMAP.md +0 -593
  188. package/docs/planning/ResearchTopics.md +0 -119
  189. package/docs/planning/public-readiness-handoff.md +0 -244
  190. package/docs/planning/public-readiness.md +0 -149
  191. package/docs/planning/video-series.md +0 -421
  192. package/docs/reference/benchmarks.md +0 -3587
  193. package/docs/reference/cli.md +0 -646
  194. package/docs/reference/distribution.md +0 -380
  195. package/docs/reference/environment.md +0 -494
  196. package/docs/reference/glossary.md +0 -39
  197. package/docs/reference/library-api.md +0 -688
  198. package/docs/reference/memory.md +0 -260
  199. package/docs/reference/models.md +0 -346
  200. package/docs/reference/server-api.md +0 -1945
  201. package/docs/reference/server-config.md +0 -973
  202. package/docs/reference/training.md +0 -462
  203. package/docs/reference/troubleshooting.md +0 -158
  204. package/src/admission-plan.ts +0 -18
  205. package/src/audio/audiotoolbox.ts +0 -112
  206. package/src/audio/conformer.ts +0 -655
  207. package/src/audio/decode.ts +0 -133
  208. package/src/audio/features.ts +0 -202
  209. package/src/audio/mic-capture.ts +0 -98
  210. package/src/audio/silero-vad.ts +0 -379
  211. package/src/audio/transcode.ts +0 -67
  212. package/src/audio/whisper-decode.ts +0 -774
  213. package/src/audio/whisper-fast.ts +0 -732
  214. package/src/audio/whisper-format.ts +0 -73
  215. package/src/audio/whisper-mel.ts +0 -185
  216. package/src/audio/whisper-timing.ts +0 -274
  217. package/src/audio/whisper-tokenizer.ts +0 -239
  218. package/src/audio/whisper-transcribe.ts +0 -410
  219. package/src/backends/mlx/assistant-target.ts +0 -46
  220. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  221. package/src/backends/mlx/autoregressive.ts +0 -129
  222. package/src/backends/mlx/batch-group.ts +0 -1874
  223. package/src/backends/mlx/cache-attachment.ts +0 -67
  224. package/src/backends/mlx/cache-layout.ts +0 -31
  225. package/src/backends/mlx/checkpoint-state.ts +0 -53
  226. package/src/backends/mlx/continuation-persistence.ts +0 -107
  227. package/src/backends/mlx/continuation-request.ts +0 -43
  228. package/src/backends/mlx/continuation.ts +0 -77
  229. package/src/backends/mlx/diffusion.ts +0 -39
  230. package/src/backends/mlx/draft-target.ts +0 -32
  231. package/src/backends/mlx/encoder-cache.ts +0 -25
  232. package/src/backends/mlx/evaluation.ts +0 -105
  233. package/src/backends/mlx/fill-append.ts +0 -58
  234. package/src/backends/mlx/fill-group.ts +0 -497
  235. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  236. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  237. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  238. package/src/backends/mlx/gateway-binding.ts +0 -174
  239. package/src/backends/mlx/graph.ts +0 -38
  240. package/src/backends/mlx/kv-codec.ts +0 -9
  241. package/src/backends/mlx/kv-maintenance.ts +0 -161
  242. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  243. package/src/backends/mlx/memory-client.ts +0 -189
  244. package/src/backends/mlx/methods.ts +0 -90
  245. package/src/backends/mlx/mixed-iteration.ts +0 -62
  246. package/src/backends/mlx/model-prompt.ts +0 -178
  247. package/src/backends/mlx/model-serving.ts +0 -80
  248. package/src/backends/mlx/paged-cache-codec.ts +0 -27
  249. package/src/backends/mlx/prefill-cohort.ts +0 -100
  250. package/src/backends/mlx/prefill-policy.ts +0 -26
  251. package/src/backends/mlx/prefill-rows.ts +0 -244
  252. package/src/backends/mlx/prefill.ts +0 -33
  253. package/src/backends/mlx/projected-context-rows.ts +0 -72
  254. package/src/backends/mlx/prompt-input.ts +0 -29
  255. package/src/backends/mlx/qwen-prompt-input.ts +0 -40
  256. package/src/backends/mlx/request-policy.ts +0 -20
  257. package/src/backends/mlx/request-state-policy.ts +0 -54
  258. package/src/backends/mlx/rollback.ts +0 -92
  259. package/src/backends/mlx/row-sampling.ts +0 -49
  260. package/src/backends/mlx/serial-executor.ts +0 -251
  261. package/src/backends/mlx/speculative-group.ts +0 -438
  262. package/src/backends/mlx/speculative-round.ts +0 -161
  263. package/src/backends/mlx/speculative.ts +0 -131
  264. package/src/backends/mlx/state-rows.ts +0 -104
  265. package/src/backends/mlx/state-views.ts +0 -46
  266. package/src/backends/mlx/target-layout-capability.ts +0 -35
  267. package/src/backends/mlx/text-engine.ts +0 -73
  268. package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
  269. package/src/chat-template.ts +0 -382
  270. package/src/cli.ts +0 -3490
  271. package/src/config.ts +0 -399
  272. package/src/contracts/completion.ts +0 -16
  273. package/src/contracts/execution.ts +0 -70
  274. package/src/contracts/generation.ts +0 -103
  275. package/src/contracts/host.ts +0 -7
  276. package/src/contracts/object-cache.ts +0 -8
  277. package/src/contracts/pi-provider.ts +0 -30
  278. package/src/contracts/pi.ts +0 -261
  279. package/src/contracts/prefix-cache.ts +0 -33
  280. package/src/contracts/resources.ts +0 -25
  281. package/src/contracts/sampling.ts +0 -23
  282. package/src/contracts/scheduling.ts +0 -54
  283. package/src/contracts/tsconfig.json +0 -14
  284. package/src/dataset/generators.ts +0 -1087
  285. package/src/dataset/index.ts +0 -12
  286. package/src/dataset/job.ts +0 -81
  287. package/src/dataset/llm.ts +0 -71
  288. package/src/dataset/registry.ts +0 -528
  289. package/src/diffusion/diffusion-generate.ts +0 -507
  290. package/src/download.ts +0 -505
  291. package/src/embed.ts +0 -77
  292. package/src/engine/admission.ts +0 -59
  293. package/src/engine/cancellation.ts +0 -38
  294. package/src/engine/engine.ts +0 -56
  295. package/src/engine/execution-coordinator.ts +0 -70
  296. package/src/engine/execution-plan.ts +0 -64
  297. package/src/engine/execution-tasks.ts +0 -44
  298. package/src/engine/namespaced-cache.ts +0 -13
  299. package/src/engine/resources.ts +0 -53
  300. package/src/engine/scheduler.ts +0 -92
  301. package/src/engine/session.ts +0 -247
  302. package/src/eval/README.md +0 -130
  303. package/src/eval/ifeval.ts +0 -81
  304. package/src/eval/kl-prompts.ts +0 -29
  305. package/src/eval/kl.ts +0 -590
  306. package/src/eval/perplexity.ts +0 -164
  307. package/src/eval/quality-db.ts +0 -92
  308. package/src/eval/reference-logits.ts +0 -207
  309. package/src/eval/runner.ts +0 -124
  310. package/src/eval/score.ts +0 -49
  311. package/src/eval/tasks/bfcl.ts +0 -535
  312. package/src/eval/tasks/gsm8k.ts +0 -98
  313. package/src/eval/tasks/hashhop.ts +0 -139
  314. package/src/eval/tasks/humaneval.ts +0 -275
  315. package/src/eval/tasks/ifeval.ts +0 -525
  316. package/src/eval/tasks/mmlu.ts +0 -157
  317. package/src/eval/ultrafeedback.ts +0 -60
  318. package/src/evaldb.ts +0 -199
  319. package/src/expert-io.ts +0 -393
  320. package/src/expert-offload-build.ts +0 -77
  321. package/src/expert-offload.ts +0 -76
  322. package/src/expert-residency.ts +0 -1134
  323. package/src/expert-usage.ts +0 -415
  324. package/src/fill/echo-index.ts +0 -188
  325. package/src/fill/fill-session.ts +0 -463
  326. package/src/fill/proposal.ts +0 -68
  327. package/src/fill/schema-rows.ts +0 -506
  328. package/src/fill/tool-boundary.ts +0 -103
  329. package/src/fit.ts +0 -249
  330. package/src/generate.ts +0 -1352
  331. package/src/grammar.ts +0 -604
  332. package/src/harness-pi.ts +0 -184
  333. package/src/hf-push.ts +0 -628
  334. package/src/hub-rest.ts +0 -242
  335. package/src/index.ts +0 -63
  336. package/src/inference/denoising.ts +0 -17
  337. package/src/inference/draft-acceptance.ts +0 -41
  338. package/src/inference/draft-output.ts +0 -49
  339. package/src/inference/graph.ts +0 -33
  340. package/src/inference/prefill.ts +0 -45
  341. package/src/inference/rollback.ts +0 -7
  342. package/src/jobs/index.ts +0 -31
  343. package/src/jobs/job-entry.ts +0 -107
  344. package/src/jobs/task-client.ts +0 -19
  345. package/src/jobs/types.ts +0 -2
  346. package/src/kv-scheme.ts +0 -279
  347. package/src/kv-store.ts +0 -1135
  348. package/src/lab/README.md +0 -22
  349. package/src/lab/curve/curve-designer.html +0 -303
  350. package/src/lab/curve/curve-sampler.ts +0 -122
  351. package/src/lab/expert-trace/expert-trace.ts +0 -69
  352. package/src/lab/paged-kv/paged-attention.ts +0 -117
  353. package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
  354. package/src/lab/paged-kv/paged-kv.ts +0 -336
  355. package/src/library.ts +0 -40
  356. package/src/lora.ts +0 -408
  357. package/src/md-modules.d.ts +0 -54
  358. package/src/media-fetch.ts +0 -442
  359. package/src/memory/article.ts +0 -558
  360. package/src/memory/chunk-validation.ts +0 -150
  361. package/src/memory/chunk.ts +0 -318
  362. package/src/memory/cluster.ts +0 -351
  363. package/src/memory/coverage.ts +0 -134
  364. package/src/memory/crosslink.ts +0 -0
  365. package/src/memory/db.ts +0 -253
  366. package/src/memory/entity.ts +0 -203
  367. package/src/memory/gate.ts +0 -187
  368. package/src/memory/history.ts +0 -152
  369. package/src/memory/ingest.ts +0 -335
  370. package/src/memory/model.ts +0 -183
  371. package/src/memory/normalize.ts +0 -671
  372. package/src/memory/parse.ts +0 -22
  373. package/src/memory/pipeline.ts +0 -275
  374. package/src/memory/prompts.ts +0 -65
  375. package/src/memory/query.ts +0 -334
  376. package/src/memory/reconcile.ts +0 -734
  377. package/src/memory/redirect.ts +0 -108
  378. package/src/memory/reindex.ts +0 -325
  379. package/src/memory/resolve.ts +0 -307
  380. package/src/memory/rest.ts +0 -245
  381. package/src/memory/route.ts +0 -272
  382. package/src/memory/schedule.ts +0 -165
  383. package/src/memory/stages.ts +0 -777
  384. package/src/memory/synthesize.ts +0 -1638
  385. package/src/memory/tools.ts +0 -749
  386. package/src/memory/vault.ts +0 -1194
  387. package/src/memory/wikify.ts +0 -1064
  388. package/src/mlx/array.ts +0 -425
  389. package/src/mlx/autograd.ts +0 -304
  390. package/src/mlx/checkpoint.ts +0 -149
  391. package/src/mlx/compile.ts +0 -168
  392. package/src/mlx/custom-vjp.ts +0 -188
  393. package/src/mlx/ffi.ts +0 -531
  394. package/src/mlx/materialize.ts +0 -23
  395. package/src/mlx/metal-kernel.ts +0 -159
  396. package/src/mlx/normalized-argmax.ts +0 -116
  397. package/src/mlx/ops.ts +0 -1108
  398. package/src/mlx/quantized-rows.ts +0 -36
  399. package/src/mlx/token-bitmask.ts +0 -52
  400. package/src/mlx/turboquant-kv-decode.ts +0 -82
  401. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  402. package/src/mlx/turboquant-ops.ts +0 -861
  403. package/src/mlx/turboquant-tables.ts +0 -237
  404. package/src/mmap.ts +0 -90
  405. package/src/model/artifact-identity.ts +0 -73
  406. package/src/model/batched-kv.ts +0 -235
  407. package/src/model/batched-mask.ts +0 -279
  408. package/src/model/batched-quant.ts +0 -212
  409. package/src/model/batched-quantized-kv.ts +0 -144
  410. package/src/model/batched-rotating-quant.ts +0 -353
  411. package/src/model/batched-rotating-state.ts +0 -149
  412. package/src/model/batched-rotating.ts +0 -338
  413. package/src/model/batched-row-storage.ts +0 -179
  414. package/src/model/batched-ssm.ts +0 -52
  415. package/src/model/batched-turboquant-kv.ts +0 -94
  416. package/src/model/compiled-decode.ts +0 -712
  417. package/src/model/decoded-kv-donor.ts +0 -12
  418. package/src/model/delayed-quantized-kv.ts +0 -72
  419. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  420. package/src/model/delayed-turboquant-kv.ts +0 -57
  421. package/src/model/diffusion-gemma.ts +0 -1026
  422. package/src/model/factory.ts +0 -215
  423. package/src/model/fingerprint.ts +0 -45
  424. package/src/model/flash-attention.ts +0 -694
  425. package/src/model/full-prefill-padding.ts +0 -56
  426. package/src/model/full-prefill-row.ts +0 -105
  427. package/src/model/gemma4-base.ts +0 -2370
  428. package/src/model/gemma4.ts +0 -1395
  429. package/src/model/generated/gemma4-12b.ts +0 -429
  430. package/src/model/generated/gemma4-26b.ts +0 -448
  431. package/src/model/generated/gemma4-e4b.ts +0 -717
  432. package/src/model/generated/index.ts +0 -18
  433. package/src/model/glm52-atlas.ts +0 -511
  434. package/src/model/glm52-cache.ts +0 -792
  435. package/src/model/glm52-config.ts +0 -262
  436. package/src/model/glm52-container.ts +0 -305
  437. package/src/model/glm52-coupling.ts +0 -388
  438. package/src/model/glm52-dsa.ts +0 -606
  439. package/src/model/glm52-expert-layout.ts +0 -253
  440. package/src/model/glm52-layout.ts +0 -152
  441. package/src/model/glm52-memory.ts +0 -500
  442. package/src/model/glm52-mla.ts +0 -792
  443. package/src/model/glm52-moe.ts +0 -223
  444. package/src/model/glm52-pilot.ts +0 -537
  445. package/src/model/glm52-reference.ts +0 -445
  446. package/src/model/glm52-residency.ts +0 -557
  447. package/src/model/glm52-resident-weights.ts +0 -135
  448. package/src/model/glm52-streamed-experts.ts +0 -453
  449. package/src/model/glm52-streamed-metal.ts +0 -610
  450. package/src/model/glm52-weights.ts +0 -503
  451. package/src/model/glm52.ts +0 -1132
  452. package/src/model/implementation.ts +0 -69
  453. package/src/model/kv-attention-view.ts +0 -70
  454. package/src/model/minicpm5.ts +0 -376
  455. package/src/model/profile.ts +0 -468
  456. package/src/model/quantized-append-attention.ts +0 -31
  457. package/src/model/qwen-append.ts +0 -12
  458. package/src/model/qwen-conv.ts +0 -52
  459. package/src/model/qwen3-delta-state.ts +0 -74
  460. package/src/model/qwen3-delta.ts +0 -451
  461. package/src/model/qwen3-moe.ts +0 -404
  462. package/src/model/qwen3-mrope.ts +0 -250
  463. package/src/model/qwen3.ts +0 -277
  464. package/src/model/qwen3_5-checkpoint.ts +0 -164
  465. package/src/model/qwen3_5.ts +0 -881
  466. package/src/model/rotating-kv-donor.ts +0 -35
  467. package/src/model/rotating-kv-layout.ts +0 -262
  468. package/src/model/rotating-row-transaction.ts +0 -35
  469. package/src/model/speculative-rotating-kv.ts +0 -101
  470. package/src/model/ssm-prefill-padding.ts +0 -39
  471. package/src/model/support.ts +0 -110
  472. package/src/model/token-groups.ts +0 -66
  473. package/src/model/trellis-balanced-scatter.ts +0 -51
  474. package/src/model/trellis-linear.ts +0 -672
  475. package/src/model/trellis-shared-m.ts +0 -151
  476. package/src/model/trellis-shared-scatter.ts +0 -144
  477. package/src/model/trellis-splitk-prefill.ts +0 -158
  478. package/src/model/trellis-tiled-prefill.ts +0 -136
  479. package/src/model/trellis-vector-expand.ts +0 -62
  480. package/src/model/trellis-wide-prefill.ts +0 -94
  481. package/src/model/turboquant-codec.ts +0 -89
  482. package/src/model/universal/archs.ts +0 -332
  483. package/src/model/universal/dense.ts +0 -612
  484. package/src/model/universal/modules.ts +0 -170
  485. package/src/model/universal/rope.ts +0 -381
  486. package/src/model/whisper.ts +0 -435
  487. package/src/native/expert_io.c +0 -523
  488. package/src/native/frame_extract.swift +0 -140
  489. package/src/native/mic_capture.swift +0 -92
  490. package/src/native-pack.ts +0 -163
  491. package/src/pi-provider.ts +0 -53
  492. package/src/pi-session.ts +0 -81
  493. package/src/pi-terminal.ts +0 -274
  494. package/src/pi-web.ts +0 -1940
  495. package/src/preflight.ts +0 -158
  496. package/src/prompt-cache.ts +0 -682
  497. package/src/quantize/allocator.ts +0 -627
  498. package/src/quantize/atomic-output.ts +0 -38
  499. package/src/quantize/calibration.ts +0 -181
  500. package/src/quantize/config-writer.ts +0 -145
  501. package/src/quantize/index.ts +0 -84
  502. package/src/quantize/job.ts +0 -162
  503. package/src/quantize/quantizer.ts +0 -666
  504. package/src/quantize/rotate.ts +0 -603
  505. package/src/quantize/safetensors-writer.ts +0 -249
  506. package/src/quantize/sensitivity.ts +0 -216
  507. package/src/quantize/trellis.ts +0 -586
  508. package/src/quantize/weight-transform.ts +0 -292
  509. package/src/registry.ts +0 -602
  510. package/src/runtime-config.ts +0 -77
  511. package/src/safetensors.ts +0 -165
  512. package/src/sampler.ts +0 -1012
  513. package/src/serve/admin-routes.ts +0 -184
  514. package/src/serve/assets/curve-terrain.html +0 -105
  515. package/src/serve/assets/training-inference-map.html +0 -575
  516. package/src/serve/audio-routes.ts +0 -323
  517. package/src/serve/aux-routes.ts +0 -132
  518. package/src/serve/batch-scheduler.ts +0 -8
  519. package/src/serve/checkpoint-identity.ts +0 -64
  520. package/src/serve/generation-gateway.ts +0 -537
  521. package/src/serve/isolate.ts +0 -609
  522. package/src/serve/kv-budget.ts +0 -14
  523. package/src/serve/lab-routes.ts +0 -341
  524. package/src/serve/lane-registry.ts +0 -45
  525. package/src/serve/model-admin-routes.ts +0 -155
  526. package/src/serve/model-binding.ts +0 -49
  527. package/src/serve/model-host.ts +0 -511
  528. package/src/serve/parent-application.ts +0 -56
  529. package/src/serve/prompt-response-trace.ts +0 -161
  530. package/src/serve/response-lease.ts +0 -24
  531. package/src/serve/session-search.ts +0 -326
  532. package/src/serve/static-routes.ts +0 -101
  533. package/src/serve/transcription-server.ts +0 -64
  534. package/src/serve/transcription-service.ts +0 -502
  535. package/src/server.ts +0 -1282
  536. package/src/spec/assistant-rows.ts +0 -93
  537. package/src/spec/assistant-source.ts +0 -126
  538. package/src/spec/cache-identity.ts +0 -22
  539. package/src/spec/deepspec-rows.ts +0 -29
  540. package/src/spec/deepspec-source.ts +0 -145
  541. package/src/spec/dflash-source.ts +0 -149
  542. package/src/spec/drafter.ts +0 -304
  543. package/src/spec/dspark/ab-stats.ts +0 -136
  544. package/src/spec/dspark/calibration.ts +0 -112
  545. package/src/spec/dspark/data-dflash.ts +0 -156
  546. package/src/spec/dspark/data.ts +0 -201
  547. package/src/spec/dspark/deepspec-module.ts +0 -776
  548. package/src/spec/dspark/generate-dflash.ts +0 -245
  549. package/src/spec/dspark/generate.ts +0 -290
  550. package/src/spec/dspark/loader.ts +0 -23
  551. package/src/spec/dspark/loss.ts +0 -213
  552. package/src/spec/dspark/module-dflash.ts +0 -560
  553. package/src/spec/dspark/module.ts +0 -540
  554. package/src/spec/dspark/quantize-drafter.ts +0 -91
  555. package/src/spec/dspark/sample.ts +0 -112
  556. package/src/spec/generate.ts +0 -276
  557. package/src/spec/glm52-mtp-graph.ts +0 -29
  558. package/src/spec/glm52-mtp-rows.ts +0 -132
  559. package/src/spec/glm52-mtp-source.ts +0 -318
  560. package/src/spec/glm52-mtp-state.ts +0 -33
  561. package/src/spec/ngram-source.ts +0 -226
  562. package/src/spec/projected-draft-rows.ts +0 -73
  563. package/src/spec/qwen-mtp-module.ts +0 -171
  564. package/src/spec/qwen-mtp-rows.ts +0 -230
  565. package/src/spec/qwen-mtp-source.ts +0 -386
  566. package/src/spec/qwen-mtp-state.ts +0 -30
  567. package/src/spec/serve-loop.ts +0 -587
  568. package/src/spec/source.ts +0 -228
  569. package/src/spec/two-model-rows.ts +0 -62
  570. package/src/spec/two-model.ts +0 -113
  571. package/src/ssd-cache.ts +0 -431
  572. package/src/ssd-durability.ts +0 -209
  573. package/src/storage/host-buffer.ts +0 -24
  574. package/src/storage/kv-writer.ts +0 -83
  575. package/src/storage/kv-writer.worker.js +0 -264
  576. package/src/storage/retention-policy.ts +0 -37
  577. package/src/tiered-prompt-cache.ts +0 -45
  578. package/src/tokenizer.ts +0 -335
  579. package/src/tool-approvals.ts +0 -131
  580. package/src/tool-call.ts +0 -695
  581. package/src/train/dataset.ts +0 -484
  582. package/src/train/diffusion-lora.ts +0 -216
  583. package/src/train/export.ts +0 -32
  584. package/src/train/flash-cce.ts +0 -1234
  585. package/src/train/forward.ts +0 -266
  586. package/src/train/fuse.ts +0 -243
  587. package/src/train/index.ts +0 -46
  588. package/src/train/job.ts +0 -205
  589. package/src/train/lora-params.ts +0 -254
  590. package/src/train/loss.ts +0 -1591
  591. package/src/train/merge.ts +0 -195
  592. package/src/train/optimizer.ts +0 -183
  593. package/src/train/prefix-shared.ts +0 -490
  594. package/src/train/rank.ts +0 -137
  595. package/src/train/segmented.ts +0 -1828
  596. package/src/train/steel-qmm-header.ts +0 -1461
  597. package/src/train/trainer.ts +0 -1221
  598. package/src/train/watch.ts +0 -542
  599. package/src/vision/diffusion-vision.ts +0 -620
  600. package/src/vision/embedder.ts +0 -176
  601. package/src/vision/preprocess.ts +0 -247
  602. package/src/vision/prompt.ts +0 -409
  603. package/src/vision/qwen3vl-preprocess.ts +0 -409
  604. package/src/vision/qwen3vl-prompt.ts +0 -233
  605. package/src/vision/qwen3vl-tower.ts +0 -566
  606. package/src/vision/siglip.ts +0 -607
  607. package/src/vision/video-frames.ts +0 -148
  608. package/src/web/app.html +0 -2155
  609. package/src/web/app.js +0 -5863
  610. package/src/web/skills/memory/SKILL.md +0 -120
  611. package/src/web/skills/web-research/SKILL.md +0 -46
  612. package/src/web/skills.ts +0 -97
  613. package/src/web/src/adapters-panel.ts +0 -288
  614. package/src/web/src/assistant.ts +0 -451
  615. package/src/web/src/chat.ts +0 -1060
  616. package/src/web/src/composer.ts +0 -962
  617. package/src/web/src/dataset.ts +0 -132
  618. package/src/web/src/finetune.ts +0 -267
  619. package/src/web/src/hub.ts +0 -428
  620. package/src/web/src/main.ts +0 -98
  621. package/src/web/src/markdown.ts +0 -0
  622. package/src/web/src/memory-panel.ts +0 -572
  623. package/src/web/src/model-picker.ts +0 -159
  624. package/src/web/src/palette.ts +0 -346
  625. package/src/web/src/protocol.ts +0 -26
  626. package/src/web/src/quantize.ts +0 -167
  627. package/src/web/src/rag.ts +0 -274
  628. package/src/web/src/sessions.ts +0 -469
  629. package/src/web/src/shell.ts +0 -804
  630. package/src/web/src/ui-catalog.ts +0 -78
  631. package/src/web/src/voice.ts +0 -141
  632. package/src/web/vendor/README.md +0 -61
  633. package/src/web-assets.ts +0 -47
  634. package/src/web-tools.ts +0 -478
  635. package/src/weights.ts +0 -255
  636. /package/src/{serve → server}/completion-sink.ts +0 -0
  637. /package/src/{serve → server}/generated-token-history.ts +0 -0
  638. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  639. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  640. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,157 +0,0 @@
1
- # Build journal — mlx-bun Lab (web UI + native quantize/train/dataset + pi web chat)
2
-
3
- *Branch `web-ui-and-native-lab`, 2026-06-13/14. Visual summary:
4
- [archive/mlx-bun-lab-report.html](../../docs/archive/mlx-bun-lab-report.html). Evidence
5
- artifact: `benchmarks/lab-verification.json` (regenerable, gitignored — produced by
6
- `scripts/verify-lab.ts`).*
7
-
8
- ## Goal
9
-
10
- Re-implement the whole mlx-bun web surface as one beautiful, keynote-grade SPA
11
- with five sections — a **full pi-agent chat**, plus **Quantize**, **Fine-tune**,
12
- and **Build Dataset** ported from the OptIQ "lab" — while keeping the status
13
- page. Compute is **native Bun/MLX (no Python)** per Josh's decision.
14
-
15
- ## The decisions that shaped it
16
-
17
- 1. **The big fork (asked up front).** Quantize + fine-tune compute could be (a)
18
- native via mlx-c, (b) orchestrate the installed OptIQ Python, or (c) proxy
19
- the running `optiq lab`. Josh chose **(a) native** — truest to mlx-bun's "the
20
- buggy layer doesn't need Python" thesis. Chat = **full pi agent** (all tools).
21
- Verification = **real tiny runs, pausing for go-ahead** before heavy jobs.
22
-
23
- 2. **Gate the riskiest unknown first.** Native LoRA needs autograd across the
24
- FFI boundary. Rather than discover mid-build that it's infeasible, the first
25
- thing built was `lab/spikes/phase-train-vag.ts`: a tiny LoRA-shaped loss
26
- (`mean((x + (x@A)@B − y)²)`) differentiated by `mlx_value_and_grad` through a
27
- `bun:ffi` JSCallback closure, checked against central finite differences.
28
- **PASS (<1e-2), exactly 2 grads returned (base frozen).** Only then was the
29
- trainer worth writing. This single check de-risked the entire subsystem.
30
-
31
- 3. **One SPA, hash-routed, text-imported.** Kept the exact single-binary
32
- embedding (`with { type: "text" }`) the status page already used — no
33
- bundler, no asset-resolution to patch in `build-binary.sh`. Design tokens
34
- lifted from `status-page.html` so the five sections feel like one product.
35
-
36
- 4. **Mixed job-execution model by safety class.** Async Metal command-buffer
37
- OOM is **uncatchable** (it `std::terminate`s). So GPU jobs (quantize,
38
- finetune) run as **crash-isolated child `mlx-bun` subprocesses** behind a GPU
39
- lease; dataset jobs (pure JS + loopback HTTP) run **in-process**. Both write
40
- the same NDJSON event log + SQLite row, so the SSE/tail path is uniform.
41
-
42
- 5. **Open-book ports, not inventions.** Each engine follows a readable source:
43
- dataset → `optiq/lab/dataset_templates.py` (13 templates, field-for-field);
44
- trainer → `optiq/lora/{trainer,dpo,config}.py` + mlx-lm `default_loss`;
45
- quantize → mlx-lm `convert.py` rules + the on-disk OptIQ model format; jobs →
46
- `optiq/lab/{jobs,db}.py`; chat → pi SDK `examples/sdk/12-full-control.ts`.
47
-
48
- ## How it was built — parallel orchestration
49
-
50
- After laying the shared job contract (`src/jobs/types.ts`), six subsystems were
51
- fanned out to concurrent sub-agents on **non-overlapping files**, then
52
- integrated centrally:
53
-
54
- | subsystem | files | how verified |
55
- |---|---|---|
56
- | shared jobs | `src/jobs/*` | 12 tests (lifecycle, GPU lease, crash isolation, zombie, SSE) |
57
- | dataset | `src/dataset/*` | 32 tests (generators, CSV, 90/10 split, 13 templates) |
58
- | quantize | `src/quantize/*` | 6 tests + real-model e2e |
59
- | trainer | `src/train/*`, `src/mlx/autograd.ts` | 19 fast + real-model e2e |
60
- | web SPA | `src/web/app.html` | 7 in-process route tests |
61
- | pi embed | `src/pi-web.ts` | 9 unit + live chat turn |
62
-
63
- Shared/owned-by-me files were edited centrally to avoid conflicts:
64
- `src/server.ts` (websocket + Lab routes + serve the SPA), `src/mlx/{ffi,ops}.ts`
65
- (autograd/random/save bindings), `src/cli.ts`, `package.json` (pinned
66
- `@earendil-works/pi-coding-agent@0.79.3`).
67
-
68
- ## Key technical findings
69
-
70
- - **`mlx_save_safetensors` exists** (mlx-c `io.h`) — the quantizer writes real
71
- HF-layout shards natively; no hand-rolled byte writer, no dtype/alignment bugs.
72
- - **`mlx_value_and_grad` + `mlx_closure_new_func_payload`** are drivable from a
73
- `bun:ffi` JSCallback (the repo already uses one for the mlx error handler).
74
- The closure swaps the differentiated primals into `LoraWeights.{a,b}`, builds
75
- the loss graph, and restores; mlx's graph keeps its own refs, so only A/B
76
- differentiate and the quantized base stays frozen.
77
- - **No optimizer in mlx-c** → AdamW hand-rolled as array ops (m/v EMA, bias
78
- correction, decoupled weight decay), each step swapping the leaf handle back
79
- into the `LoraWeights` so the next forward sees the update.
80
- - **pi tool-approval gate** is a real pre-execution hook: an inline extension
81
- via `DefaultResourceLoader.extensionFactories` registers `pi.on("tool_call")`
82
- returning `{block:true}` to deny — read-only tools auto-allow, bash/edit/write
83
- await a browser decision (120s auto-deny).
84
-
85
- ## Bugs found and fixed (during the real-model runs)
86
-
87
- - **LoRA init key shape** — `mlx_random_normal` got a `[1,2]` key (a row slice of
88
- the split-keys array) but mlx wants `[2]`. Reshaped the per-module subkey.
89
- - **Quantize e2e path** — the gated test passed a model *id* to
90
- `quantizeModelDir` instead of a resolved snapshot *path* (the job runner
91
- resolves via the registry; the direct call didn't). Pointed it at
92
- `SNAPSHOT_MINICPM5`.
93
- - **Flaky chat test** — the agent sometimes takes a tool-call turn before
94
- emitting text; the test resolved on the first `turn_end` and saw no tokens.
95
- Fixed to settle on the first turn that actually streams text. The embed was
96
- always correct (a debug run showed 25+ text deltas + a tool execution).
97
-
98
- ## Measured evidence (M4 Pro, MiniCPM5-1B, on-disk)
99
-
100
- - **Quantize:** 170 modules → **4.50 bits/weight**, **0.62 GB** dir, **7.0 s**,
101
- reopened correctly through the loader (quantized embed_tokens as `uint32`).
102
- - **Train:** 20 SFT iters, **loss 4.48 → 0.68** in **4.6 s**, 168 adapters
103
- mounted. Behavior changed — on "Tell me about your day": base replies normally,
104
- the UPPERCASE-fixture adapter replies `"MY DAY AHEAD. APPROVE AND PROPERLY…"`.
105
- - **Chat:** a real WebSocket turn streamed text + executed a tool to completion.
106
- - **Tests:** 85+ new, all green; `tsc --noEmit` clean; existing server suite
107
- 17/17 (no regressions).
108
-
109
- ## Scope: shipped vs designed-seam
110
-
111
- - **Shipped (v1):** full SPA (5 sections), pi web chat with tools + approval
112
- gate, native uniform-affine quantize, native SFT + DPO LoRA, 12/13 dataset
113
- templates, shared job system, real tiny-run verification.
114
- - **Designed seams (follow-ups):** ~~OptIQ sensitivity+knapsack mixed-precision
115
- quantization; batched (B>1) training; `hf_dataset_import`; HF push-to-hub;
116
- folding pi's assets into the compiled single binary.~~ **ALL DONE in Round 2 —
117
- see below.**
118
-
119
- ## Round 2 — the leftover seams, ported 1:1 (2026-06-14)
120
-
121
- Josh: "all of those, port, one to one functionality." Built the five remaining
122
- items as faithful ports of the OptIQ / mlx-lm source (open book), in parallel on
123
- non-overlapping files, integrated centrally (server routes + one `app.html` UI
124
- pass), and verified.
125
-
126
- - **Mixed-precision quantization** (`src/quantize/{allocator,sensitivity,
127
- calibration}.ts`) — `optimize_mixed_precision` ported **1:1** from
128
- `optiq/core/optimizer.py` (greedy knapsack + block-aware floor + protected
129
- layers + block-run guard + the uniform-vs-bf16 `_kl_reduction` sign detection).
130
- Verified **bit-identical to OptIQ** on shared synthetic inputs by spawning the
131
- venv Python in the test (4/4 cases). Per-layer KL sensitivity
132
- (`analyze_sensitivity_exact`) runs on our graph: dequantize each layer's weight
133
- to bf16, re-quantize at each candidate bit, swap into the running
134
- `QuantizedLinear`, forward on a WikiText-2 calibration set, KL vs reference.
135
- Real e2e: MiniCPM5-1B at target 5.0 bpw → 80×4-bit + 90×8-bit, **5.19 bpw, 32 s**.
136
- - **Batched (B>1) training** (`src/train/{dataset,forward,loss}.ts`) — ragged
137
- padding + a `[B,1,L,L]` causal-AND-key-validity mask via a cache wrapper (no
138
- model/op edits); B=1 stays a separate bit-identical path. Bug found: comparing a
139
- length-4 reference to a length-6 batched row conflated mask correctness with
140
- bf16 sequence-length rounding — the corrected shape-held parity test passes,
141
- confirming the mask isolates rows.
142
- - **`hf_dataset_import`** (`src/dataset/generators.ts`) — the 13th template via
143
- the HF datasets-server REST API (pagination, config resolve, label/min-chars/
144
- cap filters, three output formats).
145
- - **HF push-to-hub** (`src/hf-push.ts`) — native Hub upload (create repo → LFS
146
- preupload → S3 PUT → NDJSON commit) for model/adapter/dataset repos, token at
147
- `~/.mlx-bun/hf.json`. Mock-server tested end-to-end (11 tests); real uploads
148
- untested by design (would publish under the user's account).
149
- - **Single-binary web chat** (`scripts/build-binary.sh`) — pi's JS already
150
- bundles under `bun build --compile`; only `photon_rs_bg.wasm` is sidecarred.
151
- The compiled binary's headless pi session was verified to construct.
152
-
153
- UI: one `app.html` pass enabled the Uniform/Mixed quantize toggle + target-bpw
154
- slider, live merge/export/push on the fine-tune done step, push on quantize &
155
- dataset, and an HF-token settings gear. New server routes:
156
- `/api/finetune/{merge,export}`, `/api/{quantize,finetune,dataset}/push`,
157
- `/api/settings/hf-token`, + mixed-precision params on quantize submit.
@@ -1,489 +0,0 @@
1
- # MiniCPM5 Decode Megakernel — end-to-end build plan (handoff)
2
-
3
- **Read this top to bottom, then execute the phases in order. Each phase has an
4
- exact validation gate; do not advance until it passes. At the end you have one
5
- resident Metal kernel that computes the entire decode forward (all 24 layers →
6
- logits) in a single dispatch, never handing MLX's planner a graph to mis-plan.**
7
-
8
- This document is self-contained. You should not need to ask anything to execute it.
9
-
10
- ---
11
-
12
- ## 0. Why this exists (the problem, settled)
13
-
14
- The inference forward composes per-op MLX nodes (`quantized_matmul`, `sdpa`, …).
15
- We tried to insert our own fused `fast.metal_kernel`s (steel `BlockMMA` GEMM) into
16
- that forward to fuse+speed it up. **Confirmed dead end:** MLX's lazy buffer planner
17
- corrupts *chained* `BlockMMA` custom kernels (nondeterministic NaN/garbage/crash) —
18
- the pointwise `fused-gelu` kernel chained fine (that kernel has since been deleted,
19
- 2026-07-05, in favor of the compiled activations — unified-engine-frontier-plan.md
20
- §6), ours did not, and ≥2 of ours in one lazy graph corrupt. The only robust fixes are (a) a per-kernel eval boundary (24 syncs/
21
- forward → net **slower**, 0.5–0.8×), or (b) **don't give the planner anything to
22
- chain: one resident kernel for the whole forward.** This plan builds (b).
23
-
24
- The training path already proved the shape: its forward is MLX ops (which chain
25
- fine) + **one** terminal fused kernel (flash-CCE loss), `detachLeaf` at coarse
26
- segment boundaries (`src/train/segmented.ts`). Our megakernel is the inference
27
- analogue — but because inference has no terminal heavy-reduction, the "one kernel"
28
- must be the *whole forward*, resident, coordinating its own stages.
29
-
30
- **Scope = DECODE (single new token, M=1).** This is the only case physically
31
- expressible as one dispatch: a single Metal dispatch cannot barrier across
32
- threadgroups, so cross-token prefill attention can't be one kernel — but one token
33
- attending to the *cached* KV has no cross-token dependency. Decode is also the
34
- latency path that *is* serving speed (`[[serving-speed-is-the-user-metric]]`).
35
- Prefill stays on the existing (correct, fast, parity) MLX-ops path.
36
-
37
- **Honest perf expectation:** decode is weight-bandwidth-bound (reading ~1 GB of
38
- 4/8-bit weights per token is the floor for any method). The megakernel's win is
39
- (1) eliminating ~240 per-op dispatch launches/token, (2) no intermediate device
40
- round-trips (hidden stays resident), (3) zero MLX-planner involvement. Expect a
41
- modest but real decode-latency win (overhead elimination), not a bandwidth
42
- miracle. The *architectural* win (no corruption, fully ours) is the point.
43
-
44
- ---
45
-
46
- ## 1. Success criteria (definition of done)
47
-
48
- 1. A function `decodeMegakernel(model, tokenId, position) → logits[V]` that runs the
49
- entire MiniCPM5 decode forward in **one** `MetalKernel.apply` dispatch.
50
- 2. **Correctness gate (L1, bf16 KV):** teacher-forced greedy trajectory matches
51
- `goldens/minicpm5-parity.json` — argmax agreement **≥ 98/100**, mean per-step
52
- logit maxDiff small (this is a no-oracle Lab-gated path: ~1–2 bf16 ULP/op
53
- accumulating; it will NOT be bit-exact, gate by teacher-forced agreement + KL —
54
- the frozen-trajectory envelope method of the old `tests/perf-kernel-oracle.test.ts`,
55
- threshold ≥ 56/64-style; that test was deleted 2026-07-05 with its kernel, but
56
- the methodology stands on its own). DETERMINISTIC
57
- across runs (no nondeterminism — that was the corruption signature).
58
- 3. **Perf gate:** decode tok/s ≥ the serial baseline on a paired same-process A/B
59
- (`scripts/experiments/` style). Not slower. (Stretch: measurably faster.)
60
- 4. Default **OFF** behind `MLX_BUN_MEGAKERNEL=1`; the existing forward untouched;
61
- `bun test tests/minicpm5-*.test.ts` stays green with it off.
62
- 5. Wired into the real decode loop (`src/generate.ts`) for `--megakernel` / env.
63
-
64
- ---
65
-
66
- ## 2. Hard facts (MiniCPM5-1B-OptiQ-4bit) — do not re-derive
67
-
68
- **Snapshot:** `/Users/joshrossi/.cache/huggingface/hub/models--mlx-community--MiniCPM5-1B-OptiQ-4bit/snapshots/664aabaed233c653f82716d8dc822234d0091f78`
69
-
70
- **Config:** `hidden_size H=1536`, `num_hidden_layers=24`, `num_attention_heads=16`,
71
- `num_key_value_heads=2` (GQA, `nRep=8`), `head_dim=128`, `intermediate_size I=4608`,
72
- `vocab_size V=130560`, `rms_norm_eps=1e-6`, **`rope_theta=5_000_000`** (NOT 10000 —
73
- the code reads `t.ropeParameters.full_attention?.ropeTheta`), `tie_word_embeddings=false`.
74
- `attention scale = head_dim^-0.5 = 128^-0.5`. Plain Llama: **no MiniCPM residual
75
- scaling** (verified — `LlamaLayer.forward` is `inputNorm→attn→+x→postAttnNorm→mlp→+h`).
76
-
77
- **Per-layer attention output dims:** q_proj out = `16*128=2048`; k_proj/v_proj out =
78
- `2*128=256`; o_proj in = `2048`, out = `1536`. MLP: gate/up out = `4608`, down in =
79
- `4608`, out = `1536`. lm_head out = `130560`, in = `1536`. embed = quantized 8-bit.
80
-
81
- **Per-TENSOR mixed precision (weight quant):** every linear has its OWN `{bits,
82
- group_size}` baked in `config.quantization` (group_size always 64; bits ∈ {4,8}).
83
- This is why the megakernel must be **generated/de-branched per model** — bits is a
84
- compile-time literal per (layer, matrix), not a runtime value. Full table is in
85
- `config.json` under `quantization`; read it with `quantFor(config.quantization,
86
- path)` (`src/model/gemma4-base.ts`). Examples: L0 all 8-bit; L1 q4/k4/v8/o8/g4/u4/d8;
87
- L7 q8/k4/v4/o8/g4/u8/d4; L23 all 8-bit. **Read the table programmatically per build —
88
- do not hardcode.**
89
-
90
- **Quantized weight layout** (`QuantizedLinear`, `src/model/gemma4-base.ts:76`):
91
- - `.w`: packed `uint32` `[N_out, K_in*bits/32]`. `.scales`,`.biases`: `bf16`
92
- `[N_out, K_in/group_size]` (affine: `deq = scale*q + bias`).
93
- - Dequant math is in `STEEL_QMM_HEADER` (`dequantize<T,N,bits>`, `get_pack_factor`,
94
- `get_bytes_per_pack`) — `src/train/steel-qmm-header.ts`. REUSE IT.
95
-
96
- **KV cache:** L1 path = `KVCache` (bf16, `src/model/gemma4-base.ts:243`); its
97
- `updateAndFetch(k,v)` appends `[1,heads,1,D]` at `cache.offset` and returns the full
98
- `[1,heads,offset+1,D]`. `cache.offset` is the position. L2 path = per-layer
99
- `QuantizedKVCache` (mixed bits from `config.kvQuant`) — DEFER to Phase 4.
100
-
101
- **RoPE** (`ops.rope`, `src/mlx/ops.ts:100`): `rope(x[B,heads,L,D], dims=head_dim,
102
- base=rope_theta, offset, scale=null)`. For decode L=1, offset=position. The
103
- in-kernel formula (standard NeoX/Llama interleaved-half): for dim pair `i<D/2`,
104
- `theta_i = position / base^(2i/D)`; rotate `(x[i], x[i+D/2])` by `theta_i`. **Match
105
- `ops.rope`'s exact convention** — confirm against `src/mlx/ops.ts` rope binding and
106
- a 1-token parity probe before trusting it (Phase 2 gate catches mismatch).
107
-
108
- **Validation goldens (ALREADY EXIST, no regen needed):**
109
- - L1: `goldens/minicpm5-parity.json` (prompt_ids `[0,608,4894,304,6918,357]`,
110
- greedy_ids[100], vocab 130560) + `goldens/minicpm5-logits-step{0..99}.bin`
111
- (f32 last-position logits). Produced by `scripts/regen.ts minicpm5`.
112
- - L2: `goldens/minicpm5-kv-parity.json` + `…-kv-logits-step*.bin`.
113
-
114
- **Reference forward to copy (the source of truth for correctness):**
115
- `src/model/minicpm5.ts` — `LlamaAttention.forward` (95–193), `LlamaMLP.forward`,
116
- `LlamaLayer.forward`, `MiniCPM5Model.forwardHidden/forwardLayers/logitsFromHidden`.
117
- Transcribe THIS op-for-op into the kernel.
118
-
119
- ---
120
-
121
- ## 3. Already-built, validated building blocks (reuse, don't rebuild)
122
-
123
- All default-OFF, `bun test tests/minicpm5-*.test.ts` green. From this session:
124
- - `src/train/steel-qmm-header.ts` — `STEEL_QMM_HEADER`: MLX's verbatim quantized
125
- `BlockMMA` GEMM + `dequantize`/`QuantizedBlockLoader`/`BlockLoader`. The GEMM
126
- building blocks for every projection. `qmm_t_impl` (line 1334) is the full GEMM
127
- reference (takes dims as `constant int&` — for our metal_kernel we inline the
128
- `BlockMMA` loop with templateInt dims instead, as `FWD_STEEL_SOURCE` does).
129
- - `src/model/fused-mlp-kernel.ts` — `fusedMlp`: whole MLP (gate+up+silu+down +
130
- absorbed residual) in ONE dispatch, `hidden` never materialized, per-loader
131
- mixed-precision bits baked, atomic-accumulated f32 output. **1.9–3.4× standalone
132
- @ M=8–256, ~1 ULP correct.** This is a working prototype of one megakernel STAGE
133
- and the dequant/GEMV patterns to copy. (Note: it currently corrupts when CHAINED
134
- in the lazy graph — that's the whole reason for the megakernel; the kernel math
135
- itself is correct, proven by `fused-mlp-check.ts (deleted; git history)`.)
136
- - `src/model/steel-linear-kernel.ts` — `steelLinear`: plain quantized linear as our
137
- dispatch (q/k/v/o/down), ~1 ULP vs `ops.quantizedMatmul`. The projection GEMV
138
- pattern.
139
- - `src/mlx/metal-kernel.ts` — `MetalKernel`: `apply(inputs, {outputShapeFn|outputs,
140
- grid, threadGroup, templateInts, templateDtypes, atomicOutputs, initValue})`.
141
- `*_shape` helpers available in-kernel. `metalCapture` for Xcode GPU traces.
142
- - `src/train/segmented.ts:81` `detachLeaf` (contiguous→eval→`fromBytesCopy`) — the
143
- graph-break primitive (replicated in `minicpm5.ts`).
144
-
145
- **Probe scripts (templates for new validation):** `scripts/experiments/swiglu-*.ts`,
146
- `fused-mlp-*.ts`, `steel-linear-check.ts`, `swiglu-teacherforced.ts` (the
147
- teacher-forced gate harness — copy it).
148
-
149
- ---
150
-
151
- ## 4. Architecture of the megakernel
152
-
153
- ### 4.1 The shape
154
-
155
- One resident dispatch. State lives in **device** buffers (shared across
156
- threadgroups); the kernel walks layer 0→23 as a sequence of **stages**, with a
157
- **grid-wide barrier** between stages so stage N+1 sees all of stage N.
158
-
159
- Per layer, the stages (each a grid-wide-parallel step, barrier after):
160
- 1. `xn = RMSNorm(hidden, inputNorm_w)` → device `xn[H]`
161
- 2. `q = Wq·xn` (`[2048]`), `k = Wk·xn` (`[256]`), `v = Wv·xn` (`[256]`) — quantized
162
- GEMVs, outputs split across threadgroups → device
163
- 3. `rope(q, pos)`, `rope(k, pos)` (per head, D=128)
164
- 4. write `k,v` into KV cache at `pos` (device)
165
- 5. **attention:** for each of 16 q-heads (GQA: head h uses kv-head `h/8`), online
166
- softmax of `q_h · K[0..pos]` over the `pos+1` cached keys (scale `128^-0.5`),
167
- weighted sum of `V[0..pos]` → `attn[2048]`
168
- 6. `o = Wo·attn` (`[1536]`); `hidden += o` (residual)
169
- 7. `hn = RMSNorm(hidden, postAttnNorm_w)` → device `hn[H]`
170
- 8. MLP: `gate = Wg·hn`,`up = Wu·hn` (`[4608]`), `silu(gate)·up`, `down = Wd·(…)`
171
- (`[1536]`); `hidden += down` (residual) — the `fusedMlp` body, inlined
172
- After 24 layers: `RMSNorm(hidden, finalNorm_w)` → `lmHead·hidden` → `logits[V]`.
173
-
174
- ### 4.2 The grid-wide barrier (the crux)
175
-
176
- A single dispatch has no built-in cross-threadgroup barrier. Implement a
177
- **persistent-kernel software barrier** via two device atomics (`atomic_uint`):
178
- `arrive` (count) + `sense` (phase). Each barrier: `tg_leader` does
179
- `atomic_fetch_add(arrive,1)`; if it was the last (`==G`), reset `arrive=0` and flip
180
- `sense`; all threadgroups spin-read `sense` until it flips; `threadgroup_barrier`
181
- to fan back into the threadgroup. **HARD REQUIREMENT:** all `G` threadgroups must be
182
- **co-resident** or this deadlocks (a threadgroup waiting on one never scheduled).
183
- So `G` must be ≤ the GPU's max concurrent threadgroups.
184
-
185
- Pick `G` conservatively for the M4 Pro (~16–20 GPU cores). Phase 3 determines the
186
- safe `G` empirically (start `G=1`, raise until just before the largest occupancy;
187
- if any deadlock/timeout, lower). Use `dispatchThreads`/grid = `G * TGN`. Provide an
188
- env `MLX_BUN_MEGAKERNEL_G` to sweep. Document the chosen `G`.
189
-
190
- > **De-risking:** Phase 2 builds `G=1` (single threadgroup, `threadgroup_barrier`
191
- > only — NO software barrier, hidden in threadgroup memory) to get CORRECTNESS
192
- > first. Phase 3 generalizes to `G>1` with the software barrier for SPEED. Keep the
193
- > per-stage math identical between them.
194
-
195
- ### 4.3 Weight access — the repack (Phase 1)
196
-
197
- A `metal_kernel` can't take ~500 tensors. Repack at load time into a SMALL fixed set
198
- of device buffers + an offset/spec table:
199
- - `WBYTES`: one `uint8` buffer = every quantized weight's packed bytes concatenated
200
- in a fixed order (L0.q, L0.k, … L23.lm_head). Plus `SCALES` (bf16 concat),
201
- `BIASES` (bf16 concat). Plus `NORMS` (bf16 concat of all RMSNorm weights).
202
- - `META`: a `uint32`/`int32` table the kernel indexes by `(layer, matrix)` giving:
203
- byte-offset into WBYTES, scales offset, biases offset, N, K. **bits/group_size are
204
- baked as template literals per (layer,matrix) in the GENERATED kernel**, not in
205
- META (they drive the dequant template instantiation).
206
- - Because bits varies per matrix, the kernel is **generated** (like
207
- `scripts/gen-model.ts`): emit a `forwardLayer_L<i>` inline body per layer with that
208
- layer's 7 matrices' bits baked, OR a single body that `switch`es on a baked
209
- per-layer-constant array. Prefer generation: produces
210
- `src/model/generated/minicpm5-megakernel.ts` with all bits as literals.
211
-
212
- KV cache buffers: `KBUF`,`VBUF` device `[24, max_seq, 2, 128]` bf16 (L1). `pos` is a
213
- scalar input. Logits out: `[V]` f32.
214
-
215
- ---
216
-
217
- ## 5. Phased plan (execute in order; gate each)
218
-
219
- ### Phase 1 — Weight repack (foundation)
220
- **File:** `src/model/megakernel-pack.ts`.
221
- **Do:** `packMiniCpm5(model) → { wbytes, scales, biases, norms, meta, layout }`.
222
- Walk `model.layers[i]` (`.attn.{qProj,kProj,vProj,oProj}`, `.mlp.{gate,up,down}`,
223
- `.inputNorm`,`.postAttnNorm`) + `model.finalNorm`,`model.lmHead`,`model.embed`. For
224
- each quantized linear concat `.w` bytes → WBYTES, `.scales`→SCALES, `.biases`→BIASES
225
- (record offsets, N, K, bits, group in `layout`). Concat norm weights → NORMS. Use
226
- `MlxArray.rawBytes()` (eval first) to get bytes; build with `Bun`'s buffers; upload
227
- as `MlxArray.fromBytesCopy`.
228
- **Gate:** unit test — for 3 sample (layer,matrix), slice WBYTES/SCALES/BIASES at the
229
- recorded offset, rebuild a `QuantizedLinear`, run `ops.quantizedMatmul` on a fixed
230
- input, assert **byte-identical** to the original `.forward`. (`scripts/experiments/
231
- megapack-check.ts`.) This catches layout bugs before any kernel.
232
-
233
- ### Phase 2 — Single-threadgroup correct megakernel (G=1)
234
- **Files:** `src/model/megakernel-kernel.ts` (the `MetalKernel` + dispatch),
235
- `gen-minicpm5-megakernel.ts (deleted; git history)` (emits the per-layer-bits-baked SOURCE into
236
- `src/model/generated/minicpm5-megakernel.ts`).
237
- **Design:** ONE threadgroup (`G=1`, `TGN=256` threads). `hidden[H]` in threadgroup
238
- memory (6 KB). Loop layers; each stage uses `threadgroup_barrier`. GEMVs: the 256
239
- threads cover the N outputs in chunks (each thread does `ceil(N/256)` dot-products
240
- over K, dequantizing weight via `STEEL_QMM_HEADER`'s `dequantize`). RMSNorm: parallel
241
- sum-of-squares reduction in threadgroup. Attention: loop heads; per head, online
242
- softmax over `pos+1` cached keys (threads split the key positions, threadgroup
243
- reduce). Inline the `fusedMlp` math for stage 8 (gate/up into threadgroup, silu·up,
244
- down). KV read/write from `KBUF`/`VBUF`. Final: finalNorm + lmHead GEMV → logits[V]
245
- (threads split V).
246
- **Transcribe** `src/model/minicpm5.ts` op-for-op; match dtypes: accumulate in f32,
247
- round to bf16 at each op boundary to track the reference (`(T)` casts, as `fusedMlp`
248
- does). RoPE: match `ops.rope` exactly (verify with a single-layer probe).
249
- **Gate (THE correctness gate):** `megakernel-teacherforced.ts (deleted 2026-08-23; git history)`
250
- (copy `swiglu-teacherforced.ts`): drive 100 decode steps via the megakernel,
251
- teacher-forced on `goldens/minicpm5-parity.json`, KV cache in `KBUF`/`VBUF`. Require
252
- **≥98/100 argmax agreement, deterministic across 3 runs, no NaN.** If an early step
253
- mismatches, bisect by stage: add a debug output for `hidden` after layer L (env
254
- `MLX_BUN_MEGA_DUMP_LAYER=L`) and compare to the reference model's per-layer hidden
255
- (capture via the wrapper trick in `swiglu-layerN.ts (deleted; git history)`).
256
-
257
- ### Phase 3 — Persistent multi-threadgroup (G>1) for SPEED
258
- **Do:** generalize Phase 2 to `G` threadgroups. Move `hidden`,`xn`,`hn`,`q/k/v/attn/
259
- gate/up` to DEVICE scratch (shared). Implement the §4.2 software barrier (`arrive`/
260
- `sense` atomics, leader-flips-phase, spin). Split each stage's outputs across
261
- threadgroups by `tid.x` block (GEMV: threadgroup `g` does N-rows `[g*N/G,
262
- (g+1)*N/G)`). Determine safe `G`: sweep `MLX_BUN_MEGAKERNEL_G` from 1 up; the largest
263
- that (a) never deadlocks/timeouts and (b) maximizes tok/s. Add a 1-second host-side
264
- watchdog around the dispatch during the sweep to catch deadlock (kill + report).
265
- **Gate:** same teacher-forced ≥98/100 + deterministic, AND paired tok/s ≥ baseline.
266
- If `G>1` corrupts (barrier race), the barrier impl is wrong — verify the leader
267
- reset ordering and that EVERY thread (not just leaders) waits on `sense` before
268
- reading shared state, with a `threadgroup_barrier` + `mem_flags::mem_device` after.
269
-
270
- ### Phase 3 — RESULTS (2026-06-24)
271
-
272
- Multi-threadgroup persistent kernel built + correct. Findings, in order discovered:
273
-
274
- - **Occupancy sweep:** TGN must be a **power of 2** (the RMSNorm tree reduction
275
- `for(s=TGN/2;s>0;s>>=1)` silently drops lanes otherwise — TGN=192 gave KL 0.54).
276
- Smaller TGN packs more co-resident threadgroups (TGN=128 scales past where TGN=256
277
- collapses). Peak total threads ≈ 12–16k; G beyond co-residency **deadlocks** the
278
- software barrier (G=144 @ TGN=128 hung). Sweep needs a host watchdog.
279
- - **Barrier cost** (measured, `megakernel-barrier-time.ts`): ~3.8 µs/barrier @ G=48,
280
- ~145 barriers/token after fusion → ~0.5 ms = ~10% of decode. Not the dominant cost.
281
- - **bits-literal specialization REJECTED for the *naive* GEMV.** Templating on BITS
282
- and/or K bloated code/registers → lowered max co-resident G → regressed peak
283
- (164→102–156; full-K-unroll collapsed to 16). The kernel was occupancy/bandwidth-
284
- bound, not branch-bound. **Lesson: feed the bottleneck (DRAM-latency-hiding =
285
- occupancy×ILP), not instruction count.**
286
- - **THE win — port mlx's `qmv_fast` decode GEMV verbatim** (`quantized.h`
287
- qmv_fast_impl/load_vector/qdot → `qmv4`/`load_vec_tg`/`qdot_t`). Three things ours
288
- lacked: (1) `load_vector` pre-scales x so `qdot` is mask-only (no per-nibble
289
- shifts); (2) affine folds once per group as `scale*accum + sum*bias`; (3) **4 rows
290
- per simdgroup reusing a register-resident x_thread** → 4× arithmetic per x-load +
291
- 4 independent accumulators hiding FMA/load latency. Our weight layout already
292
- matches mlx's, all N,K divide the fast-path blocks → no safe-tail path. This is the
293
- M=1 decode primitive that was never ported (steel-qmm-header is the M>1 GEMM/BlockMMA
294
- path). **Jumped 0.70× → ~0.93–0.95× baseline; KL improved to 9.5e-4; removed the
295
- G≥24 down-tiling constraint (any G now correct).**
296
- - **Barrier fusion:** RMSNorm computed in-place per-threadgroup (`rms_norm_local`,
297
- every TG normalizes the full hidden — tiny, removes the rms→GEMV grid barrier +
298
- d_nrm device traffic) and SwiGLU `silu(gate)·up` folded into the down-proj's input
299
- staging. ~145 barriers/token (from ~194). Perf-neutral at G=32 (barriers already
300
- hidden) but removes device round-trips.
301
- - **Generated (layer-unrolled, constants-baked) kernel built** (`genSourceMt`, env
302
- `MLX_BUN_MEGAKERNEL_GEN=1`): per-(layer,matrix) bits/offsets/N/K + layer index L as
303
- compile-time literals, no META lookups, no runtime bits dispatch. Layer loop
304
- unrolled (sequential → registers reused, NO occupancy hit, unlike the inner-K
305
- unroll). **Correct (KL identical) but perf-NEUTRAL** (202.4 vs 202.9 tok/s) —
306
- confirms the kernel is bandwidth/barrier-bound, not lookup-bound. Kept behind the
307
- flag as the architecturally-correct path.
308
- - **Peak config:** TGN=256, G=32. Paired same-process A/B: megakernel **~202 tok/s**
309
- vs baseline **~216 tok/s = ~0.94×**. Correctness 97/100 teacher-forced (3 argmax
310
- near-ties; KL 9.7e-4), deterministic, no NaN.
311
- - **Remaining gap (~5%) is the resident-kernel structural tax** (~6 grid barriers/
312
- layer for the row-split GEMV producer→consumer chain + per-stage staging), which is
313
- largely fundamental to multi-threadgroup decode. Candidate lever to cross baseline:
314
- fold rope-q into attention to drop one barrier/layer. Needs a CLEAN-machine paired
315
- measurement (Josh-gated) — the session machine is loaded, only the ratio is trustworthy.
316
-
317
- ### Phase 4 — L2 quantized KV (IN PROGRESS 2026-06-24, spec ready)
318
- Swap `KBUF/VBUF` for the per-layer quantized-KV format. **Facts gathered:**
319
- - CPM5 `kv_config.json`: per-layer `bits ∈ {4,8}`, `group_size=64`, quantized along
320
- **head_dim=128** (so 2 groups per head per k/v). Loaded as `config.kvQuant`.
321
- - Layout (`QuantizedKVCache`, gemma4-base.ts:407): per (layer,pos) store quantized
322
- triple along the KVDIM=256 axis: `packed` uint32 `[256*bits/32]`, `scales`/`biases`
323
- bf16 `[256/64=4]`. Affine `deq = scale*q + bias` (== our weight dequant).
324
- - **mlx quantize formula to replicate EXACTLY** (`affine_quantize`, quantized.h:2432;
325
- this is what the golden used via optiq `updateAndFetchQuantized`):
326
- ```
327
- n_bins=(1<<bits)-1; eps=1e-7
328
- wmin=min(group), wmax=max(group)
329
- scale=max((wmax-wmin)/n_bins, eps); side=|wmin|>|wmax|; scale = side? scale : -scale
330
- edge = side? wmin : wmax; q0=round(edge/scale)
331
- scale = q0==0? scale : edge/q0; bias = q0==0? 0 : edge
332
- q = min(round((w-bias)/scale), n_bins) // dequant: scale*q+bias
333
- ```
334
- **Implementation steps:**
335
- 1. ✅ **DONE — quantize formula validated** (`kv-quant-formula-check.ts (deleted 2026-08-23; git history)`):
336
- the affine_quantize transcription matches `ops.quantize` to bf16 ULP (scale/bias diff
337
- 1.8e-3 @4-bit, 6e-5 @8-bit; dequant residual = expected bf16-storage rounding). The
338
- riskiest piece is de-risked. Compute scale/bias in f32, store bf16 (as mlx does).
339
- 2. **Host:** `MegakernelRunner` holds per-layer quantized KBUF/VBUF (packed/scales/
340
- biases) sized to kvSeq; bits per-layer baked (literal in the generated kernel).
341
- 3. **Kernel write:** after rope, each head's roped k/v (128 dims) → 2 groups of 64 →
342
- compute wmin/wmax (threadgroup or per-head reduce) → scale/bias → pack q → store at
343
- current pos. Replaces the bf16 `knewOut/vnewOut` emit.
344
- 4. **Kernel read (attend_simd):** dequant EVERY position incl. current (optiq quantizes
345
- then reads back, so the current entry must also round-trip) from packed+scales+biases.
346
-
347
- **Increment 1 (DONE, bf16-storage round-trip): 93/100, KL 1.49e-2, deterministic, no NaN**
348
- (`kv_rt_group<KVBITS>` after rope, `megakernel-kv-teacherforced.ts`,
349
- `MLX_BUN_MEGAKERNEL_KVQUANT=1`). Quantize formula correct. **Gap diagnosed:** optiq's
350
- `quantizedSdpaUnfused` uses `quantizedMatmulQT`, which dequants K/V **to f32 on-the-fly
351
- inside the matmul** — never storing a bf16 dequantized value. Increment 1 stores
352
- `bf16(scale·q+bias)` in the bf16 cache → ONE extra bf16 rounding optiq lacks → KL
353
- 15× the L1 path. NOT a formula bug; a storage-precision artifact of the shortcut.
354
- **Increment 2a — PARITY fix (store-int-q, the cleaner path):** the quantized int `q`
355
- (0..15 / 0..255) is EXACTLY representable in bf16 (ints ≤256 are exact). So store `q`
356
- itself in the existing bf16 kcache/vcache (lossless) + per-group scale/bias in small
357
- side buffers `[NLAYERS,KVSEQ,nKvHeads·headDim/64=4]` bf16; `attend_simd<KVBITS>` computes
358
- `scale·q+bias` in f32 on read = optiq's exact f32 dequant, ZERO extra rounding → should
359
- reach the attend_simd ceiling (~97, == L1). Much smaller than bit-packing: kv_rt_group
360
- stores `q` (not dequant) to d_k/d_v + emits scale/bias new-rows; host adds 4 scale/bias
361
- buffers + merges; attend dequants (current-pos scale/bias from device scratch, cached
362
- from the side-buffer inputs). NO memory win (q stored loosely as bf16), but full optiq
363
- parity — the Phase 4 correctness goal.
364
- **Increment 2a — DONE, store-int-q: 93/100, KL 1.38e-2, deterministic, no NaN.**
365
- Stores int q in kcache/vcache (exact in bf16) + bf16 scale/bias; `attend_simd_q<KVBITS>`
366
- dequants to f32 on read. **Found + fixed a real bug:** the current-pos scale/bias were
367
- first written to a NON-ATOMIC output (`sbNew`) read cross-threadgroup by attention →
368
- incoherent (stale) per [[metal-persistent-kernel-coherence]] → 78/100. Fix: route
369
- current-pos scale/bias through ATOMIC scratch `d_sb` (A_LD/A_ST), copy to `sbNew` only
370
- for the host merge; cached scale/bias read from the `sb` INPUT (coherent). → 93/100.
371
- **Surprise / honest correction:** 2a removed increment-1's extra bf16-rounding but
372
- landed at the SAME 93/100 — so **dequant precision was NOT the gap.** The residual
373
- (~14× L1's KL) is a genuine divergence between our quantized KV and optiq's, cause
374
- still OPEN: formula validated vs `ops.quantize` (bf16 ULP), all 24 layers' bits match
375
- kv_config, coherence fixed. Candidate causes to investigate: optiq may keep recent
376
- token(s) unquantized (a bf16 staging window) rather than quantize-on-write; or its
377
- fused quantized-SDPA accumulates differently than our dequant-then-online-softmax.
378
- **ROOT CAUSE FOUND (decisive, in-process vs our bit-exact reference):** the reference
379
- `QuantizedKVCache`+`quantizedSdpaUnfused` is bit-exact with the golden (maxDiff 0,
380
- `tests/minicpm5-kv-parity.test.ts`). Comparing the megakernel's dequantized layer-0 K
381
- to the reference's: **max diff 0.166 (≈ one quant step).** Comparing the *pre-quant*
382
- bf16 roped K (L1): **max diff 1 bf16 ULP** (9.0 vs 9.0625; 425/1536 elems > 1 ULP) —
383
- the `qmv4`-vs-`ops.quantizedMatmul` GEMV rounding difference, i.e. exactly what gives
384
- L1 its 9.7e-4. **Quantization is DISCONTINUOUS**, so that 1-ULP K difference lands on a
385
- q-level / group-min-max boundary and flips a level → a full quant-step dequant error →
386
- amplified 9.7e-4 → 1.38e-2. **NOT a quant bug — the megakernel's intrinsic ~1-ULP GEMV
387
- difference amplified by quant discontinuity. 93/100 is the L2 ceiling (as 97/100 is L1's);
388
- bit-exact L2 would need a bit-exact GEMV, defeating qmv4.** Gate L2 by KL + agreement
389
- (no-oracle, Lab class), not the bit-exact golden. Scripts: `megakernel-kv-{cmpkv,cmpl1,perstep}.ts`.
390
- **Increment 2b — MEMORY win (later):** bit-pack `q` into uint32 (flat KPACK/VPACK,
391
- per-layer literal offsets) to actually shrink 4-bit KV. Same logits as 2a. Deferred.
392
- **Build on the GENERATED kernel** (`genSourceMt`, `MLX_BUN_MEGAKERNEL_GEN`): the
393
- per-layer KV `bits` is fixed on disk (`kv_config.json`), so bake it as a compile-time
394
- literal per layer (same "pre-determined" principle as the weight bits) — the quantize/
395
- dequant helpers are `template<int KVBITS>`, and each unrolled layer emits its literal
396
- `KVBITS` (and `group_size=64`). No runtime kv-bits table lookup. This is why Phase 4
397
- makes the generated path the primary one (the looped kernel would need a runtime kv
398
- table). Phase 4 therefore promotes `genSourceMt` from flag-gated to the L2 default.
399
- **Gate:** `goldens/minicpm5-kv-parity.json` + `…-kv-logits-step*.bin` (already exist),
400
- teacher-forced agreement + KL. Defer until L1 solid (L1 IS solid — parity, Phase 3).
401
-
402
- ### Phase 5 — Integration + ship
403
- **Do:** `MiniCPM5Model.decodeStep(tokenId, pos) → logits` using the megakernel when
404
- `MLX_BUN_MEGAKERNEL=1`; allocate the packed buffers + KV scratch once at model load
405
- (or first decode), reuse across steps. Wire into `src/generate.ts` decode loop
406
- (n23–n44 in `docs/dag/training-inference-map.html`) — the megakernel REPLACES the
407
- per-step `forwardHidden→logitsFromHidden` at decode (M=1) only; prefill unchanged.
408
- Guard: bf16 KV (L1) only, no adapters, no vision. Add `--megakernel` CLI flag.
409
- **Gate:** `bun test tests/minicpm5-*.test.ts` green (off); a new
410
- `tests/minicpm5-megakernel.test.ts` (teacher-forced gate, env-gated in the pattern
411
- of the old `tests/perf-kernel-oracle.test.ts` — deleted 2026-07-05 with its kernel,
412
- unified-engine-frontier-plan.md §6; the envelope-gate methodology stands on its
413
- own); paired decode tok/s A/B logged. Update
414
- `STATUS.md` + `benchmarks/RESULTS.md` (preflight-gated `scripts/bench-serve.ts all` for the
415
- quotable number — `[[dirty-machine-numbers-are-garbage]]`).
416
-
417
- ---
418
-
419
- ## 5.5 Methodology for the next megakernel (learned 2026-06-24)
420
-
421
- **Copy mlx's exact code verbatim into one file → prove bit-identical (per-op harness,
422
- maxDiff 0 vs `ops.*`) → static-analyze that one file → THEN optimize incrementally,
423
- re-running the bit-exact harness after each step.** Why: a hand-port of `qmv_fast` was
424
- 1-ULP-correct but NOT bit-identical to what `ops.quantizedMatmul` actually dispatches
425
- for **4-bit M=1** (it uses a different kernel/accumulation order — `qmv4-bitexact-check.ts`
426
- shows 8-bit bit-exact, 4-bit off by exactly 1 bf16 ULP on 12/256 rows, e.g. 1552 vs 1544
427
- = adjacent bf16 values). Invisible at L1 (9.7e-4) but quantization's DISCONTINUITY
428
- amplifies it into the L2 gap. A provably-identical starting copy + per-step gating would
429
- catch any divergence the moment it's introduced, not 24 layers + a quant step downstream.
430
- Steps: (1) copy mlx `quantized.h` GEMV kernels INCLUDING the per-(bits,M,N,K) dispatch
431
- (`qmv_fast`/`qmv`/`qmv_quad`), `rms_norm`, `rope`, `sdpa`; (2) per-op maxDiff-0 harness
432
- before fusing; (3) analyze the single fused file; (4) optimize with the harness as a
433
- regression gate. This megakernel was built no-oracle-first (Lab-style, KL-gated, NOT bit-exact by design),
434
- so its 1-ULP/op drift is within contract — bit-exact is a focused REBUILD, not a patch
435
- (and may cost qmv4's speed if mlx's real 4-bit kernel is the slower qmv_quad).
436
-
437
- ## 6. Pitfalls (learned this session — do not relearn)
438
-
439
- - **Chained custom kernels corrupt under MLX's planner.** The ENTIRE reason for one
440
- kernel. Don't reintroduce intermediate custom-kernel boundaries.
441
- - **`templateDtypes:{T:x.dtype}` is required** or `T` is undeclared (Metal compile
442
- error). **`.template store<…>`** needed for dependent names when `T` is a template
443
- param. Bake dims as `templateInts` / read `x_shape[0]`, don't pass a runtime
444
- `shape` input array (extra lazy nodes).
445
- - **Match the reference's bf16 rounding op-for-op** (round to `T` at each boundary) —
446
- `metal::precise::exp`/`tanh` are the only residual vs the reference; that's
447
- no-oracle (Lab-gated) territory and fine. Do NOT compute everything in f32 then round once (drifts).
448
- - **`atomicOutputs:true` + `initValue:0`** for any atomic-accumulated output.
449
- - **GQA:** 16 q-heads, 2 kv-heads, `nRep=8` → q-head `h` reads kv-head `h/8`.
450
- - **`rope_theta=5e6`, not 10000.** scale `=128^-0.5`. No MiniCPM residual scaling.
451
- - **Software barrier deadlocks if `G` > co-resident threadgroups.** Start G=1.
452
- - **`detachLeaf` ≠ `evalAll`:** eval alone doesn't break the graph; only a fresh
453
- `fromBytesCopy` leaf does. (Relevant only if you keep any boundary.)
454
- - **`MlxArray` has `.astype`, not `ops.astype`; `ops.reshape`, not `.reshape`.**
455
- - Oracle venv for any golden regen: `/Users/joshrossi/Code/mlx-lm/.venv/bin/python`,
456
- `MLX_BUN_ORACLE_VENV=…`, `HF_HUB_DISABLE_XET=1`. (Goldens already exist; only
457
- regen if you change the prompt.)
458
-
459
- ---
460
-
461
- ## 7. File manifest (what you create)
462
-
463
- ```
464
- src/model/megakernel-pack.ts # Phase 1: weight repack + layout table
465
- gen-minicpm5-megakernel.ts (deleted; git history) # Phase 2: emit per-layer-baked kernel SOURCE
466
- src/model/generated/minicpm5-megakernel.ts # (generated) the kernel SOURCE + bits
467
- src/model/megakernel-kernel.ts # Phase 2/3: MetalKernel + decodeMegakernel()
468
- megapack-check.ts (deleted 2026-08-23; git history) # Phase 1 gate
469
- megakernel-teacherforced.ts (deleted 2026-08-23; git history) # Phase 2/3 correctness gate
470
- megakernel-perf.ts (deleted 2026-08-23; git history) # Phase 3/5 paired tok/s A/B
471
- tests/minicpm5-megakernel.test.ts # Phase 5 CI gate (env-gated)
472
- ```
473
- Modify: `src/model/minicpm5.ts` (add `decodeStep` megakernel branch),
474
- `src/generate.ts` (decode-loop branch), `src/cli.ts` (`--megakernel`).
475
-
476
- ## 8. Order of operations (the literal checklist)
477
-
478
- 1. [ ] Phase 1: `megakernel-pack.ts` + `megapack-check.ts` → byte-identical gate green.
479
- 2. [ ] Phase 2: generator + `G=1` kernel; `megakernel-teacherforced.ts` ≥98/100,
480
- deterministic ×3, no NaN. Bisect by layer if it fails.
481
- 3. [ ] Phase 3: `G>1` + software barrier; sweep `MLX_BUN_MEGAKERNEL_G`; same
482
- correctness gate + tok/s ≥ baseline. Record chosen `G`.
483
- 4. [ ] Phase 4 (optional): L2 quantized KV vs kv-parity golden.
484
- 5. [ ] Phase 5: wire into `decodeStep`/`generate.ts`, env+CLI gate, CI test,
485
- benchmark, update `STATUS.md`/`RESULTS.md`/memory
486
- `[[cpm5-generate-and-fuse-inference]]`.
487
-
488
- **Done = one resident dispatch computes the MiniCPM5 decode forward, ≥98/100
489
- teacher-forced, deterministic, ≥ baseline tok/s, default-off, CI-gated.**