mlx-bun 0.0.9 → 0.0.11

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 (179) hide show
  1. package/README.md +66 -21
  2. package/THIRD_PARTY_LICENSES.md +1 -0
  3. package/docs/archive/mlx-bun-lab-report.html +275 -0
  4. package/docs/archive/wwdc-mlx-bun.html +848 -0
  5. package/docs/design/audio-input-plan.md +356 -0
  6. package/docs/design/batching-perf-path.md +255 -0
  7. package/docs/design/batching-v2-plan.md +23 -9
  8. package/docs/design/bucketing-stage.md +2 -1
  9. package/docs/design/compat-cli-surface-design.md +4 -3
  10. package/docs/design/decode-speed-program.md +172 -0
  11. package/docs/design/docs-reorg-plan.md +1 -1
  12. package/docs/design/dspark-serving-program.md +371 -0
  13. package/docs/design/dspark-speculative-decoding.md +167 -5
  14. package/docs/design/faithful-l1-consolidation.md +310 -0
  15. package/docs/design/generic-model-support.md +8 -5
  16. package/docs/design/grammar-spec-batching-integration.md +301 -0
  17. package/docs/design/minicpm5-decode-megakernel.md +16 -11
  18. package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
  19. package/docs/design/omlx-adoption-map.md +98 -0
  20. package/docs/design/orpo-training.md +34 -30
  21. package/docs/design/paged-kv-cache.md +214 -0
  22. package/docs/design/parallel-slots.md +82 -49
  23. package/docs/design/parity-tier-dag.md +43 -33
  24. package/docs/design/repo-cleanup-plan.md +189 -0
  25. package/docs/design/runtime-isolation.md +19 -3
  26. package/docs/design/segmented-backward-training.md +18 -2
  27. package/docs/design/ssd-kv-cold-tier.md +288 -0
  28. package/docs/design/structured-output.md +484 -0
  29. package/docs/design/the-dreaming-master-plan.md +2 -3
  30. package/docs/design/turboquant-kv.md +235 -0
  31. package/docs/design/unified-engine-frontier-plan.md +774 -0
  32. package/docs/design/web-chat-beat-matrix.md +315 -0
  33. package/docs/design/web-chat-redesign.md +1112 -0
  34. package/docs/investigations/decode-roofline-lookagain.md +92 -6
  35. package/docs/investigations/dspark-handoff.md +182 -16
  36. package/docs/investigations/kernel-perf-review-2026-07.md +105 -26
  37. package/docs/investigations/lab-build-journal.md +2 -2
  38. package/docs/investigations/openwiki-evaluation.md +249 -0
  39. package/docs/planning/IDEAS.md +14 -0
  40. package/docs/planning/PRODUCT_ROADMAP.md +2 -3
  41. package/docs/planning/chat-ui-vision-memory-first.md +199 -0
  42. package/docs/planning/chat-ui-vision.md +50 -0
  43. package/docs/planning/memory-docs-and-dag-plan.md +15 -6
  44. package/docs/planning/release-notes-v0.0.10.md +57 -0
  45. package/docs/planning/release-notes-v0.0.11.md +239 -0
  46. package/docs/reference/cli.md +35 -16
  47. package/docs/reference/distribution.md +13 -6
  48. package/docs/reference/embedding.md +35 -14
  49. package/docs/reference/features-matrix.md +97 -0
  50. package/docs/reference/library-api.md +30 -9
  51. package/docs/reference/memory.md +26 -6
  52. package/docs/reference/models.md +11 -1
  53. package/docs/reference/orpo-quickstart.md +3 -4
  54. package/docs/reference/server-api.md +634 -22
  55. package/docs/reference/server-config.md +251 -119
  56. package/docs/reference/training.md +20 -16
  57. package/package.json +5 -3
  58. package/src/anthropic.ts +13 -0
  59. package/src/audio/conformer.ts +647 -0
  60. package/src/audio/decode.ts +133 -0
  61. package/src/audio/features.ts +202 -0
  62. package/src/audio/transcode.ts +67 -0
  63. package/src/cli.ts +402 -107
  64. package/src/config.ts +49 -2
  65. package/src/download.ts +40 -0
  66. package/src/eval/README.md +7 -6
  67. package/src/eval/kl.ts +59 -3
  68. package/src/eval/quality-db.ts +2 -2
  69. package/src/eval/runner.ts +3 -3
  70. package/src/expert-offload.ts +15 -2
  71. package/src/flags.ts +13 -0
  72. package/src/generate.ts +330 -16
  73. package/src/grammar.ts +585 -0
  74. package/src/hub-rest.ts +242 -0
  75. package/src/kv-store.ts +618 -122
  76. package/src/lora.ts +8 -0
  77. package/src/md-modules.d.ts +42 -0
  78. package/src/media-fetch.ts +284 -0
  79. package/src/memory/rest.ts +245 -0
  80. package/src/memory/vault.ts +74 -0
  81. package/src/mlx/array.ts +127 -19
  82. package/src/mlx/autograd.ts +2 -2
  83. package/src/mlx/ffi.ts +27 -3
  84. package/src/mlx/metal-kernel.ts +7 -4
  85. package/src/mlx/ops.ts +179 -23
  86. package/src/mlx/turboquant-ops.ts +831 -0
  87. package/src/mlx/turboquant-tables.ts +237 -0
  88. package/src/model/batched-mask.ts +62 -1
  89. package/src/model/batched-quant.ts +206 -0
  90. package/src/model/batched-rotating-quant.ts +346 -0
  91. package/src/model/batched-rotating.ts +46 -1
  92. package/src/model/compiled-decode.ts +4 -15
  93. package/src/model/factory.ts +10 -4
  94. package/src/model/gemma4-base.ts +391 -34
  95. package/src/model/gemma4.ts +122 -33
  96. package/src/model/generated/gemma4-12b.ts +9 -13
  97. package/src/model/generated/gemma4-26b.ts +10 -5
  98. package/src/model/generated/gemma4-e4b.ts +14 -9
  99. package/src/model/minicpm5.ts +58 -102
  100. package/src/model/paged-kv.ts +253 -0
  101. package/src/model/qwen3-delta.ts +123 -15
  102. package/src/model/qwen3-moe.ts +404 -0
  103. package/src/model/qwen3.ts +9 -8
  104. package/src/model/qwen3_5.ts +99 -25
  105. package/src/model/support.ts +12 -1
  106. package/src/model/universal/dense.ts +40 -5
  107. package/src/model/universal/rope.ts +36 -21
  108. package/src/pi-web.ts +1299 -82
  109. package/src/preflight.ts +27 -8
  110. package/src/prompt-cache.ts +338 -32
  111. package/src/quantize/quantizer.ts +16 -1
  112. package/src/registry.ts +61 -3
  113. package/src/serve/batch-scheduler.ts +1001 -76
  114. package/src/serve/generation-gateway.ts +292 -30
  115. package/src/serve/isolate.ts +417 -0
  116. package/src/serve/lane-registry.ts +44 -0
  117. package/src/serve/session-search.ts +326 -0
  118. package/src/server.ts +1460 -105
  119. package/src/spec/assistant-source.ts +150 -0
  120. package/src/spec/deepspec-source.ts +143 -0
  121. package/src/spec/dflash-source.ts +124 -0
  122. package/src/spec/dspark/ab-stats.ts +136 -0
  123. package/src/spec/dspark/calibration.ts +112 -0
  124. package/src/spec/dspark/deepspec-module.ts +801 -0
  125. package/src/spec/dspark/generate-dflash.ts +72 -20
  126. package/src/spec/dspark/loader.ts +23 -0
  127. package/src/spec/dspark/module-dflash.ts +278 -36
  128. package/src/spec/dspark/quantize-drafter.ts +91 -0
  129. package/src/spec/generate.ts +59 -25
  130. package/src/spec/ngram-source.ts +132 -0
  131. package/src/spec/serve-loop.ts +536 -0
  132. package/src/spec/source.ts +93 -0
  133. package/src/spec/two-model.ts +171 -0
  134. package/src/ssd-cache.ts +285 -0
  135. package/src/tokenizer.ts +288 -2
  136. package/src/tool-approvals.ts +131 -0
  137. package/src/tool-call.ts +408 -20
  138. package/src/train/flash-cce.ts +29 -26
  139. package/src/train/job.ts +0 -2
  140. package/src/train/loss.ts +53 -11
  141. package/src/train/prefix-shared.ts +30 -7
  142. package/src/train/segmented.ts +150 -46
  143. package/src/train/trainer.ts +22 -15
  144. package/src/vision/prompt.ts +217 -48
  145. package/src/web/app.html +1184 -1789
  146. package/src/web/app.js +5667 -0
  147. package/src/web/icon.svg +13 -0
  148. package/src/web/manifest.webmanifest +24 -0
  149. package/src/web/src/adapters-panel.ts +288 -0
  150. package/src/web/src/api.ts +57 -0
  151. package/src/web/src/assistant.ts +485 -0
  152. package/src/web/src/chat.ts +1050 -0
  153. package/src/web/src/composer.ts +959 -0
  154. package/src/web/src/dataset.ts +132 -0
  155. package/src/web/src/finetune.ts +267 -0
  156. package/src/web/src/hub.ts +428 -0
  157. package/src/web/src/main.ts +98 -0
  158. package/src/web/src/markdown.ts +0 -0
  159. package/src/web/src/memory-panel.ts +572 -0
  160. package/src/web/src/model-picker.ts +159 -0
  161. package/src/web/src/palette.ts +346 -0
  162. package/src/web/src/protocol.ts +62 -0
  163. package/src/web/src/quantize.ts +167 -0
  164. package/src/web/src/rag.ts +274 -0
  165. package/src/web/src/sessions.ts +469 -0
  166. package/src/web/src/shell.ts +804 -0
  167. package/src/web/src/status.ts +252 -0
  168. package/src/web/src/ui-catalog.ts +92 -0
  169. package/src/web/sw.js +71 -0
  170. package/src/web/vendor/README.md +61 -0
  171. package/src/web/vendor/hljs-LICENSE +29 -0
  172. package/src/web/vendor/hljs-theme.css +59 -0
  173. package/src/web/vendor/hljs.js +26 -0
  174. package/src/model/fused-decode-kernel.ts +0 -312
  175. package/src/model/fused-geglu-kernel.ts +0 -198
  176. package/src/model/fused-mlp-kernel.ts +0 -169
  177. package/src/model/fused-swiglu-kernel.ts +0 -240
  178. package/src/model/steel-linear-kernel.ts +0 -96
  179. package/src/status-page.html +0 -497
