mlx-bun 0.0.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +40 -24
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/{design → archive}/hlg-sampling.md +3 -3
  4. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  5. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  6. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  7. package/docs/archive/planning/release-notes-v0.0.13.md +77 -0
  8. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  9. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  10. package/docs/design/colibri-glm52-port.md +8 -1
  11. package/docs/design/compat-cli-surface-design.md +1 -1
  12. package/docs/design/decode-speed-program.md +1 -1
  13. package/docs/design/docs-reorg-plan.md +1 -1
  14. package/docs/design/dspark-serving-program.md +1 -1
  15. package/docs/design/dspark-speculative-decoding.md +53 -3
  16. package/docs/design/grammar-spec-batching-integration.md +1 -1
  17. package/docs/design/mlx-lm-serving-execution-seam.md +81 -0
  18. package/docs/design/orpo-dynamic-lambda.md +1 -1
  19. package/docs/design/orpo-future-enhancements.md +1 -1
  20. package/docs/design/orpo-training.md +2 -2
  21. package/docs/design/parity-tier-dag.md +1 -1
  22. package/docs/design/repo-cleanup-plan.md +46 -4
  23. package/docs/design/segmented-backward-training.md +2 -2
  24. package/docs/design/ssd-kv-cold-tier.md +127 -0
  25. package/docs/design/turboquant-weights.md +467 -0
  26. package/docs/design/w4a16-compute-precision-spike.md +1 -1
  27. package/docs/design/web-chat-redesign.md +5 -5
  28. package/docs/planning/chunk-task-roadmap.md +1 -1
  29. package/docs/planning/curve-sampler-research-plan.md +4 -4
  30. package/docs/planning/memory-docs-and-dag-plan.md +1 -1
  31. package/docs/planning/release-notes-v0.2.0.md +49 -0
  32. package/docs/planning/video-series.md +421 -0
  33. package/docs/reference/cli.md +15 -4
  34. package/docs/reference/distribution.md +1 -0
  35. package/docs/reference/features-matrix.md +3 -1
  36. package/docs/reference/library-api.md +41 -1
  37. package/docs/reference/models.md +33 -0
  38. package/docs/reference/server-api.md +86 -19
  39. package/docs/reference/server-config.md +57 -33
  40. package/package.json +2 -2
  41. package/src/anthropic.ts +92 -3
  42. package/src/chat-template.ts +34 -1
  43. package/src/cli.ts +131 -36
  44. package/src/config.ts +10 -0
  45. package/src/eval/kl.ts +27 -20
  46. package/src/eval/runner.ts +23 -4
  47. package/src/eval/tasks/bfcl.ts +2 -1
  48. package/src/eval/tasks/gsm8k.ts +2 -1
  49. package/src/eval/tasks/hashhop.ts +2 -1
  50. package/src/eval/tasks/humaneval.ts +5 -4
  51. package/src/eval/tasks/ifeval.ts +2 -1
  52. package/src/eval/tasks/mmlu.ts +2 -1
  53. package/src/expert-io.ts +3 -1
  54. package/src/expert-offload.ts +3 -1
  55. package/src/expert-trace.ts +3 -1
  56. package/src/fit.ts +24 -43
  57. package/src/flags.ts +7 -9
  58. package/src/generate.ts +107 -92
  59. package/src/grammar.ts +2 -1
  60. package/src/harness-pi.ts +1 -1
  61. package/src/index.ts +20 -0
  62. package/src/jobs/job-entry.ts +3 -2
  63. package/src/kv-scheme.ts +199 -0
  64. package/src/kv-store.ts +370 -205
  65. package/src/media-fetch.ts +19 -3
  66. package/src/memory/model.ts +2 -1
  67. package/src/memory/vault.ts +2 -1
  68. package/src/memory-plan.ts +18 -0
  69. package/src/mlx/ffi.ts +8 -11
  70. package/src/mlx/ops.ts +31 -6
  71. package/src/model/batched-mask.ts +1 -1
  72. package/src/model/batched-quant.ts +1 -1
  73. package/src/model/batched-rotating-quant.ts +43 -97
  74. package/src/model/batched-rotating-state.ts +73 -0
  75. package/src/model/batched-rotating.ts +56 -105
  76. package/src/model/batched-row-storage.ts +127 -0
  77. package/src/model/compiled-decode.ts +3 -2
  78. package/src/model/factory.ts +44 -49
  79. package/src/model/gemma4-base.ts +163 -10
  80. package/src/model/generated/gemma4-12b.ts +50 -146
  81. package/src/model/generated/gemma4-26b.ts +32 -92
  82. package/src/model/generated/gemma4-e4b.ts +26 -74
  83. package/src/model/glm52-cache.ts +19 -9
  84. package/src/model/glm52-memory.ts +28 -1
  85. package/src/model/minicpm5.ts +2 -1
  86. package/src/model/profile.ts +433 -0
  87. package/src/model/qwen3-delta.ts +98 -2
  88. package/src/model/qwen3-mrope.ts +252 -0
  89. package/src/model/qwen3_5.ts +217 -25
  90. package/src/native/frame_extract.swift +140 -0
  91. package/src/native-pack.ts +13 -6
  92. package/src/pi-web.ts +6 -5
  93. package/src/prompt-cache.ts +2 -2
  94. package/src/quantize/atomic-output.ts +38 -0
  95. package/src/quantize/calibration.ts +2 -1
  96. package/src/quantize/config-writer.ts +6 -0
  97. package/src/quantize/index.ts +28 -1
  98. package/src/quantize/job.ts +11 -0
  99. package/src/quantize/quantizer.ts +265 -100
  100. package/src/quantize/rotate.ts +603 -0
  101. package/src/quantize/safetensors-writer.ts +79 -1
  102. package/src/quantize/sensitivity.ts +41 -59
  103. package/src/quantize/weight-transform.ts +292 -0
  104. package/src/responses.ts +102 -2
  105. package/src/runtime-config.ts +67 -0
  106. package/src/sampler.ts +272 -0
  107. package/src/serve/admin-routes.ts +184 -0
  108. package/src/serve/aux-routes.ts +132 -0
  109. package/src/serve/batch-scheduler.ts +200 -87
  110. package/src/serve/completion-executor.ts +346 -0
  111. package/src/serve/completion-sink.ts +157 -0
  112. package/src/serve/discovery-routes.ts +205 -0
  113. package/src/serve/generation-gateway.ts +116 -109
  114. package/src/serve/kv-budget.ts +14 -0
  115. package/src/serve/lab-routes.ts +338 -0
  116. package/src/serve/model-admin-routes.ts +153 -0
  117. package/src/serve/prompt-response-trace.ts +147 -0
  118. package/src/serve/request-plan.ts +158 -0
  119. package/src/serve/static-routes.ts +101 -0
  120. package/src/server.ts +768 -1367
  121. package/src/spec/dflash-source.ts +2 -1
  122. package/src/spec/dspark/module-dflash.ts +1 -1
  123. package/src/spec/glm52-mtp-source.ts +8 -0
  124. package/src/spec/qwen-mtp-source.ts +431 -0
  125. package/src/spec/serve-loop.ts +36 -68
  126. package/src/ssd-cache.ts +17 -14
  127. package/src/ssd-durability.ts +195 -0
  128. package/src/train/flash-cce.ts +12 -11
  129. package/src/train/forward.ts +32 -1
  130. package/src/train/loss.ts +2 -1
  131. package/src/train/segmented.ts +4 -3
  132. package/src/train/trainer.ts +3 -2
  133. package/src/vision/preprocess.ts +1 -1
  134. package/src/vision/prompt.ts +47 -1
  135. package/src/vision/qwen3vl-preprocess.ts +409 -0
  136. package/src/vision/qwen3vl-prompt.ts +196 -0
  137. package/src/vision/qwen3vl-tower.ts +513 -0
  138. package/src/vision/video-frames.ts +148 -0
  139. package/src/web/app.js +10 -3
  140. package/src/web/src/chat.ts +12 -5
  141. package/src/weights.ts +81 -26
  142. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  143. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  144. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  145. /package/docs/{investigations → archive/investigations}/curve-runs/bisector-route.json +0 -0
  146. /package/docs/{investigations → archive/investigations}/curve-runs/bisector-trace.json +0 -0
  147. /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.json +0 -0
  148. /package/docs/{investigations → archive/investigations}/curve-runs/curve-terrain.svg +0 -0
  149. /package/docs/{investigations → archive/investigations}/curve-terrain.html +0 -0
  150. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  151. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  152. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  153. /package/docs/{investigations → archive/investigations}/hlg-ab.html +0 -0
  154. /package/docs/{investigations → archive/investigations}/hlg-explorer.html +0 -0
  155. /package/docs/{investigations → archive/investigations}/hlg-figs/cliff.svg +0 -0
  156. /package/docs/{investigations → archive/investigations}/hlg-figs/cross.svg +0 -0
  157. /package/docs/{investigations → archive/investigations}/hlg-figs/edges.svg +0 -0
  158. /package/docs/{investigations → archive/investigations}/hlg-figs/k20-grids.svg +0 -0
  159. /package/docs/{investigations → archive/investigations}/hlg-figs/k20-peaks.svg +0 -0
  160. /package/docs/{investigations → archive/investigations}/hlg-figs/lw-curve.svg +0 -0
  161. /package/docs/{investigations → archive/investigations}/hlg-figs/terrain.svg +0 -0
  162. /package/docs/{investigations → archive/investigations}/hlg-report.html +0 -0
  163. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-ab.json +0 -0
  164. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind-prompts.md +0 -0
  165. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-blind.json +0 -0
  166. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cliff.json +0 -0
  167. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-cross.json +0 -0
  168. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-edge.json +0 -0
  169. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-eval1.json +0 -0
  170. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-frontier.json +0 -0
  171. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid.json +0 -0
  172. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-grid2.json +0 -0
  173. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-lw-coarse.json +0 -0
  174. /package/docs/{investigations → archive/investigations}/hlg-runs/hlg-map.json +0 -0
  175. /package/docs/{investigations → archive/investigations}/hlg-runs/run0-original.log +0 -0
  176. /package/docs/{investigations → archive/investigations}/hlg-runs/runA-recommended.log +0 -0
  177. /package/docs/{investigations → archive/investigations}/hlg-runs/runAA-cliff-cpm5.log +0 -0
  178. /package/docs/{investigations → archive/investigations}/hlg-runs/runAB-grid2-e4b.log +0 -0
  179. /package/docs/{investigations → archive/investigations}/hlg-runs/runAC-peak-e4b-partial.log +0 -0
  180. /package/docs/{investigations → archive/investigations}/hlg-runs/runAD-ab-samples.log +0 -0
  181. /package/docs/{investigations → archive/investigations}/hlg-runs/runAE-edge-e4b.log +0 -0
  182. /package/docs/{investigations → archive/investigations}/hlg-runs/runAF-logit-calibration.log +0 -0
  183. /package/docs/{investigations → archive/investigations}/hlg-runs/runAG-eval-config2.log +0 -0
  184. /package/docs/{investigations → archive/investigations}/hlg-runs/runB-gain-variants.log +0 -0
  185. /package/docs/{investigations → archive/investigations}/hlg-runs/runC-pivots.log +0 -0
  186. /package/docs/{investigations → archive/investigations}/hlg-runs/runD-oetf.log +0 -0
  187. /package/docs/{investigations → archive/investigations}/hlg-runs/runE-eotf.log +0 -0
  188. /package/docs/{investigations → archive/investigations}/hlg-runs/runF-shaper-working.log +0 -0
  189. /package/docs/{investigations → archive/investigations}/hlg-runs/runG-shaper-sweep.log +0 -0
  190. /package/docs/{investigations → archive/investigations}/hlg-runs/runH-wsweep.log +0 -0
  191. /package/docs/{investigations → archive/investigations}/hlg-runs/runI-asweep.log +0 -0
  192. /package/docs/{investigations → archive/investigations}/hlg-runs/runJ-knobmap.log +0 -0
  193. /package/docs/{investigations → archive/investigations}/hlg-runs/runK-frontier.log +0 -0
  194. /package/docs/{investigations → archive/investigations}/hlg-runs/runL-widemap.log +0 -0
  195. /package/docs/{investigations → archive/investigations}/hlg-runs/runM-frontier-emb.log +0 -0
  196. /package/docs/{investigations → archive/investigations}/hlg-runs/runN-verify.log +0 -0
  197. /package/docs/{investigations → archive/investigations}/hlg-runs/runO-frontier-seed5000.log +0 -0
  198. /package/docs/{investigations → archive/investigations}/hlg-runs/runP-grid-AxSm.log +0 -0
  199. /package/docs/{investigations → archive/investigations}/hlg-runs/runQ-grid-tgW.log +0 -0
  200. /package/docs/{investigations → archive/investigations}/hlg-runs/runR-blind-transcript.log +0 -0
  201. /package/docs/{investigations → archive/investigations}/hlg-runs/runS-cross-coherence.log +0 -0
  202. /package/docs/{investigations → archive/investigations}/hlg-runs/runT-cross-cpm5.log +0 -0
  203. /package/docs/{investigations → archive/investigations}/hlg-runs/runU-cross-12b.log +0 -0
  204. /package/docs/{investigations → archive/investigations}/hlg-runs/runV-lw-e4b.log +0 -0
  205. /package/docs/{investigations → archive/investigations}/hlg-runs/runW-lw-12b.log +0 -0
  206. /package/docs/{investigations → archive/investigations}/hlg-runs/runX-lw-cpm5.log +0 -0
  207. /package/docs/{investigations → archive/investigations}/hlg-runs/runY-cliff-e4b.log +0 -0
  208. /package/docs/{investigations → archive/investigations}/hlg-runs/runZ-cliff-12b.log +0 -0
  209. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  210. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  211. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  212. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  213. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  214. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  215. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  216. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  217. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  218. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  219. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  220. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  221. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  222. /package/docs/{planning → archive/planning}/journal.md +0 -0
  223. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  224. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  225. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  226. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
