mlx-bun 0.3.0 → 0.4.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 (150) hide show
  1. package/docs/README.md +4 -1
  2. package/docs/archive/investigations/openjarvis-fit.md +198 -0
  3. package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
  4. package/docs/design/batching.md +1321 -49
  5. package/docs/design/decode-speed-program.md +331 -1163
  6. package/docs/design/generic-model-support.md +79 -3
  7. package/docs/design/kv-cache.md +198 -54
  8. package/docs/design/speculative-decoding.md +387 -24
  9. package/docs/design/turboquant.md +123 -13
  10. package/docs/design/unified-engine-frontier-plan.md +144 -142
  11. package/docs/planning/PRODUCT_ROADMAP.md +16 -0
  12. package/docs/planning/release-notes-v0.4.0.md +45 -0
  13. package/docs/reference/benchmarks.md +2457 -6
  14. package/docs/reference/cli.md +41 -2
  15. package/docs/reference/distribution.md +16 -9
  16. package/docs/reference/environment.md +55 -5
  17. package/docs/reference/models.md +13 -4
  18. package/docs/reference/server-api.md +79 -25
  19. package/docs/reference/server-config.md +178 -84
  20. package/docs/reference/troubleshooting.md +13 -21
  21. package/package.json +1 -1
  22. package/src/backends/mlx/assistant-target.ts +46 -0
  23. package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
  24. package/src/backends/mlx/batch-group.ts +368 -224
  25. package/src/backends/mlx/cache-attachment.ts +67 -0
  26. package/src/backends/mlx/cache-layout.ts +31 -0
  27. package/src/backends/mlx/checkpoint-state.ts +53 -0
  28. package/src/backends/mlx/continuation-persistence.ts +106 -0
  29. package/src/backends/mlx/continuation-request.ts +43 -0
  30. package/src/backends/mlx/continuation.ts +77 -0
  31. package/src/backends/mlx/draft-target.ts +7 -22
  32. package/src/backends/mlx/full-kv-row-append.ts +32 -0
  33. package/src/backends/mlx/full-kv-row-donor.ts +54 -0
  34. package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
  35. package/src/backends/mlx/gateway-binding.ts +77 -18
  36. package/src/backends/mlx/kv-codec.ts +9 -0
  37. package/src/backends/mlx/kv-maintenance.ts +133 -0
  38. package/src/backends/mlx/kv-tensor-rows.ts +212 -0
  39. package/src/backends/mlx/model-serving.ts +1 -1
  40. package/src/backends/mlx/prefill-cohort.ts +89 -0
  41. package/src/backends/mlx/prefill-rows.ts +200 -0
  42. package/src/backends/mlx/prefill.ts +1 -1
  43. package/src/backends/mlx/projected-context-rows.ts +72 -0
  44. package/src/backends/mlx/request-state-policy.ts +41 -0
  45. package/src/backends/mlx/rollback.ts +41 -3
  46. package/src/backends/mlx/row-sampling.ts +49 -0
  47. package/src/backends/mlx/serial-executor.ts +35 -66
  48. package/src/backends/mlx/speculative-group.ts +359 -0
  49. package/src/backends/mlx/speculative-round.ts +161 -0
  50. package/src/backends/mlx/state-rows.ts +104 -0
  51. package/src/backends/mlx/state-views.ts +21 -1
  52. package/src/backends/mlx/target-layout-capability.ts +35 -0
  53. package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
  54. package/src/cli.ts +11 -36
  55. package/src/contracts/execution.ts +11 -0
  56. package/src/contracts/prefix-cache.ts +23 -0
  57. package/src/contracts/resources.ts +7 -0
  58. package/src/contracts/sampling.ts +23 -0
  59. package/src/contracts/scheduling.ts +18 -0
  60. package/src/engine/execution-plan.ts +18 -16
  61. package/src/engine/resources.ts +12 -1
  62. package/src/engine/scheduler.ts +10 -5
  63. package/src/fit.ts +29 -15
  64. package/src/generate.ts +77 -173
  65. package/src/inference/draft-acceptance.ts +41 -0
  66. package/src/inference/draft-output.ts +49 -0
  67. package/src/inference/rollback.ts +2 -2
  68. package/src/kv-scheme.ts +70 -12
  69. package/src/kv-store.ts +70 -16
  70. package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
  71. package/src/lab/paged-kv/paged-kv.ts +25 -11
  72. package/src/mlx/array.ts +2 -1
  73. package/src/model/artifact-identity.ts +73 -0
  74. package/src/model/batched-kv.ts +235 -0
  75. package/src/model/batched-mask.ts +12 -4
  76. package/src/model/batched-quant.ts +8 -2
  77. package/src/model/batched-quantized-kv.ts +144 -0
  78. package/src/model/batched-rotating-quant.ts +76 -15
  79. package/src/model/batched-rotating-state.ts +76 -0
  80. package/src/model/batched-rotating.ts +64 -22
  81. package/src/model/batched-row-storage.ts +52 -0
  82. package/src/model/batched-ssm.ts +52 -0
  83. package/src/model/batched-turboquant-kv.ts +93 -0
  84. package/src/model/compiled-decode.ts +7 -1
  85. package/src/model/decoded-kv-donor.ts +12 -0
  86. package/src/model/delayed-quantized-kv.ts +72 -0
  87. package/src/model/delayed-rotating-quantized-kv.ts +93 -0
  88. package/src/model/delayed-turboquant-kv.ts +55 -0
  89. package/src/model/full-prefill-padding.ts +56 -0
  90. package/src/model/full-prefill-row.ts +105 -0
  91. package/src/model/gemma4-base.ts +162 -80
  92. package/src/model/gemma4.ts +34 -26
  93. package/src/model/generated/gemma4-12b.ts +4 -1
  94. package/src/model/generated/gemma4-26b.ts +4 -1
  95. package/src/model/generated/gemma4-e4b.ts +4 -1
  96. package/src/model/glm52-cache.ts +80 -4
  97. package/src/model/glm52-mla.ts +12 -6
  98. package/src/model/glm52.ts +19 -40
  99. package/src/model/kv-attention-view.ts +67 -0
  100. package/src/model/minicpm5.ts +11 -7
  101. package/src/model/qwen3-delta-state.ts +74 -0
  102. package/src/model/qwen3-delta.ts +59 -36
  103. package/src/model/qwen3_5.ts +82 -42
  104. package/src/model/rotating-kv-donor.ts +35 -0
  105. package/src/model/rotating-kv-layout.ts +262 -0
  106. package/src/model/rotating-row-transaction.ts +35 -0
  107. package/src/model/speculative-rotating-kv.ts +101 -0
  108. package/src/model/ssm-prefill-padding.ts +39 -0
  109. package/src/model/trellis-linear.ts +2 -2
  110. package/src/model/turboquant-codec.ts +83 -0
  111. package/src/model/universal/dense.ts +14 -13
  112. package/src/preflight.ts +31 -15
  113. package/src/prompt-cache.ts +42 -44
  114. package/src/sampler.ts +36 -25
  115. package/src/serve/chat-stage.ts +2 -2
  116. package/src/serve/generation-gateway.ts +39 -30
  117. package/src/serve/model-host.ts +3 -4
  118. package/src/serve/request-plan.ts +4 -3
  119. package/src/serve/request-prep.ts +2 -4
  120. package/src/serve/text-completion-stage.ts +2 -2
  121. package/src/server.ts +64 -108
  122. package/src/spec/assistant-rows.ts +92 -0
  123. package/src/spec/assistant-source.ts +28 -18
  124. package/src/spec/cache-identity.ts +22 -0
  125. package/src/spec/deepspec-rows.ts +29 -0
  126. package/src/spec/deepspec-source.ts +12 -10
  127. package/src/spec/dflash-source.ts +28 -4
  128. package/src/spec/drafter.ts +153 -142
  129. package/src/spec/dspark/deepspec-module.ts +95 -120
  130. package/src/spec/dspark/module-dflash.ts +126 -170
  131. package/src/spec/glm52-mtp-graph.ts +29 -0
  132. package/src/spec/glm52-mtp-rows.ts +132 -0
  133. package/src/spec/glm52-mtp-source.ts +53 -136
  134. package/src/spec/glm52-mtp-state.ts +33 -0
  135. package/src/spec/ngram-source.ts +92 -20
  136. package/src/spec/projected-draft-rows.ts +73 -0
  137. package/src/spec/qwen-mtp-module.ts +171 -0
  138. package/src/spec/qwen-mtp-rows.ts +219 -0
  139. package/src/spec/qwen-mtp-source.ts +81 -226
  140. package/src/spec/qwen-mtp-state.ts +30 -0
  141. package/src/spec/serve-loop.ts +70 -68
  142. package/src/spec/source.ts +80 -15
  143. package/src/spec/two-model-rows.ts +61 -0
  144. package/src/spec/two-model.ts +43 -101
  145. package/src/ssd-cache.ts +33 -21
  146. package/src/ssd-durability.ts +11 -7
  147. package/src/web/app.js +57 -57
  148. package/src/backends/mlx/memory-guard.ts +0 -40
  149. package/src/spec/prefix-state.ts +0 -45
  150. /package/docs/{planning → archive/planning}/release-notes-v0.3.0.md +0 -0
