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
@@ -414,12 +414,12 @@ satisfy, with the state of `GenerationGateway.#supportsContinuous` as read on
414
414
  | layer | today (src) | end state |
415
415
  |---|---|---|
416
416
  | dedicated per-model graph | both mechanisms (profile-routed) | unchanged |
417
- | mixed-precision KV (`affine-config`) | continuous when every configured layer is a plain or rotating KVCache the scheme can convert (`KvScheme.batchable` + the gateway's cache probe); uniform `kvBits`, SSM-layer configs, and TurboQuant route serial | one active scheme per server, any B |
418
- | LoRA adapters | serial only (`shape.hasAdapters`) | per-slot adapter state (vLLM Punica/SGMV pattern: every row runs the adapter math, non-adapter rows at scale 0) |
419
- | speculative decoding (two-model · assistant · dspark · deepspec · native MTP · ngram) | serial only; a mounted draft routes every request serial (`shape.hasDraft`, mlx_lm.server's `is_batchable = draft is None`); GLM-5.2 MTP is on by default and pins the serial verify lane (`--mtp off` restores batching) | per-slot drafting behind the `DraftSource` seam |
417
+ | mixed-precision KV (`affine-config`) | supported per-layer and uniform affine KV use continuous groups; incompatible layouts remain open | one active scheme per server, any B |
418
+ | LoRA adapters | compatible ordered adapter sets share a group; context and cache identity bind under the execution lease | per-slot adapter state (vLLM Punica/SGMV pattern: every row runs the adapter math, non-adapter rows at scale 0) |
419
+ | speculative decoding (two-model · assistant · dspark · deepspec · native MTP · ngram) | actual speculation uses the serial verifier; supported ordinary requests can batch with a mounted draft | per-slot drafting behind the `DraftSource` seam |
420
420
  | structured output (grammar) | both (per-row matchers; `MLX_BUN_GRAMMAR_BATCH=0` forces serial) | unchanged; keep the conformance gate |
421
- | sampling (temp/top-p/top-k/min-p/XTC/penalties/HLG/seed) | per-row `StepSampler` batches, including repetition penalty and logits extras; a user-fixed `seed` forces serial (mlx-lm's `_is_batchable`) | fully per-row, seeds included |
422
- | logprobs capture | serial only (`shape.wantsLogprobs`) | per-row readback in the scheduler |
421
+ | sampling (temp/top-p/top-k/min-p/XTC/penalties/HLG/seed) | per-row sampling sessions, including explicit seeds, repetition penalty and logits extras | fully per-row, seeds included |
422
+ | logprobs capture | shared row sampling and readback in continuous groups | preserve method composition |
423
423
  | vision / audio media prompts | serial only (`shape.hasVision`; embeddings-prefill bypasses the prompt cache) | batched media prefill |
424
424
  | prompt cache / SSD tier | both: scheduler joiners `take()` the longest usable prefix at admission, never-merged rows `put()` back on finish; the cold tier restores inside `take()` | block-granular sharing across rows |
425
425
  | paged KV (`--paged-kv`) | serial only, Gemma4 bf16, refuses `--batch N>1`, `--kv-quant`, `--draft-model`; bypasses the prompt cache | block-level CoW prefix sharing (vLLM oracle) |
@@ -472,132 +472,13 @@ entries so a caller cannot mutate conversion or accounting after resolution.
472
472
  `BatchScheduler.#prefillChunk` in `src/serve/batch-scheduler.ts` (the same
473
473
  conventions re-implemented over `PrefillState` for interleaved admission).
474
474
  Both are gated bit-exact, but a convention change must land in two places.
475
- 2. **Spec eligibility is decided after placement.** `planRequest` only knows
476
- `hasDraft = !!ctx.draft` (server-level), which routes every request serial
477
- while a draft is mounted. The real per-request eligibility — text-only, no
478
- adapters, no logprobs capture, bf16 KV (no `kvBits`/`kvConfig`/`turboQuant`),
479
- not paged — is evaluated inside `runGeneration` after `place()`; ineligible
480
- requests silently fall through to plain serial decode, and the lane label is
481
- corrected from `serial+spec` to `serial` only when `stats.spec` is absent
482
- (`finalLane` in the executor). Resolving this means lifting the eligibility
483
- predicate into `RequestShape` so placement and the lane label are decided
484
- once.
485
-
486
- Other seams worth knowing: `runGeneration` also decides the prompt-cache
487
- bypass (media, paged) and the adapter cache namespace; `--isolate` wraps the
488
- ENTIRE server as an engine child behind a proxy (`src/serve/isolate.ts`,
489
- `ModelPool` for `--model-pool` LRU residency and model switching by the
490
- request's `model` field) — the inter-process API is the /v1 surface itself, no
491
- second protocol.
492
-
493
- ## 7. The flag surface (src truth, grouped by layer)
494
-
495
- Read from `SERVER_FLAGS` and `applyDecodeRoute` in `src/cli.ts`. Every
496
- `MLX_BUN_*` value is read through the immutable runtime snapshot
497
- (`src/runtime-config.ts` `runtimeValue/runtimeFlag`, `src/flags.ts` `flagOn`);
498
- feature code never reads `process.env`, and the CLI installs tier/fork
499
- overrides with `configureRuntime()` before model modules load.
500
-
501
- **Parity tier (aliases):** `--l1` (default) · `--l2`. `--l3` hard-errors with a
502
- pointer to this doc.
503
-
504
- **L2 — quantization:** `--kv-quant config|off|4|8|turbo[:k<bits>v<bits>]`
505
- (default off). An older draft of this doc listed only `config|4|8|off`; the
506
- `turbo` axis is real and mutually exclusive with the affine modes.
507
-
508
- **L3 — scheduler / decode policy:** `--batch <n>` (default 8;
509
- `--decode-concurrency` alias) · `--kv-budget <GB>` · `--draft-model <query>` ·
510
- `--draft-kind two-model|assistant|dspark|deepspec|mtp|ngram` ·
511
- `--num-draft-tokens` · `--ngram-max` / `--ngram-min` · `--mtp on|off`
512
- (GLM-5.2 native MTP, on by default) · `--context-length` (GLM-5.2 reservation)
513
- · `--paged-kv` / `--paged-kv-block-size` (env `MLX_BUN_PAGED_KV=1`).
514
-
515
- **L4 — serving surface:** `--model` · `--host` · `--port` · `--memory-budget`
516
- · `--prompt-cache <GB>` · `--ssd-cache <dir>` · `--ssd-cache-max` ·
517
- `--ssd-demote-idle` · `--ssd-cache-verify` · `--isolate` · `--model-pool` ·
518
- `--unix` (internal) · `--no-open` · `--allow-private-media` · `--adapter`
519
- (`--adapter-path` alias) · `--thinking` · `--temperature`/`--temp` · `--top-p`
520
- · `--top-k` · `--max-tokens` · the HLG sampler family (`--hlg-sampling`,
521
- `--hlg-width`, `--hlg-shoulder`, `--hlg-toe`, `--hlg-pivot-offset`).
522
-
523
- **Kill switches (bit-exact A/B levers, each selects a slower same-parity
524
- path):** `--compiled-decode on|off` (`MLX_BUN_COMPILED_DECODE`) ·
525
- `--compiled-activations on|off` (`MLX_BUN_COMPILED_GEGLU` +
526
- `MLX_BUN_COMPILED_SWIGLU`; only gemma geglu and MiniCPM5 swiglu have an
527
- uncompiled form — qwen3/qwen3.5/universal compile unconditionally) ·
528
- `--fused-sdpa on|off` (`MLX_BUN_NO_FUSED_SDPA`, inverted; default follows
529
- `--kv-quant`) · `--force-wire` (`MLX_BUN_FORCE_WIRE`) · `--expert-offload`
530
- (`MLX_BUN_EXPERT_OFFLOAD`). An older draft said these were env-only; the three
531
- decode kill switches are CLI flags as well.
532
-
533
- **Lab / diagnostic env flags (no CLI flag; live with a bench and an expiry):**
534
- `MLX_BUN_GRAMMAR_JUMP=1` (jump-forward decoding, serial non-spec loop),
535
- `MLX_BUN_GRAMMAR_BATCH=0` (grammar back to serial), `MLX_BUN_BATCH_SSM=0`
536
- (SSM caches back to serial), `MLX_BUN_BATCH_NO_PIPELINE`,
537
- `MLX_BUN_BATCH_STEP_TRACE`, `MLX_BUN_BATCH_VEC_SAMPLE`, `MLX_BUN_BATCH_EXTEND`,
538
- `MLX_BUN_PREFILL_TAIL_SPLIT`, `MLX_BUN_FLASH_MIN_M`, the `MLX_BUN_CCE_*`
539
- training-kernel switches, and the memory/trace loggers. Anything
540
- output-changing is Lab by definition.
541
-
542
- **Deleted (2026-07-05, §11):** `--fused-decode`, `--fused-gelu`,
543
- `--perf-kernel`, `--l3`, and their env twins. None exist in `src/`.
544
-
545
- ## 8. Design invariants proven en route (durable, not changelog)
546
-
547
- - **Readbacks in a pipelined loop must not create ops.** `toFloat32()` on the
548
- int token array enqueued a cast BEHIND the next dispatched step, stalling the
549
- "overlapped" read a full GPU step per token. Use `MlxArray.toIntTokens()`.
550
- - **A lone row keeps bare serial-class caches.** `KVCache.makeMask(1)` is the
551
- empty mask and scalar rope; the batched step then dispatches the same graph
552
- the serial loop builds. The per-layer `BatchedDecodeMaskCache` wrapper is
553
- only for padded batches.
554
- - **Rope-array step-stability contract.** A batched cache's `ropeOffsetArr`
555
- must be stable within a decode step and refresh only at `releaseRopeArr()`;
556
- re-reading it mid-update ropes K and Q one position apart.
557
- - **Generated-file guards accept batched subclasses.** The generated forwards'
558
- per-layer `instanceof` guards pass any batched cache that subclasses a
559
- serial class, so an all-quant gemma batch decodes through the generated
560
- fast path — a feature (B=1 proved bit-exact) but a contract: batched caches
561
- must behave serially under re-reads. Test with FULL configs; one bf16 layer
562
- anywhere fails the guard and drops to the monolith.
563
- - **Quantization packs along HEAD_DIM**, so token-axis batch surgery over
564
- (packed, scales, biases) triples is byte-safe; solo rows convert at serial
565
- chunk boundaries (bit-exact by construction).
566
- - **A scheme-less path must refuse, never silently drop quantization.** The
567
- gateway only threads a `KvScheme` to the scheduler when it is batchable; the
568
- scheduler refuses an unsupported scheme at construction.
569
- - **Prefix sharing is non-consuming.** `PromptCache.take()` serves zero-copy
570
- clones (ref-counted retain so a demoted donor never unmaps pages a clone
571
- reads); the donor stays put; `put()` supersedes same-namespace prefix
572
- ancestors when the new entry is trimmable. Kills the cannibalization flaw
573
- (agent B consuming agent A's entry). v1 shares COMPUTE and durability;
574
- concurrent rows still hold separate physical KV — one shared physical prefix
575
- across rows is the block-KV frontier item, and whole-entry duplication in
576
- the disk tier is the block-granularity revisit trigger.
577
- - **Prompt-boundary snapshot.** The prompt+generation entry is untrimmable
578
- past a wrapped ring or under quantized KV, so every substantial request also
579
- snapshots a strict prompt prefix (cap `len-1`), the mlx-lm
580
- `insert_segments` invariant; a re-rendered next turn always matches.
581
- - **Never a JS callback as an mlx buffer destructor** — last-ref `Data` dtors
582
- run on the Metal completion thread (native `dlsym(free)` dtor, process-pinned
583
- mmaps).
584
- - **Isolation proxies the whole server**, not the gateway: grammar WASM,
585
- vision arrays, and sampler closures don't serialize; the /v1 surface is the
586
- IPC.
587
-
588
- ## 9. The frontier program (order matters)
589
-
590
- Ranked by expected frontier shift (memory ↓ / intelligence ↑ first, then
591
- speed). Each item is a Lab program with its own doc; `decode-speed-program.md`
592
- ranks the speed levers and is the only doc that does.
593
-
594
- 1. **Mixed-precision weights** — knapsack `--target-bpw` (ours; OptiQ-style
595
- sensitivity port in `src/quantize/sensitivity.ts`) and rotation-folded
596
- quantization (`docs/design/turboquant.md`, `--rotate-weights`). Gate:
597
- perplexity + frozen 6-task eval at equal bpw.
598
- 2. **TurboQuant KV** (`docs/design/turboquant.md`, landed v1) — orthogonal to
599
- allocation: mixed precision is the ALLOCATION axis, TurboQuant the
600
- QUANTIZER axis; they compose. Solo-only in v1.
475
+ 2. **Method selection now precedes placement.** `resolveExecution` selects
476
+ speculative or ordinary decoding from the actual request and backend
477
+ capabilities. Configuring a draft no longer forces supported ordinary work
478
+ into the serial executor. The final design has one shared executor; changing
479
+ method must not select a retired execution path. Concurrent speculation is
480
+ still an implementation gap, tracked in batching.md.
481
+
601
482
  3. **Speculative decoding depth** — DFlash/DSpark, native MTP, behind the
602
483
  `DraftSource` seam (`docs/design/speculative-decoding.md`).
603
484
  4. **Per-model graph work from the baseline** — unroll a model's flat DAG,
@@ -833,8 +714,11 @@ Small stateless ports may be implemented by functions or object literals.
833
714
  | Interface | Owns | Must not own |
834
715
  |---|---|---|
835
716
  | `CompletionClient` | Semantic completion request, event stream, terminal result, cancellation | Tensor/state handles, model class selection |
836
- | `PromptPreparer` | Template/tokenizer policy, token IDs, validated media descriptors, stable-prefix boundary | GPU scheduling or request-global model mutation |
837
- | `ExecutionPlanner` | Effective immutable policy, implementation selection, capability negotiation, fallback reasons, plan identity | Starting generation or inventing new feature support |
717
+ | `PromptPreparer` | Coordinate renderer, tokenizer and media preparation; produce token IDs and a stable-prefix boundary | Template syntax, tokenizer algorithms, GPU scheduling or request-global model mutation |
718
+ | `RequestPolicyResolver` | Resolve defaults and explicit overrides once into immutable policy | Executing model work or resampling defaults inside a token loop |
719
+ | `ChatTemplateRenderer` | Render messages, tools and reasoning markers; identify stable rendered boundaries | Tokenizer algorithms, scheduling or cache retention |
720
+ | `Tokenizer` | Encode/decode, token metadata and incremental encoding | Chat-template policy, sampling or scheduling |
721
+ | `ExecutionPlanner` | Effective immutable policy, implementation selection, capability negotiation, declared implementation choices, plan identity | Starting generation or inventing new feature support |
838
722
  | `InferenceEngine` | Session creation, runtime coordination, shutdown | Per-family forward math |
839
723
  | `GenerationSession` | Lifecycle, committed output, result, cancellation and close | Token-at-a-time assumption, KV offsets, concrete method branches |
840
724
  | `InferenceMethod` | AR/speculative/diffusion algorithm and private per-run state | HTTP framing or application sessions |
@@ -1010,14 +894,20 @@ inside forward/decode after binding. Experiment commands construct separate
1010
894
  bindings with explicit snapshots. Diagnostics may observe a running request
1011
895
  but cannot change its numeric policy halfway through execution.
1012
896
 
1013
- Admission is two-stage: validate metadata and reserve a conservative preparation
1014
- budget before compiling grammar or running media towers; then refine the
1015
- reservation using prepared sizes before execution. Count shared resident
1016
- weights once, per-run state, draft resources, media/preparation transients,
1017
- compiled state, pending output, and retained snapshot/write-behind buffers.
1018
- Reservation ownership transfers across stages and releases on every failure.
1019
- Keep prompt/context clamping semantics and aggregate KV limits consistent with
1020
- current policy. A rejected prompt must not first allocate its full GPU payload.
897
+ Configuration follows ownership: scheduling owns concurrency and queue policy;
898
+ methods own sampling, speculation and stopping; backends own kernels and state
899
+ layouts; storage owns cache budgets, tiers and persistence. The application
900
+ composition root binds these interfaces. A single-request caller supplies the
901
+ same method configuration as a batched caller. Consolidate duplicate controls
902
+ as implementations migrate, retaining documented CLI aliases where needed.
903
+
904
+ Default memory estimates are advisory by Josh's instruction. Attempt the
905
+ requested work without predicted-memory refusals or automatic token clamping.
906
+ Explicit user limits and actual layout requirements still apply. Account for
907
+ shared weights once, live state, draft resources, preparation transients and
908
+ retained/write-behind buffers. Release actual resources on failure; a
909
+ conservative estimate does not establish that execution is impossible. This
910
+ supersedes the earlier mandatory reservation-before-preparation design.
1021
911
 
1022
912
  ### 12.6 State, ownership, and persistence
1023
913
 
@@ -1074,11 +964,35 @@ and define read-old/write-new compatibility or explicit invalidation per change.
1074
964
  ### 12.7 Scheduling and inference methods
1075
965
 
1076
966
  Keep policy independent from mechanics. Scheduler chooses eligible work,
1077
- fairness, queue bounds, active-row limits, and resource reservations. A method's
967
+ fairness, queue bounds and active-row limits. A method's
1078
968
  scheduling adapter creates execution groups and implements join/leave/advance
1079
969
  over its own state. Native batch merge and filter stay inside the MLX adapter.
1080
970
  Scheduler must not import KV classes or perform a per-layer tensor loop.
1081
971
 
972
+ The consolidation target is single-request execution as a batch of one through
973
+ these same contracts. Methods publish reusable checkpoints through the shared
974
+ cache interface at completed boundaries. The cache owns prefix selection,
975
+ retention and RAM/SSD placement; scheduling does not choose a storage tier, and
976
+ decode does not inspect the request queue. Model/method codecs describe the
977
+ state needed to restore a prefix, including draft state when applicable.
978
+ Prefill and generated tokens populate the same cache. R17 in the decode-speed
979
+ program owns the current implementation and acceptance work.
980
+
981
+ Full batch feature parity is required, including speculation. The current
982
+ serial exclusions are migration work, not the target architecture. Keep an
983
+ explicit feature matrix in the batching design and retire duplicate request
984
+ lifecycles only after the batched implementation covers every serial feature
985
+ and equals or beats serial performance in matched measurements. B=1 latency
986
+ and feature-specific costs must pass independently of aggregate throughput.
987
+ The optimized kernels already built remain behind the backend interface;
988
+ scheduling policy does not choose or reimplement their arithmetic. Continue
989
+ building faster specializations, fusing operations, removing work and avoiding
990
+ materialization. Interfaces must permit complete fused work units rather than
991
+ force one call or intermediate tensor per logical operation. Measure kernel
992
+ changes separately from composition refactors so their effects are attributable. Interface
993
+ extraction must not force tensor copies, materialization, synchronization or
994
+ additional dispatches; compare B=1 latency and B>1 throughput at fixed settings.
995
+
1082
996
  An advancement is a bounded safe unit, not necessarily a token: one prefill
1083
997
  chunk, a pipelined decode step, one verify round, or one denoising iteration.
1084
998
  Budgets describe admitted resources and work hints separately. A synchronous
@@ -1876,3 +1790,91 @@ existing placement and numerical defaults. Their independent experiment programs
1876
1790
  still own promotion/deletion decisions. Quiet performance, exact target quants,
1877
1791
  unavailable oracle/drafter fixtures and the second machine remain acceptance
1878
1792
  work, not missing interface code. No frontier advance is claimed by this refactor.
1793
+
1794
+ ### 12.14 Request flow and interface ownership
1795
+
1796
+ This is the target responsibility map for Phase 6/18. A request carries one
1797
+ resolved policy: measured defaults plus explicit user overrides. Each component
1798
+ consumes its own part of that policy. The table below distinguishes existing
1799
+ contracts from implementation and migration work still in progress.
1800
+
1801
+ ```mermaid
1802
+ flowchart TB
1803
+ client["Client: chat, Pi, API or library"]
1804
+ server["Server adapter<br/>receive requests and send responses"]
1805
+ policy["Request policy interface<br/>resolve defaults and user overrides once"]
1806
+ template["Chat template interface<br/>messages and tools → rendered prompt"]
1807
+ tokenizer["Tokenizer interface<br/>rendered prompt → token IDs"]
1808
+ session["InferenceEngine / GenerationSession<br/>own request lifecycle and cancellation"]
1809
+ scheduler["Scheduler / ExecutionGroup<br/>choose when ready work runs"]
1810
+ method["InferenceMethod<br/>ordinary decode or MTP proposal / verification"]
1811
+ graph["Model graph interface<br/>prefill, forward passes and specialized kernels"]
1812
+ sampler["SamplingSession<br/>apply settings and grammar; select tokens"]
1813
+ output["Response interface<br/>decode committed tokens; emit text and tool events"]
1814
+ state["Active state interface<br/>target KV, recurrent state, draft state and positions"]
1815
+ cache["PrefixCache interface<br/>restore and retain aligned token / state checkpoints"]
1816
+ ram["RAM cache<br/>retain reusable checkpoints"]
1817
+ persistence["Persistence interface<br/>queue background writes and restore from SSD"]
1818
+
1819
+ client --> server --> policy --> template --> tokenizer --> session
1820
+ session -->|ready work| scheduler
1821
+ scheduler -->|admit and advance a batch of 1 or more| method
1822
+ method -->|inputs and state| graph
1823
+ graph -->|device logits| sampler
1824
+ sampler -->|selected tokens or proposals| method
1825
+ method -->|committed tokens only| output
1826
+ output -->|response events| server
1827
+ method <-->|borrow, update, snapshot, rollback| state
1828
+ session <-->|restore / publish prefill checkpoint| cache
1829
+ method -.->|publish generated checkpoint: still open| cache
1830
+ cache <-->|fast reuse| ram
1831
+ ram <-->|spill and restore when needed| persistence
1832
+ ```
1833
+
1834
+ The scheduler chooses **when** work runs and which requests are active. The
1835
+ inference method chooses **what numerical work** advances them. A batch of one
1836
+ uses the same method and state contracts as a larger batch. The final design
1837
+ has one shared executor and no separate serial executor to fall back to.
1838
+
1839
+ The model graph returns logits to the sampler. The sampler applies temperature,
1840
+ penalties, grammar, sampling filters and RNG policy, and returns selected tokens
1841
+ with requested logprobs. MTP keeps proposals private until verification commits
1842
+ them. The response formatter sees committed tokens only.
1843
+
1844
+ Live KV is working state for the current computation. A reusable checkpoint
1845
+ contains token IDs plus the corresponding target, recurrent and optional draft
1846
+ state. Both prefill and generated output must feed the same cache interface.
1847
+ Only an exact, rendered-stable token boundary can be published for state that
1848
+ cannot be trimmed. RAM retention, eviction, asynchronous SSD writes and restart
1849
+ restore belong behind that interface. Scheduling does not wait for an ordinary
1850
+ SSD spill; an explicit durability flush can wait for persistence.
1851
+
1852
+ | Piece | Current code or contract | Remaining work |
1853
+ |---|---|---|
1854
+ | Server / response adapters | `CompletionEngine`, `CompletionSink`, `GenerationOutput` | Preserve the same semantics through the final shared-executor cutover. |
1855
+ | Request policy | `RequestPlan`, request preparation and execution planning | Consolidate configuration into one resolved policy; choose measured defaults for typical multi-turn use and retain user overrides. |
1856
+ | Chat template | `ChatTemplate.render` | Expose the renderer as its own narrow port instead of requiring a concrete serving context. Record rendered-stable boundaries for generated checkpoint alignment. |
1857
+ | Tokenizer | `LoadedTokenizer` | Reuse the same encode/decode contract across preparation, caching and output. Keep incremental encoding inside this component. |
1858
+ | Session / scheduling | `InferenceEngine`, `GenerationSession`, `ExecutionGroup`, `SchedulingClock` | Close all feature and performance gaps before deleting the remaining serial executor. |
1859
+ | Inference method | `InferenceMethod`; shared MTP method in the isolated candidate | Complete every shared-method composition and acceptance gate. Draft depth belongs here, independently of queue policy. |
1860
+ | Numerical graph / kernels | `AutoregressiveGraph`, `GraphFactory`, backend bindings | Continue hardware-specific fusion and kernel optimization behind these contracts. |
1861
+ | Sampler | `SamplingSession` | Retain one set of sampling/history/logprob semantics for ordinary and speculative execution. |
1862
+ | Active state / codec | `Cache`, `KvAttentionState` / `KvAttentionView`, row layouts, `SpeculativeTransaction`, checkpoint attachments | Appending and attending are separate operations. One captured view can serve multiple model layers while retaining pre-write positions and tensor ownership. Delayed rotating affine and broader model/layout composition remain open. |
1863
+ | Reusable cache | `PrefixCache.take/put`, shared `PromptCache` | Generated-output checkpoints and next-request token alignment remain open. Prefill reuse is already implemented. |
1864
+ | Persistence | `ColdTier`, `SpillSink`, `SsdCacheStore`, `SsdDurabilityCoordinator` | Complete combined M4 pressure/restart acceptance. RAM and SSD remain interchangeable retention tiers behind cache operations. |
1865
+
1866
+ An interface boundary does not require copying tensors, downloading logits,
1867
+ or adding an RPC or synchronization per token. Device handles can stay lazy;
1868
+ the backend may fuse compatible graph and sampling operations while preserving
1869
+ policy, ownership and observable results. Each component can improve its own
1870
+ implementation without adding model, cache or sampling decisions to scheduling.
1871
+
1872
+ Contract sources: [generation](../../src/contracts/generation.ts),
1873
+ [scheduling](../../src/contracts/scheduling.ts),
1874
+ [sampling](../../src/contracts/sampling.ts),
1875
+ [prefix cache](../../src/contracts/prefix-cache.ts),
1876
+ [graph](../../src/inference/graph.ts),
1877
+ [tokenizer](../../src/tokenizer.ts),
1878
+ [cache tiers](../../src/prompt-cache.ts).
1879
+ Performance evidence and default selection live in
1880
+ [benchmarks.md](../reference/benchmarks.md); open acceptance remains in Phase 6/18.
@@ -6,6 +6,22 @@ the evolving UI/UX/DX north star.
6
6
 
7
7
  ## Product north star
8
8
 
9
+ Josh's guiding principle for the entire project is "The absolute BEST local
10
+ AI experience on a mac". This governs installation, application development,
11
+ everyday use and inference performance.
12
+
13
+ Offer multiple simple installation methods without asking users to manage
14
+ a Python virtual environment. Let developers import local AI into their
15
+ applications without managing a separate inference sidecar. Make JavaScript
16
+ and TypeScript first-class ways to build with local AI, without requiring
17
+ Python. Make more capable models practical on the same Mac and reduce the
18
+ time users wait for successful results.
19
+
20
+ Every workstream must serve that experience. Evaluate engineering choices
21
+ by the setup, integration and operating burden they remove, as well as the
22
+ quality, reliability, speed and hardware efficiency they deliver. Research
23
+ and publication can support the project; user benefit determines priority.
24
+
9
25
  Make the answer to "what is the best way to use local AI on my Mac?"
10
26
  boringly simple:
11
27
 
@@ -0,0 +1,45 @@
1
+ # mlx-bun v0.4.0
2
+
3
+ This release brings shared batched decode, generated-history cache reuse and
4
+ resumable generation together through separate scheduling, inference, sampling
5
+ and persistence interfaces.
6
+
7
+ - Shared ordinary and speculative execution supports Qwen MTP, prompt lookup,
8
+ standalone, assistant, DeepSpec and DSpark/DFlash providers on qualified
9
+ model/request combinations. Single requests run at B=1 through the scheduler.
10
+ - Full and rotating affine/TurboQuant cache layouts compose with delayed and
11
+ per-layer precision, speculative rollback and generated target/draft state.
12
+ - Completed decode populates the shared RAM/SSD cache so later turns can reuse
13
+ generated history. SSD persistence is queued and runs during idle ownership.
14
+ - Ordinary interrupted-generation resume now works through shared execution,
15
+ including compatible adapters and quantized caches. Restored sampler history,
16
+ pending tokens and usage accounting preserve the continuation contract.
17
+ - Ordinary paged storage and adapter-qualified prompt lookup use the same state
18
+ and execution interfaces. GLM native MTP gains shared cache transactions and
19
+ row state; final real-artifact testing remains deferred.
20
+ - Measured Trellis v13 is selected by default, and eligible Qwen KV4 speculation
21
+ is enabled. Workload-dependent lookup and other experimental settings remain
22
+ opt-in. Memory fit estimates are advisory unless users choose explicit limits.
23
+ - Benchmark reporting retains request/source identities, paired comparisons,
24
+ failures and configuration differences. CI and the release build use Bun 1.4.2.
25
+
26
+ Serving keeps its existing `--batch 8` default. A lone eligible request starts
27
+ immediately at B=1; the cap does not wait for eight requests. `--batch 1` retains
28
+ the serial executor, and unsupported compositions can still use it. Removing
29
+ serial and completing every feature combination are follow-up work.
30
+
31
+ Checkpoint capture is eventually persistent: queued snapshots can remain in RAM
32
+ during uninterrupted traffic. A successful explicit flush is the durability
33
+ boundary. Media, fill/grammar-jump and direct paged-attention work remain tracked.
34
+ No DSpark/DFlash checkpoint has been trained for this campaign; execution fixtures
35
+ do not establish trained-drafter performance.
36
+
37
+ Measured performance and its workload limits are in the
38
+ [benchmark record](https://github.com/joshuarossi/mlx-bun/blob/v0.4.0/docs/reference/benchmarks.md).
39
+ This release does not claim a matched full-task Kanban speedup or completion of
40
+ the entire optimization program. Supported settings are documented in the
41
+ [server configuration reference](https://github.com/joshuarossi/mlx-bun/blob/v0.4.0/docs/reference/server-config.md).
42
+
43
+ Requires Apple Silicon and macOS 14 or later. npm installs require Bun 1.4.0 or
44
+ later; Bun 1.4.2 is the tested version. The existing native runtime pack 0.4.0
45
+ provides MLX 0.32.2 and its matching C API.