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,602 +0,0 @@
1
- # DiffusionGemma port — plan & parity strategy
2
-
3
- Porting **DiffusionGemma-26B-A4B-it** (`mlx-community/diffusiongemma-26B-A4B-it-OptiQ-4bit`,
4
- 14 GB, model_type `diffusion_gemma`) into mlx-bun, holding **L2 parity with
5
- mlx-optiq**. This is the first **non-autoregressive** model in the codebase:
6
- it fills a fixed 256-token *canvas* and un-masks it over a few denoising steps
7
- rather than generating left-to-right. mlx-optiq docs:
8
- https://mlx-optiq.com/docs/diffusion
9
-
10
- > Status doc: STATUS.md. Durable phase log: PLAN.md. This file is the design +
11
- > phase plan for this one feature; promote the phase checklist into PLAN.md once
12
- > approved.
13
-
14
- ## The parity oracle (resolved 2026-06-24)
15
-
16
- **Stock mlx-lm/mlx-vlm cannot load this model** — there is no bf16 ancestor in
17
- the usual L1 sense. OptiQ ships a *vendored, dependency-free decoder*, so **the
18
- oracle IS mlx-optiq itself**. The mixed-precision quant in the checkpoint is the
19
- model's native weight format (not an optional L3 layer), so **matching optiq's
20
- output = L2 = the correctness floor here.**
21
-
22
- - Reference is now installed: `mlx-optiq 0.2.7` in
23
- `/Users/joshrossi/Code/mlx-lm/.venv` (upgraded from 0.2.1; the diffusion
24
- decoder needs ≥0.2.3). `mlx`/`mlx-lm`/`mlx-metal` stayed pinned at
25
- **0.31.2 / 0.31.3 / 0.31.2**, so the existing parity oracles (Gemma/CPM/Qwen)
26
- are **unaffected** — verified by `pip install --dry-run`.
27
- - Reference source (read these; they are the contract):
28
- - Model: `optiq/vlm/_mlxvlm/models/diffusion_gemma/`
29
- — `language.py` (802 L, the core: Attention, Router/Experts MoE,
30
- DecoderLayer, SelfConditioning, DecoderModel `_embed_canvas` /
31
- `_make_decoder_masks`, EncoderModel vision merge), `diffusion_gemma.py`
32
- (178 L, top `Model` + `sanitize` + `quant_predicate`), `config.py` (87 L),
33
- `processing_diffusion_gemma.py` (137 L).
34
- - Engine: `optiq/vlm/_mlxvlm/generate/diffusion.py` (1148 L) — canvas init,
35
- linear temperature schedule (`t_min`→`t_max`), the un-mask loop, the two
36
- samplers (`confidence-threshold` default in OptiQ, `entropy-bound` model
37
- default), self-conditioning feedback, stability/EOS handling.
38
- - OptiQ surface: `optiq/vlm/diffusion_gemma/{loader,generate,lora,
39
- calibration,convert,eval_adapter}.py` (load/generate/train entry points).
40
- - Run the reference with `/Users/joshrossi/Code/mlx-lm/.venv/bin/python`. Golden
41
- dumps for parity tests go through `optiq.vlm.diffusion_gemma.{load,generate}`.
42
-
43
- ## What the checkpoint actually is (from config.json + weight map)
44
-
45
- - `architectures: ["DiffusionGemmaForBlockDiffusion"]`, `canvas_length: 256`,
46
- `model_type: diffusion_gemma`, bf16 base, OptiQ mixed-precision quant
47
- (group_size 64, bits ∈ {4,8} per-tensor; 8-bit budget on **early-layer
48
- attention + routers**, 4-bit on dense MLPs — inverse of the hand-coded recipe).
49
- - **Language tower** `model.decoder`: 30 layers, hidden 2816, 16 heads. Each
50
- layer runs a **parallel dense `mlp` + routed MoE** branch (128-expert top-8,
51
- `router.proj` + `experts.{gate_up,down}_proj`) summed via 7 RMSNorms + a
52
- `layer_scalar` — conceptually the gemma4 parallel-branch idea, but DIFFERENT
53
- geometry/norm-placement than our gemma4-26B (verify, don't assume reuse).
54
- Attention is **scale=1.0, QK/V-norm post-proj/pre-RoPE, NO attention softcap**
55
- (only `final_logit_softcapping=30.0`, fp32); sliding layers hd=256/kv=8, full
56
- layers (5,11,17,23,29) hd=512/kv=2 + partial-rotary 0.25. Plus
57
- `model.decoder.self_conditioning` (gate/up/down MLP) — diffusion-specific.
58
- - **Vision tower** `model.encoder.vision_tower`: 27-layer SigLIP
59
- (`patch_embedder.input_proj` + `encoder.layers.N.{self_attn,mlp}`). We already
60
- have a SigLIP port in `src/vision/siglip.ts` (16-layer for e4b) — likely
61
- reusable with a layer-count/config delta. image-text-to-text.
62
- - Generation config: `max_denoising_steps: 48`, `max_new_tokens: 256`,
63
- `confidence_threshold: 0.005`, `t_min: 0.4`, `t_max: 0.8`,
64
- `stability_threshold: 1`, default `EntropyBoundSamplerConfig(entropy_bound=0.1)`,
65
- `eos_token_id: [1, 106, 50]`.
66
- - New tokenizer + chat template (`<|turn>role`, `<|channel>thought`,
67
- `<|tool>`/`<tool|>` blocks, `<|image|>`). The canvas is initialized to
68
- **uniform-random token IDs** (`randint(0, vocab_size)`), NOT a mask token —
69
- no discrete mask-token path is used at inference (dossier §9.1).
70
-
71
- ## Architecture deltas vs what we already have
72
-
73
- (Refined after D0 — see the dossier for exact shapes/math.)
74
-
75
- | Piece | Reuse / new |
76
- |---|---|
77
- | MoE Router + Experts (128-expert, top-8, `SwitchLinear` `_gather_sort`) | **Reuse/adapt** gemma4.ts — but router norm is **pre**-projection + `scale`·`hidden**-0.5`, weights `*per_expert_scale`; verify against gemma4 |
78
- | Attention — QK/V-norm **post-proj/pre-RoPE**, **scale=1.0, NO attn softcap**, partial-rotary 0.25 on full layers | **Adapt** gemma4.ts Attention (geometry differs: sliding hd=256/kv=8, full hd=512/kv=2; full layers reuse k as v) |
79
- | SigLIP vision tower (27 layers, hidden 1152, patch 16) | **Reuse** src/vision/siglip.ts with config delta + `MultimodalEmbedder` merge |
80
- | Quantized linear / mixed-precision load | **Reuse** loader; read per-tensor bits from `config.quantization` map (not just the predicate) |
81
- | Tied LM head via quantized `embed_tokens.as_linear()` | **New-ish** — need quantized transpose-matmul + `quantized_matmul(...,transpose=False)` for soft embeds |
82
- | `_embed_canvas` (hard embed + soft self-cond signal) | **New** |
83
- | Bidirectional / block decoder masks (`_make_decoder_masks`) | **New** — NOT causal; **the crux / highest bug-risk** |
84
- | `SelfConditioning` MLP + per-step soft-embedding feedback | **New** |
85
- | Denoising engine (uniform-random canvas init, linear temp schedule, un-mask loop, 2 samplers, stability/EOS) | **New** — a whole second generation path |
86
- | Cache mix: `KVCache` (full) + `RotatingKVCache(1024)` (sliding); decoder reads encoder K/V from cache state | **Reuse/adapt** — `_cache_offset`/`_cache_state` helpers |
87
- | Tokenizer + chat template + image processor (variable soft-token budget) | **New** |
88
- | ~~Canon depthwise-conv~~ | **N/A — absent in checkpoint** |
89
-
90
- ## Phase plan (exit criteria)
91
-
92
- ### D0 — Recon & reference dossier `[x]` (done 2026-06-24)
93
- Dossier appended below ("Reference dossier (D0)"). Headline corrections to the
94
- pre-recon assumptions: **no Canon/depthwise-conv tensors exist** (pure
95
- transformer); the **LM head is tied** (`embed_tokens.as_linear()`, a 4-bit
96
- `QuantizedEmbedding`); shapes are **hidden 2816 / 30 layers / 16 heads** (NOT the
97
- gemma4-26B geometry); each layer runs a **parallel dense-MLP + MoE** branch with
98
- **7 RMSNorms + a `layer_scalar`**; attention is **scale=1.0 with no attention
99
- softcap** (only `final_logit_softcapping=30.0`, fp32); the canvas is initialized
100
- to **uniform-random token IDs**, not a mask token. Full detail + 12 risk items
101
- below.
102
-
103
- #### Original D0 brief
104
- Read the entire reference and write it down so D1–D3 are mechanical. Done as a
105
- **parallel-reader workflow** (see Orchestration). Produces, appended to this doc:
106
- a component-by-component spec of `language.py` + `diffusion_gemma.py`, the
107
- weight-name → module map audited against the real checkpoint (0 missing / 0
108
- unused), the exact `_make_decoder_masks` construction, the denoising-loop
109
- pseudocode, both sampler algorithms, the self-conditioning data flow, and the
110
- tokenizer/chat-template/mask-token facts.
111
- **Exit:** dossier complete; weight-name audit clean; golden-dump harness sketched.
112
-
113
- ### D1 — Single-forward parity (the static graph) `[x]` (done 2026-06-24 — BIT-EXACT)
114
- **DONE.** `src/model/diffusion-gemma.ts` (`DiffusionGemmaModel`) + factory detection
115
- (`isDiffusionGemmaConfig`; serving still throws "wired in D3" — the AR `RuntimeModel`
116
- union is left untouched so the 96 script-callers don't break). One full forward over
117
- the real 14 GB checkpoint — **encoder prefill → bidirectional decoder canvas pass
118
- (`_make_decoder_masks`) → parallel dense-MLP + 128-expert MoE → SelfConditioning →
119
- tied 4-bit head → fp32 softcap** — is **BIT-EXACT vs the optiq golden**: argmax 256/256,
120
- maxDiff 0.0, relRMSE 0.0, meanKL 0.0 (`tests/diffusion-parity.test.ts`,
121
- `MLX_BUN_TEST_DIFFUSION=1`). Per-stage sub-gates (enc_hidden / dec_hidden / presoftcap /
122
- per-layer / layer-0 attn·dense·MoE) all 0.0 (`scripts/experiments/diffusion-{stage,layer,l0}-diag.ts`,
123
- golden dumps in `scripts/oracle/gen-diffusion-golden.py`).
124
- - **The one bug found + fixed (per-model parity gotcha):** the Router pre-projection
125
- norm must be the literal two-step `rms_norm(x, None, eps) * scale * hidden**-0.5`,
126
- **NOT** the gemma4-style fold `rms_norm(x, scale*hidden**-0.5)`. The fold changes the
127
- bf16 intermediate rounding → shifts the softmax routing weights by ~0.01 → 1.7% on the
128
- expert output → 12.7% by the encoder output (argmax mostly survived, masking it). Same
129
- experts selected (0/21 set mismatch); only the *weights* drifted. Copy-verbatim
130
- methodology localized it: attn / h_mid / dense-MLP were already 0.0, only the MoE branch
131
- diverged.
132
- - Vision merge deferred to D3 (golden is text-only; the 27-layer SigLIP tower is present
133
- in the checkpoint and wired via `parseSiglipConfig` when D3 lands).
134
-
135
- ### D1 — Single-forward parity (the static graph) — original brief `[x]`
136
- `src/model/diffusion-gemma.ts` + config detection (`support.ts`
137
- `isDiffusionGemmaConfig`) + factory wiring. Port embed_canvas, DecoderLayer
138
- (attn + dense MLP + MoE parallel + softcap), SelfConditioning, the bidirectional
139
- decoder masks, vision feature merge (reuse siglip), final norm + head.
140
- Sub-gates (model-free where possible, then on real weights): vision features
141
- bit-exact; one decoder layer bit-exact; MoE block bit-exact; self-conditioning
142
- bit-exact.
143
- **Exit:** **one full forward over the real 14 GB checkpoint produces logits
144
- bit-exact vs the optiq reference** (Python golden dump through
145
- `optiq.vlm.diffusion_gemma`), at a fixed canvas state. This is the L2 oracle for
146
- the graph; where the quantized GEMV is 1-ULP off (the known megakernel finding),
147
- gate by KL + argmax-agreement, not the bit-exact golden.
148
-
149
- ### D2 — Denoising generation engine `[x]` (done 2026-06-24 — token-for-token)
150
- **DONE.** `src/diffusion/diffusion-generate.ts` (`diffusionGenerate`): prefill→cache
151
- reuse, linear temp schedule, the un-mask loop, BOTH samplers (confidence-threshold =
152
- OptiQ public default, entropy-bound = engine default), self-conditioning feedback,
153
- stability/EOS, canvas block loop. **Token-for-token parity vs the optiq engine at
154
- temp 0 on a fixed seed for BOTH samplers** (`tests/diffusion-gen-parity.test.ts`,
155
- `MLX_BUN_TEST_DIFFUSION=1`; golden `scripts/oracle/gen-diffusion-gen-golden.py`):
156
- confidence 17 tok / 7 steps, entropy 15 tok / 48 steps — both `.tokens`/`.steps`/
157
- `.finishReason` identical.
158
- - **RNG parity (the crux) SOLVED:** bound `mlx_random_randint` (+`mlx_random_seed`,
159
- `mlx_cummax`, `logicalNot`/`equal`/`all`/`anyAxis`/`lessEqual`/`itemBool`) in
160
- `ffi.ts`/`ops.ts`. `ops.randint(key=null)` threads the GLOBAL mlx key, so seeding +
161
- calling randint in the same ORDER as the reference (1 canvas init + 1 re-noise per
162
- non-final step) reproduces every draw bit-for-bit. Verified standalone: 0/256
163
- mismatch across consecutive draws.
164
- - **Three bugs found + fixed:** (1) `processed = logits / schedT` must be a **division**,
165
- not reciprocal-multiply — the f32 rounding differs and the 0.9 confidence threshold
166
- is a hard cutoff that flips acceptance on 1 ULP, diverging the trajectory (was 8 vs 7
167
- steps). (2) `history.push` needs an **independent copy** (add-zero) — a reshape/view
168
- aliases the argmax buffer and reads freed data after disposal → false "stable" →
169
- premature stop. (3) **The as-loaded oracle has `model.config.generation_config = None`**,
170
- so `stable_and_confident` is a NO-OP (entropy runs all 48 steps) and the eos set is the
171
- tokenizer's `stopping_criteria` = **{1, 106}** (50 is NOT eos despite generation_config.json).
172
- L2 parity = match the oracle as it RUNS: stable-stop OFF unless explicitly configured.
173
- - Multi-block continuation (`extendPrefill`) is wired but only single-block is gated
174
- (max_tokens 64 ≤ canvas). temperature>0 (categorical) + image input deferred to D3.
175
-
176
- ### D2 — Denoising generation engine — original brief `[x]`
177
- `src/diffusion/` (or `src/generate-diffusion.ts`): canvas init, linear
178
- temperature schedule, the un-mask loop, `confidence-threshold` (OptiQ default,
179
- the L2 target) AND `entropy-bound` (model default) samplers, self-conditioning
180
- feedback, stability/EOS termination. Deterministic harness (same canvas init →
181
- same final tokens), parity vs optiq `generate(...)`.
182
- **Exit:** full deterministic generation matches optiq **token-for-token** for
183
- both samplers on a fixed seed/prompt set (text-only first). Step-count and
184
- final-canvas match.
185
-
186
- ### D3 — Serving + CLI + image input `[x]` (DONE 2026-06-24 — text + image, parity-exact)
187
- **IMAGE-TEXT-TO-TEXT DONE — token-for-token parity.** A DEDICATED DiffusionGemma vision
188
- tower (`src/vision/diffusion-vision.ts`, `DiffusionVisionTower`) — its OWN module, NOT a
189
- reuse of the e4b SigLIP path (they're separate models: e4b ships a bf16
190
- `optiq_vision.safetensors` sidecar, DiffusionGemma's vision is inline-quantized). A
191
- parity-exact op-for-op port of optiq's gemma4 `VisionModel` at the DiffusionGemma geometry
192
- (hidden 1152, head_dim 72, 16 heads, 27 layers, position_embedding_size 10240, standardize ON).
193
- Wired through the encoder vision merge (`#embedInputsVision` masked-scatter + `#encoderVisionMask`
194
- causal|overlay) into the denoising engine; `preprocess()` (resize/rescale) + `spliceImageTokens`
195
- build the prompt. **Verified end-to-end** (`tests/diffusion-vision.test.ts`): spliced ids EXACT
196
- vs optiq + generation TOKEN-FOR-TOKEN ("This is a solid gray square." on grad-768) + **served
197
- live** via the OpenAI vision API. Server branch in `handleChat` (diffusion gets its own vision
198
- lane; v1 = single image); `generate()` threads `visionPixels`.
199
- - **Three vision bugs found + fixed (copy-verbatim per-stage diffing, dequant→bit-exact):**
200
- (1) **input_proj is QUANTIZED (uint32 weight)**, so the reference's
201
- `patches.astype(input_proj.weight.dtype)` TRUNCATES the `2*(x-0.5)` patches to integers
202
- (mostly 0) before the matmul — a trained-in quirk e4b never hit (its input_proj is bf16). The
203
- patchembed went bit-exact once replicated. (2) head_dim 72 isn't a fused-SDPA size; the
204
- reference `ensure_fused_sdpa` PADS to 80 (zeros) and slices back — replicating it dropped the
205
- per-layer error from 0.28% to ~bf16-noise. (3) `down_proj` is plain bf16 (not quantized) —
206
- a `VisionLinear` that is quantized-or-plain. Residual: ~2.3% feature relRMSE from 27-layer bf16
207
- accumulation (the e4b-class tier), which the denoising is robust to → identical tokens.
208
-
209
- ### D3 — Serving + CLI + image input — earlier note `[x]`
210
- **TEXT serving DONE + verified live.** `createModel` returns `DiffusionGemmaModel`
211
- (added to the `RuntimeModel` union with the AR-only surface — `forwardHidden`/`forward`/
212
- `generate`/`forwardEmbeddings` — as throwing stubs, real `loraState`/`loraTargets`/
213
- `prefixBase`/`makeCache`, so the 96 AR script-callers still typecheck; baseline held at
214
- 117). `generate()` detects the model and routes to the denoising engine
215
- (`generateDiffusionInner`), preserving the `Generation`/`GenerateStats` contract so the CLI
216
- and server stream it through the existing token machinery. The gateway keeps diffusion on
217
- the serial lane (`willBatch` returns false for it). CLI (`mlx-bun generate`) +
218
- `greedyDecodeBitExact` bypass wired (`src/eval/runner.ts`). **Verified live** against a
219
- running server (`mlx-bun serve diffusiongemma --port 8899`): OpenAI chat (stream +
220
- non-stream), Anthropic `/v1/messages`, `/v1/models`, single- AND multi-block — all coherent
221
- ("The capital of France is Paris.", correct primary-colors answer). Gate:
222
- `tests/diffusion-serving.test.ts` (4/4, `MLX_BUN_TEST_DIFFUSION=1`). No AR regression
223
- (instanceof-guarded branches; typecheck baseline preserved).
224
- - **Streaming UX:** v1 runs the engine to completion then yields tokens (the existing SSE
225
- machinery emits them as deltas). True per-block intra-stream + temperature>0 (categorical)
226
- are noted follow-ups.
227
- - **REMAINING (image-text-to-text):** the 27-layer SigLIP tower IS in the checkpoint
228
- (`parseSiglipConfig` handles it) but the diffusion vision path is NOT wired yet — needs the
229
- encoder vision merge (`_embed_inputs` masked_scatter + `_vision_block_overlay` bidirectional
230
- overlay), the `<|image|>`→`boi+image_token*N+eoi` splice + `mm_token_type_ids`, the SigLIP
231
- weight-name adaptation (`.linear` suffix, `patch_embedder`), and an image-text-to-text golden.
232
-
233
- ### D3 — Serving + CLI + image input — original brief `[~]`
234
- Wire DiffusionGemma into `generate.ts`/`cli.ts`/`server.ts` as a model that
235
- routes to the diffusion engine instead of the AR loop. Decide streaming
236
- semantics (canvas isn't left-to-right — stream per denoise-step or on
237
- stabilization). Image input through the existing vision path. Onboarding/registry
238
- entries; supported-model lists.
239
- **Exit:** `mlx-bun serve` + `mlx-bun generate` answer text **and** image prompts;
240
- output matches optiq `generate`; HTTP test green; no regression to AR models.
241
-
242
- ### D4 — Performance (L3, optional, measured) `[ ]`
243
- `confidence-threshold` is already the fast path (optiq: ~58 tok/s code / ~9
244
- prose). Measure mlx-bun tok/s with `scripts/bench-serve.ts all`; fuse kernels only if a number
245
- says so. KV holds only the prompt (no per-step growth → no KV-quant needed).
246
- **Exit:** quotable tok/s within range of optiq on this machine; any fused kernel
247
- gated by KL + the 6-task quality suite (no bit-exact ancestor for novel fusions).
248
-
249
- ### D5 — Diffusion-native LoRA `[x]` (done 2026-06-24 — trains, loss decreases)
250
- **DONE.** `src/train/diffusion-lora.ts` (`trainDiffusionLora` + `diffusionLoss`): port of
251
- optiq's denoising-objective LoRA (NOT AR cross-entropy). Corrupt the target canvas to a
252
- random noise level t∈[t_min,t_max] (uniform-random token per position with prob t), one
253
- encoder+decoder forward, CE on the CORRUPTED positions. LoRA mounts on the decoder blocks
254
- (DEFAULT_LORA_KEYS = attn q/k/v/o + dense MLP gate/up/down; the `loraTargets` map skips
255
- full-layer v_proj), reusing the existing training stack (`buildTrainableLora`/`ValueAndGrad`/
256
- `AdamW`/`saveAdapter`). **The MoE backward works** by `stop_gradient`-ing the router top-k
257
- indices (identity in the forward → D1/D2 stay bit-exact; lets the gather_qmm vjp differentiate
258
- only the activations). Autograd flows end-to-end through BOTH passes (encoder cache sliceUpdate
259
- + decoder cross-attention concat + MoE). **Verified** (`tests/diffusion-lora.test.ts`): loss
260
- 3.91 → 1.20 over 30 iters on one example, all finite, and the trained adapter changes the canvas
261
- logits (maxDiff 22.4). Mountable via the existing AdapterManager (`lora_a`/`lora_b` layout).
262
-
263
- ### D5 — Diffusion-native LoRA — original brief `[x]`
264
- Port `optiq/vlm/diffusion_gemma/lora.py` (the denoising objective, not AR
265
- cross-entropy) into the training stack. Deferred unless prioritized.
266
- **Exit:** a LoRA trains end-to-end on `{prompt, completion}` data, loss
267
- decreases, adapter mounts and changes generation.
268
-
269
- ## Risks / open questions (resolve in D0)
270
-
271
- - **Bidirectional masks** are the highest-risk port — block-diffusion attention
272
- is not causal; `_make_decoder_masks` + `_vision_block_overlay` define it.
273
- - **"Canon depthwise-conv layers"** (per docs) — no conv tensors appeared in the
274
- quant config scan; confirm whether this checkpoint has them and where.
275
- - **Self-conditioning ordering** in the step loop (when the previous prediction
276
- is fed back) must match exactly or generation diverges.
277
- - **mask_token_id** + canvas init RNG must match optiq for deterministic parity.
278
- - **Vision tower delta**: 27 layers here vs 16 in the e4b siglip — confirm config
279
- fields, pooling, and whether the same `SiglipVisionTower` covers it.
280
- - **Streaming UX**: define how a canvas maps to token-stream SSE (D3 decision).
281
-
282
- ## Scope (confirmed with Josh 2026-06-24)
283
-
284
- - **Text + image together.** The 27-layer vision merge is built into D1 from the
285
- start; v1 ships image-text-to-text. (Heavier first-parity, but the model is
286
- natively multimodal and that's the target.)
287
- - **D5 LoRA is IN scope** — port optiq's denoising-objective `train_diffusion_lora`
288
- after the inference path (D1–D4) is green.
289
- - **Generation parity is the bar**, not serving throughput — D4 is optional/L3.
290
-
291
- ## Orchestration (sub-agents / workflows)
292
-
293
- - **D0** = one recon **workflow**: parallel Explore/reader agents, one per
294
- reference file group — (a) `language.py`, (b) `diffusion_gemma.py`+`config.py`,
295
- (c) `generate/diffusion.py`, (d) `processing`+tokenizer+chat_template, (e)
296
- `loader`/`generate`/`lora`/`convert`. Each returns a structured spec; a
297
- synthesis agent merges into the "Reference dossier" section here; a
298
- weight-audit agent diffs module names vs the real `*.safetensors` index.
299
- - **D1** = pipeline per component (vision → decoder-layer → MoE →
300
- self-conditioning → full-forward), each stage verify-gated against a Python
301
- golden dump before the next builds on it.
302
- - **D2/D3** = mostly serial (single engine), with an adversarial parity-verify
303
- agent per sampler.
304
-
305
- ---
306
-
307
- ## Reference dossier (D0)
308
-
309
- This dossier consolidates six independent reverse-engineering passes over OptiQ's vendored DiffusionGemma (`optiq.vlm._mlxvlm.models.diffusion_gemma.*`, `optiq.vlm.diffusion_gemma.*`). It is the porting contract for the mlx-bun TypeScript/MLX implementation. Class/function/tensor names and constants are quoted as found; items the reports flagged as unverified are called out in §9.
310
-
311
- ### 1. Weight schema & quant map
312
-
313
- **Checkpoint analyzed:** `DiffusionGemma-26B-A4B-it OptiQ-4bit`, ~14 GB safetensors (mixed 4/8-bit; BF16 full-precision ≈ 26–28 GB). OptiQ affine quant, `group_size=64` throughout. Sharded `model*.safetensors`, loaded lexicographically via `mx.load()` and kept lazy until eval.
314
-
315
- **Top-level config (`TextConfig` / `ModelConfig`):**
316
-
317
- | Param | Value |
318
- |---|---|
319
- | `model_type` (text / top) | `diffusion_gemma_text` / `diffusion_gemma` |
320
- | `vocab_size` | 262144 |
321
- | `hidden_size` | 2816 |
322
- | `intermediate_size` (dense MLP) | 2112 |
323
- | `moe_intermediate_size` (expert) | 704 |
324
- | `num_hidden_layers` | 30 |
325
- | `num_attention_heads` | 16 |
326
- | `num_key_value_heads` | 8 (sliding) |
327
- | `num_global_key_value_heads` | 2 (full_attention) |
328
- | `head_dim` | 256 (sliding) |
329
- | `global_head_dim` | 512 (full_attention) |
330
- | `hidden_activation` | `gelu_pytorch_tanh` |
331
- | `rms_norm_eps` | 1e-6 |
332
- | `max_position_embeddings` | 262144 |
333
- | `sliding_window` | 1024 |
334
- | `final_logit_softcapping` | 30.0 |
335
- | `num_experts` | 128 |
336
- | `top_k_experts` | 8 |
337
- | `attention_bias` | False |
338
- | `tie_word_embeddings` | True |
339
- | `use_bidirectional_attention` | `"vision"` |
340
- | `pad/eos/bos_token_id` | 0 / 1 / 2 |
341
- | `canvas_length` (ModelConfig) | 256 |
342
- | `boi_token_id` / `eoi_token_id` / `image_token_id` | 255999 / 258882 / 258880 |
343
- | `eos_token_id` (ModelConfig, list) | `[1, 106]` |
344
-
345
- **Unique tensor templates (22) and per-tensor quant:**
346
-
347
- Decoder embeddings:
348
- - `model.decoder.embed_tokens.weight` — (262144, 2816), **4-bit**. Tied LM head (no `lm_head.weight`; logits via `embed_tokens.as_linear()`).
349
-
350
- Per decoder layer `model.decoder.layers.N` (N=0..29):
351
- - `self_attn.q_proj` / `k_proj` / `o_proj` — **8-bit** (all 30 layers).
352
- - `self_attn.v_proj` — **8-bit**, present in **25 of 30** layers (absent in full_attention layers 5,11,17,23,29 — those reuse k as v).
353
- - `self_attn.q_norm.weight` / `k_norm.weight` — unquantized RMSNorm; `v_norm` — unquantized RMSNormNoScale.
354
- - `input_layernorm`, `post_attention_layernorm`, `pre_feedforward_layernorm`, `post_feedforward_layernorm`, `post_feedforward_layernorm_1`, `pre_feedforward_layernorm_2`, `post_feedforward_layernorm_2` — all unquantized RMSNorm (7 norms/layer).
355
- - `mlp.gate_proj` / `up_proj` / `down_proj` — **8-bit**.
356
- - `router.proj` — **8-bit** (Linear hidden→128, bias=False); `router.scale` (2816,), `router.per_expert_scale` (128,) — unquantized, init `mx.ones`.
357
- - `experts.gate_up_proj.weight` — (128, 1408, 2816) **4-bit** SwitchLinear; `experts.down_proj.weight` — (128, 2816, 704) **4-bit** SwitchLinear.
358
- - `layer_scalar` — (1,) unquantized, init `mx.ones`.
359
-
360
- Self-conditioning (`model.decoder.self_conditioning`):
361
- - `pre_norm` (RMSNorm) unquantized; `post_norm` (RMSNormNoScale) unquantized; `gate_proj` / `up_proj` / `down_proj` — **4-bit** (shapes mirror dense MLP: gate/up = (2112,2816), down = (2816,2112)).
362
-
363
- Final: `model.decoder.norm.weight` — unquantized RMSNorm.
364
-
365
- Encoder layer scalars: `model.encoder.language_model.layers.N.layer_scalar` — (1,) unquantized. (All other encoder *text* weights are skipped at load — the encoder text path reuses decoder weights via weakref.)
366
-
367
- Vision tower (SigLIP-style, 27 layers, hidden 1152, patch 16, 16 heads), all 4-bit projections, unquantized norms:
368
- - `model.encoder.vision_tower.patch_embedder.input_proj` — 4-bit.
369
- - `model.encoder.embed_vision.embedding_projection` — 4-bit.
370
- - `model.encoder.vision_tower.encoder.layers.N.self_attn.{q,k,v,o}_proj.linear` — 4-bit (all 27).
371
- - `model.encoder.vision_tower.encoder.layers.N.mlp.{gate,up}_proj.linear` — 4-bit; vision norms unquantized.
372
-
373
- **Quant predicate** (`LanguageModel.quant_predicate`, applied when no explicit per-module override in `config["quantization"]`):
374
- ```python
375
- def predicate(path, m):
376
- if not hasattr(m, "to_quantized"): return False
377
- if "router" in path or path.endswith(("mlp.gate_proj","mlp.up_proj","mlp.down_proj")):
378
- return {"group_size": 64, "bits": 8}
379
- return True # default → 4-bit, group_size 64
380
- ```
381
- Loader-level `class_predicate` additionally returns False unless `f"{path}.scales" in weights` and `weight.size % 64 == 0`, and honors explicit `config["quantization"][path]` overrides first. Net effect: attention q/k/v/o + dense MLP + router = 8-bit; embeddings, experts, self-conditioning, vision = 4-bit.
382
-
383
- **`sanitize()` remapping:** skip `*rotary_emb*` and `lm_head.weight` (RoPE computed at runtime, head tied); skip `model.encoder.language_model.*` except `*.layer_scalar`; keep vision keys only if vision tower present; rename `*.experts.down_proj` → `*.experts.down_proj.weight` and `*.experts.gate_up_proj` → `*.experts.gate_up_proj.weight`.
384
-
385
- **Confirmed absences:** no depthwise/Canon conv tensors anywhere (pure transformer); no separate `lm_head`; no router bias.
386
-
387
- ### 2. Decoder architecture (layer-by-layer)
388
-
389
- **`DecoderModel`:** `embed_tokens` (Embedding 262144→2816), `embed_scale = hidden_size**0.5` (≈53.07), 30× `DecoderLayer`, final `norm` (RMSNorm), `self_conditioning`.
390
-
391
- **`layer_types` pattern:** `[sliding_attention ×5, full_attention] ×5`, last layer forced full. Full-attention layers at indices **5, 11, 17, 23, 29**.
392
-
393
- **RoPE per layer type** (`initialize_rope`, `traditional=False`):
394
- - sliding_attention: `rope_theta = 10000.0`, default.
395
- - full_attention: `rope_theta = 1000000.0`, proportional scaling, `partial_rotary_factor = 0.25` (only 25% of `head_dim` rotated).
396
-
397
- **`Attention.__call__(x, mask, cache, *, decoder=False, offset=None)`** (`scale = 1.0`, **no attention softcap**):
398
- - `head_dim` = 512 if full_attention else 256; `n_kv_heads` = 2 if full else 8.
399
- - q = `q_norm(q_proj(x))` reshaped (B,L,n_heads,head_dim) → transpose → `rope(offset)`.
400
- - k = `k_norm(k_proj(x))` reshaped (B,L,n_kv_heads,head_dim) → transpose → `rope(offset)`.
401
- - v: sliding → `v_norm(v_proj(x))`; full → `v_norm(keys)` (v_proj is None, reuses k). **QK/V norm is post-projection, pre-RoPE.**
402
- - **decoder=True (canvas pass):** fetch `(encoder_keys, encoder_values)` from cache state; for sliding layers, if `offset >= encoder_len` and `encoder_len > window` (`window = sliding_window-1 = 1023`), slice encoder K/V to last 1023 and trim mask to `[..., -(window+L):]`; concat `[encoder_k, k]`, `[encoder_v, v]` on axis 2; `attn_cache=None` (no cache update on canvas pass).
403
- - **decoder=False (encoder/prefill):** `cache.update_and_fetch(k,v)`; `attn_cache=cache`.
404
- - `scaled_dot_product_attention(q,k,v,cache=attn_cache,scale=1.0,mask=mask)` → reshape → `o_proj`.
405
-
406
- **`DecoderLayer.__call__(x, mask, cache, *, decoder, offset, layer_scalar=None)`** — attention branch + **parallel dense-MLP + MoE** feedforward:
407
- ```
408
- # attention
409
- residual = x
410
- h = input_layernorm(x); h = self_attn(h, mask, cache, decoder=decoder, offset=offset)
411
- h = post_attention_layernorm(h); h = residual + h
412
- # parallel FFN
413
- residual = h
414
- h1 = post_feedforward_layernorm_1(mlp(pre_feedforward_layernorm(h))) # dense
415
- flat = residual.reshape(-1, hidden); idx, w = router(flat)
416
- h2 = experts(pre_feedforward_layernorm_2(flat), idx, w).reshape(residual.shape)
417
- h2 = post_feedforward_layernorm_2(h2)
418
- h = post_feedforward_layernorm(h1 + h2); h = residual + h
419
- return h * (layer_scalar if layer_scalar is not None else self.layer_scalar)
420
- ```
421
-
422
- **`MLP`:** `down_proj(geglu(gate_proj(x), up_proj(x)))`, where `geglu(gate,x) = gelu_approx(gate) * x` (`@mx.compile(shapeless=True)`).
423
-
424
- **`Router`:** `x = rms_norm(x, None, eps); x = x * scale * hidden_size**-0.5; scores = proj(x)`; top-8 via `argpartition(scores, kth=-8)[...,-8:]`; `weights = softmax(take_along_axis(scores, idx), precise=True) * per_expert_scale[idx]`. Normalization is **pre**-projection.
425
-
426
- **`Experts`:** `gate_up_proj` / `down_proj` are `SwitchLinear` (weight (num_experts, out, in); `gather_mm(x, W.T, rhs_indices=idx, sorted_indices)`). `x = expand_dims(x,(-2,-3))`; if `idx.size>=64` → `_gather_sort` (group tokens by expert) then `_scatter_unsort`; gate_up split at 704; `y = down_proj(geglu(gate,up))`; final `(y * w[...,None]).sum(axis=-2)`.
427
-
428
- **`_make_decoder_masks(h, caches, decoder_attention_mask)`** — returns `{layer_type → mask}`, broadcast shape `(B, 1, canvas_length, key_len)`, `key_len = encoder_len + canvas_length`. `valid_encoder_len = min(_cache_offset(cache), encoder_len)`.
429
- - **full_attention:** canvas attends bidirectionally to all valid encoder positions + itself. If `encoder_len == valid_encoder_len` and no external mask → mask is `None` (all valid). Otherwise build a key-row: `concat([arange(encoder_len) < valid_encoder_len, ones(canvas_length, bool)])`, broadcast to `(B,1,canvas_length,key_len)`. External masks are sliced to `[..., -key_len:]` then broadcast.
430
- - **sliding_attention:** `window_prefix = max(sliding_window-1, 0) = 1023`. If `encoder_len == valid_encoder_len and encoder_len <= window_prefix` → `None`. Else keep encoder positions in `[max(0, valid_encoder_len - 1023), valid_encoder_len)`: `encoder_mask = (arange(encoder_len) >= start) & (arange < valid_encoder_len)`; `row = concat([encoder_mask, ones(canvas_length, bool)])`. With external mask, AND it elementwise with `keep` before broadcasting.
431
-
432
- Key invariant: **canvas tokens always see all kept encoder positions and are bidirectional among themselves (no causal mask on the canvas)** — diffusion decoding, not autoregressive. (Encoder pass `_make_encoder_masks` is the causal/sliding + vision-overlay one; see §4.)
433
-
434
- `DecoderModel.__call__(canvas_ids, cache, self_conditioning_logits, self_conditioning_embeddings, decoder_attention_mask)`: embed canvas (§3) → `masks = _make_decoder_masks(...)` → `offset = _cache_offset(cache[0])` → loop layers with `decoder=True, offset=offset` → `norm(h)`.
435
-
436
- ### 3. Self-conditioning
437
-
438
- `SelfConditioning` module: `pre_norm` (RMSNorm), `gate_proj`/`up_proj`/`down_proj` (dense-MLP shapes), `post_norm` (RMSNormNoScale).
439
- ```python
440
- def __call__(inputs_embeds, signal):
441
- normed = pre_norm(signal)
442
- s = down_proj(geglu(gate_proj(normed), up_proj(normed)))
443
- return post_norm(inputs_embeds + s)
444
- ```
445
-
446
- `DecoderModel._embed_canvas(canvas_ids, sc_logits=None, sc_embeddings=None)`:
447
- - Hard: `inputs_embeds = embed_tokens(canvas_ids) * embed_scale`.
448
- - Soft signal: if `sc_embeddings` given → cast to dtype; elif `sc_logits is None` → `zeros_like`; else `probs = softmax(sc_logits, precise=True)`, then `soft = probs @ embed_tokens.weight` (or `mx.quantized_matmul(probs, weight, scales, biases, transpose=False, group_size, bits)` if `QuantizedEmbedding`), `* embed_scale`.
449
- - Return `self_conditioning(inputs_embeds, soft)`.
450
-
451
- In the engine, after each denoising step except the last, soft embeddings are computed from the current logits (`_diffusion_entropy_and_soft_embeddings`) and fed as `self_conditioning_embeddings` to the next step.
452
-
453
- ### 4. Vision tower & merge
454
-
455
- **`EncoderModel`** (holds `weakref` to decoder): `language_model` (`EncoderLanguageModel` — weakref + 30 `EncoderLayerScalar`, each `layer_scalar = mx.ones((1,))`), `vision_tower` (`VisionModel`, optional), `embed_vision` (`MultimodalEmbedder`, vision_hidden→text_hidden, eps=vision rms_norm_eps).
456
-
457
- **`make_cache(max_size=None)`** (30 entries by `layer_types`): `StaticPrefixKVCache(max_size)` if max_size given; else `KVCache()` (unbounded) for full_attention; else `RotatingKVCache(max_size=sliding_window=1024)` for sliding.
458
-
459
- **Patch embed** (`VisionPatchEmbedder`): reshape pixels to (B,C,pH,p,pW,p) → transpose (B,pH,pW,p,p,C) → reshape (B, pH·pW, C·p·p) → `2*(patches-0.5)` (normalize to [-1,1]) → `input_proj`. `get_image_features = embed_vision(vision_tower(pixel_values))`.
460
-
461
- **`_embed_inputs(input_ids, pixel_values)`:** `vision_mask = (ids==image_token_id) | (ids==video_token_id?)`; `llm_input_ids = where(vision_mask, pad_token_id, input_ids)`; `inputs_embeds = embed_tokens(llm_input_ids) * embed_scale`; if pixel_values: `masked_scatter(inputs_embeds, vision_mask_expanded, get_image_features(...))`.
462
-
463
- **`_vision_block_overlay(mm_token_type_ids, seq_len)`:** only when `use_bidirectional_attention == "vision"`. `is_vision = (mmtt==1)|(mmtt==2)`; contiguous-block IDs via `cumsum(starts)`; returns `(q_blocks != -1) & (q_blocks == k_blocks)` — tokens in the same image block attend bidirectionally.
464
-
465
- **`_make_encoder_masks(h, cache, attention_mask, mm_token_type_ids)`:** fast path uses `create_attention_mask(window_size=sliding_window if sliding else None)`. Slow path: base causal `q_pos >= k_pos`; AND sliding window `q_pos < k_pos + sliding_window` for sliding layers; OR vision overlay; AND key/padding mask. Overlay applied only in prompt (`key_len == N`), not continuation.
466
-
467
- **`EncoderModel.__call__`:** embed → masks → 30 layers with `decoder=False` and external `layer_scalar = language_model.layers[i].layer_scalar` → `decoder.norm(h), cache`.
468
-
469
- **Image preprocessing (`Gemma4ImageProcessor`):** `patch_size=16`, `max_soft_tokens=280` (allowed 70/140/280/560/1120), `pooling_kernel_size=3`, resize divisor `48 = pooling·patch`, aspect-preserving bicubic resize to multiples of 48 within `max_patches = max_soft_tokens·9` budget; rescale `1/255`; `do_normalize=False` by default (mean/std 0.5 if enabled); channel-first (C,H,W). `num_soft_tokens = (H//16)·(W//16) // 9`.
470
-
471
- ### 5. Tokenizer / chat template / canvas
472
-
473
- **Special tokens** (Gemma4 tokenizer): `bos=2 <bos>`, `eos=[1,106]` (`<eos>`, `<turn|>`), `pad=0 <pad>`, `mask=4 <mask>`, `image_token=258880 <|image|>`, `boi=255999 <|image>`, `eoi=258882 <image|>`, `video=258884 <|video|>`, `audio=258881 <|audio|>`. (Token IDs 258880/255999/258882 confirmed against `ModelConfig`; mask=4 and video=258884 flagged in §9.)
474
-
475
- **Image splicing** (`Gemma4Processor`, `image_seq_length=280`): build `full_image_sequence = boi + image_token*280 + eoi`; per-image, replace each `<|image|>` placeholder with `boi + image_token*N + eoi` where N = that image's actual soft-token count (fallback to fixed 280). Then tokenize.
476
-
477
- **`mm_token_type_ids`** marks tokens: `0`=text/pad, `1`=image (258880), `2`=video (258884), `3`=audio (258881).
478
-
479
- **Chat template** (`chat_template.jinja`, Gemma4): turn markers `<|turn>user\n`, `<|turn>model\n`, `<turn|>\n`, optional `<|turn>system\n…<turn|>\n`; generation prompt continues from `<|turn>model\n`, optionally injecting `<|channel>thought\n<channel|>` when thinking enabled.
480
-
481
- **Canvas:** a fixed 256-token (`canvas_length`) block that the decoder denoises. Initialized to uniform-random token IDs per block (`mx.random.randint(0, vocab_size, (B, canvas_length))`), **not** the mask token. Block diffusion: multiple canvas blocks generated sequentially until `max_tokens` / EOS.
482
-
483
- ### 6. Denoising engine pseudocode
484
-
485
- `stream_diffusion_generate(model, processor, tokenizer, input_ids, pixel_values=None, attention_mask=None, *, max_tokens, skip_special_token_ids, temperature=0.0, max_denoising_steps=48, diffusion_full_canvas=False, diffusion_min_canvas_length=64, diffusion_max_canvas_length=256, diffusion_static_cache=False, diffusion_sampler="entropy-bound", diffusion_threshold=0.9, diffusion_compile=False, diffusion_show_unmasking=False, diffusion_unmasking_interval=1, mm_token_type_ids=None, prefill_step_size=None)`:
486
-
487
- ```
488
- # 1. PREFILL — encoder builds KV cache (optionally chunked by prefill_step_size)
489
- encoder(input_ids, attention_mask, cache, pixel_values, mm_token_type_ids)
490
-
491
- # 2. CANVAS LOOP — block diffusion
492
- while generated_tokens < max_new_tokens:
493
- canvas_length = min(diffusion_max_canvas_length, remaining, model.config.canvas_length)
494
- current_canvas = randint(0, vocab_size, (B, canvas_length)).astype(input_ids.dtype)
495
- sc_embeddings = None
496
- diffusion_history = [] # ring buffer for stability check
497
- draft_reveal_mask = zeros(bool) # confidence sampler
498
- draft_canvas = current_canvas
499
-
500
- # 3. DENOISING LOOP — high t-step → 1
501
- for cur_step in reversed(range(1, max_denoising_steps + 1)):
502
- logits = decoder_logits_without_sc(current_canvas) if sc_embeddings is None \
503
- else decoder_logits_with_sc(current_canvas, sc_embeddings)
504
- # logits already softcapped: tanh(x_fp32 / 30.0) * 30.0
505
-
506
- sched_t = _diffusion_linear_temperature(cur_step, max_denoising_steps, schedule_cfg)
507
- processed = logits / sched_t if sched_t is not None else logits # t_min .4 → t_max .8
508
-
509
- argmax_canvas = argmax(processed, -1)
510
- if cur_step == 1 and not diffusion_show_unmasking: break # skip final re-noise
511
- denoiser_canvas = argmax_canvas if temperature <= 0 \
512
- else categorical(processed / temperature)
513
-
514
- # 4. SAMPLER → acceptance_mask + next sc_embeddings (see §7)
515
- ... acceptance_mask, sc_embeddings(next) ...
516
-
517
- # 5. FREEZE accepted, RE-NOISE the rest
518
- # entropy: current_canvas = where(acceptance_mask, accepted, randint(...))
519
- # confidence: current_canvas = where(draft_reveal_mask | acceptance_mask, accepted, randint(...))
520
-
521
- # 6. EARLY STOPS
522
- if confidence sampler and cur_step>1 and all(draft_reveal_mask):
523
- accepted_canvas = draft_canvas; break
524
- if _diffusion_stable_and_confident(argmax_canvas, processed, history, stopping_cfg): break
525
-
526
- # 7. EMIT tokens from final argmax_canvas; check tokenizer.stopping_criteria(token) for EOS
527
- ```
528
-
529
- `_diffusion_stable_and_confident`: stable if last `stability_threshold` (default 1) canvases are identical; confident if `mean(token_entropy) < confidence_threshold` (default 0.005); returns `stable and confident`. (Distinct from the sampler's `entropy_bound=0.1`.)
530
-
531
- `_diffusion_linear_temperature`: `t_min + (t_max - t_min)*(cur_step/max_steps)`, defaults `t_min=0.4, t_max=0.8`; **divides** logits.
532
-
533
- ### 7. The two samplers
534
-
535
- **Entropy-bound (default in engine):** `entropy_bound = 0.1`. Per step:
536
- - `cur_step>1`: `token_entropy, next_sc_embeddings = _diffusion_entropy_and_soft_embeddings(processed, soft_embedding_weight, embed_scale)`; else `token_entropy = _diffusion_token_entropy(processed)`, `next_sc=None`.
537
- - `_diffusion_entropy_transfer_mask(entropy, entropy_bound)`: sort positions by ascending entropy; `cum = cumsum(sorted_entropy)`, `cmax = cummax(sorted_entropy)`; `sorted_sel = (cum - cmax) <= entropy_bound`; unsort via `put_along_axis`. Accepts the lowest-entropy positions until cumulative-minus-running-max exceeds the bound.
538
- - `accepted = where(mask, denoiser_canvas, current_canvas)`; non-accepted re-noised next step; `draft_reveal_mask = mask`, `draft_canvas = argmax_canvas`.
539
-
540
- **Confidence-threshold:** `diffusion_threshold = 0.9` (CLI `args.threshold`, else 0.9). Per step:
541
- - `confidence = _diffusion_token_probability(processed, denoiser_canvas)` = `exp(token_logits - logsumexp(logits))` in fp32.
542
- - `_diffusion_confidence_transfer_mask(confidence, unrevealed_mask, threshold, force_all=(cur_step==1))`: if `force_all` → return `unrevealed_mask` (accept all on final step); else `transfer = unrevealed & (confidence >= threshold)`; for sequences with no qualifier, force the single highest-confidence unrevealed position (`argmax(where(unrevealed, confidence, -inf))`); return `transfer | forced`.
543
- - `accepted = where(mask, denoiser_canvas, draft_canvas)`; `current_canvas = where(draft_reveal_mask | mask, accepted, randint(...))`; `draft_reveal_mask |= mask`; `draft_canvas = where(mask, accepted, draft_canvas)`.
544
-
545
- Defaults discrepancy across reports: the engine signature default is `diffusion_sampler="entropy-bound"`, while the public `generate()` (`generate.py`) default is `DEFAULT_SAMPLER="confidence-threshold"`. The port should treat the public-API default as confidence-threshold and the engine internal default as entropy-bound.
546
-
547
- `_diffusion_sample_canvas(processed, dtype, temperature)`: fp32; `temperature<=0` → argmax; else `categorical(logits / temperature)`.
548
-
549
- ### 8. OptiQ load / generate / lora API
550
-
551
- **`load_diffusion_gemma(...) → (model, config_dict)`** (`loader.py:55–96`): `load_config()` → `Model(ModelConfig.from_dict(config))` → `_gather_weights()` (lazy mmap) → `model.sanitize(weights)` → quantize via `nn.quantize(group_size, bits, mode, class_predicate)` (predicate honors explicit `config["quantization"][path]`, then `to_quantized` support, `weight.size % 64 == 0`, and `f"{path}.scales" in weights`) → `model.load_weights(...)` → `mx.eval` → `model.eval()`.
552
-
553
- **`generate(model, tokenizer, prompt, *, images=None, max_tokens=512, temperature=0.0, sampler="confidence-threshold", verbose=False) → str`** (`generate.py:169`): wraps `stream_generate()`, concatenates non-draft segments (`is_draft=False`), retries ≤3× if output collapses to empty.
554
-
555
- **`stream_generate()` → Generator[`GenerationResult`]** fields: `text`, `is_draft`, `draft_text`, `diffusion_step`, `diffusion_total_steps`, `diffusion_canvas_index`, plus metrics `prompt_tokens/generation_tokens/total_tokens`, `prompt_tps/generation_tps`, `diffusion_canvas_tokens`, `diffusion_denoising_steps`, `diffusion_work_tokens` (= canvas_tokens·steps), `finish_reason` ∈ {`stop`,`length`,`stable`}.
556
-
557
- **`_build_image_inputs(model, tokenizer, prompt, images)`** → `(input_ids, pixel_values, mm_token_type_ids)`: SigLIP preprocess + per-image `<|image|>` → `boi + image_token*N + eoi` expansion; `mm_token_type_ids = 1` where `t==image_token_id`.
558
-
559
- **LoRA** (`lora.py`):
560
- - `DEFAULT_LORA_KEYS = ["self_attn.q_proj","self_attn.k_proj","self_attn.v_proj","self_attn.o_proj","mlp.gate_proj","mlp.up_proj","mlp.down_proj"]` — applied to all decoder layers via `linear_to_lora_layers(model, len(model.layers), cfg)`. MoE experts and router stay frozen; encoder reuses decoder weights.
561
- - `diffusion_loss(model, prompt_ids, target_ids, *, t_min, t_max, vocab_size)`: sample `t = uniform(t_min, t_max)`; `corrupt = uniform((B,L)) < t`; `noise = randint(0, vocab_size, (B,L))`; `canvas = where(corrupt, noise, target_ids)`; `logits = model(input_ids=prompt_ids, canvas_ids=canvas).logits`; `ce = cross_entropy(logits, target_ids, reduction="none")`; return `(ce * corrupt).sum() / max(corrupt.sum(), 1)` — **CE on corrupted positions only**.
562
- - `train_diffusion_lora(model_path, data_dir, adapter_path, *, rank=8, scale=8.0, dropout=0.0, num_layers=-1, iters=200, learning_rate=1e-4, max_canvas=256, report_every=10, seed=0)`. Data: `{data_dir}/train.jsonl` with `{prompt,completion}` | `{messages:[...]}` | `{text}`. Scale 8.0 (vs AR default 20 which collapses).
563
- - Adapter out: `adapter_config.json` (`base_model`, `fine_tune_type="lora"`, `model_type="diffusion_gemma"`, `lora_parameters`{rank,scale,dropout,keys}) + `adapters.safetensors`.
564
-
565
- **Defaults table:**
566
-
567
- | Param | Default | Source |
568
- |---|---|---|
569
- | `DEFAULT_SAMPLER` | `confidence-threshold` | generate.py:26 |
570
- | `DEFAULT_MAX_TOKENS` | 512 | generate.py:27 |
571
- | `DEFAULT_DIFFUSION_MAX_DENOISING_STEPS` | 48 | diffusion.py:21 |
572
- | `DEFAULT_DIFFUSION_MIN_CANVAS_LENGTH` | 64 | diffusion.py:20 |
573
- | canvas_length | 256 | ModelConfig |
574
- | temperature schedule t_min / t_max | 0.4 / 0.8 | diffusion.py / generation_config.json |
575
- | entropy_bound (sampler) | 0.1 | diffusion.py:662 |
576
- | confidence threshold (sampler) | 0.9 | diffusion.py:597 |
577
- | stability_threshold / confidence_threshold (stop) | 1 / 0.005 | generation_config.json |
578
- | LoRA rank / scale | 8 / 8.0 | lora.py:99–100 |
579
-
580
- If `sampler_config._cls_name != "EntropyBoundSamplerConfig"`, the engine raises `NotImplementedError`.
581
-
582
- ### 9. Open questions / risks for the TS port
583
-
584
- **Confirmed (low risk), called out because the brief asked:**
585
- - **Canon / depthwise conv: ABSENT.** No Conv tensors in the checkpoint; pure transformer (vision tower is attention-based SigLIP). Port needs no conv path.
586
- - **Tied head: YES.** No `lm_head.weight`; logits via `embed_tokens.as_linear()` (a `QuantizedEmbedding`). The port must implement quantized `as_linear` (transpose-matmul against the 4-bit embedding table) and the matching `quantized_matmul(probs, weight, scales, biases, transpose=False, group_size, bits)` path used for soft embeddings in `_embed_canvas`.
587
-
588
- **Unclear / verify before/while porting:**
589
- 1. **`mask_token_id` value — UNCERTAIN.** `<mask>`=4 in `tokenizer_config.json`, but no engine path uses a mask token: the canvas is initialized to **uniform-random token IDs** (`randint(0, vocab_size, …)`), and re-noising also uses random IDs. Treat canvas init/re-noise as uniform-random; don't assume a discrete mask token unless a code path consuming id 4 is found.
590
- 2. **`video_token_id`.** `ModelConfig.video_token_id = None` in the checkpoint, but tokenizer lists 258884 and `mm_token_type_ids` uses `==2` for video. Gate the video branch on a non-None config value; confirm whether this checkpoint supports video.
591
- 3. **Default sampler conflict.** `generate.py` default = `confidence-threshold`; engine default = `entropy-bound`; `generation_config.json` ships `EntropyBoundSamplerConfig`. Public `generate()` → confidence-threshold; low-level engine → entropy-bound.
592
- 4. **`eos_token_id` is a list `[1, 106]`** at ModelConfig level (106 = `<turn|>`) but `1` at TextConfig level. Stopping criteria must accept the full set.
593
- 5. **Mask broadcasting precision in `_make_decoder_masks`.** Highest bug-risk area. The interaction between in-attention encoder K/V slicing (last 1023) and the precomputed mask trim (`[..., -(window+L):]`) must be replicated exactly. Build a per-op parity harness (copy-verbatim methodology) before optimizing.
594
- 6. **`RotatingKVCache` (ring, max_size=1024) vs `KVCache` (unbounded) vs `StaticPrefixKVCache`.** `_cache_offset` / `_cache_state` helpers must be ported faithfully; the decoder reads encoder K/V out of cache state — offset/order bugs corrupt cross-attention silently.
595
- 7. **`SwitchLinear` + `_gather_sort`/`_scatter_unsort`** with the `idx.size >= 64` sort threshold and `sorted_indices` flag. Match `mlx_lm.models.switch_layers` exactly; 4-bit quant amplifies tiny mismatches.
596
- 8. **Quant predicate ordering.** Loader honors explicit `config["quantization"][path]` overrides before the predicate's blanket "8-bit attention." Read the real per-tensor map, not just the predicate.
597
- 9. **`use_clipped_linears` / calibration-scalar gating** in `sanitize()` for vision/embed_vision keys — confirm the exact keep/drop condition against source.
598
- 10. **`partial_rotary_factor=0.25` on full-attention RoPE** (only 25% of the 512-dim head rotated) + `traditional=False` + proportional scaling at theta 1e6 — verify rotary-dim split/interleaving against `initialize_rope` output.
599
- 11. **Image processor budget math** (`max_patches = max_soft_tokens·9`, divisor 48, bicubic, `do_normalize=False`, patch pre-scale `2*(x-0.5)`) and per-image variable soft-token count feeding `<|image|>` expansion — replicate exactly so encoder seq lengths line up with `mm_token_type_ids`.
600
- 12. **`final_logit_softcapping` is fp32**: `tanh(x.astype(float32)/30.0)*30.0` (`@mx.compile(shapeless=True)`); the upcast matters for parity.
601
-
602
- Source-line references are approximate locators from the six passes, not verified citations.