mlx-bun 0.0.4

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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +362 -0
  3. package/THIRD_PARTY_LICENSES.md +41 -0
  4. package/bin/mlx-bun.mjs +32 -0
  5. package/docs/curve-designer.html +303 -0
  6. package/docs/design/adapters-end-to-end.md +201 -0
  7. package/docs/design/compat-cli-surface-design.md +227 -0
  8. package/docs/design/docs-reorg-plan.md +152 -0
  9. package/docs/design/hlg-sampling.md +340 -0
  10. package/docs/design/memory-system.md +339 -0
  11. package/docs/design/optimization_plan.md +344 -0
  12. package/docs/design/parallel-slots.md +515 -0
  13. package/docs/design/runtime-isolation.md +247 -0
  14. package/docs/design/segmented-backward-training.md +508 -0
  15. package/docs/investigations/chunk-finetune-experiment.md +84 -0
  16. package/docs/investigations/curve-bisector-routing.md +123 -0
  17. package/docs/investigations/curve-runs/bisector-route.json +98 -0
  18. package/docs/investigations/curve-runs/bisector-trace.json +1393 -0
  19. package/docs/investigations/curve-runs/curve-terrain.json +1179 -0
  20. package/docs/investigations/curve-runs/curve-terrain.svg +42 -0
  21. package/docs/investigations/curve-terrain.html +105 -0
  22. package/docs/investigations/expert-offload-single-user-moe.md +292 -0
  23. package/docs/investigations/hlg-ab.html +34 -0
  24. package/docs/investigations/hlg-explorer.html +238 -0
  25. package/docs/investigations/hlg-figs/cliff.svg +1 -0
  26. package/docs/investigations/hlg-figs/cross.svg +1 -0
  27. package/docs/investigations/hlg-figs/edges.svg +1 -0
  28. package/docs/investigations/hlg-figs/k20-grids.svg +1 -0
  29. package/docs/investigations/hlg-figs/k20-peaks.svg +1 -0
  30. package/docs/investigations/hlg-figs/lw-curve.svg +1 -0
  31. package/docs/investigations/hlg-figs/terrain.svg +1 -0
  32. package/docs/investigations/hlg-report.html +262 -0
  33. package/docs/investigations/hlg-runs/hlg-ab.json +149 -0
  34. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +87 -0
  35. package/docs/investigations/hlg-runs/hlg-blind.json +107 -0
  36. package/docs/investigations/hlg-runs/hlg-cliff.json +197 -0
  37. package/docs/investigations/hlg-runs/hlg-cross.json +505 -0
  38. package/docs/investigations/hlg-runs/hlg-edge.json +388 -0
  39. package/docs/investigations/hlg-runs/hlg-eval1.json +25 -0
  40. package/docs/investigations/hlg-runs/hlg-frontier.json +86 -0
  41. package/docs/investigations/hlg-runs/hlg-grid.json +242 -0
  42. package/docs/investigations/hlg-runs/hlg-grid2.json +343 -0
  43. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +110 -0
  44. package/docs/investigations/hlg-runs/hlg-map.json +383 -0
  45. package/docs/investigations/hlg-runs/run0-original.log +159 -0
  46. package/docs/investigations/hlg-runs/runA-recommended.log +131 -0
  47. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +26 -0
  48. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +18 -0
  49. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +25 -0
  50. package/docs/investigations/hlg-runs/runAD-ab-samples.log +8 -0
  51. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +51 -0
  52. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +10 -0
  53. package/docs/investigations/hlg-runs/runAG-eval-config2.log +16 -0
  54. package/docs/investigations/hlg-runs/runB-gain-variants.log +192 -0
  55. package/docs/investigations/hlg-runs/runC-pivots.log +162 -0
  56. package/docs/investigations/hlg-runs/runD-oetf.log +132 -0
  57. package/docs/investigations/hlg-runs/runE-eotf.log +162 -0
  58. package/docs/investigations/hlg-runs/runF-shaper-working.log +86 -0
  59. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +101 -0
  60. package/docs/investigations/hlg-runs/runH-wsweep.log +176 -0
  61. package/docs/investigations/hlg-runs/runI-asweep.log +176 -0
  62. package/docs/investigations/hlg-runs/runJ-knobmap.log +49 -0
  63. package/docs/investigations/hlg-runs/runK-frontier.log +19 -0
  64. package/docs/investigations/hlg-runs/runL-widemap.log +65 -0
  65. package/docs/investigations/hlg-runs/runM-frontier-emb.log +15 -0
  66. package/docs/investigations/hlg-runs/runN-verify.log +116 -0
  67. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +15 -0
  68. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +15 -0
  69. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +15 -0
  70. package/docs/investigations/hlg-runs/runR-blind-transcript.log +79 -0
  71. package/docs/investigations/hlg-runs/runS-cross-coherence.log +33 -0
  72. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +33 -0
  73. package/docs/investigations/hlg-runs/runU-cross-12b.log +33 -0
  74. package/docs/investigations/hlg-runs/runV-lw-e4b.log +22 -0
  75. package/docs/investigations/hlg-runs/runW-lw-12b.log +22 -0
  76. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +22 -0
  77. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +26 -0
  78. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +26 -0
  79. package/docs/investigations/hlg-sampling-investigation.md +970 -0
  80. package/docs/investigations/lab-build-journal.md +157 -0
  81. package/docs/investigations/pi-builtin-investigation.md +308 -0
  82. package/docs/investigations/starter-model-port-handoff.md +323 -0
  83. package/docs/planning/IDEAS.md +26 -0
  84. package/docs/planning/PRODUCT_ROADMAP.md +426 -0
  85. package/docs/planning/ResearchTopics.md +119 -0
  86. package/docs/planning/chunk-task-roadmap.md +77 -0
  87. package/docs/planning/journal.md +155 -0
  88. package/docs/reference/distribution.md +121 -0
  89. package/docs/reference/embedding.md +168 -0
  90. package/docs/reference/library-api.md +153 -0
  91. package/docs/reference/server-api.md +224 -0
  92. package/docs/reference/server-config.md +243 -0
  93. package/docs/segmented-backward-handoff.md +148 -0
  94. package/package.json +68 -0
  95. package/src/anthropic.ts +491 -0
  96. package/src/chat-template.ts +186 -0
  97. package/src/cli.ts +1075 -0
  98. package/src/config.ts +229 -0
  99. package/src/curve-sampler.ts +122 -0
  100. package/src/dataset/generators.ts +1087 -0
  101. package/src/dataset/index.ts +12 -0
  102. package/src/dataset/job.ts +81 -0
  103. package/src/dataset/llm.ts +71 -0
  104. package/src/dataset/registry.ts +528 -0
  105. package/src/download.ts +283 -0
  106. package/src/eval/README.md +86 -0
  107. package/src/eval/kl-prompts.ts +29 -0
  108. package/src/eval/kl.ts +294 -0
  109. package/src/eval/quality-db.ts +92 -0
  110. package/src/eval/runner.ts +102 -0
  111. package/src/eval/score.ts +49 -0
  112. package/src/eval/tasks/bfcl.ts +500 -0
  113. package/src/eval/tasks/gsm8k.ts +85 -0
  114. package/src/eval/tasks/hashhop.ts +109 -0
  115. package/src/eval/tasks/humaneval.ts +252 -0
  116. package/src/eval/tasks/ifeval.ts +354 -0
  117. package/src/eval/tasks/mmlu.ts +145 -0
  118. package/src/evaldb.ts +199 -0
  119. package/src/expert-offload-build.ts +77 -0
  120. package/src/expert-offload.ts +61 -0
  121. package/src/expert-trace.ts +67 -0
  122. package/src/fit.ts +254 -0
  123. package/src/generate.ts +451 -0
  124. package/src/harness-pi.ts +177 -0
  125. package/src/hf-push.ts +628 -0
  126. package/src/index.ts +22 -0
  127. package/src/jobs/db.ts +139 -0
  128. package/src/jobs/index.ts +29 -0
  129. package/src/jobs/job-entry.ts +106 -0
  130. package/src/jobs/runner.ts +304 -0
  131. package/src/jobs/sse.ts +114 -0
  132. package/src/jobs/types.ts +58 -0
  133. package/src/kv-store.ts +185 -0
  134. package/src/lora.ts +314 -0
  135. package/src/md-modules.d.ts +7 -0
  136. package/src/mlx/array.ts +237 -0
  137. package/src/mlx/autograd.ts +267 -0
  138. package/src/mlx/checkpoint.ts +116 -0
  139. package/src/mlx/compile.ts +158 -0
  140. package/src/mlx/custom-vjp.ts +140 -0
  141. package/src/mlx/ffi.ts +433 -0
  142. package/src/mlx/metal-kernel.ts +130 -0
  143. package/src/mlx/ops.ts +707 -0
  144. package/src/mmap.ts +90 -0
  145. package/src/model/batched-mask.ts +204 -0
  146. package/src/model/batched-rotating.ts +300 -0
  147. package/src/model/compiled-decode.ts +681 -0
  148. package/src/model/factory.ts +27 -0
  149. package/src/model/fingerprint.ts +33 -0
  150. package/src/model/flash-attention.ts +674 -0
  151. package/src/model/fused-decode-kernel.ts +305 -0
  152. package/src/model/fused-geglu-kernel.ts +87 -0
  153. package/src/model/gemma4-base.ts +1616 -0
  154. package/src/model/gemma4.ts +1012 -0
  155. package/src/model/generated/gemma4-12b.ts +526 -0
  156. package/src/model/generated/gemma4-26b.ts +500 -0
  157. package/src/model/generated/gemma4-e4b.ts +757 -0
  158. package/src/model/generated/index.ts +18 -0
  159. package/src/model/minicpm5.ts +278 -0
  160. package/src/model/qwen3-delta.ts +224 -0
  161. package/src/model/qwen3_5.ts +429 -0
  162. package/src/model/support.ts +46 -0
  163. package/src/native-pack.ts +152 -0
  164. package/src/pi-provider.ts +95 -0
  165. package/src/pi-terminal.ts +264 -0
  166. package/src/pi-web.ts +924 -0
  167. package/src/preflight.ts +123 -0
  168. package/src/prompt-cache.ts +113 -0
  169. package/src/quantize/allocator.ts +627 -0
  170. package/src/quantize/calibration.ts +180 -0
  171. package/src/quantize/config-writer.ts +132 -0
  172. package/src/quantize/index.ts +57 -0
  173. package/src/quantize/job.ts +136 -0
  174. package/src/quantize/quantizer.ts +462 -0
  175. package/src/quantize/safetensors-writer.ts +171 -0
  176. package/src/quantize/sensitivity.ts +234 -0
  177. package/src/registry.ts +336 -0
  178. package/src/responses.ts +774 -0
  179. package/src/safetensors.ts +158 -0
  180. package/src/sampler.ts +517 -0
  181. package/src/serve/batch-scheduler.ts +362 -0
  182. package/src/serve/generation-gateway.ts +160 -0
  183. package/src/server.ts +1891 -0
  184. package/src/spec/drafter.ts +261 -0
  185. package/src/spec/generate.ts +221 -0
  186. package/src/status-page.html +497 -0
  187. package/src/tokenizer.ts +38 -0
  188. package/src/tool-call.ts +242 -0
  189. package/src/train/dataset.ts +461 -0
  190. package/src/train/export.ts +32 -0
  191. package/src/train/forward.ts +229 -0
  192. package/src/train/index.ts +43 -0
  193. package/src/train/job.ts +148 -0
  194. package/src/train/lora-params.ts +194 -0
  195. package/src/train/loss.ts +342 -0
  196. package/src/train/merge.ts +195 -0
  197. package/src/train/optimizer.ts +176 -0
  198. package/src/train/rank.ts +101 -0
  199. package/src/train/segmented.ts +520 -0
  200. package/src/train/trainer.ts +600 -0
  201. package/src/tui.ts +161 -0
  202. package/src/vision/embedder.ts +168 -0
  203. package/src/vision/preprocess.ts +247 -0
  204. package/src/vision/prompt.ts +158 -0
  205. package/src/vision/siglip.ts +599 -0
  206. package/src/web/app.html +2471 -0
  207. package/src/web/skills/web-research/SKILL.md +44 -0
  208. package/src/web/skills.ts +59 -0
  209. package/src/web-tools.ts +426 -0
  210. package/src/weights.ts +91 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Josh Rossi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,362 @@