@@ -64,6 +64,24 @@ mlx-bun serve GLM-5.2 --context-length 4096
64
64
  semantics, and which combinations compose live in
65
65
  **[server-config.md](server-config.md)** — this page only names them.
66
66
 
67
+ Library requests support delayed uniform or per-layer affine conversion with
68
+ qualified shared drafting providers, including rotating caches. CLI affine
69
+ quantization starts at zero.
70
+
71
+ The unreleased shared executor supports qualified Qwen MTP and start-zero
72
+ TurboQuant at one or several active rows; `--batch 1` still selects the
73
+ legacy serial control. No new CLI flag is required for the shared interfaces.
74
+ Full-attention and rotating targets, including Llama, MiniCPM and Gemma, use shared bf16, uniform KV4/KV8, per-layer affine KV or TurboQuant
75
+ prompt lookup and standalone drafting, including generated RAM/SSD state.
76
+ Gemma assistant drafting also shares these target layouts and caches its last true target
77
+ hidden alongside target KV through the same RAM/SSD interfaces.
78
+ DeepSpec and DSpark use the same executor and persist their projected context through those
79
+ interfaces. DeepSpec accepts bf16 or affine-quantized weights; DSpark retains
80
+ its Markov/RNN head and checkpoint confidence policy.
81
+
82
+ Shared Qwen MTP retains completed decode state through the existing
83
+ `--prompt-cache` and `--ssd-cache` settings.
84
+
67
85
  | Group | Flags |
68
86
  |---|---|
69
87
  | Network | `--host`, `--port`, `--no-open`, `--allow-private-media` |
@@ -71,13 +89,17 @@ semantics, and which combinations compose live in
71
89
  | Process model | `--isolate`, `--model-pool`, `--unix` (internal — the engine half of `--isolate`) |
72
90
  | Scheduling | `--batch` (`--decode-concurrency` accepted as the mlx_lm.server alias) |
73
91
  | KV cache | `--kv-quant`, `--paged-kv`, `--paged-kv-block-size` |
