@mlx-bun/inference 0.0.0-stage → 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 (467) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1475 -2
  3. package/THIRD_PARTY_NOTICES.md +3404 -0
  4. package/dist/native/libmlx_bun_ane.dylib +0 -0
  5. package/dist/native/libmlx_bun_expert_io.dylib +0 -0
  6. package/dist/native/mlx-bun-frame-extract +0 -0
  7. package/examples/fit-model.ts +25 -0
  8. package/examples/qwen3-forward.ts +42 -0
  9. package/examples/qwen3-generate.ts +28 -0
  10. package/examples/trellis-expand.ts +23 -0
  11. package/package.json +129 -4
  12. package/src/adapters/available.ts +110 -0
  13. package/src/adapters/index.ts +3 -0
  14. package/src/adapters/manager.ts +316 -0
  15. package/src/artifacts/auxiliary-files.ts +45 -0
  16. package/src/artifacts/config-dialects.ts +94 -0
  17. package/src/artifacts/config.ts +341 -0
  18. package/src/artifacts/draft-vocabulary.ts +30 -0
  19. package/src/artifacts/expert-offload-build.ts +75 -0
  20. package/src/artifacts/expert-offload.ts +89 -0
  21. package/src/artifacts/experts/index.ts +3 -0
  22. package/src/artifacts/experts/io.ts +386 -0
  23. package/src/artifacts/experts/residency.ts +1134 -0
  24. package/src/artifacts/experts/usage.ts +415 -0
  25. package/src/artifacts/fingerprint.ts +44 -0
  26. package/src/artifacts/glm52/container.ts +305 -0
  27. package/src/artifacts/glm52/expert-layout.ts +249 -0
  28. package/src/artifacts/glm52/index.ts +7 -0
  29. package/src/artifacts/glm52/kernel-layout.ts +124 -0
  30. package/src/artifacts/glm52/layout.ts +152 -0
  31. package/src/artifacts/glm52/memory.ts +500 -0
  32. package/src/artifacts/glm52/resident-weights.ts +135 -0
  33. package/src/artifacts/glm52/weights.ts +503 -0
  34. package/src/artifacts/glm52-config.ts +262 -0
  35. package/src/artifacts/identity.ts +94 -0
  36. package/src/artifacts/index.ts +15 -0
  37. package/src/artifacts/mmap.ts +90 -0
  38. package/src/artifacts/safetensors-writer.ts +249 -0
  39. package/src/artifacts/safetensors.ts +164 -0
  40. package/src/artifacts/turboquant-fold.ts +42 -0
  41. package/src/artifacts/weight-audit.ts +43 -0
  42. package/src/artifacts/weights.ts +255 -0
  43. package/src/contracts/index.ts +2 -0
  44. package/src/contracts/mlx/attention.ts +8 -0
  45. package/src/contracts/mlx/cache.ts +510 -0
  46. package/src/contracts/mlx/checkpoint.ts +10 -0
  47. package/src/contracts/mlx/draft-checkpoint.ts +14 -0
  48. package/src/contracts/mlx/draft-projection.ts +27 -0
  49. package/src/contracts/mlx/draft-target.ts +84 -0
  50. package/src/contracts/mlx/drafter.ts +176 -0
  51. package/src/contracts/mlx/forward-work.ts +23 -0
  52. package/src/contracts/mlx/glm52-weights.ts +25 -0
  53. package/src/contracts/mlx/graph.ts +309 -0
  54. package/src/contracts/mlx/head.ts +6 -0
  55. package/src/contracts/mlx/index.ts +15 -0
  56. package/src/contracts/mlx/kv-codec.ts +9 -0
  57. package/src/contracts/mlx/media.ts +113 -0
  58. package/src/contracts/mlx/positions.ts +16 -0
  59. package/src/contracts/mlx/row-table.ts +2 -0
  60. package/src/contracts/mlx/trainable.ts +103 -0
  61. package/src/contracts/mlx/whisper.ts +96 -0
  62. package/src/contracts/portable/composition.ts +106 -0
  63. package/src/contracts/portable/denoising.ts +17 -0
  64. package/src/contracts/portable/execution.ts +79 -0
  65. package/src/contracts/portable/generation.ts +103 -0
  66. package/src/contracts/portable/graph.ts +80 -0
  67. package/src/contracts/portable/index.ts +15 -0
  68. package/src/contracts/portable/memory-plan.ts +26 -0
  69. package/src/contracts/portable/object-cache.ts +8 -0
  70. package/src/contracts/portable/prefill.ts +63 -0
  71. package/src/contracts/portable/prefix-cache.ts +33 -0
  72. package/src/contracts/portable/prompt.ts +2 -0
  73. package/src/contracts/portable/resources.ts +25 -0
  74. package/src/contracts/portable/rollback.ts +7 -0
  75. package/src/contracts/portable/sampling.ts +44 -0
  76. package/src/contracts/portable/scheduling.ts +54 -0
  77. package/src/contracts/portable/token-delivery.ts +4 -0
  78. package/src/contracts/portable/tsconfig.json +14 -0
  79. package/src/embeddings/index.ts +1 -0
  80. package/src/embeddings/text.ts +85 -0
  81. package/src/execution/admission.ts +59 -0
  82. package/src/execution/batch-group.ts +1240 -0
  83. package/src/execution/batch-types.ts +225 -0
  84. package/src/execution/cancellation.ts +38 -0
  85. package/src/execution/continuation-request.ts +45 -0
  86. package/src/execution/continuation-types.ts +10 -0
  87. package/src/execution/coordinator.ts +70 -0
  88. package/src/execution/denoising-group.ts +180 -0
  89. package/src/execution/engine.ts +56 -0
  90. package/src/execution/fill-group.ts +505 -0
  91. package/src/execution/fit.ts +184 -0
  92. package/src/execution/gateway-binding.ts +178 -0
  93. package/src/execution/grammar-group.ts +224 -0
  94. package/src/execution/index.ts +18 -0
  95. package/src/execution/methods.ts +75 -0
  96. package/src/execution/mixed-iteration.ts +96 -0
  97. package/src/execution/plan.ts +65 -0
  98. package/src/execution/prefill-cohort.ts +100 -0
  99. package/src/execution/prefill-rows.ts +263 -0
  100. package/src/execution/request-policy.ts +19 -0
  101. package/src/execution/row-sampling.ts +49 -0
  102. package/src/execution/scheduler.ts +92 -0
  103. package/src/execution/session.ts +247 -0
  104. package/src/execution/speculative-group.ts +797 -0
  105. package/src/execution/tasks.ts +44 -0
  106. package/src/generation/autoregressive.ts +840 -0
  107. package/src/generation/bindings/autoregressive.ts +104 -0
  108. package/src/generation/bindings/denoising.ts +60 -0
  109. package/src/generation/bindings/prefill-policy.ts +36 -0
  110. package/src/generation/checkpoint-identity.ts +60 -0
  111. package/src/generation/denoising.ts +85 -0
  112. package/src/generation/diffusion.ts +583 -0
  113. package/src/generation/fill/echo-index.ts +188 -0
  114. package/src/generation/fill/index.ts +3 -0
  115. package/src/generation/fill/proposal.ts +69 -0
  116. package/src/generation/fill/schema-rows.ts +506 -0
  117. package/src/generation/fill/session.ts +464 -0
  118. package/src/generation/fill/tool-boundary.ts +103 -0
  119. package/src/generation/fill-append.ts +58 -0
  120. package/src/generation/generate.ts +44 -0
  121. package/src/generation/grammar-step.ts +39 -0
  122. package/src/generation/index.ts +14 -0
  123. package/src/generation/phase-dispatch.ts +115 -0
  124. package/src/generation/prefill.ts +33 -0
  125. package/src/generation/result.ts +49 -0
  126. package/src/generation/scopes.ts +157 -0
  127. package/src/generation/speculative/acceptance.ts +41 -0
  128. package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
  129. package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
  130. package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
  131. package/src/generation/speculative/bindings/binding.ts +106 -0
  132. package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
  133. package/src/generation/speculative/bindings/draft-target.ts +10 -0
  134. package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
  135. package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
  136. package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
  137. package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
  138. package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
  139. package/src/generation/speculative/cache-identity.ts +16 -0
  140. package/src/generation/speculative/draft-kind.ts +149 -0
  141. package/src/generation/speculative/draft-policy.ts +41 -0
  142. package/src/generation/speculative/draft-registry.ts +118 -0
  143. package/src/generation/speculative/draft-vocab.ts +110 -0
  144. package/src/generation/speculative/index.ts +17 -0
  145. package/src/generation/speculative/output.ts +49 -0
  146. package/src/generation/speculative/round.ts +307 -0
  147. package/src/generation/speculative/source.ts +144 -0
  148. package/src/generation/speculative/sources/assistant-source.ts +64 -0
  149. package/src/generation/speculative/sources/deepspec-source.ts +70 -0
  150. package/src/generation/speculative/sources/dflash-source.ts +84 -0
  151. package/src/generation/speculative/sources/dflash2-source.ts +86 -0
  152. package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
  153. package/src/generation/speculative/sources/ngram-source.ts +165 -0
  154. package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
  155. package/src/generation/speculative/sources/two-model.ts +75 -0
  156. package/src/generation/types.ts +194 -0
  157. package/src/index.ts +20 -0
  158. package/src/input/audio/audiotoolbox.ts +112 -0
  159. package/src/input/audio/decode.ts +133 -0
  160. package/src/input/audio/features.ts +202 -0
  161. package/src/input/audio/index.ts +6 -0
  162. package/src/input/audio/transcode.ts +67 -0
  163. package/src/input/audio/whisper-mel.ts +185 -0
  164. package/src/input/audio/whisper-tokenizer.ts +238 -0
  165. package/src/input/chat-template.ts +389 -0
  166. package/src/input/index.ts +6 -0
  167. package/src/input/media-fetch.ts +439 -0
  168. package/src/input/packed-mixed.ts +43 -0
  169. package/src/input/special-tokens.ts +75 -0
  170. package/src/input/token-groups.ts +49 -0
  171. package/src/input/tokenizer.ts +336 -0
  172. package/src/input/tool-call.ts +689 -0
  173. package/src/input/vision/index.ts +3 -0
  174. package/src/input/vision/preprocess.ts +247 -0
  175. package/src/input/vision/prompt.ts +398 -0
  176. package/src/input/vision/qwen3vl-preprocess.ts +409 -0
  177. package/src/input/vision/qwen3vl-prompt.ts +237 -0
  178. package/src/input/vision/video-frames.ts +100 -0
  179. package/src/kernels/ane/fill.ts +101 -0
  180. package/src/kernels/ane/linear.ts +416 -0
  181. package/src/kernels/attention/flash.ts +694 -0
  182. package/src/kernels/attention/kvq4-decode.ts +155 -0
  183. package/src/kernels/attention/masks.ts +40 -0
  184. package/src/kernels/attention/multi-query.ts +193 -0
  185. package/src/kernels/attention/paged.ts +130 -0
  186. package/src/kernels/delta/gated.ts +306 -0
  187. package/src/kernels/delta/index.ts +3 -0
  188. package/src/kernels/delta/silu-rms-heads.ts +69 -0
  189. package/src/kernels/delta/state.ts +74 -0
  190. package/src/kernels/gated-rms-norm.ts +64 -0
  191. package/src/kernels/glm52/layout.ts +37 -0
  192. package/src/kernels/glm52/streamed-experts.ts +360 -0
  193. package/src/kernels/logits.ts +38 -0
  194. package/src/kernels/quantization/affine-row.ts +154 -0
  195. package/src/kernels/quantization/affine-rows.ts +36 -0
  196. package/src/kernels/quantization/affine-split-mma.ts +144 -0
  197. package/src/kernels/quantization/affine3-mma.ts +117 -0
  198. package/src/kernels/quantization/affine3-rows.ts +103 -0
  199. package/src/kernels/quantization/affine4-mma.ts +113 -0
  200. package/src/kernels/quantization/affine4-rows.ts +102 -0
  201. package/src/kernels/residual-rms-norm.ts +65 -0
  202. package/src/kernels/sampling/index.ts +2 -0
  203. package/src/kernels/sampling/normalized-argmax.ts +116 -0
  204. package/src/kernels/sampling/token-bitmask.ts +52 -0
  205. package/src/kernels/softcap.ts +35 -0
  206. package/src/kernels/trellis/codebook.ts +123 -0
  207. package/src/kernels/trellis/down-codebook.ts +184 -0
  208. package/src/kernels/trellis/down-factored.ts +180 -0
  209. package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
  210. package/src/kernels/trellis/down-mma-wide.ts +195 -0
  211. package/src/kernels/trellis/down-mma.ts +145 -0
  212. package/src/kernels/trellis/expand.ts +42 -0
  213. package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
  214. package/src/kernels/trellis/gate-up-codebook.ts +137 -0
  215. package/src/kernels/trellis/gate-up-factored.ts +197 -0
  216. package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
  217. package/src/kernels/trellis/gate-up-mma.ts +148 -0
  218. package/src/kernels/trellis/gate-up.ts +248 -0
  219. package/src/kernels/trellis/geometry.ts +22 -0
  220. package/src/kernels/trellis/index.ts +103 -0
  221. package/src/kernels/trellis/launch.ts +12 -0
  222. package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
  223. package/src/kernels/trellis/mixed-gate-up.ts +146 -0
  224. package/src/kernels/trellis/qualified.ts +34 -0
  225. package/src/kernels/trellis/reduce.ts +150 -0
  226. package/src/kernels/trellis/scatter.ts +407 -0
  227. package/src/kernels/trellis/splitk-prefill.ts +152 -0
  228. package/src/kernels/trellis/tiled-prefill.ts +131 -0
  229. package/src/kernels/trellis/vector-expand.ts +58 -0
  230. package/src/kernels/trellis/wide-prefill.ts +89 -0
  231. package/src/kernels/turboquant/decode.ts +82 -0
  232. package/src/kernels/turboquant/index.ts +5 -0
  233. package/src/kernels/turboquant/inverse.ts +122 -0
  234. package/src/kernels/turboquant/ops.ts +860 -0
  235. package/src/kernels/turboquant/signs.ts +130 -0
  236. package/src/kernels/turboquant/tables.ts +124 -0
  237. package/src/layers/affine-verify-linear.ts +75 -0
  238. package/src/layers/ane-prefill-split.ts +283 -0
  239. package/src/layers/compiled-normalization.ts +41 -0
  240. package/src/layers/delta-heads.ts +23 -0
  241. package/src/layers/dense-embedding.ts +29 -0
  242. package/src/layers/dense-linear.ts +32 -0
  243. package/src/layers/dequantized-linear.ts +72 -0
  244. package/src/layers/folded-attention.ts +42 -0
  245. package/src/layers/geglu.ts +130 -0
  246. package/src/layers/helpers.ts +7 -0
  247. package/src/layers/index.ts +36 -0
  248. package/src/layers/kv4-decode-attention.ts +15 -0
  249. package/src/layers/layer-norm.ts +24 -0
  250. package/src/layers/loading.ts +92 -0
  251. package/src/layers/lora.ts +150 -0
  252. package/src/layers/normalization.ts +9 -0
  253. package/src/layers/prefix-layout.ts +33 -0
  254. package/src/layers/quantized-append-attention.ts +32 -0
  255. package/src/layers/quantized-attention.ts +353 -0
  256. package/src/layers/quantized-embedding.ts +39 -0
  257. package/src/layers/quantized-linear.ts +137 -0
  258. package/src/layers/quantized-switch-linear.ts +37 -0
  259. package/src/layers/qwen-append.ts +12 -0
  260. package/src/layers/qwen-mrope.ts +238 -0
  261. package/src/layers/rope-2d.ts +110 -0
  262. package/src/layers/rope.ts +389 -0
  263. package/src/layers/swiglu.ts +23 -0
  264. package/src/layers/trellis-codebook-down.ts +52 -0
  265. package/src/layers/trellis-codebook-gate-up.ts +42 -0
  266. package/src/layers/trellis-down.ts +117 -0
  267. package/src/layers/trellis-gate-up.ts +71 -0
  268. package/src/layers/trellis-linear.ts +211 -0
  269. package/src/layers/trellis-prompt.ts +102 -0
  270. package/src/layers/trellis-reduce-gate-up.ts +73 -0
  271. package/src/layers/trellis-scatter-down.ts +98 -0
  272. package/src/models/ane-install.ts +74 -0
  273. package/src/models/audio/conformer.ts +656 -0
  274. package/src/models/cache-composition.ts +44 -0
  275. package/src/models/capabilities.ts +89 -0
  276. package/src/models/chat-template.ts +10 -0
  277. package/src/models/composition.ts +107 -0
  278. package/src/models/diffusion-gemma/experts.ts +59 -0
  279. package/src/models/diffusion-gemma/family.ts +21 -0
  280. package/src/models/diffusion-gemma/model.ts +900 -0
  281. package/src/models/diffusion-gemma/vision.ts +549 -0
  282. package/src/models/drafter-loaders.ts +49 -0
  283. package/src/models/drafters.ts +39 -0
  284. package/src/models/factory.ts +351 -0
  285. package/src/models/families.ts +70 -0
  286. package/src/models/family.ts +125 -0
  287. package/src/models/gemma4/assistant-target.ts +46 -0
  288. package/src/models/gemma4/assistant.ts +327 -0
  289. package/src/models/gemma4/blocks.ts +755 -0
  290. package/src/models/gemma4/compiled-decode.ts +452 -0
  291. package/src/models/gemma4/family.ts +39 -0
  292. package/src/models/gemma4/gemma4-12b.ts +677 -0
  293. package/src/models/gemma4/gemma4-26b.ts +544 -0
  294. package/src/models/gemma4/gemma4-e4b.ts +599 -0
  295. package/src/models/gemma4/graphs.ts +147 -0
  296. package/src/models/gemma4/media.ts +88 -0
  297. package/src/models/gemma4/model.ts +860 -0
  298. package/src/models/gemma4/serving-blocks.ts +72 -0
  299. package/src/models/gemma4/trainable.ts +186 -0
  300. package/src/models/gemma4/written-blocks.ts +232 -0
  301. package/src/models/gemma4/written-forward.ts +159 -0
  302. package/src/models/gemma4/written-input.ts +29 -0
  303. package/src/models/glm52/atlas.ts +507 -0
  304. package/src/models/glm52/coupling.ts +388 -0
  305. package/src/models/glm52/dsa.ts +601 -0
  306. package/src/models/glm52/family.ts +20 -0
  307. package/src/models/glm52/mla.ts +812 -0
  308. package/src/models/glm52/model.ts +1127 -0
  309. package/src/models/glm52/moe.ts +223 -0
  310. package/src/models/glm52/mtp.ts +60 -0
  311. package/src/models/glm52/pilot.ts +537 -0
  312. package/src/models/glm52/reference.ts +445 -0
  313. package/src/models/glm52/residency.ts +557 -0
  314. package/src/models/glm52/streamed-experts.ts +447 -0
  315. package/src/models/graph.ts +59 -0
  316. package/src/models/implementation.ts +230 -0
  317. package/src/models/index.ts +15 -0
  318. package/src/models/media-input.ts +15 -0
  319. package/src/models/memory-plan.ts +39 -0
  320. package/src/models/minicpm5/family.ts +29 -0
  321. package/src/models/minicpm5/model.ts +394 -0
  322. package/src/models/minicpm5/trainable.ts +90 -0
  323. package/src/models/profile.ts +337 -0
  324. package/src/models/qwen/blocks.ts +658 -0
  325. package/src/models/qwen/checkpoint.ts +190 -0
  326. package/src/models/qwen/families.ts +57 -0
  327. package/src/models/qwen/media-input.ts +161 -0
  328. package/src/models/qwen/mtp.ts +101 -0
  329. package/src/models/qwen/qwen3-moe.ts +355 -0
  330. package/src/models/qwen/qwen3.ts +323 -0
  331. package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
  332. package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
  333. package/src/models/qwen/qwen3_5.ts +433 -0
  334. package/src/models/qwen/written-layers.ts +285 -0
  335. package/src/models/row-table.ts +1 -0
  336. package/src/models/runtime.ts +73 -0
  337. package/src/models/speculative/deepspec-artifact.ts +10 -0
  338. package/src/models/speculative/deepspec.ts +744 -0
  339. package/src/models/speculative/dflash.ts +553 -0
  340. package/src/models/speculative/dflash2-artifact.ts +9 -0
  341. package/src/models/speculative/dflash2-operations.ts +1 -0
  342. package/src/models/speculative/dflash2.ts +576 -0
  343. package/src/models/speculative/loader.ts +29 -0
  344. package/src/models/support.ts +79 -0
  345. package/src/models/universal/archs.ts +365 -0
  346. package/src/models/universal/blocks.ts +479 -0
  347. package/src/models/universal/dense.ts +255 -0
  348. package/src/models/universal/family.ts +17 -0
  349. package/src/models/vision/qwen3vl.ts +566 -0
  350. package/src/models/vision/siglip.ts +520 -0
  351. package/src/models/vision/unified.ts +176 -0
  352. package/src/models/whisper/family.ts +19 -0
  353. package/src/models/whisper/fast.ts +734 -0
  354. package/src/models/whisper/model.ts +430 -0
  355. package/src/runtime/compiled-trace.ts +16 -0
  356. package/src/runtime/config.ts +77 -0
  357. package/src/runtime/expert-trace.ts +71 -0
  358. package/src/runtime/graph-forward.ts +65 -0
  359. package/src/runtime/integers.ts +6 -0
  360. package/src/runtime/native.ts +16 -0
  361. package/src/runtime/resources.ts +53 -0
  362. package/src/runtime/row-operations.ts +42 -0
  363. package/src/runtime/trace.ts +161 -0
  364. package/src/sampling/curve.ts +122 -0
  365. package/src/sampling/draft.ts +55 -0
  366. package/src/sampling/extras.ts +45 -0
  367. package/src/sampling/filters.ts +134 -0
  368. package/src/sampling/grammar.ts +630 -0
  369. package/src/sampling/hlg.ts +290 -0
  370. package/src/sampling/index.ts +9 -0
  371. package/src/sampling/processors.ts +142 -0
  372. package/src/sampling/sampler.ts +120 -0
  373. package/src/sampling/step.ts +343 -0
  374. package/src/sampling/types.ts +161 -0
  375. package/src/sampling/window-filters.ts +46 -0
  376. package/src/scoring/decisions.ts +96 -0
  377. package/src/scoring/full-sequence.ts +103 -0
  378. package/src/scoring/index.ts +9 -0
  379. package/src/scoring/kl.ts +29 -0
  380. package/src/scoring/perplexity.ts +165 -0
  381. package/src/scoring/response-logps.ts +65 -0
  382. package/src/state/affine-attention.ts +180 -0
  383. package/src/state/attachment.ts +68 -0
  384. package/src/state/attention-read.ts +215 -0
  385. package/src/state/batched-kv.ts +294 -0
  386. package/src/state/batched-mask.ts +445 -0
  387. package/src/state/batched-quant.ts +389 -0
  388. package/src/state/batched-quantized-kv.ts +209 -0
  389. package/src/state/batched-rotating-quant.ts +457 -0
  390. package/src/state/batched-rotating-state.ts +149 -0
  391. package/src/state/batched-rotating.ts +430 -0
  392. package/src/state/batched-row-storage.ts +179 -0
  393. package/src/state/batched-ssm.ts +54 -0
  394. package/src/state/batched-turboquant-kv.ts +144 -0
  395. package/src/state/bf16-first-kv.ts +663 -0
  396. package/src/state/cache-service.ts +592 -0
  397. package/src/state/capabilities.ts +65 -0
  398. package/src/state/checkpoint.ts +46 -0
  399. package/src/state/continuation-persistence.ts +126 -0
  400. package/src/state/decode-step-inputs.ts +31 -0
  401. package/src/state/decoded-kv-donor.ts +12 -0
  402. package/src/state/delayed-quantized-kv.ts +141 -0
  403. package/src/state/delayed-rotating-quantized-kv.ts +178 -0
  404. package/src/state/delayed-turboquant-kv.ts +123 -0
  405. package/src/state/durability.ts +214 -0
  406. package/src/state/encoder-cache.ts +26 -0
  407. package/src/state/full-kv-row-append.ts +33 -0
  408. package/src/state/full-kv-row-donor.ts +54 -0
  409. package/src/state/full-prefill-padding.ts +56 -0
  410. package/src/state/full-prefill-row.ts +112 -0
  411. package/src/state/full-transitioning-kv-rows.ts +70 -0
  412. package/src/state/glm52-cache.ts +783 -0
  413. package/src/state/grouped-heads-kv4-cache.ts +26 -0
  414. package/src/state/index.ts +38 -0
  415. package/src/state/kv-attention-view.ts +77 -0
  416. package/src/state/kv-budget.ts +14 -0
  417. package/src/state/kv-scheme.ts +315 -0
  418. package/src/state/kv-storage.ts +57 -0
  419. package/src/state/kv-tensor-rows.ts +212 -0
  420. package/src/state/kv.ts +324 -0
  421. package/src/state/kv4-head256-cache.ts +46 -0
  422. package/src/state/latent-legos.ts +895 -0
  423. package/src/state/layout.ts +277 -0
  424. package/src/state/leases.ts +26 -0
  425. package/src/state/paged/cache.ts +689 -0
  426. package/src/state/paged/codec.ts +36 -0
  427. package/src/state/paged/rows.ts +223 -0
  428. package/src/state/persistence-types.ts +173 -0
  429. package/src/state/persistence-worker.ts +83 -0
  430. package/src/state/persistence.ts +1036 -0
  431. package/src/state/persistence.worker.d.ts +3 -0
  432. package/src/state/persistence.worker.js +264 -0
  433. package/src/state/prefix-cache.ts +685 -0
  434. package/src/state/quantized-kv.ts +369 -0
  435. package/src/state/quantized-tensor.ts +26 -0
  436. package/src/state/request-policy.ts +48 -0
  437. package/src/state/retention-policy.ts +37 -0
  438. package/src/state/rollback.ts +131 -0
  439. package/src/state/rotating-kv-donor.ts +35 -0
  440. package/src/state/rotating-kv-layout.ts +341 -0
  441. package/src/state/rotating-kv.ts +529 -0
  442. package/src/state/rotating-quantized-kv.ts +616 -0
  443. package/src/state/rotating-row-transaction.ts +35 -0
  444. package/src/state/rows.ts +104 -0
  445. package/src/state/softcap-attention.ts +118 -0
  446. package/src/state/speculative/glm52-mtp-state.ts +38 -0
  447. package/src/state/speculative/qwen-mtp-state.ts +34 -0
  448. package/src/state/speculative-rotating-kv.ts +113 -0
  449. package/src/state/ssd-cache.ts +434 -0
  450. package/src/state/ssm-prefill-padding.ts +39 -0
  451. package/src/state/ssm.ts +422 -0
  452. package/src/state/target-layout.ts +7 -0
  453. package/src/state/tiered-prefix-cache.ts +45 -0
  454. package/src/state/training-cache.ts +300 -0
  455. package/src/state/transitioning-kv-rows.ts +267 -0
  456. package/src/state/turboquant-attention.ts +97 -0
  457. package/src/state/turboquant-codec.ts +90 -0
  458. package/src/state/turboquant-kv.ts +442 -0
  459. package/src/state/views.ts +26 -0
  460. package/src/transcription/index.ts +3 -0
  461. package/src/transcription/silero-vad.ts +379 -0
  462. package/src/transcription/whisper/decode.ts +773 -0
  463. package/src/transcription/whisper/format.ts +74 -0
  464. package/src/transcription/whisper/index.ts +4 -0
  465. package/src/transcription/whisper/timing.ts +274 -0
  466. package/src/transcription/whisper/transcribe.ts +394 -0
  467. package/src/transcription/whisper/types.ts +22 -0
