mlx-bun 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -21
- package/THIRD_PARTY_LICENSES.md +1 -0
- package/docs/archive/mlx-bun-lab-report.html +275 -0
- package/docs/archive/wwdc-mlx-bun.html +848 -0
- package/docs/design/audio-input-plan.md +356 -0
- package/docs/design/batching-perf-path.md +255 -0
- package/docs/design/batching-v2-plan.md +23 -9
- package/docs/design/bucketing-stage.md +2 -1
- package/docs/design/compat-cli-surface-design.md +4 -3
- package/docs/design/decode-speed-program.md +172 -0
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dspark-serving-program.md +371 -0
- package/docs/design/dspark-speculative-decoding.md +167 -5
- package/docs/design/faithful-l1-consolidation.md +310 -0
- package/docs/design/generic-model-support.md +8 -5
- package/docs/design/grammar-spec-batching-integration.md +301 -0
- package/docs/design/minicpm5-decode-megakernel.md +16 -11
- package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
- package/docs/design/omlx-adoption-map.md +98 -0
- package/docs/design/orpo-training.md +34 -30
- package/docs/design/paged-kv-cache.md +214 -0
- package/docs/design/parallel-slots.md +82 -49
- package/docs/design/parity-tier-dag.md +43 -33
- package/docs/design/repo-cleanup-plan.md +189 -0
- package/docs/design/runtime-isolation.md +19 -3
- package/docs/design/segmented-backward-training.md +18 -2
- package/docs/design/ssd-kv-cold-tier.md +288 -0
- package/docs/design/structured-output.md +484 -0
- package/docs/design/the-dreaming-master-plan.md +2 -3
- package/docs/design/turboquant-kv.md +235 -0
- package/docs/design/unified-engine-frontier-plan.md +774 -0
- package/docs/design/web-chat-beat-matrix.md +315 -0
- package/docs/design/web-chat-redesign.md +1112 -0
- package/docs/investigations/decode-roofline-lookagain.md +92 -6
- package/docs/investigations/dspark-handoff.md +182 -16
- package/docs/investigations/kernel-perf-review-2026-07.md +105 -26
- package/docs/investigations/lab-build-journal.md +2 -2
- package/docs/investigations/openwiki-evaluation.md +249 -0
- package/docs/planning/IDEAS.md +14 -0
- package/docs/planning/PRODUCT_ROADMAP.md +2 -3
- package/docs/planning/chat-ui-vision-memory-first.md +199 -0
- package/docs/planning/chat-ui-vision.md +50 -0
- package/docs/planning/memory-docs-and-dag-plan.md +15 -6
- package/docs/planning/release-notes-v0.0.10.md +57 -0
- package/docs/planning/release-notes-v0.0.11.md +239 -0
- package/docs/reference/cli.md +35 -16
- package/docs/reference/distribution.md +13 -6
- package/docs/reference/embedding.md +35 -14
- package/docs/reference/features-matrix.md +97 -0
- package/docs/reference/library-api.md +30 -9
- package/docs/reference/memory.md +26 -6
- package/docs/reference/models.md +11 -1
- package/docs/reference/orpo-quickstart.md +3 -4
- package/docs/reference/server-api.md +634 -22
- package/docs/reference/server-config.md +251 -119
- package/docs/reference/training.md +20 -16
- package/package.json +5 -3
- package/src/anthropic.ts +13 -0
- package/src/audio/conformer.ts +647 -0
- package/src/audio/decode.ts +133 -0
- package/src/audio/features.ts +202 -0
- package/src/audio/transcode.ts +67 -0
- package/src/cli.ts +402 -107
- package/src/config.ts +49 -2
- package/src/download.ts +40 -0
- package/src/eval/README.md +7 -6
- package/src/eval/kl.ts +59 -3
- package/src/eval/quality-db.ts +2 -2
- package/src/eval/runner.ts +3 -3
- package/src/expert-offload.ts +15 -2
- package/src/flags.ts +13 -0
- package/src/generate.ts +330 -16
- package/src/grammar.ts +585 -0
- package/src/hub-rest.ts +242 -0
- package/src/kv-store.ts +618 -122
- package/src/lora.ts +8 -0
- package/src/md-modules.d.ts +42 -0
- package/src/media-fetch.ts +284 -0
- package/src/memory/rest.ts +245 -0
- package/src/memory/vault.ts +74 -0
- package/src/mlx/array.ts +127 -19
- package/src/mlx/autograd.ts +2 -2
- package/src/mlx/ffi.ts +27 -3
- package/src/mlx/metal-kernel.ts +7 -4
- package/src/mlx/ops.ts +179 -23
- package/src/mlx/turboquant-ops.ts +831 -0
- package/src/mlx/turboquant-tables.ts +237 -0
- package/src/model/batched-mask.ts +62 -1
- package/src/model/batched-quant.ts +206 -0
- package/src/model/batched-rotating-quant.ts +346 -0
- package/src/model/batched-rotating.ts +46 -1
- package/src/model/compiled-decode.ts +4 -15
- package/src/model/factory.ts +10 -4
- package/src/model/gemma4-base.ts +391 -34
- package/src/model/gemma4.ts +122 -33
- package/src/model/generated/gemma4-12b.ts +9 -13
- package/src/model/generated/gemma4-26b.ts +10 -5
- package/src/model/generated/gemma4-e4b.ts +14 -9
- package/src/model/minicpm5.ts +58 -102
- package/src/model/paged-kv.ts +253 -0
- package/src/model/qwen3-delta.ts +123 -15
- package/src/model/qwen3-moe.ts +404 -0
- package/src/model/qwen3.ts +9 -8
- package/src/model/qwen3_5.ts +99 -25
- package/src/model/support.ts +12 -1
- package/src/model/universal/dense.ts +40 -5
- package/src/model/universal/rope.ts +36 -21
- package/src/pi-web.ts +1299 -82
- package/src/preflight.ts +27 -8
- package/src/prompt-cache.ts +338 -32
- package/src/quantize/quantizer.ts +16 -1
- package/src/registry.ts +61 -3
- package/src/serve/batch-scheduler.ts +1001 -76
- package/src/serve/generation-gateway.ts +292 -30
- package/src/serve/isolate.ts +417 -0
- package/src/serve/lane-registry.ts +44 -0
- package/src/serve/session-search.ts +326 -0
- package/src/server.ts +1460 -105
- package/src/spec/assistant-source.ts +150 -0
- package/src/spec/deepspec-source.ts +143 -0
- package/src/spec/dflash-source.ts +124 -0
- package/src/spec/dspark/ab-stats.ts +136 -0
- package/src/spec/dspark/calibration.ts +112 -0
- package/src/spec/dspark/deepspec-module.ts +801 -0
- package/src/spec/dspark/generate-dflash.ts +72 -20
- package/src/spec/dspark/loader.ts +23 -0
- package/src/spec/dspark/module-dflash.ts +278 -36
- package/src/spec/dspark/quantize-drafter.ts +91 -0
- package/src/spec/generate.ts +59 -25
- package/src/spec/ngram-source.ts +132 -0
- package/src/spec/serve-loop.ts +536 -0
- package/src/spec/source.ts +93 -0
- package/src/spec/two-model.ts +171 -0
- package/src/ssd-cache.ts +285 -0
- package/src/tokenizer.ts +288 -2
- package/src/tool-approvals.ts +131 -0
- package/src/tool-call.ts +408 -20
- package/src/train/flash-cce.ts +29 -26
- package/src/train/job.ts +0 -2
- package/src/train/loss.ts +53 -11
- package/src/train/prefix-shared.ts +30 -7
- package/src/train/segmented.ts +150 -46
- package/src/train/trainer.ts +22 -15
- package/src/vision/prompt.ts +217 -48
- package/src/web/app.html +1184 -1789
- package/src/web/app.js +5667 -0
- package/src/web/icon.svg +13 -0
- package/src/web/manifest.webmanifest +24 -0
- package/src/web/src/adapters-panel.ts +288 -0
- package/src/web/src/api.ts +57 -0
- package/src/web/src/assistant.ts +485 -0
- package/src/web/src/chat.ts +1050 -0
- package/src/web/src/composer.ts +959 -0
- package/src/web/src/dataset.ts +132 -0
- package/src/web/src/finetune.ts +267 -0
- package/src/web/src/hub.ts +428 -0
- package/src/web/src/main.ts +98 -0
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +572 -0
- package/src/web/src/model-picker.ts +159 -0
- package/src/web/src/palette.ts +346 -0
- package/src/web/src/protocol.ts +62 -0
- package/src/web/src/quantize.ts +167 -0
- package/src/web/src/rag.ts +274 -0
- package/src/web/src/sessions.ts +469 -0
- package/src/web/src/shell.ts +804 -0
- package/src/web/src/status.ts +252 -0
- package/src/web/src/ui-catalog.ts +92 -0
- package/src/web/sw.js +71 -0
- package/src/web/vendor/README.md +61 -0
- package/src/web/vendor/hljs-LICENSE +29 -0
- package/src/web/vendor/hljs-theme.css +59 -0
- package/src/web/vendor/hljs.js +26 -0
- package/src/model/fused-decode-kernel.ts +0 -312
- package/src/model/fused-geglu-kernel.ts +0 -198
- package/src/model/fused-mlp-kernel.ts +0 -169
- package/src/model/fused-swiglu-kernel.ts +0 -240
- package/src/model/steel-linear-kernel.ts +0 -96
- package/src/status-page.html +0 -497
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# mlx-bun v0.0.10 — batching parity, SSD KV cache, sharper serving
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
**Concurrent serving now matches the fastest MLX batching servers.** With
|
|
6
|
+
`--batch 4`, aggregate throughput at 4 parallel requests is at parity with
|
|
7
|
+
(or ahead of) oMLX on every shared model we benchmarked on an M1 Max —
|
|
8
|
+
MiniCPM5-1B 349 vs 339 tok/s, gemma-4-e4b within 3%, Qwen3.5-4B within 1% —
|
|
9
|
+
with 2–3× better time-to-first-token under load, and per-token SSE
|
|
10
|
+
streaming (no burst batching of your tokens).
|
|
11
|
+
|
|
12
|
+
- **Qwen3.5 joins the batch lane.** Hybrid gated-DeltaNet (SSM) caches grew
|
|
13
|
+
the dynamic-batching ops; the batched decode path is verified
|
|
14
|
+
token-for-token identical against mlx-lm's B=2 batched oracle.
|
|
15
|
+
- **Sampler features batch too.** Repetition/presence/frequency penalties,
|
|
16
|
+
`logit_bias`, `min_p`, and XTC now run per-row inside the batch instead
|
|
17
|
+
of forcing requests onto the serial path. (Qwen3.5 ships a default
|
|
18
|
+
repetition penalty — before this fix it silently serialized every
|
|
19
|
+
request under `--batch N`.)
|
|
20
|
+
|
|
21
|
+
**SSD cold tier for the prompt cache** (`--ssd-cache <dir>`). Long-context
|
|
22
|
+
prefix KV now spills to disk on eviction, is snapshotted after requests
|
|
23
|
+
settle, and **survives server restarts**: re-attaching a 13.7k-token
|
|
24
|
+
coding-agent conversation went from a 12.1 s full re-prefill to a 0.24 s
|
|
25
|
+
zero-copy mmap restore in our measurements — with 0% steady-state decode
|
|
26
|
+
overhead (nothing runs on the token loop). Entries are keyed by model
|
|
27
|
+
fingerprint + KV scheme + tokenizer hash + adapter namespace; corrupt or
|
|
28
|
+
incompatible files self-quarantine; the tier is always safe to delete.
|
|
29
|
+
Companion flags: `--ssd-cache-max <GB>` (default 32), `--ssd-cache-verify`.
|
|
30
|
+
|
|
31
|
+
## Fixes
|
|
32
|
+
|
|
33
|
+
- `serve`/`benchmark` now honor `--model <path-or-query>` as an explicit
|
|
34
|
+
override (it was previously accepted but ignored — auto-pick could
|
|
35
|
+
silently load a different model). A directory containing `config.json`
|
|
36
|
+
loads directly from that path.
|
|
37
|
+
- The server stays responsive during long serial generations: `/stats`,
|
|
38
|
+
`/health`, and new connections previously stalled until the generation
|
|
39
|
+
finished (measured 2.5 s on a 512-token run); they now answer in tens of
|
|
40
|
+
milliseconds, with no measurable decode cost.
|
|
41
|
+
|
|
42
|
+
## Internals
|
|
43
|
+
|
|
44
|
+
- KV persistence format v2: all cache kinds round-trip (bf16, per-layer
|
|
45
|
+
quantized, sliding-window, quantized sliding-window, SSM), with
|
|
46
|
+
integrity hashes, invalidation metadata, and atomic writes.
|
|
47
|
+
- Golden `.bin` parity fixtures are no longer tracked in git (they are
|
|
48
|
+
machine-specific and regenerable); JSON manifests remain.
|
|
49
|
+
- New design docs: `docs/design/omlx-adoption-map.md` (feature adoption
|
|
50
|
+
scoreboard), `docs/design/batching-perf-path.md`,
|
|
51
|
+
`docs/design/ssd-kv-cold-tier.md`.
|
|
52
|
+
|
|
53
|
+
Full details and measured numbers: STATUS.md and the design docs above.
|
|
54
|
+
Credit where due: the SSD-tier and batching comparisons were driven by
|
|
55
|
+
studying [oMLX](https://github.com/jundot/omlx) (Apache 2.0) — a strong
|
|
56
|
+
project whose ideas we benchmarked and adapted where they made mlx-bun
|
|
57
|
+
faster.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# v0.0.11 release notes
|
|
2
|
+
|
|
3
|
+
Everything since v0.0.10. The big themes: mlx-bun grows a second modality
|
|
4
|
+
(audio), a constrained-decoding stack (structured output / grammar),
|
|
5
|
+
speculative decoding, a novel KV-cache quantization scheme (TurboQuant),
|
|
6
|
+
completed dynamic batching, crash-isolated serving, and a ground-up
|
|
7
|
+
rewrite of the web chat UI. The decode-default story also resets: after
|
|
8
|
+
benchmarking every custom performance kernel in a paired A/B against the
|
|
9
|
+
bit-exact baseline and finding none of them won, they were deleted —
|
|
10
|
+
naked `mlx-bun serve` now runs the faithful, mlx-lm-parity kernel set by
|
|
11
|
+
default.
|
|
12
|
+
|
|
13
|
+
## Audio input (Gemma e4b)
|
|
14
|
+
|
|
15
|
+
- OpenAI-style `input_audio` parts are served end-to-end: WAV/AAC
|
|
16
|
+
transcode, a USM mel-spectrogram extractor, and a Conformer tower
|
|
17
|
+
ported bit-exact (rel-RMSE 2.4e-8) against the oracle.
|
|
18
|
+
- Build-out ran phase-by-phase (A0–A5): a `bun:ffi` conv2d binding for
|
|
19
|
+
the front-end, the multimodal prompt builder wired to the LM, and
|
|
20
|
+
capability discovery for audio surfaced consistently across
|
|
21
|
+
`/v1/models`, `/library`, and the pi websocket handshake — mirroring
|
|
22
|
+
how vision capability is advertised.
|
|
23
|
+
|
|
24
|
+
## Structured output / grammar-constrained generation
|
|
25
|
+
|
|
26
|
+
- `response_format` (`json_object`, `json_schema`, `text`) and
|
|
27
|
+
`guided_grammar`/`guided_regex`/`guided_choice`/`structured_outputs`
|
|
28
|
+
are wired into both `/v1/chat/completions` and `/v1/completions`, built
|
|
29
|
+
on `xgrammar`: a `GrammarController` masks logits inside the decode
|
|
30
|
+
loop, with a per-`TokenizerInfo` compiler cache and a `MLX_BUN_GRAMMAR=0`
|
|
31
|
+
kill switch.
|
|
32
|
+
- The batched lane got its own per-row matcher path so grammar-constrained
|
|
33
|
+
requests don't force a request onto the serial lane, and grammar
|
|
34
|
+
composes with `--draft-model` (the verify walk respects the mask).
|
|
35
|
+
- Two defects found and fixed during the build: the oMLX-parity
|
|
36
|
+
JSON-system-prompt degrade path (used when a client can't do function
|
|
37
|
+
calling) had gone unreachable behind a dropped hint, and the WASM
|
|
38
|
+
grammar controllers were leaking on every early-400 reject path
|
|
39
|
+
(SSRF-blocked media, vision/audio validation failures, bad
|
|
40
|
+
`logit_bias`, etc.) — both fixed, with disposal now covering all paths.
|
|
41
|
+
|
|
42
|
+
## Speculative decoding
|
|
43
|
+
|
|
44
|
+
- `--draft-model` lands in serve: a two-model, serial-lane speculative
|
|
45
|
+
decoding path verified token-for-token against mlx-lm's own
|
|
46
|
+
`--draft-model` oracle (`DraftSource` seam, rewind rule, batched-verify
|
|
47
|
+
lm-head).
|
|
48
|
+
- An ngram / jump-forward draft kind ships alongside the model-based
|
|
49
|
+
drafter for cases with no small model available.
|
|
50
|
+
- A DeepSpec (DSpark) drafter-quant research track is underway
|
|
51
|
+
(Phase 1 landed) targeting the larger models; per
|
|
52
|
+
`benchmarks/RESULTS.md`, the first live pair (12B target +
|
|
53
|
+
bf16 DeepSpec drafter, 26–33% acceptance) measured net **slower**
|
|
54
|
+
than serial on a loaded box — spec decoding stays **off by default**
|
|
55
|
+
pending a quantized-drafter pair that clears the 1.3× bar.
|
|
56
|
+
|
|
57
|
+
## TurboQuant KV-cache quantization
|
|
58
|
+
|
|
59
|
+
- `--kv-quant turbo[:kXvY]` adds a rotation-based per-group KV
|
|
60
|
+
quantization scheme (sign-FWHT + Lloyd-Max for values), proven
|
|
61
|
+
bit-exact against the vendored vllm-metal reference
|
|
62
|
+
(`goldens/turboquant.json`).
|
|
63
|
+
- Per `benchmarks/RESULTS.md`, the k8v3 default holds a lower KL-vs-bf16
|
|
64
|
+
than affine uniform kv4 while compressing KV **2.56×** (measured on
|
|
65
|
+
MiniCPM5-1B, paired quality/KL harness). It's a memory/context lever,
|
|
66
|
+
not a speed lever — v1 dequant-on-fetch is slower per decode step at
|
|
67
|
+
long context, so it stays **opt-in, not default**, per the project's
|
|
68
|
+
"levers must earn defaults" rule.
|
|
69
|
+
|
|
70
|
+
## Batching: Phase D completed
|
|
71
|
+
|
|
72
|
+
- Aggregate `--kv-budget <GB>` admission with FIFO queuing, vectorized
|
|
73
|
+
homogeneous sampling (penalties/`logit_bias`/`min_p`/XTC all run
|
|
74
|
+
per-row in the batch), and extend-join so a request can join an
|
|
75
|
+
in-flight batch mid-flight instead of waiting for a full drain.
|
|
76
|
+
- Per-row RoPE lands for Tier-0 universal architectures, lifting the
|
|
77
|
+
batching gate for those models (e.g. Llama).
|
|
78
|
+
- Batched mixed-precision quantized KV — the first stack to compose
|
|
79
|
+
per-row KV quantization with batching.
|
|
80
|
+
- `--batch` default changes from 1 to 8 (see Breaking changes below).
|
|
81
|
+
|
|
82
|
+
## Runtime isolation
|
|
83
|
+
|
|
84
|
+
- `--isolate` runs the engine as a crash-isolated child process behind a
|
|
85
|
+
reactor parent, restarting with backoff on a crash — the product rule
|
|
86
|
+
is **"the AI may crash, the UI never may"** (`docs/design/runtime-isolation.md`).
|
|
87
|
+
- `--model-pool` extends this to a child-per-model pool: LRU residency,
|
|
88
|
+
spawn-overlap switching (no dead time swapping models), and
|
|
89
|
+
`POST /admin/drain` for graceful eviction.
|
|
90
|
+
|
|
91
|
+
## Optional paged KV cache
|
|
92
|
+
|
|
93
|
+
- `--paged-kv` adds a vLLM-style block-table paged KV cache
|
|
94
|
+
(`PagedKVCache` + `BlockPool`) feeding the unchanged stock SDPA via
|
|
95
|
+
block gather. Default off; v1 scope is serial `--batch 1`, the
|
|
96
|
+
Gemma4 family, bf16 — it refuses to silently combine with
|
|
97
|
+
`--batch N>1`, `--kv-quant`, or `--draft-model`.
|
|
98
|
+
|
|
99
|
+
## Prompt cache overhaul
|
|
100
|
+
|
|
101
|
+
- Prefix-sharing v1: `take()` now returns non-consuming, ref-counted
|
|
102
|
+
clones instead of consuming/trimming the donor entry, so multiple
|
|
103
|
+
agents or sessions can reuse one prefill without cannibalizing each
|
|
104
|
+
other's cache.
|
|
105
|
+
- The SSD cold tier is promoted from an add-on to a first-class property
|
|
106
|
+
of the cache store, with `--ssd-demote-idle <seconds>` (default 300,
|
|
107
|
+
requires `--ssd-cache`) freeing GPU memory by demoting idle entries;
|
|
108
|
+
`/stats` gains `ssd_cache.demotions`.
|
|
109
|
+
- A stable-boundary snapshot fix restores prompt-cache hits for
|
|
110
|
+
multi-turn agent traffic beyond the sliding window — this was
|
|
111
|
+
previously a silent cache miss on turn 2+ of long conversations.
|
|
112
|
+
- The SSD write-behind flush is now idle-gated, so background disk
|
|
113
|
+
writes stop contending with decode at long context; the earlier A7
|
|
114
|
+
RSS regression on the ssd-cache path closed with zero-copy write views
|
|
115
|
+
and a streamed copy-restore.
|
|
116
|
+
|
|
117
|
+
## Web chat UI: ground-up redesign (Phases 0–3)
|
|
118
|
+
|
|
119
|
+
- Streaming render rewritten to be block-memoized (no more O(n²)
|
|
120
|
+
re-parse of the growing message), with a typed module split.
|
|
121
|
+
- New surfaces: a memory panel, adapter routing, a system-prompt editor,
|
|
122
|
+
an approval gate, client-side RAG over chat history (BM25, no server
|
|
123
|
+
round-trip), a Model Hub, Canvas, per-message sampling controls,
|
|
124
|
+
self-healing tool calls, a command palette, full-text search, and
|
|
125
|
+
chat export.
|
|
126
|
+
- An app-aware assistant (v1) that can see and navigate its own UI —
|
|
127
|
+
spotlight/selector resolution now treats a model-invented or malformed
|
|
128
|
+
selector as a miss instead of throwing inside the websocket handler.
|
|
129
|
+
- PWA support, plus a round of visual-QA fixes: the phone nav band,
|
|
130
|
+
light-theme code legibility, a composer placeholder regression, memory
|
|
131
|
+
panel markdown chrome, the theme-toggle Auto button, a clipped status
|
|
132
|
+
pill, and tab flex-shrink pinned to zero on phones.
|
|
133
|
+
|
|
134
|
+
## Security: media-fetch SSRF hardening
|
|
135
|
+
|
|
136
|
+
- `image_url`/`audio_url` fetches now refuse private/loopback/link-local/
|
|
137
|
+
CGNAT destinations and non-`http(s)` schemes by default, re-validate
|
|
138
|
+
through up to 5 redirect hops, enforce a 10s wall-clock timeout, and
|
|
139
|
+
cap bodies at 64MB.
|
|
140
|
+
- LAN use needs the new escape hatch: `--allow-private-media` /
|
|
141
|
+
`MLX_BUN_ALLOW_PRIVATE_MEDIA=1`.
|
|
142
|
+
|
|
143
|
+
## Fixed
|
|
144
|
+
|
|
145
|
+
- 12B `/v1/completions` probe parity: adopted the oracle's step-0
|
|
146
|
+
prefill convention (drain to len−1, step 0 from an `L=1` forward of the
|
|
147
|
+
last prompt token). Per `benchmarks/RESULTS.md`, live HTTP probes are
|
|
148
|
+
now byte-identical to `mlx_lm.server`/optiq serve for MiniCPM5-1B,
|
|
149
|
+
gemma-4-e4b, and gemma-4-12B, on completion and chat probes, in both
|
|
150
|
+
the `--batch 8` default lane and `--batch 1`.
|
|
151
|
+
- BPE detokenization now mirrors mlx-lm's bare-space hold-back and
|
|
152
|
+
never-finalize semantics exactly.
|
|
153
|
+
- Cache invariant enforced on every tier; an FFI destructor deadlock
|
|
154
|
+
fixed; write-behind made non-blocking; per-row batch cache extraction
|
|
155
|
+
fixed.
|
|
156
|
+
- `generate()` now yields a macrotask between prefill chunks, keeping the
|
|
157
|
+
isolation path (and the server generally) responsive during long
|
|
158
|
+
prefills.
|
|
159
|
+
- Benchmark harness: the `--batch 1` serial control arm now runs by
|
|
160
|
+
default (it had been skippable, which undermined validating the new
|
|
161
|
+
batch-8 default); `--engine` is additive instead of mutually exclusive
|
|
162
|
+
with the serve pass; the batch-path test now asserts cumulative
|
|
163
|
+
`/stats` `submitted_rows` instead of a stale `cachedTokens=0` premise.
|
|
164
|
+
- `any_whitespace` grammar knob landed; batch-grammar gates re-anchored
|
|
165
|
+
to compact separators.
|
|
166
|
+
|
|
167
|
+
## Infra / benchmark harness redesign
|
|
168
|
+
|
|
169
|
+
- `benchmark.sh` + `scripts/bench-modes.ts` now spawn real CLI paths
|
|
170
|
+
instead of bench-local server wrappers, folding bit-parity and
|
|
171
|
+
peak-memory checks into the same serve cells (one pass instead of
|
|
172
|
+
several). New columns: cold-start and restart-survival; new workload:
|
|
173
|
+
`xl` (~4k prefix); a feature-matrix benchmark mode.
|
|
174
|
+
- Repo cleanup: `lab/` consolidation, a binary-tracking hygiene gate, and
|
|
175
|
+
a git history rewrite that took the repo from 182MB to 20MB, removing
|
|
176
|
+
the last multi-MB binaries from the index.
|
|
177
|
+
- New design docs: `unified-engine-frontier-plan`, `dspark-serving-program`
|
|
178
|
+
(the DeepSpec/DSpark spec-decode research program), `structured-output`,
|
|
179
|
+
`web-chat-redesign` (+ app-aware-assistant, superset-doctrine, and
|
|
180
|
+
visual-QA-bar notes), `paged-kv-cache`, `batching-perf-path` /
|
|
181
|
+
`batching-v2-plan`, `runtime-isolation`.
|
|
182
|
+
- Doc drift swept: env-lever table entries (`MLX_BUN_DSPARK_MINCONF`,
|
|
183
|
+
`MLX_BUN_BATCH_NO_PIPELINE`, `MLX_BUN_EXPERT_OFFLOAD`), ngram
|
|
184
|
+
draft-kind coverage in README/server-api, doc-map sync, an openwiki
|
|
185
|
+
investigation write-up. STATUS.md was rewritten to current-state-only
|
|
186
|
+
several times through the release to keep handoff notes accurate.
|
|
187
|
+
- A new ground rule was recorded in CLAUDE.md: changes to the served
|
|
188
|
+
surface must update the user-facing reference docs in the same commit.
|
|
189
|
+
- A direct optiq mixed-KV bit-parity golden replaced an indirect
|
|
190
|
+
ours-vs-ours comparison; a bit-exact L1 parity harness was added for
|
|
191
|
+
the Qwen3-30B-A3B MoE port, with uniform quantized KV confirmed
|
|
192
|
+
bit-exact against mlx-lm's own `--kv-bits` and a stock-mlx-lm baseline
|
|
193
|
+
added to the bench.
|
|
194
|
+
- Dependency/CI bumps: pi, `actions/checkout`, and the site build moved
|
|
195
|
+
off a deprecated Node runtime.
|
|
196
|
+
|
|
197
|
+
## Breaking / behavior changes
|
|
198
|
+
|
|
199
|
+
- **`--batch` default changes from 1 to 8.** Serving now batches by
|
|
200
|
+
default whenever concurrent requests arrive; pass `--batch 1` to force
|
|
201
|
+
strict serial (needed if you require arrival-order-independent,
|
|
202
|
+
bit-identical numerics).
|
|
203
|
+
- **The default decode path changed.** Naked, no-flag serving now runs
|
|
204
|
+
the faithful, bit-exact-vs-mlx-lm kernel set — what `--l1` used to
|
|
205
|
+
mean. `--l1`/`--l2`/`--l3` are now pure aliases over per-feature flags
|
|
206
|
+
rather than distinct performance tiers. Every custom-kernel path that
|
|
207
|
+
didn't beat this baseline in a paired A/B was deleted in the same
|
|
208
|
+
pass: the fused-decode kernel, fused-gelu/fused-swiglu/fused-mlp/
|
|
209
|
+
steel-linear Metal kernels, the flash-decode "perf" kernel and its
|
|
210
|
+
frozen-oracle test scaffolding, `--l3`, and `FaithfulMiniCPM5` /
|
|
211
|
+
`MLX_BUN_CPM5_FAITHFUL`. None of these flags or code paths exist
|
|
212
|
+
anymore. `MLX_BUN_FAITHFUL` is also gone (superseded by the faithful
|
|
213
|
+
kernels being the default) — the server's unset `--kv-quant` now
|
|
214
|
+
defaults to bf16 instead of `kv_config`, so getting the old default
|
|
215
|
+
composition requires an explicit `--kv-quant`.
|
|
216
|
+
- **New CLI/API surface:** `--kv-quant turbo[:kXvY]` (TurboQuant),
|
|
217
|
+
`--paged-kv` (off by default; refuses to combine with `--batch N>1`,
|
|
218
|
+
`--kv-quant`, or `--draft-model`), `--draft-model` (speculative
|
|
219
|
+
decoding), `--isolate` and `--model-pool` (runtime isolation),
|
|
220
|
+
`--kv-budget <GB>` (aggregate KV admission), `--ssd-demote-idle
|
|
221
|
+
<seconds>` (default 300, requires `--ssd-cache`),
|
|
222
|
+
`--allow-private-media` / `MLX_BUN_ALLOW_PRIVATE_MEDIA=1`, and the
|
|
223
|
+
`response_format` / `guided_grammar` / `guided_regex` / `guided_choice`
|
|
224
|
+
/ `structured_outputs` request fields (kill switch:
|
|
225
|
+
`MLX_BUN_GRAMMAR=0`).
|
|
226
|
+
- **Media fetches are restricted by default.** `image_url`/`audio_url`
|
|
227
|
+
now refuse private/loopback/link-local/CGNAT destinations and
|
|
228
|
+
non-`http(s)` schemes — previously they fetched anything. LAN media
|
|
229
|
+
needs the new escape hatch above.
|
|
230
|
+
- **`PromptCache.take()` semantics changed.** Cache hits now return
|
|
231
|
+
non-consuming, ref-counted clones instead of consuming/trimming the
|
|
232
|
+
donor entry — code holding onto the old consuming behavior should
|
|
233
|
+
re-check callers.
|
|
234
|
+
|
|
235
|
+
Full details and measured numbers: STATUS.md, `benchmarks/RESULTS.md`,
|
|
236
|
+
and the design docs referenced above. Credit where due: TurboQuant's
|
|
237
|
+
codec is verified bit-exact against the vendored vllm-metal reference,
|
|
238
|
+
and the speculative-decoding path is verified token-for-token against
|
|
239
|
+
mlx-lm's own `--draft-model` implementation.
|
package/docs/reference/cli.md
CHANGED
|
@@ -11,14 +11,15 @@ candidates — just make it more specific.
|
|
|
11
11
|
Bare `mlx-bun` with no verb runs `serve` — the appliance path: first run
|
|
12
12
|
downloads a starter model, serves it, and opens the chat UI.
|
|
13
13
|
|
|
14
|
-
>
|
|
14
|
+
> All verbs below ship in current releases; the release notes in
|
|
15
|
+
> `docs/planning/` record when each verb first appeared.
|
|
15
16
|
|
|
16
17
|
## Serving & chat
|
|
17
18
|
|
|
18
19
|
### `serve` — run the server
|
|
19
20
|
|
|
20
|
-
Start the OpenAI/Anthropic-compatible server plus the
|
|
21
|
-
|
|
21
|
+
Start the OpenAI/Anthropic-compatible server plus the browser web app (chat,
|
|
22
|
+
status, and model tools).
|
|
22
23
|
|
|
23
24
|
```sh
|
|
24
25
|
mlx-bun serve # default model, http://localhost:8080
|
|
@@ -34,16 +35,32 @@ Common flags (full list in [server-config.md](server-config.md)):
|
|
|
34
35
|
| `--host <addr>` | Interface to bind (default `127.0.0.1`, loopback only; `0.0.0.0` to expose) |
|
|
35
36
|
| `--port <n>` | Listen port (default 8080) |
|
|
36
37
|
| `--memory-budget <GB>` | Reject loads/requests that can't fit the budget |
|
|
37
|
-
| `--kv-
|
|
38
|
+
| `--kv-budget <GB>` | Aggregate KV budget across concurrent batch rows: over-budget joiners queue until rows finish; a request over the budget alone is rejected (off unless set) |
|
|
39
|
+
| `--prompt-cache <GB>` | RAM prefix-KV cache, byte-capped LRU (default 8; `0` disables). Non-consuming prefix sharing: a hit clones instead of consuming the donor entry |
|
|
40
|
+
| `--isolate` | Run the inference engine as a crash-isolated child process on a unix socket; this process becomes a pure reverse-proxy (instant under GPU load, auto-respawns on engine crash) — default off ([runtime-isolation.md](../design/runtime-isolation.md)) |
|
|
41
|
+
| `--model-pool <n>` | With `--isolate`: cap on resident per-model engine children (default 1); over the cap the LRU engine drains its cache to the SSD tier and exits, respawning on demand |
|
|
42
|
+
| `--ssd-cache <dir>` | SSD cold tier under the prompt cache: idle/evicted prefix-KV spills to disk and restores on reattach, surviving restarts (default off — [ssd-kv-cold-tier.md](../design/ssd-kv-cold-tier.md)) |
|
|
43
|
+
| `--ssd-cache-max <GB>` | SSD tier byte cap, oldest-mtime evicted first (default 32; `--ssd-cache` only) |
|
|
44
|
+
| `--ssd-cache-verify` | Verify every tensor hash on restore, not just the header (integrity paranoia; roughly doubles restore reads; `--ssd-cache` only) |
|
|
45
|
+
| `--ssd-demote-idle <sec>` | Idle prompt-cache entries spill to the SSD tier and free GPU memory after this long (default 300 with `--ssd-cache`; `0` disables) |
|
|
46
|
+
| `--kv-quant config\|off\|4\|8\|turbo[:k<bits>v<bits>]` | KV cache quantization: per-layer `kv_config.json`, bf16, uniform bits, or TurboQuant (rotation-based, default `k8v3` — [turboquant-kv.md](../design/turboquant-kv.md); full-attention layers only, solo-only/serial) (default `off` — quantized KV trades 5–20% decode speed for memory headroom, so it's opt-in) |
|
|
47
|
+
| `--paged-kv` | OPTIONAL vLLM-style paged KV cache (block pool + gather before the stock SDPA; env `MLX_BUN_PAGED_KV=1`) — bit-exact with the plain path, default off. v1: serial only (pins `--batch 1` unless `--batch` is given; refuses `--batch N>1`, `--kv-quant`, `--draft-model`), Gemma4-family, bf16; bypasses the prompt cache and runs uncompiled decode. Expect a small decode cost at batch=1 — v1 ships the abstraction for batched/CoW follow-ups ([paged-kv-cache.md](../design/paged-kv-cache.md)) |
|
|
48
|
+
| `--paged-kv-block-size <n>` | Tokens per KV block (default 256 = the plain cache's growth step; `--paged-kv` only) |
|
|
38
49
|
| `--adapter <dir>` | Mount a LoRA adapter at startup (`--adapter-path` accepted as the mlx_lm.server alias) |
|
|
39
|
-
| `--
|
|
50
|
+
| `--draft-model <path\|query>` | Speculative decoding: a drafter proposes, the main model verifies — exact results, faster decode when drafts land. The artifact's kind is auto-detected: a full same-tokenizer model (mlx_lm.server parity), a Gemma `-assistant` KV-borrowing drafter, a DSpark checkpoint trained here, or one of DeepSeek's released DSpark drafters (DeepSpec `Gemma4DSparkModel`, e.g. `dspark_gemma4_12b_block7`). Serial lane only: with `--batch N` a mounted draft routes every request serial |
|
|
51
|
+
| `--draft-kind <kind>` | Override draft-artifact detection: `two-model` \| `assistant` \| `dspark` \| `deepspec` \| `ngram` (default: auto). `ngram` is MODEL-FREE prompt lookup — no `--draft-model` (mounting one alongside it is refused): drafts are copied from the request's own prompt+generation whenever its trailing tokens have occurred earlier in that stream, verified by the target like any other draft, so results stay exact at any temperature. Zero weights, zero memory; best on agentic/RAG/code-edit traffic that re-emits spans already in context (prior art: prompt-lookup decoding / vLLM's `ngram` proposer) |
|
|
52
|
+
| `--num-draft-tokens <n>` | Drafts per verify round (default 3; `ngram` defaults to 10 — drafting is free; a DSpark draft pins to its trained block width) |
|
|
53
|
+
| `--ngram-max <k>` / `--ngram-min <k>` | Prompt-lookup match-window bounds, `--draft-kind ngram` only: the longest/shortest trailing k-gram searched (defaults 3 / 1, the reference values) |
|
|
54
|
+
| `--batch <n>` | Continuous-batched serving cap, mlx-lm B=N parity (default 8; `--batch 1` pins the serial path) |
|
|
40
55
|
| `--temperature` / `--top-p` / `--top-k` / `--max-tokens` | Server-wide sampling defaults (per-request fields still win) |
|
|
41
|
-
| `--l1` / `--l2`
|
|
56
|
+
| `--l1` / `--l2` | Parity tier alias: bit-exact to mlx-lm / bit-exact to mlx-optiq. **No tier = `--l1`** (the default since 2026-07-05 — output-changing levers are opt-in until they beat the L1 baseline in a paired A/B). Each expands to per-fork flags (`--compiled-decode`, `--compiled-activations`, `--fused-sdpa`, `--kv-quant`); a fork flag overrides one. `--l3` was removed 2026-07-05 and now errors (the Lab replaces it — [unified-engine-frontier-plan.md](../design/unified-engine-frontier-plan.md)). See [server-config.md](server-config.md#fidelity-tiers-and-the-decode-route---l1----l2). |
|
|
57
|
+
| `--allow-private-media` | Let `image_url`/`audio_url` parts fetch from private/loopback/link-local hosts (blocked by default — SSRF guard; the 10 s timeout and 64 MB cap on remote media apply either way) |
|
|
42
58
|
| `--no-open` | Don't auto-open the chat UI |
|
|
43
59
|
|
|
44
60
|
Endpoints: `/v1/chat/completions`, `/v1/completions`, `/v1/messages`,
|
|
45
61
|
`/v1/responses`, `/v1/models`, `/v1/adapters`, `/health`, `/stats`, `/fit`,
|
|
46
|
-
`/library`, `/downloads` —
|
|
62
|
+
`/library`, `/downloads` — web app at `/` (lands on chat); `/chat`, `/status`,
|
|
63
|
+
`/quantize`, `/finetune`, `/dataset` deep-link its views. Full
|
|
47
64
|
schemas in [server-api.md](server-api.md).
|
|
48
65
|
|
|
49
66
|
### `pi` — built-in coding agent
|
|
@@ -73,7 +90,7 @@ mlx-bun harness pi --remove # disconnect
|
|
|
73
90
|
|
|
74
91
|
Load the model, generate, print, exit — no server. Renders through the chat
|
|
75
92
|
template by default; `--raw` feeds the prompt verbatim. Sampling and
|
|
76
|
-
decode-path levers mirror `serve` (`--temperature`, `--seed`, `--l1/--l2
|
|
93
|
+
decode-path levers mirror `serve` (`--temperature`, `--seed`, `--l1/--l2`,
|
|
77
94
|
`--kv-quant`, …).
|
|
78
95
|
|
|
79
96
|
```sh
|
|
@@ -81,7 +98,7 @@ mlx-bun generate gemma "a haiku about metal shaders"
|
|
|
81
98
|
mlx-bun gen e4b --prompt "…" --max-tokens 512 --seed 42
|
|
82
99
|
```
|
|
83
100
|
|
|
84
|
-
### `embed` — text embeddings
|
|
101
|
+
### `embed` — text embeddings
|
|
85
102
|
|
|
86
103
|
One-shot embeddings from a local Qwen3-Embedding model (last-token pooled,
|
|
87
104
|
L2-normalized — bit-exact vs mlx-lm). One JSON vector per input line.
|
|
@@ -125,10 +142,12 @@ mlx-bun ls --vision --max-size 10GB # filter
|
|
|
125
142
|
mlx-bun ls --all-revisions # one row per cached snapshot
|
|
126
143
|
```
|
|
127
144
|
|
|
128
|
-
### `gc` — reclaim disk
|
|
145
|
+
### `gc` — reclaim disk
|
|
129
146
|
|
|
130
147
|
Delete superseded snapshots and the blobs only they reference. Prints the plan
|
|
131
|
-
by default; `--yes` actually deletes.
|
|
148
|
+
by default; `--yes` actually deletes. Same planner (`src/registry.ts`
|
|
149
|
+
`planGc`/`executeGc`) also backs `GET /api/gc/plan` / `POST /api/gc/execute`
|
|
150
|
+
on a running `serve` — see [server-api.md](server-api.md#get-apigcplan--post-apigcexecute).
|
|
132
151
|
|
|
133
152
|
```sh
|
|
134
153
|
mlx-bun gc # dry-run: what would be reclaimed
|
|
@@ -170,7 +189,7 @@ Tails an adapter dir's `metrics.jsonl` and renders live loss / speed / memory.
|
|
|
170
189
|
mlx-bun train-watch ~/.cache/mlx-bun/mlx-bun-finetunes/orpo-e4b
|
|
171
190
|
```
|
|
172
191
|
|
|
173
|
-
### `fuse` — merge an adapter into the base
|
|
192
|
+
### `fuse` — merge an adapter into the base
|
|
174
193
|
|
|
175
194
|
Folds LoRA deltas into the base weights and writes a standalone snapshot; a
|
|
176
195
|
quantized base keeps its exact quantization layout. Flag names match
|
|
@@ -181,7 +200,7 @@ mlx-bun fuse e4b --adapter ./adapters --save-path ./fused_model
|
|
|
181
200
|
mlx-bun serve ./fused_model
|
|
182
201
|
```
|
|
183
202
|
|
|
184
|
-
### `convert` — quantize a model
|
|
203
|
+
### `convert` — quantize a model
|
|
185
204
|
|
|
186
205
|
Quantize an HF model (local path, downloaded model, or repo id) into a local
|
|
187
206
|
MLX snapshot — uniform 4/8-bit, or mixed-precision via `--target-bpw`
|
|
@@ -192,7 +211,7 @@ mlx-bun convert Qwen/Qwen3-4B -q # uniform 4-bit
|
|
|
192
211
|
mlx-bun convert Qwen/Qwen3-4B --target-bpw 4.5 # mixed precision
|
|
193
212
|
```
|
|
194
213
|
|
|
195
|
-
### `upload` — push to the Hugging Face Hub
|
|
214
|
+
### `upload` — push to the Hugging Face Hub
|
|
196
215
|
|
|
197
216
|
Native push-to-hub, no Python: creates the repo if needed, uploads via the
|
|
198
217
|
git-LFS batch protocol, commits. Flag names match `mlx_lm.upload`.
|
|
@@ -201,7 +220,7 @@ git-LFS batch protocol, commits. Flag names match `mlx_lm.upload`.
|
|
|
201
220
|
mlx-bun upload --path ./fused_model --upload-repo you/my-model
|
|
202
221
|
```
|
|
203
222
|
|
|
204
|
-
### `perplexity` — evaluate on your data
|
|
223
|
+
### `perplexity` — evaluate on your data
|
|
205
224
|
|
|
206
225
|
`mlx_lm.perplexity` methodology exactly, over a **local** `.jsonl`/`.txt` file.
|
|
207
226
|
|
|
@@ -226,7 +245,7 @@ mlx-bun memory synthesize # conversations → cross-linked articles, all
|
|
|
226
245
|
mlx-bun memory schedule --at 03:00
|
|
227
246
|
```
|
|
228
247
|
|
|
229
|
-
`setup` is an alias for `memory
|
|
248
|
+
`setup` is an alias for `memory`.
|
|
230
249
|
|
|
231
250
|
## Measurement
|
|
232
251
|
|
|
@@ -156,8 +156,8 @@ manual escape hatch if offline.
|
|
|
156
156
|
|
|
157
157
|
## npm / bunx
|
|
158
158
|
|
|
159
|
-
mlx-bun is published to npm as `mlx-bun
|
|
160
|
-
|
|
159
|
+
mlx-bun is published to npm as `mlx-bun`. Install or run without a
|
|
160
|
+
permanent install:
|
|
161
161
|
|
|
162
162
|
```sh
|
|
163
163
|
# one-off (no install)
|
|
@@ -168,10 +168,17 @@ bun install -g mlx-bun
|
|
|
168
168
|
mlx-bun --version
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
The npm package
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
The npm package is a thin launcher (no postinstall hook): it imports
|
|
172
|
+
`src/cli.ts` under Bun, which fetches the small (~52 MB compressed,
|
|
173
|
+
sha256-verified) native runtime pack — `libmlxc`/`libmlx`/`libjaccl`/
|
|
174
|
+
`mlx.metallib` — into `~/Library/Caches/mlx-bun/` on first invocation if
|
|
175
|
+
no copy is already resolvable (env / beside the binary / cache /
|
|
176
|
+
Homebrew). This is the same npm/sidecar mechanism described above, not
|
|
177
|
+
the signed/notarized Homebrew bundle. `bun publish` is already run
|
|
178
|
+
automatically and idempotently as step 4 of `PUBLISH=1
|
|
179
|
+
./scripts/release-binary.sh`; run it standalone only as a manual
|
|
180
|
+
fallback/retry:
|
|
174
181
|
|
|
175
182
|
```sh
|
|
176
|
-
bun publish #
|
|
183
|
+
bun publish # manual retry; package.json version must already be bumped
|
|
177
184
|
```
|
|
@@ -19,15 +19,29 @@ Produces a relocatable directory:
|
|
|
19
19
|
|
|
20
20
|
| file | what | size (arm64) |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| `mlx-bun` | the CLI/server, compiled with `bun build --compile` | ~
|
|
22
|
+
| `mlx-bun` | the CLI/server, compiled with `bun build --compile` | ~70-75 MB |
|
|
23
23
|
| `libmlxc.dylib` | mlx-c, rewritten to load `@loader_path/libmlx.dylib` | ~0.7 MB |
|
|
24
24
|
| `libmlx.dylib` | mlx core (+`@loader_path` rpath added for libjaccl) | ~15 MB |
|
|
25
25
|
| `libjaccl.dylib` | mlx's distributed-comm dependency | ~0.6 MB |
|
|
26
26
|
| `mlx.metallib` | Metal kernels — libmlx loads it from its own directory | ~150 MB |
|
|
27
27
|
| `photon_rs_bg.wasm` | pi image codec — only the web chat's `read`-on-image path; resolved next to the executable | ~1.8 MB |
|
|
28
28
|
|
|
29
|
+
`build-binary.sh` also sidecars a second set of assets for the embedded
|
|
30
|
+
pi **terminal** (`mlx-bun pi`, `src/pi-terminal.ts` → `InteractiveMode`),
|
|
31
|
+
none of which the headless web chat touches:
|
|
32
|
+
|
|
33
|
+
| file | what |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `theme/*.json` | TUI color themes (built-in dark/light fallback exists in code; shipped so custom theme reads and the schema resolve) |
|
|
36
|
+
| `assets/*.png` | TUI startup art |
|
|
37
|
+
| `export-html/template.html`, `export-html/vendor/*.js` | the `/export` command's HTML template + vendor JS |
|
|
38
|
+
| `package.json`, `CHANGELOG.md` | version banner + startup changelog |
|
|
39
|
+
| `native/darwin/prebuilds/<arch>/darwin-modifiers.node` | pi-tui's native modifier-key detection (degrades gracefully if absent) |
|
|
40
|
+
|
|
29
41
|
Library resolution order (src/mlx/ffi.ts): `MLX_BUN_LIBMLXC` env var →
|
|
30
|
-
`libmlxc.dylib` next to the executable →
|
|
42
|
+
`libmlxc.dylib` next to the executable → the downloaded native-pack
|
|
43
|
+
cache (`~/Library/Caches/mlx-bun/native-v<ver>-<arch>/`, populated on
|
|
44
|
+
first run when no sidecar or homebrew install is found) → homebrew
|
|
31
45
|
(`/opt/homebrew/lib`, `/usr/local/lib`). The whole directory can be
|
|
32
46
|
renamed/moved; nothing references absolute paths after the build
|
|
33
47
|
script's `install_name_tool` fixups.
|
|
@@ -60,21 +74,28 @@ crashing), so it is best-effort, not load-bearing.
|
|
|
60
74
|
> The web chat itself is unaffected (its provider is text-only), and the
|
|
61
75
|
> wasm is shipped so it will work once Bun's support lands.
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
## Embedded pi terminal (`mlx-bun pi`)
|
|
78
|
+
|
|
79
|
+
The compiled binary also embeds pi's full interactive TUI
|
|
80
|
+
(`src/pi-terminal.ts` → `InteractiveMode`), reachable via `mlx-bun pi`.
|
|
81
|
+
Unlike the headless web chat, the terminal *does* resolve theme JSON,
|
|
82
|
+
startup art, the `/export` template, the version banner, and the
|
|
83
|
+
pi-tui native modifier-key helper by path — the second table above is
|
|
84
|
+
sidecar'd for exactly this mode. `src/pi-web.ts`'s session (the web
|
|
85
|
+
chat) still builds with `noThemes`/`noSkills`/`noExtensions`/
|
|
86
|
+
`noPromptTemplates`/`noContextFiles`, so none of that table is reached
|
|
87
|
+
from `/ws/chat` — only `mlx-bun pi` uses it.
|
|
88
|
+
|
|
89
|
+
**Omitted** (upstream pi ships these beside its own binary; neither
|
|
90
|
+
mlx-bun mode reaches them):
|
|
65
91
|
|
|
66
92
|
| omitted asset | what it's for |
|
|
67
93
|
|---|---|
|
|
68
|
-
| `
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
These are gated behind TUI code paths and the explicit `exportToHtml()`
|
|
74
|
-
method; `createAgentSession`'s import graph (`core/sdk.js`) pulls in no
|
|
75
|
-
theme/asset/export-html loaders, and `src/pi-web.ts` builds the session
|
|
76
|
-
with `noThemes`/`noSkills`/`noExtensions`/`noPromptTemplates`/
|
|
77
|
-
`noContextFiles`.
|
|
94
|
+
| `docs/`, `examples/`, `README.md` | upstream's own help/doc browsing, not exposed by either mlx-bun entry point |
|
|
95
|
+
|
|
96
|
+
`createAgentSession`'s import graph (`core/sdk.js`) pulls in no loaders
|
|
97
|
+
for these; both `src/pi-web.ts` and `src/pi-terminal.ts` build sessions
|
|
98
|
+
that never reference them.
|
|
78
99
|
|
|
79
100
|
**Verifying it.** `build-binary.sh` runs `scripts/verify-binary-pi.ts` as
|
|
80
101
|
a sibling compiled binary inside the bundle (so `process.execPath` points
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Features matrix — everything mlx-bun serves, in one table
|
|
2
|
+
|
|
3
|
+
The complete option inventory: every serving feature, its default, which
|
|
4
|
+
execution lane it runs on, which fidelity tier verifies it, and its knob.
|
|
5
|
+
This is the *what-exists* view; per-flag detail lives in
|
|
6
|
+
[server-config.md](server-config.md), wire formats in
|
|
7
|
+
[server-api.md](server-api.md), CLI verbs in [cli.md](cli.md).
|
|
8
|
+
|
|
9
|
+
**Lanes:** `serial` = the default single-queue path; `batch` = the
|
|
10
|
+
`--batch N` continuous-batching engine (bf16 compat mode, mlx-lm B=N
|
|
11
|
+
parity). **Tiers:** L1 = bit-exact vs mlx-lm · L2 = bit-exact vs mlx-optiq
|
|
12
|
+
· Lab = no external oracle; KL/eval-gated experiments (see
|
|
13
|
+
[server-config.md → Fidelity tiers](server-config.md#fidelity-tiers-and-the-decode-route---l1----l2)).
|
|
14
|
+
|
|
15
|
+
## Serving & performance
|
|
16
|
+
|
|
17
|
+
| Feature | Default | Lane | Tier | Knob |
|
|
18
|
+
| --- | --- | --- | --- | --- |
|
|
19
|
+
| OpenAI chat completions (`/v1/chat/completions`, SSE) | on | both | — | — |
|
|
20
|
+
| Raw text completions (`/v1/completions`) | on | both | — | — |
|
|
21
|
+
| Anthropic Messages (`/v1/messages`) | on | both | — | — |
|
|
22
|
+
| OpenAI Responses shim (`/v1/responses`) | on | both | — | — |
|
|
23
|
+
| Continuous batching (mlx-lm B=N parity) | **on** — cap 8 (default flipped 2026-07-05; a lone request runs the exact serial engine, so the cap engages only under real concurrency; `--batch 1` pins serial) | batch | L1 | `--batch <n>` |
|
|
24
|
+
| Prompt cache (prefix KV reuse) | on, 8 GB | both | — | `--prompt-cache <GB>` (0 = off) |
|
|
25
|
+
| **SSD KV cold tier** (cache survives eviction + restarts) | off | both | — | `--ssd-cache <dir>` (+ `-max`, `-verify`, `-demote-idle <sec>` — default 300s once `--ssd-cache` is on, `0` disables) |
|
|
26
|
+
| **Runtime isolation** (engine runs as a crash-isolated CHILD process behind a reverse-proxy parent; UI stays instant under GPU load and survives engine crashes — [runtime-isolation.md](../design/runtime-isolation.md)) | off | both | — | `--isolate` |
|
|
27
|
+
| **Model pool** (with `--isolate`: LRU-capped RESIDENT model engines; over the cap the least-recently-used engine demotes its cache to the SSD tier and exits, respawning on the next request) | 1 resident engine | both | — | `--model-pool <n>` (requires `--isolate`) |
|
|
28
|
+
| Mixed-precision KV (`kv_config.json`, optiq's scheme) | **off** — opt-in (default flipped to bf16 2026-07-05; quantized KV trades 5–20% decode for memory headroom) | serial + **batch** (per-layer configs batch on every shipped model — full-attention layers Phase 3.1, rotating layers milestone 2; uniform bits stay serial) | L2 | `--kv-quant config\|off\|4\|8`, `--l2` |
|
|
29
|
+
| **TurboQuant KV** (rotation-based: affine keys + FWHT/Lloyd-Max values) | off — opt-in ([turboquant-kv.md](../design/turboquant-kv.md); v1 dequantize-on-fetch, full-attention layers only, no speed claim) | serial only (solo-only, unconditionally) | Lab (codec is oracle-backed vs vllm-metal; the cache class is unvalidated) | `--kv-quant turbo[:k<bits>v<bits>]` (default `k8v3`) |
|
|
30
|
+
| Compiled decode (bit-exact graph replay) | on, every tier | serial | L1/L2 | `--compiled-decode on\|off` |
|
|
31
|
+
| Compiled activations (faithful geglu/swiglu — mlx-lm's `@mx.compile`) | **on**, every tier | both | L1 | `--compiled-activations on\|off` |
|
|
32
|
+
| Fused SDPA (optiq-exact quantized-KV attention) | follows `--kv-quant`: on for `config`, off for uniform/bf16 | serial | L2 | `--fused-sdpa on\|off` |
|
|
33
|
+
| **Paged KV** (vLLM-style block pool: host-side block table + gather before the stock SDPA, no fused kernel in v1 — [paged-kv-cache.md](../design/paged-kv-cache.md)) | off | serial (pins `--batch 1` unless `--batch` given) | gated bit-exact vs the plain `KVCache` (`tests/paged-kv-parity.test.ts`) | `--paged-kv`, `--paged-kv-block-size <n>` (default 256) — v1 scope Gemma4-family bf16; refuses `--batch N>1`, `--kv-quant`, `--draft-model` |
|
|
34
|
+
| **Speculative decoding** (auto-detected drafter: two-model = mlx-lm parity/L1, Gemma `-assistant` = optiq/L2, DSpark local = Lab, DeepSpec released = DeepSpec-reference oracle) | off | serial (forces all-serial) | per-drafter oracle | `--draft-model <path\|query>`, `--draft-kind`, `--num-draft-tokens` |
|
|
35
|
+
| Speculative decoding, **model-free prompt lookup** (drafts copied from the request's own context; port of prompt-lookup decoding / vLLM `ngram`; zero weights) | off | serial (forces all-serial) | lossless by verify (gated vs non-spec greedy, tests/spec-ngram.test.ts) | `--draft-kind ngram` (no `--draft-model`), `--ngram-max`, `--ngram-min`, `--num-draft-tokens` (default 10) |
|
|
36
|
+
| Memory admission (refuse what can't fit; never GPU-OOM) | on (RAM × 0.75) | both | — | `--memory-budget <GB>` |
|
|
37
|
+
| Aggregate KV admission for batch rows (queue, don't OOM) | off | batch | — | `--kv-budget <GB>` |
|
|
38
|
+
| Expert offload (MoE cold experts on mmap) | off | serial | Lab | `--expert-offload` |
|
|
39
|
+
| Extend-join (O(1) batch admission) | on | batch | L1 (own oracle) | `MLX_BUN_BATCH_EXTEND=0` |
|
|
40
|
+
| Vectorized greedy batch sampling | on | batch | bit-equal A/B | `MLX_BUN_BATCH_VEC_SAMPLE=0` |
|
|
41
|
+
|
|
42
|
+
## Request features
|
|
43
|
+
|
|
44
|
+
| Feature | Default | Lane | Tier | Knob |
|
|
45
|
+
| --- | --- | --- | --- | --- |
|
|
46
|
+
| **Structured output** (`response_format` json_object/json_schema) | on | both | L2 (oMLX) | request field; `MLX_BUN_GRAMMAR=0` kills |
|
|
47
|
+
| Structured output **jump-forward** (grammar-forced spans emitted without per-token forwards; SGLang technique via xgrammar) | **off** | serial only | Lab (string-lossless + validity gates, tests/grammar-jump.test.ts; token stream may differ — no oracle) | `MLX_BUN_GRAMMAR_JUMP=1` |
|
|
48
|
+
| `guided_grammar` (EBNF) / `guided_regex`¹ / `guided_choice` / `structured_outputs` | on | both | L2 | request fields |
|
|
49
|
+
| Structured output × speculative decoding | on (when both active) | serial | Lab (validity+equivalence) | — |
|
|
50
|
+
| Quantized KV × speculative decoding (any axis: uniform / config / turbo) | KV scheme wins — drafted requests decode serially WITHOUT speculation (spec lane is bf16-KV-only in v1; startup warning) | serial | — | omit `--kv-quant` to speculate |
|
|
51
|
+
| Tool calling (Gemma sentinel / CPM+Qwen XML) + `role:"tool"` loops | on | both | — | request `tools` |
|
|
52
|
+
| Vision (`image_url` parts; PNG/JPEG/HEIC/AVIF/WebP/TIFF/GIF/BMP) | on (models with a tower); remote fetches refuse private/loopback/link-local hosts by default (SSRF guard) | serial | L1/L2 | `--allow-private-media` / `MLX_BUN_ALLOW_PRIVATE_MEDIA=1` re-permits LAN hosts |
|
|
53
|
+
| **Audio input** (`input_audio`/`audio`/`audio_url` parts; WAV native, mp3/m4a/flac/ogg/aiff via CoreAudio; ≤30 s / 750 tokens per clip; mixes with images) | on (models with `audio_config` + sidecar tower: e4b); remote fetches refuse private/loopback/link-local hosts by default (SSRF guard) | serial³ | L2 (greedy stream EXACT vs the optiq internal model) | `--allow-private-media` / `MLX_BUN_ALLOW_PRIVATE_MEDIA=1` re-permits LAN hosts |
|
|
54
|
+
| LoRA adapters (mount at start / hot-swap at runtime) | off | serial | — | `--adapter <dir>`, `POST /v1/adapters` |
|
|
55
|
+
| Sampling: temperature / top-p / top-k / min-p / XTC / logit_bias / presence+frequency+repetition penalties | per-request | both | L1 (mlx-lm-faithful) | request fields / server defaults |
|
|
56
|
+
| `logprobs` / `top_logprobs` (mlx-lm semantics) | off | serial | L1 | request fields |
|
|
57
|
+
| Fixed `seed` reproducibility | off | serial (routes solo) | — | request field |
|
|
58
|
+
| Thinking-mode control (hybrid-reasoning models) | model default | both | — | `--thinking`, `chat_template_kwargs` |
|
|
59
|
+
| Stop sequences / streaming / usage accounting | on | both | — | request fields |
|
|
60
|
+
| HLG tone-curve sampling | off | serial | Lab | `--hlg-sampling on` |
|
|
61
|
+
| Spec-decode telemetry (`usage.speculation`) | on with a draft | serial | — | — |
|
|
62
|
+
| Per-turn lane telemetry (`usage.lane`: serial / serial+spec / batched) | on | both | — | — |
|
|
63
|
+
|
|
64
|
+
## Model coverage (per-model validated cells — no generic path is trusted untested)
|
|
65
|
+
|
|
66
|
+
| Family | Serial | Batch | Notes |
|
|
67
|
+
| --- | --- | --- | --- |
|
|
68
|
+
| MiniCPM5 (cpm5) | ✅ L1/L2 | ✅ | the starter model |
|
|
69
|
+
| Gemma 4 (1B/e4b/12B/26B, + vision e4b/12B, + audio e4b) | ✅ L1/L2 | ✅ | sliding+full interleaved; MoE 26B |
|
|
70
|
+
| Qwen3.5 (gated-DeltaNet hybrid) | ✅ L1/L2 | ✅ (SSM path) | `MLX_BUN_BATCH_SSM=0` reverts |
|
|
71
|
+
| DiffusionGemma-26B (non-autoregressive) | ✅ (own engine) | — serial always | first bit-exact non-AR port |
|
|
72
|
+
| Tier-0 universal (llama/qwen2/qwen3/olmo2/…, 11 archs) | ✅ L1 | ✅ plain full-attention archs² | gemma2-family/sliding universal → serial |
|
|
73
|
+
|
|
74
|
+
## Beyond serving (the same binary)
|
|
75
|
+
|
|
76
|
+
Training (`mlx-bun train`: ORPO/DPO/SFT, flash-CCE, segmented backward) ·
|
|
77
|
+
quantization (`convert`, `fuse`, `--target-bpw`) · embeddings · local
|
|
78
|
+
memory/wiki (`mlx-bun memory`) · pi agent integration (`mlx-bun pi`) ·
|
|
79
|
+
model registry + `fit` planner + `gc` · HF `upload` · web chat Model Hub
|
|
80
|
+
(`/api/hub/local` + `/search` + `/download`, per-row `/fit` verdict;
|
|
81
|
+
`/api/hub/serve` is `restart_required`-only today — no in-process model-swap
|
|
82
|
+
seam reaches `/ws/chat`, see [server-api.md](server-api.md#model-hub-apihub))
|
|
83
|
+
· **PWA installability** (manifest + shell-only service worker —
|
|
84
|
+
installable, instant static-shell paint; explicitly not offline chat, see
|
|
85
|
+
[server-api.md](server-api.md#web-app-static-routes--pwa-installability)).
|
|
86
|
+
See [cli.md](cli.md) and the per-topic reference docs.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
¹ `guided_regex` accepts the regex∩EBNF subset today (no `\d`/anchors — those degrade to prompt injection); real regex support is tracked.
|
|
90
|
+
² Gated token-exact vs mlx-lm B=2 (static + dynamic join/leave) on Llama-3.2-3B.
|
|
91
|
+
³ Audio is a capability neither ancestor SERVES: mlx-lm strips the audio tower at load and 400s non-text parts; optiq ships the full machinery but never wires it into its serve frontend — mlx-bun exposes it. Serial lane by design (embeddings prefill; batched audio has no oracle → novel-territory gates would apply), prompt cache skipped.
|
|
92
|
+
|
|
93
|
+
Composition rules and exclusions (what combines with what):
|
|
94
|
+
[server-config.md → Compatibility matrix](server-config.md#compatibility-matrix).
|
|
95
|
+
Performance per mode: `scripts/bench-modes.ts` (TTFT / prefill / decode /
|
|
96
|
+
peak-memory across every cell above); feature-composition perf:
|
|
97
|
+
`scripts/bench-feature-matrix.ts`.
|