mlx-bun 0.0.10 → 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.
Files changed (173) hide show
  1. package/README.md +66 -21
  2. package/THIRD_PARTY_LICENSES.md +1 -0
  3. package/docs/archive/mlx-bun-lab-report.html +275 -0
  4. package/docs/archive/wwdc-mlx-bun.html +848 -0
  5. package/docs/design/audio-input-plan.md +356 -0
  6. package/docs/design/batching-perf-path.md +58 -35
  7. package/docs/design/batching-v2-plan.md +23 -9
  8. package/docs/design/bucketing-stage.md +2 -1
  9. package/docs/design/compat-cli-surface-design.md +4 -3
  10. package/docs/design/decode-speed-program.md +172 -0
  11. package/docs/design/docs-reorg-plan.md +1 -1
  12. package/docs/design/dspark-serving-program.md +371 -0
  13. package/docs/design/dspark-speculative-decoding.md +167 -5
  14. package/docs/design/faithful-l1-consolidation.md +310 -0
  15. package/docs/design/generic-model-support.md +8 -5
  16. package/docs/design/grammar-spec-batching-integration.md +301 -0
  17. package/docs/design/minicpm5-decode-megakernel.md +16 -11
  18. package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
  19. package/docs/design/omlx-adoption-map.md +14 -6
  20. package/docs/design/orpo-training.md +34 -30
  21. package/docs/design/paged-kv-cache.md +214 -0
  22. package/docs/design/parallel-slots.md +82 -49
  23. package/docs/design/parity-tier-dag.md +43 -33
  24. package/docs/design/repo-cleanup-plan.md +189 -0
  25. package/docs/design/runtime-isolation.md +19 -3
  26. package/docs/design/segmented-backward-training.md +3 -2
  27. package/docs/design/ssd-kv-cold-tier.md +130 -0
  28. package/docs/design/structured-output.md +484 -0
  29. package/docs/design/the-dreaming-master-plan.md +2 -3
  30. package/docs/design/turboquant-kv.md +235 -0
  31. package/docs/design/unified-engine-frontier-plan.md +774 -0
  32. package/docs/design/web-chat-beat-matrix.md +315 -0
  33. package/docs/design/web-chat-redesign.md +1112 -0
  34. package/docs/investigations/dspark-handoff.md +182 -16
  35. package/docs/investigations/lab-build-journal.md +2 -2
  36. package/docs/investigations/openwiki-evaluation.md +249 -0
  37. package/docs/planning/IDEAS.md +14 -0
  38. package/docs/planning/PRODUCT_ROADMAP.md +2 -3
  39. package/docs/planning/chat-ui-vision-memory-first.md +199 -0
  40. package/docs/planning/chat-ui-vision.md +50 -0
  41. package/docs/planning/memory-docs-and-dag-plan.md +15 -6
  42. package/docs/planning/release-notes-v0.0.11.md +239 -0
  43. package/docs/reference/cli.md +35 -16
  44. package/docs/reference/distribution.md +13 -6
  45. package/docs/reference/embedding.md +35 -14
  46. package/docs/reference/features-matrix.md +97 -0
  47. package/docs/reference/library-api.md +30 -9
  48. package/docs/reference/memory.md +26 -6
  49. package/docs/reference/models.md +11 -1
  50. package/docs/reference/orpo-quickstart.md +3 -4
  51. package/docs/reference/server-api.md +634 -22
  52. package/docs/reference/server-config.md +250 -121
  53. package/docs/reference/training.md +14 -15
  54. package/package.json +5 -3
  55. package/src/anthropic.ts +13 -0
  56. package/src/audio/conformer.ts +647 -0
  57. package/src/audio/decode.ts +133 -0
  58. package/src/audio/features.ts +202 -0
  59. package/src/audio/transcode.ts +67 -0
  60. package/src/cli.ts +356 -105
  61. package/src/config.ts +49 -2
  62. package/src/download.ts +40 -0
  63. package/src/eval/README.md +7 -6
  64. package/src/eval/kl.ts +59 -3
  65. package/src/eval/quality-db.ts +2 -2
  66. package/src/eval/runner.ts +3 -3
  67. package/src/expert-offload.ts +15 -2
  68. package/src/flags.ts +13 -0
  69. package/src/generate.ts +330 -24
  70. package/src/grammar.ts +585 -0
  71. package/src/hub-rest.ts +242 -0
  72. package/src/kv-store.ts +442 -88
  73. package/src/lora.ts +8 -0
  74. package/src/md-modules.d.ts +42 -0
  75. package/src/media-fetch.ts +284 -0
  76. package/src/memory/rest.ts +245 -0
  77. package/src/memory/vault.ts +74 -0
  78. package/src/mlx/array.ts +106 -19
  79. package/src/mlx/autograd.ts +2 -2
  80. package/src/mlx/ffi.ts +27 -3
  81. package/src/mlx/metal-kernel.ts +7 -4
  82. package/src/mlx/ops.ts +153 -23
  83. package/src/mlx/turboquant-ops.ts +831 -0
  84. package/src/mlx/turboquant-tables.ts +237 -0
  85. package/src/model/batched-mask.ts +62 -1
  86. package/src/model/batched-quant.ts +206 -0
  87. package/src/model/batched-rotating-quant.ts +346 -0
  88. package/src/model/batched-rotating.ts +46 -1
  89. package/src/model/compiled-decode.ts +4 -15
  90. package/src/model/factory.ts +10 -4
  91. package/src/model/gemma4-base.ts +363 -39
  92. package/src/model/gemma4.ts +122 -33
  93. package/src/model/generated/gemma4-12b.ts +9 -13
  94. package/src/model/generated/gemma4-26b.ts +10 -5
  95. package/src/model/generated/gemma4-e4b.ts +14 -9
  96. package/src/model/minicpm5.ts +58 -102
  97. package/src/model/paged-kv.ts +253 -0
  98. package/src/model/qwen3-delta.ts +76 -15
  99. package/src/model/qwen3-moe.ts +404 -0
  100. package/src/model/qwen3.ts +9 -8
  101. package/src/model/qwen3_5.ts +88 -23
  102. package/src/model/support.ts +12 -1
  103. package/src/model/universal/dense.ts +40 -5
  104. package/src/model/universal/rope.ts +36 -21
  105. package/src/pi-web.ts +1299 -82
  106. package/src/preflight.ts +27 -8
  107. package/src/prompt-cache.ts +293 -33
  108. package/src/quantize/quantizer.ts +16 -1
  109. package/src/registry.ts +57 -2
  110. package/src/serve/batch-scheduler.ts +965 -72
  111. package/src/serve/generation-gateway.ts +214 -11
  112. package/src/serve/isolate.ts +417 -0
  113. package/src/serve/lane-registry.ts +44 -0
  114. package/src/serve/session-search.ts +326 -0
  115. package/src/server.ts +1380 -135
  116. package/src/spec/assistant-source.ts +150 -0
  117. package/src/spec/deepspec-source.ts +143 -0
  118. package/src/spec/dflash-source.ts +124 -0
  119. package/src/spec/dspark/ab-stats.ts +136 -0
  120. package/src/spec/dspark/calibration.ts +112 -0
  121. package/src/spec/dspark/deepspec-module.ts +801 -0
  122. package/src/spec/dspark/generate-dflash.ts +72 -20
  123. package/src/spec/dspark/loader.ts +23 -0
  124. package/src/spec/dspark/module-dflash.ts +278 -36
  125. package/src/spec/dspark/quantize-drafter.ts +91 -0
  126. package/src/spec/generate.ts +59 -25
  127. package/src/spec/ngram-source.ts +132 -0
  128. package/src/spec/serve-loop.ts +536 -0
  129. package/src/spec/source.ts +93 -0
  130. package/src/spec/two-model.ts +171 -0
  131. package/src/ssd-cache.ts +107 -35
  132. package/src/tokenizer.ts +288 -2
  133. package/src/tool-approvals.ts +131 -0
  134. package/src/tool-call.ts +408 -20
  135. package/src/train/flash-cce.ts +2 -2
  136. package/src/train/job.ts +0 -2
  137. package/src/train/trainer.ts +1 -4
  138. package/src/vision/prompt.ts +217 -48
  139. package/src/web/app.html +1184 -1789
  140. package/src/web/app.js +5667 -0
  141. package/src/web/icon.svg +13 -0
  142. package/src/web/manifest.webmanifest +24 -0
  143. package/src/web/src/adapters-panel.ts +288 -0
  144. package/src/web/src/api.ts +57 -0
  145. package/src/web/src/assistant.ts +485 -0
  146. package/src/web/src/chat.ts +1050 -0
  147. package/src/web/src/composer.ts +959 -0
  148. package/src/web/src/dataset.ts +132 -0
  149. package/src/web/src/finetune.ts +267 -0
  150. package/src/web/src/hub.ts +428 -0
  151. package/src/web/src/main.ts +98 -0
  152. package/src/web/src/markdown.ts +0 -0
  153. package/src/web/src/memory-panel.ts +572 -0
  154. package/src/web/src/model-picker.ts +159 -0
  155. package/src/web/src/palette.ts +346 -0
  156. package/src/web/src/protocol.ts +62 -0
  157. package/src/web/src/quantize.ts +167 -0
  158. package/src/web/src/rag.ts +274 -0
  159. package/src/web/src/sessions.ts +469 -0
  160. package/src/web/src/shell.ts +804 -0
  161. package/src/web/src/status.ts +252 -0
  162. package/src/web/src/ui-catalog.ts +92 -0
  163. package/src/web/sw.js +71 -0
  164. package/src/web/vendor/README.md +61 -0
  165. package/src/web/vendor/hljs-LICENSE +29 -0
  166. package/src/web/vendor/hljs-theme.css +59 -0
  167. package/src/web/vendor/hljs.js +26 -0
  168. package/src/model/fused-decode-kernel.ts +0 -312
  169. package/src/model/fused-geglu-kernel.ts +0 -198
  170. package/src/model/fused-mlp-kernel.ts +0 -169
  171. package/src/model/fused-swiglu-kernel.ts +0 -240
  172. package/src/model/steel-linear-kernel.ts +0 -96
  173. package/src/status-page.html +0 -497
