mlx-bun 0.4.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 (158) hide show
  1. package/README.md +57 -32
  2. package/docs/README.md +55 -42
  3. package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
  4. package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
  5. package/docs/design/batching.md +220 -7
  6. package/docs/design/decode-speed-program.md +121 -66
  7. package/docs/design/generic-model-support.md +116 -0
  8. package/docs/design/kv-cache.md +380 -211
  9. package/docs/design/speculative-decoding.md +165 -26
  10. package/docs/design/turboquant.md +20 -7
  11. package/docs/design/unified-engine-frontier-plan.md +120 -13
  12. package/docs/planning/PRODUCT_ROADMAP.md +5 -6
  13. package/docs/planning/public-readiness-handoff.md +244 -0
  14. package/docs/planning/public-readiness.md +149 -0
  15. package/docs/reference/benchmarks.md +3463 -3489
  16. package/docs/reference/cli.md +62 -1
  17. package/docs/reference/distribution.md +29 -4
  18. package/docs/reference/environment.md +104 -8
  19. package/docs/reference/library-api.md +18 -4
  20. package/docs/reference/memory.md +1 -1
  21. package/docs/reference/models.md +17 -5
  22. package/docs/reference/server-api.md +181 -11
  23. package/docs/reference/server-config.md +232 -105
  24. package/package.json +2 -2
  25. package/src/audio/audiotoolbox.ts +112 -0
  26. package/src/audio/conformer.ts +8 -0
  27. package/src/audio/mic-capture.ts +98 -0
  28. package/src/audio/silero-vad.ts +379 -0
  29. package/src/audio/whisper-decode.ts +774 -0
  30. package/src/audio/whisper-fast.ts +732 -0
  31. package/src/audio/whisper-format.ts +73 -0
  32. package/src/audio/whisper-mel.ts +185 -0
  33. package/src/audio/whisper-timing.ts +274 -0
  34. package/src/audio/whisper-tokenizer.ts +239 -0
  35. package/src/audio/whisper-transcribe.ts +410 -0
  36. package/src/backends/mlx/autoregressive.ts +22 -1
  37. package/src/backends/mlx/batch-group.ts +111 -27
  38. package/src/backends/mlx/cache-layout.ts +3 -3
  39. package/src/backends/mlx/continuation-persistence.ts +5 -4
  40. package/src/backends/mlx/encoder-cache.ts +25 -0
  41. package/src/backends/mlx/fill-append.ts +21 -8
  42. package/src/backends/mlx/fill-group.ts +497 -0
  43. package/src/backends/mlx/gateway-binding.ts +56 -12
  44. package/src/backends/mlx/kv-maintenance.ts +30 -2
  45. package/src/backends/mlx/mixed-iteration.ts +62 -0
  46. package/src/backends/mlx/model-prompt.ts +46 -32
  47. package/src/backends/mlx/model-serving.ts +4 -3
  48. package/src/backends/mlx/paged-cache-codec.ts +27 -0
  49. package/src/backends/mlx/prefill-cohort.ts +15 -4
  50. package/src/backends/mlx/prefill-policy.ts +26 -0
  51. package/src/backends/mlx/prefill-rows.ts +57 -13
  52. package/src/backends/mlx/prompt-input.ts +29 -0
  53. package/src/backends/mlx/qwen-prompt-input.ts +40 -0
  54. package/src/backends/mlx/request-state-policy.ts +18 -5
  55. package/src/backends/mlx/rollback.ts +6 -3
  56. package/src/backends/mlx/serial-executor.ts +16 -7
  57. package/src/backends/mlx/speculative-group.ts +105 -26
  58. package/src/backends/mlx/speculative.ts +24 -7
  59. package/src/backends/mlx/target-layout-capability.ts +1 -1
  60. package/src/backends/mlx/transitioning-kv-rows.ts +8 -0
  61. package/src/cli.ts +322 -0
  62. package/src/contracts/execution.ts +10 -0
  63. package/src/contracts/object-cache.ts +8 -0
  64. package/src/contracts/pi-provider.ts +1 -1
  65. package/src/contracts/pi.ts +3 -1
  66. package/src/contracts/prefix-cache.ts +13 -3
  67. package/src/contracts/scheduling.ts +9 -0
  68. package/src/engine/execution-plan.ts +23 -12
  69. package/src/engine/execution-tasks.ts +44 -0
  70. package/src/engine/namespaced-cache.ts +13 -0
  71. package/src/engine/scheduler.ts +22 -2
  72. package/src/fill/fill-session.ts +14 -3
  73. package/src/generate.ts +28 -22
  74. package/src/grammar.ts +13 -0
  75. package/src/inference/prefill.ts +6 -0
  76. package/src/kv-scheme.ts +22 -0
  77. package/src/kv-store.ts +115 -79
  78. package/src/lab/README.md +5 -1
  79. package/src/lab/paged-kv/paged-attention.ts +117 -0
  80. package/src/lab/paged-kv/paged-kv-rows.ts +33 -5
  81. package/src/lab/paged-kv/paged-kv.ts +108 -42
  82. package/src/md-modules.d.ts +5 -0
  83. package/src/mlx/array.ts +37 -0
  84. package/src/mlx/ffi.ts +19 -0
  85. package/src/mlx/normalized-argmax.ts +116 -0
  86. package/src/mlx/ops.ts +97 -10
  87. package/src/model/batched-turboquant-kv.ts +6 -5
  88. package/src/model/delayed-quantized-kv.ts +3 -3
  89. package/src/model/delayed-rotating-quantized-kv.ts +1 -1
  90. package/src/model/delayed-turboquant-kv.ts +6 -4
  91. package/src/model/factory.ts +26 -0
  92. package/src/model/fingerprint.ts +12 -0
  93. package/src/model/full-prefill-row.ts +1 -1
  94. package/src/model/gemma4-base.ts +25 -6
  95. package/src/model/gemma4.ts +82 -2
  96. package/src/model/kv-attention-view.ts +4 -1
  97. package/src/model/profile.ts +29 -4
  98. package/src/model/quantized-append-attention.ts +31 -0
  99. package/src/model/qwen3-mrope.ts +7 -9
  100. package/src/model/qwen3_5.ts +99 -37
  101. package/src/model/support.ts +8 -0
  102. package/src/model/token-groups.ts +66 -0
  103. package/src/model/trellis-linear.ts +2 -2
  104. package/src/model/turboquant-codec.ts +6 -0
  105. package/src/model/whisper.ts +435 -0
  106. package/src/native/mic_capture.swift +92 -0
  107. package/src/native-pack.ts +6 -5
  108. package/src/pi-web.ts +9 -1
  109. package/src/prompt-cache.ts +265 -43
  110. package/src/runtime-config.ts +1 -5
  111. package/src/safetensors.ts +9 -2
  112. package/src/sampler.ts +5 -4
  113. package/src/serve/audio-routes.ts +323 -0
  114. package/src/serve/chat-request.ts +2 -0
  115. package/src/serve/chat-stage.ts +17 -12
  116. package/src/serve/completion-executor.ts +9 -1
  117. package/src/serve/discovery-routes.ts +17 -1
  118. package/src/serve/generated-token-history.ts +57 -0
  119. package/src/serve/generation-gateway.ts +52 -11
  120. package/src/serve/model-binding.ts +7 -1
  121. package/src/serve/preparation.ts +3 -3
  122. package/src/serve/prompt-response-trace.ts +14 -0
  123. package/src/serve/request-plan.ts +2 -0
  124. package/src/serve/request-prep.ts +38 -21
  125. package/src/serve/transcription-server.ts +64 -0
  126. package/src/serve/transcription-service.ts +502 -0
  127. package/src/server.ts +132 -164
  128. package/src/spec/assistant-rows.ts +1 -0
  129. package/src/spec/deepspec-rows.ts +1 -1
  130. package/src/spec/dflash-source.ts +1 -1
  131. package/src/spec/glm52-mtp-source.ts +1 -0
  132. package/src/spec/ngram-source.ts +27 -5
  133. package/src/spec/projected-draft-rows.ts +2 -2
  134. package/src/spec/qwen-mtp-rows.ts +11 -0
  135. package/src/spec/qwen-mtp-source.ts +3 -0
  136. package/src/spec/serve-loop.ts +5 -3
  137. package/src/spec/source.ts +14 -1
  138. package/src/spec/two-model-rows.ts +1 -0
  139. package/src/spec/two-model.ts +2 -2
  140. package/src/ssd-cache.ts +76 -17
  141. package/src/ssd-durability.ts +20 -22
  142. package/src/storage/host-buffer.ts +24 -0
  143. package/src/storage/kv-writer.ts +83 -0
  144. package/src/storage/kv-writer.worker.js +264 -0
  145. package/src/storage/retention-policy.ts +37 -0
  146. package/src/tiered-prompt-cache.ts +45 -0
  147. package/src/vision/embedder.ts +8 -0
  148. package/src/vision/prompt.ts +101 -65
  149. package/src/vision/qwen3vl-prompt.ts +101 -64
  150. package/src/vision/qwen3vl-tower.ts +11 -0
  151. package/src/vision/siglip.ts +8 -0
  152. package/src/web/app.html +12 -0
  153. package/src/web/app.js +160 -0
  154. package/src/web/src/chat.ts +4 -1
  155. package/src/web/src/composer.ts +3 -0
  156. package/src/web/src/voice.ts +141 -0
  157. package/src/web/sw.js +1 -1
  158. /package/docs/{planning → archive/planning}/release-notes-v0.4.0.md +0 -0
