mlx-bun 0.3.0 → 0.5.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 (231) hide show
  1. package/README.md +57 -32
  2. package/docs/README.md +58 -42
  3. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
  4. package/docs/archive/investigations/openjarvis-fit.md +198 -0
  5. package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
  6. package/docs/archive/planning/release-notes-v0.4.0.md +45 -0
  7. package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
  8. package/docs/design/batching.md +1535 -50
  9. package/docs/design/decode-speed-program.md +394 -1171
  10. package/docs/design/generic-model-support.md +195 -3
  11. package/docs/design/kv-cache.md +538 -225
  12. package/docs/design/speculative-decoding.md +552 -50
  13. package/docs/design/turboquant.md +140 -17
  14. package/docs/design/unified-engine-frontier-plan.md +259 -150
  15. package/docs/planning/PRODUCT_ROADMAP.md +21 -6
  16. package/docs/planning/public-readiness-handoff.md +244 -0
  17. package/docs/planning/public-readiness.md +149 -0
  18. package/docs/reference/benchmarks.md +3537 -1112
  19. package/docs/reference/cli.md +102 -2
  20. package/docs/reference/distribution.md +45 -13
  21. package/docs/reference/environment.md +159 -13
  22. package/docs/reference/library-api.md +18 -4
  23. package/docs/reference/memory.md +1 -1
  24. package/docs/reference/models.md +28 -7
  25. package/docs/reference/server-api.md +257 -33
  26. package/docs/reference/server-config.md +372 -151
  27. package/docs/reference/troubleshooting.md +13 -21
  28. package/package.json +2 -2
  29. package/src/audio/audiotoolbox.ts +112 -0
  30. package/src/audio/conformer.ts +8 -0
  31. package/src/audio/mic-capture.ts +98 -0
  32. package/src/audio/silero-vad.ts +379 -0
  33. package/src/audio/whisper-decode.ts +774 -0
  34. package/src/audio/whisper-fast.ts +732 -0
  35. package/src/audio/whisper-format.ts +73 -0
  36. package/src/audio/whisper-mel.ts +185 -0
  37. package/src/audio/whisper-timing.ts +274 -0
  38. package/src/audio/whisper-tokenizer.ts +239 -0
  39. package/src/audio/whisper-transcribe.ts +410 -0
  40. package/src/backends/mlx/assistant-target.ts +46 -0
  41. package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
  42. package/src/backends/mlx/autoregressive.ts +22 -1
  43. package/src/backends/mlx/batch-group.ts +464 -236
  44. package/src/backends/mlx/cache-attachment.ts +67 -0
  45. package/src/backends/mlx/cache-layout.ts +31 -0
  46. package/src/backends/mlx/checkpoint-state.ts +53 -0
  47. package/src/backends/mlx/continuation-persistence.ts +107 -0
  48. package/src/backends/mlx/continuation-request.ts +43 -0
  49. package/src/backends/mlx/continuation.ts +77 -0
  50. package/src/backends/mlx/draft-target.ts +7 -22
  51. package/src/backends/mlx/encoder-cache.ts +25 -0
  52. package/src/backends/mlx/fill-append.ts +21 -8
  53. package/src/backends/mlx/fill-group.ts +497 -0
  54. package/src/backends/mlx/full-kv-row-append.ts +32 -0
  55. package/src/backends/mlx/full-kv-row-donor.ts +54 -0
  56. package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
  57. package/src/backends/mlx/gateway-binding.ts +122 -19
  58. package/src/backends/mlx/kv-codec.ts +9 -0
  59. package/src/backends/mlx/kv-maintenance.ts +161 -0
  60. package/src/backends/mlx/kv-tensor-rows.ts +212 -0
  61. package/src/backends/mlx/mixed-iteration.ts +62 -0
  62. package/src/backends/mlx/model-prompt.ts +46 -32
  63. package/src/backends/mlx/model-serving.ts +5 -4
  64. package/src/backends/mlx/paged-cache-codec.ts +27 -0
  65. package/src/backends/mlx/prefill-cohort.ts +100 -0
  66. package/src/backends/mlx/prefill-policy.ts +26 -0
  67. package/src/backends/mlx/prefill-rows.ts +244 -0
  68. package/src/backends/mlx/prefill.ts +1 -1
  69. package/src/backends/mlx/projected-context-rows.ts +72 -0
  70. package/src/backends/mlx/prompt-input.ts +29 -0
  71. package/src/backends/mlx/qwen-prompt-input.ts +40 -0
  72. package/src/backends/mlx/request-state-policy.ts +54 -0
  73. package/src/backends/mlx/rollback.ts +45 -4
  74. package/src/backends/mlx/row-sampling.ts +49 -0
  75. package/src/backends/mlx/serial-executor.ts +51 -73
  76. package/src/backends/mlx/speculative-group.ts +438 -0
  77. package/src/backends/mlx/speculative-round.ts +161 -0
  78. package/src/backends/mlx/speculative.ts +24 -7
  79. package/src/backends/mlx/state-rows.ts +104 -0
  80. package/src/backends/mlx/state-views.ts +21 -1
  81. package/src/backends/mlx/target-layout-capability.ts +35 -0
  82. package/src/backends/mlx/transitioning-kv-rows.ts +173 -0
  83. package/src/cli.ts +333 -36
  84. package/src/contracts/execution.ts +21 -0
  85. package/src/contracts/object-cache.ts +8 -0
  86. package/src/contracts/pi-provider.ts +1 -1
  87. package/src/contracts/pi.ts +3 -1
  88. package/src/contracts/prefix-cache.ts +33 -0
  89. package/src/contracts/resources.ts +7 -0
  90. package/src/contracts/sampling.ts +23 -0
  91. package/src/contracts/scheduling.ts +27 -0
  92. package/src/engine/execution-plan.ts +38 -25
  93. package/src/engine/execution-tasks.ts +44 -0
  94. package/src/engine/namespaced-cache.ts +13 -0
  95. package/src/engine/resources.ts +12 -1
  96. package/src/engine/scheduler.ts +32 -7
  97. package/src/fill/fill-session.ts +14 -3
  98. package/src/fit.ts +29 -15
  99. package/src/generate.ts +105 -195
  100. package/src/grammar.ts +13 -0
  101. package/src/inference/draft-acceptance.ts +41 -0
  102. package/src/inference/draft-output.ts +49 -0
  103. package/src/inference/prefill.ts +6 -0
  104. package/src/inference/rollback.ts +2 -2
  105. package/src/kv-scheme.ts +92 -12
  106. package/src/kv-store.ts +183 -93
  107. package/src/lab/README.md +5 -1
  108. package/src/lab/paged-kv/paged-attention.ts +117 -0
  109. package/src/lab/paged-kv/paged-kv-rows.ts +111 -0
  110. package/src/lab/paged-kv/paged-kv.ts +126 -46
  111. package/src/md-modules.d.ts +5 -0
  112. package/src/mlx/array.ts +39 -1
  113. package/src/mlx/ffi.ts +19 -0
  114. package/src/mlx/normalized-argmax.ts +116 -0
  115. package/src/mlx/ops.ts +97 -10
  116. package/src/model/artifact-identity.ts +73 -0
  117. package/src/model/batched-kv.ts +235 -0
  118. package/src/model/batched-mask.ts +12 -4
  119. package/src/model/batched-quant.ts +8 -2
  120. package/src/model/batched-quantized-kv.ts +144 -0
  121. package/src/model/batched-rotating-quant.ts +76 -15
  122. package/src/model/batched-rotating-state.ts +76 -0
  123. package/src/model/batched-rotating.ts +64 -22
  124. package/src/model/batched-row-storage.ts +52 -0
  125. package/src/model/batched-ssm.ts +52 -0
  126. package/src/model/batched-turboquant-kv.ts +94 -0
  127. package/src/model/compiled-decode.ts +7 -1
  128. package/src/model/decoded-kv-donor.ts +12 -0
  129. package/src/model/delayed-quantized-kv.ts +72 -0
  130. package/src/model/delayed-rotating-quantized-kv.ts +93 -0
  131. package/src/model/delayed-turboquant-kv.ts +57 -0
  132. package/src/model/factory.ts +26 -0
  133. package/src/model/fingerprint.ts +12 -0
  134. package/src/model/full-prefill-padding.ts +56 -0
  135. package/src/model/full-prefill-row.ts +105 -0
  136. package/src/model/gemma4-base.ts +183 -82
  137. package/src/model/gemma4.ts +116 -28
  138. package/src/model/generated/gemma4-12b.ts +4 -1
  139. package/src/model/generated/gemma4-26b.ts +4 -1
  140. package/src/model/generated/gemma4-e4b.ts +4 -1
  141. package/src/model/glm52-cache.ts +80 -4
  142. package/src/model/glm52-mla.ts +12 -6
  143. package/src/model/glm52.ts +19 -40
  144. package/src/model/kv-attention-view.ts +70 -0
  145. package/src/model/minicpm5.ts +11 -7
  146. package/src/model/profile.ts +29 -4
  147. package/src/model/quantized-append-attention.ts +31 -0
  148. package/src/model/qwen3-delta-state.ts +74 -0
  149. package/src/model/qwen3-delta.ts +59 -36
  150. package/src/model/qwen3-mrope.ts +7 -9
  151. package/src/model/qwen3_5.ts +177 -75
  152. package/src/model/rotating-kv-donor.ts +35 -0
  153. package/src/model/rotating-kv-layout.ts +262 -0
  154. package/src/model/rotating-row-transaction.ts +35 -0
  155. package/src/model/speculative-rotating-kv.ts +101 -0
  156. package/src/model/ssm-prefill-padding.ts +39 -0
  157. package/src/model/support.ts +8 -0
  158. package/src/model/token-groups.ts +66 -0
  159. package/src/model/trellis-linear.ts +4 -4
  160. package/src/model/turboquant-codec.ts +89 -0
  161. package/src/model/universal/dense.ts +14 -13
  162. package/src/model/whisper.ts +435 -0
  163. package/src/native/mic_capture.swift +92 -0
  164. package/src/native-pack.ts +6 -5
  165. package/src/pi-web.ts +9 -1
  166. package/src/preflight.ts +31 -15
  167. package/src/prompt-cache.ts +306 -86
  168. package/src/runtime-config.ts +1 -5
  169. package/src/safetensors.ts +9 -2
  170. package/src/sampler.ts +37 -25
  171. package/src/serve/audio-routes.ts +323 -0
  172. package/src/serve/chat-request.ts +2 -0
  173. package/src/serve/chat-stage.ts +19 -14
  174. package/src/serve/completion-executor.ts +9 -1
  175. package/src/serve/discovery-routes.ts +17 -1
  176. package/src/serve/generated-token-history.ts +57 -0
  177. package/src/serve/generation-gateway.ts +88 -38
  178. package/src/serve/model-binding.ts +7 -1
  179. package/src/serve/model-host.ts +3 -4
  180. package/src/serve/preparation.ts +3 -3
  181. package/src/serve/prompt-response-trace.ts +14 -0
  182. package/src/serve/request-plan.ts +6 -3
  183. package/src/serve/request-prep.ts +40 -25
  184. package/src/serve/text-completion-stage.ts +2 -2
  185. package/src/serve/transcription-server.ts +64 -0
  186. package/src/serve/transcription-service.ts +502 -0
  187. package/src/server.ts +185 -261
  188. package/src/spec/assistant-rows.ts +93 -0
  189. package/src/spec/assistant-source.ts +28 -18
  190. package/src/spec/cache-identity.ts +22 -0
  191. package/src/spec/deepspec-rows.ts +29 -0
  192. package/src/spec/deepspec-source.ts +12 -10
  193. package/src/spec/dflash-source.ts +28 -4
  194. package/src/spec/drafter.ts +153 -142
  195. package/src/spec/dspark/deepspec-module.ts +95 -120
  196. package/src/spec/dspark/module-dflash.ts +126 -170
  197. package/src/spec/glm52-mtp-graph.ts +29 -0
  198. package/src/spec/glm52-mtp-rows.ts +132 -0
  199. package/src/spec/glm52-mtp-source.ts +54 -136
  200. package/src/spec/glm52-mtp-state.ts +33 -0
  201. package/src/spec/ngram-source.ts +115 -21
  202. package/src/spec/projected-draft-rows.ts +73 -0
  203. package/src/spec/qwen-mtp-module.ts +171 -0
  204. package/src/spec/qwen-mtp-rows.ts +230 -0
  205. package/src/spec/qwen-mtp-source.ts +84 -226
  206. package/src/spec/qwen-mtp-state.ts +30 -0
  207. package/src/spec/serve-loop.ts +73 -69
  208. package/src/spec/source.ts +93 -15
  209. package/src/spec/two-model-rows.ts +62 -0
  210. package/src/spec/two-model.ts +45 -103
  211. package/src/ssd-cache.ts +107 -36
  212. package/src/ssd-durability.ts +29 -27
  213. package/src/storage/host-buffer.ts +24 -0
  214. package/src/storage/kv-writer.ts +83 -0
  215. package/src/storage/kv-writer.worker.js +264 -0
  216. package/src/storage/retention-policy.ts +37 -0
  217. package/src/tiered-prompt-cache.ts +45 -0
  218. package/src/vision/embedder.ts +8 -0
  219. package/src/vision/prompt.ts +101 -65
  220. package/src/vision/qwen3vl-prompt.ts +101 -64
  221. package/src/vision/qwen3vl-tower.ts +11 -0
  222. package/src/vision/siglip.ts +8 -0
  223. package/src/web/app.html +12 -0
  224. package/src/web/app.js +217 -57
  225. package/src/web/src/chat.ts +4 -1
  226. package/src/web/src/composer.ts +3 -0
  227. package/src/web/src/voice.ts +141 -0
  228. package/src/web/sw.js +1 -1
  229. package/src/backends/mlx/memory-guard.ts +0 -40
  230. package/src/spec/prefix-state.ts +0 -45
  231. /package/docs/{planning → archive/planning}/release-notes-v0.3.0.md +0 -0
