@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,90 @@
1
+ import { runtimeConfig, type RuntimeConfig } from "../runtime/config";
2
+ import type { MlxArray } from "@mlx-bun/mlx/array";
3
+ import * as tq from "../kernels/turboquant/ops";
4
+ import type { KvCodec } from "../contracts/mlx/kv-codec";
5
+
6
+ /** Resolve once when a cache is created; storage copies retain this policy. */
7
+ export function turboQuantFusedDecode(config: RuntimeConfig = runtimeConfig()): boolean {
8
+ return config.flag("MLX_BUN_TURBOQUANT_FUSED_DECODE", true);
9
+ }
10
+
11
+ /** One TurboQuant-encoded (K, V) storage tuple — the 5 arrays
12
+ * packages/inference/src/state/persistence.ts
13
+ * and toQuantized/fromKVCache pass around. Not `ops.QuantizedTensor`
14
+ * (mlx's affine int4/int8 scheme): this is the rotation + Lloyd-Max
15
+ * layout (`02d723a:docs/design/turboquant.md`), asymmetric-affine for keys,
16
+ * FWHT+Lloyd-Max for values. Field order is the persistence.ts tensor-slot
17
+ * contract — do not reorder without updating snapshotCache/loadKvCache. */
18
+ export interface TurboQuantTensor {
19
+ kIdx: MlxArray;
20
+ kScales: MlxArray;
21
+ kZeros: MlxArray;
22
+ vPacked: MlxArray;
23
+ vScales: MlxArray;
24
+ }
25
+
26
+ export const disposeTurboQuant = (t: TurboQuantTensor): void => {
27
+ t.kIdx.dispose();
28
+ t.kScales.dispose();
29
+ t.kZeros.dispose();
30
+ t.vPacked.dispose();
31
+ t.vScales.dispose();
32
+ };
33
+
34
+ /** Numerical representation only. Storage owns capacity, positions, row
35
+ * membership and persistence; this codec retains the existing kernel choices. */
36
+ export class TurboQuantCodec implements KvCodec<TurboQuantTensor> {
37
+ constructor(readonly kBits: number, readonly vBits: number, readonly fusedDecode: boolean) {}
38
+
39
+ /** Encode k/v [B,H,L,D] into a fresh TurboQuantTensor (packed at rest). */
40
+ encode(k: MlxArray, v: MlxArray): TurboQuantTensor {
41
+ const signed = this.kBits === 8;
42
+ const kEnc = tq.encodeKeys(k, this.kBits, signed);
43
+ const kIdxPacked = this.kBits < 8 ? tq.packBits(kEnc.indices, this.kBits) : kEnc.indices;
44
+ if (kIdxPacked !== kEnc.indices) kEnc.indices.dispose();
45
+ const vEnc = tq.encodeValues(v, this.vBits);
46
+ const vIdxPacked = this.vBits < 8 ? tq.packBits(vEnc.indices, this.vBits) : vEnc.indices;
47
+ if (vIdxPacked !== vEnc.indices) vEnc.indices.dispose();
48
+ return {
49
+ kIdx: kIdxPacked, kScales: kEnc.scales, kZeros: kEnc.zeros,
50
+ vPacked: vIdxPacked, vScales: vEnc.scales,
51
+ };
52
+ }
53
+
54
+ /** Decode a TurboQuantTensor's active [B,H,upTo,*] window back to
55
+ * bf16 [B,H,upTo,headDim] (k, v). Caller owns the returned arrays.
56
+ * deferV leaves V in the rotated domain (decodeValuesRotated) — the
57
+ * caller un-rotates its attention output instead (tq.unrotateValues). */
58
+ decode(t: TurboQuantTensor, upTo: number, headDim: number, deferV = false): [MlxArray, MlxArray] {
59
+ const cut = (a: MlxArray): MlxArray => {
60
+ const [B, H, , D] = a.shape as [number, number, number, number];
61
+ return a.slice([0, 0, 0, 0], [B, H, upTo, D]);
62
+ };
63
+ if (this.fusedDecode) {
64
+ const inputs = [cut(t.kIdx), cut(t.kScales), cut(t.kZeros), cut(t.vPacked), cut(t.vScales)] as const;
65
+ try {
66
+ const decoded = tq.tryDecodePackedKv(inputs, this.kBits, this.vBits, headDim, deferV);
67
+ if (decoded) return decoded;
68
+ } finally { for (const input of inputs) input.dispose(); }
69
+ }
70
+ const kIdxCut = cut(t.kIdx);
71
+ const kIdxUnpacked = this.kBits < 8 ? tq.unpackBits(kIdxCut, this.kBits, headDim) : kIdxCut;
72
+ if (kIdxUnpacked !== kIdxCut) kIdxCut.dispose();
73
+ const kScalesCut = cut(t.kScales);
74
+ const kZerosCut = cut(t.kZeros);
75
+ const k = tq.decodeKeys(kIdxUnpacked, kScalesCut, kZerosCut);
76
+ for (const a of [kIdxUnpacked, kScalesCut, kZerosCut]) a.dispose();
77
+
78
+ const vPackedCut = cut(t.vPacked);
79
+ const vIdxUnpacked = this.vBits < 8 ? tq.unpackBits(vPackedCut, this.vBits, headDim) : vPackedCut;
80
+ if (vIdxUnpacked !== vPackedCut) vPackedCut.dispose();
81
+ const vScalesCut = cut(t.vScales);
82
+ const v = deferV
83
+ ? tq.decodeValuesRotated(vIdxUnpacked, vScalesCut, this.vBits)
84
+ : tq.decodeValues(vIdxUnpacked, vScalesCut, this.vBits);
85
+ for (const a of [vIdxUnpacked, vScalesCut]) a.dispose();
86
+
87
+ return [k, v];
88
+ }
89
+
90
+ }
@@ -0,0 +1,442 @@
1
+ import { MlxArray } from "@mlx-bun/mlx/array";
2
+ import { Dtype } from "@mlx-bun/mlx/ffi";
3
+ import * as ops from "@mlx-bun/mlx/ops";
4
+ import type { AttentionRead,BlockAttentionCache,Cache,CommittedAttentionCache,CompiledDecodeCache,DecodeSlot,DecodeStepInputs,DecodeStepPlan,DecodeTrace,KvDonorAttention,KvDonorRows,Mask } from "../contracts/mlx/cache";
5
+ import type { RotatedValueAttentionState } from "./kv-storage";
6
+ import type { KvCodec } from "../contracts/mlx/kv-codec";
7
+ import * as tq from "../kernels/turboquant/ops";
8
+ import { TURBOQUANT_HEAD_DIMS } from "../kernels/turboquant/signs";
9
+ import { createCausalMask } from "../kernels/attention/masks";
10
+ import { decodedKvDonorAttention } from "./decoded-kv-donor";
11
+ import type { KVCache } from "./kv";
12
+ import type { RotatingKVCache } from "./rotating-kv";
13
+ import { TurboQuantCodec,disposeTurboQuant,turboQuantFusedDecode,type TurboQuantTensor } from "./turboquant-codec";
14
+ import { AttentionMasks, causalLease, sdpaRead, unmaskedLease } from "./attention-read";
15
+ import type { TurboQuantFetches, TurboQuantKernels } from "./turboquant-attention";
16
+
17
+ /** TurboQuant KV cache (`02d723a:docs/design/turboquant.md`): encoded at rest,
18
+ * decoded by its named reads with the kernel set the graph composed
19
+ * (`TurboQuantKernels`). Its own storage class, not a KVCache subclass; its
20
+ * row layouts are declared in `state/layout`. Only full-attention
21
+ * (non-rotating) layers use it; sliding-window layers stay bf16.
22
+ *
23
+ * Storage: 5 arrays per the vllm-metal reference's `turbo_quant_encode`
24
+ * tuple — kIdx (int8 unpacked at kBits=8, else bit-packed uint8),
25
+ * kScales/kZeros (fp16, one pair per 32-group), vPacked (bit-packed
26
+ * uint8, no-op at vBits=8), vScales (fp16, one per 32-group).
27
+ * updateAndFetch quantizes ONLY the newly appended tokens (unlike
28
+ * QuantizedKVCache's whole-buffer-toQuantized conversion path — the
29
+ * streaming append is the whole point of a live cache) and returns the
30
+ * DEQUANTIZED bf16 active window so ops.sdpa runs unmodified — v1 pays
31
+ * a full-window dequant every step; the deferred-InvFWHT trick is a
32
+ * documented non-goal until the quality gate passes.
33
+ *
34
+ * Reads: each named read appends and attends with the kernel set the cache
35
+ * was built with (`turboQuantKernels`): decoded values and the stock SDPA
36
+ * (`denseTurboQuantKernels`, Qwen 3.5's graph) or rotated values, the stock
37
+ * SDPA and the inverse rotation of its output (`deferredTurboQuantKernels`,
38
+ * Gemma 4's and MiniCPM5's). No caller sees rotated values. Decode passes no
39
+ * mask, a window the fused causal mask; at one query the two compute the same
40
+ * bits. A committed span is the window read: the graphs read TurboQuant
41
+ * storage the same way whether or not the positions are decided. This cache is
42
+ * full-attention only, so no read builds a mask array.
43
+ * Built empty, it encodes from token zero, unlike the served path's
44
+ * `Bf16FirstTurboQuantKVCache`, so its first chunk's numbers differ.
45
+ *
46
+ * Compiled decode: a concat slot. The stored prefix is decoded outside the
47
+ * recorded graph, in the domain the kernel set's read takes
48
+ * (`TurboQuantKernels.fetch`), with the codec's own decode; inside it the new
49
+ * position is encoded, decoded from its codes and joined to the prefix, and the
50
+ * set reads the joined window as the ordinary decode read does. Decoding is per
51
+ * position and the codec's fused kernel computes the eager decode's arithmetic,
52
+ * so the joined window equals the window the ordinary read decodes. The codes
53
+ * leave the graph unpacked; the write after the step packs and stores them. */
54
+ export class TurboQuantKVCache implements CommittedAttentionCache, BlockAttentionCache, CompiledDecodeCache {
55
+ minimumReusableOffset = 0;
56
+ readonly stateNeedsDispose = true;
57
+ static readonly STEP = 256;
58
+ /** Set only by compiled-decode trace adapters (see Cache); a real cache
59
+ * leaves it unset. */
60
+ readonly ropeOffsetArr?: MlxArray;
61
+ #kv: TurboQuantTensor | null = null;
62
+ offset = 0;
63
+ /** Validated lazily on first update, once head_dim is known. */
64
+ #headDim: number | null = null;
65
+ /** Experimental operation selection, captured once for this cache. */
66
+ readonly #codec: KvCodec<TurboQuantTensor>;
67
+
68
+ constructor(readonly kBits: number, readonly vBits: number,
69
+ /** How the named reads attend, named by the graph the cache serves; copies,
70
+ * row layouts and extracted rows keep it. */
71
+ readonly turboQuantKernels: TurboQuantKernels,
72
+ readonly fusedDecode = turboQuantFusedDecode(),
73
+ /** The per-forward masks of the model this cache belongs to. Row layouts
74
+ * made from this cache (`state/layout`) share them. */
75
+ readonly masks = new AttentionMasks()) {
76
+ this.#codec = new TurboQuantCodec(kBits, vBits, fusedDecode);
77
+ }
78
+
79
+ appendDecode(k: MlxArray, v: MlxArray): AttentionRead {
80
+ return this.turboQuantKernels.read(this.#fetches(k, v), unmaskedLease);
81
+ }
82
+
83
+ appendWindow(k: MlxArray, v: MlxArray): AttentionRead {
84
+ return this.turboQuantKernels.read(this.#fetches(k, v), causalLease);
85
+ }
86
+
87
+ /** The window read: a committed span on this storage (Qwen 3.5's
88
+ * `appendKnown`) attends with the same call as a window. */
89
+ appendCommitted(k: MlxArray, v: MlxArray): AttentionRead {
90
+ return this.appendWindow(k, v);
91
+ }
92
+
93
+ /** A block read (`BlockAttentionCache.readBlock`): the stored positions
94
+ * decoded, then the block's keys and values, not appended, through the stock
95
+ * fused SDPA with no mask (the dense read, whichever kernel set the cache
96
+ * composes, since the block's values are not in the rotated domain). */
97
+ readBlock(k: MlxArray, v: MlxArray): AttentionRead {
98
+ if (!this.#kv || this.#headDim === null) throw new Error("cache is empty");
99
+ const [storedK, storedV] = this.#decode(this.#kv, this.offset, this.#headDim, false);
100
+ let keys: MlxArray, values: MlxArray;
101
+ try {
102
+ keys = ops.concatAxis([storedK, k], 2);
103
+ try { values = ops.concatAxis([storedV, v], 2); }
104
+ catch (error) { keys.dispose(); throw error; }
105
+ } finally { storedK.dispose(); storedV.dispose(); }
106
+ return sdpaRead(keys, values, unmaskedLease);
107
+ }
108
+
109
+ /** The appends a kernel set chooses between for `k` and `v`. */
110
+ #fetches(k: MlxArray, v: MlxArray): TurboQuantFetches {
111
+ return {
112
+ decoded: () => this.updateAndFetch(k, v),
113
+ rotated: () => {
114
+ const [keys, values] = this.updateAndFetchDeferredV(k, v);
115
+ return { keys, values, restore: tq.unrotateValues };
116
+ },
117
+ };
118
+ }
119
+
120
+ captureDonorRows(): KvDonorRows {
121
+ if (!this.#kv || this.#headDim === null) throw new Error("cache is empty");
122
+ const [keys, values] = this.#decode(this.#kv, this.offset, this.#headDim, false);
123
+ const B = keys.shape[0]!;
124
+ return { keys, values, offsets: Array(B).fill(this.offset),
125
+ starts: Array(B).fill(0), ends: Array(B).fill(this.offset) };
126
+ }
127
+ captureDonorAttention(): KvDonorAttention { return decodedKvDonorAttention(this.captureDonorRows()); }
128
+
129
+ get rotatedValueAttention(): RotatedValueAttentionState { return this; }
130
+ signature(): string { return `kv:turboquant:${this.kBits}:${this.vBits}`; }
131
+
132
+ bytesPerToken(): number {
133
+ const steps = this.#seqLen();
134
+ if (steps === 0 || !this.#kv) return 0;
135
+ return [
136
+ this.#kv.kIdx, this.#kv.kScales, this.#kv.kZeros,
137
+ this.#kv.vPacked, this.#kv.vScales,
138
+ ].reduce((bytes, array) => bytes + array.nbytes, 0) / steps;
139
+ }
140
+
141
+ get headDim(): number | null {
142
+ return this.#headDim;
143
+ }
144
+
145
+ #validateHeadDim(dim: number): void {
146
+ if (this.#headDim !== null) return;
147
+ const supported: readonly number[] = TURBOQUANT_HEAD_DIMS;
148
+ if (dim % 32 !== 0 || !supported.includes(dim)) {
149
+ throw new Error(
150
+ `TurboQuantKVCache: head_dim ${dim} must be divisible by 32 and in {${supported.join(",")}}`,
151
+ );
152
+ }
153
+ this.#headDim = dim;
154
+ }
155
+
156
+ #packedKDim(dim: number): number {
157
+ return tq.packedDim(dim, this.kBits);
158
+ }
159
+ #packedVDim(dim: number): number {
160
+ return tq.packedDim(dim, this.vBits);
161
+ }
162
+ #nGroups(dim: number): number {
163
+ return dim / tq.TURBOQUANT_BLOCK_SIZE;
164
+ }
165
+
166
+ #alloc(B: number, H: number, T: number, dim: number): TurboQuantTensor {
167
+ const kIdxDtype = this.kBits === 8 ? Dtype.int8 : Dtype.uint8;
168
+ return {
169
+ kIdx: ops.zeros([B, H, T, this.#packedKDim(dim)], kIdxDtype),
170
+ kScales: ops.zeros([B, H, T, this.#nGroups(dim)], Dtype.float16),
171
+ kZeros: ops.zeros([B, H, T, this.#nGroups(dim)], Dtype.float16),
172
+ vPacked: ops.zeros([B, H, T, this.#packedVDim(dim)], Dtype.uint8),
173
+ vScales: ops.zeros([B, H, T, this.#nGroups(dim)], Dtype.float16),
174
+ };
175
+ }
176
+
177
+ #seqLen(): number {
178
+ return this.#kv ? this.#kv.kIdx.shape[2]! : 0;
179
+ }
180
+
181
+ #encode(k: MlxArray, v: MlxArray): TurboQuantTensor { return this.#codec.encode(k, v); }
182
+ #decode(t: TurboQuantTensor, upTo: number, headDim: number, deferV = false): [MlxArray, MlxArray] {
183
+ return this.#codec.decode(t, upTo, headDim, deferV);
184
+ }
185
+
186
+ /** Quantize the newly-appended k/v, append into growable packed
187
+ * storage (256-step growth, mirroring KVCache), advance offset, then
188
+ * dequantize the WHOLE active window (v1 semantics) so the standard
189
+ * ops.sdpa path runs unmodified. */
190
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
191
+ const D = this.#append(k, v);
192
+ return this.#decode(this.#kv!, this.offset, D);
193
+ }
194
+
195
+ /** Deferred-inverse-FWHT read path: same append, but the returned V
196
+ * window stays in the ROTATED domain. The caller MUST un-rotate its
197
+ * attention output with tq.unrotateValues (see SharedKv.vRotated) —
198
+ * same decode by linearity, O(q·d log d) per step instead of
199
+ * O(T·d log d). Opt-in per attention site; sites that don't know about
200
+ * rotation keep calling updateAndFetch and stay correct. */
201
+ updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
202
+ const D = this.#append(k, v);
203
+ return this.#decode(this.#kv!, this.offset, D, true);
204
+ }
205
+
206
+ /** Shared append: grow storage, encode the new tokens, slice-write them
207
+ * at the previous offset, advance offset. Returns head_dim. */
208
+ #append(k: MlxArray, v: MlxArray): number {
209
+ const [B, H, L, D] = k.shape as [number, number, number, number];
210
+ this.#validateHeadDim(D);
211
+ const prev = this.offset;
212
+ this.#reserve(B, H, L, D);
213
+
214
+ this.offset += L;
215
+ this.#write(this.#encode(k, v), prev);
216
+ return D;
217
+ }
218
+
219
+ /** Room for `L` more positions: 256-step growth, mirroring KVCache. */
220
+ #reserve(B: number, H: number, L: number, D: number): void {
221
+ const prev = this.offset;
222
+ if (!this.#kv || prev + L > this.#seqLen()) {
223
+ const nSteps = Math.floor((TurboQuantKVCache.STEP + L - 1) / TurboQuantKVCache.STEP);
224
+ const newSteps = nSteps * TurboQuantKVCache.STEP;
225
+ if (this.#kv && prev % TurboQuantKVCache.STEP !== 0) {
226
+ const trimTo = (t: TurboQuantTensor): TurboQuantTensor => {
227
+ const cut = (a: MlxArray): MlxArray => {
228
+ const [b, h, , d] = a.shape as [number, number, number, number];
229
+ const s = a.slice([0, 0, 0, 0], [b, h, prev, d]);
230
+ a.dispose();
231
+ return s;
232
+ };
233
+ return { kIdx: cut(t.kIdx), kScales: cut(t.kScales), kZeros: cut(t.kZeros),
234
+ vPacked: cut(t.vPacked), vScales: cut(t.vScales) };
235
+ };
236
+ this.#kv = trimTo(this.#kv);
237
+ }
238
+ const grown = this.#alloc(B, H, newSteps, D);
239
+ if (this.#kv) {
240
+ const cat = (a: MlxArray, b: MlxArray): MlxArray => {
241
+ const out = ops.concatAxis([a, b], 2);
242
+ a.dispose();
243
+ b.dispose();
244
+ return out;
245
+ };
246
+ this.#kv = {
247
+ kIdx: cat(this.#kv.kIdx, grown.kIdx),
248
+ kScales: cat(this.#kv.kScales, grown.kScales),
249
+ kZeros: cat(this.#kv.kZeros, grown.kZeros),
250
+ vPacked: cat(this.#kv.vPacked, grown.vPacked),
251
+ vScales: cat(this.#kv.vScales, grown.vScales),
252
+ };
253
+ } else {
254
+ this.#kv = grown;
255
+ }
256
+ }
257
+ }
258
+
259
+ /** Write encoded positions (taking ownership) at `prev`, up to the offset. */
260
+ #write(enc: TurboQuantTensor, prev: number): void {
261
+ const write = (dst: MlxArray, src: MlxArray): MlxArray => {
262
+ const [b, h, , d] = dst.shape as [number, number, number, number];
263
+ const u = ops.sliceUpdate(dst, src, [0, 0, prev, 0], [b, h, this.offset, d]);
264
+ dst.dispose();
265
+ src.dispose();
266
+ return u;
267
+ };
268
+ this.#kv = {
269
+ kIdx: write(this.#kv!.kIdx, enc.kIdx),
270
+ kScales: write(this.#kv!.kScales, enc.kScales),
271
+ kZeros: write(this.#kv!.kZeros, enc.kZeros),
272
+ vPacked: write(this.#kv!.vPacked, enc.vPacked),
273
+ vScales: write(this.#kv!.vScales, enc.vScales),
274
+ };
275
+ }
276
+
277
+ /** Compiled decode: the growth half of a one-position append, without the write. */
278
+ prepareDecodeStep(): DecodeStepPlan {
279
+ if (!this.#kv || this.#headDim === null) throw new Error("compiled decode on an empty cache");
280
+ const [B, H] = this.#kv.kIdx.shape as [number, number];
281
+ this.#reserve(B, H, 1, this.#headDim);
282
+ return { fetch: "concat", writePos: this.offset, activeLen: this.offset };
283
+ }
284
+
285
+ decodePhase(): DecodeStepPlan["fetch"] { return "concat"; }
286
+
287
+ decodeSlot(_plan: DecodeStepPlan): DecodeSlot {
288
+ return turboQuantDecodeSlot(this.offset, this.kBits, this.vBits, this.turboQuantKernels);
289
+ }
290
+
291
+ /** The stored prefix, decoded as the kernel set's read decodes it (owned by the step). */
292
+ decodeInputs(plan: DecodeStepPlan, step: DecodeStepInputs): MlxArray[] {
293
+ const [keys, values] = this.#decode(this.#kv!, plan.activeLen, this.#headDim!, this.turboQuantKernels.fetch === "rotated");
294
+ return [step.own(keys), step.own(values)];
295
+ }
296
+
297
+ /** The write half: pack the step's codes as the codec packs them and store
298
+ * them at the offset. Takes ownership of `outputs`; returns the storage to
299
+ * evaluate with the step. */
300
+ commitDecodeStep(_slot: DecodeSlot, outputs: MlxArray[]): MlxArray[] {
301
+ const [kCodes, kScales, kZeros, vCodes, vScales] = outputs as [MlxArray, MlxArray, MlxArray, MlxArray, MlxArray];
302
+ const kIdx = this.kBits < 8 ? tq.packBits(kCodes, this.kBits) : kCodes;
303
+ if (kIdx !== kCodes) kCodes.dispose();
304
+ const vPacked = this.vBits < 8 ? tq.packBits(vCodes, this.vBits) : vCodes;
305
+ if (vPacked !== vCodes) vCodes.dispose();
306
+ const prev = this.offset;
307
+ this.offset = prev + 1;
308
+ this.#write({ kIdx, kScales, kZeros, vPacked, vScales }, prev);
309
+ const kv = this.#kv!;
310
+ return [kv.kIdx, kv.kScales, kv.kZeros, kv.vPacked, kv.vScales];
311
+ }
312
+
313
+ /** Same rule as KVCache/QuantizedKVCache.makeMask (mlx-lm cache.py:114-125):
314
+ * windowless multi-token chunks are the string "causal" at any offset —
315
+ * TurboQuant v1 is full-attention-only, never windowed. */
316
+ makeMask(N: number, windowSize: number | null): Mask {
317
+ if (N === 1) return { mode: "", arr: null };
318
+ if (windowSize === null) return { mode: "causal", arr: null };
319
+ if (this.offset === 0 && N <= windowSize) return { mode: "causal", arr: null };
320
+ return { mode: "array", arr: createCausalMask(N, this.offset, windowSize) };
321
+ }
322
+
323
+ /** Trimmed-to-offset arrays in documented order: kIdx, kScales, kZeros,
324
+ * vPacked, vScales (packages/inference/src/state/persistence.ts tensor-slot ordering for kind
325
+ * "turboquant"). Empty cache → []. */
326
+ state(): MlxArray[] {
327
+ if (!this.#kv) return [];
328
+ const cut = (a: MlxArray): MlxArray => {
329
+ const [B, H, , D] = a.shape as [number, number, number, number];
330
+ return a.slice([0, 0, 0, 0], [B, H, this.offset, D]);
331
+ };
332
+ return [cut(this.#kv.kIdx), cut(this.#kv.kScales), cut(this.#kv.kZeros),
333
+ cut(this.#kv.vPacked), cut(this.#kv.vScales)];
334
+ }
335
+
336
+ isTrimmable(): boolean {
337
+ return true;
338
+ }
339
+
340
+ /** Drop the last n tokens (byte-safe: groups run along head_dim, so a
341
+ * token-axis slice never splits a group). Same front/back convention
342
+ * as KVCache.trim: offset shrinks, storage is overwritten on next write. */
343
+ trim(n: number): void {
344
+ this.offset = Math.max(0, this.offset - n);
345
+ }
346
+
347
+ /** Adopt persisted/converted state (takes ownership of the arrays). */
348
+ restoreState(t: TurboQuantTensor, offset: number, headDim: number): void {
349
+ this.dispose();
350
+ this.#kv = t;
351
+ this.offset = offset;
352
+ this.#headDim = headDim;
353
+ }
354
+
355
+ /** Convert an existing bf16 KVCache/RotatingKVCache's live window in one
356
+ * shot: quantize the whole [.., :offset, :] region, preserve offset,
357
+ * dispose the source's arrays. Mirrors KVCache.toQuantized/
358
+ * RotatingKVCache.toQuantized's contract (source cache is consumed).
359
+ * `kernels` is the converted cache's read: for a graph's KVCache, the read
360
+ * the graph composed into it (`KVCache.turboQuantKernels`). `fusedDecode` is
361
+ * the converted cache's decode policy; a caller that has resolved it passes
362
+ * it, the default reads the runtime configuration. */
363
+ static fromKVCache(cache: KVCache | RotatingKVCache, kBits: number, vBits: number, kernels: TurboQuantKernels,
364
+ fusedDecode = turboQuantFusedDecode()): TurboQuantKVCache {
365
+ const q = new TurboQuantKVCache(kBits, vBits, kernels, fusedDecode);
366
+ const [k, v] = cache.keys && cache.values ? cache.temporalView() : [null, null];
367
+ if (k && v) {
368
+ const D = k.shape[3]!;
369
+ q.#validateHeadDim(D);
370
+ const enc = q.#encode(k, v);
371
+ k.dispose();
372
+ v.dispose();
373
+ q.#kv = enc;
374
+ q.#headDim = D;
375
+ }
376
+ q.offset = cache.offset;
377
+ q.minimumReusableOffset = (cache as Cache).minimumReusableOffset ?? 0;
378
+ cache.dispose();
379
+ return q;
380
+ }
381
+
382
+ dispose(): void {
383
+ if (this.#kv) disposeTurboQuant(this.#kv);
384
+ this.#kv = null;
385
+ this.offset = 0;
386
+ this.minimumReusableOffset = 0;
387
+ this.masks.clear();
388
+ }
389
+ }
390
+
391
+ /** A TurboQuant cache inside a compiled decode trace: the new position is
392
+ * encoded and decoded from its codes in the graph, joined to the prefix the
393
+ * cache decoded outside it, and read by the cache's kernel set; the five codes
394
+ * (indices unpacked) are the closure outputs. */
395
+ class TracedTurboQuantKVCache implements DecodeTrace {
396
+ outs: MlxArray[] = [];
397
+ constructor(
398
+ readonly offset: number,
399
+ readonly kBits: number,
400
+ readonly vBits: number,
401
+ readonly kernels: TurboQuantKernels,
402
+ readonly prefixKeys: MlxArray,
403
+ readonly prefixValues: MlxArray,
404
+ readonly ropeOffsetArr: MlxArray,
405
+ ) {}
406
+
407
+ appendDecode(k: MlxArray, v: MlxArray): AttentionRead {
408
+ const kEnc = tq.encodeKeys(k, this.kBits, this.kBits === 8);
409
+ const vEnc = tq.encodeValues(v, this.vBits);
410
+ this.outs = [kEnc.indices, kEnc.scales, kEnc.zeros, vEnc.indices, vEnc.scales];
411
+ const rotated = this.kernels.fetch === "rotated";
412
+ const join = (prefix: MlxArray, row: MlxArray): MlxArray => {
413
+ try { return ops.concatAxis([prefix, row], 2); } finally { row.dispose(); }
414
+ };
415
+ const keys = join(this.prefixKeys, tq.decodeKeys(kEnc.indices, kEnc.scales, kEnc.zeros));
416
+ let values: MlxArray;
417
+ try {
418
+ values = join(this.prefixValues, rotated ? tq.decodeValuesRotated(vEnc.indices, vEnc.scales, this.vBits)
419
+ : tq.decodeValues(vEnc.indices, vEnc.scales, this.vBits));
420
+ } catch (error) { keys.dispose(); throw error; }
421
+ return this.kernels.read({
422
+ decoded: () => [keys, values],
423
+ rotated: () => ({ keys, values, restore: tq.unrotateValues }),
424
+ }, unmaskedLease);
425
+ }
426
+
427
+ signature(): string { return `kv:turboquant-trace:${this.kBits}:${this.vBits}`; }
428
+ state(): MlxArray[] { return []; }
429
+ isTrimmable(): boolean { return false; }
430
+ trim(): void { throw new Error("a compiled decode trace is not trimmed"); }
431
+ dispose(): void {}
432
+ }
433
+
434
+ /** The concat slot of a TurboQuant cache at `offset` whose reads take `kernels`.
435
+ * The kernel set is part of the graph shape: a dense and a deferred set trace
436
+ * different reads. */
437
+ export function turboQuantDecodeSlot(offset: number, kBits: number, vBits: number, kernels: TurboQuantKernels): DecodeSlot {
438
+ return {
439
+ key: `tq-cat:${kBits}:${vBits}:${kernels.fetch}`, fetch: "concat", inputs: 2, outputs: 5,
440
+ trace: (inputs, ropeOffset) => new TracedTurboQuantKVCache(offset, kBits, vBits, kernels, inputs[0]!, inputs[1]!, ropeOffset),
441
+ };
442
+ }
@@ -0,0 +1,26 @@
1
+ import { type Cache } from "../contracts/mlx/cache";
2
+ import { disposeResources } from "../runtime/resources";
3
+ import { isBatchableCache } from "./capabilities";
4
+ import { cloneKvCaches } from "./persistence";
5
+ import { type CacheCodecProvider } from "./persistence-types";
6
+
7
+ /** Precision/storage transitions constrain reuse independently of scheduling. */
8
+ export function minimumReusableOffset(caches: readonly Pick<Cache, "minimumReusableOffset">[]): number {
9
+ return Math.max(0, ...caches.map(cache => cache.minimumReusableOffset ?? 0));
10
+ }
11
+
12
+ /** Snapshot an adopted single-row state into persistence-compatible caches.
13
+ * Row layouts own compact extraction; ordinary serial caches use their codec. */
14
+ export function cloneSingleRowState(caches: readonly Cache[], codecs?: CacheCodecProvider): Cache[] {
15
+ const owned: Cache[] = [];
16
+ try {
17
+ for (const cache of caches) owned.push(...(isBatchableCache(cache)
18
+ ? [cache.extractRow(0)] : cloneKvCaches([cache], codecs)));
19
+ return owned;
20
+ } catch (error) {
21
+ try { disposeResources(owned); } catch (cleanupError) { throw new AggregateError([error, cleanupError], "single-row snapshot failed"); }
22
+ throw error;
23
+ }
24
+ }
25
+
26
+ export { leaseCacheState,leaseCacheStates } from "./leases";
@@ -0,0 +1,3 @@
1
+ export * from "./whisper/index";
2
+ // Voice-activity detection segments audio for transcription.
3
+ export { SileroVad, resolveSileroVadPath, type SpeechTimestampOptions, type VadSegment, type VadStreamState } from "./silero-vad";