74
- | Adapters | `--adapter` (`--adapter-path` alias) |
75
- | Speculative decoding | `--draft-model`, `--draft-kind`, `--num-draft-tokens`, `--ngram-max`, `--ngram-min` |
92
+ | Adapters (compatible sets can batch) | `--adapter` (`--adapter-path` alias) |
93
+ | Speculative decoding (Qwen methods and full-attention/rotating lookup/standalone and Gemma assistant/DeepSpec/DSpark drafting share execution) | `--draft-model`, `--draft-kind`, `--num-draft-tokens`, `--ngram-max`, `--ngram-min` |
76
94
  | GLM-5.2 | `--mtp`, `--context-length` |
77
95
  | Sampling defaults | `--temperature` (`--temp` alias), `--top-p`, `--top-k`, `--max-tokens`, `--thinking`, `--hlg-sampling`, `--hlg-width`, `--hlg-shoulder`, `--hlg-toe`, `--hlg-pivot-offset` |
78
96
  | Parity tier | `--l1`, `--l2` (`--l3` errors — removed) |
79
97
  | Kill switches | `--compiled-decode`, `--compiled-activations`, `--fused-sdpa`, `--force-wire`, `--expert-offload` |
80
98
 
99
+ Serving treats memory estimates as advisory by default and attempts the request.
100
+ `--memory-budget` opts into estimated admission limits; `--kv-budget` opts
101
+ into aggregate KV limits. `--max-tokens` supplies an explicit output cap.
102
+
81
103
  Endpoints: `/v1/chat/completions`, `/v1/completions`, `/v1/messages`,
82
104
  `/v1/responses`, `/v1/embeddings`, `/v1/models`, `/v1/adapters`, `/health`,