@@ -0,0 +1,421 @@
1
+ # mlx-bun video series
2
+
3
+ ## Episode 1: local AI on a Mac without the setup ritual
4
+
5
+ ### Production brief
6
+
7
+ - **Working title:** Running a 27B Model Locally on a Mac, Without Python
8
+ - **Thumbnail text:** 27B. LOCAL. NO PYTHON.
9
+ - **Target length:** 8 minutes
10
+ - **Audience:** developers who know what local models are but have not used MLX
11
+ - **Machine:** whichever machine is used for the final recorded benchmark; show
12
+ its chip and memory alongside the results
13
+ - **Demo model:** `mlx-community/Qwen3.8-27B-OptiQ-4bit`, already downloaded
14
+ - **Presentation:** calm, direct, mostly terminal and browser capture with short
15
+ pieces to camera
16
+ - **Question the episode answers:** Can local AI on a Mac feel like installed
17
+ software instead of an ML engineering project?
18
+
19
+ This episode proves the product before explaining the implementation. It does
20
+ not tour every feature. Memory, training, model ports, Bun FFI, and the failed
21
+ experiments belong in later episodes.
22
+
23
+ ### Recording setup
24
+
25
+ Prepare two terminal profiles:
26
+
27
+ 1. A clean profile for the installation shot. Do not begin the model download
28
+ during the take unless the download duration is known and useful on camera.
29
+ 2. The normal development profile with Qwen3.8-27B already downloaded. Use
30
+ this for the live server and API demonstrations.
31
+
32
+ Before recording, confirm the exact model selector accepted on the machine:
33
+
34
+ ```sh
35
+ mlx-bun ls
36
+ mlx-bun fit Qwen3.8-27B --ctx 8192
37
+ ```
38
+
39
+ Start the demo server shortly before the live take if model loading or browser
40
+ placement would interrupt the opening. The visible command should still be the
41
+ real command:
42
+
43
+ ```sh
44
+ mlx-bun serve Qwen3.8-27B
45
+ ```
46
+
47
+ Use a fresh chat and disable notifications. Set the terminal to at least 18 pt.
48
+ Keep the browser and terminal at the same zoom level throughout.
49
+
50
+ ### Shot-by-shot script
51
+
52
+ #### 0:00 to 0:12, cold open
53
+
54
+ **Picture**
55
+
56
+ Close shot of an empty terminal. Type `mlx-bun serve Qwen3.8-27B`. Cut directly
57
+ to the browser as the chat opens. Ask a short question whose answer starts
58
+ quickly.
59
+
60
+ Suggested prompt:
61
+
62
+ > Why can a 27-billion-parameter model fit on this 32 GB Mac? Answer in three
63
+ > sentences and show the memory arithmetic.
64
+
65
+ Do not wait for the full answer. Once several words appear, cut to camera.
66
+
67
+ **Voiceover**
68
+
69
+ > That is a 27-billion-parameter Qwen model running locally on a Mac. There is no
70
+ > Python server behind it, no API key, and no request leaving the machine. One
71
+ > command started the model, the chat interface, and an API that existing tools
72
+ > can use. This is mlx-bun.
73
+
74
+ **On-screen text**
75
+
76
+ ```text
77
+ <machine> · Qwen3.8-27B · 20.4 GB weights
78
+ ```
79
+
80
+ #### 0:12 to 0:35, state the problem
81
+
82
+ **Picture**
83
+
84
+ Piece to camera. Brief cuts to a deliberately restrained list of familiar setup
85
+ artifacts: a Python environment, package installation, a server command, and a
86
+ model configuration file. Do not use fake error messages.
87
+
88
+ **Dialogue**
89
+
90
+ > Local AI already works on a Mac. The rough part is everything around the
91
+ > model. You choose a model that might fit, assemble a Python environment,
92
+ > start a server, and then configure the application that was supposed to use
93
+ > it.
94
+ >
95
+ > I wanted the default path to feel like installing software. Run one command,
96
+ > get a useful local assistant, and keep the controls available when you
97
+ > actually need them.
98
+
99
+ #### 0:35 to 1:10, show the real first-run path
100
+
101
+ **Picture**
102
+
103
+ Show the installation command in the clean terminal. Use a pre-recorded,
104
+ time-compressed capture of the first run beside it. Label the time compression.
105
+
106
+ ```sh
107
+ curl -fsSL https://mlx-bun.dev/install.sh | sh
108
+ mlx-bun
109
+ ```
110
+
111
+ Show the starter model becoming ready. Then cut to the explicit Qwen download
112
+ command used before the main recording:
113
+
114
+ ```sh
115
+ mlx-bun get mlx-community/Qwen3.8-27B-OptiQ-4bit
116
+ ```
117
+
118
+ **Voiceover**
119
+
120
+ > The installer downloads a signed, notarized executable. On a fresh machine,
121
+ > bare `mlx-bun` downloads a sub-gigabyte MiniCPM5 starter so the first chat can
122
+ > begin quickly.
123
+ >
124
+ > This episode is about a much larger model, Qwen3.8-27B. Its quantized weights
125
+ > are 20.4 gigabytes, so I downloaded them before recording. I do not think
126
+ > watching a twenty-gigabyte progress bar would improve the video.
127
+
128
+ **Verification note**
129
+
130
+ Capture this section again if first-run behavior changes. Do not narrate the
131
+ device-aware model selection described in the product roadmap until that lookup
132
+ is implemented.
133
+
134
+ #### 1:10 to 1:55, one process with three ways in
135
+
136
+ **Picture**
137
+
138
+ Use a simple screen composition with the running `mlx-bun` process in the
139
+ center. Reveal the chat UI, an HTTP request, and a TypeScript file around it.
140
+
141
+ ```text
142
+ Chat UI
143
+ \
144
+ mlx-bun + MLX + Metal
145
+ / \
146
+ OpenAI-compatible API TypeScript library
147
+ ```
148
+
149
+ **Dialogue**
150
+
151
+ > The visible chat is only one way into the runtime. The same process exposes an
152
+ > OpenAI-compatible API, so applications that already speak that protocol can
153
+ > point at localhost. A Bun application can also call the generation library
154
+ > directly without running a sidecar server.
155
+ >
156
+ > Those are different entry points into the same native runtime. The model
157
+ > weights stay on the Mac, and MLX runs the computation through Metal on the
158
+ > Apple Silicon GPU.
159
+
160
+ #### 1:55 to 2:35, prove the API claim
161
+
162
+ **Picture**
163
+
164
+ Keep the server log visible on the left. Run this request on the right:
165
+
166
+ ```sh
167
+ curl http://localhost:8080/v1/chat/completions \
168
+ -H 'Content-Type: application/json' \
169
+ -d '{
170
+ "messages": [{
171
+ "role": "user",
172
+ "content": "Name two reasons unified memory helps local inference."
173
+ }],
174
+ "max_tokens": 96,
175
+ "temperature": 0
176
+ }'
177
+ ```
178
+
179
+ Format or crop the JSON response so the answer and usage fields remain legible.
180
+
181
+ **Voiceover**
182
+
183
+ > This is a normal chat-completions request. Change an OpenAI client base URL to
184
+ > `localhost:8080`, and the request goes to the model loaded by mlx-bun. There
185
+ > is no translation service on the internet and no account involved.
186
+
187
+ Do not say every OpenAI feature is implemented. The claim is protocol
188
+ compatibility for the documented API, not the entirety of OpenAI's platform.
189
+
190
+ #### 2:35 to 3:25, explain why Bun matters
191
+
192
+ **Picture**
193
+
194
+ Piece to camera, followed by a clean runtime diagram. Avoid scrolling through
195
+ large source files here.
196
+
197
+ ```text
198
+ TypeScript application
199
+ ↓
200
+ Bun
201
+ ↓
202
+ mlx-bun native bindings
203
+ ↓
204
+ MLX
205
+ ↓
206
+ Apple Silicon GPU
207
+ ```
208
+
209
+ **Dialogue**
210
+
211
+ > Most MLX applications are written in Python. mlx-bun calls MLX's native C API
212
+ > from Bun instead.
213
+ >
214
+ > It loads safetensors weights, builds the model graph, manages the attention
215
+ > cache, samples tokens, and serves requests from TypeScript. It does not start
216
+ > Python in the background.
217
+ >
218
+ > That does not make GPU matrix multiplication faster because it passed through
219
+ > JavaScript. The GPU still sets that limit. It does make the application easier
220
+ > to distribute, and it gives the server less work to do around each model step.
221
+
222
+ #### 3:25 to 4:15, show the finite-matrix idea
223
+
224
+ **Picture**
225
+
226
+ Run the fit command. Hold on the result long enough to read memory use, safe
227
+ context, and predicted speed. Use the exact live output rather than rebuilding
228
+ it for the edit.
229
+
230
+ ```sh
231
+ mlx-bun fit Qwen3.8-27B --ctx 8192
232
+ ```
233
+
234
+ **Dialogue**
235
+
236
+ > The project makes one opinionated bet. Local AI on a Mac is a finite problem.
237
+ > Apple ships a known set of chips and memory sizes. mlx-bun supports a curated
238
+ > set of model families. We can measure those combinations instead of asking
239
+ > every user to become a model-serving specialist.
240
+ >
241
+ > The `fit` command estimates whether a model and context length are safe before
242
+ > loading the weights. The default path can be simple because the measurements
243
+ > and the failure checks live underneath it.
244
+
245
+ Do not claim that automatic device-specific recommendation is complete. Today,
246
+ the fit contract is the concrete proof of this idea.
247
+
248
+ #### 4:15 to 5:20, correctness before speed
249
+
250
+ **Picture**
251
+
252
+ Show a two-column comparison titled `Python oracle` and `mlx-bun`. Animate two
253
+ small arrays of identical hexadecimal or decimal values. Follow with a quick
254
+ shot of a parity test name from the repository.
255
+
256
+ ```text
257
+ same model + same input
258
+
259
+ mlx-lm logits = mlx-bun logits
260
+ bit-exact
261
+ ```
262
+
263
+ **Dialogue**
264
+
265
+ > Reimplementing a model runtime creates a nasty testing problem. Wrong models
266
+ > still write convincing sentences.
267
+ >
268
+ > So I do not use "the answer looked reasonable" as the correctness test. For
269
+ > the compatibility path, the Python MLX implementation is the oracle. The same
270
+ > weights and inputs go through both runtimes, and the tests compare the logits
271
+ > before a token is selected.
272
+ >
273
+ > On the validated paths, those logits match bit for bit. That catches errors in
274
+ > attention masks, normalization, position encoding, cache updates, and model
275
+ > loading that a chat demo can easily hide.
276
+ >
277
+ > Compatibility work has a strict order here. Match the reference first. Then
278
+ > measure any optimization against that baseline.
279
+
280
+ #### 5:20 to 6:20, measured performance
281
+
282
+ **Picture**
283
+
284
+ Display one benchmark card. Do not show the entire results table.
285
+
286
+ ```text
287
+ Qwen3.8-27B · <machine> · warm HTTP serving
288
+ Preflight-gated run · 2026-08-22 · Bun 1.4.0
289
+
290
+ short decode decode at 15.8k warm TTFT aggregate ×4
291
+ mlx-bun 18.6 tok/s 17.0 tok/s 116 ms 19.5 tok/s
292
+ mlx-lm 16.5 tok/s 14.8 tok/s 284 ms 18.6 tok/s
293
+ ```
294
+
295
+ Add a small source line: `benchmarks/RESULTS.md`.
296
+
297
+ **Dialogue**
298
+
299
+ > Here is a measured comparison on this Mac. Same Qwen model snapshot, same
300
+ > machine state, warm HTTP serving.
301
+ >
302
+ > On the short request, mlx-bun generated 18.6 tokens per second. mlx-lm
303
+ > generated 16.5. At about 15,800 tokens of context, the result was 17.0 versus
304
+ > 14.8. Warm time to first token was 116 milliseconds for mlx-bun and 284 for
305
+ > mlx-lm.
306
+ >
307
+ > This is the kind of comparison I care about. It uses the server path an
308
+ > application actually hits, the same weights, and a machine that passed the
309
+ > benchmark preflight. The GPU sets the speed limit, but the runtime still has
310
+ > to reach that limit without wasting memory or time around each token.
311
+
312
+ **Editorial note**
313
+
314
+ These are controlled same-machine results. Put the run date and Bun version on screen.
315
+ If a fresh cleared-machine benchmark is available before publication, replace
316
+ the full card and narration together. Never mix live numbers with this table.
317
+
318
+ #### 6:20 to 7:05, show the larger project without losing focus
319
+
320
+ **Picture**
321
+
322
+ Use four quick clips, about three seconds each:
323
+
324
+ 1. An image input in chat.
325
+ 2. The Markdown memory directory.
326
+ 3. A training progress display.
327
+ 4. A benchmark or parity result from the lab.
328
+
329
+ Return to camera before naming more features.
330
+
331
+ **Dialogue**
332
+
333
+ > Serving is the base of the project, not its boundary. The same binary also has
334
+ > image and video input on supported Qwen models, a local Markdown memory, LoRA
335
+ > training, and a lab for decoding and quantization experiments.
336
+ >
337
+ > I am not going to explain those in a thirty-second feature list. Each one has
338
+ > an implementation story, measurements, and a few failures worth discussing.
339
+ > Those will be separate videos.
340
+
341
+ #### 7:05 to 7:50, close and set up episode 2
342
+
343
+ **Picture**
344
+
345
+ Return to the original chat. Show the local URL, server process, and activity
346
+ monitor GPU history in quick succession. End on the repository and site URLs.
347
+
348
+ **Dialogue**
349
+
350
+ > mlx-bun is my attempt to make local AI on Apple Silicon behave like software
351
+ > you can install, use, and build on.
352
+ >
353
+ > The short version is one command. Underneath it is a TypeScript model runtime,
354
+ > native bindings, model-family ports, memory accounting, and a lot of parity
355
+ > tests.
356
+ >
357
+ > In the next video, I am going to trace one generated token through that whole
358
+ > path, from the TypeScript call to MLX and the GPU, then back to the response.
359
+ > That is where the project gets strange.
360
+
361
+ **On-screen text**
362
+
363
+ ```text
364
+ mlx-bun.dev
365
+ github.com/joshuarossi/mlx-bun
366
+ ```
367
+
368
+ ### Required captures
369
+
370
+ - Clean installer command.
371
+ - Time-compressed first run with MiniCPM5 becoming ready.
372
+ - The explicit Qwen3.8-27B download command or a short time-compressed capture.
373
+ - `mlx-bun serve Qwen3.8-27B` startup and automatic browser open.
374
+ - A short streamed chat response.
375
+ - A successful `/v1/chat/completions` curl request.
376
+ - Live `mlx-bun fit Qwen3.8-27B --ctx 8192` output.
377
+ - One real parity test or stored oracle comparison.
378
+ - Four brief clips for vision, memory, training, and benchmark evidence.
379
+ - Repository and website closing shot.
380
+
381
+ ### Graphics to prepare
382
+
383
+ Only three graphics are needed:
384
+
385
+ 1. The three-entry-point runtime diagram.
386
+ 2. The bit-exact oracle comparison.
387
+ 3. The dated Qwen3.8-27B benchmark card.
388
+
389
+ Keep them monochrome except for one mlx-bun accent color. Use the same terminal
390
+ font in the diagrams and screen recordings.
391
+
392
+ ### Recording-day checks
393
+
394
+ - Confirm the current release installation command.
395
+ - Confirm the Qwen3.8-27B selector and exact displayed model name.
396
+ - Confirm port 8080 is unused before the take.
397
+ - Confirm no private paths, tokens, conversations, or model-cache usernames are
398
+ visible.
399
+ - Confirm first-run behavior still matches the narration.
400
+ - Confirm the API request and response fields against the current reference
401
+ docs.
402
+ - Confirm the benchmark card still matches `benchmarks/RESULTS.md`.
403
+ - Record room tone and five seconds of clean terminal footage for edits.
404
+ - Capture every important terminal action twice.
405
+
406
+ ### Episode 2 handoff
407
+
408
+ The promised follow-up is: **What happens when mlx-bun generates one token?**
409
+
410
+ Trace this path without turning the episode into a source-tree tour:
411
+
412
+ ```text
413
+ request
414
+ → tokenizer
415
+ → model forward pass
416
+ → MLX lazy graph evaluation
417
+ → Metal execution
418
+ → GPU sampling
419
+ → one token returned to Bun
420
+ → streamed protocol event
421
+ ```
@@ -35,7 +35,7 @@ Common flags (full list in [server-config.md](server-config.md)):
35
35
  |---|---|