@@ -0,0 +1,360 @@
1
+ import { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { Dtype } from "@mlx-bun/mlx/ffi";
3
+ import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
4
+ import { positiveInteger } from "../../runtime/integers";
5
+ import { GLM52_EXPERT_SLOT_ALIGNMENT, validateRange, type Glm52CanonicalMetalLayout } from "./layout";
6
+ export { GLM52_EXPERT_SLOT_ALIGNMENT } from "./layout";
7
+ export type { Glm52CanonicalMetalLayout, Glm52CanonicalQ4MetalLayout, Glm52CanonicalQ8MetalLayout } from "./layout";
8
+
9
+ const SIMD_WIDTH = 32;
10
+ const SIMD_GROUPS = 4;
11
+ const THREADS = SIMD_WIDTH * SIMD_GROUPS;
12
+ const ROWS_PER_THREADGROUP = SIMD_GROUPS;
13
+
14
+ /** Wrap a live, aligned ExpertIOSlabStore slot without copying it.
15
+ *
16
+ * The residency lease remains owned by the caller and must not be released
17
+ * until the returned expert output has been evaluated and the GPU stream has
18
+ * completed.
19
+ */
20
+ export function glm52CanonicalQ4SlotView(
21
+ pointer: number,
22
+ layout: Glm52CanonicalMetalLayout,
23
+ ): MlxArray {
24
+ if (!Number.isSafeInteger(pointer) || pointer <= 0)
25
+ throw new Error("expert slot pointer must be a positive safe integer");
26
+ if (pointer % GLM52_EXPERT_SLOT_ALIGNMENT !== 0) {
27
+ throw new Error(
28
+ "expert slot pointer must be page-aligned for zero-copy Metal access",
29
+ );
30
+ }
31
+ return MlxArray.fromPointer(pointer, [layout.slotBytes], Dtype.uint8);
32
+ }
33
+
34
+ export const glm52CanonicalQ8SlotView = glm52CanonicalQ4SlotView;
35
+
36
+ // One simdgroup owns one output row. The packed Q4 byte stream and F32 row
37
+ // scales are read straight from the canonical residency slot. Every product is
38
+ // explicitly dequantized to float and accumulated with an F32 FMA.
39
+ const GATE_UP_SOURCE = String.raw`
40
+ const uint lane = thread_index_in_simdgroup;
41
+ const uint sg = simdgroup_index_in_threadgroup;
42
+ const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
43
+ const uint sample = thread_position_in_grid.z;
44
+ if (row >= (uint)I || sample >= (uint)M) return;
45
+ const device T* sampleX = x + (ulong)sample * (ulong)H;
46
+
47
+ const device uint32_t* gateRow =
48
+ (const device uint32_t*)(slot + GATE_W) + (ulong)row * (ulong)(H / 8);
49
+ const device uint32_t* upRow =
50
+ (const device uint32_t*)(slot + UP_W) + (ulong)row * (ulong)(H / 8);
51
+ const device float* gateScales = (const device float*)(slot + GATE_S);
52
+ const device float* upScales = (const device float*)(slot + UP_S);
53
+ const float gateScale = gateScales[row];
54
+ const float upScale = upScales[row];
55
+
56
+ float gateAcc = 0.0f;
57
+ float upAcc = 0.0f;
58
+ for (uint word = lane; word < (uint)(H / 8); word += 32u) {
59
+ const uint32_t gatePacked = gateRow[word];
60
+ const uint32_t upPacked = upRow[word];
61
+ const uint k0 = word * 8u;
62
+ for (uint place = 0; place < 8u; ++place) {
63
+ const float xv = float(sampleX[k0 + place]);
64
+ const float gateWeight =
65
+ float((gatePacked >> (place * 4u)) & 0xFu) - 8.0f;
66
+ const float upWeight =
67
+ float((upPacked >> (place * 4u)) & 0xFu) - 8.0f;
68
+ gateAcc = metal::fma(gateWeight, xv, gateAcc);
69
+ upAcc = metal::fma(upWeight, xv, upAcc);
70
+ }
71
+ }
72
+
73
+ const float gate = metal::simd_sum(gateAcc) * gateScale;
74
+ const float up = metal::simd_sum(upAcc) * upScale;
75
+ if (lane == 0u) {
76
+ // Match the production BF16 graph's materialization boundaries: gate and
77
+ // up are rounded to T, then SiLU and the product are rounded to T.
78
+ const T gateT = T(gate);
79
+ const T upT = T(up);
80
+ const T sigmoidT = T(1.0f / (1.0f + metal::precise::exp(-float(gateT))));
81
+ const T siluT = T(float(gateT) * float(sigmoidT));
82
+ mid[(ulong)sample * (ulong)I + row] =
83
+ T(float(siluT) * float(upT));
84
+ }
85
+ `;
86
+
87
+ const DOWN_SOURCE = String.raw`
88
+ const uint lane = thread_index_in_simdgroup;
89
+ const uint sg = simdgroup_index_in_threadgroup;
90
+ const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
91
+ const uint sample = thread_position_in_grid.z;
92
+ if (row >= (uint)H || sample >= (uint)M) return;
93
+ const device T* sampleMid = mid + (ulong)sample * (ulong)I;
94
+
95
+ const device uint32_t* downRow =
96
+ (const device uint32_t*)(slot + DOWN_W) + (ulong)row * (ulong)(I / 8);
97
+ const device float* downScales = (const device float*)(slot + DOWN_S);
98
+ const float scale = downScales[row];
99
+ float acc = 0.0f;
100
+ for (uint word = lane; word < (uint)(I / 8); word += 32u) {
101
+ const uint32_t packed = downRow[word];
102
+ const uint k0 = word * 8u;
103
+ for (uint place = 0; place < 8u; ++place) {
104
+ const float xv = float(sampleMid[k0 + place]);
105
+ const float weight =
106
+ float((packed >> (place * 4u)) & 0xFu) - 8.0f;
107
+ acc = metal::fma(weight, xv, acc);
108
+ }
109
+ }
110
+ const float value = metal::simd_sum(acc) * scale;
111
+ if (lane == 0u)
112
+ out[(ulong)sample * (ulong)H + row] = T(value);
113
+ `;
114
+
115
+ const Q8_GATE_UP_SOURCE = String.raw`
116
+ const uint lane = thread_index_in_simdgroup;
117
+ const uint sg = simdgroup_index_in_threadgroup;
118
+ const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
119
+ const uint sample = thread_position_in_grid.z;
120
+ if (row >= (uint)I || sample >= (uint)M) return;
121
+
122
+ const device int8_t* gateRow =
123
+ (const device int8_t*)(slot + GATE_W) + (ulong)row * (ulong)H;
124
+ const device int8_t* upRow =
125
+ (const device int8_t*)(slot + UP_W) + (ulong)row * (ulong)H;
126
+ const device float* gateScales = (const device float*)(slot + GATE_S);
127
+ const device float* upScales = (const device float*)(slot + UP_S);
128
+ const float gateScale = gateScales[row];
129
+ const float upScale = upScales[row];
130
+ float gateAcc = 0.0f;
131
+ float upAcc = 0.0f;
132
+ for (uint k = lane; k < (uint)H; k += 32u) {
133
+ const float xv = float(x[(ulong)sample * (ulong)H + k]);
134
+ gateAcc = metal::fma(float(gateRow[k]), xv, gateAcc);
135
+ upAcc = metal::fma(float(upRow[k]), xv, upAcc);
136
+ }
137
+ const float gate = metal::simd_sum(gateAcc) * gateScale;
138
+ const float up = metal::simd_sum(upAcc) * upScale;
139
+ if (lane == 0u) {
140
+ const T gateT = T(gate);
141
+ const T upT = T(up);
142
+ const T sigmoidT = T(1.0f / (1.0f + metal::precise::exp(-float(gateT))));
143
+ const T siluT = T(float(gateT) * float(sigmoidT));
144
+ mid[(ulong)sample * (ulong)I + row] = T(float(siluT) * float(upT));
145
+ }
146
+ `;
147
+
148
+ const Q8_DOWN_SOURCE = String.raw`
149
+ const uint lane = thread_index_in_simdgroup;
150
+ const uint sg = simdgroup_index_in_threadgroup;
151
+ const uint row = thread_position_in_grid.y * (uint)ROWS_TG + sg;
152
+ const uint sample = thread_position_in_grid.z;
153
+ if (row >= (uint)H || sample >= (uint)M) return;
154
+
155
+ const device int8_t* downRow =
156
+ (const device int8_t*)(slot + DOWN_W) + (ulong)row * (ulong)I;
157
+ const device float* downScales = (const device float*)(slot + DOWN_S);
158
+ const float scale = downScales[row];
159
+ float acc = 0.0f;
160
+ for (uint k = lane; k < (uint)I; k += 32u) {
161
+ const float xv = float(mid[(ulong)sample * (ulong)I + k]);
162
+ acc = metal::fma(float(downRow[k]), xv, acc);
163
+ }
164
+ const float value = metal::simd_sum(acc) * scale;
165
+ if (lane == 0u) out[(ulong)sample * (ulong)H + row] = T(value);
166
+ `;
167
+
168
+ /** What differs between the Q4 and Q8 kernel families: the Metal sources, the
169
+ * weight packing the validators check, and the error wording. */
170
+ interface CanonicalExecutorSpec {
171
+ readonly label: "Q4" | "Q8";
172
+ readonly kernelPrefix: string;
173
+ readonly gateUpSource: string;
174
+ readonly downSource: string;
175
+ /** Weights stored per byte (Q4 packs two, Q8 one). */
176
+ readonly weightsPerByte: 1 | 2;
177
+ /** Output dimensions must divide by this many packed weights (Q4: one uint32 word). */
178
+ readonly dimensionMultiple: number;
179
+ readonly subject: string;
180
+ }
181
+
182
+ function validateCall(
183
+ input: MlxArray,
184
+ slot: MlxArray,
185
+ layout: Glm52CanonicalMetalLayout,
186
+ spec: CanonicalExecutorSpec,
187
+ ): void {
188
+ const hidden = positiveInteger(layout.hiddenSize, "hidden size");
189
+ const intermediate = positiveInteger(
190
+ layout.intermediateSize,
191
+ "intermediate size",
192
+ );
193
+ positiveInteger(layout.slotBytes, "expert slot bytes");
194
+ if (hidden % spec.dimensionMultiple !== 0 || intermediate % spec.dimensionMultiple !== 0) {
195
+ throw new Error(
196
+ `${spec.label} expert dimensions must be divisible by ${spec.dimensionMultiple}`,
197
+ );
198
+ }
199
+ if (hidden % ROWS_PER_THREADGROUP !== 0 ||
200
+ intermediate % ROWS_PER_THREADGROUP !== 0) {
201
+ throw new Error(
202
+ `${spec.label} expert output dimensions must be divisible by ${ROWS_PER_THREADGROUP}`,
203
+ );
204
+ }
205
+ if (
206
+ input.shape.length !== 2 ||
207
+ !Number.isSafeInteger(input.shape[0]) ||
208
+ input.shape[0]! < 1 ||
209
+ input.shape[1] !== hidden
210
+ ) {
211
+ throw new Error(`${spec.subject} requires input [M,${hidden}]`);
212
+ }
213
+ if (input.dtype !== Dtype.bfloat16 && input.dtype !== Dtype.float32)
214
+ throw new Error(`${spec.subject} requires bfloat16 or float32 input`);
215
+ if (slot.dtype !== Dtype.uint8 || slot.shape.length !== 1)
216
+ throw new Error("canonical expert slot must be a flat uint8 array");
217
+ if (slot.size < layout.slotBytes)
218
+ throw new Error("canonical expert slot array is shorter than its layout");
219
+
220
+ const perByte = spec.weightsPerByte;
221
+ const ranges = [
222
+ [layout.downWeightOffset, hidden * intermediate / perByte, "down weights"],
223
+ [layout.gateWeightOffset, intermediate * hidden / perByte, "gate weights"],
224
+ [layout.upWeightOffset, intermediate * hidden / perByte, "up weights"],
225
+ [layout.downScaleOffset, hidden * 4, "down scales"],
226
+ [layout.gateScaleOffset, intermediate * 4, "gate scales"],
227
+ [layout.upScaleOffset, intermediate * 4, "up scales"],
228
+ ] as const;
229
+ for (const [offset, length, label] of ranges)
230
+ validateRange(offset, length, layout.slotBytes, label);
231
+ }
232
+
233
+ /**
234
+ * Row-independent routed-SwiGLU kernel pair (gate/up, then down) that reads a
235
+ * canonical residency slot directly. M=1 decode and a pinned speculative
236
+ * verify batch use the identical source and dispatch geometry; only grid.z
237
+ * changes. The Q4 and Q8 families below differ only in their spec.
238
+ */
239
+ class Glm52CanonicalMetalExecutor {
240
+ readonly #gateUp: MetalKernel;
241
+ readonly #down: MetalKernel;
242
+ readonly #spec: CanonicalExecutorSpec;
243
+ #disposed = false;
244
+
245
+ constructor(spec: CanonicalExecutorSpec) {
246
+ this.#spec = spec;
247
+ this.#gateUp = new MetalKernel({
248
+ name: `${spec.kernelPrefix}_gate_up`,
249
+ inputNames: ["x", "slot"],
250
+ outputNames: ["mid"],
251
+ source: spec.gateUpSource,
252
+ ensureRowContiguous: true,
253
+ });
254
+ this.#down = new MetalKernel({
255
+ name: `${spec.kernelPrefix}_down`,
256
+ inputNames: ["mid", "slot"],
257
+ outputNames: ["out"],
258
+ source: spec.downSource,
259
+ ensureRowContiguous: true,
260
+ });
261
+ }
262
+
263
+ execute(
264
+ input: MlxArray,
265
+ slot: MlxArray,
266
+ layout: Glm52CanonicalMetalLayout,
267
+ ): MlxArray {
268
+ if (this.#disposed)
269
+ throw new Error(`${this.#spec.subject} executor used after dispose`);
270
+ validateCall(input, slot, layout, this.#spec);
271
+ const samples = input.shape[0]!;
272
+ const templateInts = {
273
+ M: samples,
274
+ H: layout.hiddenSize,
275
+ I: layout.intermediateSize,
276
+ DOWN_W: layout.downWeightOffset,
277
+ GATE_W: layout.gateWeightOffset,
278
+ UP_W: layout.upWeightOffset,
279
+ DOWN_S: layout.downScaleOffset,
280
+ GATE_S: layout.gateScaleOffset,
281
+ UP_S: layout.upScaleOffset,
282
+ ROWS_TG: ROWS_PER_THREADGROUP,
283
+ };
284
+ const [mid] = this.#gateUp.apply([input, slot], {
285
+ outputs: [{
286
+ shape: [samples, layout.intermediateSize],
287
+ dtype: input.dtype,
288
+ }],
289
+ grid: [
290
+ THREADS,
291
+ layout.intermediateSize / ROWS_PER_THREADGROUP,
292
+ samples,
293
+ ],
294
+ threadGroup: [THREADS, 1, 1],
295
+ templateDtypes: { T: input.dtype },
296
+ templateInts,
297
+ });
298
+ if (!mid) throw new Error(`${this.#spec.subject} gate/up kernel returned no output`);
299
+ try {
300
+ const [output] = this.#down.apply([mid, slot], {
301
+ outputs: [{
302
+ shape: [samples, layout.hiddenSize],
303
+ dtype: input.dtype,
304
+ }],
305
+ grid: [
306
+ THREADS,
307
+ layout.hiddenSize / ROWS_PER_THREADGROUP,
308
+ samples,
309
+ ],
310
+ threadGroup: [THREADS, 1, 1],
311
+ templateDtypes: { T: input.dtype },
312
+ templateInts,
313
+ });
314
+ if (!output) throw new Error(`${this.#spec.subject} down kernel returned no output`);
315
+ return output;
316
+ } finally {
317
+ mid.dispose();
318
+ }
319
+ }
320
+
321
+ dispose(): void {
322
+ if (this.#disposed) return;
323
+ this.#disposed = true;
324
+ this.#gateUp.dispose();
325
+ this.#down.dispose();
326
+ }
327
+ }
328
+
329
+ /** Q4 (packed nibbles, affine zero-point 8) kernel family for the GLM-5.2
330
+ * production geometry. */
331
+ export class Glm52CanonicalQ4MetalExecutor extends Glm52CanonicalMetalExecutor {
332
+ constructor() {
333
+ super({
334
+ label: "Q4",
335
+ kernelPrefix: "mlx_bun_glm52_q4_slot",
336
+ gateUpSource: GATE_UP_SOURCE,
337
+ downSource: DOWN_SOURCE,
338
+ weightsPerByte: 2,
339
+ dimensionMultiple: 8,
340
+ subject: "GLM Metal streamed expert decode",
341
+ });
342
+ }
343
+ }
344
+
345
+ /** Fixed M=1..gamma signed-Q8 kernel family for native MTP draft and accepted
346
+ * token absorption. The row dot-product and materialization boundaries are
347
+ * identical for every batch width; only grid.z changes. */
348
+ export class Glm52CanonicalQ8MetalExecutor extends Glm52CanonicalMetalExecutor {
349
+ constructor() {
350
+ super({
351
+ label: "Q8",
352
+ kernelPrefix: "mlx_bun_glm52_q8_slot",
353
+ gateUpSource: Q8_GATE_UP_SOURCE,
354
+ downSource: Q8_DOWN_SOURCE,
355
+ weightsPerByte: 1,
356
+ dimensionMultiple: 1,
357
+ subject: "GLM MTP Metal streamed expert",
358
+ });
359
+ }
360
+ }
@@ -0,0 +1,38 @@
1
+ import { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+
4
+
5
+ /** tanh(x / cap) * cap with weak-scalar semantics, UNFUSED (standalone div, tanh,
6
+ * mul), as plain-Python oracle sites compose it. The mx.compile'd one
7
+ * (gemma4_text.py) is `compiledLogitSoftcap` in softcap.ts; the two are
8
+ * separate on purpose so each site dispatches the same kernels as its oracle. */
9
+ export function logitSoftcap(logits: MlxArray, cap: number): MlxArray {
10
+ const capArr = ops.scalarLike(cap, logits);
11
+ const scaled = ops.div(logits, capArr);
12
+ const t = ops.tanh(scaled);
13
+ scaled.dispose();
14
+ const out = ops.mul(t, capArr);
15
+ t.dispose();
16
+ capArr.dispose();
17
+ return out;
18
+ }
19
+
20
+ /** argmax over vocab at the last sequence position of [1, L, V]. */
21
+ export function argmaxLastPosition(logits: MlxArray): number {
22
+ const [, L, V] = logits.shape as [number, number, number];
23
+ const last = logits.slice([0, L - 1, 0], [1, L, V]);
24
+ const am = ops.argmaxAxis(last, -1);
25
+ const id = ops.itemUint32(am);
26
+ last.dispose();
27
+ am.dispose();
28
+ return id;
29
+ }
30
+
31
+ /** Last-position logits as f32 (for the parity harness). */
32
+ export function lastPositionLogits(logits: MlxArray): Float32Array {
33
+ const [, L, V] = logits.shape as [number, number, number];
34
+ const last = logits.slice([0, L - 1, 0], [1, L, V]);
35
+ const f32 = last.toFloat32();
36
+ last.dispose();
37
+ return f32;
38
+ }
@@ -0,0 +1,154 @@
1
+ import { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
3
+
4
+ // One-row matmul over MLX affine 3- or 4-bit, group-64 weights: y = x · Wᵀ for
5
+ // a single activation row, bit for bit MLX 0.32.2's affine_qmv_fast (the
6
+ // kernel MLX's quantized matmul runs at one row). Every output keeps MLX's
7
+ // arithmetic: each lane owns 16 values of every 512-value K step (lane·16 on),
8
+ // computes MLX's load_vector (x_thread, its bf16 sum) and qdot over them,
9
+ // adds each step's qdot into its partial from 0 in K order, and the 32
10
+ // partials meet in simd_sum. Only what does not touch a bit changes:
11
+ // - load_vector runs once per threadgroup, every 16-value chunk of x into
12
+ // threadgroup memory, where MLX recomputes it per simdgroup and K step (its
13
+ // bf16 sum chain is software-rounded on the M1 Max);
14
+ // - each masked weight field's float is formed as (q | 2^23 bits) − 2^23, which
15
+ // is float(q) exactly (q < 2^23), in place of an integer convert;
16
+ // - R = 4 rows per simdgroup and S = 16 simdgroups per threadgroup (MLX: 4
17
+ // and 2), so 64 rows share one threadgroup's x.
18
+ // Measured on the M1 Max, each layer's weights back to back, against MLX's
19
+ // quantized matmul at one row: 3-bit 5120 to 10240 outputs 0.082 ms against
20
+ // 0.100, 5120 to 6144 0.053 against 0.062, 6144 to 5120 0.054 against 0.062,
21
+ // 4-bit 5120 to 248320 (the head) 1.96 against 2.26; 4-bit projections of 1024
22
+ // to 12288 outputs level or up to 3% slower (left on MLX's).
23
+ const R = 4, S = 16;
24
+
25
+ const HEADER = String.raw`
26
+ // MLX 0.32.2 load_vector (quantized.h) at values_per_thread 16, bits 3 and 4.
27
+ template <typename T, int bits>
28
+ inline float mlxq_load_vector(const device T* x, thread float* x_thread) {
29
+ float sum = 0;
30
+ if (bits == 3) {
31
+ for (int i = 0; i < 16; i += 8) {
32
+ sum += x[i] + x[i + 1] + x[i + 2] + x[i + 3] + x[i + 4] + x[i + 5] +
33
+ x[i + 6] + x[i + 7];
34
+ x_thread[i] = x[i];
35
+ x_thread[i + 1] = x[i + 1] / 8.0f;
36
+ x_thread[i + 2] = x[i + 2] / 64.0f;
37
+ x_thread[i + 3] = x[i + 3] / 2.0f;
38
+ x_thread[i + 4] = x[i + 4] / 16.0f;
39
+ x_thread[i + 5] = x[i + 5] / 128.0f;
40
+ x_thread[i + 6] = x[i + 6] / 4.0f;
41
+ x_thread[i + 7] = x[i + 7] / 32.0f;
42
+ }
43
+ } else {
44
+ for (int i = 0; i < 16; i += 4) {
45
+ sum += x[i] + x[i + 1] + x[i + 2] + x[i + 3];
46
+ x_thread[i] = x[i];
47
+ x_thread[i + 1] = x[i + 1] / 16.0f;
48
+ x_thread[i + 2] = x[i + 2] / 256.0f;
49
+ x_thread[i + 3] = x[i + 3] / 4096.0f;
50
+ }
51
+ }
52
+ return sum;
53
+ }
54
+
55
+ // float(q) for a masked field q < 2^23: q in the mantissa of 2^23, minus 2^23.
56
+ #define MLXQ_F(q) (as_type<float>((uint)(q) | 0x4B000000u) - 8388608.0f)
57
+
58
+ // MLX 0.32.2 qdot (quantized.h) at values_per_thread 16, bits 3 and 4: the
59
+ // same products and additions in the same order.
60
+ template <int bits>
61
+ inline float mlxq_qdot(const device uint8_t* w, const threadgroup float* x_thread, float scale, float bias, float sum) {
62
+ float accum = 0;
63
+ if (bits == 3) {
64
+ for (int i = 0; i < 2; i++) {
65
+ x_thread += 8 * i;
66
+ w += 3 * i;
67
+ accum += MLXQ_F(w[0] & 0x07) * x_thread[0];
68
+ accum += MLXQ_F(w[0] & 0x38) * x_thread[1];
69
+ accum += MLXQ_F(w[0] & 0xc0) * x_thread[2];
70
+ accum += MLXQ_F(w[1] & 0x01) * (x_thread[2] * 256.0f);
71
+ accum += MLXQ_F(w[1] & 0x0e) * x_thread[3];
72
+ accum += MLXQ_F(w[1] & 0x70) * x_thread[4];
73
+ accum += MLXQ_F(w[1] & 0x80) * x_thread[5];
74
+ accum += MLXQ_F(w[2] & 0x03) * (x_thread[5] * 256.0f);
75
+ accum += MLXQ_F(w[2] & 0x1c) * x_thread[6];
76
+ accum += MLXQ_F(w[2] & 0xe0) * x_thread[7];
77
+ }
78
+ } else {
79
+ const device uint16_t* ws = (const device uint16_t*)w;
80
+ for (int i = 0; i < 4; i++) {
81
+ accum +=
82
+ (x_thread[4 * i] * MLXQ_F(ws[i] & 0x000f) +
83
+ x_thread[4 * i + 1] * MLXQ_F(ws[i] & 0x00f0) +
84
+ x_thread[4 * i + 2] * MLXQ_F(ws[i] & 0x0f00) +
85
+ x_thread[4 * i + 3] * MLXQ_F(ws[i] & 0xf000));
86
+ }
87
+ }
88
+ return scale * accum + sum * bias;
89
+ }
90
+ `;
91
+
92
+ const SOURCE = String.raw`
93
+ constexpr int BPL = 2 * BITS; // weight bytes per lane per K step
94
+ constexpr int ROWB = K * BITS / 8;
95
+ constexpr int GROUPS = K / 64;
96
+ const uint sg = simdgroup_index_in_threadgroup, lane = thread_index_in_simdgroup;
97
+ // MLX's x_thread for every 16-value chunk of x, and each chunk's sum.
98
+ threadgroup float xs[K];
99
+ threadgroup float ss[K / 16];
100
+ for (int c = (int)(sg * 32 + lane); c < K / 16; c += S * 32) {
101
+ thread float t[16];
102
+ ss[c] = mlxq_load_vector<T, BITS>(x + c * 16, t);
103
+ for (int j = 0; j < 16; j++) xs[c * 16 + j] = t[j];
104
+ }
105
+ threadgroup_barrier(mem_flags::mem_threadgroup);
106
+ const int row0 = (threadgroup_position_in_grid.y * S + sg) * R;
107
+ const device uint8_t* ws = (const device uint8_t*)w + row0 * ROWB + lane * BPL;
108
+ const device T* sc = scales + row0 * GROUPS + lane / 4;
109
+ const device T* bi = biases + row0 * GROUPS + lane / 4;
110
+ float result[R] = {0};
111
+ #pragma clang loop unroll_count(1)
112
+ for (int k = 0; k < K; k += 512) {
113
+ const float sum = ss[(k + lane * 16) / 16];
114
+ const threadgroup float* x_thread = xs + k + lane * 16;
115
+ for (int r = 0; r < R; r++) {
116
+ const float s = sc[r * GROUPS];
117
+ const float b = bi[r * GROUPS];
118
+ result[r] += mlxq_qdot<BITS>(ws + r * ROWB, x_thread, s, b, sum);
119
+ }
120
+ ws += 32 * BPL;
121
+ sc += 8;
122
+ bi += 8;
123
+ }
124
+ for (int r = 0; r < R; r++) {
125
+ const float v = simd_sum(result[r]);
126
+ if (lane == 0) y[row0 + r] = static_cast<T>(v);
127
+ }
128
+ `;
129
+
130
+ let kernel: MetalKernel | undefined;
131
+
132
+ /** Eligible: affine 3- or 4-bit, group 64, one row, K a multiple of 512 whose x_thread fits threadgroup memory
133
+ * (K <= 7680), N a multiple of 64. */
134
+ export function affineRowEligible(rows: number, w: MlxArray, scales: MlxArray, spec: { bits: number; groupSize: number; mode: string }): boolean {
135
+ if (spec.mode !== "affine" || spec.groupSize !== 64 || (spec.bits !== 3 && spec.bits !== 4)) return false;
136
+ const N = scales.shape[0]!, K = scales.shape[1]! * 64;
137
+ return rows === 1 && K % 512 === 0 && K <= 7680 && N % (R * S) === 0 && w.shape.length === 2;
138
+ }
139
+
140
+ /** x [..., K] (one row) · Wᵀ → [..., N] in x's dtype, for `bits`-bit weights: MLX's quantized matmul's bits.
141
+ * Borrows inputs. */
142
+ export function affineRow(x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray, bits: 3 | 4): MlxArray {
143
+ const K = x.shape.at(-1)!, M = x.size / K, N = scales.shape[0]!;
144
+ if (M !== 1) throw new Error(`affineRow: M=${M}, one row only`);
145
+ kernel ??= new MetalKernel({ name: "mlx_bun_affine_row", inputNames: ["x", "w", "scales", "biases"],
146
+ outputNames: ["y"], source: SOURCE, header: HEADER, ensureRowContiguous: true });
147
+ const [y] = kernel.apply([x, w, scales, biases], {
148
+ outputs: [{ shape: [...x.shape.slice(0, -1), N], dtype: x.dtype }],
149
+ grid: [32, N / R, 1], threadGroup: [32, S, 1],
150
+ templateDtypes: { T: x.dtype },
151
+ templateInts: { N, K, BITS: bits, R, S },
152
+ });
153
+ return y!;
154
+ }
@@ -0,0 +1,36 @@
1
+ import type { MlxArray } from "@mlx-bun/mlx/array";
2
+ import * as ops from "@mlx-bun/mlx/ops";
3
+
4
+ /** Run independent affine M=1 matrix-vector products in one native batched call.
5
+ * Weight/scales/bias views share their original buffers. MLX 0.32.2's
6
+ * flattened M>=2 path uses different arithmetic, so appending known tokens
7
+ * must explicitly select this layout when preserving decode state.
8
+ */
9
+ export function quantizedMatmulRows(
10
+ x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray | null,
11
+ spec: ops.QuantSpec,
12
+ ): MlxArray {
13
+ const shape = x.shape, width = shape[shape.length - 1]!;
14
+ const rows = shape.reduce((a, b) => a * b, 1) / width;
15
+ if (rows === 1) return ops.quantizedMatmul(x, w, scales, biases, spec);
16
+ if (spec.mode !== "affine")
17
+ throw new Error("Independent-row batched matmul requires affine quantization");
18
+ if (w.shape.length !== 2 || scales.shape.length !== 2 || (biases && biases.shape.length !== 2))
19
+ throw new Error("Independent-row matmul requires shared two-dimensional weights");
20
+ const views: MlxArray[] = [];
21
+ const view = (value: MlxArray, newShape: number[]) => {
22
+ const result = ops.reshape(value, newShape); views.push(result); return result;
23
+ };
24
+ let output: MlxArray | undefined;
25
+ try {
26
+ const input = view(x, [rows, 1, width]);
27
+ const weight = view(w, [1, ...w.shape]);
28
+ const scale = view(scales, [1, ...scales.shape]);
29
+ const bias = biases ? view(biases, [1, ...biases.shape]) : null;
30
+ output = ops.quantizedMatmul(input, weight, scale, bias, spec);
31
+ return ops.reshape(output, [...shape.slice(0, -1), output.shape.at(-1)!]);
32
+ } finally {
33
+ output?.dispose();
34
+ for (const value of views) value.dispose();
35
+ }
36
+ }