@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,445 @@
1
+ // Padding-aware attention masks for batched (B>1) serving — decode side.
2
+ //
3
+ // Companion to packages/inference/src/scoring/full-sequence.ts's buildBatchedPadMask, which builds the
4
+ // PREFILL mask ([B,1,L,L], offset 0, right-padded — the training case, already
5
+ // parity-proven by packages/training/tests/native/batch-e2e.test.ts). This module covers the
6
+ // DECODE case for batched serving: left-padded rows sharing one growing
7
+ // [B,H,S,D] KV buffer, where a step forwards N new query tokens at a nonzero
8
+ // offset. (The two builders should be consolidated when batched prefill is
9
+ // wired into the serving path — phase S1a, `02d723a:docs/design/batching.md`.)
10
+ //
11
+ // Why left-padding for decode: with rows right-aligned in the KV buffer, every
12
+ // row's next write lands in the SAME column, so one advancing offset serves
13
+ // the whole batch. Each row's RoPE position is still per-row (handled via the
14
+ // array-offset path, ropeOffsetArr / ops.ropeDynamic — not here); the only
15
+ // thing the mask must encode is (a) causality, (b) each row's left padding,
16
+ // and (c) the sliding window, if any.
17
+
18
+ import { cacheSignature, isPlainKvCache } from "./capabilities";
19
+ import { MlxArray } from "@mlx-bun/mlx/array";
20
+ import * as ops from "@mlx-bun/mlx/ops";
21
+ import { createCausalMask } from "../kernels/attention/masks";
22
+ import { runtimeValue } from "../runtime/config";
23
+ import { KVCache } from "./kv";
24
+ import { type AttentionCache, type AttentionRead, type BatchableCache, type Cache, type Mask } from "../contracts/mlx/cache";
25
+ import { AttentionMasks, causalLease, sdpaRead, unmaskedLease, withLease, type MaskLease } from "./attention-read";
26
+ import type { KvStorage } from "./kv-storage";
27
+
28
+ /** Padding-aware key-validity mask for batched DECODE with left-padded rows.
29
+ *
30
+ * The shared KV buffer has width S (== cache offset once this step's keys are
31
+ * written). Row b has `leftPad[b]` padding columns on the left, then its real
32
+ * keys. A step forwards N new query tokens whose absolute positions are
33
+ * S-N .. S-1 (N == 1 in the steady decode state; N > 1 only for a batched
34
+ * prefill chunk).
35
+ *
36
+ * Returns [B, 1, N, S] bool, true where query i of row b may attend to key j:
37
+ * allow[b,0,i,j] = (j <= S-N+i) causal (query i at abs pos S-N+i)
38
+ * AND (j >= leftPad[b]) key past row b's left padding
39
+ * AND (S-N+i < j+window) within the sliding window*
40
+ * (*only when windowSize !== null)
41
+ *
42
+ * Built from createCausalMask(N, S-N, windowSize) (the model's own [N, S] bool
43
+ * matrix) broadcast over B, AND-ed with a per-row [B,1,1,S] key-validity mask
44
+ * — exactly mirroring buildBatchedPadMask, but at a decode offset with
45
+ * left-padding instead of offset-0 right-padding. Caller owns the result. */
46
+ export function buildBatchedDecodeMask(
47
+ B: number,
48
+ N: number,
49
+ S: number,
50
+ leftPad: number[],
51
+ windowSize: number | null,
52
+ ): MlxArray {
53
+ // [N, S] causal(+window): queries at abs positions S-N..S-1, keys 0..S-1.
54
+ const causal2d = createCausalMask(N, S - N, windowSize); // [N, S] bool
55
+ const causal4d = ops.reshape(causal2d, [1, 1, N, S]);
56
+ causal2d.dispose();
57
+
58
+ // Per-row key validity: keyValid[b,0,0,j] = j >= leftPad[b]. Build as int32
59
+ // {0,1} then compare to bool, broadcasting over the query axis (i).
60
+ const keyValidData = new Int32Array(B * S);
61
+ for (let b = 0; b < B; b++) {
62
+ const pad = leftPad[b]!;
63
+ for (let j = 0; j < S; j++) keyValidData[b * S + j] = j >= pad ? 1 : 0;
64
+ }
65
+ const keyValidI = MlxArray.fromInt32(keyValidData, [B, 1, 1, S]);
66
+ const zero = MlxArray.fromInt32(new Int32Array([0]), []);
67
+ const keyValid = ops.less(zero, keyValidI); // 0 < x → bool, true where x==1
68
+ zero.dispose();
69
+ keyValidI.dispose();
70
+
71
+ const allow = ops.logicalAnd(causal4d, keyValid); // [1,1,N,S] & [B,1,1,S]
72
+ causal4d.dispose();
73
+ keyValid.dispose();
74
+ return allow; // [B, 1, N, S] bool
75
+ }
76
+
77
+ /** Cache wrapper for batched DECODE with left-padded rows. Delegates all KV
78
+ * storage to a real, shape-[B,H,S,D] cache (KVCache et al. are already
79
+ * B-generic), but overrides two things the stock single-sequence path gets
80
+ * wrong for a padded batch:
81
+ *
82
+ * - `makeMask` returns the per-row left-padding decode mask
83
+ * (buildBatchedDecodeMask) instead of the stock mask, which is EMPTY at
84
+ * N==1 and would let every row attend to its own (garbage) left padding.
85
+ * - `ropeOffsetArr` exposes each row's REAL position (= pre-write offset −
86
+ * leftPad[b]) as an int32 [B] array, so RoPE is applied per-row via the
87
+ * array-offset path (mlx fast_rope_dynamic — verified to rotate each row
88
+ * by its own offset). The scalar offset would mis-position every padded
89
+ * row by its pad amount.
90
+ *
91
+ * `leftPad` is fixed for the batch's lifetime (set when the left-padded decode
92
+ * buffer is assembled from per-row prefills). The position array is recomputed
93
+ * only when the inner offset advances (once per decode step), so the two reads
94
+ * within a step (K before updateAndFetch, Q after — see Attention.forward)
95
+ * return the SAME array; the attention graph holds it until eval, so it must
96
+ * not be freed mid-step. */
97
+ export class BatchedDecodeMaskCache implements Cache {
98
+ #ropeArr: MlxArray | null = null;
99
+ #ropeForOffset = -1;
100
+
101
+ /** A decorator, not a storage kind: never claims the inner cache's
102
+ * identity (isPlainKvCache(wrapper) must stay false — it is not a
103
+ * KVCache), but records what it wraps for diagnostics/persistence. */
104
+ signature(): string { return `kv:batched-mask:${cacheSignature(this.inner)}`; }
105
+
106
+ constructor(
107
+ private readonly inner: KvStorage,
108
+ private readonly B: number,
109
+ private readonly leftPad: number[],
110
+ private readonly window: number | null,
111
+ ) {}
112
+
113
+ get offset(): number {
114
+ return this.inner.offset;
115
+ }
116
+
117
+ get ropeOffsetArr(): MlxArray {
118
+ const off = this.inner.offset;
119
+ if (this.#ropeArr && this.#ropeForOffset === off) return this.#ropeArr;
120
+ this.#ropeArr?.dispose(); // prior step's array; its graph is already evaluated
121
+ const data = new Int32Array(this.B);
122
+ for (let b = 0; b < this.B; b++) data[b] = off - this.leftPad[b]!;
123
+ this.#ropeArr = MlxArray.fromInt32(data, [this.B]);
124
+ this.#ropeForOffset = off;
125
+ return this.#ropeArr;
126
+ }
127
+
128
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
129
+ return this.inner.updateAndFetch(k, v);
130
+ }
131
+
132
+ makeMask(N: number, _windowSize: number | null): Mask {
133
+ // forwardLayers calls makeMask BEFORE this step's updateAndFetch, so the
134
+ // KV the attention then fetches spans inner.offset + N keys. Build a fresh
135
+ // mask each step and hand the model ownership — forwardLayers disposes
136
+ // mask.arr after building the layer graph (mlx refcounts it into the sdpa
137
+ // nodes, so post-build dispose is safe), so no view bookkeeping is needed.
138
+ const S = this.inner.offset + N;
139
+ return { mode: "array", arr: buildBatchedDecodeMask(this.B, N, S, this.leftPad, this.window) };
140
+ }
141
+
142
+ /** Free the per-step RoPE position array WITHOUT disposing the delegated KV
143
+ * (inner). For callers that rebuild a fresh wrapper around a persistent inner
144
+ * cache each decode step (the dynamic-B scheduler / parity harness) — the
145
+ * inner outlives the wrapper, so wrapper.dispose() would be wrong. */
146
+ releaseRopeArr(): void {
147
+ this.#ropeArr?.dispose();
148
+ this.#ropeArr = null;
149
+ this.#ropeForOffset = -1;
150
+ }
151
+
152
+ state(): MlxArray[] {
153
+ return this.inner.state();
154
+ }
155
+ isTrimmable(): boolean {
156
+ return this.inner.isTrimmable();
157
+ }
158
+ trim(n: number): void {
159
+ this.inner.trim(n);
160
+ }
161
+ dispose(): void {
162
+ this.inner.dispose();
163
+ this.#ropeArr?.dispose();
164
+ this.#ropeArr = null;
165
+ }
166
+ }
167
+
168
+ // --- Dynamic-B cache ops (continuous batching) ---------------------------
169
+ // Ports of mlx-lm's per-cache batch methods (merge / filter), the operations
170
+ // the --batch N scheduler needs that our fixed-B verified forward doesn't have.
171
+ // They run per layer on the raw [.,H,.,D] K/V; the scheduler applies them
172
+ // across layers and wraps the result in a BatchedDecodeMaskCache. Pure array
173
+ // surgery; numerical correctness is gated separately vs an mlx-lm dynamic golden.
174
+
175
+ /** mlx-lm `merge`: stack N single-row KV slices into one left-padded
176
+ * [B,H,Smax,D] batch. Each row is its sequence's KV sliced to its true length
177
+ * L_i; we left-pad each by Smax−L_i so all rows align on the right edge (the
178
+ * layout decode + BatchedDecodeMaskCache expect). Generalizes
179
+ * realBatchedGreedy's assemble to N rows. Caller owns the result; the input
180
+ * row arrays are not disposed. */
181
+ export function mergeKVRows(
182
+ rows: { keys: MlxArray; values: MlxArray }[],
183
+ ): { keys: MlxArray; values: MlxArray; leftPad: number[]; width: number } {
184
+ const lens = rows.map((r) => r.keys.shape[2]!);
185
+ const width = Math.max(...lens);
186
+ const leftPad = lens.map((l) => width - l);
187
+ const leftPadTo = (a: MlxArray, pad: number): MlxArray => {
188
+ if (pad === 0) return a.slice([0, 0, 0, 0], a.shape as number[]); // fresh full view
189
+ const [B, H, , D] = a.shape as [number, number, number, number];
190
+ const z = ops.zeros([B, H, pad, D], a.dtype);
191
+ const out = ops.concatAxis([z, a], 2);
192
+ z.dispose();
193
+ return out;
194
+ };
195
+ const ks = rows.map((r, i) => leftPadTo(r.keys, leftPad[i]!));
196
+ const vs = rows.map((r, i) => leftPadTo(r.values, leftPad[i]!));
197
+ const keys = ops.concatAxis(ks, 0);
198
+ const values = ops.concatAxis(vs, 0);
199
+ for (const a of [...ks, ...vs]) a.dispose();
200
+ return { keys, values, leftPad, width };
201
+ }
202
+
203
+ /** mlx-lm `BatchKVCache.extend`: append ONE new right-justified row to an
204
+ * existing batched [B,H,S,D] buffer WITHOUT re-extracting the running rows
205
+ * (the O(B·S) slice storm mergeKVRows pays on a join). Pad whichever side
206
+ * is narrower on the LEFT to the common width, then a single B-axis concat.
207
+ * Existing rows' left-pads grow by the batch-side pad (they never shrink —
208
+ * mlx-lm extend semantics; the decode mask covers pads either way).
209
+ * Caller owns the result; inputs are not disposed. */
210
+ export function extendKVRows(
211
+ keys: MlxArray, values: MlxArray, leftPad: number[],
212
+ row: { keys: MlxArray; values: MlxArray },
213
+ ): { keys: MlxArray; values: MlxArray; leftPad: number[]; width: number } {
214
+ const S = keys.shape[2]!;
215
+ const Lr = row.keys.shape[2]!;
216
+ const width = Math.max(S, Lr);
217
+ const padTo = (a: MlxArray, pad: number): MlxArray => {
218
+ if (pad === 0) return a.slice([0, 0, 0, 0], a.shape as number[]); // fresh full view
219
+ const [B, H, , D] = a.shape as [number, number, number, number];
220
+ const z = ops.zeros([B, H, pad, D], a.dtype);
221
+ const out = ops.concatAxis([z, a], 2);
222
+ z.dispose();
223
+ return out;
224
+ };
225
+ const bk = padTo(keys, width - S);
226
+ const bv = padTo(values, width - S);
227
+ const rk = padTo(row.keys, width - Lr);
228
+ const rv = padTo(row.values, width - Lr);
229
+ const outK = ops.concatAxis([bk, rk], 0);
230
+ const outV = ops.concatAxis([bv, rv], 0);
231
+ for (const a of [bk, bv, rk, rv]) a.dispose();
232
+ return {
233
+ keys: outK,
234
+ values: outV,
235
+ leftPad: [...leftPad.map((p) => p + (width - S)), width - Lr],
236
+ width,
237
+ };
238
+ }
239
+
240
+ /** mlx-lm `BatchKVCache.extract` (models/cache.py:1080): pull row `i` out of
241
+ * a batched full-attention cache into a fresh SERIAL KVCache — the row slice
242
+ * past its left padding, materialized as an OWNED contiguous copy (extraction
243
+ * outlives the batch; the source buffers are about to be filter()ed or
244
+ * disposed). `leftPad` is the scheduler's per-row padding for this row (full
245
+ * layers share one offset; the scheduler owns the pad list). Bit-exact vs a
246
+ * solo run by construction: merge/extend/filter are byte-preserving per row
247
+ * (`02d723a:tests/parity/batched-decode-parity.test.ts`,
248
+ * `02d723a:tests/unit/batched-extract.test.ts`) and this is a pure
249
+ * slice+copy of those bytes. Off the hot decode path. */
250
+ export function extractKVRow(cache: KVCache, leftPad: number, i: number): KVCache {
251
+ if (!cache.keys || !cache.values) throw new Error("extractKVRow: empty cache");
252
+ const S = cache.offset; // valid columns (buffer may be STEP-padded past it)
253
+ const cut = (a: MlxArray): MlxArray => {
254
+ const [, H, , D] = a.shape as [number, number, number, number];
255
+ const view = a.slice([i, 0, leftPad, 0], [i + 1, H, S, D]);
256
+ const own = ops.copyOf(view); // TRUE copy: contiguous(view) is a no-op VIEW when already contiguous — pins the source buffer (2026-08-20 DeltaNet conv leak class)
257
+ view.dispose();
258
+ return own;
259
+ };
260
+ const out = new KVCache();
261
+ out.restoreState(cut(cache.keys), cut(cache.values), S - leftPad);
262
+ return out;
263
+ }
264
+
265
+ /** mlx-lm `filter`: keep only `keep` (sorted row indices) along the batch
266
+ * axis — eviction of finished sequences from a batched [B,H,S,D] buffer.
267
+ * `trimLeft` removes padding shared by all survivors (the caller updates
268
+ * offsets and per-row padding). Caller owns the result; inputs stay owned. */
269
+ export function filterKVRows(
270
+ keys: MlxArray, values: MlxArray, keep: number[], trimLeft = 0,
271
+ ): { keys: MlxArray; values: MlxArray } {
272
+ const idx = MlxArray.fromInt32(Int32Array.from(keep), [keep.length]);
273
+ const take = (a: MlxArray) => {
274
+ const rows = ops.takeAxis(a, idx, 0);
275
+ if (trimLeft === 0) return rows;
276
+ const trimmed = rows.slice([0, 0, trimLeft, 0], rows.shape);
277
+ rows.dispose();
278
+ return trimmed;
279
+ };
280
+ const k = take(keys);
281
+ const v = take(values);
282
+ idx.dispose();
283
+ return { keys: k, values: v };
284
+ }
285
+
286
+ /** The layout of a running batch's plain full-attention KV: one [B,H,S,D] buffer
287
+ * whose rows are right-aligned (left-padded), so every row appends into the same
288
+ * column (mlx-lm BatchKVCache). It composes the primitives above: `mergeRows`
289
+ * joins one solo row to a running batch (`extendKVRows`, or the O(B·S) re-merge
290
+ * with MLX_BUN_BATCH_EXTEND=0), `filterRows` evicts rows and their shared
291
+ * padding, `extractRow` pulls a row back out as an owned serial cache. While any
292
+ * row is padded the layout gives attention the padding-aware decode mask and
293
+ * per-row RoPE positions (BatchedDecodeMaskCache); while none is, it is exactly
294
+ * the serial cache, so a batch of equal-length rows runs the serial graph.
295
+ * Sources of `mergeRows` are borrowed: a running layout or one serial KVCache,
296
+ * then one serial KVCache per joining row.
297
+ *
298
+ * Reads: append to the shared buffer, then the stock fused SDPA. While no row
299
+ * is padded, decode passes no mask and a window the fused causal mask, as the
300
+ * serial cache does; while one is, both pass the padding-aware mask
301
+ * (`buildBatchedDecodeMask`) for their query count, built once per forward
302
+ * through `masks`. */
303
+ export class PaddedKVRows implements AttentionCache, BatchableCache {
304
+ #inner = new KVCache();
305
+ #leftPad: number[] = [];
306
+ #padded = false;
307
+ #view: BatchedDecodeMaskCache | null = null;
308
+
309
+ constructor(
310
+ /** The per-forward masks of the model this layout serves. */
311
+ readonly masks = new AttentionMasks(),
312
+ ) {}
313
+
314
+ signature(): string { return "kv:padded-rows"; }
315
+ get batchSize(): number | null { return this.#leftPad.length || null; }
316
+ get leftPad(): readonly number[] { return this.#leftPad; }
317
+ get rowOffsets(): readonly number[] { return this.#leftPad.map((pad) => this.#inner.offset - pad); }
318
+ get offset(): number { return this.#inner.offset; }
319
+ /** Present only while a row is padded: each row's own position. */
320
+ get ropeOffsetArr(): MlxArray | undefined { return this.#padded ? this.#step().ropeOffsetArr : undefined; }
321
+ makeEmptyBatch(): PaddedKVRows { return new PaddedKVRows(this.masks); }
322
+
323
+ appendDecode(k: MlxArray, v: MlxArray): AttentionRead {
324
+ return this.#read(k, v, this.#mask(1, unmaskedLease));
325
+ }
326
+
327
+ appendWindow(k: MlxArray, v: MlxArray): AttentionRead {
328
+ return this.#read(k, v, this.#mask(k.shape[2]!, causalLease));
329
+ }
330
+
331
+ #read(k: MlxArray, v: MlxArray, mask: MaskLease): AttentionRead {
332
+ return withLease(mask, held => {
333
+ const [keys, values] = this.#inner.updateAndFetch(k, v);
334
+ return sdpaRead(keys, values, held);
335
+ });
336
+ }
337
+
338
+ /** `unpadded` while no row is padded; otherwise the padding-aware mask of an
339
+ * `N`-position append, before it. */
340
+ #mask(N: number, unpadded: MaskLease): MaskLease {
341
+ if (!this.#padded) return unpadded;
342
+ const S = this.#inner.offset + N;
343
+ return this.masks.lease("padded-kv", `${N}|${S}|${this.#leftPad.join(",")}`,
344
+ () => ({ mode: "array", arr: buildBatchedDecodeMask(this.#leftPad.length, N, S, this.#leftPad, null) }));
345
+ }
346
+
347
+ bytesPerToken(): number {
348
+ return this.#leftPad.length ? this.#inner.bytesPerToken() / this.#leftPad.length : 0;
349
+ }
350
+ projectedBytes(tokens: number): number { return this.bytesPerToken() * tokens; }
351
+
352
+ #step(): BatchedDecodeMaskCache {
353
+ return this.#view ??= new BatchedDecodeMaskCache(this.#inner, this.#leftPad.length, [...this.#leftPad], null);
354
+ }
355
+ updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#inner.updateAndFetch(k, v); }
356
+ makeMask(N: number, windowSize: number | null): Mask {
357
+ return this.#padded ? this.#step().makeMask(N, windowSize) : this.#inner.makeMask(N, windowSize);
358
+ }
359
+ /** Free this step's RoPE positions after the step's graph is built; the KV stays. */
360
+ releaseRopeArr(): void { this.#view?.releaseRopeArr(); }
361
+ state(): MlxArray[] { return this.#inner.state(); }
362
+ isTrimmable(): boolean { return this.#inner.isTrimmable(); }
363
+ trim(n: number): void { this.#inner.trim(n); }
364
+
365
+ #set(inner: KVCache, leftPad: number[]): void {
366
+ this.#view?.releaseRopeArr(); this.#view = null;
367
+ this.#inner = inner; this.#leftPad = leftPad; this.#padded = leftPad.some((pad) => pad !== 0);
368
+ }
369
+
370
+ mergeRows(rows: readonly Cache[]): void {
371
+ if (this.#leftPad.length) throw new Error("PaddedKVRows.mergeRows needs an empty batch");
372
+ if (!rows.length) return;
373
+ const held = new Set<MlxArray>();
374
+ const own = (array: MlxArray): MlxArray => { held.add(array); return array; };
375
+ type Part = { keys: MlxArray; values: MlxArray; pads: number[] };
376
+ const partOf = (cache: Cache): Part => {
377
+ if (cache instanceof PaddedKVRows) {
378
+ const [keys, values] = cache.#inner.temporalView();
379
+ return { keys: own(keys), values: own(values), pads: [...cache.#leftPad] };
380
+ }
381
+ if (!isPlainKvCache(cache)) throw new Error(`full-attention batch cannot merge ${cache.signature()}`);
382
+ const [keys, values] = cache.temporalView();
383
+ return { keys: own(keys), values: own(values), pads: [0] };
384
+ };
385
+ const extend = runtimeValue("MLX_BUN_BATCH_EXTEND") !== "0";
386
+ try {
387
+ let acc = partOf(rows[0]!);
388
+ if (rows.length === 1) {
389
+ const single = mergeKVRows(acc.pads.map((pad, b) => ({
390
+ keys: own(acc.keys.slice([b, 0, pad, 0], [b + 1, acc.keys.shape[1]!, acc.keys.shape[2]!, acc.keys.shape[3]!])),
391
+ values: own(acc.values.slice([b, 0, pad, 0], [b + 1, acc.values.shape[1]!, acc.values.shape[2]!, acc.values.shape[3]!])),
392
+ })));
393
+ acc = { keys: own(single.keys), values: own(single.values), pads: single.leftPad };
394
+ }
395
+ for (const source of rows.slice(1)) {
396
+ const row = partOf(source);
397
+ if (row.pads.length !== 1) throw new Error("PaddedKVRows joins one solo row at a time");
398
+ let joined: { keys: MlxArray; values: MlxArray; leftPad: number[] };
399
+ if (extend) {
400
+ // mlx-lm BatchKVCache.extend: one pad and one concat; existing pads grow, never shrink.
401
+ joined = extendKVRows(acc.keys, acc.values, acc.pads, row);
402
+ } else {
403
+ // The O(B·S) re-merge re-normalizes every row's padding.
404
+ const [, H, off, D] = acc.keys.shape as [number, number, number, number];
405
+ const vD = acc.values.shape[3]!;
406
+ const rowsOut = acc.pads.map((pad, b) => ({
407
+ keys: own(acc.keys.slice([b, 0, pad, 0], [b + 1, H, off, D])),
408
+ values: own(acc.values.slice([b, 0, pad, 0], [b + 1, H, off, vD])),
409
+ }));
410
+ joined = mergeKVRows([...rowsOut, row]);
411
+ }
412
+ acc = { keys: own(joined.keys), values: own(joined.values), pads: joined.leftPad };
413
+ }
414
+ held.delete(acc.keys); held.delete(acc.values);
415
+ const cache = new KVCache();
416
+ cache.restoreState(acc.keys, acc.values, acc.keys.shape[2]!);
417
+ this.#set(cache, acc.pads);
418
+ } finally { for (const array of held) array.dispose(); }
419
+ }
420
+
421
+ extractRow(row: number): Cache {
422
+ return this.#inner.keys ? extractKVRow(this.#inner, this.#leftPad[row]!, row) : new KVCache();
423
+ }
424
+
425
+ filterRows(keep: readonly number[]): void {
426
+ if (!keep.length) { this.dispose(); return; }
427
+ const kept = keep.map((row) => this.#leftPad[row]!);
428
+ // mlx-lm BatchKVCache.filter removes the padding every survivor shares.
429
+ const shared = Math.min(...kept);
430
+ const [keys, values] = this.#inner.temporalView();
431
+ let filtered: { keys: MlxArray; values: MlxArray };
432
+ try { filtered = filterKVRows(keys, values, [...keep], shared); }
433
+ finally { keys.dispose(); values.dispose(); }
434
+ const cache = new KVCache();
435
+ cache.restoreState(filtered.keys, filtered.values, this.#inner.offset - shared);
436
+ this.#inner.dispose();
437
+ this.#set(cache, kept.map((pad) => pad - shared));
438
+ }
439
+
440
+ dispose(): void {
441
+ this.#inner.dispose();
442
+ this.#set(new KVCache(), []);
443
+ this.masks.clear();
444
+ }
445
+ }