36
36
  | `--host <addr>` | Interface to bind (default `127.0.0.1`, loopback only; `0.0.0.0` to expose) |
37
37
  | `--port <n>` | Listen port (default 8080) |
38
- | `--memory-budget <GB>` | Reject loads/requests that can't fit the budget |
38
+ | `--memory-budget <GB>` | Admission ceiling for requests (bills the active `--kv-quant` scheme; clamps `max_tokens`, 400s only prompts that leave no room — never refuses to start) |
39
39
  | `--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) |
40
40
  | `--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 |
41
41
  | `--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)) |
@@ -49,12 +49,12 @@ Common flags (full list in [server-config.md](server-config.md)):
49
49
  | `--paged-kv-block-size <n>` | Tokens per KV block (default 256 = the plain cache's growth step; `--paged-kv` only) |
50
50
  | `--adapter <dir>` | Mount a LoRA adapter at startup (`--adapter-path` accepted as the mlx_lm.server alias) |
51
51
  | `--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 |
52
- | `--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
+ | `--draft-kind <kind>` | Override draft-artifact detection: `two-model` \| `assistant` \| `dspark` \| `deepspec` \| `mtp` \| `ngram` (default: auto). `mtp` is a NATIVE multi-token-prediction head split from the target's own release (e.g. `mlx-community/Qwen3.8-27B-MTP-bf16`; auto-detected from a `*_mtp` `model_type`) — it binds the target's embeddings/lm-head and taps its pre-final-norm hiddens, and the serve loop rolls the target's recurrent DeltaNet caches back across rejected drafts via bit-exact snapshot/replay. `--draft-kind mtp` WITHOUT `--draft-model` resolves the companion from the artifact's own `mtp/` subfolder (single-repo bundles, e.g. the TQ releases) and refuses with a clear error when the model bundles none. `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) |
53
53
  | `--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) |
54
54
  | `--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) |