@@ -0,0 +1,237 @@
1
+ // GENERATED by scripts/regen-turboquant-goldens.ts — DO NOT EDIT.
2
+ // Regenerate: bun scripts/regen-turboquant-goldens.ts
3
+ //
4
+ // Source: lab/repro/vllm-metal-turboquant/turboquant_reference.py (vendored
5
+ // vllm-metal oracle, see PROVENANCE.md in that directory), executed under
6
+ // the oracle venv (mlx 0.31.2) via gen_goldens.py.
7
+ //
8
+ // - SIGN_VECTORS[headDim]: the fwht() sign table (±1), derived from
9
+ // mx.random.randint(0, 2, shape=(headDim,), key=mx.random.key(42)) —
10
+ // deterministic across machines and spill/restore by construction.
11
+ // - LLOYD_MAX[bits]: Lloyd-Max (centroids, boundaries) for value
12
+ // quantization at the given bit width. bits=3 is the vendored reference's
13
+ // hardcoded fast-path table (CENTROIDS_3BIT/BOUNDARIES_3BIT); other widths
14
+ // are the reference's own iterative Lloyd-Max fit on a unit-normal source
15
+ // (mx.random.normal(key=mx.random.key(0)), 500 iterations), computed once
16
+ // here and hardcoded — no runtime iteration in the TS port.
17
+
18
+ export const TURBOQUANT_HEAD_DIMS = [64, 128, 256, 512] as const;
19
+ export type TurboQuantHeadDim = (typeof TURBOQUANT_HEAD_DIMS)[number];
20
+
21
+ export const SIGN_VECTORS: Record<TurboQuantHeadDim, number[]> = {
22
+ 64: [
23
+ 1, -1, -1, -1, 1, 1, 1, -1, 1, 1,
24
+ -1, 1, -1, 1, -1, 1, -1, 1, 1, -1,
25
+ 1, -1, -1, 1, -1, -1, -1, 1, -1, -1,
26
+ 1, -1, 1, -1, 1, 1, 1, 1, 1, 1,
27
+ 1, 1, 1, -1, 1, 1, -1, 1, 1, -1,
28
+ -1, -1, -1, 1, -1, -1, 1, -1, 1, 1,
29
+ 1, -1, -1, 1,
30
+ ],
31
+ 128: [
32
+ -1, 1, -1, -1, 1, 1, 1, 1, -1, -1,
33
+ -1, -1, -1, 1, -1, -1, -1, 1, 1, -1,
34
+ -1, -1, 1, 1, 1, -1, 1, -1, 1, -1,
35
+ -1, 1, 1, 1, 1, 1, -1, 1, -1, 1,
36
+ 1, -1, 1, 1, 1, 1, 1, 1, 1, 1,
37
+ -1, -1, -1, 1, 1, -1, 1, -1, 1, -1,
38
+ 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
39
+ -1, -1, -1, -1, -1, 1, -1, -1, 1, 1,
40
+ -1, -1, 1, 1, 1, 1, 1, -1, 1, -1,
41
+ 1, -1, 1, 1, -1, 1, 1, -1, -1, -1,
42
+ -1, 1, -1, -1, -1, 1, 1, -1, -1, 1,
43
+ -1, 1, 1, 1, 1, 1, 1, -1, -1, 1,
44
+ 1, 1, -1, 1, -1, 1, 1, 1,
45
+ ],
46
+ 256: [
47
+ 1, -1, 1, 1, -1, 1, 1, 1, 1, 1,
48
+ 1, 1, -1, -1, 1, 1, 1, -1, -1, 1,
49
+ 1, -1, -1, -1, 1, -1, 1, 1, 1, -1,
50
+ -1, 1, -1, 1, 1, 1, -1, 1, 1, 1,
51
+ 1, -1, 1, -1, 1, 1, -1, 1, -1, 1,
52
+ -1, -1, 1, 1, -1, -1, 1, 1, 1, 1,
53
+ -1, 1, 1, 1, -1, -1, -1, -1, 1, -1,
54
+ -1, 1, 1, 1, -1, 1, -1, 1, 1, 1,
55
+ -1, 1, -1, -1, 1, -1, -1, 1, -1, 1,
56
+ 1, -1, 1, -1, 1, -1, -1, 1, 1, -1,
57
+ 1, 1, -1, -1, 1, -1, -1, -1, -1, 1,
58
+ -1, 1, 1, 1, -1, -1, -1, 1, 1, -1,
59
+ -1, -1, 1, 1, -1, 1, -1, -1, -1, -1,
60
+ 1, -1, -1, 1, 1, -1, 1, -1, 1, 1,
61
+ -1, -1, -1, -1, -1, -1, -1, -1, 1, -1,
62
+ 1, 1, 1, -1, 1, -1, -1, -1, 1, 1,
63
+ -1, -1, 1, 1, -1, -1, -1, 1, -1, 1,
64
+ -1, -1, -1, 1, -1, 1, -1, -1, -1, 1,
65
+ 1, -1, 1, 1, 1, -1, 1, 1, -1, -1,
66
+ 1, -1, -1, -1, 1, -1, 1, -1, -1, 1,
67
+ -1, 1, 1, -1, -1, 1, -1, -1, 1, 1,
68
+ 1, 1, -1, -1, 1, 1, -1, -1, -1, 1,
69
+ -1, 1, -1, 1, -1, -1, -1, 1, -1, 1,
70
+ -1, 1, -1, -1, 1, 1, 1, 1, -1, -1,
71
+ -1, -1, 1, -1, 1, 1, 1, -1, 1, -1,
72
+ -1, -1, -1, -1, -1, -1,
73
+ ],
74
+ 512: [
75
+ -1, 1, -1, -1, -1, -1, 1, -1, -1, -1,
76
+ 1, -1, 1, 1, -1, 1, 1, -1, -1, 1,
77
+ -1, -1, 1, 1, 1, 1, 1, 1, -1, -1,
78
+ -1, 1, -1, 1, -1, -1, 1, 1, 1, 1,
79
+ -1, -1, 1, 1, 1, -1, -1, -1, -1, -1,
80
+ 1, -1, -1, -1, -1, -1, 1, -1, 1, 1,
81
+ 1, -1, -1, 1, -1, -1, 1, 1, 1, -1,
82
+ -1, 1, 1, 1, -1, 1, 1, -1, -1, -1,
83
+ -1, 1, 1, -1, -1, -1, 1, 1, 1, 1,
84
+ -1, 1, -1, 1, -1, -1, -1, 1, 1, 1,
85
+ 1, -1, -1, 1, 1, -1, 1, 1, -1, 1,
86
+ -1, -1, 1, -1, 1, -1, 1, 1, -1, -1,
87
+ 1, 1, -1, -1, -1, 1, -1, 1, -1, -1,
88
+ -1, -1, 1, 1, 1, -1, 1, 1, -1, -1,
89
+ -1, -1, -1, 1, -1, -1, -1, 1, 1, 1,
90
+ -1, 1, -1, -1, 1, -1, -1, 1, 1, -1,
91
+ -1, -1, 1, 1, 1, 1, -1, 1, 1, -1,
92
+ 1, 1, -1, 1, 1, 1, -1, 1, -1, -1,
93
+ -1, 1, -1, -1, -1, 1, 1, -1, 1, -1,
94
+ -1, -1, 1, 1, 1, 1, -1, -1, 1, 1,
95
+ 1, -1, -1, -1, -1, 1, -1, -1, 1, -1,
96
+ 1, -1, 1, -1, 1, 1, 1, 1, -1, 1,
97
+ -1, 1, -1, 1, 1, -1, 1, -1, -1, -1,
98
+ 1, -1, 1, -1, 1, -1, 1, 1, -1, -1,
99
+ 1, -1, 1, -1, -1, -1, 1, -1, -1, -1,
100
+ -1, 1, -1, -1, -1, 1, 1, 1, -1, 1,
101
+ 1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
102
+ 1, 1, -1, -1, -1, 1, 1, 1, 1, -1,
103
+ -1, 1, 1, 1, -1, 1, 1, 1, 1, 1,
104
+ 1, -1, -1, 1, 1, 1, -1, 1, -1, 1,
105
+ -1, 1, 1, 1, 1, 1, -1, -1, 1, 1,
106
+ -1, 1, 1, -1, 1, -1, -1, 1, 1, -1,
107
+ -1, 1, -1, 1, 1, 1, -1, -1, -1, -1,
108
+ 1, 1, 1, -1, 1, -1, 1, 1, -1, 1,
109
+ 1, 1, 1, 1, 1, -1, 1, 1, -1, -1,
110
+ 1, 1, -1, -1, -1, -1, -1, 1, 1, 1,
111
+ 1, -1, 1, 1, -1, -1, -1, 1, -1, 1,
112
+ 1, 1, 1, 1, 1, -1, -1, 1, -1, -1,
113
+ -1, 1, -1, 1, 1, -1, 1, 1, -1, 1,
114
+ -1, 1, 1, 1, -1, 1, -1, 1, 1, 1,
115
+ 1, -1, 1, 1, 1, 1, 1, -1, 1, -1,
116
+ 1, -1, 1, 1, 1, -1, 1, -1, 1, 1,
117
+ 1, 1, -1, -1, -1, -1, 1, -1, -1, 1,
118
+ 1, -1, -1, -1, -1, 1, 1, -1, 1, -1,
119
+ 1, -1, -1, -1, 1, 1, 1, -1, -1, -1,
120
+ -1, -1, 1, -1, -1, -1, -1, -1, 1, -1,
121
+ 1, 1, -1, 1, 1, -1, -1, -1, 1, 1,
122
+ 1, 1, 1, -1, 1, 1, -1, 1, -1, 1,
123
+ -1, 1, -1, -1, 1, -1, -1, 1, -1, 1,
124
+ -1, -1, -1, 1, 1, -1, -1, -1, 1, -1,
125
+ 1, -1, -1, 1, -1, 1, 1, -1, 1, -1,
126
+ 1, 1,
127
+ ],
128
+ };
129
+
130
+ export const TURBOQUANT_VALUE_BITS = [2, 3, 4, 5, 8] as const;
131
+ export type TurboQuantValueBits = (typeof TURBOQUANT_VALUE_BITS)[number];
132
+
133
+ export interface LloydMaxTable {
134
+ centroids: number[];
135
+ boundaries: number[];
136
+ }
137
+
138
+ export const LLOYD_MAX: Record<TurboQuantValueBits, LloydMaxTable> = {
139
+ 2: {
140
+ centroids: [
141
+ -1.5144749879837036, -0.45822522044181824, 0.44863229990005493, 1.512152075767517,
142
+ ],
143
+ boundaries: [
144
+ -0.9863501191139221, -0.004796460270881653, 0.9803922176361084,
145
+ ],
146
+ },
147
+ 3: {
148
+ centroids: [
149
+ -2.151949882507324, -1.3439099788665771, -0.7560099959373474, -0.24508999288082123, 0.24508999288082123, 0.7560099959373474, 1.3439099788665771, 2.151949882507324,
150
+ ],
151
+ boundaries: [
152
+ -1.7479300498962402, -1.0499600172042847, -0.5005499720573425, 0, 0.5005499720573425, 1.0499600172042847, 1.7479300498962402,
153
+ ],
154
+ },
155
+ 4: {
156
+ centroids: [
157
+ -2.765662670135498, -2.0995230674743652, -1.6401931047439575, -1.27662992477417, -0.9602491855621338, -0.673130452632904, -0.4038185179233551, -0.14272712171077728, 0.11716290563344955, 0.3790881633758545,
158
+ 0.6475776433944702, 0.9337777495384216, 1.2523075342178345, 1.6217625141143799, 2.0757482051849365, 2.7207279205322266,
159
+ ],
160
+ boundaries: [
161
+ -2.4325928688049316, -1.8698580265045166, -1.458411455154419, -1.1184395551681519, -0.8166898488998413, -0.5384745001792908, -0.2732728123664856, -0.012782108038663864, 0.24812553822994232, 0.5133329033851624,
162
+ 0.7906776666641235, 1.0930426120758057, 1.437035083770752, 1.8487553596496582, 2.398238182067871,
163
+ ],
164
+ },
165
+ 5: {
166
+ centroids: [
167
+ -3.3482043743133545, -2.7744688987731934, -2.3917815685272217, -2.0885653495788574, -1.8382762670516968, -1.6273508071899414, -1.436827301979065, -1.2599420547485352, -1.0958681106567383, -0.9411738514900208,
168
+ -0.7932286262512207, -0.6509044766426086, -0.5113211274147034, -0.37372007966041565, -0.23732160031795502, -0.10278966277837753, 0.031794287264347076, 0.16419854760169983, 0.3001006245613098, 0.4395425021648407,
169
+ 0.5808256268501282, 0.7275802493095398, 0.8738965392112732, 1.029397964477539, 1.1942607164382935, 1.3728704452514648, 1.5676456689834595, 1.778881311416626, 2.0225136280059814, 2.3095157146453857,
170
+ 2.666351556777954, 3.2329483032226562,
171
+ ],
172
+ boundaries: [
173
+ -3.0613365173339844, -2.583125114440918, -2.24017333984375, -1.9634208679199219, -1.7328135967254639, -1.5320889949798584, -1.3483846187591553, -1.1779050827026367, -1.0185209512710571, -0.8672012090682983,
174
+ -0.7220665216445923, -0.581112802028656, -0.4425206184387207, -0.30552083253860474, -0.17005562782287598, -0.03549768775701523, 0.09799641370773315, 0.23214958608150482, 0.36982154846191406, 0.5101840496063232,
175
+ 0.654202938079834, 0.8007383942604065, 0.9516472816467285, 1.1118292808532715, 1.2835655212402344, 1.4702579975128174, 1.6732635498046875, 1.9006974697113037, 2.1660146713256836, 2.48793363571167,
176
+ 2.9496498107910156,
177
+ ],
178
+ },
179
+ 8: {
180
+ centroids: [
181
+ -4.505678653717041, -4.454618453979492, -4.436285972595215, -4.401589393615723, -4.363239765167236, -4.332197189331055, -4.2975006103515625, -4.26280403137207, -4.228107929229736, -4.193411827087402,
182
+ -4.15871524810791, -4.124018669128418, -4.094437599182129, -4.045758247375488, -4.002621173858643, -4.002171516418457, -3.942392110824585, -3.916801691055298, -3.8863255977630615, -3.8477535247802734,
183
+ -3.7988643646240234, -3.762794256210327, -3.7445151805877686, -3.706960439682007, -3.67462158203125, -3.6314854621887207, -3.5950119495391846, -3.580256938934326, -3.539952516555786, -3.5038421154022217,
184
+ -3.4671568870544434, -3.4281671047210693, -3.398128032684326, -3.363133192062378, -3.3274667263031006, -3.2919743061065674, -3.2577857971191406, -3.2200968265533447, -3.192981719970703, -3.157238006591797,
185
+ -3.111802577972412, -3.0821340084075928, -3.050640344619751, -3.0148110389709473, -2.980158805847168, -2.946451425552368, -2.9111905097961426, -2.8747212886810303, -2.8412270545959473, -2.8061022758483887,
186
+ -2.7716867923736572, -2.7376010417938232, -2.7029218673706055, -2.6634461879730225, -2.630377769470215, -2.5996179580688477, -2.560898780822754, -2.525275230407715, -2.4947545528411865, -2.461374044418335,
187
+ -2.424009323120117, -2.3877711296081543, -2.3505144119262695, -2.3163986206054688, -2.282374858856201, -2.243652820587158, -2.2097959518432617, -2.1770639419555664, -2.14471697807312, -2.1113693714141846,
188
+ -2.078673839569092, -2.044494390487671, -2.00639271736145, -1.969869613647461, -1.9300988912582397, -1.8931699991226196, -1.8589746952056885, -1.8212106227874756, -1.7848811149597168, -1.7465486526489258,
189
+ -1.7103406190872192, -1.6743196249008179, -1.6393754482269287, -1.6040517091751099, -1.5708216428756714, -1.5365480184555054, -1.5035663843154907, -1.469175100326538, -1.4350656270980835, -1.4022008180618286,
190
+ -1.3688998222351074, -1.3355891704559326, -1.3018449544906616, -1.2698367834091187, -1.235532283782959, -1.20119047164917, -1.1664694547653198, -1.1305049657821655, -1.0959333181381226, -1.0614327192306519,
191
+ -1.0274933576583862, -0.9940271377563477, -0.9609262347221375, -0.9273853898048401, -0.8929082751274109, -0.8593462705612183, -0.8261474967002869, -0.7937442660331726, -0.7588282823562622, -0.7230017185211182,
192
+ -0.6871297359466553, -0.6517745852470398, -0.6162516474723816, -0.5811514854431152, -0.5462668538093567, -0.5111281275749207, -0.4750610589981079, -0.4394458830356598, -0.404982328414917, -0.36981964111328125,
193
+ -0.334137886762619, -0.29919183254241943, -0.26465246081352234, -0.23168867826461792, -0.20013625919818878, -0.1672147512435913, -0.13413792848587036, -0.1003282368183136, -0.06571532040834427, -0.03132394701242447,
194
+ 0.0043428270146250725, 0.04037246108055115, 0.07451506704092026, 0.1076153963804245, 0.14043505489826202, 0.17426857352256775, 0.20812825858592987, 0.2430444061756134, 0.27797263860702515, 0.31494686007499695,
195
+ 0.35088568925857544, 0.38743987679481506, 0.4222983717918396, 0.4568648338317871, 0.49128350615501404, 0.5256633758544922, 0.5586227178573608, 0.5910427570343018, 0.6249552369117737, 0.6594945788383484,
196
+ 0.6942835450172424, 0.7278474569320679, 0.7624561786651611, 0.7963849902153015, 0.8309887647628784, 0.8644483685493469, 0.8981063961982727, 0.9320245385169983, 0.96662437915802, 1.0006996393203735,
197
+ 1.0343793630599976, 1.0680633783340454, 1.101860523223877, 1.135563611984253, 1.1707100868225098, 1.2038687467575073, 1.2388564348220825, 1.2744282484054565, 1.3102103471755981, 1.3441187143325806,
198
+ 1.3769688606262207, 1.4091657400131226, 1.4424594640731812, 1.477047085762024, 1.5127589702606201, 1.5482075214385986, 1.58390474319458, 1.6193201541900635, 1.655400037765503, 1.6924738883972168,
199
+ 1.7273176908493042, 1.7618744373321533, 1.7985631227493286, 1.8349602222442627, 1.871187686920166, 1.9062069654464722, 1.9427249431610107, 1.9807021617889404, 2.0164709091186523, 2.049800395965576,
200
+ 2.083641529083252, 2.1192877292633057, 2.1556057929992676, 2.189804792404175, 2.2249133586883545, 2.257265329360962, 2.2943572998046875, 2.3308892250061035, 2.3646092414855957, 2.3981246948242188,
201
+ 2.4336130619049072, 2.4672765731811523, 2.5033960342407227, 2.535766124725342, 2.5712199211120605, 2.606968879699707, 2.6403279304504395, 2.6730761528015137, 2.7094709873199463, 2.745285749435425,
202
+ 2.7794594764709473, 2.8145666122436523, 2.851815938949585, 2.885251045227051, 2.919672727584839, 2.9549803733825684, 2.987560510635376, 3.021491289138794, 3.061659097671509, 3.102267026901245,
203
+ 3.1455001831054688, 3.1724164485931396, 3.2052037715911865, 3.2377841472625732, 3.2674646377563477, 3.3014495372772217, 3.3345274925231934, 3.3695874214172363, 3.4068915843963623, 3.442040205001831,
204
+ 3.4636645317077637, 3.5024828910827637, 3.5508108139038086, 3.5749337673187256, 3.604726791381836, 3.6401684284210205, 3.692674160003662, 3.7173542976379395, 3.7488019466400146, 3.7978620529174805,
205
+ 3.8167099952697754, 3.856578826904297, 3.890836000442505, 3.9217264652252197, 3.974006175994873, 4.009003639221191, 4.029621601104736, 4.079561233520508, 4.099013805389404, 4.1337103843688965,
206
+ 4.172705173492432, 4.198393821716309, 4.237799167633057, 4.272495746612549, 4.307192325592041, 4.341888427734375,
207
+ ],
208
+ boundaries: [
209
+ -4.4801483154296875, -4.4454522132873535, -4.418937683105469, -4.382414817810059, -4.347718238830566, -4.314848899841309, -4.280152320861816, -4.245455741882324, -4.210760116577148, -4.176063537597656,
210
+ -4.141366958618164, -4.109228134155273, -4.070097923278809, -4.0241899490356445, -4.002396583557129, -3.9722819328308105, -3.9295969009399414, -3.9015636444091797, -3.867039680480957, -3.8233089447021484,
211
+ -3.780829429626465, -3.753654718399048, -3.7257378101348877, -3.690791130065918, -3.6530535221099854, -3.613248825073242, -3.587634563446045, -3.5601048469543457, -3.521897315979004, -3.485499382019043,
212
+ -3.447661876678467, -3.413147449493408, -3.3806304931640625, -3.3452999591827393, -3.309720516204834, -3.2748799324035645, -3.238941192626953, -3.2065391540527344, -3.17510986328125, -3.1345202922821045,
213
+ -3.096968173980713, -3.066387176513672, -3.0327258110046387, -2.9974849224090576, -2.9633049964904785, -2.928821086883545, -2.892955780029297, -2.857974052429199, -2.823664665222168, -2.7888946533203125,
214
+ -2.7546439170837402, -2.720261573791504, -2.6831841468811035, -2.646912097930908, -2.6149978637695312, -2.580258369445801, -2.5430870056152344, -2.5100150108337402, -2.4780642986297607, -2.4426918029785156,
215
+ -2.4058902263641357, -2.369142770767212, -2.333456516265869, -2.299386739730835, -2.2630138397216797, -2.22672438621521, -2.193429946899414, -2.160890579223633, -2.1280431747436523, -2.0950217247009277,
216
+ -2.061583995819092, -2.0254435539245605, -1.9881311655044556, -1.9499843120574951, -1.9116344451904297, -1.8760724067687988, -1.840092658996582, -1.8030458688735962, -1.7657148838043213, -1.7284445762634277,
217
+ -1.6923301219940186, -1.6568474769592285, -1.621713638305664, -1.5874366760253906, -1.5536848306655884, -1.520057201385498, -1.4863708019256592, -1.452120304107666, -1.418633222579956, -1.3855502605438232,
218
+ -1.35224449634552, -1.3187170028686523, -1.2858408689498901, -1.2526845932006836, -1.2183613777160645, -1.1838300228118896, -1.1484872102737427, -1.113219141960144, -1.0786830186843872, -1.044463038444519,
219
+ -1.0107603073120117, -0.9774767160415649, -0.9441558122634888, -0.9101468324661255, -0.8761272430419922, -0.8427468538284302, -0.8099458813667297, -0.776286244392395, -0.7409150004386902, -0.7050657272338867,
220
+ -0.6694521903991699, -0.6340131163597107, -0.5987015962600708, -0.5637091398239136, -0.5286974906921387, -0.4930945932865143, -0.45725345611572266, -0.4222140908241272, -0.3874009847640991, -0.35197877883911133,
221
+ -0.3166648745536804, -0.2819221615791321, -0.24817056953907013, -0.21591246128082275, -0.18367549777030945, -0.15067633986473083, -0.11723308265209198, -0.08302177488803864, -0.04851963371038437, -0.013490559533238411,
222
+ 0.022357644513249397, 0.0574437640607357, 0.09106522798538208, 0.12402522563934326, 0.15735182166099548, 0.1911984086036682, 0.22558632493019104, 0.26050853729248047, 0.29645973443984985, 0.332916259765625,
223
+ 0.36916279792785645, 0.4048691391944885, 0.43958160281181335, 0.47407418489456177, 0.5084734559059143, 0.5421430468559265, 0.5748327374458313, 0.6079989671707153, 0.642224907875061, 0.6768890619277954,
224
+ 0.7110655307769775, 0.7451518177986145, 0.7794206142425537, 0.8136868476867676, 0.8477185964584351, 0.8812773823738098, 0.9150654673576355, 0.9493244886398315, 0.9836620092391968, 1.0175395011901855,
225
+ 1.0512213706970215, 1.0849618911743164, 1.118712067604065, 1.1531368494033813, 1.1872894763946533, 1.221362590789795, 1.2566423416137695, 1.2923192977905273, 1.3271645307540894, 1.3605437278747559,
226
+ 1.3930673599243164, 1.4258126020431519, 1.4597532749176025, 1.4949030876159668, 1.5304832458496094, 1.5660561323165894, 1.6016124486923218, 1.6373600959777832, 1.6739369630813599, 1.7098958492279053,
227
+ 1.744596004486084, 1.7802188396453857, 1.8167617321014404, 1.8530739545822144, 1.8886973857879639, 1.9244658946990967, 1.9617135524749756, 1.9985865354537964, 2.0331356525421143, 2.066720962524414,
228
+ 2.1014647483825684, 2.137446880340576, 2.1727051734924316, 2.2073590755462646, 2.241089344024658, 2.275811195373535, 2.3126232624053955, 2.3477492332458496, 2.3813669681549072, 2.4158687591552734,
229
+ 2.4504446983337402, 2.4853363037109375, 2.5195810794830322, 2.553493022918701, 2.589094400405884, 2.6236484050750732, 2.6567020416259766, 2.6912736892700195, 2.7273783683776855, 2.7623724937438965,
230
+ 2.7970130443573, 2.833191394805908, 2.8685336112976074, 2.9024620056152344, 2.937326431274414, 2.9712705612182617, 3.004525899887085, 3.0415751934051514, 3.081963062286377, 3.1238837242126465,
231
+ 3.1589584350585938, 3.188810110092163, 3.22149395942688, 3.25262451171875, 3.284457206726074, 3.317988395690918, 3.352057456970215, 3.3882393836975098, 3.4244658946990967, 3.452852249145508,
232
+ 3.4830737113952637, 3.526646852493286, 3.5628724098205566, 3.5898303985595703, 3.6224474906921387, 3.666421413421631, 3.705014228820801, 3.7330780029296875, 3.773332118988037, 3.807286024093628,
233
+ 3.836644411087036, 3.8737072944641113, 3.9062812328338623, 3.947866439819336, 3.9915049076080322, 4.019312858581543, 4.054591178894043, 4.089287757873535, 4.11636209487915, 4.153207778930664,
234
+ 4.185549736022949, 4.218096733093262, 4.255147457122803, 4.289844036102295, 4.324540138244629,
235
+ ],
236
+ },
237
+ };
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { MlxArray } from "../mlx/array";
19
19
  import * as ops from "../mlx/ops";
