mlx-bun 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +94 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/archive/investigations/openjarvis-fit.md +198 -0
  8. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  9. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  10. package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
  11. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  12. package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
  13. package/docs/design/batching.md +1841 -0
  14. package/docs/design/decode-speed-program.md +2346 -169
  15. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  16. package/docs/design/generic-model-support.md +655 -334
  17. package/docs/design/kv-cache.md +824 -0
  18. package/docs/design/orpo-training.md +673 -1239
  19. package/docs/design/speculative-decoding.md +1990 -0
  20. package/docs/design/turboquant.md +2212 -0
  21. package/docs/design/unified-engine-frontier-plan.md +1833 -727
  22. package/docs/design/web-chat-redesign.md +666 -1083
  23. package/docs/planning/PRODUCT_ROADMAP.md +50 -1
  24. package/docs/planning/release-notes-v0.4.0.md +45 -0
  25. package/docs/planning/video-series.md +2 -2
  26. package/docs/reference/benchmarks.md +3613 -0
  27. package/docs/reference/cli.md +434 -160
  28. package/docs/reference/distribution.md +328 -182
  29. package/docs/reference/environment.md +398 -0
  30. package/docs/reference/glossary.md +39 -0
  31. package/docs/reference/library-api.md +566 -168
  32. package/docs/reference/memory.md +227 -64
  33. package/docs/reference/models.md +113 -6
  34. package/docs/reference/server-api.md +944 -429
  35. package/docs/reference/server-config.md +740 -338
  36. package/docs/reference/training.md +393 -254
  37. package/docs/reference/troubleshooting.md +158 -0
  38. package/package.json +9 -4
  39. package/src/audio/decode.ts +2 -2
  40. package/src/audio/features.ts +2 -2
  41. package/src/backends/mlx/assistant-target.ts +46 -0
  42. package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
  43. package/src/backends/mlx/autoregressive.ts +108 -0
  44. package/src/backends/mlx/batch-group.ts +1790 -0
  45. package/src/backends/mlx/cache-attachment.ts +67 -0
  46. package/src/backends/mlx/cache-layout.ts +31 -0
  47. package/src/backends/mlx/checkpoint-state.ts +53 -0
  48. package/src/backends/mlx/continuation-persistence.ts +106 -0
  49. package/src/backends/mlx/continuation-request.ts +43 -0
  50. package/src/backends/mlx/continuation.ts +77 -0
  51. package/src/backends/mlx/diffusion.ts +39 -0
  52. package/src/backends/mlx/draft-target.ts +32 -0
  53. package/src/backends/mlx/evaluation.ts +105 -0
  54. package/src/backends/mlx/fill-append.ts +45 -0
  55. package/src/backends/mlx/full-kv-row-append.ts +32 -0
  56. package/src/backends/mlx/full-kv-row-donor.ts +54 -0
  57. package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
  58. package/src/backends/mlx/gateway-binding.ts +130 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/kv-codec.ts +9 -0
  61. package/src/backends/mlx/kv-maintenance.ts +133 -0
  62. package/src/backends/mlx/kv-tensor-rows.ts +212 -0
  63. package/src/backends/mlx/memory-client.ts +189 -0
  64. package/src/backends/mlx/methods.ts +90 -0
  65. package/src/backends/mlx/model-prompt.ts +164 -0
  66. package/src/backends/mlx/model-serving.ts +79 -0
  67. package/src/backends/mlx/prefill-cohort.ts +89 -0
  68. package/src/backends/mlx/prefill-rows.ts +200 -0
  69. package/src/backends/mlx/prefill.ts +33 -0
  70. package/src/backends/mlx/projected-context-rows.ts +72 -0
  71. package/src/backends/mlx/request-policy.ts +20 -0
  72. package/src/backends/mlx/request-state-policy.ts +41 -0
  73. package/src/backends/mlx/rollback.ts +89 -0
  74. package/src/backends/mlx/row-sampling.ts +49 -0
  75. package/src/backends/mlx/serial-executor.ts +242 -0
  76. package/src/backends/mlx/speculative-group.ts +359 -0
  77. package/src/backends/mlx/speculative-round.ts +161 -0
  78. package/src/backends/mlx/speculative.ts +114 -0
  79. package/src/backends/mlx/state-rows.ts +104 -0
  80. package/src/backends/mlx/state-views.ts +46 -0
  81. package/src/backends/mlx/target-layout-capability.ts +35 -0
  82. package/src/backends/mlx/text-engine.ts +73 -0
  83. package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
  84. package/src/cli.ts +66 -58
  85. package/src/client.ts +66 -0
  86. package/src/config.ts +21 -4
  87. package/src/contracts/completion.ts +16 -0
  88. package/src/contracts/execution.ts +60 -0
  89. package/src/contracts/generation.ts +103 -0
  90. package/src/contracts/host.ts +7 -0
  91. package/src/contracts/jobs.ts +66 -0
  92. package/src/contracts/pi-provider.ts +30 -0
  93. package/src/contracts/pi.ts +259 -0
  94. package/src/contracts/prefix-cache.ts +23 -0
  95. package/src/contracts/resources.ts +25 -0
  96. package/src/contracts/sampling.ts +23 -0
  97. package/src/contracts/scheduling.ts +45 -0
  98. package/src/contracts/tsconfig.json +14 -0
  99. package/src/diffusion/diffusion-generate.ts +218 -153
  100. package/src/embed.ts +1 -1
  101. package/src/engine/admission.ts +59 -0
  102. package/src/engine/cancellation.ts +38 -0
  103. package/src/engine/engine.ts +56 -0
  104. package/src/engine/execution-coordinator.ts +70 -0
  105. package/src/engine/execution-plan.ts +53 -0
  106. package/src/engine/resources.ts +53 -0
  107. package/src/engine/scheduler.ts +72 -0
  108. package/src/engine/session.ts +247 -0
  109. package/src/eval/README.md +44 -1
  110. package/src/eval/kl.ts +236 -3
  111. package/src/eval/reference-logits.ts +207 -0
  112. package/src/eval/runner.ts +8 -96
  113. package/src/fill/echo-index.ts +188 -0
  114. package/src/fill/fill-session.ts +452 -0
  115. package/src/fill/proposal.ts +68 -0
  116. package/src/fill/schema-rows.ts +506 -0
  117. package/src/fill/tool-boundary.ts +103 -0
  118. package/src/fit.ts +30 -16
  119. package/src/generate.ts +709 -417
  120. package/src/grammar.ts +12 -7
  121. package/src/harness-pi.ts +14 -33
  122. package/src/hub-rest.ts +4 -4
  123. package/src/index.ts +7 -0
  124. package/src/inference/denoising.ts +17 -0
  125. package/src/inference/draft-acceptance.ts +41 -0
  126. package/src/inference/draft-output.ts +49 -0
  127. package/src/inference/graph.ts +33 -0
  128. package/src/inference/prefill.ts +39 -0
  129. package/src/inference/rollback.ts +7 -0
  130. package/src/jobs/db.ts +4 -0
  131. package/src/jobs/index.ts +2 -0
  132. package/src/jobs/job-entry.ts +12 -12
  133. package/src/jobs/runner.ts +98 -13
  134. package/src/jobs/task-client.ts +19 -0
  135. package/src/jobs/types.ts +2 -58
  136. package/src/kv-scheme.ts +70 -12
  137. package/src/kv-store.ts +177 -63
  138. package/src/lab/README.md +18 -0
  139. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  140. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  141. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  142. package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
  143. package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
  144. package/src/library.ts +40 -0
  145. package/src/lora.ts +18 -0
  146. package/src/media-fetch.ts +1 -1
  147. package/src/memory/article.ts +1 -1
  148. package/src/memory/chunk.ts +1 -1
  149. package/src/memory/model.ts +30 -169
  150. package/src/memory/prompts.ts +1 -1
  151. package/src/memory/rest.ts +1 -1
  152. package/src/memory/schedule.ts +1 -1
  153. package/src/memory/stages.ts +4 -1
  154. package/src/memory/vault.ts +1 -3
  155. package/src/mlx/array.ts +11 -11
  156. package/src/mlx/compile.ts +12 -2
  157. package/src/mlx/ffi.ts +58 -11
  158. package/src/mlx/materialize.ts +23 -0
  159. package/src/mlx/metal-kernel.ts +10 -11
  160. package/src/mlx/ops.ts +33 -13
  161. package/src/mlx/quantized-rows.ts +36 -0
  162. package/src/mlx/token-bitmask.ts +52 -0
  163. package/src/mlx/turboquant-kv-decode.ts +82 -0
  164. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  165. package/src/mlx/turboquant-ops.ts +33 -3
  166. package/src/mlx/turboquant-tables.ts +2 -2
  167. package/src/model/artifact-identity.ts +73 -0
  168. package/src/model/batched-kv.ts +235 -0
  169. package/src/model/batched-mask.ts +20 -6
  170. package/src/model/batched-quant.ts +9 -3
  171. package/src/model/batched-quantized-kv.ts +144 -0
  172. package/src/model/batched-rotating-quant.ts +78 -17
  173. package/src/model/batched-rotating-state.ts +76 -0
  174. package/src/model/batched-rotating.ts +65 -23
  175. package/src/model/batched-row-storage.ts +52 -0
  176. package/src/model/batched-ssm.ts +52 -0
  177. package/src/model/batched-turboquant-kv.ts +93 -0
  178. package/src/model/compiled-decode.ts +8 -2
  179. package/src/model/decoded-kv-donor.ts +12 -0
  180. package/src/model/delayed-quantized-kv.ts +72 -0
  181. package/src/model/delayed-rotating-quantized-kv.ts +93 -0
  182. package/src/model/delayed-turboquant-kv.ts +55 -0
  183. package/src/model/diffusion-gemma.ts +8 -8
  184. package/src/model/factory.ts +76 -33
  185. package/src/model/fingerprint.ts +1 -1
  186. package/src/model/flash-attention.ts +3 -3
  187. package/src/model/full-prefill-padding.ts +56 -0
  188. package/src/model/full-prefill-row.ts +105 -0
  189. package/src/model/gemma4-base.ts +179 -80
  190. package/src/model/gemma4.ts +40 -32
  191. package/src/model/generated/gemma4-12b.ts +6 -3
  192. package/src/model/generated/gemma4-26b.ts +6 -3
  193. package/src/model/generated/gemma4-e4b.ts +6 -3
  194. package/src/model/glm52-cache.ts +80 -4
  195. package/src/model/glm52-dsa.ts +13 -9
  196. package/src/model/glm52-memory.ts +1 -1
  197. package/src/model/glm52-mla.ts +12 -6
  198. package/src/model/glm52.ts +19 -40
  199. package/src/model/implementation.ts +69 -0
  200. package/src/model/kv-attention-view.ts +67 -0
  201. package/src/model/minicpm5.ts +17 -15
  202. package/src/model/profile.ts +12 -2
  203. package/src/model/qwen-append.ts +12 -0
  204. package/src/model/qwen-conv.ts +52 -0
  205. package/src/model/qwen3-delta-state.ts +74 -0
  206. package/src/model/qwen3-delta.ts +59 -36
  207. package/src/model/qwen3_5-checkpoint.ts +164 -0
  208. package/src/model/qwen3_5.ts +202 -78
  209. package/src/model/rotating-kv-donor.ts +35 -0
  210. package/src/model/rotating-kv-layout.ts +262 -0
  211. package/src/model/rotating-row-transaction.ts +35 -0
  212. package/src/model/speculative-rotating-kv.ts +101 -0
  213. package/src/model/ssm-prefill-padding.ts +39 -0
  214. package/src/model/support.ts +1 -1
  215. package/src/model/trellis-balanced-scatter.ts +51 -0
  216. package/src/model/trellis-linear.ts +672 -0
  217. package/src/model/trellis-shared-m.ts +151 -0
  218. package/src/model/trellis-shared-scatter.ts +144 -0
  219. package/src/model/trellis-splitk-prefill.ts +158 -0
  220. package/src/model/trellis-tiled-prefill.ts +136 -0
  221. package/src/model/trellis-vector-expand.ts +62 -0
  222. package/src/model/trellis-wide-prefill.ts +94 -0
  223. package/src/model/turboquant-codec.ts +83 -0
  224. package/src/model/universal/dense.ts +15 -14
  225. package/src/model/universal/modules.ts +8 -1
  226. package/src/native-pack.ts +6 -5
  227. package/src/pi-provider.ts +6 -64
  228. package/src/pi-web.ts +8 -315
  229. package/src/preflight.ts +31 -15
  230. package/src/prompt-cache.ts +110 -69
  231. package/src/quantize/allocator.ts +1 -1
  232. package/src/quantize/config-writer.ts +10 -3
  233. package/src/quantize/rotate.ts +4 -4
  234. package/src/quantize/trellis.ts +586 -0
  235. package/src/responses.ts +28 -1
  236. package/src/runtime-config.ts +18 -4
  237. package/src/sampler.ts +40 -29
  238. package/src/serve/batch-scheduler.ts +8 -1671
  239. package/src/serve/chat-request.ts +391 -0
  240. package/src/serve/chat-stage.ts +195 -0
  241. package/src/serve/checkpoint-identity.ts +64 -0
  242. package/src/serve/completion-executor.ts +15 -7
  243. package/src/serve/discovery-routes.ts +12 -13
  244. package/src/serve/generation-gateway.ts +183 -213
  245. package/src/serve/http.ts +159 -0
  246. package/src/serve/inference-request.ts +79 -0
  247. package/src/serve/isolate.ts +297 -105
  248. package/src/serve/lab-routes.ts +3 -0
  249. package/src/serve/lane-registry.ts +2 -1
  250. package/src/serve/model-admin-routes.ts +7 -5
  251. package/src/serve/model-binding.ts +43 -0
  252. package/src/serve/model-host.ts +511 -0
  253. package/src/serve/openai-wire.ts +103 -0
  254. package/src/serve/parent-application.ts +56 -0
  255. package/src/serve/pipeline.ts +33 -0
  256. package/src/serve/preparation.ts +33 -0
  257. package/src/serve/request-plan.ts +23 -18
  258. package/src/serve/request-prep.ts +344 -0
  259. package/src/serve/response-lease.ts +24 -0
  260. package/src/serve/responses-client.ts +69 -0
  261. package/src/serve/session-completion-engine.ts +110 -0
  262. package/src/serve/session-search.ts +1 -1
  263. package/src/serve/static-routes.ts +5 -5
  264. package/src/serve/text-completion-stage.ts +104 -0
  265. package/src/serve/token-streams.ts +519 -0
  266. package/src/server.ts +335 -2711
  267. package/src/spec/assistant-rows.ts +92 -0
  268. package/src/spec/assistant-source.ts +31 -55
  269. package/src/spec/cache-identity.ts +22 -0
  270. package/src/spec/deepspec-rows.ts +29 -0
  271. package/src/spec/deepspec-source.ts +16 -14
  272. package/src/spec/dflash-source.ts +36 -12
  273. package/src/spec/drafter.ts +153 -142
  274. package/src/spec/dspark/ab-stats.ts +3 -3
  275. package/src/spec/dspark/calibration.ts +2 -2
  276. package/src/spec/dspark/deepspec-module.ts +97 -122
  277. package/src/spec/dspark/generate-dflash.ts +2 -2
  278. package/src/spec/dspark/generate.ts +2 -2
  279. package/src/spec/dspark/loader.ts +1 -1
  280. package/src/spec/dspark/module-dflash.ts +129 -172
  281. package/src/spec/dspark/module.ts +3 -3
  282. package/src/spec/dspark/quantize-drafter.ts +1 -1
  283. package/src/spec/generate.ts +1 -1
  284. package/src/spec/glm52-mtp-graph.ts +29 -0
  285. package/src/spec/glm52-mtp-rows.ts +132 -0
  286. package/src/spec/glm52-mtp-source.ts +54 -137
  287. package/src/spec/glm52-mtp-state.ts +33 -0
  288. package/src/spec/ngram-source.ts +93 -21
  289. package/src/spec/projected-draft-rows.ts +73 -0
  290. package/src/spec/qwen-mtp-module.ts +171 -0
  291. package/src/spec/qwen-mtp-rows.ts +219 -0
  292. package/src/spec/qwen-mtp-source.ts +147 -195
  293. package/src/spec/qwen-mtp-state.ts +30 -0
  294. package/src/spec/serve-loop.ts +201 -201
  295. package/src/spec/source.ts +110 -13
  296. package/src/spec/two-model-rows.ts +61 -0
  297. package/src/spec/two-model.ts +44 -102
  298. package/src/ssd-cache.ts +99 -19
  299. package/src/ssd-durability.ts +36 -20
  300. package/src/tool-call.ts +51 -14
  301. package/src/train/flash-cce.ts +2 -2
  302. package/src/train/forward.ts +6 -0
  303. package/src/train/loss.ts +1 -1
  304. package/src/train/prefix-shared.ts +5 -1
  305. package/src/train/segmented.ts +5 -3
  306. package/src/train/trainer.ts +6 -6
  307. package/src/vision/prompt.ts +1 -1
  308. package/src/vision/qwen3vl-tower.ts +52 -10
  309. package/src/web/app.html +1 -1
  310. package/src/web/app.js +62 -60
  311. package/src/web/src/adapters-panel.ts +3 -3
  312. package/src/web/src/assistant.ts +2 -36
  313. package/src/web/src/chat.ts +4 -4
  314. package/src/web/src/composer.ts +4 -4
  315. package/src/web/src/hub.ts +1 -1
  316. package/src/web/src/markdown.ts +0 -0
  317. package/src/web/src/memory-panel.ts +1 -1
  318. package/src/web/src/model-picker.ts +1 -1
  319. package/src/web/src/palette.ts +1 -1
  320. package/src/web/src/protocol.ts +3 -39
  321. package/src/web/src/sessions.ts +1 -1
  322. package/src/web/src/ui-catalog.ts +4 -18
  323. package/src/web-assets.ts +47 -0
  324. package/src/weights.ts +55 -9
  325. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  326. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  327. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  328. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  329. package/docs/archive/investigations/hlg-ab.html +0 -34
  330. package/docs/archive/investigations/hlg-explorer.html +0 -238
  331. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  332. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  333. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  334. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  335. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  336. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  337. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  338. package/docs/archive/investigations/hlg-report.html +0 -262
  339. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  340. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  341. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  342. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  343. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  344. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  345. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  346. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  347. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  348. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  349. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  350. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  351. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  352. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  353. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  354. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  355. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  356. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  357. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  358. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  359. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  360. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  361. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  362. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  363. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  364. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  365. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  366. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  367. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  368. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  369. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  370. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  371. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  372. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  373. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  374. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  375. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  376. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  377. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  378. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  379. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  380. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  381. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  382. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  383. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  384. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  385. package/docs/archive/mlx-bun-lab-report.html +0 -275
  386. package/docs/archive/wwdc-mlx-bun.html +0 -848
  387. package/docs/design/adapters-end-to-end.md +0 -205
  388. package/docs/design/batching-perf-path.md +0 -255
  389. package/docs/design/batching-v2-plan.md +0 -529
  390. package/docs/design/bucketing-stage.md +0 -197
  391. package/docs/design/compat-cli-surface-design.md +0 -230
  392. package/docs/design/docs-reorg-plan.md +0 -165
  393. package/docs/design/dspark-serving-program.md +0 -371
  394. package/docs/design/dspark-speculative-decoding.md +0 -318
  395. package/docs/design/grammar-spec-batching-integration.md +0 -301
  396. package/docs/design/memory-inference-path.md +0 -422
  397. package/docs/design/memory-synthesis.md +0 -287
  398. package/docs/design/memory-system.md +0 -237
  399. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  400. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  401. package/docs/design/omlx-adoption-map.md +0 -98
  402. package/docs/design/orpo-dynamic-lambda.md +0 -220
  403. package/docs/design/orpo-future-enhancements.md +0 -340
  404. package/docs/design/paged-kv-cache.md +0 -214
  405. package/docs/design/parallel-slots.md +0 -567
  406. package/docs/design/parity-tier-dag.md +0 -161
  407. package/docs/design/repo-cleanup-plan.md +0 -231
  408. package/docs/design/runtime-isolation.md +0 -263
  409. package/docs/design/segmented-backward-training.md +0 -543
  410. package/docs/design/spec-decode-larger-targets.md +0 -229
  411. package/docs/design/ssd-kv-cold-tier.md +0 -415
  412. package/docs/design/structured-output.md +0 -484
  413. package/docs/design/tauri-desktop-app.md +0 -127
  414. package/docs/design/the-dreaming-master-plan.md +0 -445
  415. package/docs/design/turboquant-kv.md +0 -235
  416. package/docs/design/turboquant-weights.md +0 -467
  417. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  418. package/docs/design/web-chat-beat-matrix.md +0 -315
  419. package/docs/design/write-pipeline-entity.md +0 -468
  420. package/docs/planning/IDEAS.md +0 -69
  421. package/docs/reference/embedding.md +0 -197
  422. package/docs/reference/features-matrix.md +0 -101
  423. package/docs/reference/orpo-quickstart.md +0 -135
  424. package/src/flags.ts +0 -11
  425. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  426. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  427. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  428. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  429. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  430. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  431. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  432. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  433. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -3,38 +3,61 @@
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; `bunx mlx-bun` works out of the box, or
7
- `import … from "mlx-bun"` for programmatic use.
6
+ `mlx-bun` on npm; `bunx mlx-bun` runs the CLI, `import … from "mlx-bun"`
7
+ (or `./src/index` in a clone) is the programmatic surface.
8
+
9
+ This page is the canonical description of that surface. The source of
10
+ truth is the package export map: `src/index.ts` preserves the native compatibility API,
11
+ `src/library.ts` supplies the CPU-safe `mlx-bun/engine` entry, and
12
+ `src/client.ts` supplies `mlx-bun/client`. These exports are the semver contract;
13
+ everything else under `src/` is internal and may change without notice.
14
+ Every export is documented below, grouped the way `src/index.ts` groups
15
+ them.
8
16
 