55
55
  | `--mtp on\|off` | GLM-5.2 checkpoint-native MTP (default on). Native MTP uses the exact serial verify lane; `off` enables ordinary continuous batching (batched per-row MTP is post-release) |
56
56
  | `--context-length <n>` | GLM-5.2 context reserved by the header-only resource equation (default 4096) |
57
- | `--batch <n>` | Continuous-batched serving cap, mlx-lm B=N parity (default 8; `--batch 1` pins the serial path) |
57
+ | `--batch <n>` | Continuous-scheduler concurrency cap, mlx-lm parity at the same B (default 8; a lone admitted request uses the gated B=1 fast path, while `--batch 1` pins the preserved strict serial executor) |
58
58
  | `--temperature` / `--top-p` / `--top-k` / `--max-tokens` | Server-wide sampling defaults (per-request fields still win; GLM-5.2 defaults to the planned 128 generated tokens) |
59
59
  | `--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). |
60
60
  | `--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) |
@@ -170,11 +170,15 @@ mlx-bun gc --yes # delete
170
170
 
171
171
  Deterministic memory assessment: does it fit, what's the max context, predicted
172
172
  tok/s. Weights bytes from safetensors headers, KV bytes/token from the config,
173
- calibrated prefill transient, wired-memory ceiling.
173
+ calibrated prefill transient, wired-memory ceiling. `--kv-quant` mirrors serve's
174
+ flag: it bills the quantized cache at its true bytes/element, so the reported
175
+ window matches what serving with the same flag admits (a 4-bit cache holds
176
+ ~3.5× the context of bf16 in the same budget).
174
177
 