20
- import { createCausalMask, type Cache, type Mask } from "./gemma4-base";
20
+ import { createCausalMask, KVCache, type Cache, type Mask } from "./gemma4-base";
21
21
 
22
22
  /** Padding-aware key-validity mask for batched DECODE with left-padded rows.
23
23
  *
@@ -190,6 +190,67 @@ export function mergeKVRows(
190
190
  return { keys, values, leftPad, width };
191
191
  }
192
192
 
193
+ /** mlx-lm `BatchKVCache.extend`: append ONE new right-justified row to an
194
+ * existing batched [B,H,S,D] buffer WITHOUT re-extracting the running rows
195
+ * (the O(B·S) slice storm mergeKVRows pays on a join). Pad whichever side
196
+ * is narrower on the LEFT to the common width, then a single B-axis concat.
197
+ * Existing rows' left-pads grow by the batch-side pad (they never shrink —
198
+ * mlx-lm extend semantics; the decode mask covers pads either way).
199
+ * Caller owns the result; inputs are not disposed. */
200
+ export function extendKVRows(
201
+ keys: MlxArray, values: MlxArray, leftPad: number[],
202
+ row: { keys: MlxArray; values: MlxArray },
203
+ ): { keys: MlxArray; values: MlxArray; leftPad: number[]; width: number } {
204
+ const S = keys.shape[2]!;
205
+ const Lr = row.keys.shape[2]!;
206
+ const width = Math.max(S, Lr);
207
+ const padTo = (a: MlxArray, pad: number): MlxArray => {
208
+ if (pad === 0) return a.slice([0, 0, 0, 0], a.shape as number[]); // fresh full view
209
+ const [B, H, , D] = a.shape as [number, number, number, number];
210
+ const z = ops.zeros([B, H, pad, D], a.dtype);
211
+ const out = ops.concatAxis([z, a], 2);
212
+ z.dispose();
213
+ return out;
214
+ };
215
+ const bk = padTo(keys, width - S);
216
+ const bv = padTo(values, width - S);
217
+ const rk = padTo(row.keys, width - Lr);
218
+ const rv = padTo(row.values, width - Lr);
219
+ const outK = ops.concatAxis([bk, rk], 0);
220
+ const outV = ops.concatAxis([bv, rv], 0);
221
+ for (const a of [bk, bv, rk, rv]) a.dispose();
222
+ return {
223
+ keys: outK,
224
+ values: outV,
225
+ leftPad: [...leftPad.map((p) => p + (width - S)), width - Lr],
226
+ width,
227
+ };
228
+ }
229
+
230
+ /** mlx-lm `BatchKVCache.extract` (models/cache.py:1080): pull row `i` out of
231
+ * a batched full-attention cache into a fresh SERIAL KVCache — the row slice
232
+ * past its left padding, materialized as an OWNED contiguous copy (extraction
233
+ * outlives the batch; the source buffers are about to be filter()ed or
234
+ * disposed). `leftPad` is the scheduler's per-row padding for this row (full
235
+ * layers share one offset; the scheduler owns the pad list). Bit-exact vs a
236
+ * solo run by construction: merge/extend/filter are byte-preserving per row
237
+ * (tests/batched-decode-parity, tests/batched-extract) and this is a pure
238
+ * slice+copy of those bytes. Off the hot decode path. */
239
+ export function extractKVRow(cache: KVCache, leftPad: number, i: number): KVCache {
240
+ if (!cache.keys || !cache.values) throw new Error("extractKVRow: empty cache");
241
+ const S = cache.offset; // valid columns (buffer may be STEP-padded past it)
242
+ const cut = (a: MlxArray): MlxArray => {
243
+ const [, H, , D] = a.shape as [number, number, number, number];
244
+ const view = a.slice([i, 0, leftPad, 0], [i + 1, H, S, D]);
245
+ const own = ops.contiguous(view);
246
+ view.dispose();
247
+ return own;
248
+ };
249
+ const out = new KVCache();
250
+ out.restoreState(cut(cache.keys), cut(cache.values), S - leftPad);
251
+ return out;
252
+ }
253
+
193
254
  /** mlx-lm `filter`: keep only `keep` (sorted row indices) along the batch
194
255
  * axis — eviction of finished sequences from a batched [B,H,S,D] buffer.
195
256
  * Caller owns the result; inputs are not disposed. */