83
105
  `/stats`, `/fit`, `/library`, `/downloads`; the web app at `/` (`/chat`,
@@ -469,6 +491,11 @@ and optiq reference servers and writes a dated report:
469
491
  ```sh
470
492
  bun scripts/bench-serve.ts all
471
493
  bun scripts/bench-serve.ts all --models cpm5,e4b --arms mlx-bun,mlx-lm --out report.md
494
+ bun scripts/bench-serve.ts all --models qwen27b --arms mlx-bun-serial,mlx-bun --logprobs --top-logprobs 3
495
+ # Same KV settings on both executors
496
+ bun scripts/bench-serve.ts all --models qwen27b --arms mlx-bun-serial,mlx-bun --kv-quant 4
497
+ # Same adapter settings on both execution arms:
498
+ bun scripts/bench-serve.ts all --models e4b --arms mlx-bun-serial,mlx-bun --adapter fixtures/adapters/upper
472
499
  ```
473
500
 
474
501
  Curated results: [benchmarks.md](benchmarks.md).
@@ -544,3 +571,15 @@ behind each "not covered" row is in
544
571
  Native verbs with no mlx-lm counterpart — `pi`, `harness pi`, `embed`,
545
572
  `fit`, `train-watch`, `memory`, `evals` — are the surface that makes mlx-bun
546
573
  more than a drop-in.
574
+
575
+
576
+ `--kv-quant turbo` composes with grouped lookup, standalone, Gemma assistant,
577
+ DeepSpec and DSpark drafting on supported targets. Sliding layers stay bf16.
578
+ See [batching design](../design/batching.md) for composition evidence and limits.
579
+
580
+ Paged KV retains the configured `--batch` size. Gemma4 bf16 requests use the shared executor and bypass the prompt cache; quantized KV and draft combinations remain unavailable. See [server configuration](server-config.md).
581
+
582
+ `--generation-checkpoint N` requires `--ssd-cache` and queues owned snapshots
583
+ for eligible ordinary requests. Shared continuation supports eligible Qwen, Llama, MiniCPM and Gemma4 requests with or without adapters, excluding media, grammar, fill, paging, logprobs or speculation.
584
+ Repeat the identical request to replay and resume; flush before shutdown for
585
+ eventual queued snapshots to become durable.
@@ -186,6 +186,13 @@ npm if the version is live. Env overrides: `MLX_BUN_SIGN_IDENTITY`,
186
186
  `NOTARY_PROFILE`, `BUILD_DIR`, `OUT_DIR` (release); `OUT_DIR`, `REPO`,
187
187
  `TAP_REPO` (publish).
188
188
 
189
+ For an SSH-driven release, a local keychain unlock may still leave
190
+ `notarytool` reporting `keychainLocked` in the SSH session. The v0.3.0
191
+ release succeeded by running `release-binary.sh` in the M1 Max's local
192
+ Terminal, then `publish-release.sh` over SSH. Both steps used the same clean
193
+ merged source and `OUT_DIR`. Check `npm whoami` from the actual release
194
+ checkout too: a project `.npmrc` can override the user's configuration.
195
+
189
196
  ### The notarization lesson (v0.2.0, 2026-08-24)
190
197
 
191
198
  The first v0.2.0 submission came back **Invalid**. The release added
@@ -311,7 +318,7 @@ sh scripts/build-native-pack.sh <ver> dist-native
311
318
  gh release create native-v<ver> \
312
319
  dist-native/mlx-bun-native-v<ver>-arm64.tar.gz \
313
320
  dist-native/mlx-bun-native-v<ver>-arm64.tar.gz.sha256 \
314
- --title "mlx-bun native runtime v<ver>" --notes "<what changed>"
321
+ --latest=false --title "mlx-bun native runtime v<ver>" --notes "<what changed>"
315
322
  ```
316
323
 
317
324
  `build-native-pack.sh` stages the same six files with the same
@@ -323,13 +330,13 @@ package. Pre-publish smoke: a fresh-cache extraction plus a real MLX
323
330
  `dlopen` (and, when the helpers changed, an expert read or a video decode).
324
331
  The pack is ad-hoc signed only — `build-native-pack.sh` never touches a
325
332
  Developer ID and nothing notarizes it; it is `dlopen`ed by the user's own Bun
326
- process, not by our signed executable. The most recent pack
327
- (`native-v0.3.0`, published 2026-08-22) added `mlx-bun-frame-extract`.
333
+ process, not by our signed executable. Use `--latest=false` so a native pack
334
+ does not replace the application release behind the direct installer's URL.
328
335
 
329
- The working tree targets native pack 0.4.0 with MLX 0.32.2 and MLX-C
330
- `c74db5307cc8ce122f48d97ef951b30578674e7f`. Its six-file macOS 14 candidate
331
- has been built and verified locally. It is not published. Release the native
332
- asset before publishing any package that uses this manifest. Runtime bindings
336
+ Native pack 0.4.0 was published on 2026-09-08 with MLX 0.32.2 and MLX-C
337
+ `c74db5307cc8ce122f48d97ef951b30578674e7f`. Its six-file macOS 14 archive
338
+ was uploaded from the M1 Max and downloaded again; its checksum matches the
339
+ package manifest. It was published before mlx-bun v0.3.0. Runtime bindings
333
340
  match this one C API, and startup checks the linked core version. The old
334
341
  runtime and source controls remain separate for benchmark comparisons.
335
342
 
@@ -338,8 +345,8 @@ CI builds that pinned C wrapper with
338
345
  `MLX_BUN_LIBMLXC` to its `lib/libmlxc.dylib`. The script verifies the official
339
346
  macOS 14 MLX-Metal archive hash and checks out the exact MLX-C commit. It
340
347
  extracts native libraries and headers without running Python or compiling
341
- Metal sources. This also reproduces local native-pack build inputs before
342
- the candidate is published; Homebrew's C wrapper is not the CI runtime.
348
+ Metal sources. This also reproduces the native-pack build inputs;
349
+ Homebrew's C wrapper is not the CI runtime.
343
350
 
344
351
  Managed jobs in the compiled binary re-exec that binary through a private job
345
352
  entry, with the database/log paths passed in the environment. They do not depend
@@ -134,10 +134,10 @@ eleven skips on the old core. The local native-pack
134
134
  candidate under `native-pack-candidate/` extracts into `native-pack-extracted/`;
135
135
  all five Mach-O files target 14.0, have valid ad-hoc signatures and load their
136
136
  non-system dependencies from siblings. These checks ran on macOS 27, not an
137
- actual macOS 14 host. The production native-pack manifest now targets this
138
- 0.4.0 candidate and the verified local cache is
139
- `/Users/joshrossi/Library/Caches/mlx-bun/native-v0.4.0-arm64`. The candidate
140
- has not been published. The consolidated source passes 52 complete Qwen
137
+ actual macOS 14 host. This build is published as native pack 0.4.0 and the
138
+ verified local cache is
139
+ `/Users/joshrossi/Library/Caches/mlx-bun/native-v0.4.0-arm64`. Publication
140
+ details are in distribution.md. The consolidated source passes 52 complete Qwen
141
141
  forward/state/continuation cases and six Trellis generations, with fixed
142
142
  source/runtime/reference hashes and clean worker exits. The consolidated
143
143
  model-free suite passes 1,875 tests with ten skips; all typechecks pass.
@@ -179,6 +179,40 @@ against an M4 Pro output fixture. The test still requires exact output and
179
179
  state hashes; Python runs only the external reference. Trellis variant
180
180
  matrix tests allow 30 seconds for first-use kernel compilation on CI.
181
181
 
182
+ The M1 Max override was replayed on 2026-09-08 with the same inputs using
183
+ `/Users/joshrossi/Code/mlx-lm/.venv-mlx-0.32.2-macos14` (MLX/MLX-Metal
184
+ 0.32.2, MLX-LM 0.31.3). Bun matches its outputs and recurrent-state hashes
185
+ exactly. The review file is
186
+ `reports/qwen38-closeout/qwen-delta-m1-review.json`.
187
+
188
+ Qwen3-Embedding tests also resolve `goldenPath("qwen3-embed")`, including
189
+ machine overrides, and skip when their required binary blobs are absent.
190
+ Regenerate with the matching Python and
191
+ `scripts/oracle/gen-qwen3-embed-golden.py <artifact> --replay goldens/qwen3-embed/meta.json`;
192
+ `--output-dir` selects a review directory. Replay preserves the text and token
193
+ IDs and checks the tokenizer against them. Metadata records the device,
194
+ runtime, artifact config, original metadata and output hashes. The M1 Max
195
+ replay matches hidden states and pooled embeddings exactly. Cosine uses both
196
+ vector norms: bf16 normalization does not guarantee a unit norm when the
197
+ result is subsequently read as float32.
198
+
199
+ The same M1 validation found June MiniCPM logit blobs without runtime
200
+ provenance. Explicit `bun scripts/regen.ts minicpm5` with the matching venv
201
+ preserves the artifact, prompt IDs and greedy trajectory; the regenerated
202
+ logits match Bun exactly. The generator now records runtime/device/config
203
+ provenance and all blob hashes. The old files and reviewed differences remain
204
+ under `reports/qwen38-closeout/minicpm5-golden-before/` and
205
+ `minicpm5-golden-review.json`.
206
+
207
+ The Gemma bf16 and mixed-KV M1 replays also pass with unchanged prompt IDs
208
+ on MLX 0.32.2. This current M1 venv contains **mlx-optiq 0.2.7** by installed
209
+ distribution metadata, matching the documented M4 installation; it is not
210
+ the historical 0.2.15 environment. OptiQ's module `__version__` incorrectly
211
+ reports 0.2.5 there, so regeneration records distribution metadata. Old
212
+ fixtures remain under `reports/qwen38-closeout/{parity,mixed-kv}-golden-before/`.
213
+ These are current-stack parity checks, not a runtime-only A/B against the
214
+ older mixed-KV fixture's 0.31.2/0.2.15 combination.
215
+
182
216
  Readable oracle source, in that `site-packages/`: `mlx_lm/models/gemma3.py`
183
217
  and `mlx_lm/server.py` for the port targets; `mlx_lm/models/cache.py` for the
184
218
  cache classes; `mlx_lm/tokenizer_utils.py` for prompt rendering;
@@ -250,7 +284,19 @@ servers DO validate: the Bearer token must start with `sk-optiq-`
250
284
 
251
285
  ## Bun and FFI facts
252
286
 
253
- **Bun ≥ 1.4.0.** `package.json` `engines.bun` is `>=1.4.0`. `Bun.Image`
287
+ **Development and CI: Bun 1.4.2.** Build the committed frontend bundle with
288
+ that version so its byte-for-byte freshness check uses the same compiler.
289
+ Performance comparisons record the runtime and use the same version in both
290
+ arms; historical results retain their original version.
291
+
292
+ Non-login SSH on the M4 Pro can omit `/Users/joshrossi/.bun/bin` from `PATH`.
293
+ Put that directory on the remote command's PATH when running the test suite:
294
+ launching the parent Bun by absolute path alone does not let subprocess tests
295
+ find `bun`. The quantized-row validation's initial seven command-entry/job
296
+ failures disappear with that invocation corrected; no product code change was
297
+ needed for those failures.
298
+
299
+ **Runtime minimum: Bun ≥ 1.4.0.** `package.json` `engines.bun` is `>=1.4.0`. `Bun.Image`
254
300
  (native OS codecs via ImageIO, EXIF auto-orient, off-thread) is the vision
255
301
  decode path (`src/vision/preprocess.ts`); `Bun.Image.resize` is deliberately
256
302
  not used. The FFI ABI fix below is the other reason for the pin.
@@ -346,3 +392,7 @@ holds self-contained reproductions for upstream bug reports (the two Bun
346
392
  issues above, plus `optiq-mixed-kv-inert` and `vllm-metal-turboquant`); they
347
393
  are not part of the build. Committed absolute paths (the venv, the weights
348
394
  snapshot, `serve.sh`) are machine-specific on purpose — do not "correct" them.
395
+
396
+ The quantized-KV golden producer (`bun scripts/regen.ts kvq`) passes an absolute output directory to Python, since the child runs from `scripts/`. This preserves the selected machine directory and existing prompt IDs. Missing M4 blobs were regenerated from the pinned oracle into the isolated `delayed-affine-m4-oracle` override; numerical expectations were not produced by the inference implementation. Evidence: `reports/qwen38-closeout/composition-baseline/oracle-generator-path/`.
397
+
398
+ The M1 rotating-KV reference still identified MLX 0.31.2 and disagreed with both unchanged control and candidate on the KV8 greedy prefix. Regeneration with pinned MLX 0.32.2 retains the prompt IDs and passes exact logits plus both continuation checks. The matching M1 override and M4 reference caches are refreshed; previous manifests/blobs and provenance are retained with the generator-path evidence.
@@ -34,7 +34,7 @@ already-downloaded repos (it must match exactly one repo; see
34
34
  | `Qwen3-30B` | `mlx-community/Qwen3-30B-A3B-Thinking-2507-4bit` | Qwen3-MoE (`qwen3_moe`) · dedicated `qwen3-moe` graph · L1 (opt-in slow parity test, `MLX_BUN_TEST_QWEN3_MOE=1`) | text | two-model · ngram | bf16 (the artifact ships no kv_config.json) | Sparse top-k SwitchGLU experts. |
35
35
  | `diffusiongemma` | `mlx-community/diffusiongemma-26B-A4B-it-OptiQ-4bit` | DiffusionGemma (`diffusion_gemma`) · dedicated graph + **denoising loop** (non-autoregressive) · L2 (optiq) | text · **image** (single image; its own inline-quantized SigLIP tower). No video / audio | — (no AR verify loop) | — (canvas model; the `--kv-quant` axis does not apply) | Serial lane always. Diffusion-native LoRA trains. |
36
36
  | `GLM-5.2-colibri` | `mateogrgic/GLM-5.2-colibri-int4-with-int8-mtp` @ `3cc8db99b1b13fc79325d987ba3c1c430766b3b8` | GLM-5.2 (`glm_moe_dsa`) · **Colibri container loader** + dedicated `glm5.2` graph (streamed experts) · **exact artifact profile**, L3 | text | **checkpoint-native MTP** (`--mtp on`, the default; mutually exclusive with `--draft-model`) | checkpoint-native compressed **MLA/DSA** cache (its own scheme; SSD tier v3 persists it) | Fixed-context admission: `--context-length` (4096) and `--max-tokens` (128) are reserved by the header-only resource equation before weights open; `--memory-budget` defaults to min(25 GiB, RAM). `--mtp off` enables ordinary batching. Embeddings, vision/audio, adapters, and training are unsupported. See [below](#direct-container-artifacts-glm-5x). |
37
- | Tier-0 generic | validated manifest (`tests/support/universal-manifest.ts`): `Llama-3.2-1B-Instruct-4bit`, `Qwen2.5-0.5B-Instruct-4bit`, `Qwen3-0.6B-4bit`, `quantized-gemma-2b-it`, `gemma-2-2b-it-4bit`, `Phi-3.5-mini-instruct-4bit`, `GLM-4-9B-0414-4bit`, `granite-3.3-2b-instruct-4bit`, `starcoder2-3b-4bit`, `SmolLM3-3B-4bit` (all `mlx-community/`) and `ekryski/OLMo-2-0425-1B-Instruct-4bit` | 11 `model_type`s: `llama`, `smollm3`, `qwen2`, `qwen3`, `gemma`, `gemma2`, `phi3`, `olmo2`, `glm4`, `granite`, `starcoder2` (+ mlx-lm remaps `mistral`, `iquestcoder` → `llama`) · `universal-dense` graph · L1 | text | two-model · ngram | bf16 (the validated cell) | `ls` labels these `supported (generic)`. Batch only for plain full-attention archs (gated B=2 on Llama-3.2-3B); gemma2-family / sliding-window archs run serial. No compiled specialization. A `model_type` absent from the table refuses to load. |
37
+ | Tier-0 generic | validated manifest (`tests/support/universal-manifest.ts`): `Llama-3.2-1B-Instruct-4bit`, `Qwen2.5-0.5B-Instruct-4bit`, `Qwen3-0.6B-4bit`, `quantized-gemma-2b-it`, `gemma-2-2b-it-4bit`, `Phi-3.5-mini-instruct-4bit`, `GLM-4-9B-0414-4bit`, `granite-3.3-2b-instruct-4bit`, `starcoder2-3b-4bit`, `SmolLM3-3B-4bit` (all `mlx-community/`) and `ekryski/OLMo-2-0425-1B-Instruct-4bit` | 11 `model_type`s: `llama`, `smollm3`, `qwen2`, `qwen3`, `gemma`, `gemma2`, `phi3`, `olmo2`, `glm4`, `granite`, `starcoder2` (+ mlx-lm remaps `mistral`, `iquestcoder` → `llama`) · `universal-dense` graph · L1 | text | two-model · ngram | bf16; unreleased Llama full-attention uniform KV4/KV8 | `ls` labels these `supported (generic)`. Shared full-attention execution is qualified on Llama-3.2-3B at B1/B4, including speculative KV4/KV8 and generated RAM/SSD state; gemma2-family / sliding-window archs run serial. No compiled specialization. A `model_type` absent from the table refuses to load. |
38
38
 
39
39
  Rules that apply across rows (all enforced in `src/server.ts`):
40
40
 
@@ -45,8 +45,11 @@ Rules that apply across rows (all enforced in `src/server.ts`):
45
45
  MTP is its own row. Qwen MTP companions may store dense or affine-quantized
46
46
  projections; the loader uses each module's quantization metadata. Target
47
47
  verification is unchanged, while draft acceptance and speed depend on the
48
- companion. Any quantized KV scheme excludes the speculative lane
49
- (requests keep the scheme and decode serially without speculation).
48
+ companion. In the unreleased shared executor, qualified plain, uniform,
49
+ per-layer affine and TurboQuant layouts compose with drafting through the
50
+ target/provider interfaces. Positive affine conversion thresholds are also
51
+ supported by library requests. The loaded model and provider determine
52
+ which combinations can execute.
50
53
  - **Qwen3.5-family checkpoint generations.** Both ship the same graph and both
51
54
  load: the mlx-lm-converted naming (`language_model.model.*`,
52
55
  `language_model.lm_head`, `vision_tower.*`) and the transformers-5.8 export
@@ -60,7 +63,9 @@ Rules that apply across rows (all enforced in `src/server.ts`):
60
63
  - **KV schemes.** `--kv-quant 4|8` (uniform, L1) and `config` (per-layer
61
64
  `kv_config.json`, L2) apply to the autoregressive families that ship the
62
65
  file; `turbo` additionally requires a full-attention `head_dim` in
63
- {64, 128, 256, 512} (refused at startup otherwise) and is solo-only;
66
+ {64, 128, 256, 512} (refused at startup otherwise). Qualified TurboQuant
67
+ targets support shared ordinary and speculative execution; sliding layers
68
+ remain bf16;
64
69
  `--paged-kv` is Gemma 4 only, bf16, serial. Flag detail:
65
70
  [server-config.md](server-config.md).
66
71
  - **Modalities.** Image requests reach a Gemma 4 model only when
@@ -323,3 +328,7 @@ Safety rails:
323
328
 
324
329
  After a real deletion, gc re-scans so the registry drops the reaped
325
330
  rows immediately.
331
+
332
+
333
+ Non-Qwen grouped TurboQuant drafting, including assistant donor attention, uses
334
+ the shared cache interfaces. See [composition coverage](../design/batching.md#turboquant-donor-attention-and-grouped-draft-composition).
@@ -117,8 +117,8 @@ Request body (OpenAI chat schema; unknown fields ignored):
117
117
  "stream": false,
118
118
  "max_tokens": 1024, // or max_completion_tokens (wins); unset =
119
119
  // no cap — generate until EOS or the
120
- // admitted context is exhausted (admission
121
- // clamps; --max-tokens sets a server default)
120
+ // an explicit operator limit is reached;
121
+ // --max-tokens sets a server default
122
122
  "temperature": 0.7, // 0 = greedy
123
123
  "top_p": 0, "top_k": 0, // 0 = off
124
124
  "seed": 1234, // omit for time-derived
@@ -369,15 +369,34 @@ Speculative decoding is a **server-level mode** (`serve --draft-model`,
369
369
  or the model-free `serve --draft-kind ngram`, which mounts no draft
370
370
  model); there is no per-request draft field. The `speculation` usage
371
371
  extension appears on chat and text completions alike, non-streaming and on
372
- the final stream chunk. Spec-eligible requests are text-only on base weights
373
- (no adapter or logprobs capture, bf16 KV by default); experimental Qwen
374
- uniform KV4 start=0 can qualify through `MLX_BUN_QWEN_SPEC_KV4=1`.
375
- Ineligible requests decode normally and omit the field. The spec path bypasses
376
- the ordinary prompt cache. Qwen MTP with `MLX_BUN_MTP_PROMPT_CACHE=1` can reuse
377
- a paired target/draft RAM prefix, reported in `cached_tokens`; the default is
378
- zero. These prefixes do not persist across restarts. While a draft is mounted every request routes
379
- through the serial lane (mlx_lm.server parity: `is_batchable = draft is
380
- None`) — speculation and `--batch N` are different modes.
372
+ the final stream chunk. Spec-eligible requests are text-only on base weights.
373
+ In the unreleased shared executor, Qwen grouped MTP, prompt lookup and standalone drafting support logprobs and
374
+ bf16, uniform KV4/KV8 or per-layer affine KV, or TurboQuant. Qwen MTP and prompt lookup also support
375
+ positive library conversion thresholds for uniform KV4/KV8. Per-layer mixed KV and
376
+ adapters remain incompatible with speculation. Strict legacy serial execution
377
+ retains its narrower bf16/KV4 policy. The KV4 compatibility control is
378
+ `MLX_BUN_QWEN_SPEC_KV4=0`.
379
+ Ineligible requests decode normally and omit the field. Qwen MTP reuses paired
380
+ target/draft prefill and completed decode state through the shared RAM/SSD cache by default in the
381
+ unreleased source, reported in `cached_tokens`. Configured SSD persistence
382
+ supports reuse after restart; `MLX_BUN_MTP_PROMPT_CACHE=0` disables reuse.
383
+ Generated checkpoints cover processed tokens and use queued SSD persistence.
384
+ Long-conversation performance acceptance remains open. Qwen prompt lookup also
385
+ retains prefill and completed-output state in the common RAM/SSD cache, with
386
+ its committed token history stored as a companion attachment. Standalone drafting
387
+ on Qwen targets stores the draft model’s attention/recurrent state and pending
388
+ last draft token as companion state, including restoration after restart. Qualified Qwen
389
+ MTP, prompt lookup and standalone drafting use shared execution at one or several active rows and
390
+ report `usage.lane: "batched"`. Supported full-attention and rotating targets, including
391
+ Llama, MiniCPM and Gemma, use shared bf16, uniform KV4/KV8 or per-layer affine KV or TurboQuant lookup and standalone drafting, with
392
+ logprobs and generated RAM/SSD checkpoints. Delayed and per-layer affine rotating speculation uses the same row lifecycle; positive conversion thresholds are a library setting, while served schemes default to zero. Gemma assistant drafting also shares execution with bf16, uniform KV4/KV8 or per-layer affine KV or TurboQuant target storage and stores its
393
+ last true target hidden alongside generated RAM/SSD prefixes. Donor attention
394
+ handles row validity independently of the draft graph. DeepSpec and DSpark also use these shared target layouts and the common RAM/SSD cache
395
+ for projected context, with independent confidence-based proposal lengths.
396
+ Other provider/target combinations still
397
+ use the serial executor.
398
+ Prompt-lookup concurrent performance acceptance remains open. Ineligible requests can use continuous ordinary execution
399
+ when their features support it, even while a draft is mounted.
381
400
 
382
401
  `usage.fill` reports **token fast-forwarding** (`MLX_BUN_FILL=strict|echo`).
383
402
  Strict mode appends structural spans from the request's tools and chat
@@ -400,6 +419,13 @@ echo index proposed and `injected` is what survived. `wastedSamples` counts
400
419
  only ASSERT fills (a verify fill consumes the in-flight sample as its first
401
420
  position's check instead of discarding it).
402
421
 
422
+ An explicit `seed` controls a request-local random stream and can use
423
+ continuous execution. Repeating a request reproduces sampling at the same
424
+ model arithmetic and batch composition. Different batch shapes can change
425
+ floating-point results even with identical random draws; `--batch 1` retains
426
+ the existing fixed-shape control. This differs from mlx-lm's policy of routing
427
+ explicit seeds to its single-request generator.
428
+
403
429
  ### logprobs / top_logprobs
404
430
 
405
431
  With `"logprobs": true` and/or `"top_logprobs": k` (1–11), the
@@ -432,8 +458,8 @@ penalties), **before** the sampler's temperature/top-p/top-k/min-p/XTC.
432
458
  mlx-lm's top-k order is unspecified (argpartition); ours is sorted
433
459
  descending — the same set, so the entry is deterministically the argmax.
434
460
  Stream chunks never carry logprobs (mlx-lm's streaming responses don't
435
- either). Requests with logprobs run on the serial lane under `--batch N`
436
- (like the other mlx-lm sampler extensions). Invalid values are rejected
461
+ either). Requests with logprobs can use ordinary continuous execution under
462
+ `--batch N`; other feature exclusions still apply. Invalid values are rejected
437
463
  with mlx-lm's exact messages (see Errors).
438
464
 
439
465
  ### Streaming
@@ -544,11 +570,10 @@ Design and fidelity notes: [docs/reference/server-api.md](./server-api.md).
544
570
 
545
571
  ### Errors
546
572
 
547
- Runtime memory pressure can fail a request even after prompt-length admission.
548
- The serial memory guard first reclaims older prompt-cache entries, persisting
549
- them to the SSD tier when configured. If insufficient GPU headroom remains,
550
- the normal request/stream error path reports the failure. Saved in-flight
551
- generation checkpoints remain available for an identical retry.
573
+ Memory estimates do not reject or shorten requests by default. Actual runtime
574
+ failures use the request/stream error path when recoverable; native Metal
575
+ allocation failure can terminate the process. Saved generation checkpoints
576
+ remain available for an identical retry.
552
577
 
553
578
  All errors are `{ "error": { "message": …, ... } }`.
554
579
 
@@ -567,10 +592,12 @@ All errors are `{ "error": { "message": …, ... } }`.
567
592
  'low', 'medium', 'high', 'xhigh'`).