175
178
  ```sh
176
179
  mlx-bun fit gemma --ctx 32768 # for this machine
177
180
  mlx-bun fit gemma --ctx 8192 --skus # across the Apple Silicon lineup
181
+ mlx-bun fit qwen --kv-quant 4 # window with a 4-bit-quantized KV cache
178
182
  ```
179
183
 
180
184
  The direct Colibri GLM-5.2 artifact is handled by its streamed-runtime
@@ -235,8 +239,15 @@ MLX snapshot — uniform 4/8-bit, or mixed-precision via `--target-bpw`
235
239
  ```sh
236
240
  mlx-bun convert Qwen/Qwen3-4B -q # uniform 4-bit
237
241
  mlx-bun convert Qwen/Qwen3-4B --target-bpw 4.5 # mixed precision
242
+ mlx-bun convert ./qwen3.5-bf16 -q --rotate-weights # fold then quantize
238
243
  ```
239
244
 
245
+ `--rotate-weights` makes the offline rotation part of the same conversion
246
+ pipeline and records its recipe in `optiq_metadata.json`. The adapter is
247
+ selected from the model/tensor schema (Llama, Qwen3.5 trunk, or Qwen MTP);
248
+ `--rotation-seed` defaults to `42`. Folding requires full-precision source
249
+ weights and an MTP companion must use the same seed as its trunk.
250
+
240
251
  ### `upload` — push to the Hugging Face Hub
