mlx-bun 0.4.0 → 1.0.0

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