9
17
  One rule everywhere: **one generation at a time per process** (one GPU).
10
18
  The server serializes through a queue (or, under `--batch N`, its
11
19
  continuous scheduler — a server scheduling mechanism, not a library
12
20
  surface); direct library callers must do the same.
13
21
 
22
+ ## Export map
23
+
24
+ | Group | Values | Types |
25
+ |---|---|---|
26
+ | [Native runtime](#native-runtime) | `ensureNativeRuntime`, `nativeRuntimeDir` | — |
27
+ | [Config and weights](#config-and-weights) | `loadModelConfig`, `Weights` | — |
28
+ | [Model profiles](#model-profiles) | `resolveModelProfile`, `configFingerprint`, `externalArtifactFingerprint`, `BUILTIN_ARTIFACT_PROFILES`, `ENGINE_CAPABILITIES` | `ArtifactModelProfile`, `EngineCapability`, `FidelityTarget`, `FidelityTier`, `GenerationLoop`, `ModelArtifactIdentity`, `ModelExecutionComposition`, `ModelGraph`, `ModelLoader`, `ModelProfile`, `ModelSpecialization`, `ResolveModelProfileOptions`, `ResolvedModelProfile` |
29
+ | [Opening a model](#opening-a-model) | `openModel`, `createModel`, `openGlm52RuntimeModel` | `RuntimeModel`, `ModelOpenOptions`, `Glm52RuntimeOpenOptions` |
30
+ | [Engine-owned implementations](#engine-owned-implementations) | `ModelImplementationRegistry` | `ModelImplementation`, `ModelImplementationProvider`, `ModelServingBinding`, `ModelPromptBuilder`, `BuiltPrompt`, `ServedModelInfo`, `ServingContext`, `ModelHostSource`, `AdapterService`, `MlxGatewayBinding`, `MlxBatchGroup`, `MlxSerialServices`, `MlxSerialBinding` |
31
+ | [Concrete model classes](#concrete-model-classes) | `Gemma4Model`, `MiniCPM5Model`, `Qwen3Model` | — |
32
+ | [Tokenizer](#tokenizer) | `loadTokenizer` | — |
33
+ | [Chat templates](#chat-templates) | `ChatTemplate` | — |
34
+ | [Generation](#generatemodel-prompttokens-options--generation) | `generate` | — |
35
+ | [Text embeddings](#text-embeddings) | `embedOne`, `embedMany`, `isEmbeddingModel`, `withInstruction` | `EmbedResult` |
36
+ | [Model discovery](#model-discovery-registry) | `Registry` | — |
37
+ | [Downloads](#downloads) | `downloadModel` | — |
38
+ | [Memory fit](#memory-fit) | `fit`, `skuMatrix`, `thisMachine`, `chooseAutoModel`, `recommendedRepoId`, `largestRecommendedRepoId`, `DEFAULT_REPO_ID`, `COEXIST_FRACTION` | — |
39
+ | [Serving](#serving) | `loadContext`, `createServer`, `shutdownServer` | `LoadContextOptions`, `ServerContext`, `ServerOptions` |
40
+
41
+ Types that are *not* exported (`GenerateOptions`, `GenerateStats`,
42
+ `GeneratedToken`, `Generation`, `LoadedTokenizer`, `ChatMessage`,
43
+ `RenderOptions`, `ModelConfig`, `MachineSpec`, `FitReport`, `ModelRecord`,
44
+ `DownloadOptions`, `Cache`) are reachable through inference — e.g.
45
+ `Parameters<typeof generate>[2]`, `Awaited<ReturnType<typeof loadTokenizer>>`.
46
+
14
47
  ## Quick start
15
48
 
16
49
  ```ts
17
- import {
18
- ensureNativeRuntime,
19
- openModel, // artifact-aware dispatch, including bounded GLM-5.2
20
- loadTokenizer,
21
- ChatTemplate,
22
- generate,
23
- } from "mlx-bun"; // or "./src/index" in-repo
24
-
25
- await ensureNativeRuntime();
50
+ import { initializeMlx } from "mlx-bun/engine";
51
+
52
+ // Bootstrap finishes before the native API is imported.
53
+ const { openModel, loadTokenizer, ChatTemplate, generate } = await initializeMlx();
26
54
  const dir = "/path/to/hf-snapshot"; // mlx-bun ls prints these
27
- const model = await openModel(dir, {
28
- // Used only by the direct Colibri GLM-5.2 artifact; other models ignore it.
29
- contextTokens: 4096, maxGenerationTokens: 256, enableMtp: false,
30
- });
55
+ const model = await openModel(dir);
31
56
  const tok = await loadTokenizer(dir);
32
57
  const template = await ChatTemplate.load(dir);
33
- // Gemma4Model and MiniCPM5Model can still be imported directly from "mlx-bun"
34
- // if you need the concrete type. Qwen35Model is NOT exported from the public
35
- // package — use openModel/RuntimeModel (preferred artifact-aware dispatch), or import from
36
- // "./src/model/qwen3_5" in-repo.
37
58
 
59
+ // The template emits <bos> as text and the tokenizer post-processor prepends
60
+ // one too; trim the duplicate exactly the way the server does.
38
61
  const ids = tok.encode(template.render([{ role: "user", content: "hi" }]));
39
62
  const promptIds = ids[0] === ids[1] && ids[0] === tok.bosTokenId ? ids.slice(1) : ids;
40
63
 
@@ -45,232 +68,607 @@ console.log(tok.decode(out, true));
45
68
  console.log(gen.stats); // set once iteration ends
46
69
  ```
47
70
 
48
- Ordinary safetensors loading is lazy (mmap + mlx native loader). GLM-5.2 first
49
- runs its header-only exact process equation, then opens the bounded Colibri
50
- resident/expert tiers; impossible plans fail before either tier is committed.
71
+ Ordinary safetensors loading is lazy (mmap + mlx native loader). GLM-5.2
72
+ first runs its header-only exact process equation, then opens the bounded
73
+ Colibri resident/expert tiers; impossible plans fail before either tier is
74
+ committed.
75
+
76
+ ## Clients and isolated hosts
77
+
78
+ `mlx-bun/client` and `mlx-bun/engine` can be imported without native MLX.
79
+ `createCompletionClient` accepts a `/v1` URL and optionally a borrowed host
80
+ implementing `forward(Request): Promise<Response>`. It sends JSON completions,
81
+ forces `stream: false`, passes cancellation through `signal`, and never retries
82
+ a POST. Full SSE streaming remains available through the host's `forward` port
83
+ and the existing HTTP API. A supplied host belongs to the caller.
84
+
85
+ ```ts
86
+ import { createCompletionClient, openIsolatedHost } from "mlx-bun/engine";
87
+
88
+ const host = await openIsolatedHost("/path/to/hf-snapshot");
89
+ try {
90
+ const client = createCompletionClient({ baseUrl: "http://engine/v1", host });
91
+ const result = await client.complete({
92
+ body: { messages: [{ role: "user", content: "Hello" }], max_tokens: 64 },
93
+ });
94
+ console.log(result.choices[0]?.message?.content);
95
+ } finally {
96
+ await host.close();
97
+ }
98
+ ```
99
+
100
+ `openIsolatedHost(model, { arguments?, readyTimeoutMs? })` starts the existing
101
+ CLI on a private Unix socket and resolves after health readiness. `close()`
102
+ waits for the worker to exit. `createDirectHost(handler, shutdown?)` adapts an
103
+ in-process HTTP handler to the same contract; its shutdown callback owns any
104
+ native resources and response streams. `createInferenceEngine` and
105
+ `CancellationSource` expose the portable token-session API for registered methods.
106
+ The original root exports remain compatible and require an installed native runtime.
107
+
108
+ Sessions support queued `stream`, bounded `collect`, and direct `callback`
109
+ delivery. Callback control supplies `onTokens(ids, logprobs?)`; execution awaits
110
+ it, and returning `false` requests a normal stop. The callback owns any retained
111
+ output storage. Methods must honor that stop and finish cleanup before returning
112
+ metrics. HTTP uses this mode to preserve immediate stop/tool parsing without a
113
+ second queue. Method IDs and private state belong to the implementation.
114
+
115
+ ## Engine-owned implementations
116
+
117
+ `ModelImplementationRegistry<Source, Model>` validates a resolved artifact profile
118
+ and selects registered engine code. Its `select()` checks the named implementation,
119
+ graph, loader and construction loop; missing or incompatible code fails before
120
+ construction. `with()` returns a new immutable registry. Model files supply data,
121
+ not executable registrations.
122
+
123
+ `createModel(weights, config, profile, implementations)` and
124
+ `openModel(directory, { profiles, implementations })` preserve the supplied
125
+ registry's return interface. Resident implementations can therefore return their
126
+ own graph/method binding without inheriting a concrete model class. The caller
127
+ owns already-open weights; a successfully opened implementation must retain the
128
+ weights it uses. Resident overrides cannot replace the Colibri streamed loader.
129
+
130
+ For a complete implementation, pass a
131
+ `ModelImplementationProvider<ModelHostSource, Promise<ServerContext<Model>>>`
132
+ to `loadContext(directory, modelId, { profiles, implementations })`. Selection
133
+ precedes either default weight loader. The selected code owns loading, including
134
+ partial-allocation cleanup on failure, and returns the model metadata and a
135
+ `serving` binding. `ModelServingBinding` supplies planning, serial methods, batch
136
+ groups, prompt/media preparation, restore, diagnostics and optional embeddings.
137
+ One implementation may expose multiple methods with its own selection policy.
138
+ The host owns loaded resources; the server borrows the context.
139
+
140
+ The executable replacement example is
141
+ [`tests/serve/model-replacement.test.ts`](../../tests/serve/model-replacement.test.ts).
142
+ It selects one synthetic exact artifact registration and exercises two custom
143
+ methods through the public loader and HTTP API, without a concrete model class.
144
+
145
+ ## Native runtime
146
+
147
+ Library consumers bypass the CLI's first-run step, so call
148
+ `ensureNativeRuntime()` once before constructing a model on a machine that
149
+ may not have the MLX native runtime yet. It downloads the sha256-verified
150
+ native pack (MLX plus the direct-Colibri expert-I/O helper) to
151
+ `~/Library/Caches/mlx-bun/` and is a no-op when the runtime is already
152
+ present (beside the executable, already cached, `MLX_BUN_LIBMLXC`, or
153
+ installed via homebrew). It resolves to the runtime directory.
154
+ `nativeRuntimeDir()` returns the resolved directory, or `null` on a fresh
155
+ machine.
156
+
157
+ ```ts
158
+ import { ensureNativeRuntime, nativeRuntimeDir } from "mlx-bun";
159
+
160
+ if (nativeRuntimeDir() === null) console.log("first run: fetching the native pack");
161
+ const runtime = await ensureNativeRuntime({
162
+ onProgress: (received, total) => console.log(`${received}/${total}`),
163
+ });
164
+ console.log(runtime);
165
+ ```
166
+
167
+ `ensureNativeRuntime(opts?)` also accepts `url`, `sha256`, `sizeBytes`, and
168
+ `destDir` overrides for pinned/offline installs; production callers pass
169
+ nothing.
170
+
171
+ ## Config and weights
172
+
173
+ `loadModelConfig(modelDir)` parses `config.json` (and, for GLM-5.2,
174
+ `generation_config.json`) into the normalized `ModelConfig` every other
175
+ entry point consumes: `modelType`, `text` (layer/head geometry, rope,
176
+ window, MoE), `quantization`, `kvQuant` (from `kv_config.json`),
177
+ `eosTokenIds`, `hasVisionSidecar`, and the `raw` object.
178
+
179
+ `Weights.open(modelDir)` opens every safetensors shard through the mlx
180
+ native loader on the CPU stream: header-parsed, mmap-backed, no bytes
181
+ materialize until a tensor is touched. It is the input to `createModel` and
182
+ to the concrete model constructors. Ownership is explicit: `dispose()`
183
+ frees every native map and cached tensor; `releaseShard(file)` frees one
184
+ shard's materialized tensors (it transparently re-opens on the next
185
+ `tensor()` that needs it — the seam whole-model transforms use at 27B
186
+ scale). `tensor(name)`, `has(name)`, `info(name)`, `fileOf(name)`, and
187
+ `tensorNames` are the read side.
188
+
189
+ ```ts
190
+ import { loadModelConfig, Weights } from "mlx-bun";
191
+
192
+ const config = await loadModelConfig(dir);
193
+ const weights = await Weights.open(dir);
194
+ console.log(config.modelType, config.text.numHiddenLayers, weights.tensorNames.length);
195
+ weights.dispose();
196
+ ```
51
197
 
52
198
  ## Model profiles
53
199
 
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:
200
+ `openModel()`, `createModel()`, and `loadContext()` resolve a declared
201
+ model profile before they open weights. A profile declares construction
202
+ only — loader, model graph, generation loop, specialization — and carries
203
+ its fidelity contract (`l1` bit-exact vs mlx-lm, `l2` bit-exact vs
204
+ mlx-optiq, `l3` measured). Exact artifact profiles outrank family
205
+ profiles; dedicated/generated graphs outrank the universal fallback.
206
+ Profile selection never rewrites generation options: `kvConfig`,
207
+ TurboQuant KV, adapters, grammar, sampling, or an explicitly configured
208
+ draft method stay authoritative.
209
+
210
+ `resolveModelProfile(config, options?)` returns a `ResolvedModelProfile`:
211
+ `profile` (the declaration), `artifact` (`fingerprint` —
212
+ `hf:<org>/<repo>@<revision>` when the path is an immutable Hugging Face
213
+ snapshot, else `null` — plus `configFingerprint`), and `exactArtifact`.
214
+ `configFingerprint(config)` is the stable hash over every config field that
215
+ changes the decode graph structure plus the kv_config layout.
216
+ `externalArtifactFingerprint(modelDir)` is the HF-snapshot identity rule
217
+ on its own. `BUILTIN_ARTIFACT_PROFILES` lists the exact declarations this
218
+ repository backs with parity or measured evidence; `ENGINE_CAPABILITIES`
219
+ is the frozen capability vocabulary profiles declare against.
220
+
221
+ ```ts
222
+ import { loadModelConfig, resolveModelProfile } from "mlx-bun";
223
+
224
+ const config = await loadModelConfig(dir);
225
+ const resolved = resolveModelProfile(config);
226
+ console.log(resolved.profile.id);
227
+ console.log(resolved.artifact.fingerprint); // hf:<org>/<repo>@<revision> or null
228
+ console.log(resolved.profile.fidelity); // { tier, oracle, claim }
229
+ console.log(resolved.profile.execution); // { loader, graph, loop, specialization }
230
+ ```
231
+
232
+ For a local artifact that is not stored under an immutable HF snapshot
233
+ path, an application supplies its own provenance fingerprint and exact
234
+ declaration. The resolver checks the declared config fingerprint and the
235
+ required engine capabilities; an exact match that fails either check
236
+ refuses instead of taking the family or generic fallback.
58
237
 
59
238
  ```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
239
+ import {
240
+ configFingerprint,
241
+ loadModelConfig,
242
+ resolveModelProfile,
243
+ type ArtifactModelProfile,
244
+ } from "mlx-bun";
245
+
246
+ const config = await loadModelConfig(dir);
247
+ const mine: ArtifactModelProfile = {
248
+ id: "acme/gemma4-e4b-custom@2026-08",
249
+ artifactFingerprint: "acme:gemma4-e4b-custom@sha256-…",
250
+ configFingerprint: configFingerprint(config),
251
+ fidelity: { tier: "l1", oracle: "mlx-lm", claim: "bit-exact" },
252
+ // Must cover everything the execution composition implies.
253
+ requiredCapabilities: ["safetensors", "autoregressive", "gemma4-graph"],
254
+ execution: {
255
+ loader: "safetensors", graph: "gemma4",
256
+ loop: "autoregressive", specialization: "dedicated",
257
+ },
258
+ };
259
+ const resolved = resolveModelProfile(config, {
260
+ artifactFingerprint: mine.artifactFingerprint,
261
+ artifactProfiles: [mine],
262
+ });
263
+ console.log(resolved.exactArtifact); // true
65
264
  ```
66
265
 
67
- Direct library construction can resolve and pass the same immutable value:
266
+ ## Opening a model
267
+
268
+ `openModel(modelDir, options?)` is the artifact-aware constructor and the
269
+ preferred entry point: it loads the config, resolves the profile, and
270
+ either opens safetensors weights and builds the graph the profile names, or
271
+ — when the profile's loader is `colibri` — routes to
272
+ `openGlm52RuntimeModel`. It resolves to a `RuntimeModel`, the union every
273
+ other API accepts (`Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model
274
+ | Qwen3MoeModel | DiffusionGemmaModel | Glm52Model | UniversalDenseModel`).
275
+ `options` is `Glm52RuntimeOpenOptions`; models other than the direct
276
+ Colibri GLM-5.2 artifact ignore it.
277
+
278
+ `createModel(weights, config, resolved?)` is the direct-construction form
279
+ for callers that already hold `Weights` and a `ModelConfig` (and
280
+ optionally a previously resolved profile — it re-validates the profile
281
+ against the config). It throws for the GLM-5.2 graph, which has no
282
+ ordinary safetensors index and must go through `openModel` /
283
+ `openGlm52RuntimeModel`.
284
+
285
+ `openGlm52RuntimeModel(modelDir, options?)` opens the direct Colibri
286
+ artifact through the bounded expert runtime and resolves to `{ model, plan }`
287
+ — the model plus the exact header-derived memory plan (`Glm52MemoryPlan`)
288
+ that was checked before any resident tensor or expert slab was touched.
289
+ Options: `memoryBudgetBytes` (whole-process ceiling; default = the smaller
290
+ of the validated preset and physical RAM), `contextTokens`,
291
+ `maxGenerationTokens`, `batchSize`, `enableMtp` (default on),
292
+ `mtpDraftTokens`, `libraryPath` (expert-I/O dylib override), and
293
+ `machineBytes` (test seam).
68
294
 
69
295
  ```ts
70
- import { loadModelConfig, Weights, resolveModelProfile, createModel } from "mlx-bun";
296
+ import { createModel, loadModelConfig, openModel, resolveModelProfile, Weights } from "mlx-bun";
71
297
 
298
+ // Preferred.
299
+ const model = await openModel(dir);
300
+
301
+ // Equivalent for safetensors artifacts, with each step visible.
72
302
  const config = await loadModelConfig(dir);
73
303
  const profile = resolveModelProfile(config);
74
- const model = createModel(await Weights.open(dir), config, profile);
304
+ const same = createModel(await Weights.open(dir), config, profile);
305
+
306
+ // The direct Colibri GLM-5.2 artifact takes its load-only resource controls here.
307
+ const glm = await openModel(glmDir, {
308
+ contextTokens: 4096, maxGenerationTokens: 256, enableMtp: false,
309
+ });
310
+ ```
311
+
312
+ Direct `generate()` on a GLM-5.2 model supports the full
313
+ sampling/grammar/logprobs contract but does not itself mount a drafter;
314
+ native MTP is a serving-context feature (`loadContext`).
315
+
316
+ ## Concrete model classes
317
+
318
+ `Gemma4Model`, `MiniCPM5Model`, and `Qwen3Model` are exported today for
319
+ callers that need the concrete type (e.g. `model.layers`,
320
+ `model.loraTargets()`, `Qwen3Model.embedPooled`). Each constructs as
321
+ `new X(weights, config)` from `Weights` + `ModelConfig` and exposes
322
+ `makeCache(): Cache[]`, `forward(tokens, cache)`, and `config`.
323
+
324
+ > **Prefer `openModel` / `createModel`.** These class exports may be removed
325
+ > from the public surface: the factory is the supported way to obtain a
326
+ > `RuntimeModel`, and the other graphs (Qwen3.5, Qwen3-MoE, DiffusionGemma,
327
+ > GLM-5.2, the universal dense graph) are already reachable only through
328
+ > it. Narrow with `instanceof` when a concrete type is genuinely needed
329
+ > (`isEmbeddingModel` below is the shipped example).
330
+
331
+ ```ts
332
+ import { Gemma4Model, loadModelConfig, Weights } from "mlx-bun";
333
+
334
+ const config = await loadModelConfig(dir);
335
+ const gemma = new Gemma4Model(await Weights.open(dir), config); // same object createModel returns
336
+ console.log(gemma.layers.length, gemma.windowSize);
337
+ ```
338
+
339
+ ## Tokenizer
340
+
341
+ `loadTokenizer(modelDir)` loads `tokenizer.json` + `tokenizer_config.json`
342
+ through a pure-JS tokenizer (no native code; embeds in `bun build
343
+ --compile`) with round-trip parity against the Python oracle. The result
344
+ exposes `encode(text, addSpecialTokens = true)`, `decode(ids,
345
+ skipSpecialTokens = false)`, `idToToken(id)` (the undecoded vocab piece,
346
+ for logprobs), `bosTokenId`, `eosTokenId`, and the streaming-detokenizer
347
+ parity flags (`trimsLeadingSpace`, `bareSpaceTokenId`) the server consults.
348
+ `encode` memoizes byte-identical inputs and splices incrementally on
349
+ shared prefixes.
350
+
351
+ ```ts
352
+ import { loadTokenizer } from "mlx-bun";
353
+
354
+ const tok = await loadTokenizer(dir);
355
+ const ids = tok.encode("The quick brown fox", false); // no BOS
356
+ console.log(ids, tok.decode(ids), tok.idToToken(ids[0]!));
75
357
  ```
76
358
 
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
-
92
- > **First run:** library consumers bypass the CLI's first-run step, so
93
- > call `await ensureNativeRuntime()` once before constructing a model on
94
- > a machine that may not have the MLX native runtime yet — it downloads
95
- > the sha256-verified native pack (MLX plus the direct-Colibri expert-I/O
96
- > helper) to `~/Library/Caches/mlx-bun/` and is
97
- > a no-op when the runtime is already present (beside the executable,
98
- > already cached, or installed via homebrew). `nativeRuntimeDir()`
99
- > returns the resolved directory, or `null` on a fresh machine.
359
+ ## Chat templates
360
+
361
+ `ChatTemplate.load(modelDir, { disableThinking? })` renders the model's own
362
+ `chat_template` (from `tokenizer_config.json` or `chat_template.jinja`)
363
+ via a pure-JS Jinja engine, so the template cannot rot when the model
364
+ updates it; GLM-5.2's pinned template ships as a built-in renderer.
365
+ `render(messages, options?)` takes OpenAI-shaped `ChatMessage`s (`role`,
366
+ `content`, `tool_calls`, `tool_call_id`, `name`, `reasoning`) and
367
+ `RenderOptions`: `addGenerationPrompt` (default true), `tools`,
368
+ `enableThinking`, `reasoningEffort` (`"xhigh" | "medium" | "low"`, only for
369
+ templates that read it — see `readsReasoningEffort`), and
370
+ `preserveThinking`. Capability flags on the instance: `supportsThinking`,
371
+ `thinkingFormat` (`"think-tag" | "gemma-channel" | null`),
372
+ `readsReasoningEffort`, `readsPreserveThinking`.
373
+
374
+ ```ts
375
+ import { ChatTemplate } from "mlx-bun";
376
+
377
+ const template = await ChatTemplate.load(dir);
378
+ const rendered = template.render(
379
+ [
380
+ { role: "system", content: "Answer briefly." },
381
+ { role: "user", content: "What is 2+2?" },
382
+ ],
383
+ { enableThinking: template.supportsThinking ? false : undefined },
384
+ );
385
+ ```
100
386
 
101
387
  ## generate(model, promptTokens, options) → Generation
102
388
 
103
- `Generation` is an `AsyncIterable<GeneratedToken>` with a `stats`
104
- property (`GenerateStats`, available after iteration completes — also
105
- after an early `break`, which cleanly cancels the in-flight step).
389
+ `generate` is the streaming port of mlx-lm's `generate_step`: chunked
390
+ prefill with per-chunk cache evaluation, pipelined decode via async eval,
391
+ on-device sampling (only the chosen token id crosses to JS). It returns a
392
+ `Generation`: an `AsyncIterable<GeneratedToken>` (`{ token, index,
393
+ logprobs? }`) with a `stats` property (`GenerateStats`, available after
394
+ iteration completes — also after an early `break`, which cleanly cancels
395
+ the in-flight step). Adapters, the wired-memory scope, and GLM usage
396
+ flushing are all scoped to the iteration.
106
397
 
107
398
  `GenerateOptions` (all optional):
108
399
 
109
400
  | option | default | notes |
110
401
  |---|---|---|
111
402
  | `maxTokens` | 512 | |
112
- | `temperature` / `topP` / `topK` / `seed` | greedy when temperature 0 | on-device sampling; seeded per (seed, step) |
403
+ | `temperature` / `topP` / `topK` / `minP` / `minTokensToKeep` / `seed` | greedy when temperature 0 | on-device sampling; seeded per (seed, step); reference chain order top-p → min-p → XTC → top-k |
404
+ | `xtcProbability` / `xtcThreshold` / `xtcSpecialTokens` | off | XTC ("exclude top choices") |
405
+ | `hlg` / `curve` | off | tone-curve and log-prob transfer-curve samplers (replace the flat temperature slope) |
406
+ | `logitBias` | none | additive per-token-id bias, applied before penalties |
113
407
  | `repetitionPenalty` / `repetitionContextSize` | off / 20 | |
408
+ | `presencePenalty` / `frequencyPenalty` (+ `…ContextSize`) | off | OpenAI-style, may be negative |
114
409
  | `eosTokenIds` | from config | includes the tool-handoff token |
115
410
  | `prefillChunkSize` | 2048 | matches mlx-lm |
411
+ | `signal` | none | `AbortSignal`; cancels before allocation, between AR prefill chunks, and between decode steps. An active native operation completes before the next cancellation check. |
412
+ | `onPrefillDone` / `snapshotAt` | — | fired once at the stable cache boundary before further KV is written (the server's prompt-cache snapshot hook) |
116
413
  | `cache` | fresh | pre-warmed `Cache[]`; `cache[0].offset` tokens are treated as already prefilled. Caller keeps ownership |
117
- | `kvBits` / `kvGroupSize` / `quantizedKvStart` | off / 64 / 5000 | uniform KV quantization (4 or 8), incl. rotating caches (Phase 9) |
414
+ | `kvBits` / `kvGroupSize` / `quantizedKvStart` | off / 64 / 5000 | uniform KV quantization (4 or 8), incl. rotating caches |
118
415
  | `kvConfig` | off | per-layer mixed precision from `kv_config.json` (`config.kvQuant`); overrides `kvBits`, start 0 — optiq serve semantics |
416
+ | `turboQuant` | off | TurboQuant KV scheme (docs/design/turboquant.md), a rotation-based alternative to uniform `kvBits`; mutually exclusive with `kvBits`/`kvConfig` |
417
+ | `pagedKv` | off | `{ blockSize? }` — OPTIONAL paged KV cache (docs/design/kv-cache.md); v1 scope is serial batch=1 Gemma4-family bf16, mutually exclusive with `kvBits`/`kvConfig`/`turboQuant`/draft/compiled decode |
119
418
  | `adapters` | none | mounted LoRA adapter ids, applied for exactly this generation |
120
- | `promptEmbeddings` / `imageMask` | — | vision path (see `src/vision/`) |
121
- | `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 |
419
+ | `promptEmbeddings` / `imageMask` / `multimodalMask` / `visionPixels` | — | vision and audio paths (see `src/vision/`, docs/design/generic-model-support.md); caller keeps ownership |
420
+ | `logprobs` / `topLogprobs` | off | per-token logprob capture (mlx_lm.server parity): the emitted token's log-probability and the top-k (id, logprob) pairs — full-vocab log-softmax after logits processors, before the sampler |
122
421
  | `grammar` | off | a compiled `GrammarController` (`src/grammar.ts`) for structured output — masks invalid tokens to `-inf` each step (L2-class, oMLX oracle) |
123
- | `turboQuant` | off | TurboQuant KV scheme (docs/design/turboquant-kv.md), a rotation-based alternative to uniform `kvBits`; mutually exclusive with `kvBits`/`kvConfig` |
124
- | `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 |
125
422
 
126
423
  `GenerateStats`: `promptTokens`, `cachedTokens`, `generatedTokens`,
127
- `prefillTps` / `prefillMs`, `decodeTps` / `decodeMs`, `cacheTokens`
128
- (the exact token sequence whose KV is in the cache — feed it to
129
- `PromptCache.put`). Timing semantics match mlx-lm: the prompt clock
130
- runs until the first token arrives (the prefill→decode boundary is
131
- prompt time), the decode clock from there. An optional `spec` field
132
- carries speculative-decoding telemetry (serve `--draft-model` path
133
- only): `drafted`, `accepted`, `targetCalls`, and per-draft-position
424
+ `prefillTps` / `prefillMs`, `decodeTps` / `decodeMs`, `cacheTokens` (the
425
+ exact token sequence whose KV is in the cache — feed it to
426
+ `PromptCache.put`). Timing semantics match mlx-lm: the prompt clock runs
427
+ until the first token arrives (the prefill→decode boundary is prompt time),
428
+ the decode clock from there. An optional `spec` field carries
429
+ speculative-decoding telemetry (serve `--draft-model` path only):
430
+ `drafted`, `accepted`, `targetCalls`, and per-draft-position
134
431
  `draftedByPos` / `acceptedByPos` counts.
135
432
 
136
- ## Serving pieces
137
-
138
433
  ```ts
139
- import { loadContext, createServer } from "mlx-bun";
434
+ import { generate } from "mlx-bun";
140
435
 
141
- const ctx = await loadContext(dir, "my-model", { memoryBudgetBytes: 12e9 });
142
- const server = createServer(ctx, 8080, {
143
- promptCacheBytes: 2e9, // byte-capped LRU (never count-capped)
144
- kvQuant: undefined, // default: kv_config.json; "off" | "config" | bits
145
- memoryBudgetBytes: 12e9, // admission control — the only OOM defense
436
+ const gen = generate(model, promptIds, {
437
+ maxTokens: 256,
438
+ temperature: 0.7, topP: 0.95, seed: 42, // reproducible sampling
439
+ kvBits: 8, quantizedKvStart: 4096, // optional: quantized KV past 4k
440
+ logprobs: true, topLogprobs: 5,
146
441
  });
442
+ for await (const t of gen) {
443
+ process.stdout.write(tok.decode([t.token], true));
444
+ if (t.index > 64) break; // early break cancels cleanly
445
+ }
446
+ console.log(gen.stats?.decodeTps, gen.stats?.cacheTokens.length);
147
447
  ```
148
448
 
149
- `loadContext` refuses models that cannot serve any context within the
150
- budget (pre-GPU, mmap-only check). The server exposes OpenAI chat
151
- completions, Anthropic `/v1/messages`, OpenAI Responses, embeddings,
152
- adapters, and `/stats` — [server-api.md](./server-api.md).
449
+ ## Text embeddings
153
450
 
154
- For GLM-5.2, pass its load-only resource controls under `glm`:
451
+ Qwen3-Embedding models run on the plain-Qwen3 backbone, the only graph
452
+ exposing `embedPooled` (last-token hidden → L2-norm). `isEmbeddingModel`
453
+ narrows a `RuntimeModel` to `Qwen3Model`; `embedOne(model, tok, text,
454
+ instruction?)` and `embedMany(model, tok, texts, instruction?)` encode
455
+ without BOS, append the `<|endoftext|>` pooling token, run one forward per
456
+ text (the runtime is single-sequence), and return `EmbedResult`s:
457
+ `vector` (`Float32Array`, L2-normalized, so dot product = cosine) and
458
+ `tokens`. `withInstruction(text, instruction?)` applies Qwen3-Embedding's
459
+ query format (`Instruct: …\nQuery:…`); documents embed raw, only queries
460
+ take an instruction. These are the same vectors the CLI (`mlx-bun embed`)
461
+ and `/v1/embeddings` produce — bit-exact vs mlx-lm.
155
462
 
156
463
  ```ts
157
- const ctx = await loadContext(dir, "glm-5.2", {
158
- glm: {
159
- contextTokens: 4096,
160
- maxGenerationTokens: 128,
161
- batchSize: 8,
162
- enableMtp: true, // default; native MTP routes requests serial+spec
163
- },
164
- });
464
+ import { embedMany, embedOne, isEmbeddingModel, loadTokenizer, openModel } from "mlx-bun";
465
+
466
+ const model = await openModel(embedDir); // a Qwen3-Embedding snapshot
467
+ if (!isEmbeddingModel(model)) throw new Error("not an embedding model");
468
+ const tok = await loadTokenizer(embedDir);
469
+
470
+ const query = embedOne(model, tok, "cats", "Given a query, retrieve relevant passages");
471
+ const docs = embedMany(model, tok, ["the cat sat", "a kitten rested", "tax law"]);
472
+ const cosine = (a: Float32Array, b: Float32Array) =>
473
+ a.reduce((s, x, i) => s + x * b[i]!, 0);
474
+ console.log(docs.map((d) => cosine(query.vector, d.vector)));
165
475
  ```
166
476
 
167
- `openModel()` and `loadContext()` both select the streamed Colibri runtime;
168
- callers do not use `Weights.open()` or `createModel()` for this artifact.
169
- Direct `generate()` supports the full sampling/grammar/logprobs contract but
170
- does not itself mount a drafter; native MTP is a serving-context feature.
477
+ ## Model discovery (Registry)
171
478
 
172
- ## Text embeddings
479
+ `Registry` is the model index behind `mlx-bun ls` / `scan` / `/v1/models`: a
480
+ `bun:sqlite` cache (default `~/.cache/mlx-bun/registry.sqlite`, or
481
+ `":memory:"`) of every snapshot in the Hugging Face hub cache (`HF_HUB_CACHE`
482
+ > `HF_HOME/hub` > `~/.cache/huggingface/hub`). `scan(hubDir?)` re-walks the
483
+ cache and returns the record count; `list(filter?)` returns `ModelRecord`s
484
+ (`path`, `repoId`, `modelType`, `sizeBytes`, `expertsBytes`, `sidecarBytes`,
485
+ quantization fields, vision/audio/kv-config/tool-template capability flags,
486
+ `license`) filtered by `vision`, `maxBytes`, or a substring `query`;
487
+ `listCanonical(filter?)` collapses to one record per repo (the `refs/main`
488
+ snapshot); `resolve(query)` returns exactly one model or throws listing the
489
+ candidates; `close()` closes the database.
173
490
 
174
491
  ```ts
175
- import { createModel, loadTokenizer, isEmbeddingModel, embedMany } from "mlx-bun";
176
-
177
- const model = createModel(await Weights.open(dir), config); // Qwen3-Embedding
178
- if (!isEmbeddingModel(model)) throw new Error("not an embedding model");
179
- const tok = await loadTokenizer(dir);
492
+ import { Registry } from "mlx-bun";
180
493
 
181
- const [a, b] = embedMany(model, tok, ["the cat sat", "a kitten rested"]);
182
- // a.vector / b.vector are Float32Array, L2-normalized → dot product = cosine.
494
+ const reg = new Registry();
495
+ await reg.scan();
496
+ for (const m of reg.listCanonical({ vision: true })) console.log(m.repoId, m.sizeBytes);
497
+ const m = reg.resolve("e4b"); // unique-match query
498
+ console.log(m.path);
499
+ reg.close();
183
500
  ```
184
501
 
185
- `isEmbeddingModel` narrows a `RuntimeModel` to the plain-Qwen3 backbone (the
186
- only one exposing `embedPooled`: last-token hidden → L2-norm). `embedOne`/
187
- `embedMany` append the `<|endoftext|>` pooling token and, given an optional
188
- `instruction`, apply Qwen3-Embedding's query format. Same vectors the CLI
189
- (`mlx-bun embed`) and `/v1/embeddings` produce — bit-exact vs mlx-lm.
190
-
191
- ## Prompt cache (in-process prefix reuse)
502
+ ## Downloads
192
503
 
193
- > **Note:** `PromptCache`, `AdapterManager`, `saveKvCache`, and
194
- > `loadKvCache` are **not** re-exported from the public `"mlx-bun"`
195
- > entry point (`src/index.ts`). Import them from their internal paths
196
- > as shown below; this is intentional — they are implementation-level
197
- > pieces rather than stable public API surface.
504
+ `downloadModel(repoId, opts?)` fetches a Hugging Face repo into the
505
+ standard hub cache layout (blobs + snapshots + refs), resumable and
506
+ sha-verified through the same machinery the native pack uses, with a disk
507
+ preflight that credits verified blobs and partial prefixes before the first
508
+ payload. It resolves to the snapshot directory — the `dir` every other
509
+ entry point takes. `DownloadOptions`: `revision` (default `main`),
510
+ `cacheDir`, `endpoint`, `token` (default: the local HF login), and
511
+ `onProgress(file, received, total)`. Multi-GB downloads are the caller's
512
+ decision; nothing here starts one implicitly.
198
513
 
199
514
  ```ts
200
- import { PromptCache } from "./src/prompt-cache";
201
- const cache = new PromptCache(2e9); // BYTE-capped LRU
202
- const hit = cache.take(promptIds, /*namespace*/ ""); // longest common prefix, trimmed
203
- const caches = hit?.caches ?? model.makeCache();
204
- const gen = generate(model, promptIds, { cache: caches });
205
- for await (const t of gen) { /* … */ }
206
- cache.put(gen.stats!.cacheTokens, caches, ""); // hand ownership back
515
+ import { DEFAULT_REPO_ID, downloadModel } from "mlx-bun";
516
+
517
+ const dir = await downloadModel(DEFAULT_REPO_ID, {
518
+ onProgress: (file, received, total) => console.log(file, received, total),
519
+ });
520
+ console.log(dir); // …/snapshots/<sha>
207
521
  ```
208
522
 
209
- Namespace entries by adapter spec when LoRA is in play (KV computed
210
- under one adapter must not seed another's prefill).
523
+ ## Memory fit
524
+
525
+ `fit` is the deterministic memory contract behind `mlx-bun fit` and server
526
+ admission: weights (bytes from the registry) + KV at the requested context
527
+ (config geometry; sliding-window layers saturate at the window; quantized
528
+ schemes billed via `kvScheme`) + prefill transient (chunk × calibrated
529
+ bytes/token) against the machine's usable envelope (RAM × wired fraction,
530
+ or an explicit `usableBytes` budget). MoE-aware: decode reads active
531
+ experts only. Signature: `fit(config, weightsBytes, ctx, machine =
532
+ thisMachine(), chunk = 2048, expertsBytes = 0, usableBytes?, kvScheme?)` →
533
+ `FitReport` (`fits`, `maxSafeContext`, `predictedDecodeTps`, `weightsBytes`,
534
+ `kvBytes`, `transientBytes`, `totalBytes`, `usableBytes`, …).
535
+
536
+ `thisMachine(bandwidthGBs?)` detects the chip and RAM; `skuMatrix(config,
537
+ weightsBytes, ctx, expertsBytes?, kvScheme?)` runs `fit` across the
538
+ representative Apple Silicon SKU table. `DEFAULT_REPO_ID` is the default
539
+ model everywhere (e4b); `recommendedRepoId()` returns it;
540
+ `largestRecommendedRepoId(ramBytes?)` is the per-RAM-tier "biggest Gemma"
541
+ for explicit opt-in. `chooseAutoModel(candidates, defaultRepoId,
542
+ fitsFullBudget, fitsCoexistBudget)` is the pure auto-pick rule (default if
543
+ it fits → largest that leaves `COEXIST_FRACTION` headroom → largest that
544
+ fits at all); the caller supplies the fit predicates.
211
545
 
212
- ## KV-cache persistence (cross-process)
546
+ ```ts
547
+ import {
548
+ chooseAutoModel, COEXIST_FRACTION, DEFAULT_REPO_ID, fit, loadModelConfig,
549
+ Registry, skuMatrix, thisMachine,
550
+ } from "mlx-bun";
551
+
552
+ const reg = new Registry();
553
+ await reg.scan();
554
+ const m = reg.resolve("12B");
555
+ const config = await loadModelConfig(m.path);
556
+
557
+ const report = fit(config, m.sizeBytes, 8192, thisMachine(), 2048, m.expertsBytes);
558
+ console.log(report.fits, report.maxSafeContext, report.predictedDecodeTps);
559
+ console.table(skuMatrix(config, m.sizeBytes, 8192, m.expertsBytes));
560
+
561
+ const machine = thisMachine();
562
+ const pick = chooseAutoModel(
563
+ reg.listCanonical(),
564
+ DEFAULT_REPO_ID,
565
+ (c) => c.sizeBytes <= machine.ramBytes * 0.75,
566
+ (c) => c.sizeBytes <= machine.ramBytes * COEXIST_FRACTION,
567
+ );
568
+ console.log(pick?.repoId);
569
+ ```
570
+
571
+ ## Serving
572
+
573
+ `loadContext(modelDir, modelId?, opts?)` builds the `ServerContext` the
574
+ server runs on: the `model` (via the same profile resolution as
575
+ `openModel`), `profile`, `tokenizer`, `template`, `modelId`, lazily-loaded
576
+ vision/audio towers, the `adapters` manager, the model's `kvConfig`,
577
+ `genDefaults` from `generation_config.json`, an optional speculative
578
+ `draft` provider, and `glmMemoryPlan` for the Colibri runtime. It refuses
579
+ models that cannot serve any context within the budget (pre-GPU, mmap-only
580
+ check). `LoadContextOptions`: `memoryBudgetBytes`, `glm`
581
+ (`Glm52RuntimeOpenOptions`, ignored by other models), and the speculative
582
+ decoding trio `draftModelDir` / `numDraftTokens` / `draftKind`
583
+ (`"dspark" | "deepspec" | "assistant" | "two-model" | "ngram" | "mtp"`)
584
+ plus `ngramMax` / `ngramMin`. `profiles` and `implementations` supply the
585
+ engine-owned construction registry described above.
586
+
587
+ `createServer(ctx, port = 0, serverOptions?)` returns Bun's `Server`
588
+ exposing OpenAI chat completions, Anthropic `/v1/messages`, OpenAI
589
+ Responses, embeddings, adapters, and `/stats` —
590
+ [server-api.md](./server-api.md). `ServerOptions` mirrors the CLI flags in
591
+ [server-config.md](./server-config.md): `promptCacheBytes` (byte-capped
592
+ LRU, never count-capped), `kvBudgetBytes`, `kvQuant` (`"off" | "config" |
593
+ bits`; unset = bf16), `turboQuant`, `pagedKv`, `memoryBudgetBytes`
594
+ (admission control — the only OOM defense), `batch`, `hostname`,
595
+ `unixSocket`, `owner`, `defaultThinking`, `defaultTemperature` /
596
+ `defaultTopP` / `defaultTopK` / `defaultMaxTokens`, `defaultAdapter`, `hlg`,
597
+ and the SSD tier (`ssdCacheDir`, `ssdCacheMaxBytes`, `ssdDemoteIdleSec`,
598
+ `ssdCacheVerify`).
599
+
600
+ `shutdownServer(server, timeoutMs = 120_000)` stops accepting requests, waits for
601
+ active work and managed jobs, flushes durable caches, and closes session/runtime
602
+ services. It returns `{ stopped, timedOut, durability }`. A timeout reports
603
+ unfinished shutdown; it does not dispose a borrowed model context.
213
604
 
214
605
  ```ts
215
- import { saveKvCache, loadKvCache } from "./src/kv-store";
216
- saveKvCache("/tmp/prefix.kv", tokens, caches); // page-aligned file
217
- const { tokens, caches } = loadKvCache("/tmp/prefix.kv", model, {
218
- modelId, configFingerprint, tokenizerHash, verify: true,
606
+ import { createServer, loadContext } from "mlx-bun";
607
+
608
+ const ctx = await loadContext(dir, "my-model", { memoryBudgetBytes: 12e9 });
609
+ const server = createServer(ctx, 8080, {
610
+ promptCacheBytes: 2e9,
611
+ kvQuant: "config", // apply the model's kv_config.json
612
+ memoryBudgetBytes: 12e9,
613
+ owner: "embedded",
219
614
  });
220
- // Restore streams one tensor at a time from a read-only mmap into MLX-owned
221
- // storage, drops each clean file-page range, and unmaps before returning.
222
- // Note: loadKvCache accepts anything with makeCache(): Cache[] — it validates
223
- // the entry count against the cache list makeCache() returns (not
224
- // model.layers.length, which is wrong for KV-shared models like e4b, whose
225
- // makeCache() returns donors only). RuntimeModel qualifies directly, no cast.
615
+ console.log(server.port);
616
+ // … later
617
+ server.stop();
226
618
  ```
227
619
 
228
- Format v3 supports plain, rotating, quantized, TurboQuant, SSM, and GLM-5.2
229
- checkpoint-native caches. GLM rows use `mla`, `mla-dsa`, and `mtp-mla` kinds and
230
- store only compressed latent/RoPE/DSA state—never reconstructed full K/V.
231
- `Glm52NativeMtpSource.clonePersistentCache()` returns an owned MTP snapshot;
232
- `restorePersistentCache()` adopts a validated restored `mtp-mla` row. Writes
233
- are page-aligned, hashed, atomic (`.tmp` + fsync + rename), and available in
234
- both synchronous and event-loop-yielding `saveKvCacheAsync` forms.
235
-
236
- ## Registry + fit (model discovery and memory math)
620
+ For GLM-5.2, pass its load-only resource controls under `glm`:
237
621
 
238
622
  ```ts
239
- import { Registry, fit, skuMatrix } from "mlx-bun";
240
-
241
- const reg = new Registry(); // bun:sqlite, scans the HF cache
242
- await reg.scan();
243
- const m = reg.resolve("12B"); // unique-match query, throws on ambiguity
244
- const report = fit(await loadModelConfig(m.path), m.sizeBytes, 8192,
245
- undefined, undefined, m.expertsBytes);
246
- // report: { fits, maxSafeContext, predictedDecodeTps, kvBytes, … }
623
+ const glmCtx = await loadContext(glmDir, "glm-5.2", {
624
+ glm: {
625
+ contextTokens: 4096,
626
+ maxGenerationTokens: 128,
627
+ batchSize: 8,
628
+ enableMtp: true, // default; native MTP routes requests serial+spec
629
+ },
630
+ });
247
631
  ```
248
632
 
249
- `fit` is calibrated per chip family (MoE-aware: decode reads active
250
- experts only). The server uses it for admission; `mlx-bun fit <query>`
251
- is the CLI face.
633
+ ## Internal pieces referenced above (not exported)
252
634
 
253
- ## LoRA hot-swap
635
+ `PromptCache` (`src/prompt-cache.ts`, the byte-capped in-process prefix
636
+ cache), `AdapterManager` (`src/lora.ts`, LoRA hot-swap — reachable as
637
+ `ctx.adapters` on a `ServerContext`), and `saveKvCache` / `loadKvCache`
638
+ (`src/kv-store.ts`, cross-process KV persistence) are deliberately **not**
639
+ re-exported from `"mlx-bun"`: they are implementation-level and may change
640
+ without notice. In a clone they import from their internal paths; the
641
+ `adapters` and `cache` options of `generate` are the public seams they plug
642
+ into.
254
643
 
255
644
  ```ts
256
- import { AdapterManager } from "./src/lora";
257
- const adapters = new AdapterManager(model);
258
- await adapters.mount("upper", "/path/to/adapter-dir"); // all-or-nothing validation
259
- const gen = generate(model, promptIds, { adapters: ["upper"] });
645
+ import { PromptCache } from "./src/prompt-cache";
646
+ const cache = new PromptCache(2e9); // BYTE-capped LRU
647
+ const hit = cache.take(promptIds, /*namespace*/ ""); // longest common prefix, trimmed
648
+ const caches = hit?.caches ?? model.makeCache();
649
+ const gen = generate(model, promptIds, { cache: caches });
650
+ for await (const t of gen) { /* … */ }
651
+ cache.put(gen.stats!.cacheTokens, caches, ""); // hand ownership back
260
652
  ```
261
653
 
262
- Mount N adapters on one base; selection is per-generation; an
263
- unselected adapter costs nothing; base logits are byte-identical after
264
- any amount of switching.
654
+ Namespace entries by adapter spec when LoRA is in play (KV computed under
655
+ one adapter must not seed another's prefill).
265
656
 
266
657
  ## Memory & disposal rules
267
658
 
268
659
  - `MlxArray` wrappers have explicit `.dispose()` with a
269
660
  FinalizationRegistry backstop — dispose what you create in hot paths.
270
661
  - Caches you own (from `model.makeCache()` outside the prompt cache):
271
- dispose each via `c.dispose()` when done.
662
+ dispose each via `c.dispose()` when done. `Weights` you opened yourself:
663
+ `weights.dispose()`.
272
664
  - GPU OOM is **uncatchable** (mlx throws from a Metal completion
273
665
  handler → process death). Use `fit`/admission before generating, not
274
666
  try/catch around it.
275
667
  - Never read a typed array that native code wrote in a hot path; use
276
- the `read.*` helpers (bun#32054 — see PLAN-archive.md Phase 4 findings).
668
+ the `read.*` helpers (bun#32054 — see the Phase 4 findings in the PLAN
669
+ archive: `git show 3199c75:PLAN-archive.md`).
670
+
671
+ When embedding the high-level library in your own compiled application, pass
672
+ `command: ["/path/to/mlx-bun"]` to `openIsolatedHost`. Source/package consumers
673
+ use the installed CLI source automatically; compiled callers supply the worker
674
+ executable explicitly because their own executable is not the mlx-bun CLI.