241
252
 
242
253
  Native push-to-hub, no Python: creates the repo if needed, uploads via the
@@ -15,6 +15,7 @@ tarball (~73 MB compressed, ~228 MB installed):
15
15
  | `mlx-bun` | the CLI/server (`bun build --compile`), ~61 MB |
16
16
  | `libmlxc.dylib`, `libmlx.dylib`, `libjaccl.dylib` | MLX native runtime |
17
17
  | `libmlx_bun_expert_io.dylib` | bounded native expert-streaming helper required by direct Colibri GLM-5.2 |
18
+ | `mlx-bun-frame-extract` | AVFoundation frame-extraction sidecar for video input (darwin-native codecs, the afconvert doctrine) |
18
19
  | `mlx.metallib` | Metal kernels (~150 MB — the bulk) |
19
20
  | pi assets | `photon_rs_bg.wasm`, `theme/`, `assets/`, `export-html/`, `native/`, `package.json`, `CHANGELOG.md` |
20
21
 
@@ -20,7 +20,7 @@ parity). **Tiers:** L1 = bit-exact vs mlx-lm · L2 = bit-exact vs mlx-optiq
20
20
  | Raw text completions (`/v1/completions`) | on | both | — | — |
21
21
  | Anthropic Messages (`/v1/messages`) | on | both | — | — |
