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,819 +0,0 @@
1
- // Concrete model graph for Qwen3.5 hybrid models (e.g. Qwen3.6-27B-OptiQ-4bit).
2
- // Port target: mlx_lm.models.qwen3_5 (+ qwen3_next Attention/MLP/RMSNormGated,
3
- // gated_delta recurrence). The architecture is a 64-layer stack where every
4
- // `fullAttentionInterval`-th layer is standard softmax attention and the rest
5
- // are gated-DeltaNet linear-attention layers. Weights carry a
6
- // `language_model.` prefix.
7
- //
8
- // Parity bars: bf16 KV → bit-exact vs mlx-lm; mixed-precision KV → bit-exact vs
9
- // mlx-optiq (the 16 full-attention layers quantized per kv_config.json, via the
10
- // shared maybeQuantizeKv path).
11
-
12
- import type { ModelConfig } from "../config";
13
- import { gatedDeltaState } from "./qwen3-delta-state";
14
- import type { Weights } from "../weights";
15
- import { MlxArray } from "../mlx/array";
16
- import { Dtype, deviceArchitecture } from "../mlx/ffi";
17
- import * as ops from "../mlx/ops";
18
- import { CompiledFunction } from "../mlx/compile";
19
- import { qwenAppendChunkSize } from "./qwen-append";
20
- import { TrellisLinear, fusedGateUpEligible, fusedGateUpSwiglu, TRELLIS_MATVEC_MAX_M } from "./trellis-linear";
21
- import {
22
- argmaxLastPosition,
23
- disposeTriple,
24
- disposing,
25
- KVCache,
26
- LoraState,
27
- QuantizedEmbedding,
28
- QuantizedLinear,
29
- quantizedSdpa,
30
- RMSNorm,
31
- type Cache,
32
- type Mask,
33
- } from "./gemma4-base";
34
- import { qwen35WeightsView } from "./qwen3_5-checkpoint";
35
- import { materializeCopy } from "../mlx/materialize";
36
- import { gatedDeltaUpdate, SSMCache } from "./qwen3-delta";
37
- import type { QwenConvolution } from "./qwen-conv";
38
- import {
39
- activeMrope, applyInterleavedRope, buildMropePositions, mropeInvFreq,
40
- setActiveMrope, type MropeRequestState,
41
- } from "./qwen3-mrope";
42
-
43
- const PREFIX = "language_model";
44
-
45
- // ── Compiled activations ─────────────────────────────────────────────────────
46
- // The oracle (mlx_lm/models/activations.py + qwen3_next.py) wraps BOTH swiglu
47
- // activations in `@partial(mx.compile, shapeless=True)`. We match it: every
48
- // activation site below (the MLP swiglu, the RMSNormGated `_precise_swiglu`, and
49
- // the conv `nn.silu`) runs through a compiled closure unconditionally, so the
50
- // dispatched kernel set matches the oracle op-for-op (= mlx-lm, bit-exact). Traced
51
- // once (shapeless), replayed thereafter; autograd-safe (mx.compile threads VJP
52
- // through the traced graph). `compiledSwiglu` is exported so the parity test can
53
- // assert the closure exists and the MLP actually uses it.
54
-
55
- /** activations.py: `@mx.compile def swiglu(gate, x): return nn.silu(gate) * x`.
56
- * nn.silu(g) == g * sigmoid(g); mx.compile fuses sigmoid+mul+mul → one kernel. */
57
- let _swigluClosure: CompiledFunction | null = null;
58
- export function compiledSwiglu(gate: MlxArray, up: MlxArray): MlxArray {
59
- if (!_swigluClosure) {
60
- _swigluClosure = new CompiledFunction((inputs) => {
61
- const g = inputs[0]!, u = inputs[1]!;
62
- const sig = ops.sigmoid(g);
63
- const silu = ops.mul(g, sig); sig.dispose();
64
- const out = ops.mul(silu, u); silu.dispose();
65
- return [out];
66
- });
67
- }
68
- return _swigluClosure.apply([gate, up])[0]!;
69
- }
70
-
71
- /** qwen3_next.py `_precise_swiglu(h, gate, x)`:
72
- * gate = nn.silu(gate.astype(f32)); x = x.astype(f32); (gate*x).astype(h.dtype)
73
- * Used by Qwen3NextRMSNormGated. Inputs: (h=hidden for the out dtype, gate=z,
74
- * x=rms_norm(hidden)). mx.compile fuses the two casts + silu + mul + cast. */
75
- let _preciseSwigluClosure: CompiledFunction | null = null;
76
- export function compiledPreciseSwiglu(h: MlxArray, gate: MlxArray, x: MlxArray): MlxArray {
77
- if (!_preciseSwigluClosure) {
78
- _preciseSwigluClosure = new CompiledFunction((inputs) => {
79
- const hh = inputs[0]!, g = inputs[1]!, xx = inputs[2]!;
80
- const gf = g.astype(Dtype.float32);
81
- const sig = ops.sigmoid(gf);
82
- const silu = ops.mul(gf, sig); gf.dispose(); sig.dispose();
83
- const xf = xx.astype(Dtype.float32);
84
- const prod = ops.mul(silu, xf); silu.dispose(); xf.dispose();
85
- const out = prod.astype(hh.dtype); prod.dispose();
86
- return [out];
87
- });
88
- }
89
- return _preciseSwigluClosure.apply([h, gate, x])[0]!;
90
- }
91
-
92
- /** mlx.nn.silu is `@partial(mx.compile, shapeless=True) def silu(x): x*sigmoid(x)`
93
- * — a COMPILED kernel, not a standalone sigmoid+mul. Used for the conv
94
- * activation `nn.silu(conv1d(...))`, matching mlx-lm's fused BV2ISigmoid…_V_. */
95
- let _siluClosure: CompiledFunction | null = null;
96
- export function compiledSilu(x: MlxArray): MlxArray {
97
- if (!_siluClosure) {
98
- _siluClosure = new CompiledFunction((inputs) => {
99
- const xx = inputs[0]!;
100
- const sig = ops.sigmoid(xx);
101
- const out = ops.mul(xx, sig); sig.dispose(); // x * sigmoid(x)
102
- return [out];
103
- });
104
- }
105
- return _siluClosure.apply([x])[0]!;
106
- }
107
-
108
- // NOTE: the attention output gate is `self.o_proj(output * mx.sigmoid(gate))`
109
- // INLINE in mlx-lm (qwen3_next.py) — NOT @mx.compile. It is intentionally kept
110
- // as a standalone sigmoid + multiply in Qwen3Attention.forward so the dispatched
111
- // kernel set matches the reference; there is no compiled-output-gate helper.
112
-
113
- /** Gated-DeltaNet linear-attention layer (mlx-lm GatedDeltaNet). */
114
- export class GatedDeltaNet {
115
- /** Per-model experiment seam; null retains the oracle graph. The same
116
- * implementation advances speculative rollback prefixes. Borrowed inputs,
117
- * owned activation and independent state tail; no global runtime mutation. */
118
- convolution: QwenConvolution | null = null;
119
- readonly inProjQkv: QuantizedLinear;
120
- readonly inProjZ: QuantizedLinear;
121
- readonly inProjB: QuantizedLinear;
122
- readonly inProjA: QuantizedLinear;
123
- readonly outProj: QuantizedLinear;
124
- readonly convWeight: MlxArray;
125
- readonly aLog: MlxArray;
126
- readonly dtBias: MlxArray;
127
- readonly normWeight: MlxArray;
128
- readonly eps: number;
129
- readonly numKHeads: number;
130
- readonly numVHeads: number;
131
- readonly headKDim: number;
132
- readonly headVDim: number;
133
- readonly keyDim: number;
134
- readonly valueDim: number;
135
- readonly convKernel: number;
136
-
137
- constructor(weights: Weights, config: ModelConfig, prefix: string) {
138
- const t = config.text;
139
- this.numKHeads = t.linearNumKeyHeads;
140
- this.numVHeads = t.linearNumValueHeads;
141
- this.headKDim = t.linearKeyHeadDim;
142
- this.headVDim = t.linearValueHeadDim;
143
- this.keyDim = this.headKDim * this.numKHeads;
144
- this.valueDim = this.headVDim * this.numVHeads;
145
- this.convKernel = t.linearConvKernelDim;
146
- this.eps = t.rmsNormEps;
147
- this.inProjQkv = QuantizedLinear.load(weights, `${prefix}.in_proj_qkv`, config);
148
- this.inProjZ = QuantizedLinear.load(weights, `${prefix}.in_proj_z`, config);
149
- this.inProjB = QuantizedLinear.load(weights, `${prefix}.in_proj_b`, config);
150
- this.inProjA = QuantizedLinear.load(weights, `${prefix}.in_proj_a`, config);
151
- this.outProj = QuantizedLinear.load(weights, `${prefix}.out_proj`, config);
152
- this.convWeight = weights.tensor(`${prefix}.conv1d.weight`);
153
- this.aLog = weights.tensor(`${prefix}.A_log`);
154
- this.dtBias = weights.tensor(`${prefix}.dt_bias`);
155
- this.normWeight = weights.tensor(`${prefix}.norm.weight`);
156
- }
157
-
158
- #convolve(qkv: MlxArray, state: MlxArray, rowLengths?: readonly number[]): [MlxArray, MlxArray] {
159
- if (this.convolution) {
160
- const result = this.convolution(qkv, state, this.convWeight);
161
- if (!rowLengths) return result;
162
- using input = ops.concatAxis([state, qkv], 1);
163
- const tail = this.#convTail(input, qkv.shape[1]!, rowLengths);
164
- result[1].dispose();
165
- return [result[0], tail];
166
- }
167
- const [, S, D] = qkv.shape as [number, number, number];
168
- const input = ops.concatAxis([state, qkv], 1);
169
- // MLX copy and contiguous can both alias the whole prefill buffer.
170
- // Materialize only the tail so cache residency follows its logical size.
171
- const tail = this.#convTail(input, S, rowLengths);
172
- const conv = ops.conv1d(input, this.convWeight, 1, 0, 1, D);
173
- input.dispose();
174
- const out = compiledSilu(conv);
175
- conv.dispose();
176
- return [out, tail];
177
- }
178
-
179
- #convTail(input: MlxArray, processed: number, rowLengths?: readonly number[]): MlxArray {
180
- const [B, , D] = input.shape as [number, number, number];
181
- const nKeep = this.convKernel - 1;
182
- if (rowLengths) {
183
- using indices = ops.fromInt32(rowLengths.flatMap(length =>
184
- Array.from({ length: nKeep }, (_, position) => length + position)), [B, nKeep, 1]);
185
- return ops.takeAlongAxis(input, indices, 1);
186
- }
187
- using view = input.slice([0, processed, 0], [B, processed + nKeep, D]);
188
- return materializeCopy(view);
189
- }
190
-
191
- forward(x: MlxArray, cache: SSMCache, independentRows = false, mask?: MlxArray | null): MlxArray {
192
- const [B, S] = x.shape as [number, number, number];
193
- const convDim = this.keyDim * 2 + this.valueDim;
194
- const nKeep = this.convKernel - 1;
195
- const prof = (globalThis as Record<string, unknown>).__deltaProf as
196
- Record<string, number>
197
- | undefined;
198
-
199
- // Armed speculative verify round (serve loop, serial lane): this forward
200
- // must be rewindable. Snapshot = hand the REPLACED state slots to the
201
- // round instead of disposing (free — arrays are immutable); record the
202
- // position-local kernel inputs (qkv/a/b) and install the prefix replay.
203
- const spec = cache.specRound;
204
- if (spec) {
205
- if (!spec.armed)
206
- throw new Error("GatedDeltaNet: spec round already recorded this round");
207
- spec.armed = false;
208
- spec.S = S;
209
- spec.replay = (c, keep) => this.#replaySpecPrefix(c, keep);
210
- }
211
-
212
- const t0 = prof ? performance.now() : 0;
213
- let qkv = this.inProjQkv.forward(x, independentRows); // [B,S,convDim]
214
- let z = this.inProjZ.forward(x, independentRows);
215
- z = disposing(z, ops.reshape(z, [B, S, this.numVHeads, this.headVDim]));
216
- const b = this.inProjB.forward(x, independentRows); // [B,S,numVHeads]
217
- const a = this.inProjA.forward(x, independentRows);
218
- if (prof) { ops.evalAll([qkv, z, b, a]); prof.proj = (prof.proj ?? 0) + performance.now() - t0; }
219
- const tc = prof ? performance.now() : 0; // [B,S,numVHeads]
220
-
221
- // The model shares one padding mask across recurrent layers. Direct layer
222
- // callers can request the same cache-owned mask without a model wrapper.
223
- using localMask = mask === undefined ? cache.prefillPadding?.makeMask(S) ?? null : null;
224
- const ssmMask = mask ?? localMask;
225
- if (ssmMask) {
226
- using expanded = ops.reshape(ssmMask, [B, S, 1]);
227
- using zero = MlxArray.fromBytesCopy(new Uint8Array(qkv.dtype === Dtype.float32 ? 4 : 2), [], qkv.dtype);
228
- qkv = disposing(qkv, ops.where(expanded, qkv, zero));
229
- }
230
- // Convolution keeps each row's last real-token tail, including empty rows.
231
- const convState =
232
- cache.conv ?? ops.zeros([B, nKeep, convDim], x.dtype);
233
- const [convOut, newConv] = this.#convolve(qkv, convState, cache.prefillPadding?.convolutionLengths(S));
234
- if (!cache.conv) convState.dispose();
235
- if (spec) spec.qkv = qkv;
236
- else qkv.dispose();
237
- if (spec) spec.prevConv = cache.conv;
238
- else cache.conv?.dispose();
239
- cache.conv = newConv;
240
-
241
- if (prof) { ops.evalAll([convOut]); prof.conv = (prof.conv ?? 0) + performance.now() - tc; }
242
- const tn = prof ? performance.now() : 0;
243
-
244
- const [qFlat, kFlat, vFlat] = ops.split(
245
- convOut, [this.keyDim, 2 * this.keyDim], -1,
246
- ) as [MlxArray, MlxArray, MlxArray];
247
- convOut.dispose();
248
- let q = ops.reshape(qFlat, [B, S, this.numKHeads, this.headKDim]);
249
- qFlat.dispose();
250
- let k = ops.reshape(kFlat, [B, S, this.numKHeads, this.headKDim]);
251
- kFlat.dispose();
252
- const v = disposing(vFlat, ops.reshape(vFlat, [B, S, this.numVHeads, this.headVDim]));
253
-
254
- // inv_scale = head_k_dim ** -0.5; q *= inv_scale², k *= inv_scale.
255
- const invScale = Math.pow(this.headKDim, -0.5);
256
- q = disposing(q, ops.rmsNorm(q, null, 1e-6));
257
- q = disposing(q, ops.mulScalar(q, invScale * invScale));
258
- k = disposing(k, ops.rmsNorm(k, null, 1e-6));
259
- k = disposing(k, ops.mulScalar(k, invScale));
260
- if (prof) { ops.evalAll([q, k]); prof.norms = (prof.norms ?? 0) + performance.now() - tn; }
261
- const tk = prof ? performance.now() : 0;
262
-
263
- const [out, newState] = gatedDeltaUpdate(
264
- q, k, v, a, b, this.aLog, this.dtBias, cache.recurrent, ssmMask,
265
- );
266
- if (prof) { ops.evalAll([out]); prof.kernel = (prof.kernel ?? 0) + performance.now() - tk; }
267
- const to = prof ? performance.now() : 0;
268
- q.dispose();
269
- k.dispose();
270
- v.dispose();
271
- if (spec) {
272
- spec.a = a;
273
- spec.b = b;
274
- spec.prevRecurrent = cache.recurrent;
275
- } else {
276
- a.dispose();
277
- b.dispose();
278
- cache.recurrent?.dispose();
279
- }
280
- cache.recurrent = newState;
281
- cache.advance(S);
282
-
283
- // RMSNormGated: silu(z_f32) * rms_norm(out)_f32, cast back to out dtype.
284
- const gated = this.rmsNormGated(out, z);
285
- out.dispose();
286
- z.dispose();
287
- const merged = ops.reshape(gated, [B, S, this.valueDim]);
288
- gated.dispose();
289
- const result = this.outProj.forward(merged, independentRows);
290
- merged.dispose();
291
- if (prof) { ops.evalAll([result]); prof.out = (prof.out ?? 0) + performance.now() - to; }
292
- return result;
293
- }
294
-
295
- /** Speculative rollback replay: with the pre-round snapshot RESTORED onto
296
- * `cache`, re-advance the first `keep` window tokens from the recorded
297
- * position-local inputs. Every op mirrors forward() on the same values —
298
- * conv windows, silu, per-position norms, and the kernel's serial prefix
299
- * are all independent of the rejected tail — so the resulting conv +
300
- * recurrent state is BIT-EXACTLY what a forward over only the accepted
301
- * prefix would have produced. Output projections (z / out_proj) are state-
302
- * free and skipped: only the states matter here. */
303
- #replaySpecPrefix(cache: SSMCache, keep: number | readonly number[]): void {
304
- const r = cache.specRound;
305
- if (!r || !r.qkv || !r.a || !r.b)
306
- throw new Error("GatedDeltaNet replay without recorded round inputs");
307
- const B = r.qkv.shape[0]!;
308
- const count = typeof keep === "number" ? keep : Math.max(...keep);
309
- const rowLengths = typeof keep === "number" ? undefined : keep;
310
- const convDim = this.keyDim * 2 + this.valueDim;
311
- const nKeep = this.convKernel - 1;
312
-
313
- const qkvPfxView = r.qkv.slice([0, 0, 0], [B, count, convDim]);
314
- const qkvPfx = ops.contiguous(qkvPfxView);
315
- qkvPfxView.dispose();
316
- const convState = cache.conv ?? ops.zeros([B, nKeep, convDim], qkvPfx.dtype);
317
- const [convOut, newConv] = this.#convolve(qkvPfx, convState, rowLengths);
318
- if (!cache.conv) convState.dispose();
319
- qkvPfx.dispose();
320
- cache.conv?.dispose();
321
- cache.conv = newConv;
322
-
323
- const [qFlat, kFlat, vFlat] = ops.split(
324
- convOut, [this.keyDim, 2 * this.keyDim], -1,
325
- ) as [MlxArray, MlxArray, MlxArray];
326
- convOut.dispose();
327
- let q = rowLengths ? null : ops.reshape(qFlat, [B, count, this.numKHeads, this.headKDim]);
328
- qFlat.dispose();
329
- let k = ops.reshape(kFlat, [B, count, this.numKHeads, this.headKDim]);
330
- kFlat.dispose();
331
- const v = disposing(vFlat, ops.reshape(vFlat, [B, count, this.numVHeads, this.headVDim]));
332
- const invScale = Math.pow(this.headKDim, -0.5);
333
- if (q) {
334
- q = disposing(q, ops.rmsNorm(q, null, 1e-6));
335
- q = disposing(q, ops.mulScalar(q, invScale * invScale));
336
- }
337
- k = disposing(k, ops.rmsNorm(k, null, 1e-6));
338
- k = disposing(k, ops.mulScalar(k, invScale));
339
-
340
- const aPfxView = r.a.slice([0, 0, 0], [B, count, this.numVHeads]);
341
- const aPfx = ops.contiguous(aPfxView);
342
- aPfxView.dispose();
343
- const bPfxView = r.b.slice([0, 0, 0], [B, count, this.numVHeads]);
344
- const bPfx = ops.contiguous(bPfxView);
345
- bPfxView.dispose();
346
-
347
- let newState: MlxArray;
348
- if (rowLengths) {
349
- // Unequal prefixes need a row-length-aware recurrence. The state-only
350
- // kernel omits query/output work. Uniform replay retains its measured
351
- // kernel until the separate optimization demonstrates a serving win.
352
- newState = gatedDeltaState(k, v, aPfx, bPfx, this.aLog, this.dtBias, cache.recurrent, rowLengths);
353
- } else {
354
- const [output, state] = gatedDeltaUpdate(q!, k, v, aPfx, bPfx, this.aLog, this.dtBias, cache.recurrent);
355
- output.dispose(); newState = state;
356
- }
357
- q?.dispose();
358
- k.dispose();
359
- v.dispose();
360
- aPfx.dispose();
361
- bPfx.dispose();
362
- cache.recurrent?.dispose();
363
- cache.recurrent = newState;
364
- if (typeof keep === "number") cache.advance(keep);
365
- else cache.advanceRows(keep);
366
- }
367
-
368
- private rmsNormGated(hidden: MlxArray, gate: MlxArray): MlxArray {
369
- const xn = ops.rmsNorm(hidden, this.normWeight, this.eps); // bf16
370
- // Oracle: _precise_swiglu(hidden, gate, xn) — ALWAYS one @mx.compile kernel
371
- // (matches mlx-lm; no unfused silu+mul+cast).
372
- const res = compiledPreciseSwiglu(hidden, gate, xn);
373
- xn.dispose();
374
- return res;
375
- }
376
- }
377
-
378
- /** Full (softmax) attention with output gate + q/k norm + partial RoPE. */
379
- export class Qwen3Attention {
380
- readonly qProj: QuantizedLinear;
381
- readonly kProj: QuantizedLinear;
382
- readonly vProj: QuantizedLinear;
383
- readonly oProj: QuantizedLinear;
384
- readonly qNorm: RMSNorm;
385
- readonly kNorm: RMSNorm;
386
- readonly nHeads: number;
387
- readonly nKvHeads: number;
388
- readonly headDim: number;
389
- readonly scale: number;
390
- readonly ropeDims: number;
391
- readonly ropeBase: number;
392
-
393
- constructor(weights: Weights, config: ModelConfig, prefix: string) {
394
- const t = config.text;
395
- this.nHeads = t.numAttentionHeads;
396
- this.nKvHeads = t.numKeyValueHeads;
397
- this.headDim = t.headDim;
398
- this.scale = Math.pow(this.headDim, -0.5);
399
- this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
400
- this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
401
- this.qProj = QuantizedLinear.load(weights, `${prefix}.q_proj`, config);
402
- this.kProj = QuantizedLinear.load(weights, `${prefix}.k_proj`, config);
403
- this.vProj = QuantizedLinear.load(weights, `${prefix}.v_proj`, config);
404
- this.oProj = QuantizedLinear.load(weights, `${prefix}.o_proj`, config);
405
- this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
406
- this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
407
- }
408
-
409
- forward(x: MlxArray, mask: Mask, cache: Cache, independentRows = false): MlxArray {
410
- const [B, L] = x.shape as [number, number, number];
411
-
412
- // q_proj emits 2× head_dim per head → split into queries + gate.
413
- const qp = this.qProj.forward(x, independentRows);
414
- const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
415
- const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
416
- qpr.dispose();
417
- const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
418
-
419
- let k = this.kProj.forward(x, independentRows);
420
- let v = this.vProj.forward(x, independentRows);
421
-
422
- // q/k norm over head_dim BEFORE transpose (reference order).
423
- let q = this.qNorm.forward(qHeads);
424
- qHeads.dispose();
425
- q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3]));
426
- k = disposing(k, ops.reshape(k, [B, L, this.nKvHeads, this.headDim]));
427
- k = disposing(k, this.kNorm.forward(k));
428
- k = disposing(k, ops.transposeAxes(k, [0, 2, 1, 3]));
429
- v = disposing(v, ops.reshape(v, [B, L, this.nKvHeads, this.headDim]));
430
- v = disposing(v, ops.transposeAxes(v, [0, 2, 1, 3]));
431
-
432
- // Batched decode: the scheduler's mask wrapper exposes each row's REAL
433
- // position as ropeOffsetArr (rows have different prompt lengths); the
434
- // dynamic-offset kernel is the same fast::rope, bit-exact vs the static
435
- // form (tests/unit/compile.test.ts). Serial lane: scalar offset, unchanged.
436
- // Vision requests (serial lane only) install activeMrope for the current
437
- // forward: 3D interleaved positions via the manual apply — the reference
438
- // never uses the fused fast-rope kernel when position_ids are supplied,
439
- // so this IS the oracle's own arithmetic. Text-only stays on ops.rope.
440
- const mr = activeMrope;
441
- const offArr = (cache as { ropeOffsetArr?: MlxArray }).ropeOffsetArr;
442
- if (mr) {
443
- q = disposing(q, applyInterleavedRope(q, mr));
444
- k = disposing(k, applyInterleavedRope(k, mr));
445
- } else {
446
- q = disposing(q, offArr
447
- ? ops.ropeDynamic(q, this.ropeDims, this.ropeBase, offArr, null)
448
- : ops.rope(q, this.ropeDims, this.ropeBase, cache.offset, null));
449
- k = disposing(k, offArr
450
- ? ops.ropeDynamic(k, this.ropeDims, this.ropeBase, offArr, null)
451
- : ops.rope(k, this.ropeDims, this.ropeBase, cache.offset, null));
452
- }
453
-
454
- let attn: MlxArray;
455
- const quantized = cache.quantizedAttention;
456
- if (cache.attentionState) {
457
- const view = cache.attentionState.appendAndFetch(k, v);
458
- k.dispose(); v.dispose();
459
- try { attn = view.attend(q, this.scale, mask); } finally { view.dispose(); }
460
- } else if (quantized) {
461
- const [keys, values] = quantized.updateAndFetchQuantized(k, v);
462
- k.dispose();
463
- v.dispose();
464
- attn = quantizedSdpa(q, keys, values, this.scale, mask, quantized.groupSize, quantized.bits);
465
- disposeTriple(keys);
466
- disposeTriple(values);
467
- } else {
468
- const [keys, values] = cache.updateAndFetch(k, v);
469
- k.dispose();
470
- v.dispose();
471
- attn = ops.sdpa(q, keys, values, this.scale, mask.mode, mask.arr);
472
- keys.dispose();
473
- values.dispose();
474
- }
475
- q.dispose();
476
-
477
- const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
478
- attn.dispose();
479
- const merged = ops.reshape(attnT, [B, L, -1]);
480
- attnT.dispose();
481
- // qwen3_next.py:158 return self.o_proj(output * mx.sigmoid(gate))
482
- // INLINE, not @mx.compile — copy the exact ops.
483
- const sig = ops.sigmoid(gate);
484
- gate.dispose();
485
- const gated = ops.mul(merged, sig);
486
- merged.dispose();
487
- sig.dispose();
488
- const out = this.oProj.forward(gated, independentRows);
489
- gated.dispose();
490
- return out;
491
- }
492
- }
493
-
494
- /** MLP projections are affine-quantized (QuantizedLinear) or, in a Q2b
495
- * packed-trellis artifact, TrellisLinear — chosen per module by the config's
496
- * quantization entry (`mode: "trellis"`). */
497
- export type MlpLinear = QuantizedLinear | TrellisLinear;
498
- function loadMlpLinear(weights: Weights, path: string, config: ModelConfig): MlpLinear {
499
- if (!TrellisLinear.isTrellis(config, path)) return QuantizedLinear.load(weights, path, config);
500
- // M4 Pro measurements support the 27B down projection at M3/4. The
501
- // operation also checks variant, dtype and packed-code layout eligibility.
502
- const useSharedScatterCodebook = config.text.hiddenSize === 5120 &&
503
- config.text.intermediateSize === 17408 && path.endsWith(".down_proj");
504
- return TrellisLinear.load(weights, path, config, useSharedScatterCodebook);
505
- }
506
-
507
- export class Qwen3MLP {
508
- readonly gate: MlpLinear;
509
- readonly up: MlpLinear;
510
- readonly down: MlpLinear;
511
-
512
- constructor(weights: Weights, config: ModelConfig, prefix: string) {
513
- this.gate = loadMlpLinear(weights, `${prefix}.gate_proj`, config);
514
- this.up = loadMlpLinear(weights, `${prefix}.up_proj`, config);
515
- this.down = loadMlpLinear(weights, `${prefix}.down_proj`, config);
516
- }
517
-
518
- forward(x: MlxArray, inputRowContiguous = false, independentRows = false): MlxArray {
519
- // Packed-trellis decode (M ≤ 4): gate, up and the swiglu in ONE kernel —
520
- // x read once, no gate/up vectors materialized. This Lab path retains
521
- // the packed activation arithmetic documented in turboquant.md.
522
- if (this.gate instanceof TrellisLinear && this.up instanceof TrellisLinear &&
523
- fusedGateUpEligible(this.gate, this.up) &&
524
- x.shape.slice(0, -1).reduce((a, b) => a * b, 1) <= TRELLIS_MATVEC_MAX_M) {
525
- const hidden = fusedGateUpSwiglu(x, this.gate, this.up);
526
- const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
527
- hidden.dispose();
528
- return out;
529
- }
530
- const g = this.gate instanceof TrellisLinear ? this.gate.forward(x, inputRowContiguous) : this.gate.forward(x, independentRows);
531
- const u = this.up instanceof TrellisLinear ? this.up.forward(x, inputRowContiguous) : this.up.forward(x, independentRows);
532
- // Oracle: down_proj(swiglu(gate_proj(x), up_proj(x))) — swiglu ALWAYS compiled
533
- // (mlx-lm's @mx.compile swiglu; no unfused silu+mul, matches its kernel set).
534
- const hidden = compiledSwiglu(g, u);
535
- g.dispose();
536
- u.dispose();
537
- const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
538
- hidden.dispose();
539
- return out;
540
- }
541
- }
542
-
543
- export class Qwen3Layer {
544
- readonly isLinear: boolean;
545
- readonly linearAttn: GatedDeltaNet | null = null;
546
- readonly selfAttn: Qwen3Attention | null = null;
547
- readonly mlp: Qwen3MLP;
548
- readonly inputNorm: RMSNorm;
549
- readonly postAttnNorm: RMSNorm;
550
-
551
- constructor(weights: Weights, config: ModelConfig, layerIdx: number) {
552
- const prefix = `${PREFIX}.model.layers.${layerIdx}`;
553
- this.isLinear = (layerIdx + 1) % config.text.fullAttentionInterval !== 0;
554
- if (this.isLinear)
555
- this.linearAttn = new GatedDeltaNet(weights, config, `${prefix}.linear_attn`);
556
- else this.selfAttn = new Qwen3Attention(weights, config, `${prefix}.self_attn`);
557
- this.mlp = new Qwen3MLP(weights, config, `${prefix}.mlp`);
558
- this.inputNorm = new RMSNorm(weights.tensor(`${prefix}.input_layernorm.weight`), config.text.rmsNormEps);
559
- this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
560
- }
561
-
562
- forward(x: MlxArray, faMask: Mask, cache: Cache, independentRows = false, ssmMask?: MlxArray | null): MlxArray {
563
- const xn = this.inputNorm.forward(x);
564
- const r = this.isLinear
565
- ? this.linearAttn!.forward(xn, cache as SSMCache, independentRows, ssmMask)
566
- : this.selfAttn!.forward(xn, faMask, cache, independentRows);
567
- xn.dispose();
568
- const h = ops.add(x, r);
569
- r.dispose();
570
- const hn = this.postAttnNorm.forward(h);
571
- // RMSNorm allocates an aligned row-contiguous output. Pass that layout
572
- // proof so Trellis can match native small-prefill matmul without an eval.
573
- const m = this.mlp.forward(hn, true, independentRows);
574
- hn.dispose();
575
- const out = ops.add(h, m);
576
- h.dispose();
577
- m.dispose();
578
- return out;
579
- }
580
- }
581
-
582
- export class Qwen35Model {
583
- readonly config: ModelConfig;
584
- readonly weightsBytes: number;
585
- /** Base path for LoRA target keys (weights carry the language_model prefix). */
586
- readonly prefixBase = "language_model.model";
587
- readonly loraState = new LoraState();
588
- readonly embed: QuantizedEmbedding;
589
- readonly layers: Qwen3Layer[];
590
- readonly finalNorm: RMSNorm;
591
- /** null when tied: the output head reuses embed_tokens (embed.asLinear). */
592
- readonly lmHead: QuantizedLinear | null;
593
- readonly tied: boolean;
594
- readonly faIdx: number;
595
-
596
- constructor(weights: Weights, config: ModelConfig) {
597
- // Checkpoint-generation normalization BEFORE the first tensor() call: the
598
- // 5.8-family export names the trunk `model.language_model.*` with a
599
- // top-level `lm_head`, ships `mtp.*` in-repo, and stores RMSNorm gains as
600
- // γ−1 with HF-layout conv1d. The view maps all of that onto the names and
601
- // values this graph reads (mlx-lm's post-sanitize space). No-op — nothing
602
- // installed at all — for artifacts already in that space.
603
- const view = qwen35WeightsView(weights);
604
- if (view) weights.setView(view);
605
- this.config = config;
606
- this.tied = config.text.tieWordEmbeddings;
607
- this.weightsBytes = [...weights.shards.files.values()]
608
- .reduce((a, f) => a + f.mmap.size, 0);
609
- this.embed = QuantizedEmbedding.load(weights, `${PREFIX}.model.embed_tokens`, config);
610
- this.layers = Array.from(
611
- { length: config.text.numHiddenLayers },
612
- (_, i) => new Qwen3Layer(weights, config, i),
613
- );
614
- this.finalNorm = new RMSNorm(weights.tensor(`${PREFIX}.model.norm.weight`), config.text.rmsNormEps);
615
- // Tied models (e.g. Qwen3.5-4B) ship no lm_head; the reference uses
616
- // embed_tokens.as_linear (mlx-lm qwen3_5 TextModel.__call__).
617
- this.lmHead = this.tied ? null : QuantizedLinear.load(weights, `${PREFIX}.lm_head`, config);
618
- this.faIdx = config.text.fullAttentionInterval - 1;
619
- }
620
-
621
- loraTargets(): Map<string, QuantizedLinear> {
622
- const out = new Map<string, QuantizedLinear>();
623
- for (let i = 0; i < this.layers.length; i++) {
624
- const l = this.layers[i]!;
625
- const p = `${PREFIX}.model.layers.${i}`;
626
- if (l.selfAttn) {
627
- out.set(`${p}.self_attn.q_proj`, l.selfAttn.qProj);
628
- out.set(`${p}.self_attn.k_proj`, l.selfAttn.kProj);
629
- out.set(`${p}.self_attn.v_proj`, l.selfAttn.vProj);
630
- out.set(`${p}.self_attn.o_proj`, l.selfAttn.oProj);
631
- } else if (l.linearAttn) {
632
- out.set(`${p}.linear_attn.in_proj_qkv`, l.linearAttn.inProjQkv);
633
- out.set(`${p}.linear_attn.in_proj_z`, l.linearAttn.inProjZ);
634
- out.set(`${p}.linear_attn.in_proj_b`, l.linearAttn.inProjB);
635
- out.set(`${p}.linear_attn.in_proj_a`, l.linearAttn.inProjA);
636
- out.set(`${p}.linear_attn.out_proj`, l.linearAttn.outProj);
637
- }
638
- // Trellis-coded MLP tensors are not LoRA targets (no adapter seam yet).
639
- if (l.mlp.gate instanceof QuantizedLinear) out.set(`${p}.mlp.gate_proj`, l.mlp.gate);
640
- if (l.mlp.up instanceof QuantizedLinear) out.set(`${p}.mlp.up_proj`, l.mlp.up);
641
- if (l.mlp.down instanceof QuantizedLinear) out.set(`${p}.mlp.down_proj`, l.mlp.down);
642
- }
643
- return out;
644
- }
645
-
646
- makeCache(): Cache[] {
647
- return this.layers.map((l) => (l.isLinear ? new SSMCache() : new KVCache()));
648
- }
649
-
650
- /** The 27B append path keeps the native M1 affine arithmetic while Trellis
651
- * shares up to four rows. Other shapes retain single-token execution. */
652
- createAppend(policy: { hasAdapters: boolean; pagedKv: boolean }) {
653
- const t = this.config.text;
654
- if (policy.hasAdapters || policy.pagedKv || this.loraState.active.length || this.mrope ||
655
- t.hiddenSize !== 5120 || t.intermediateSize !== 17408 || this.layers.length !== 64 ||
656
- t.headDim !== 256 || t.numAttentionHeads !== 24 || t.numKeyValueHeads !== 4)
657
- return null;
658
- if (deviceArchitecture() !== "applegpu_g16s") return null;
659
- const qualified = (linear: QuantizedLinear | TrellisLinear) => {
660
- if (linear instanceof TrellisLinear) return true;
661
- const { mode, bits, groupSize } = linear.spec;
662
- return mode === "affine" && [2, 3, 4, 6, 8].includes(bits) && [32, 64, 128].includes(groupSize);
663
- };
664
- for (const layer of this.layers) {
665
- const a = layer.linearAttn, s = layer.selfAttn;
666
- const attention = a ? [a.inProjQkv, a.inProjZ, a.inProjB, a.inProjA, a.outProj]
667
- : [s!.qProj, s!.kProj, s!.vProj, s!.oProj];
668
- if (!attention.every(qualified) || ![layer.mlp.gate, layer.mlp.up, layer.mlp.down].every(qualified))
669
- return null;
670
- }
671
- return {
672
- maxChunkSize: (state: readonly Cache[]) => qwenAppendChunkSize(state[0]!.offset),
673
- forwardHidden: (ids: MlxArray, cache: Cache[]): MlxArray => {
674
- if (ids.shape.length !== 2 || ids.shape[0] !== 1 || ids.shape[1]! > 4)
675
- throw new Error("Qwen committed-token append supports one row and at most four positions");
676
- const hidden = this.embed.encode(ids);
677
- return this.forwardLayers(hidden, cache, true);
678
- },
679
- };
680
- }
681
-
682
- forwardHidden(ids: MlxArray, cache: Cache[]): MlxArray {
683
- const h = this.embed.encode(ids);
684
- return this.forwardLayers(h, cache);
685
- }
686
-
687
- /** Active vision mRoPE request state (serial lane; set by the generation
688
- * gateway around a vision request's run, null for text-only — which keeps
689
- * the bit-exact fast-rope path). While set, forwardLayers installs the
690
- * per-forward interleaved cos/sin consumed by every full-attn layer. */
691
- mrope: MropeRequestState | null = null;
692
- #mropeInvFreq: MlxArray | null = null;
693
-
694
- /** Vision prefill: spliced input embeddings [1, L, H] (image features
695
- * overwriting image-token rows — the caller builds them). Caller keeps
696
- * ownership of `embeds`. bidir/ids/multimodal are the Gemma-shaped extras
697
- * and are unused: Qwen3.5 vision attends fully causally. */
698
- forwardEmbeddings(
699
- embeds: MlxArray, cache: Cache[], _bidir: MlxArray | null,
700
- _ids: MlxArray | null = null, _multimodal: MlxArray | null = null,
701
- ): MlxArray {
702
- const h = ops.contiguous(embeds); // fresh handle — the layer loop consumes it
703
- return this.forwardLayers(h, cache);
704
- }
705
-
706
- /** Layer-output tap (same contract as Gemma4Model.hiddenTap): the serve
707
- * loop's forwardMaybeTap sets it around prefill/verify forwards so
708
- * KV-borrowing draft sources (native Qwen MTP) can read PRE-final-norm
709
- * hiddens — layer 63's output stream, what mlx-vlm captures with
710
- * skip_final_norm=True. No-op (and graph-identical) when null. */
711
- hiddenTap: { layers: Set<number>; pos?: number; captured: Map<number, MlxArray> } | null = null;
712
-
713
- /** Store layer `i`'s residual stream ([1,L,H]) if the tap requests it.
714
- * Copies so the loop's h.dispose() can't free the capture. */
715
- protected captureLayer(i: number, h: MlxArray): void {
716
- const tap = this.hiddenTap;
717
- if (!tap || !tap.layers.has(i)) return;
718
- const H = h.shape[2]!;
719
- const src = tap.pos !== undefined ? h.slice([0, tap.pos, 0], [1, tap.pos + 1, H]) : h;
720
- const copy = ops.contiguous(src);
721
- if (src !== h) src.dispose();
722
- tap.captured.set(i, copy);
723
- }
724
-
725
- protected forwardLayers(h0: MlxArray, cache: Cache[], independentRows = false): MlxArray {
726
- const L = h0.shape[1]!;
727
- // One full-attention mask shared by all full layers (same offset); linear
728
- // layers see no ssm mask at B=1.
729
- const faMask = cache[this.faIdx]!.makeMask(L, null);
730
- using ssmMask = (cache[0] as SSMCache).prefillPadding?.makeMask(L) ?? null;
731
- // Vision requests: one interleaved-mRoPE cos/sin table per forward,
732
- // shared by all 12 full-attention layers (positions are layer-invariant).
733
- let mropeFwd: ReturnType<typeof buildMropePositions> | null = null;
734
- if (this.mrope) {
735
- const t = this.config.text;
736
- const ropeDims = Math.trunc(t.headDim * t.partialRotaryFactor);
737
- const base = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
738
- this.#mropeInvFreq ??= mropeInvFreq(ropeDims, base);
739
- mropeFwd = buildMropePositions(
740
- this.mrope, cache[this.faIdx]!.offset, L, this.#mropeInvFreq, ropeDims,
741
- );
742
- setActiveMrope(mropeFwd);
743
- }
744
- let h: MlxArray | null = h0;
745
- const prof = (globalThis as Record<string, unknown>).__deltaProf as
746
- Record<string, number>
747
- | undefined;
748
- try {
749
- for (let i = 0; i < this.layers.length; i++) {
750
- const tl = prof ? performance.now() : 0;
751
- const next = this.layers[i]!.forward(h, faMask, cache[i]!, independentRows, ssmMask);
752
- h.dispose();
753
- h = next;
754
- if (L > TRELLIS_MATVEC_MAX_M) {
755
- // Affine prefills also need bounded evaluation: a retained prefix
756
- // history can leave too little room for the entire deferred graph.
757
- // Materialize the cache outputs too. In particular, the tiny
758
- // copied conv tail is not a dependency of `h`; leaving it lazy
759
- // pins the whole prefill conv buffer until the chunk ends.
760
- const state = cache[i]!.state();
761
- try { ops.evalAll([h, ...state]); }
762
- finally { if (cache[i]!.stateNeedsDispose) for (const a of state) a.dispose(); }
763
- }
764
- if (prof) {
765
- ops.evalAll([h]);
766
- const key = this.layers[i]!.isLinear ? "layerLin" : "layerFull";
767
- prof[key] = (prof[key] ?? 0) + performance.now() - tl;
768
- }
769
- this.captureLayer(i, h); // native-MTP pre-final-norm tap (no-op unless set)
770
- }
771
- const out = disposing(h, this.finalNorm.forward(h));
772
- h = null; // consumed — the finally must not double-free
773
- return out;
774
- } finally {
775
- if (mropeFwd) {
776
- setActiveMrope(null);
777
- mropeFwd.posIds.dispose();
778
- }
779
- // A mid-loop layer throw must not strand the mask array or the
780
- // in-flight [1,L,H] residual (2026-08-18 review).
781
- faMask.arr?.dispose();
782
- h?.dispose();
783
- }
784
- }
785
-
786
- logitsFromHidden(h: MlxArray): MlxArray {
787
- return this.tied ? this.embed.asLinear(h) : this.lmHead!.forward(h);
788
- }
789
-
790
- forward(tokens: number[] | MlxArray, cache: Cache[]): MlxArray {
791
- const ids = Array.isArray(tokens)
792
- ? ops.fromInt32(tokens, [1, tokens.length])
793
- : tokens;
794
- const h = this.forwardHidden(ids, cache);
795
- if (Array.isArray(tokens)) ids.dispose();
796
- const logits = this.logitsFromHidden(h);
797
- h.dispose();
798
- return logits;
799
- }
800
-
801
- generate(promptTokens: number[], maxTokens: number, eosIds: number[] = []): number[] {
802
- const cache = this.makeCache();
803
- const out: number[] = [];
804
- try {
805
- let tokens = promptTokens;
806
- for (let step = 0; step < maxTokens; step++) {
807
- const logits = this.forward(tokens, cache);
808
- const next = argmaxLastPosition(logits);
809
- logits.dispose();
810
- if (eosIds.includes(next)) break;
811
- out.push(next);
812
- tokens = [next];
813
- }
814
- } finally {
815
- for (const c of cache) c.dispose();
816
- }
817
- return out;
818
- }
819
- }