@@ -1,3 +1,9 @@
1
+ /** Request-level chunk policy. Execution consumes the selected size; model
2
+ * backends can account for their temporary workspace without changing admission. */
3
+ export interface PrefillPolicy {
4
+ chunkSize(promptTokens: number): number;
5
+ }
6
+
1
7
  export interface PrefillPosition {
2
8
  readonly length: number;
3
9
  readonly position: number;
@@ -1,7 +1,7 @@
1
1
  /** A complete transaction over every participating target state layer.
2
2
  * A failed begin/resolve invalidates the run; its state must be discarded. */
3
- export interface SpeculativeTransaction {
3
+ export interface SpeculativeTransaction<Acceptance = number> {
4
4
  canBegin(drafts: number): boolean;
5
5
  begin(drafts: number): void;
6
- resolve(accepted: number): void;
6
+ resolve(accepted: Acceptance): void;
7
7
  }
package/src/kv-scheme.ts CHANGED
@@ -86,6 +86,42 @@ export function kvBytesAt(
86
86
  value.linearStateBytes;
87
87
  }
88
88
 
89
+ /** Head dims MLX 0.32.2's fused multi-query SDPA kernel serves for L > 8
90
+ * queries on M1–M4 GPUs (mlx/backend/metal/scaled_dot_product_attention.cpp
91
+ * `use_fallback`: 192 and 256 always fall back off-NAX; `has_fused_kernel`
92
+ * lists 64/72/80/96/128 for the full kernel). Every other head dim runs
93
+ * mlx/fast.cpp's composed fallback, which materializes the bf16 scores
94
+ * tensor [heads, L, S] plus a bool mask per full-attention layer. The
95
+ * decode kernel (L ≤ 8) fuses 256, so this term is a prefill-only cost. */
96
+ export const FUSED_SDPA_HEAD_DIMS: ReadonlySet<number> = new Set([64, 72, 80, 96, 128]);
97
+ const SDPA_SCORES_BYTES = 2; // bf16 scores in q's dtype
98
+ const SDPA_FUSED_MAX_QUERIES = 8;
99
+
100
+ /** Peak bytes of the SDPA-fallback scores tensor live during one prefill
101
+ * chunk of `chunk` queries against a `ctx`-token context: the last chunk
102
+ * sees the longest keys, and the lazy graph keeps one layer's scores live
103
+ * at a time. Zero when every attention layer's head dim fuses. Sliding
104
+ * layers see at most `window + L` keys. Admission-only (fit and the
105
+ * in-flight memory guard); nothing here changes what is computed. */
106
+ export function sdpaFallbackBytes(config: ModelConfig, chunk: number, ctx: number): number {
107
+ const text = config.text;
108
+ const heads = text.numAttentionHeads || 0;
109
+ const queries = Math.min(chunk, ctx);
110
+ if (heads <= 0 || queries <= SDPA_FUSED_MAX_QUERIES) return 0;
111
+ let peak = 0;
112
+ const layers = text.numHiddenLayers || text.layerTypes.length;
113
+ for (let layer = 0; layer < layers; layer++) {
114
+ const type = text.layerTypes[layer] ?? "full_attention";
115
+ if (type === "linear_attention") continue;
116
+ const sliding = type === "sliding_attention" && text.slidingWindow > 0;
117
+ const headDim = sliding ? text.headDim : text.globalHeadDim;
118
+ if (FUSED_SDPA_HEAD_DIMS.has(headDim)) continue;
119
+ const keys = sliding ? Math.min(ctx, text.slidingWindow + queries) : ctx;
120
+ peak = Math.max(peak, heads * queries * keys * SDPA_SCORES_BYTES);
121
+ }
122
+ return peak;
123
+ }
124
+
89
125
  export class KvScheme {
90
126
  readonly kind: KvSchemeKind;
91
127
  readonly options: Readonly<ResolvedKvSchemeOptions>;
@@ -112,10 +148,17 @@ export class KvScheme {
112
148
  get cacheKey(): string {
113
149
  if (this.kind === "turbo") {
114
150
  const turbo = this.options.turboQuant!;
115
- return `turbo-k${turbo.kBits}v${turbo.vBits}`;
151
+ const start = this.options.quantizedKvStart ?? 0;
152
+ return `turbo-k${turbo.kBits}v${turbo.vBits}${start > 0 ? `-start${start}` : ""}`;
153
+ }
154
+ if (this.kind === "affine-uniform") {
155
+ const start = this.options.quantizedKvStart ?? 5000, group = this.options.kvGroupSize ?? 64;
156
+ return `kv${this.options.kvBits}${group !== 64 ? `-g${group}` : ""}${start > 0 ? `-start${start}` : ""}`;
157
+ }
158
+ if (this.kind === "affine-config") {
159
+ const start = this.options.quantizedKvStart ?? 0;
160
+ return `config${start > 0 ? `-start${start}` : ""}`;
116
161
  }
117
- if (this.kind === "affine-uniform") return `kv${this.options.kvBits}`;
118
- if (this.kind === "affine-config") return "config";
119
162
  return "bf16";
120
163
  }
121
164
 
@@ -129,6 +172,28 @@ export class KvScheme {
129
172
  return "bf16";
130
173
  }
131
174
 
175
+ /** Configured attention storage, not live allocator occupancy. Recurrent
176
+ * layers retain their own state and never acquire an attention KV codec. */
177
+ describe(config: ModelConfig) {
178
+ const layers: Record<string, number> = {};
179
+ const attention = { global: 0, sliding_window: 0 };
180
+ let recurrent_layers = 0;
181
+ const perLayer = new Map(this.options.kvConfig?.map(entry => [entry.layerIdx, entry.bits]));
182
+ for (let layer = 0; layer < config.text.numHiddenLayers; layer++) {
183
+ const type = config.text.layerTypes[layer] ?? "full_attention";
184
+ if (type === "linear_attention") { recurrent_layers++; continue; }
185
+ const sliding = type === "sliding_attention";
186
+ attention[sliding ? "sliding_window" : "global"]++;
187
+ const bits = this.kind === "affine-config" ? perLayer.get(layer)
188
+ : this.kind === "affine-uniform" ? this.options.kvBits : undefined;
189
+ const codec = this.kind === "turbo" && !sliding
190
+ ? `turbo-k${this.options.turboQuant!.kBits}v${this.options.turboQuant!.vBits}`
191
+ : bits ? `kv${bits}` : "bf16";
192
+ layers[codec] = (layers[codec] ?? 0) + 1;
193
+ }
194
+ return { mode: this.label, layers, attention, recurrent_layers };
195
+ }
196
+
132
197
  bytesAt(config: ModelConfig, tokens: number): number {
133
198
  // TurboQuant stays conservatively billed as bf16 until its packed layout
134
199
  // exposes a stable projector.
@@ -154,14 +219,24 @@ export class KvScheme {
154
219
  return this.kind === "turbo" ? {} : this.generationOptions;
155
220
  }
156
221
 
157
- /** The current batch engine accepts only per-layer affine schemes whose
158
- * named cache instances can perform the required conversion. The capability
159
- * probe is mandatory for quantized schemes so config shape alone can never
160
- * authorize placement. */
161
- batchable(config: ModelConfig, canConvert?: (layerIdx: number) => boolean): boolean {
222
+ /** Affine layouts convert during solo prefill. TQ also has a transitional
223
+ * row layout that preserves each request's delayed conversion boundary. */
224
+ batchable(config: ModelConfig, canConvert?: (layerIdx: number) => boolean,
225
+ cacheLayerCount = config.text.numHiddenLayers, capabilities: { delayedAffine?: boolean } = {}): boolean {
162
226
  if (this.kind === "bf16") return true;
163
- if (this.kind !== "affine-config") return false;
227
+ if (this.kind !== "affine-config" && this.kind !== "affine-uniform" && this.kind !== "turbo") return false;
164
228
  if (!canConvert) return false;
229
+ const start = this.options.quantizedKvStart ?? (this.kind === "affine-uniform" ? 5000 : 0);
230
+ if (start > 0 && this.kind !== "turbo" && !capabilities.delayedAffine) return false;
231
+ if (this.kind === "affine-uniform" || this.kind === "turbo") {
232
+ // Some models share the donor prefix's KV in later layers. Probe the
233
+ // actual cache list, just as uniform conversion does.
234
+ for (let layer = 0; layer < cacheLayerCount; layer++) {
235
+ if (config.text.layerTypes[layer] === "linear_attention") continue;
236
+ if (!canConvert(layer)) return false;
237
+ }
238
+ return true;
239
+ }
165
240
  return this.options.kvConfig!.every((entry) => {
166
241
  if (entry.layerIdx < 0 || entry.layerIdx >= config.text.numHiddenLayers) return false;
167
242
  return (config.text.layerTypes[entry.layerIdx] ?? "full_attention") !== "linear_attention" &&
@@ -175,11 +250,15 @@ export function resolveKvScheme(input: {
175
250
  turboQuant?: TurboQuantScheme;
176
251
  config?: readonly KvQuantSpec[] | null;
177
252
  missingConfig?: "bf16" | "error";
253
+ quantizedKvStart?: number;
178
254
  }): KvScheme {
255
+ const start = input.quantizedKvStart;
256
+ if (start !== undefined && (!Number.isSafeInteger(start) || start < 0))
257
+ throw new Error("quantizedKvStart must be a nonnegative integer");
179
258
  if (input.turboQuant) {
180
259
  return new KvScheme("turbo", {
181
260
  turboQuant: input.turboQuant,
182
- quantizedKvStart: 0,
261
+ quantizedKvStart: start ?? 0,
183
262
  });
184
263
  }
185
264
  if (input.override === "off" || input.override === undefined)
@@ -190,10 +269,11 @@ export function resolveKvScheme(input: {
190
269
  throw new Error("model has no kv_config.json (--kv-quant config)");
191
270
  return new KvScheme("bf16", {});
192
271
  }
193
- return new KvScheme("affine-config", { kvConfig: [...input.config] });
272
+ return new KvScheme("affine-config", { kvConfig: [...input.config],
273
+ ...(start === undefined ? {} : { quantizedKvStart: start }) });
194
274
  }
195
275
  return new KvScheme("affine-uniform", {
196
276
  kvBits: input.override,
197
- quantizedKvStart: 0,
277
+ quantizedKvStart: start ?? 0,
198
278
  });
199
279
  }