22
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>` |
23
+ | Continuous batching (mlx-lm parity at the same B) | **on** — cap 8 (default flipped 2026-07-05; a lone admitted request uses the gated B=1 fast path and concurrent requests can form B=N; `--batch 1` pins the strict serial executor) | batch | L1 | `--batch <n>` |
24
24
  | Prompt cache (prefix KV reuse) | on, 8 GB | both | — | `--prompt-cache <GB>` (0 = off) |
25
25
  | **SSD KV cold tier** (cache survives eviction + restarts; v3 includes GLM compressed MLA/DSA state without full-K/V reconstruction) | off | both | — | `--ssd-cache <dir>` (+ `-max`, `-verify`, `-demote-idle <sec>` — default 300s once `--ssd-cache` is on, `0` disables) |
26
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` |
@@ -51,6 +51,7 @@ parity). **Tiers:** L1 = bit-exact vs mlx-lm · L2 = bit-exact vs mlx-optiq
51
51
  | 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 |
52
52
  | Tool calling (Gemma sentinel / CPM+Qwen XML / GLM `arg_key`+`arg_value`) + `role:"tool"` loops | on | both | — | request `tools` |
53
53
  | 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 |
54
+ | **Video input** (`video_url`/`video` parts; mp4/mov H.264/HEVC/ProRes/… via the AVFoundation sidecar `mlx-bun-frame-extract`; 2 fps sampling, ≤768 frames, 256 MB body cap; never combines with audio) | on (Qwen3.5-family models); same SSRF guard | serial | mlx-vlm oracle (frames pipeline bit-exact; tests/qwen38-vision.test.ts) | `--allow-private-media` for LAN hosts |
54
55
  | **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 |
55
56
  | LoRA adapters (mount at start / hot-swap at runtime) | off | serial | — | `--adapter <dir>`, `POST /v1/adapters` |
56
57
  | 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 |
@@ -69,6 +70,7 @@ parity). **Tiers:** L1 = bit-exact vs mlx-lm · L2 = bit-exact vs mlx-optiq
69
70
  | MiniCPM5 (cpm5) | ✅ L1/L2 | ✅ | the starter model |
70
71
  | Gemma 4 (1B/e4b/12B/26B, + vision e4b/12B, + audio e4b) | ✅ L1/L2 | ✅ | sliding+full interleaved; MoE 26B |
71
72
  | Qwen3.5 (gated-DeltaNet hybrid) | ✅ L1/L2 | ✅ (SSM path) | `MLX_BUN_BATCH_SSM=0` reverts |
73
+ | Qwen3.8-27B (same qwen3_5 graph) | ✅ L1 (no L2 oracle) | ✅ (SSM path) | thinking depths + preserve_thinking; native MTP head mounts via `--draft-model` (DeltaNet spec rollback = snapshot/replay, lossless-gated; quiet-box A/B: 0.82× — correct but slower, opt-in); **vision (images) AND video SERVE** — bf16 tower sidecar + mRoPE + embed splice, oracle = mlx-vlm on pinned mlx (preprocessor/tower/positions bit-exact, e2e token-exact on 2/3 fixtures, serve smokes green; tests/qwen38-vision.test.ts); video files decode via the shipped AVFoundation sidecar (`mlx-bun-frame-extract`, src/native/frame_extract.swift — 2 fps, ≤768 frames, temporal pairs, per-frame-group attention) |
72
74
  | GLM-5.2 / Colibri (pre-release port gate) | ✅ chat/text, Messages, Responses, SSE, tools, grammar, logprobs, library/CLI | ✅ compressed MLA/DSA scheduler | native MTP defaults to serial+spec; `--mtp off` enables ordinary batching; artifact-aware CLI/API/status `fit` and live main/MTP expert-tier telemetry; embeddings, vision/audio, adapters, and training are explicitly unsupported; per-row batched MTP is post-release |
73
75
  | DiffusionGemma-26B (non-autoregressive) | ✅ (own engine) | — serial always | first bit-exact non-AR port |