@@ -0,0 +1,206 @@
1
+ // Batched QUANTIZED KV for continuous batching (Phase 3.1 of
2
+ // docs/design/unified-engine-frontier-plan.md) — the quantized twins of
3
+ // batched-mask.ts's dynamic-B ops, over (packed, scales, biases) triples.
4
+ //
5
+ // Why this is safe surgery: mlx quantization packs along HEAD_DIM (the last
6
+ // axis; packed last-dim = D/(32/bits), scales/biases last-dim = D/groupSize),
7
+ // so the TOKEN axis (axis 2) stays token-granular in all three tensors —
8
+ // left-padding, concat, and row-filter at token granularity never touch a
9
+ // quantization group. Pad columns hold zeros; they are never attended (the
10
+ // padding mask covers them) and never dequantized into anything a real row
11
+ // reads. QuantizedKVCache.updateAndFetchQuantized already handles arbitrary
12
+ // starting widths (it trims to `offset` and regrows in STEP chunks), so a
13
+ // merged exact-width buffer needs no STEP alignment here.
14
+ //
15
+ // Composition contract (the L2 oracle binds per ROW): each joiner solo-
16
+ // prefills with the SAME per-layer conversion the serial path runs
17
+ // (maybeQuantizeKv at chunk boundaries — see BatchScheduler.#quantizeSolo),
18
+ // so a row's quantized bytes are bit-exact vs serial `--kv-quant config` by
19
+ // construction; this module only re-arranges those bytes across the batch
20
+ // axis. Gates: tests/batched-kv-quant-parity.test.ts.
21
+
22
+ import { MlxArray } from "../mlx/array";
23
+ import * as ops from "../mlx/ops";
24
+ import { QuantizedKVCache, type Mask } from "./gemma4-base";
25
+ import { buildBatchedDecodeMask } from "./batched-mask";
26
+
27
+ export interface QuantRow {
28
+ keys: ops.QuantizedTensor;
29
+ values: ops.QuantizedTensor;
30
+ }
31
+
32
+ const disposeTriple = (t: ops.QuantizedTensor): void => {
33
+ t.packed.dispose();
34
+ t.scales.dispose();
35
+ t.biases.dispose();
36
+ };
37
+
38
+ /** Apply an MlxArray op to each member of a triple. Each tensor keeps its
39
+ * OWN last-dim (packed vs scales/biases differ) — helpers must never share
40
+ * one `D` across the triple (recon hazard, 2026-07-05). */
41
+ const tripleMap = (
42
+ t: ops.QuantizedTensor, f: (a: MlxArray) => MlxArray,
43
+ ): ops.QuantizedTensor => ({ packed: f(t.packed), scales: f(t.scales), biases: f(t.biases) });
44
+
45
+ /** Left-pad one tensor of a triple by `pad` tokens (axis 2) with zeros of
46
+ * its own dtype/last-dim. pad=0 returns a fresh full view (mirrors
47
+ * batched-mask.ts leftPadTo so ownership is uniform). */
48
+ const leftPadTo = (a: MlxArray, pad: number): MlxArray => {
49
+ if (pad === 0) return a.slice([0, 0, 0, 0], a.shape as number[]);
50
+ const [B, H, , D] = a.shape as [number, number, number, number];
51
+ const z = ops.zeros([B, H, pad, D], a.dtype);
52
+ const out = ops.concatAxis([z, a], 2);
53
+ z.dispose();
54
+ return out;
55
+ };
56
+
57
+ /** Quantized twin of mergeKVRows: stack N single-row quantized KV slices
58
+ * into one left-padded [B,H,Smax,*] triple pair. Caller owns the result;
59
+ * input rows are not disposed. */
60
+ export function mergeQuantRows(rows: QuantRow[]): {
61
+ keys: ops.QuantizedTensor; values: ops.QuantizedTensor; leftPad: number[]; width: number;
62
+ } {
63
+ const lens = rows.map((r) => r.keys.packed.shape[2]!);
64
+ const width = Math.max(...lens);
65
+ const leftPad = lens.map((l) => width - l);
66
+ const padded = rows.map((r, i) => ({
67
+ keys: tripleMap(r.keys, (a) => leftPadTo(a, leftPad[i]!)),
68
+ values: tripleMap(r.values, (a) => leftPadTo(a, leftPad[i]!)),
69
+ }));
70
+ const cat = (pick: (r: QuantRow) => ops.QuantizedTensor): ops.QuantizedTensor => ({
71
+ packed: ops.concatAxis(padded.map((r) => pick(r).packed), 0),
72
+ scales: ops.concatAxis(padded.map((r) => pick(r).scales), 0),
73
+ biases: ops.concatAxis(padded.map((r) => pick(r).biases), 0),
74
+ });
75
+ const keys = cat((r) => r.keys);
76
+ const values = cat((r) => r.values);
77
+ for (const r of padded) { disposeTriple(r.keys); disposeTriple(r.values); }
78
+ return { keys, values, leftPad, width };
79
+ }
80
+
81
+ /** Quantized twin of extendKVRows: append ONE right-justified quantized row
82
+ * to an existing batched triple pair in one pad + one B-axis concat.
83
+ * Existing pads grow, never shrink (mlx-lm extend semantics). Caller owns
84
+ * the result; inputs are not disposed. */
85
+ export function extendQuantRows(
86
+ keys: ops.QuantizedTensor, values: ops.QuantizedTensor, leftPad: number[], row: QuantRow,
87
+ ): { keys: ops.QuantizedTensor; values: ops.QuantizedTensor; leftPad: number[]; width: number } {
88
+ const S = keys.packed.shape[2]!;
89
+ const Lr = row.keys.packed.shape[2]!;
90
+ const width = Math.max(S, Lr);
91
+ const bk = tripleMap(keys, (a) => leftPadTo(a, width - S));
92
+ const bv = tripleMap(values, (a) => leftPadTo(a, width - S));
93
+ const rk = tripleMap(row.keys, (a) => leftPadTo(a, width - Lr));
94
+ const rv = tripleMap(row.values, (a) => leftPadTo(a, width - Lr));
95
+ const cat = (b: ops.QuantizedTensor, r: ops.QuantizedTensor): ops.QuantizedTensor => ({
96
+ packed: ops.concatAxis([b.packed, r.packed], 0),
97
+ scales: ops.concatAxis([b.scales, r.scales], 0),
98
+ biases: ops.concatAxis([b.biases, r.biases], 0),
99
+ });
100
+ const outK = cat(bk, rk);
101
+ const outV = cat(bv, rv);
102
+ for (const t of [bk, bv, rk, rv]) disposeTriple(t);
103
+ return {
104
+ keys: outK, values: outV,
105
+ leftPad: [...leftPad.map((p) => p + (width - S)), width - Lr],
106
+ width,
107
+ };
108
+ }
109
+
110
+ /** Quantized twin of extractKVRow (mlx-lm BatchKVCache.extract,
111
+ * models/cache.py:1080, over triples): pull row `i` past its left padding
112
+ * into a fresh SERIAL QuantizedKVCache with OWNED contiguous copies.
113
+ * Token-axis slicing is byte-safe because quantization packs along
114
+ * HEAD_DIM (file header) — a row's quantized bytes come out exactly as
115
+ * the serial oracle wrote them. */
116
+ export function extractQuantRow(
117
+ cache: QuantizedKVCache, leftPad: number, i: number,
118
+ ): QuantizedKVCache {
119
+ if (!cache.keys || !cache.values) throw new Error("extractQuantRow: empty cache");
120
+ const S = cache.offset;
121
+ const cut = (t: ops.QuantizedTensor): ops.QuantizedTensor =>
122
+ tripleMap(t, (a) => {
123
+ const [, H, , D] = a.shape as [number, number, number, number];
124
+ const view = a.slice([i, 0, leftPad, 0], [i + 1, H, S, D]);
125
+ const own = ops.contiguous(view);
126
+ view.dispose();
127
+ return own;
128
+ });
129
+ const out = new QuantizedKVCache(cache.groupSize, cache.bits);
130
+ out.restoreState(cut(cache.keys), cut(cache.values), S - leftPad);
131
+ return out;
132
+ }
133
+
134
+ /** Quantized twin of filterKVRows: keep `keep` rows along the batch axis.
135
+ * Caller owns the result; inputs are not disposed. */
136
+ export function filterQuantRows(
137
+ keys: ops.QuantizedTensor, values: ops.QuantizedTensor, keep: number[],
138
+ ): { keys: ops.QuantizedTensor; values: ops.QuantizedTensor } {
139
+ const idx = MlxArray.fromInt32(Int32Array.from(keep), [keep.length]);
140
+ const take = (t: ops.QuantizedTensor): ops.QuantizedTensor =>
141
+ tripleMap(t, (a) => ops.takeAxis(a, idx, 0));
142
+ const k = take(keys);
143
+ const v = take(values);
144
+ idx.dispose();
145
+ return { keys: k, values: v };
146
+ }
147
+
148
+ /** Per-step cache wrapper for batched QUANTIZED decode with left-padded
149
+ * rows — the quantized twin of BatchedDecodeMaskCache. Subclasses
150
+ * QuantizedKVCache so attention's `instanceof` dispatch routes to
151
+ * quantizedSdpa exactly as for a serial quantized cache; delegates KV
152
+ * storage to the persistent batched inner and overrides the two things a
153
+ * padded batch needs: the padding-aware decode mask and per-row RoPE
154
+ * positions. Ephemeral — the scheduler builds a fresh wrapper each step
155
+ * and calls releaseRopeArr() after (never dispose(), which would free the
156
+ * inner). Rope is captured ONCE per step before updateAndFetch (attention
157
+ * reads it pre-write; see minicpm5.ts LlamaAttention.forward), so an
158
+ * eagerly-built [B] position array is exact. */
159
+ export class BatchedQuantDecodeMaskCache extends QuantizedKVCache {
160
+ override readonly ropeOffsetArr: MlxArray;
161
+
162
+ constructor(
163
+ private readonly inner: QuantizedKVCache,
164
+ private readonly B: number,
165
+ private readonly leftPad: number[],
166
+ ) {
167
+ super(inner.groupSize, inner.bits);
168
+ this.offset = inner.offset;
169
+ const data = new Int32Array(B);
170
+ for (let b = 0; b < B; b++) data[b] = inner.offset - leftPad[b]!;
171
+ this.ropeOffsetArr = MlxArray.fromInt32(data, [B]);
172
+ }
173
+
174
+ override updateAndFetchQuantized(k: MlxArray, v: MlxArray): [ops.QuantizedTensor, ops.QuantizedTensor] {
175
+ const r = this.inner.updateAndFetchQuantized(k, v);
176
+ this.offset = this.inner.offset;
177
+ return r;
178
+ }
179
+
180
+ override makeMask(N: number, _windowSize: number | null): Mask {
181
+ // Same convention as BatchedDecodeMaskCache: built pre-write, S spans
182
+ // offset + N; quantizedSdpaUnfused expands it for the 5-D GQA scores.
183
+ const S = this.inner.offset + N;
184
+ return { mode: "array", arr: buildBatchedDecodeMask(this.B, N, S, this.leftPad, null) };
185
+ }
186
+
187
+ /** Free the per-step RoPE array WITHOUT touching the persistent inner —
188
+ * name and signature must match the scheduler's structural
189
+ * `(c as { releaseRopeArr?: () => void })` cast. */
190
+ releaseRopeArr(): void {
191
+ this.ropeOffsetArr.dispose();
192
+ }
193
+
194
+ override state(): MlxArray[] {
195
+ return this.inner.state();
196
+ }
197
+ override isTrimmable(): boolean {
198
+ return this.inner.isTrimmable();
199
+ }
200
+ override trim(n: number): void {
201
+ this.inner.trim(n);
202
+ }
203
+ override dispose(): void {
204
+ this.inner.dispose();
205
+ }
206
+ }