568
593
  - `400` with `"type": "memory_admission"`, `"code":
569
594
  "context_over_budget"` — the PROMPT itself leaves no generation slot
570
- within the max safe context (`src/serve/request-plan.ts`). A prompt that
595
+ within an explicitly configured context/budget limit (`src/serve/request-plan.ts`). A prompt that
571
596
  fits is never rejected for a broad `max_tokens`: the upper bound is
572
597
  capped to the remaining room (`max_tokens` is a ceiling, not a promise)
573
- and generation proceeds. The ceiling is visible at `/stats`.
598
+ and generation proceeds. This is opt-in via an operator budget/context cap
599
+ (or the fixed GLM-5.2 layout), never enforced from default fit estimates.
600
+ `/stats.admission.enforced_context_tokens` is null when no cap applies.
574
601
  - `429` with `"type": "resource_admission"`, `"code": "queue_full"` when
575
602
  a preparation or generation queue has 64 waiting requests. If SSE headers
576
603
  are already sent, the stream emits its terminal error instead.
@@ -590,7 +617,7 @@ and adapter selection as chat.
590
617
  "prompt": "Once upon a time", // REQUIRED, non-empty string only (token
591
618
  // arrays rejected, matching mlx_lm.server)
592
619
  "max_tokens": 512, // unset = no cap: generate until EOS or the