1
+ # mlx-bun
2
+
3
+ Native MLX inference for Bun. Run quantized LLMs on Apple Silicon from
4
+ TypeScript — no Python, no sidecar server, one runtime.
5
+
6
+ Measured head-to-head on an M4 Pro (24 GB), same models, same day:
7
+ logits **bit-exact** against the Python reference; served over HTTP the
8
+ **fastest stack on every model tested** (TTFT 45–89 ms vs python's
9
+ 220–327 ms, zero server overhead vs mlx-lm's −5–6%); prefill up to
10
+ **1.8×** mlx-lm at 8k context; cold start → first token of a cached
11
+ prompt in **394 ms**. Full table: [benchmarks](#benchmarks).
12
+
13
+ ## Getting started
14
+
15
+ You need an Apple Silicon Mac (MLX is Metal-only, so macOS only by
16
+ design). Three ways in.
17
+
18
+ ### Homebrew (recommended)
19
+
20
+ A self-contained, signed + notarized binary — no toolchain to install by
21
+ hand:
22
+
23
+ ```sh
24
+ brew install joshuarossi/tap/mlx-bun
25
+ mlx-bun
26
+ ```
27
+
28
+ That's the whole thing. The bottle already bundles the MLX native
29
+ runtime, so the only thing the first run fetches is the model.
30
+
31
+ ### bunx (no install)
32
+
33
+ Already have [Bun](https://bun.sh) ≥ 1.3.14? Run it straight from npm,
34
+ nothing to install:
35
+
36
+ ```sh
37
+ bunx mlx-bun
38
+ ```
39
+
40
+ First run fetches the MLX native runtime (~52 MB) and the model into your
41
+ caches, then serves. (Bun only — `npx mlx-bun` under Node exits with a
42
+ "requires Bun" notice by design.)
43
+
44
+ ### From source
45
+
46
+ Needs [Bun](https://bun.sh) ≥ 1.3.14 — no Python, no native library to
47
+ install by hand:
48
+
49
+ ```sh
50
+ # 1. Install Bun
51
+ curl -fsSL https://bun.sh/install | bash
52
+ exec $SHELL -l
53
+
54
+ # 2. Clone, install, link the CLI
55
+ git clone https://github.com/joshuarossi/mlx-bun.git && cd mlx-bun
56
+ bun install
57
+ bun run link-cli # adds the `mlx-bun` command — or run `bun src/cli.ts <verb>`
58
+
59
+ # 3. Run it — that's the whole thing
60
+ mlx-bun
61
+ ```
62
+
63
+ Bare `mlx-bun` is an alias for `mlx-bun serve`. On its first run, with no
64
+ model named, it does everything for you:
65
+
66
+ 1. pulls the MLX native runtime (~52 MB) into `~/Library/Caches/mlx-bun/`
67
+ (skipped on the Homebrew install — the runtime ships in the bottle);
68
+ 2. downloads the sub-GB `MiniCPM5-1B` starter and serves it, so you're
69
+ chatting in well under a minute;
70
+ 3. starts downloading `gemma-4-e4b` (the stronger 4B model) in the
71
+ background — it becomes the default on your next `mlx-bun serve`, on a
72
+ 16 GB+ Mac;
73
+ 4. opens the chat UI in your browser at
74
+ [`http://localhost:8090/#/chat`](http://localhost:8090/#/chat)
75
+ (pass `--no-open` to skip).
76
+
77
+ Want a specific model instead? Name it — `mlx-bun serve e4b` (substring
78
+ match against your downloaded models), grabbing it first with
79
+ `mlx-bun get <repo-id>` if you don't have it. See
80
+ [Supported models](#supported-models) for the full list and what fits
81
+ your machine.
82
+
83
+ **Prefer the API?** It's OpenAI-compatible — any OpenAI client works:
84
+
85
+ ```sh
86
+ curl http://localhost:8090/v1/chat/completions \
87
+ -H 'Content-Type: application/json' \
88
+ -d '{
89
+ "messages": [{"role": "user", "content": "Hello!"}],
90
+ "max_tokens": 128, "temperature": 0.7
91
+ }'
92
+ ```
93
+
94
+ That's it. The server serves the one model it was started with (the
95
+ request's `model` field is ignored and the loaded model id is echoed
96
+ back in responses).
97
+
98
+ ## Supported models
99
+
100
+ Scope is deliberate: a few model families held to **bit-exact** logit
101
+ parity with the Python reference, rather than dozens held to none.
102
+ Currently MiniCPM5 plus the Gemma-4 OptiQ quants:
103
+
104
+ | Model | Download | Fits on | Vision | Notes |
105
+ |---|---|---|---|---|
106
+ | [`mlx-community/MiniCPM5-1B-OptiQ-4bit`](https://huggingface.co/mlx-community/MiniCPM5-1B-OptiQ-4bit) | 0.92 GB | 8 GB | — | Sub-GB option for 8 GB machines; bit-exact 100-step oracle parity (bf16 + mixed KV), tool calling + agent loop verified |
107
+ | [`mlx-community/gemma-4-e4b-it-OptiQ-4bit`](https://huggingface.co/mlx-community/gemma-4-e4b-it-OptiQ-4bit) | 7.0 GB | 16 GB | — | **Recommended starter** (16 GB+); ~54 tok/s |
108
+ | [`mlx-community/gemma-4-12B-it-OptiQ-4bit`](https://huggingface.co/mlx-community/gemma-4-12B-it-OptiQ-4bit) | 8.4 GB | 16 GB | ✓ | Vision sidecar + tool calling, both verified end-to-end |
109
+ | [`mlx-community/gemma-4-26B-A4B-it-OptiQ-4bit`](https://huggingface.co/mlx-community/gemma-4-26B-A4B-it-OptiQ-4bit) | 18 GB | 24 GB | — | MoE (top-8 of 128 experts); ~54 tok/s — the python servers crash loading it on 24 GB |
110
+
111
+ Not sure what fits your machine? `bun src/cli.ts fit <model> --ctx 8192`
112
+ gives a deterministic answer (see below). Qwen 3.x is next on the
113
+ roadmap (see [PLAN.md](./PLAN.md)).
114
+
115
+ ## Why
116
+
117
+ MLX is Apple's ML framework: hand-tuned Metal kernels for Apple Silicon,
118
+ with official bindings for Python, C++, Swift, and C — but no JavaScript
119
+ story. Today, a JS/TS app that wants local MLX inference must shell out to
120
+ a Python server (mlx-lm, optiq) and accept that stack's fragility: venv
121
+ setup, brittle download tooling, segfaults on exit, monkey-patched HTTP
122
+ layers.
123
+
124
+ The performance-critical work — every matmul, every attention pass — lives
125
+ in MLX's C++/Metal core and is exposed through `mlx-c`. The Python layer on
126
+ top is pure orchestration: model loading, tokenization, the sampling loop,
127
+ serving. That layer is performance-neutral (the GPU dominates), so it can
128
+ be rewritten in any runtime without losing speed — and Bun is the right
129
+ one:
130
+
131
+ - **`bun:ffi`** — lowest-overhead FFI of any JS runtime; binds `mlx-c`
132
+ directly, no node-gyp, no binding compilation.
133
+ - **Lazy native weight loading** — mlx's loader materializes tensors on
134
+ first use; opening an 8.9 GB model takes milliseconds, and warm
135
+ restarts are near-instant on cached pages.
136
+ - **`Bun.Image`** — native OS image codecs (HEIC, AVIF, WebP, JPEG, …)
137
+ for the vision path, EXIF auto-orientation included.
138
+ - **`bun:sqlite`** — built-in storage for the model registry and eval DB.
139
+ - **One binary, one toolchain** — runtime, package manager, test runner.
140
+
141
+ ## CLI
142
+
143
+ Commands are shown as `bun src/cli.ts <verb>` (runs straight from a
144
+ clone). Once installed/linked, `mlx-bun <verb>` is the identical command.
145
+
146
+ ```sh
147
+ bun src/cli.ts get mlx-community/gemma-4-12B-it-OptiQ-4bit # resumable, verified download
148
+ bun src/cli.ts scan # index your HF cache into the registry
149
+ bun src/cli.ts ls # list models (size, params, quant, capabilities)
150
+ bun src/cli.ts ls --vision --max-size 10GB
151
+ bun src/cli.ts fit gemma --ctx 32768 # memory contract: fits? max context? predicted tok/s
152
+ bun src/cli.ts fit gemma --ctx 8192 --skus # ...same, across the Apple Silicon lineup
153
+ bun src/cli.ts serve gemma --port 8090 # OpenAI-compatible server
154
+ bun src/cli.ts serve gemma --memory-budget 18 # ...with admission control (GB)
155
+ bun src/cli.ts evals # recorded benchmark runs
156
+ ./benchmark.sh # head-to-head matrix vs mlx-lm/optiq (reboot first;
157
+ # preflight-gated, resumable, writes benchmarks-h2h-<date>.md)
158
+ ```
159
+
160
+ Model arguments are substring queries against the registry (`e4b`,
161
+ `26B`, `12B-it`); a query matching more than one model errors out
162
+ listing the candidates — just make it more specific.
163
+
164
+ `fit` is deterministic, not vibes: weights bytes from safetensors
165
+ headers, KV bytes/token from the config (sliding-window layers capped,
166
+ MoE active-expert bytes for decode), calibrated prefill transient, and
167
+ the machine's wired-memory ceiling. Predictions are recorded next to
168
+ measured peaks in the eval DB.
169
+
170
+ ## HTTP API
171
+
172
+ `serve` exposes an OpenAI-compatible API. Anything that speaks the
173
+ OpenAI protocol can point at it — the OpenAI SDK
174
+ (`baseURL: "http://localhost:8090/v1"`, any non-empty `apiKey`), or
175
+ agent CLIs like pi/OpenClaw via their provider config.
176
+
177
+ - **`POST /v1/chat/completions`** — streaming (SSE) and non-streaming;
178
+ `temperature`, `top_p`, `top_k`, `max_tokens`, `seed`,
179
+ `repetition_penalty`, `stop` (string or array, matched on decoded
180
+ text with streaming hold-back); omitted sampling fields default to
181
+ the model's own `generation_config.json` recipe; usage includes
182
+ `cached_tokens`. Full schemas in
183
+ [docs/reference/server-api.md](./docs/reference/server-api.md); start
184
+ flags and the `--batch N` compatibility matrix in
185
+ [docs/reference/server-config.md](./docs/reference/server-config.md).
186
+ - **Tool calling** — pass OpenAI `tools`; each family's native format
187
+ is parsed into `tool_calls` JSON with `finish_reason: "tool_calls"`
188
+ (Gemma 4 `<|tool_call>` sentinel tokens; MiniCPM5
189
+ `<function name=…>` XML with schema-aware argument decoding);
190
+ `role: "tool"` round-trips, including multi-turn agent loops.
191
+ - **Vision** — `image_url` content parts (data: URLs or http/s), on
192
+ models with the vision sidecar. PNG, JPEG, HEIC, AVIF, WebP, TIFF,
193
+ GIF, BMP via native OS codecs.
194
+ - **Prompt caching** — a byte-capped LRU KV cache reuses the longest
195
+ common token prefix across requests (multi-turn conversations
196
+ re-prefill only the new turn) — automatic, no client changes.
197
+ Entries are adapter-specific when LoRA adapters are in play.
198
+ - **LoRA hot-swap** — mount adapters at startup (`--adapter id=dir`)
199
+ or at runtime (`POST /v1/adapters`), select per request with the
200
+ `adapter` body field (`"id"`, stacked `"a+b"`, or `"none"`). The
201
+ base model never reloads; an unselected adapter costs nothing.
202
+ - **Mixed-precision KV** — when the model repo ships `kv_config.json`
203
+ (every Gemma-4 OptiQ repo does), per-layer KV quantization applies
204
+ automatically (full-attention layers; sliding layers stay bf16).
205
+ Measured decode-neutral at 8k on the 12B with KV bytes ÷4 on the
206
+ quantized layers. `--no-kv-quant` forces bf16; `--kv-bits N` forces
207
+ uniform bits. Long prefills over quantized caches run a fused
208
+ FlashAttention-2 tiling that never materializes the full scores
209
+ matrix (bounded transient; `MLX_BUN_NO_FUSED_SDPA=1` to disable).
210
+ `MLX_BUN_FUSED_DECODE=1` extends the tiling to single-token decode —
211
+ experimental, default off (measured ~4% slower on Gemma @8k kv8;
212
+ kept for model families where it may win).
213
+ - **Memory admission control** — `--memory-budget <GB>` refuses to load
214
+ a model that can't serve within the budget and rejects requests whose
215
+ `prompt + max_tokens` exceed the budget's max safe context with a 400
216
+ (`type: "memory_admission"`) *before* generating. The GPU
217
+ out-of-memory crash it prevents is uncatchable by design — rejection
218
+ up front is the only defense. Ceiling visible at `GET /stats`.
219
+ - **Anthropic Messages API** (`POST /v1/messages`, on by default) —
220
+ point any Anthropic-SDK tool at the server
221
+ (`ANTHROPIC_BASE_URL=http://localhost:8090`); Claude Code runs
222
+ against it as a fully local backend. Streaming event grammar, native
223
+ tool_use/tool_result mapping, image blocks via the vision path.
224
+ - **OpenAI Responses API** (`POST /v1/responses`) — the protocol
225
+ Codex/Cursor/Continue speak; includes `previous_response_id`
226
+ resumption backed by a TTL + byte-capped store (visible in /stats).
227
+ - **`GET /v1/models`**, **`GET /stats`** (cache hit rates, bytes,
228
+ active KV scheme, response store), **`GET/POST/DELETE /v1/adapters`**.
229
+
230
+ ## Library
231
+
232
+ The server is one consumer of a library-first API. Not yet published to
233
+ npm — clone the repo and import from `src/`. Full reference:
234
+ [docs/reference/library-api.md](./docs/reference/library-api.md). For shipping inside a
235
+ Mac app (Tauri/Electron sidecar), `./scripts/build-binary.sh` produces
236
+ a relocatable single-binary bundle — recipe incl. signing/notarization
237
+ in [docs/reference/embedding.md](./docs/reference/embedding.md).
238
+
239
+ ```ts
240
+ import { loadModelConfig } from "./src/config";
241
+ import { Weights } from "./src/weights";
242
+ import { Gemma4Model } from "./src/model/gemma4";
243
+ import { generate } from "./src/generate";
244
+ import { loadTokenizer } from "./src/tokenizer";
245
+ import { ChatTemplate } from "./src/chat-template";
246
+
247
+ const dir = "/path/to/hf/snapshot";
248
+ const model = new Gemma4Model(await Weights.open(dir), await loadModelConfig(dir));
249
+ const tok = await loadTokenizer(dir);
250
+ const template = await ChatTemplate.load(dir);
251
+
252
+ const ids = tok.encode(template.render([{ role: "user", content: "Hi!" }]));
253
+ const gen = generate(model, ids, {
254
+ maxTokens: 256,
255
+ temperature: 0.7, topP: 0.95, seed: 42, // reproducible sampling
256
+ kvBits: 8, quantizedKvStart: 4096, // optional: quantized KV past 4k
257
+ });
258
+ for await (const t of gen) process.stdout.write(tok.decode([t.token], true));
259
+ console.log(gen.stats); // prompt/decode tok/s, cached tokens, ...
260
+ ```
261
+
262
+ KV caches can be persisted to disk (page-aligned files that reload as
263
+ zero-copy GPU-safe mmaps — see `src/kv-store.ts`) so a standard agent
264
+ preamble prefills once, ever.
265
+
266
+ ## Benchmarks
267
+
268
+ Head-to-head against the Python stacks (mlx-lm 0.31.3, mlx-optiq 0.2.1),
269
+ same machine (M4 Pro 24 GB), same day, same HF snapshots, preflight-gated
270
+ clean machine, median-of-N with warmups discarded. Full curated table
271
+ (parity / performance / quality) with per-row provenance:
272
+ [benchmarks/RESULTS.md](./benchmarks/RESULTS.md).
273
+
274
+ | | mlx-bun | mlx-lm | optiq |
275
+ |---|---|---|---|
276
+ | **TTFT, served (warm)** | **45–90 ms** | 219–224 ms | 222–331 ms |
277
+ | **server start → ready** | **0.36–0.47 s** | 0.76–0.98 s | 0.79–1.00 s |
278
+ | **decode through HTTP** (e4b / 12B / 26B) | **54.5** / 25.2 / **54.9** | 53.5 / — / 52.2 | 53.5 / **25.5** / † |
279
+ | **server tax vs own direct decode** | **≈ 0%** | −5…−7% | ≈ 0% |
280
+ | **direct decode** (engine only) | −1.9…−4.4% vs mlx-lm | baseline | −0.8…−1.2% |
281
+ | **12B decode @8k context** | 23.3 (23.0 kv-mixed) | **24.4** | 23.2 kv-mixed |
282
+
283
+ Honest negatives, same table: in this matrix our direct decode trailed
284
+ mlx-lm on every model (12B −1.9%, 26B −2.9%, e4b −4.4% at short
285
+ context; the 12B gap grew to −4.5% @8k), and optiq's served 12B edges
286
+ ours by ~1% (25.5 vs 25.2) while paying 3.7× the TTFT. **Post-matrix
287
+ (2026-06-11) the decode gap was root-caused and fixed** — a
288
+ prefill→decode allocator-reclaim stall that mlx-lm clears with
289
+ `mx.clear_cache` and bills to prompt time (we billed it to decode);
290
+ after the reference-faithful fix, same-session paired runs put the 12B
291
+ *ahead* at short context (25.1 vs 24.0) and at parity @8k (23.8 vs
292
+ 23.9). Clean-machine re-measure pending; e4b retains a ~5% per-step
293
+ host-overhead residual (see PLAN.md "Decode gap RESOLVED"). Served through HTTP — how agents actually use a local
294
+ model — mlx-bun has the fastest decode on e4b and the 26B, and the
295
+ fastest TTFT and startup everywhere by 2–5×. † = cell failed: optiq
296
+ serve produced no output on the 26B (the Metal OOM crash class from
297
+ python's non-lazy load transient — reproduced in isolation; mlx-bun
298
+ and mlx-lm both served the same model from the same machine state).
299
+ One further optiq cell is blocked on an upstream optiq bug; both are
300
+ documented in the results file.
301
+
302
+ These numbers are the 2026-06-11 cleared-machine re-run with the
303
+ long-context guard active (every @8k row verified at its requested
304
+ context — an earlier harness bug had fed python @8k baselines
305
+ ~31-token prompts; fixed, guarded, and re-measured).
306
+
307
+ ## Correctness
308
+
309
+ Logit parity with mlx-lm (same weights, Python reference) is the
310
+ project's oracle. The test suite holds the forward pass **bit-exact**
311
+ against it — including every quantized-KV configuration (kv8, kv4, and
312
+ the 26B's mixed per-layer scheme) and the fused quantized-attention
313
+ prefill, which is bit-exact against optiq's reference implementation.
314
+ Every ported helper follows the reference implementation's exact op
315
+ composition, down to constants built at load time (the one latent
316
+ divergence ever found — rope frequencies computed host-side instead of
317
+ on-device — was root-caused and fixed in Phase 10; see PLAN.md
318
+ findings). Golden files are regenerated only by explicit scripts
319
+ (`scripts/regen-*.ts`) running the Python oracle.
320
+
321
+ ```sh
322
+ bun test # fast tier runs everywhere; model-loaded tests auto-skip
323
+ # unless the reference snapshot is in your HF cache
324
+ ```
325
+
326
+ ## Troubleshooting
327
+
328
+ - **`dlopen` / `libmlxc.dylib` not found** — the MLX runtime
329
+ auto-downloads on first `serve` into `~/Library/Caches/mlx-bun/`; if a
330
+ download was interrupted, just rerun `serve` (it resumes). To point at
331
+ your own copy instead, set `MLX_BUN_LIBMLXC=/path/to/libmlxc.dylib`.
332
+ - **`no models match`** — run `bun src/cli.ts scan` after downloading;
333
+ models must be in the standard HF cache (`~/.cache/huggingface/hub`).
334
+ - **HF download stalls at 0%** — use `bun src/cli.ts get <org/repo>`
335
+ (plain HTTPS, no Xet, resumes where it left off); for the Python CLI,
336
+ `HF_HUB_DISABLE_XET=1` before the download command.
337
+ - **Slow decode on a model near your RAM ceiling** — close memory-heavy
338
+ apps; a model that doesn't fit the wired-memory budget pages weights
339
+ every token (check with `fit`).
340
+ - **Bun version** — requires ≥ 1.3.14 (`bun upgrade`); pinned for
341
+ `Bun.Image` and verified FFI behavior.
342
+
343
+ ## Status
344
+
345
+ Pre-alpha, moving fast. See [PLAN.md](./PLAN.md) for phases, exit
346
+ criteria, measured numbers, and the findings log. Complete: load path,
347
+ bit-exact model port (12B dense / e4b per-layer-input / 26B MoE),
348
+ sampling + serving (tools, vision, prompt cache), registry/fit/KV
349
+ persistence, quantized + mixed-precision KV serving (rotating-cache
350
+ KV-quant included, Phase 9) with fused quantized prefill (Phase 10),
351
+ LoRA hot-swap with per-request selection, resumable verified downloads
352
+ (`mlx-bun get`), memory admission control, and the head-to-head
353
+ benchmark harness, and the decode-gap root-cause fix (clear_cache
354
+ placement + boundary accounting, 2026-06-11 — 12B now at-or-above
355
+ mlx-lm decode in paired runs). Open: e4b's ~5% host-overhead decode
356
+ residual (Phase 7), Anthropic messages + Responses API (Phase 11),
357
+ SigLIP vision for e4b/26B, Qwen 3.x, the embeddable single-binary
358
+ build.
359
+
360
+ ## License
361
+
362
+ MIT. Third-party attributions: [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md).
@@ -0,0 +1,41 @@
1
+ # Third-party licenses
2
+
3
+ mlx-bun is MIT-licensed. It builds on, links against, and ports code
4
+ from the following projects.
5
+
6
+ ## Linked libraries
7
+
8
+ | Project | License | Use |
9
+ |---|---|---|
10
+ | [MLX](https://github.com/ml-explore/mlx) (Apple) | MIT | All GPU compute — Metal kernels, lazy eval engine (`libmlx`) |
11
+ | [mlx-c](https://github.com/ml-explore/mlx-c) (Apple) | MIT | C API we bind via `bun:ffi` (`libmlxc.dylib`, installed via Homebrew) |
12
+
13
+ ## Ported code
14
+
15
+ These files are TypeScript ports of Python sources; the port preserves
16
+ the upstream algorithm and is a derivative work of the original.
17
+
18
+ | Source | License | Ported into |
19
+ |---|---|---|
20
+ | [mlx-lm](https://github.com/ml-explore/mlx-lm) (Apple, MIT) — `models/gemma4_text.py`, `models/cache.py`, `models/base.py`, `models/rope_utils.py`, `sample_utils.py`, `tool_parsers/gemma4.py` | MIT | `src/model/gemma4.ts`, `src/sampler.ts`, `src/tool-call.ts` |
21
+ | [mlx-vlm](https://github.com/Blaizzy/mlx-vlm) (BSD-3-Clause) — Gemma-4 unified `VisionEmbedder`, `MultimodalEmbedder`, image preprocessing (as vendored in mlx-optiq) | BSD-3-Clause | `src/vision/embedder.ts`, `src/vision/preprocess.ts` |
22
+ | mlx-optiq (MIT) — vision sidecar wiring, fused quantized SDPA orchestration | MIT | `src/vision/`, quantized-KV paths in `src/model/gemma4.ts` |
23
+ | [Pillow](https://github.com/python-pillow/Pillow) (MIT-CMU / HPND) — `ImagingResample` bicubic convolution algorithm | HPND | `resizeBicubic` in `src/vision/preprocess.ts` (algorithm port) |
24
+
25
+ ## Runtime npm dependencies
26
+
27
+ | Package | License | Use |
28
+ |---|---|---|
29
+ | `@huggingface/tokenizers` | Apache-2.0 | Pure-JS tokenizer (tokenizer.json) |
30
+ | `@huggingface/jinja` | MIT | Chat-template rendering |
31
+ | `fast-png` | MIT | PNG decode (raw pixels for the vision path) |
32
+
33
+ Run `bun pm licenses` (or inspect `node_modules/*/LICENSE`) for the full
34
+ text of each npm dependency's license, including transitive ones.
35
+
36
+ ## Models
37
+
38
+ Model weights (e.g. `mlx-community/gemma-4-12B-it-OptiQ-4bit`) are not
39
+ part of this repository and are governed by their own licenses and use
40
+ policies (e.g. Google's Gemma Terms of Use). Check the model card on
41
+ Hugging Face before redistribution.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bun
2
+ // npm-bin launcher. Plain .mjs so that *any* runtime can parse it and the
3
+ // runtime guard below fires before anything touches bun:ffi / bun:sqlite
4
+ // (a guard inside cli.ts would never run under Node — its static imports
5
+ // hoist and fail on the bun: scheme first).
6
+
7
+ const MIN_BUN = "1.3.14"; // Bun.Image + verified FFI behavior (see README)
8
+
9
+ if (typeof Bun === "undefined") {
10
+ console.error(
11
+ `mlx-bun requires Bun >= ${MIN_BUN} (this is ${process.release?.name ?? "an unknown runtime"} ${process.version}).\n` +
12
+ `Install it: curl -fsSL https://bun.sh/install | bash\n` +
13
+ `Then run: bunx mlx-bun`,
14
+ );
15
+ process.exit(1);
16
+ }
17
+
18
+ const older = (a, b) => {
19
+ const x = a.split(".").map(Number), y = b.split(".").map(Number);
20
+ for (let i = 0; i < 3; i++) if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) < (y[i] ?? 0);
21
+ return false;
22
+ };
23
+ if (older(Bun.version, MIN_BUN)) {
24
+ console.error(`mlx-bun requires Bun >= ${MIN_BUN} (you have ${Bun.version}). Run: bun upgrade`);
25
+ process.exit(1);
26
+ }
27
+ if (process.platform !== "darwin" || process.arch !== "arm64") {
28
+ console.error("mlx-bun runs on Apple Silicon Macs only (MLX is Metal-only by design).");
29
+ process.exit(1);
30
+ }
31
+
32
+ await import("../src/cli.ts");