@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,422 @@
1
+ // Gated DeltaNet recurrence for Qwen3.5 linear-attention layers.
2
+ //
3
+ // Port target: mlx_lm.models.gated_delta (compute_g + the `gated_delta_step`
4
+ // Metal kernel) and the recurrent-state cache. mlx-lm runs the GPU kernel by
5
+ // default (use_kernel = not training), and its float accumulation / simd_sum
6
+ // reduction order differ from the pure-ops fallback — so BIT-EXACT parity with
7
+ // mlx-lm requires the SAME kernel, dispatched with the SAME grid/threadgroup.
8
+ // We port the non-vectorized variant (g.ndim == 3). Padded prefill selects
9
+ // the oracle masked specialization; unpadded execution retains its kernel.
10
+ //
11
+ // Numerics (must match the reference dtypes exactly — mlx infers the kernel's
12
+ // pointer element types from the input arrays):
13
+ // q, k bf16 [B, T, Hk, Dk] (after inv_scale * rms_norm(., None))
14
+ // v bf16 [B, T, Hv, Dv]
15
+ // g f32 [B, T, Hv] (= exp(-exp(A_log_f32) * softplus(a+dt_bias)))
16
+ // beta bf16 [B, T, Hv] (= sigmoid(b))
17
+ // state_in f32 [B, Hv, Dv, Dk]
18
+ // y (out) bf16 [B, T, Hv, Dv] (InT)
19
+ // state_out f32 [B, Hv, Dv, Dk] (StT)
20
+ // GQA is handled inside the kernel (hk_idx = hv_idx / (Hv/Hk)); q/k stay at Hk.
21
+
22
+ import { MlxArray } from "@mlx-bun/mlx/array";
23
+ import { Dtype } from "@mlx-bun/mlx/ffi";
24
+ import { materializeCopy } from "@mlx-bun/mlx/materialize";
25
+ import * as ops from "@mlx-bun/mlx/ops";
26
+ import type { GatedDeltaCache, GatedDeltaParameters, Mask } from "../contracts/mlx/cache";
27
+ import { gatedDeltaUpdate } from "../kernels/delta/gated";
28
+ import { gatedDeltaState } from "../kernels/delta/state";
29
+ import { disposing } from "../layers/helpers";
30
+ import type { SsmPrefillPadding } from "./ssm-prefill-padding";
31
+
32
+ /** The causal convolution of `qkv` [B, S, convDim] over the stored `state`
33
+ * [B, K-1, convDim], copied from GatedDeltaNet's `#convolve` without the
34
+ * activation, which belongs to the block's glue (`layer.heads`). Returns the
35
+ * raw conv1d output and the next state: each row's last K-1 input columns,
36
+ * ending at `rowLengths[row]` when given, else at the window's end. Borrows
37
+ * its inputs. */
38
+ function convolve(qkv: MlxArray, state: MlxArray, weight: MlxArray, rowLengths?: readonly number[]): [MlxArray, MlxArray] {
39
+ const [, S, D] = qkv.shape as [number, number, number];
40
+ const input = ops.concatAxis([state, qkv], 1);
41
+ // MLX copy and contiguous can both alias the whole prefill buffer.
42
+ // Materialize only the tail so cache residency follows its logical size.
43
+ const tail = convTail(input, S, weight.shape[1]! - 1, rowLengths);
44
+ const conv = ops.conv1d(input, weight, 1, 0, 1, D);
45
+ input.dispose();
46
+ return [conv, tail];
47
+ }
48
+
49
+ function convTail(input: MlxArray, processed: number, nKeep: number, rowLengths?: readonly number[]): MlxArray {
50
+ const [B, , D] = input.shape as [number, number, number];
51
+ if (rowLengths) {
52
+ using indices = ops.fromInt32(rowLengths.flatMap(length =>
53
+ Array.from({ length: nKeep }, (_, position) => length + position)), [B, nKeep, 1]);
54
+ return ops.takeAlongAxis(input, indices, 1);
55
+ }
56
+ using view = input.slice([0, processed, 0], [B, processed + nKeep, D]);
57
+ return materializeCopy(view);
58
+ }
59
+
60
+ /** The recorded recurrence inputs of a verify window: the convolution inputs and
61
+ * the kernel's position-local inputs (the heads' k and v, the a and b
62
+ * projections), each [B, S, ...], and the layer they came from. The state after
63
+ * the first `keep` positions is the same arithmetic prefix whether or not the
64
+ * rest of the window was ever processed (the kernel walks positions in order;
65
+ * the convolution and the glue are per position), so a rejected tail needs no
66
+ * recomputation: the retained rows fold into the pre-round state. */
67
+ interface SsmWindowRows {
68
+ qkv: MlxArray;
69
+ k: MlxArray;
70
+ v: MlxArray;
71
+ a: MlxArray;
72
+ b: MlxArray;
73
+ layer: GatedDeltaParameters;
74
+ }
75
+
76
+ /** One armed speculative verify round on an SSMCache: the pre-round state
77
+ * snapshot (retained refs — MLX arrays are immutable, so this is free) plus
78
+ * the window's recorded rows. `recurDecode`/`recurWindow` record them. */
79
+ export interface SsmSpecRound {
80
+ /** True from specRoundBegin() until the layer's forward records. */
81
+ armed: boolean;
82
+ prevConv: MlxArray | null;
83
+ prevRecurrent: MlxArray | null;
84
+ prevOffset: number;
85
+ prevOffsets: number[] | null;
86
+ /** The window's recorded rows; null until the verify forward records. */
87
+ rows: SsmWindowRows | null;
88
+ /** Window length S of the recorded verify forward. */
89
+ S: number;
90
+ }
91
+
92
+ /** Recurrent cache for a gated-DeltaNet layer — port of mlx-lm
93
+ * cache.ArraysCache(size=2): slot 0 = causal-conv state [B, K-1, conv_dim],
94
+ * slot 1 = recurrent state [B, Hv, Dv, Dk] f32. `recurDecode` and
95
+ * `recurWindow` own both slots and the kernels that update them
96
+ * (GatedDeltaCache); the DeltaNet block hands them its projections. `advance(N)` only
97
+ * tracks token count for B=1 single-stream (lengths / left_padding are
98
+ * batched-decode concerns). Not a key/value cache: no KV scheme converts it.
99
+ *
100
+ * A rolled-back speculative round leaves the snapshot in the slots and its
101
+ * retained window rows pending; they fold in (the state-only kernel over the
102
+ * recorded rows, and the convolution tail) the next time anything reads the
103
+ * state: the next forward, `state()`, the slots, row moves or persistence. The
104
+ * rollback itself builds no graph. */
105
+ export class SSMCache implements GatedDeltaCache {
106
+ #conv: MlxArray | null = null;
107
+ #recurrent: MlxArray | null = null;
108
+ /** Rows a rolled-back round retained, not yet folded into the slots, with each row's kept count. */
109
+ #pending: { rows: SsmWindowRows; keep: readonly number[] } | null = null;
110
+ offset = 0;
111
+ prefillPadding: SsmPrefillPadding | null = null;
112
+ /** Live speculative verify round; null outside a verify transaction. */
113
+ specRound: SsmSpecRound | null = null;
114
+ /** Per-row token coverage (batch lane only; null on serial B=1 caches,
115
+ * where `offset` IS the row's count). mlx-lm's ArraysCache tracks no
116
+ * offset at all (cache.py:594-722) — per-sequence coverage is the
117
+ * server's bookkeeping; ours lives here because prompt-cache put()
118
+ * keys on EXACT coverage (#putOrDispose offset === tokens.length) and
119
+ * merged rows carry different totals. Seeded by mergeRows, advanced in
120
+ * lockstep by advance(), row-filtered by filter(). */
121
+ offsets: number[] | null = null;
122
+
123
+ /** The causal-conv state, with any retained rows folded in. */
124
+ get conv(): MlxArray | null { this.#fold(); return this.#conv; }
125
+ set conv(value: MlxArray | null) { this.#fold(); this.#conv = value; }
126
+ /** The recurrent state, with any retained rows folded in. */
127
+ get recurrent(): MlxArray | null { this.#fold(); return this.#recurrent; }
128
+ set recurrent(value: MlxArray | null) { this.#fold(); this.#recurrent = value; }
129
+
130
+ signature(): string { return "ssm"; }
131
+ bytesPerToken(): number { return 0; }
132
+ get batchSize(): number { return this.offsets?.length ?? 1; }
133
+
134
+ /** Row `i`'s own token coverage (per-row when batched, `offset` serial). */
135
+ rowOffset(i: number): number {
136
+ return this.offsets ? this.offsets[i]! : this.offset;
137
+ }
138
+
139
+ /** Linear-attn layers don't go through the KV update path. */
140
+ updateAndFetch(): [MlxArray, MlxArray] {
141
+ throw new Error("SSMCache has no KV updateAndFetch (gated-DeltaNet layer)");
142
+ }
143
+
144
+ makeMask(N: number, _windowSize: number | null): Mask {
145
+ const arr = this.prefillPadding?.makeMask(N) ?? null;
146
+ return { mode: arr ? "array" : "", arr };
147
+ }
148
+
149
+ recurDecode(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters): MlxArray {
150
+ return this.#recur(qkv, a, b, layer, null);
151
+ }
152
+
153
+ recurWindow(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters): MlxArray {
154
+ return this.#recur(qkv, a, b, layer, this.prefillPadding);
155
+ }
156
+
157
+ /** GatedDeltaNet.forward from its projections to the recurrence output,
158
+ * copied: zero the padded positions, convolve over the stored conv state,
159
+ * run the block's glue, run the gated-delta kernel over the stored
160
+ * recurrent state, replace both states and advance. Inside an armed round
161
+ * the replaced states and the inputs go to the round instead of being
162
+ * disposed, and the round replays through `layer`. */
163
+ #recur(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters,
164
+ padding: SsmPrefillPadding | null): MlxArray {
165
+ const [B, S, convDim] = qkv.shape as [number, number, number];
166
+ const nKeep = layer.convWeight.shape[1]! - 1;
167
+ this.#fold();
168
+
169
+ const spec = this.specRound;
170
+ if (spec) {
171
+ if (!spec.armed)
172
+ throw new Error("SSMCache: spec round already recorded this round");
173
+ spec.armed = false;
174
+ spec.S = S;
175
+ }
176
+
177
+ using mask = padding?.makeMask(S) ?? null;
178
+ if (mask) {
179
+ using expanded = ops.reshape(mask, [B, S, 1]);
180
+ using zero = MlxArray.fromBytesCopy(new Uint8Array(qkv.dtype === Dtype.float32 ? 4 : 2), [], qkv.dtype);
181
+ qkv = disposing(qkv, ops.where(expanded, qkv, zero));
182
+ }
183
+ // Convolution keeps each row's last real-token tail, including empty rows.
184
+ const convState = this.#conv ?? ops.zeros([B, nKeep, convDim], qkv.dtype);
185
+ const [conv, newConv] = convolve(qkv, convState, layer.convWeight, padding?.convolutionLengths(S));
186
+ if (!this.#conv) convState.dispose();
187
+ if (spec) spec.prevConv = this.#conv;
188
+ else this.#conv?.dispose();
189
+ this.#conv = newConv;
190
+
191
+ const { q, k, v } = layer.heads(conv);
192
+ conv.dispose();
193
+ const [out, newState] = gatedDeltaUpdate(q, k, v, a, b, layer.aLog, layer.dtBias, this.#recurrent, mask);
194
+ q.dispose();
195
+ if (spec) {
196
+ spec.rows = { qkv, k, v, a, b, layer };
197
+ spec.prevRecurrent = this.#recurrent;
198
+ } else {
199
+ for (const array of [qkv, k, v, a, b]) array.dispose();
200
+ this.#recurrent?.dispose();
201
+ }
202
+ this.#recurrent = newState;
203
+ this.advance(S);
204
+ return out;
205
+ }
206
+
207
+ /** Fold a rolled-back round's retained rows into the slots: each row's
208
+ * convolution tail ends at its kept count, and the state-only kernel walks the
209
+ * kept positions of the recorded k, v, a and b from the restored state. The
210
+ * values are the verify window's own, and the kernel's recurrence is the
211
+ * update kernel's, so both slots are bit for bit those of a call over only
212
+ * the retained positions. */
213
+ #fold(): void {
214
+ const pending = this.#pending;
215
+ if (!pending) return;
216
+ this.#pending = null;
217
+ const { rows, keep } = pending;
218
+ try {
219
+ const [B, , convDim] = rows.qkv.shape as [number, number, number];
220
+ const nKeep = rows.layer.convWeight.shape[1]! - 1;
221
+ const uniform = keep.every(count => count === keep[0]);
222
+ using zeros = this.#conv ? null : ops.zeros([B, nKeep, convDim], rows.qkv.dtype);
223
+ using input = ops.concatAxis([this.#conv ?? zeros!, rows.qkv], 1);
224
+ const conv = convTail(input, keep[0]!, nKeep, uniform ? undefined : keep);
225
+ let recurrent: MlxArray;
226
+ try { recurrent = gatedDeltaState(rows.k, rows.v, rows.a, rows.b, rows.layer.aLog, rows.layer.dtBias, this.#recurrent, keep); }
227
+ catch (error) { conv.dispose(); throw error; }
228
+ this.#conv?.dispose();
229
+ this.#recurrent?.dispose();
230
+ this.#conv = conv;
231
+ this.#recurrent = recurrent;
232
+ } finally { for (const array of [rows.qkv, rows.k, rows.v, rows.a, rows.b]) array.dispose(); }
233
+ }
234
+
235
+ advance(n: number): void {
236
+ if (this.prefillPadding) { this.advanceRows(this.prefillPadding.advance(n)); return; }
237
+ this.offset += n;
238
+ // Batched rows step together (one forward advances every row), so the
239
+ // per-row counts move in lockstep; they DIFFER only in their merge-time
240
+ // seeds (each row's prompt length).
241
+ if (this.offsets) for (let i = 0; i < this.offsets.length; i++) this.offsets[i]! += n;
242
+ }
243
+
244
+ /** Methods may retain different verified prefixes for each active request. */
245
+ advanceRows(counts: readonly number[]): void {
246
+ if (!this.offsets) { this.offset += counts[0]!; return; }
247
+ for (let row = 0; row < this.offsets.length; row++) this.offsets[row]! += counts[row]!;
248
+ this.offset = Math.max(...this.offsets);
249
+ }
250
+
251
+ state(): MlxArray[] {
252
+ this.#fold();
253
+ const out: MlxArray[] = [];
254
+ if (this.#conv) out.push(this.#conv);
255
+ if (this.#recurrent) out.push(this.#recurrent);
256
+ return out;
257
+ }
258
+
259
+ isTrimmable(): boolean {
260
+ return false; // recurrent state can't be trimmed per token — see specRound*
261
+ }
262
+
263
+ trim(_n: number): void {
264
+ throw new Error("SSMCache is not trimmable");
265
+ }
266
+
267
+ // --- speculative verify-round support -----------------------------------
268
+ // The serve loop arms a round before the verify forward and resolves it
269
+ // after the accept walk. The layer's forward, seeing an armed round, hands
270
+ // its replaced state slots to the round instead of disposing them and
271
+ // records the window's rows. Cache/interface contract: rollback(keep)
272
+ // restores the snapshot and retains the first `keep` window rows, which fold
273
+ // in bit-exactly before the state is next read; commit frees the snapshot
274
+ // and the recordings.
275
+
276
+ specRoundBegin(): void {
277
+ this.#dropSpecRound(); // defensive: a mid-round throw left a stale round
278
+ this.#fold();
279
+ this.specRound = {
280
+ armed: true,
281
+ prevConv: null,
282
+ prevRecurrent: null,
283
+ prevOffset: this.offset,
284
+ prevOffsets: this.offsets ? [...this.offsets] : null,
285
+ rows: null,
286
+ S: 0,
287
+ };
288
+ }
289
+
290
+ specRoundCommit(): void {
291
+ this.#dropSpecRound();
292
+ }
293
+
294
+ specRoundRollback(keep: number | readonly number[]): void {
295
+ const r = this.specRound;
296
+ if (!r) throw new Error("SSMCache.specRoundRollback without an armed round");
297
+ if (r.armed)
298
+ throw new Error("SSMCache.specRoundRollback before the verify forward recorded");
299
+ if (typeof keep === "number" ? keep < 0 || keep > r.S : keep.some(count => count < 0 || count > r.S))
300
+ throw new Error(`SSMCache.specRoundRollback keep=${keep} outside window S=${r.S}`);
301
+ if (!r.rows) throw new Error("SSMCache.specRoundRollback with no recorded window");
302
+ // Restore the pre-round snapshot (ownership moves back to the cache).
303
+ this.#conv?.dispose();
304
+ this.#recurrent?.dispose();
305
+ this.#conv = r.prevConv;
306
+ this.#recurrent = r.prevRecurrent;
307
+ r.prevConv = null;
308
+ r.prevRecurrent = null;
309
+ this.offset = r.prevOffset;
310
+ this.offsets = r.prevOffsets;
311
+ const rows = typeof keep === "number" ? Array<number>(r.rows.qkv.shape[0]!).fill(keep) : keep;
312
+ if (rows.some(count => count > 0)) {
313
+ // The kept rows move to the cache, which folds them in before the next read.
314
+ this.#pending = { rows: r.rows, keep: [...rows] };
315
+ r.rows = null;
316
+ }
317
+ if (typeof keep === "number") this.advance(keep);
318
+ else this.advanceRows(keep);
319
+ this.#dropSpecRound();
320
+ }
321
+
322
+ #dropSpecRound(): void {
323
+ const r = this.specRound;
324
+ if (!r) return;
325
+ r.prevConv?.dispose();
326
+ r.prevRecurrent?.dispose();
327
+ if (r.rows) for (const array of [r.rows.qkv, r.rows.k, r.rows.v, r.rows.a, r.rows.b]) array.dispose();
328
+ this.specRound = null;
329
+ }
330
+
331
+ dispose(): void {
332
+ this.#dropSpecRound();
333
+ const pending = this.#pending?.rows;
334
+ this.#pending = null;
335
+ if (pending) for (const array of [pending.qkv, pending.k, pending.v, pending.a, pending.b]) array.dispose();
336
+ this.#conv?.dispose();
337
+ this.#recurrent?.dispose();
338
+ this.#conv = null;
339
+ this.#recurrent = null;
340
+ this.prefillPadding = null;
341
+ }
342
+
343
+ // --- batch-lane dynamic-B ops (BatchScheduler only) ----------------------
344
+ // Both state slots are plain [B, ...] tensors with no temporal axis and no
345
+ // left-padding, so the batched twins of mergeKVRows/filterKVRows collapse
346
+ // to B-axis concat/take. `offset` is bookkeeping only for SSM layers (RoPE
347
+ // reads the FULL-attention caches; the linear-attn forward never reads it),
348
+ // so merged rows carrying different token counts share the max; the exact
349
+ // per-row counts live in `offsets` (extraction's coverage key).
350
+
351
+ /** Merge a fully-prefilled solo row into a running batched cache (`prev`
352
+ * null ⇒ the solo row becomes the batch). The returned cache owns fresh
353
+ * arrays — or, when there is nothing to concat, arrays STOLEN from `solo`
354
+ * (nulled there so the scheduler's unconditional dispose of the solo
355
+ * caches stays safe). `prev`/`solo` disposal remains the caller's job. */
356
+ static mergeRows(prev: SSMCache | null, solo: SSMCache): SSMCache {
357
+ if (!solo.conv || !solo.recurrent)
358
+ throw new Error("SSMCache.mergeRows: solo row has no state (prefill first)");
359
+ const out = new SSMCache();
360
+ if (prev) {
361
+ if (!prev.conv || !prev.recurrent)
362
+ throw new Error("SSMCache.mergeRows: batched cache has no state");
363
+ out.conv = ops.concatAxis([prev.conv, solo.conv], 0);
364
+ out.recurrent = ops.concatAxis([prev.recurrent, solo.recurrent], 0);
365
+ } else {
366
+ out.conv = solo.conv;
367
+ out.recurrent = solo.recurrent;
368
+ solo.conv = null;
369
+ solo.recurrent = null;
370
+ }
371
+ out.offset = Math.max(prev?.offset ?? 0, solo.offset);
372
+ // Seed per-row coverage: an adopted serial `prev` (offsets null) is one
373
+ // row whose exact count is its scalar offset.
374
+ out.offsets = [...(prev ? prev.offsets ?? [prev.offset] : []), solo.offset];
375
+ return out;
376
+ }
377
+
378
+ /** Evict rows not in `keep` (ascending row indices), in place. */
379
+ filter(keep: number[]): void {
380
+ if (this.conv && this.recurrent) {
381
+ const idx = ops.fromInt32(keep, [keep.length]);
382
+ const conv = ops.takeAxis(this.conv, idx, 0);
383
+ const recurrent = ops.takeAxis(this.recurrent, idx, 0);
384
+ idx.dispose();
385
+ this.conv.dispose();
386
+ this.recurrent.dispose();
387
+ this.conv = conv;
388
+ this.recurrent = recurrent;
389
+ }
390
+ this.prefillPadding?.filter(keep);
391
+ if (this.offsets) {
392
+ this.offsets = keep.map((i) => this.offsets![i]!);
393
+ this.offset = Math.max(0, ...this.offsets);
394
+ }
395
+ }
396
+
397
+ filterRows(keep: readonly number[]): void { this.filter([...keep]); }
398
+
399
+ /** Row `i` as a fresh SERIAL cache — port of mlx-lm ArraysCache.extract
400
+ * (cache.py:673-676: `cache.cache = [c[idx : idx + 1] for c in self.cache]`,
401
+ * a B-axis slice of every state slot). A proper subset gets compact storage
402
+ * so retaining one row does not pin the other rows. A singleton retains an
403
+ * independent handle over its complete allocation. Offset =
404
+ * the row's OWN coverage, not the shared max. */
405
+ extractRow(i: number): SSMCache {
406
+ if (!this.conv || !this.recurrent)
407
+ throw new Error("SSMCache.extractRow: empty cache");
408
+ const cut = (a: MlxArray): MlxArray => {
409
+ const lo = a.shape.map(() => 0);
410
+ lo[0] = i;
411
+ const hi = [...a.shape];
412
+ hi[0] = i + 1;
413
+ using view = a.slice(lo, hi);
414
+ return a.shape[0] === 1 ? ops.contiguous(view) : materializeCopy(view);
415
+ };
416
+ const out = new SSMCache();
417
+ out.conv = cut(this.conv);
418
+ out.recurrent = cut(this.recurrent);
419
+ out.offset = this.rowOffset(i);
420
+ return out;
421
+ }
422
+ }
@@ -0,0 +1,7 @@
1
+ import type { SpeculativeRowLayout } from "../contracts/mlx/cache";
2
+
3
+ /** The verification rows a storage's row-layout declaration names
4
+ * (`state/layout`'s table). */
5
+ export type TargetRowLayout = SpeculativeRowLayout;
6
+
7
+ export { targetRowLayoutFactory } from "./layout";
@@ -0,0 +1,45 @@
1
+ import { PromptCache, cacheBytes, type ColdTier } from "./prefix-cache";
2
+ import { cloneKvCaches, SpillQueue } from "./persistence";
3
+ import { SsdDurabilityCoordinator } from "./durability";
4
+ import { disposeResources } from "../runtime/resources";
5
+ import type { SsdCacheStore } from "./ssd-cache";
6
+
7
+ /** One cache with RAM and SSD storage. Inference sees the same put/take
8
+ * interface. Recency and capacity choose RAM residents; a pending write is
9
+ * never treated as a completed demotion. */
10
+ export class TieredPromptCache extends PromptCache {
11
+ protected override promoteRestores = true;
12
+ readonly spillQueue: SpillQueue;
13
+ readonly durability: SsdDurabilityCoordinator;
14
+
15
+ constructor(
16
+ maxBytes: number,
17
+ storage: Pick<SsdCacheStore, "storeAsync" | "hasDurablePrefix">,
18
+ cold: ColdTier,
19
+ clone = cloneKvCaches,
20
+ readonly writeBehind = true,
21
+ ) {
22
+ super(maxBytes, null, cold, clone);
23
+ this.spillQueue = new SpillQueue(Infinity, cacheBytes, async item => {
24
+ return storage.hasDurablePrefix(item.tokens, item.ns) ||
25
+ await storage.storeAsync(item.tokens, item.caches, item.ns, undefined, item.attachments);
26
+ }, disposeResources, () => false);
27
+ this.durability = new SsdDurabilityCoordinator(this, this.spillQueue, clone,
28
+ (tokens, ns) => storage.hasDurablePrefix(tokens, ns), 0, 5_000, () => {
29
+ // Writing alone does not remove a RAM entry. Apply capacity and
30
+ // pressure policy after the completed writer releases its views.
31
+ this.evictToBudget();
32
+ this.reclaim();
33
+ });
34
+ this.canEvict = entry => {
35
+ if (storage.hasDurablePrefix(entry.tokens, entry.ns)) return true;
36
+ this.durability.schedule(entry.tokens, entry.ns, false);
37
+ return false;
38
+ };
39
+ }
40
+
41
+ override put(...args: Parameters<PromptCache["put"]>): void {
42
+ super.put(...args);
43
+ if (this.writeBehind) this.durability.schedule(args[0], args[2]);
44
+ }
45
+ }