74
76
  | Tier-0 universal (llama/qwen2/qwen3/olmo2/…, 11 archs) | ✅ L1 | ✅ plain full-attention archs² | gemma2-family/sliding universal → serial |
@@ -8,7 +8,7 @@ admission — is importable directly into a Bun process. Published as
8
8
 
9
9
  One rule everywhere: **one generation at a time per process** (one GPU).
10
10
  The server serializes through a queue (or, under `--batch N`, its
11
- continuous-batching scheduler — a server-lane feature, not a library
11
+ continuous scheduler — a server scheduling mechanism, not a library
12
12
  surface); direct library callers must do the same.
13
13
 
14
14
  ## Quick start
@@ -49,6 +49,46 @@ Ordinary safetensors loading is lazy (mmap + mlx native loader). GLM-5.2 first
49
49
  runs its header-only exact process equation, then opens the bounded Colibri
50
50
  resident/expert tiers; impossible plans fail before either tier is committed.
51
51
 
52
+ ## Model profiles
53
+
54
+ `openModel()` and `loadContext()` resolve a declared model profile before they
55
+ open weights. `loadContext()` exposes that decision as `ctx.profile`, including
56
+ the exact external fingerprint when the path is an immutable Hugging Face
57
+ snapshot:
58
+
59
+ ```ts
60
+ const ctx = await loadContext(dir);
61
+ console.log(ctx.profile.profile.id);
62
+ console.log(ctx.profile.artifact.fingerprint); // hf:<org>/<repo>@<revision> or null
63
+ console.log(ctx.profile.profile.fidelity);
64
+ console.log(ctx.profile.profile.execution); // loader + graph + loop + specialization
65
+ ```
66
+
67
+ Direct library construction can resolve and pass the same immutable value:
68
+
69
+ ```ts
70
+ import { loadModelConfig, Weights, resolveModelProfile, createModel } from "mlx-bun";
71
+
72
+ const config = await loadModelConfig(dir);
73
+ const profile = resolveModelProfile(config);
74
+ const model = createModel(await Weights.open(dir), config, profile);
75
+ ```
76
+
77
+ For a local artifact that is not stored under an immutable HF snapshot path, an
78
+ application may supply its own provenance fingerprint and exact declaration via
79
+ `resolveModelProfile(config, { artifactFingerprint, artifactProfiles })`.
80
+ Declarations use the exported `ArtifactModelProfile`, `FidelityTarget`,
81
+ `EngineCapability`, and `ModelExecutionComposition` types. The resolver checks
82
+ the declared config fingerprint (compute it with exported
83
+ `configFingerprint(config)`) and required engine capabilities. An exact match
84
+ that fails either check refuses instead of taking the family or generic
85
+ fallback.
86
+
87
+ Profile selection is limited to model construction. Generation options remain
88
+ authoritative: passing mixed `kvConfig`, TurboQuant KV, adapters, grammar,
89
+ sampling, or an explicitly configured draft method is not rewritten by the
90
+ profile.
91
+
52
92
  > **First run:** library consumers bypass the CLI's first-run step, so
53
93
  > call `await ensureNativeRuntime()` once before constructing a model on
54
94
  > a machine that may not have the MLX native runtime yet — it downloads
@@ -106,6 +106,39 @@ The artifact is an external MIT-licensed derivative of
106
106
  weights. Model weights are not bundled with mlx-bun; review the artifact model
107
107
  card before redistribution.
108
108
 
109
+ ## Declared model profiles
110
+
111
+ Model loading resolves one immutable profile before weights are opened. A
112
+ profile names four things: the external artifact fingerprint when one is
113
+ known, its fidelity target, the engine capabilities it requires, and the
114
+ composed loader/model-graph/generation-loop path.
115
+
116
+ Exact artifact profiles outrank family profiles. The current exact declarations
117
+ are:
118
+
119
+ | profile | external artifact | fidelity | execution |
120
+ | --- | --- | --- | --- |
121
+ | `qwen3.8-27b-optiq-4bit` | `mlx-community/Qwen3.8-27B-OptiQ-4bit` revision `b04599de95d7a9bfbd7f208d347c0f10d9432a42` | L1: bit-exact mlx-lm | safetensors + dedicated Qwen3.5/3.8 graph + autoregressive loop |
122
+ | `glm5.2-colibri-int4-int8-mtp` | `mateogrgic/GLM-5.2-colibri-int4-with-int8-mtp` revision `3cc8db99b1b13fc79325d987ba3c1c430766b3b8` | L3: measured, no matching tier oracle | Colibri container + dedicated GLM-5.2 graph + autoregressive loop |
123
+
124
+ The fingerprint is the Hugging Face repo plus immutable snapshot revision, so
125
+ moving the cache does not change it. A mutable alias such as `snapshots/staged`
126
+ or an arbitrary local directory is not treated as exact. Those artifacts use
127
+ the dedicated model-family profile when one matches, then the universal dense
128
+ profile when that architecture is supported. Unsupported architectures still
129
+ refuse with the existing targeted/generic support list.
130
+
131
+ Profiles select construction only. They do not enable, disable, or replace MTP,
132
+ mixed-precision KV from `kv_config.json`, TurboQuant KV, adapters, grammar, or
133
+ sampling. Those methods remain explicit flags/request fields or documented
134
+ defaults. If an exact profile requires a capability the running engine does not
135
+ have, loading refuses; it never downgrades to another model path.
136
+
137
+ Fidelity labels mean L1 = bit-exact mlx-lm, L2 = bit-exact mlx-optiq, and L3 =
138
+ measured evidence without a matching oracle. Model profiles do not imply that a
139
+ quantized artifact retains the base model's benchmark score; quality evidence
140
+ belongs to that exact artifact.
141
+
109
142
  ## scan + the registry
110
143
 
111
144
  `mlx-bun scan` walks `models--*/snapshots/*` reading only `config.json`