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,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.