@@ -0,0 +1,37 @@
1
+ # mlx-bun v0.5.0
2
+
3
+ Whisper speech-to-text: transcription, dictation and voice input, bit-exact
4
+ against mlx-whisper and faster than both mlx-whisper and whisper.cpp on the
5
+ same clips.
6
+
7
+ - `mlx-community/whisper-large-v3-turbo` (any fp16 `mlx-community/whisper-*`)
8
+ loads as a new encoder-decoder family. The oracle-parity graph matches
9
+ mlx-whisper 0.4.3 bit-exactly on the mel spectrogram, the encoder output and
10
+ every decoder logit; the default fast path (fused attention, fused cross-K/V,
11
+ a compiled decoder step with device-side logit filters, pipelined greedy,
12
+ in-graph beam top-k) is held to at most two differing tokens per clip. M1 Max,
13
+ 11 s clip: 340 ms greedy (mlx-whisper 371), 424 ms beam-5 (whisper.cpp 499).
14
+ - Decoding: greedy, sampling with the temperature fallback ladder, beam search
15
+ (mlx-whisper has none; matches whisper.cpp's beam-5 transcripts), prompts and
16
+ vocabulary hints fitted to the 223-token budget, language detection,
17
+ translation, >30 s seek loop, word-level timestamps.
18
+ - `POST /v1/audio/transcriptions` and `/v1/audio/translations` (OpenAI shape,
19
+ multipart or JSON, json/verbose_json/text/srt/vtt, SSE streaming) on every
20
+ server, with Whisper loaded per take beside the chat model and released right
21
+ after it; `mlx-bun serve <whisper model>` runs a transcription-only server.
22
+ - Streaming sessions (`/v1/audio/sessions`): audio uploaded while recording,
23
+ every finished 30 s window transcribed during capture — a 62 s take returns
24
+ 327 ms after its last chunk.
25
+ - Silero VAD v6.2 gate (ported from the reference graph, verified against the
26
+ pip package): silence returns an empty transcript without running Whisper;
27
+ runs on the stream inside sessions.
28
+ - In-process audio decoding through AudioToolbox (3.7 ms for an 11 s mp3 vs
29
+ 72 ms via afconvert); any CoreAudio-readable container.
30
+ - `mlx-bun transcribe <file>` and `mlx-bun dictate` (push-to-talk from the
31
+ microphone, print / clipboard / typed keystrokes; new `mlx-bun-mic-capture`
32
+ sidecar), and a hold-to-talk mic in the web composer for any chat model.
33
+ - Measured and shelved: a Core ML encoder on the Neural Engine (3.3× slower
34
+ than the GPU on the M1 Max); whisper.cpp-style encoder context truncation
35
+ ships as an off-by-default Lab option.
36
+
37
+ Native pack `native-v0.5.0` adds the `mlx-bun-mic-capture` helper.
@@ -136,7 +136,7 @@ continuous iff
136
136
  model is not DiffusionGemmaModel (non-autoregressive)
137
137
  AND batch > 1
138
138
  AND #modelCachesBatchable() (cache-capability gate, below)
139
- AND !hasVision (offset-0 prefill + bidirectional mask)
139
+ AND (!hasVision OR model supports prepared media input)
140
140
  AND adapter state supports compatible groups (when adapters are selected)
141
141
  AND KV scheme supports the loaded cache layouts
142
142
  AND selected method has a grouped implementation
@@ -546,13 +546,68 @@ They do not require repeating completed checks or withholding the current defaul
546
546
  | Scheduling and ordinary decode | Default cap eight uses shared execution at B1/B>1; explicit `--batch 1` retains serial | Keep the batched default using existing measurements; address specific regressions separately. Serial removal is deferred |
547
547
  | Speculative decode | Qwen MTP, prompt lookup, standalone, assistant, DeepSpec and seeded DSpark/DFlash providers are integrated through shared B1/B>1 interfaces, with the native/HTTP/cache checks recorded below | Finish specific unsupported combinations and measured regressions; GLM artifact testing is deferred and no trained DSpark/DFlash checkpoint exists |
548
548
  | Sampling and logprobs | Shared sampler contract; ordinary groups capture logprobs and accept explicit seeds | Extend same-B oracle/feature compositions and matched performance; preserve per-request RNG/history |
549
- | Grammar and fill | Grammar batches where enabled; grammar jump and fill remain serial-only | Shared constraint/proposal contracts and qualified batch compositions |
550
- | Adapters and media | Compatible adapter groups use shared execution; media remains serial | Adapter performance and broader compositions; media preparation and compatible state through shared execution |
551
- | KV layout | Full and rotating affine/TurboQuant layouts, delayed/per-layer transitions, speculative donors and ordinary paged storage are integrated and tested below | Direct paged-attention kernel, block sharing and quantized/speculative paging remain separate extensions; do not repeat the completed row-layout gates |
552
- | Prefix and output reuse | Ordinary execution and Qwen MTP prefill/completed decode use the shared RAM/SSD cache | Finish composed native, long Kanban and pressure/timing acceptance; identical retention semantics at B=1 and B>1 |
549
+ | Grammar and fill | Shared grammar proposals use grouped verification. Shared strict fill composes prefill, rows, sampling and committed append; native and HTTP/cache/timing gates pass | Shared echo uses grouped verification and row rollback; Qwen MTP consumes verified echo through the provider interface. Asserted fill with speculation, other providers and other named combinations remain. Keep grammar proposals opt-in after mixed timing results |
550
+ | Adapters and media | Compatible adapter groups use shared execution; Gemma4 image/audio and Qwen image/video inputs, encoder caching and generated media-prefix reuse pass native/HTTP and M4 comparisons | Chunked media prefill, lone-media latency costs, adapter performance and broader compositions |
551
+ | KV layout | Full and rotating affine/TurboQuant layouts, delayed/per-layer transitions, speculative donors and ordinary paged storage are integrated and tested below | C1–C5 add shared SSD blocks and optional direct bf16/affine paged attention with native/HTTP evidence. TQ pages and paged speculation remain extensions; reuse completed row-layout gates |
552
+ | Prefix and output reuse | Ordinary and grouped methods publish generated target/companion state to one RAM/SSD cache | Native/HTTP and full Kanban retention/durability acceptance are complete; investigate new regressions without reopening unchanged gates |
553
553
  | Generation resume | Shared ordinary resume is integrated after both-machine native/HTTP compiled, quantized/delayed and mixed-grammar checks, combined suites and eight M4 timing arms. The packed-model fixture now owns fresh weights per server | Adapter resume is integrated through the same policy; native/HTTP composition checks accompany it. Reuse the completed ordinary resume evidence |
554
554
  | Usage and cleanup | The gateway forwards timing/rates; the missing first-token timestamp in ordinary batch admission is now fixed | Consistent events/accounting, row cancellation, failure cleanup and persistence on shutdown |
555
- | Configuration | Placement still chooses path-specific implementations | One resolved configuration per concern; no setting silently disappears when B changes |
555
+ | Configuration | Bound configuration reaches each owner, including delayed KV conversion, copied codecs and paged cache identity; the flag-owner inventory is complete | Preserve the contract when adding settings; completed policy tests and fixed-settings performance remain accepted |
556
+
557
+ ### Prepared media and execution tasks
558
+
559
+ `MlxPromptInput` lets the model binding consume prepared embeddings and return
560
+ an owned final hidden row. `MlxPrefillCohort` retains initialization, precision
561
+ maintenance, projection and completion. An indivisible media attention span
562
+ declines prefill joins and mixed token packing; it processes the whole prompt
563
+ before entering ordinary shared decode. Sampling, row merging, retirement and
564
+ cancellation remain in their existing owners. Prepared media producers supply
565
+ an opaque identity for their exact media, encoder and preceding token prefix.
566
+ The ordinary cache combines this with the adapter namespace to retain generated
567
+ conversation state in RAM/SSD. Inputs without that identity remain uncached;
568
+ token IDs alone do not identify media contents. The input binding resumes
569
+ positioned text from the retained offset. Cache ownership and precision boundaries
570
+ are described in [kv-cache](kv-cache.md#514-prepared-media-prefix-identity).
571
+
572
+ Native preparation uses `ExecutionTasks` through the portable scheduler's
573
+ optional task boundary. The queue owns cancellation and failure settlement;
574
+ the scheduler owns when to advance it under the existing execution lease.
575
+ A task cannot replace model state. Exclusive mutations retain drain priority,
576
+ including a waiter arriving while preparation is running. Started work settles
577
+ before its owner releases native resources. The queue uses the portable
578
+ cancellation contract; the MLX binding adapts request AbortSignals.
579
+ `ModelPromptBuilder` receives a `PromptNativeWork` callback. Media downloads
580
+ and container transcoding finish outside that callback; lazy tower loading
581
+ and native prompt construction enter it. Encoder-local preprocessing remains
582
+ part of prompt construction. Grammar compilation enters the same execution
583
+ domain without putting template rendering or tokenization on the queue.
584
+
585
+ Gemma4 binds image/audio embeddings to this input interface. Qwen binds
586
+ image/video embeddings and a `MlxDecodeState` carrying each request's position
587
+ delta. The backend passes the current row order to that state after joins and
588
+ retirement. Its forward uses the cache's logical position array on device;
589
+ attention receives explicit positions without a module-global variable.
590
+ Input-state compatibility remains separate from cache-layout compatibility.
591
+ Media forwards with additional position state decline mixed-token packing.
592
+ Qwen image/video preprocessing completes outside the native execution lease.
593
+ Exact encoder features now use the shared cache object port; SSD reads finish
594
+ before native preparation. Cache placement remains independent of batching.
595
+ Diffusion retains its separate method. Qwen's source-controlled native comparison
596
+ and padded B1/B2/B4 affine/TurboQuant checks pass on both Macs, as do real image
597
+ HTTP checks on the packed 27B. The M4 image/video ABBA comparison preserves full
598
+ usage with faster concurrent completion; different batch geometries change some
599
+ responses. See [Qwen media evidence](../reference/benchmarks.md#shared-qwen-image-and-video-execution-2026-09-13)
600
+ for the source identity checks, probe correction and latency costs.
601
+ The input and task tests cover atomic boundaries, final projection geometry,
602
+ full same-machine Gemma logits/cache/continuation, active B1/B2 logit identity,
603
+ cancellation and mutation priority. Both-machine audio HTTP checks preserve the existing
604
+ transcription golden, exercise actual B2 decode and complete text generation
605
+ while an audio download is held open. The complete model-free suite and all
606
+ three TypeScript projects pass. The M4 ABBA media comparison preserves every
607
+ response and token count; concurrent completion improves with documented lone-
608
+ request and first-submitted-request costs in [benchmarks.md](../reference/benchmarks.md#shared-gemma-media-execution-2026-09-13);
609
+ see `tests/parity/shared-media-input.test.ts` and
610
+ `reports/prefill-observation/shared-media-*`.
556
611
 
557
612
  Tests must exercise actual B>1 work, not requests routed back to serial.
558
613
  Completed checks remain evidence for their recorded source and settings. A new
@@ -1818,11 +1873,19 @@ Both-machine tiny-model controls cover exact B1 proposals/absorption, zero-propo
1818
1873
 
1819
1874
  ### Paged storage through shared row and request-state interfaces
1820
1875
 
1821
- `PagedKvRows` owns per-request block pools, immutable snapshots, gathering, masks, offsets and row membership. The existing executor binds it through the cache-layout interface. `MlxRequestStatePolicy` supplies compatible state construction and a prompt-cache port; paging selects no prefix store. Switching back to ordinary requests restores their cache port. The same paging constructor serves the compatibility entry point and shared prefill. B1 keeps the existing paged numerical graph; B>1 gathers padded attention inputs while retaining separate persistent pools. Gemma12 full sampled logits and HTTP choices match the ordinary same-B controls, including seeded sampling, logprobs and grammar. Both-machine model-free, SSD and typechecks pass. The native storage comparison fixes cohort membership and snapshot boundaries on both sides, covering small blocks and the default block boundary; otherwise cache publication changes the numerical prefill graph. The matched M4 source comparison is effectively flat, and shared paging improves concurrent throughput versus serial while retaining effectively equal B1 decode. Concurrent text can differ across batch geometries; the exact storage oracle compares the same geometry. Timing and limitations live in benchmarks.md; evidence is `reports/qwen38-closeout/composition-baseline/paged-rows/`. Paged attention kernels, block-level prefix sharing, quantized paging and speculative paging remain separate work.
1876
+ `PagedKvRows` owns per-request block pools, immutable snapshots, gathering, masks, offsets and row membership. The existing executor binds it through the cache-layout interface. `MlxRequestStatePolicy` supplies compatible state construction and a prompt-cache port; paging selects no prefix store. Switching back to ordinary requests restores their cache port. The same paging constructor serves the compatibility entry point and shared prefill. B1 keeps the existing paged numerical graph; B>1 gathers padded attention inputs while retaining separate persistent pools. Gemma12 full sampled logits and HTTP choices match the ordinary same-B controls, including seeded sampling, logprobs and grammar. Both-machine model-free, SSD and typechecks pass. The native storage comparison fixes cohort membership and snapshot boundaries on both sides, covering small blocks and the default block boundary; otherwise cache publication changes the numerical prefill graph. The matched M4 source comparison is effectively flat, and shared paging improves concurrent throughput versus serial while retaining effectively equal B1 decode. Concurrent text can differ across batch geometries; the exact storage oracle compares the same geometry. Timing and limitations live in benchmarks.md; evidence is `reports/qwen38-closeout/composition-baseline/paged-rows/`. Subsequent C1–C5 work adds shared SSD blocks and optional direct bf16/affine paged attention; its measured results are recorded in benchmarks.md. TQ pages and paged speculation remain separate extensions.
1822
1877
 
1823
1878
  The sampler now exposes an optional stateless operation for compatible independent score rows. Ordinary decode and speculative verification share normalized-score argmax. Verification can sample a rectangle in one device operation and readback; acceptance still consumes only each request's valid positions. Grammar, processors, stochastic sampling, logprob capture and custom sampler functions retain their request-local operation. Same-shape numerical tests include rounding-created ties and large vocabularies; both-machine native/generated RAM/SSD and suites pass. Twelve matched M4 arms preserve ordinary throughput and improve lookup aggregate throughput; startup and trajectory limits are recorded in benchmarks.md. Lookup defaults remain unchanged. Evidence: `reports/qwen38-closeout/composition-baseline/independent-sampling/`.
1824
1879
 
1825
1880
 
1881
+ The affine same-B oracle now covers full-attention and rotating mixed-precision
1882
+ rows, including unequal lengths, window crossing, reordering and retirement.
1883
+ Equal-length rotating storage uses the pinned OptiQ implementation. Padded
1884
+ rotating cases compose it with stock mlx-lm batch positions and masks; they do
1885
+ not substitute solo-row logits for the batch oracle. The test-only reference
1886
+ and its limits are described in [benchmarks.md](../reference/benchmarks.md#same-batch-rotating-affine-oracle).
1887
+ Existing delayed-conversion, provider and lifecycle tests remain separate evidence.
1888
+
1826
1889
  ### Campaign checkout consolidation
1827
1890
 
1828
1891
  The PR incorporates the accepted implementations from the M1 and M4 campaign
@@ -1839,3 +1902,153 @@ after their source files and reports were verified in a content-addressed local
1839
1902
  `reports/pr-closeout/checkout-archive/`, with per-checkout manifests, SHA-256
1840
1903
  objects and the recorded base commit. The PR retains the implementation and
1841
1904
  findings; bulky raw experiment data stays local under the repository policy.
1905
+
1906
+
1907
+ ### Scheduling reference algorithms and request observations
1908
+
1909
+ The next scheduling baseline is an iteration token budget, following the
1910
+ upstream implementations below. Review date: 2026-09-12. Read implementation
1911
+ order as well as configuration docs; scheduling first does not necessarily
1912
+ mean delivering output before the iteration's other GPU work finishes.
1913
+
1914
+ | Reference | Algorithm and ownership | Application here |
1915
+ |---|---|---|
1916
+ | [vLLM V1 scheduler, `dff76bc`](https://github.com/vllm-project/vllm/blob/dff76bc3e8d702901e6dda5971f9506a6c1bc00f/vllm/v1/core/sched/scheduler.py#L562) | Tracks computed tokens against required tokens, including speculative positions. Allocates the iteration budget to running requests before waiting requests. Prefill chunks and decode tokens can enter the same execution batch. | Represent remaining work through the scheduling interface; methods supply valid token work and the backend owns batch packing and kernels. |
1917
+ | [llama.cpp server, `3057bb6`](https://github.com/ggml-org/llama.cpp/blob/3057bb66c86c46d5781e50e85462a760ba7d1feb/tools/server/server-context.cpp#L2974) | Adds generating/drafting slots before filling the remaining batch capacity with compatible prompt tokens. Logical batch and physical microbatch capacities are distinct. HTTP workers own parsing, templates and streaming; one inference thread owns slots. | Preserve execution ownership while allowing request preparation and response transport to progress independently. A shared request count is insufficient; account for token work too. |
1918
+ | [mlx-lm generator, `dcbcf78`](https://github.com/ml-explore/mlx-lm/blob/dcbcf786c0cf56f9a12fabe9468c887781431ae2/mlx_lm/generate.py#L1783) | Advances generation first, admits/splits prompt rows, then processes bounded prompt chunks. Prompt and generation batches remain separate. Responses return after prompt processing, whose cache evaluation synchronizes. The pinned oracle has the same order. | This is the closest numerical/backend reference, but its response timing is not evidence that output is immediately flushed. Compare its actual request timeline. |
1919
+ | [LangChain streaming](https://docs.langchain.com/oss/python/langchain/streaming) | Routes model-provided text, reasoning and tool-call chunks through the agent application. | Reference for stream semantics; this layer does not select GPU prefill/decode work. |
1920
+
1921
+ [vLLM's tuning guidance](https://docs.vllm.ai/en/latest/configuration/optimization/#chunked-prefill)
1922
+ explicitly treats token budget as a latency/throughput tradeoff. Its GPU-specific
1923
+ budget recommendations are not Apple Silicon defaults. Start with the established
1924
+ algorithm, measure the same submitted work on M4, and retain deviations only
1925
+ with recorded benefits and costs. No framework guarantees one universally best
1926
+ chunk size or latency policy.
1927
+
1928
+ The established backend exposes `advance()` and `advancePreparation()`
1929
+ operations. `MlxPrefillRows` groups prompt rows, while ordinary and speculative
1930
+ methods advance decoding rows separately. The existing preparation budget limits
1931
+ cohort admission; it is not vLLM's combined per-iteration token budget. True mixed
1932
+ execution therefore needs backend work as well as scheduling policy. Padding a
1933
+ single decode token to a long prompt's width would defeat the intended saving.
1934
+
1935
+ Phase 18 S1b adds bounded mixed work. The method retains request identity,
1936
+ computed/required positions and candidate state. Scheduling allocates
1937
+ bounded work to active requests and then queued requests; the backend executes
1938
+ compatible selections. Sampling and cache ownership remain unchanged. Acceptance
1939
+ covers lone requests, arrivals during prefill and decode, unequal lengths,
1940
+ cancellation/retirement, speculative work and quantized state. Compare first
1941
+ semantic output, inter-output gaps, total workload time, occupancy, padding and
1942
+ memory alongside aggregate throughput. Same-B numerical contracts remain binding.
1943
+
1944
+ The P2R observer now separates row preparation, forward, remaining cache
1945
+ execution, maintenance, checkpoint capture, projection, completion and companion
1946
+ work. Shared work IDs and process-local time origins allow cross-request
1947
+ attribution without counting shared GPU work several times. Bounded initial
1948
+ routing spans expose hidden control tokens. Measurement semantics and the report
1949
+ command live in [server-config](../reference/server-config.md); measured results
1950
+ live in [benchmarks](../reference/benchmarks.md#prefill-observation-and-scheduling-screen).
1951
+ The simple active-before-prefill experiment remains an unadopted patch in the
1952
+ local report directory; it is not the mixed execution design above.
1953
+
1954
+ A second bounded screen submits shared prefill evaluation asynchronously and
1955
+ polls MLX completion while allowing Bun to service I/O. MLX's
1956
+ [`array::is_available`](https://github.com/ml-explore/mlx/blob/main/mlx/array.cpp)
1957
+ checks the completion event, not merely graph submission; the pinned native
1958
+ pack exports mlx-c's `_mlx_array_is_available`. This avoids holding the host
1959
+ thread during the GPU wait, but changes when requests become visible to the
1960
+ existing scheduler. Both M1 MiniCPM arm orders delay the first staggered
1961
+ request substantially; aggregate results are inconsistent. The candidate is
1962
+ removed. [Measurements](../reference/benchmarks.md#nonblocking-prefill-completion-screen)
1963
+ retain the excluded patch. Host responsiveness and request scheduling must be
1964
+ measured together before retaining a different completion boundary.
1965
+
1966
+
1967
+ #### Mixed token execution
1968
+
1969
+ The Lab Gemma and Qwen3.5/3.8 paths execute running decode and queued prompt work together.
1970
+ `ExecutionGroup.mixedPreparation` reports running-token demand and the minimum
1971
+ prompt work needed for progress. The scheduler reserves that demand and assigns
1972
+ the remaining iteration budget to preparation. Existing cohort admission still
1973
+ accounts for total prompt work. Removing that limit was measured and rejected:
1974
+ on this M4 workload, larger cohorts lost throughput and delayed later first
1975
+ outputs. Both variants execute the same bounded mixed-token model port.
1976
+
1977
+ `MlxPreparationWork` carries a token allowance and forward operation into the
1978
+ shared preparation driver. It limits actual tokens across rows while preserving
1979
+ planned precision conversions and checkpoint endpoints. `runMixedTokenIteration`
1980
+ collects the preparation and decode forward inputs. The model's `MixedTokenModel`
1981
+ port receives independent `TokenGroup` inputs with their existing row geometry
1982
+ and caches. Decode finishes sampling, publication and retirement before a
1983
+ completed preparation can join the active rows. A restored or cancelled
1984
+ preparation can finish without producing model work; an exhausted decode can
1985
+ publish its pending token without another forward.
1986
+
1987
+ Gemma reuses its existing attention and feed-forward layer interfaces. Attention
1988
+ retains each group's RoPE positions, masks, SDPA shape and cache operations.
1989
+ `mapPackedTokens` concatenates only real tokens for the feed-forward block and
1990
+ restores each group's output shape afterwards. It adds no padding or host
1991
+ readback. This is shared feed-forward execution, not a fully packed attention
1992
+ kernel. The unpacked control keeps the same scheduler budget to isolate the
1993
+ packing benefit. The original single-group model call remains unchanged.
1994
+
1995
+ Packing can change GEMV/GEMM dispatch relative to solo generation. The numerical
1996
+ contract therefore uses the pinned oracle with the same packed feed-forward
1997
+ geometry and independent attention shapes. The real-model test passes on M4 for
1998
+ Gemma e4b, 12B and 26B with bf16 and uniform affine KV4, continuation, and unequal
1999
+ decode/prefill row counts. The
2000
+ uniform-KV oracle explicitly uses stock quantized SDPA on both sides. Native
2001
+ state tests also cover bounded preparation with immediate and delayed affine
2002
+ and TurboQuant conversion. Failure, cancellation and retirement tests exercise
2003
+ the shared execution group and the work coordinator.
2004
+
2005
+ Qwen reuses its attention/DeltaNet and feed-forward blocks through the same
2006
+ model port. Masks, convolution tails and recurrent state remain per group;
2007
+ layer evaluation materializes both residual and cache outputs to bound prefill
2008
+ memory. Each `TokenGroup` can supply a borrowed layer-capture callback and
2009
+ request preservation of its matmul geometry. Speculative methods use those
2010
+ options for target verification, including pending tokens and draft candidates,
2011
+ and keep provider companion updates in their preparation/verification adapters.
2012
+ The scheduler sees the method’s target-token demand, not its draft algorithm.
2013
+ Hidden captures belong to each work item instead of a shared mutable model tap.
2014
+
2015
+ The measured default decision is to retain mixed execution as opt-in. Gemma
2016
+ first-output latency improves with an aggregate-throughput cost; Qwen MTP has
2017
+ no first-output win and loses aggregate throughput, despite a shorter worst
2018
+ streaming pause. This closes the bounded S1b comparison. Trace phase
2019
+ `engine.mixed_forward` records the model-call construction span and real token
2020
+ counts; GPU waits remain at the existing evaluation/readback boundaries. The
2021
+ [measured comparison](../reference/benchmarks.md#mixed-prefill-and-decode-token-work)
2022
+ records latency, throughput, streaming gaps and the unpacked control. Runtime
2023
+ settings live in [server-config](../reference/server-config.md).
2024
+
2025
+
2026
+ ### Grammar candidates through the shared verifier
2027
+
2028
+ `GrammarController.proposeTokens` reads xgrammar's forced continuation without
2029
+ advancing the matcher. It joins the existing WASM queue so concurrent requests
2030
+ cannot overlap calls into the single WASM instance. A request-owned constraint
2031
+ port supplies candidates to the same grouped verifier used by learned drafts
2032
+ and prompt lookup. The sampler applies each request's mask and commits its
2033
+ accepted tokens; scheduling only sees method work. Target graph binding no
2034
+ longer requires a serial draft constructor.
2035
+
2036
+ The constraint provider reuses prompt lookup's committed-history ownership and
2037
+ checkpoint format. It never searches history and has no serial draft method.
2038
+ Empty candidate lists use the verifier's existing one-token step. Candidate
2039
+ rejection and row retirement keep target and companion state aligned through
2040
+ the existing transaction and output interfaces.
2041
+
2042
+ `MLX_BUN_GRAMMAR_JUMP=1` selects this method for eligible shared structured-output
2043
+ requests without a configured drafter; `MLX_BUN_GRAMMAR_DRAFT_TOKENS` controls
2044
+ depth separately from batching. Shared logprobs remain available because every
2045
+ output is sampled. Explicit serial retains direct retokenized jump-forward.
2046
+ That algorithm skips target sampling for forced spans; the shared proposal
2047
+ algorithm verifies them. Both remain opt-in and must be compared separately.
2048
+
2049
+ The focused MiniCPM and packed-Qwen checks cover nonmutating proposals, B1 and
2050
+ concurrent requests, independent stopping/cancellation, affine/TurboQuant and
2051
+ logprob delivery. Fixed bf16 MiniCPM outputs match ordinary decoding. Affine
2052
+ MiniCPM can choose different valid tokenizations across verification widths;
2053
+ repeated fixed-configuration outputs match. Same-geometry verifier/KV oracles
2054
+ remain the numerical contract. Performance is recorded in benchmarks.md.