593
- // admitted context (DEVIATION: mlx_lm.server
620
+ // an explicit operator limit (DEVIATION: mlx_lm.server
594
621
  // defaults 512 — pass --max-tokens 512 to
595
622
  // reproduce); max_completion_tokens wins
596
623
  "stream": false,
@@ -849,8 +876,9 @@ vision/audio, adapters, and training are not emulated by the serving port.
849
876
  "pending_spill_bytes": 0, "dropped_spills": 0,
850
877
  "failed_spills": 0, "longest_durable_prefix_tokens": 0 },
851
878
  "admission": {
852
- "max_safe_context": 0, // tokens; requests above this 400
853
- "memory_budget_bytes": null, // explicit budget, or null (machine default)
879
+ "max_safe_context": 0, // advisory fit estimate, tokens
880
+ "enforced_context_tokens": null, // explicit cap or fixed layout; null = none
881
+ "memory_budget_bytes": null, // explicit budget, or null
854
882
  "usable_bytes": 0,
855
883
  "weights_bytes": 0
856
884
  },
@@ -924,7 +952,7 @@ generic all-resident SKU estimator is inapplicable
924
952
  ```jsonc
925
953
  {
926
954
  "machine": { "chip": "M4 Pro", "ram_bytes": 0, "bandwidth_gbs": 0.0 },
927
- "context_tokens": 8192, // current admission ceiling
955
+ "context_tokens": 8192, // estimated context capacity
928
956
  "typical_context_tokens": 8192, // min(8192, context_tokens)
929
957
  "typical_decode_tps": 0.0, // predicted at typical_context_tokens
930
958
  "measured_decode_tps": null, // real number from eval DB, or null
@@ -959,7 +987,11 @@ generic all-resident SKU estimator is inapplicable
959
987
  ## Adapters (LoRA hot-swap)
960
988
 
961
989
  Routes in `src/serve/model-admin-routes.ts`; the manager is
962
- `AdapterManager` in `src/lora.ts`.
990
+ `AdapterManager` in `src/lora.ts`. Requests selecting the same ordered adapter
991
+ set can share a continuous group. Different sets wait for the current group
992
+ to finish and then use the same executor. Prefix-cache identity is resolved
993
+ under the execution lease, so replacing an adapter while a request is queued
994
+ cannot reuse state from its previous weights.
963
995
 
964
996
  - `GET /v1/adapters` — `{ adapters: [{ id, path, rank, scale, size_bytes,
965
997
  mounted_layers, ram_bytes }] }` — currently-mounted adapters only.
@@ -1719,3 +1751,25 @@ Under `--isolate`, the parent owns Responses continuation history and exposes it
1719
1751
  bounded store counters in `GET /engine` as `response_store`. Model-worker restart
1720
1752
  or eviction preserves that history; restarting the parent clears it. Direct
1721
1753
  serving retains the same process-local TTL and byte limits.
1754
+
1755
+
1756
+ Supported grouped TurboQuant drafting retains `usage.speculation`, seeded
1757
+ sampling, logprobs, structured output and generated RAM/SSD continuations.
1758
+ The cache codec does not select the scheduler or sampler implementation.
1759
+ Composition coverage: [batching design](../design/batching.md).
1760
+
1761
+ With paged KV enabled, supported Gemma4 bf16 text requests report the batched lane when submitted through continuous scheduling. Seeded sampling, logprobs and grammar use the same interfaces. These requests bypass prompt-cache lookup/publication; paging does not provide SSD persistence.
1762
+
1763
+ Ordinary adapter checkpoints are isolated by ordered adapter IDs and mounted
1764
+ content revisions. Base-model requests cannot restore or remove an adapter checkpoint.
1765
+ With generation checkpointing enabled, repeating an identical eligible request
1766
+ replays its saved completion prefix and resumes from the saved pending token.
1767
+ Cancellation retains queued snapshots; successful completion removes them.
1768
+ `POST /admin/cache/flush` drains both prompt spills and generation checkpoints,
1769
+ and its elapsed time and durability statistics include both queues. A hard
1770
+ process exit can lose snapshots that have not drained.
1771
+
1772
+ For resumed generation checkpoints, `usage.prompt_tokens_details.cached_tokens`
1773
+ counts only the reused original prompt, bounded by `usage.prompt_tokens`. Saved
1774
+ completion tokens are replayed as completion output and do not increase that
1775
+ prompt-cache usage counter.