@@ -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
- > Verbs marked **(v0.0.9)** ship in v0.0.9; everything else is in v0.0.8.
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 status page and browser
21
- chat UI.
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-quant config\|off\|4\|8` | KV cache quantization: per-layer `kv_config.json`, bf16, or uniform bits (default `config`) |
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
- | `--batch <n>` | Continuous-batched bf16 serving (default 1 = serial) |
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` / `--l3` | Parity tier: bit-exact to mlx-lm / bit-exact to mlx-optiq / best performance |
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` — status page at `/`, browser chat at `/chat`. Full
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/--l3`,
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 **(v0.0.9)**
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 **(v0.0.9)**
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 **(v0.0.9)**
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 **(v0.0.9)**
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 **(v0.0.9)**
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 **(v0.0.9)**
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` **(v0.0.9)**.
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` (current: 0.0.4). Install or
160
- run without a permanent install:
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 wraps the same signed binary via a postinstall download
172
- step (it does not bundle the ~228 MB dylibs/metallib in the npm tarball
173
- itself). Publish to npm as part of a release:
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 # from repo root; package.json version must already be bumped
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` | ~61 MB |
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 → homebrew
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
- **Intentionally omitted** (TUI-only assets pi ships beside its own binary,
64
- never reached by the headless web chat):
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
- | `theme/*.json` | TUI color themes (loaded via `initTheme`; we pass `noThemes`) |
69
- | `assets/*.png` | TUI announcement/interactive images |
70
- | `export-html/*` | the `/export-html` command's template + vendor JS |
71
- | `docs/`, `examples/`, `README.md`, `CHANGELOG.md` | TUI help/doc browsing |
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`.
@@ -3,16 +3,19 @@
3
3
  The HTTP server is one consumer of a library-first API. Everything it
4
4
  does — generation, sampling, KV schemes, LoRA, vision, caching, memory
5
5
  admission — is importable directly into a Bun process. Published as
6
- `mlx-bun` on npm (0.0.4); `bunx mlx-bun` works out of the box, or
6
+ `mlx-bun` on npm; `bunx mlx-bun` works out of the box, or
7
7
  `import … from "mlx-bun"` for programmatic use.
8
8
 
9
- One rule everywhere: **one generation at a time per process** (one GPU,
10
- batch=1). The server serializes through a queue; do the same.
9
+ One rule everywhere: **one generation at a time per process** (one GPU).
10
+ The server serializes through a queue (or, under `--batch N`, its
11
+ continuous-batching scheduler — a server-lane feature, not a library
12
+ surface); direct library callers must do the same.
11
13
 
12
14
  ## Quick start
13
15
 
14
16
  ```ts
15
17
  import {
18
+ ensureNativeRuntime,
16
19
  loadModelConfig,
17
20
  Weights,
18
21
  createModel, // dispatches to Gemma4Model / MiniCPM5Model / Qwen35Model
@@ -21,6 +24,7 @@ import {
21
24
  generate,
22
25
  } from "mlx-bun"; // or "./src/index" in-repo
23
26
 
27
+ await ensureNativeRuntime();
24
28
  const dir = "/path/to/hf-snapshot"; // mlx-bun ls prints these
25
29
  const config = await loadModelConfig(dir);
26
30
  const model = createModel(await Weights.open(dir), config); // returns RuntimeModel
@@ -29,7 +33,7 @@ const template = await ChatTemplate.load(dir);
29
33
  // Gemma4Model and MiniCPM5Model can still be imported directly from "mlx-bun"
30
34
  // if you need the concrete type. Qwen35Model is NOT exported from the public
31
35
  // package — use createModel/RuntimeModel (preferred dispatch), or import from
32
- // "./src/model/qwen35" in-repo.
36
+ // "./src/model/qwen3_5" in-repo.
33
37
 
34
38
  const ids = tok.encode(template.render([{ role: "user", content: "hi" }]));
35
39
  const promptIds = ids[0] === ids[1] && ids[0] === tok.bosTokenId ? ids.slice(1) : ids;
@@ -44,6 +48,14 @@ console.log(gen.stats); // set once iteration ends
44
48
  Weight loading is lazy (mmap + mlx native loader): construction is
45
49
  milliseconds; weights materialize on first forward.
46
50
 
51
+ > **First run:** library consumers bypass the CLI's first-run step, so
52
+ > call `await ensureNativeRuntime()` once before constructing a model on
53
+ > a machine that may not have the MLX native runtime yet — it downloads
54
+ > the sha256-verified native pack to `~/Library/Caches/mlx-bun/` and is
55
+ > a no-op when the runtime is already present (beside the executable,
56
+ > already cached, or installed via homebrew). `nativeRuntimeDir()`
57
+ > returns the resolved directory, or `null` on a fresh machine.
58
+
47
59
  ## generate(model, promptTokens, options) → Generation
48
60
 
49
61
  `Generation` is an `AsyncIterable<GeneratedToken>` with a `stats`
@@ -64,13 +76,20 @@ after an early `break`, which cleanly cancels the in-flight step).
64
76
  | `kvConfig` | off | per-layer mixed precision from `kv_config.json` (`config.kvQuant`); overrides `kvBits`, start 0 — optiq serve semantics |
65
77
  | `adapters` | none | mounted LoRA adapter ids, applied for exactly this generation |
66
78
  | `promptEmbeddings` / `imageMask` | — | vision path (see `src/vision/`) |
79
+ | `logprobs` / `topLogprobs` | off | per-token logprob capture (mlx_lm.server parity): `logprobs` captures the emitted token's log-probability, `topLogprobs` (0/unset = off) captures the top-k (id, logprob) pairs — same full-vocab log-softmax distribution mlx-lm's generate_step samples from, taken after logits processors and before the sampler |
80
+ | `grammar` | off | a compiled `GrammarController` (`src/grammar.ts`) for structured output — masks invalid tokens to `-inf` each step (L2-class, oMLX oracle) |
81
+ | `turboQuant` | off | TurboQuant KV scheme (docs/design/turboquant-kv.md), a rotation-based alternative to uniform `kvBits`; mutually exclusive with `kvBits`/`kvConfig` |
82
+ | `pagedKv` | off | `{ blockSize? }` — OPTIONAL paged KV cache (docs/design/paged-kv-cache.md); v1 scope is serial batch=1 Gemma4-family bf16, mutually exclusive with `kvBits`/`kvConfig`/`turboQuant`/draft/compiled decode |
67
83
 
68
84
  `GenerateStats`: `promptTokens`, `cachedTokens`, `generatedTokens`,
69
85
  `prefillTps` / `prefillMs`, `decodeTps` / `decodeMs`, `cacheTokens`
70
86
  (the exact token sequence whose KV is in the cache — feed it to
71
87
  `PromptCache.put`). Timing semantics match mlx-lm: the prompt clock
72
88
  runs until the first token arrives (the prefill→decode boundary is
73
- prompt time), the decode clock from there.
89
+ prompt time), the decode clock from there. An optional `spec` field
90
+ carries speculative-decoding telemetry (serve `--draft-model` path
91
+ only): `drafted`, `accepted`, `targetCalls`, and per-draft-position
92
+ `draftedByPos` / `acceptedByPos` counts.
74
93
 
75
94
  ## Serving pieces
76
95
 
@@ -80,7 +99,7 @@ import { loadContext, createServer } from "mlx-bun";
80
99
  const ctx = await loadContext(dir, "my-model", { memoryBudgetBytes: 12e9 });
81
100
  const server = createServer(ctx, 8080, {
82
101
  promptCacheBytes: 2e9, // byte-capped LRU (never count-capped)
83
- kvQuant: undefined, // default: kv_config.json; "off" | bits
102
+ kvQuant: undefined, // default: kv_config.json; "off" | "config" | bits
84
103
  memoryBudgetBytes: 12e9, // admission control — the only OOM defense
85
104
  });
86
105
  ```
@@ -135,11 +154,13 @@ under one adapter must not seed another's prefill).
135
154
  ```ts
136
155
  import { saveKvCache, loadKvCache } from "./src/kv-store";
137
156
  saveKvCache("/tmp/prefix.kv", tokens, caches); // page-aligned file
138
- const { tokens, caches, mmap } = loadKvCache("/tmp/prefix.kv", model as Gemma4Model);
157
+ const { tokens, caches, mmap } = loadKvCache("/tmp/prefix.kv", model);
139
158
  // reload is a zero-copy MAP_PRIVATE mmap straight to the GPU (~1 ms);
140
159
  // keep `mmap` referenced as long as the caches live
141
- // Note: loadKvCache is typed to accept Gemma4Model, but only uses model.layers.length
142
- // at runtime — a cast from RuntimeModel is safe for any model that has a layers array.
160
+ // Note: loadKvCache accepts anything with makeCache(): Cache[] — it validates
161
+ // the entry count against the cache list makeCache() returns (not
162
+ // model.layers.length, which is wrong for KV-shared models like e4b, whose
163
+ // makeCache() returns donors only). RuntimeModel qualifies directly, no cast.
143
164
  ```
144
165
 
145
166
  Quantized caches are not persistable yet (documented gap).
@@ -42,18 +42,38 @@ mlx-bun memory unschedule
42
42
 
43
43
  ## Agent tools
44
44
 
45
- Both `mlx-bun pi` and the web chat expose these read-only tools:
45
+ Both `mlx-bun pi` and the web chat expose these read-only tools, in the recommended call order — FIND the article, READ it small, follow the graph, fall back to reference docs and utilities, search only as a last resort:
46
46
 
47
- - `memory_search`
48
- - `memory_read`
49
- - `memory_toc`
50
- - `memory_section`
51
- - `memory_links`
47
+ **FIND** (deterministic lookup, never a vector search):
48
+ - `memory_resolve` — name/alias → the article
49
+ - `memory_category` — category/type/series → members
50
+
51
+ **READ** (TOC → one section, not a full-article dump):
52
+ - `memory_read` — TOC + lead by default; `force=true` for the full article
53
+ - `memory_section` — one section's body (the default granularity)
54
+
55
+ **Follow the graph**:
56
+ - `memory_links` — outbound/inbound wikilinks
57
+ - `memory_infobox` — read an article's infobox fields (there is no infobox *query* tool — the infobox is content the model reads, not a filter)
58
+
59
+ **Reference docs** (mlx-bun's own docs, mirrored read-only into the vault):
60
+ - `reference_search`
61
+ - `reference_read`
62
+ - `reference_list`
63
+
64
+ **Utility**:
52
65
  - `memory_list` — user articles plus read-only `Reference/*` docs
53
66
  - `memory_status` — vault path, setup state, article count, git state, last synthesis run (from the vault's git log), schedule state, recent changed articles
54
67
 
68
+ **Last resort**:
69
+ - `memory_search` — substring fallback when FIND failed
70
+
55
71
  The tools are always registered. If no vault exists, they return a setup message instead of disappearing. Search/read results prefixed with `Reference/` are mlx-bun docs, not user memory articles.
56
72
 
73
+ ## Web chat REST surface
74
+
75
+ The web chat's Memory panel talks to a separate, HTTP-only `/api/memory/*` route surface (status/list/search/article/links/history/diff + a `POST /api/memory/init` consent-card endpoint) — thin wrappers over the same vault read path above, documented in [server-api.md](server-api.md#get-apimemory--post-apimemoryinit). The agent-tool surface above is unaffected by it.
76
+
57
77
  ## Obsidian
58
78
 
59
79
  ```bash
@@ -79,6 +79,8 @@ Schema (table `models`, one row **per snapshot**, keyed on `path`):
79
79
  | `quant_bits`, `quant_group_size`, `quant_mode` | quantization |
80
80
  | `has_vision_sidecar` | sidecar file present |
81
81
  | `vision_config_type` | `config.json`'s `vision_config.model_type` when it names a vision tower (`*_vision`) |
82
+ | `has_audio_config` | `config.json` declares an `audio_config` block |
83
+ | `has_audio_tower` | sidecar header names `audio_tower.*` tensors |
82
84
  | `has_kv_config`, `has_tool_template`, `license` | capabilities/terms |
83
85
  | `scanned_at` | freshness (canonical tie-break) |
84
86
 
@@ -89,6 +91,14 @@ Schema (table `models`, one row **per snapshot**, keyed on `path`):
89
91
  Presence of a `vision_config` key alone is **not** a signal — Qwen3.5
90
92
  nests a copy of its own text config there.
91
93
 
94
+ **Audio capability** is `has_audio_config AND has_audio_tower`: both
95
+ legs are required because the 12B OptiQ snapshot carries an
96
+ `audio_config` but a stub sidecar whose only audio entry is
97
+ `embed_audio.embedding_projection.weight` (no `audio_tower.*` Conformer
98
+ tensors) — config presence alone would advertise audio that fails at
99
+ request time. The check reads only the sidecar's safetensors header,
100
+ never tensor bytes.
101
+
92
102
  ## Query resolution rules (per verb)
93
103
 
94
104
  Every verb resolves fuzzy queries the same way, with revision collapsing
@@ -98,7 +108,7 @@ so a stale snapshot never makes a repo "ambiguous":
98
108
  row per repo** (the canonical revision). `--all-revisions` shows the
99
109
  per-snapshot truth with the canonical marked `*`. Capabilities column
100
110
  labels the support tier: `supported (targeted)` (dedicated forward,
101
- L2/L3 paths) vs `supported (generic)` (Tier-0 universal module).
111
+ L2/opt-in paths) vs `supported (generic)` (Tier-0 universal module).
102
112
  - **`fit` / `serve` / `train` / … (single-model verbs)** — `resolve()`:
103
113
  the query must match exactly one **repo**; multiple cached revisions of
104
114
  that repo collapse to the canonical snapshot (refs/main, else most
@@ -14,9 +14,9 @@ mlx-bun train <model> --data <dir>
14
14
  - `<model>` is a query or snapshot path (e.g. `e4b`, `MiniCPM5`); omit it to auto-pick.
15
15
  - `--data <dir>` is a directory with `train.jsonl` (+ optional `valid.jsonl`); rows are
16
16
  `{"prompt": "...", "chosen": "...", "rejected": "..."}`.
17
- - The command **auto-detects e4b/Gemma** and sets its required training env flags
18
- (`MLX_BUN_PERF_KERNEL=0`, `MLX_BUN_FUSED_GELU=0`) before loading the model — you don't
19
- set them yourself. The full stack (flash head + prefix-share + segmented) is on by default.
17
+ - The full stack (flash head + prefix-share + segmented) is on by default. No env-flag
18
+ setup is needed for any model (the old Gemma training-flag requirement is gone —
19
+ the no-vjp kernels were deleted 2026-07-05).
20
20
  - It prints a plan box (dataset counts, format, the resolved knobs), streams per-step loss
21
21
  + peak memory, and saves a **mountable** adapter (`mlx-bun serve <model> --adapter <dir>`).
22
22
  - `--dry-run` inspects the dataset and prints the plan without training. `mlx-bun help train`
@@ -59,7 +59,6 @@ mlx-bun train e4b --data ./prefs --resume ~/.cache/mlx-bun/mlx-bun-finetunes/orp
59
59
  MODEL=/path/to/snapshot DATA=/path/to/datadir bun scripts/train-orpo.ts
60
60
  ```
61
61
  - `DATA` is a directory with `train.jsonl` (+ optional `valid.jsonl`).
62
- - Auto-detects e4b/Gemma and sets the training env flags, same as the CLI.
63
62
 
64
63
  ### Preconfigured defaults (override via env)
65
64
  | env | default | meaning |