mlx-bun 0.0.13 → 0.3.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 (427) hide show
  1. package/README.md +71 -578
  2. package/bin/mlx-bun.mjs +1 -1
  3. package/docs/README.md +91 -0
  4. package/docs/{design → archive}/hlg-sampling.md +3 -3
  5. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  6. package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
  7. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  8. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  9. package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
  10. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  11. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  12. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
  13. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  14. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  15. package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
  16. package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
  17. package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
  18. package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
  19. package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
  20. package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
  21. package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
  22. package/docs/design/batching.md +569 -0
  23. package/docs/design/decode-speed-program.md +3178 -169
  24. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  25. package/docs/design/generic-model-support.md +579 -334
  26. package/docs/design/kv-cache.md +680 -0
  27. package/docs/design/orpo-training.md +673 -1239
  28. package/docs/design/speculative-decoding.md +1627 -0
  29. package/docs/design/turboquant.md +2102 -0
  30. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  31. package/docs/design/web-chat-redesign.md +666 -1083
  32. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  33. package/docs/planning/release-notes-v0.3.0.md +35 -0
  34. package/docs/planning/video-series.md +421 -0
  35. package/docs/reference/benchmarks.md +1162 -0
  36. package/docs/reference/cli.md +395 -149
  37. package/docs/reference/distribution.md +321 -181
  38. package/docs/reference/environment.md +348 -0
  39. package/docs/reference/glossary.md +39 -0
  40. package/docs/reference/library-api.md +580 -142
  41. package/docs/reference/memory.md +227 -64
  42. package/docs/reference/models.md +137 -6
  43. package/docs/reference/server-api.md +938 -422
  44. package/docs/reference/server-config.md +652 -320
  45. package/docs/reference/training.md +393 -254
  46. package/docs/reference/troubleshooting.md +166 -0
  47. package/package.json +10 -5
  48. package/src/admission-plan.ts +18 -0
  49. package/src/anthropic.ts +92 -3
  50. package/src/audio/decode.ts +2 -2
  51. package/src/audio/features.ts +2 -2
  52. package/src/backends/mlx/autoregressive.ts +108 -0
  53. package/src/backends/mlx/batch-group.ts +1646 -0
  54. package/src/backends/mlx/diffusion.ts +39 -0
  55. package/src/backends/mlx/draft-target.ts +47 -0
  56. package/src/backends/mlx/evaluation.ts +105 -0
  57. package/src/backends/mlx/fill-append.ts +45 -0
  58. package/src/backends/mlx/gateway-binding.ts +71 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/memory-client.ts +189 -0
  61. package/src/backends/mlx/memory-guard.ts +40 -0
  62. package/src/backends/mlx/methods.ts +90 -0
  63. package/src/backends/mlx/model-prompt.ts +164 -0
  64. package/src/backends/mlx/model-serving.ts +79 -0
  65. package/src/backends/mlx/prefill.ts +33 -0
  66. package/src/backends/mlx/request-policy.ts +20 -0
  67. package/src/backends/mlx/rollback.ts +51 -0
  68. package/src/backends/mlx/serial-executor.ts +273 -0
  69. package/src/backends/mlx/speculative.ts +114 -0
  70. package/src/backends/mlx/state-views.ts +26 -0
  71. package/src/backends/mlx/text-engine.ts +73 -0
  72. package/src/cli.ts +189 -61
  73. package/src/client.ts +66 -0
  74. package/src/config.ts +21 -4
  75. package/src/contracts/completion.ts +16 -0
  76. package/src/contracts/execution.ts +49 -0
  77. package/src/contracts/generation.ts +103 -0
  78. package/src/contracts/host.ts +7 -0
  79. package/src/contracts/jobs.ts +66 -0
  80. package/src/contracts/pi-provider.ts +30 -0
  81. package/src/contracts/pi.ts +259 -0
  82. package/src/contracts/resources.ts +18 -0
  83. package/src/contracts/scheduling.ts +27 -0
  84. package/src/contracts/tsconfig.json +14 -0
  85. package/src/diffusion/diffusion-generate.ts +218 -153
  86. package/src/embed.ts +1 -1
  87. package/src/engine/admission.ts +59 -0
  88. package/src/engine/cancellation.ts +38 -0
  89. package/src/engine/engine.ts +56 -0
  90. package/src/engine/execution-coordinator.ts +70 -0
  91. package/src/engine/execution-plan.ts +51 -0
  92. package/src/engine/resources.ts +42 -0
  93. package/src/engine/scheduler.ts +67 -0
  94. package/src/engine/session.ts +247 -0
  95. package/src/eval/README.md +44 -1
  96. package/src/eval/kl.ts +263 -23
  97. package/src/eval/reference-logits.ts +207 -0
  98. package/src/eval/runner.ts +10 -79
  99. package/src/eval/tasks/bfcl.ts +2 -1
  100. package/src/eval/tasks/gsm8k.ts +2 -1
  101. package/src/eval/tasks/hashhop.ts +2 -1
  102. package/src/eval/tasks/humaneval.ts +5 -4
  103. package/src/eval/tasks/ifeval.ts +2 -1
  104. package/src/eval/tasks/mmlu.ts +2 -1
  105. package/src/expert-io.ts +3 -1
  106. package/src/expert-offload.ts +3 -1
  107. package/src/fill/echo-index.ts +188 -0
  108. package/src/fill/fill-session.ts +452 -0
  109. package/src/fill/proposal.ts +68 -0
  110. package/src/fill/schema-rows.ts +506 -0
  111. package/src/fill/tool-boundary.ts +103 -0
  112. package/src/fit.ts +24 -43
  113. package/src/generate.ts +740 -337
  114. package/src/grammar.ts +14 -8
  115. package/src/harness-pi.ts +15 -34
  116. package/src/hub-rest.ts +4 -4
  117. package/src/index.ts +27 -0
  118. package/src/inference/denoising.ts +17 -0
  119. package/src/inference/graph.ts +33 -0
  120. package/src/inference/prefill.ts +39 -0
  121. package/src/inference/rollback.ts +7 -0
  122. package/src/jobs/db.ts +4 -0
  123. package/src/jobs/index.ts +2 -0
  124. package/src/jobs/job-entry.ts +15 -14
  125. package/src/jobs/runner.ts +98 -13
  126. package/src/jobs/task-client.ts +19 -0
  127. package/src/jobs/types.ts +2 -58
  128. package/src/kv-scheme.ts +199 -0
  129. package/src/kv-store.ts +459 -234
  130. package/src/lab/README.md +18 -0
  131. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  132. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  133. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +7 -5
  134. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  135. package/src/library.ts +40 -0
  136. package/src/lora.ts +18 -0
  137. package/src/media-fetch.ts +20 -4
  138. package/src/memory/article.ts +1 -1
  139. package/src/memory/chunk.ts +1 -1
  140. package/src/memory/model.ts +32 -170
  141. package/src/memory/prompts.ts +1 -1
  142. package/src/memory/rest.ts +1 -1
  143. package/src/memory/schedule.ts +1 -1
  144. package/src/memory/stages.ts +4 -1
  145. package/src/memory/vault.ts +3 -4
  146. package/src/mlx/array.ts +9 -10
  147. package/src/mlx/compile.ts +12 -2
  148. package/src/mlx/ffi.ts +66 -22
  149. package/src/mlx/materialize.ts +23 -0
  150. package/src/mlx/metal-kernel.ts +10 -11
  151. package/src/mlx/ops.ts +64 -19
  152. package/src/mlx/quantized-rows.ts +36 -0
  153. package/src/mlx/token-bitmask.ts +52 -0
  154. package/src/mlx/turboquant-kv-decode.ts +82 -0
  155. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  156. package/src/mlx/turboquant-ops.ts +33 -3
  157. package/src/mlx/turboquant-tables.ts +2 -2
  158. package/src/model/batched-mask.ts +9 -3
  159. package/src/model/batched-quant.ts +2 -2
  160. package/src/model/batched-rotating-quant.ts +45 -99
  161. package/src/model/batched-rotating-state.ts +73 -0
  162. package/src/model/batched-rotating.ts +57 -106
  163. package/src/model/batched-row-storage.ts +127 -0
  164. package/src/model/compiled-decode.ts +4 -3
  165. package/src/model/diffusion-gemma.ts +8 -8
  166. package/src/model/factory.ts +87 -49
  167. package/src/model/fingerprint.ts +1 -1
  168. package/src/model/flash-attention.ts +3 -3
  169. package/src/model/gemma4-base.ts +187 -17
  170. package/src/model/gemma4.ts +6 -6
  171. package/src/model/generated/gemma4-12b.ts +52 -148
  172. package/src/model/generated/gemma4-26b.ts +34 -94
  173. package/src/model/generated/gemma4-e4b.ts +28 -76
  174. package/src/model/glm52-cache.ts +19 -9
  175. package/src/model/glm52-dsa.ts +13 -9
  176. package/src/model/glm52-memory.ts +28 -1
  177. package/src/model/implementation.ts +69 -0
  178. package/src/model/minicpm5.ts +7 -8
  179. package/src/model/profile.ts +443 -0
  180. package/src/model/qwen-append.ts +12 -0
  181. package/src/model/qwen-conv.ts +52 -0
  182. package/src/model/qwen3-delta.ts +98 -2
  183. package/src/model/qwen3-mrope.ts +252 -0
  184. package/src/model/qwen3_5-checkpoint.ts +164 -0
  185. package/src/model/qwen3_5.ts +336 -60
  186. package/src/model/support.ts +1 -1
  187. package/src/model/trellis-balanced-scatter.ts +51 -0
  188. package/src/model/trellis-linear.ts +672 -0
  189. package/src/model/trellis-shared-m.ts +151 -0
  190. package/src/model/trellis-shared-scatter.ts +144 -0
  191. package/src/model/trellis-splitk-prefill.ts +158 -0
  192. package/src/model/trellis-tiled-prefill.ts +136 -0
  193. package/src/model/trellis-vector-expand.ts +62 -0
  194. package/src/model/trellis-wide-prefill.ts +94 -0
  195. package/src/model/universal/dense.ts +1 -1
  196. package/src/model/universal/modules.ts +8 -1
  197. package/src/native/frame_extract.swift +140 -0
  198. package/src/native-pack.ts +15 -7
  199. package/src/pi-provider.ts +6 -64
  200. package/src/pi-web.ts +14 -320
  201. package/src/prompt-cache.ts +97 -54
  202. package/src/quantize/allocator.ts +1 -1
  203. package/src/quantize/atomic-output.ts +38 -0
  204. package/src/quantize/calibration.ts +2 -1
  205. package/src/quantize/config-writer.ts +16 -3
  206. package/src/quantize/index.ts +28 -1
  207. package/src/quantize/job.ts +11 -0
  208. package/src/quantize/quantizer.ts +265 -100
  209. package/src/quantize/rotate.ts +603 -0
  210. package/src/quantize/safetensors-writer.ts +79 -1
  211. package/src/quantize/sensitivity.ts +41 -59
  212. package/src/quantize/trellis.ts +586 -0
  213. package/src/quantize/weight-transform.ts +292 -0
  214. package/src/responses.ts +130 -3
  215. package/src/runtime-config.ts +81 -0
  216. package/src/sampler.ts +276 -4
  217. package/src/serve/admin-routes.ts +184 -0
  218. package/src/serve/aux-routes.ts +132 -0
  219. package/src/serve/batch-scheduler.ts +8 -1558
  220. package/src/serve/chat-request.ts +391 -0
  221. package/src/serve/chat-stage.ts +195 -0
  222. package/src/serve/checkpoint-identity.ts +64 -0
  223. package/src/serve/completion-executor.ts +354 -0
  224. package/src/serve/completion-sink.ts +157 -0
  225. package/src/serve/discovery-routes.ts +204 -0
  226. package/src/serve/generation-gateway.ts +211 -243
  227. package/src/serve/http.ts +159 -0
  228. package/src/serve/inference-request.ts +79 -0
  229. package/src/serve/isolate.ts +297 -105
  230. package/src/serve/kv-budget.ts +14 -0
  231. package/src/serve/lab-routes.ts +341 -0
  232. package/src/serve/lane-registry.ts +2 -1
  233. package/src/serve/model-admin-routes.ts +155 -0
  234. package/src/serve/model-binding.ts +43 -0
  235. package/src/serve/model-host.ts +512 -0
  236. package/src/serve/openai-wire.ts +103 -0
  237. package/src/serve/parent-application.ts +56 -0
  238. package/src/serve/pipeline.ts +33 -0
  239. package/src/serve/preparation.ts +33 -0
  240. package/src/serve/prompt-response-trace.ts +147 -0
  241. package/src/serve/request-plan.ts +162 -0
  242. package/src/serve/request-prep.ts +346 -0
  243. package/src/serve/response-lease.ts +24 -0
  244. package/src/serve/responses-client.ts +69 -0
  245. package/src/serve/session-completion-engine.ts +110 -0
  246. package/src/serve/session-search.ts +1 -1
  247. package/src/serve/static-routes.ts +101 -0
  248. package/src/serve/text-completion-stage.ts +104 -0
  249. package/src/serve/token-streams.ts +519 -0
  250. package/src/server.ts +523 -3551
  251. package/src/spec/assistant-source.ts +10 -44
  252. package/src/spec/deepspec-source.ts +6 -6
  253. package/src/spec/dflash-source.ts +9 -8
  254. package/src/spec/dspark/ab-stats.ts +3 -3
  255. package/src/spec/dspark/calibration.ts +2 -2
  256. package/src/spec/dspark/deepspec-module.ts +2 -2
  257. package/src/spec/dspark/generate-dflash.ts +2 -2
  258. package/src/spec/dspark/generate.ts +2 -2
  259. package/src/spec/dspark/loader.ts +1 -1
  260. package/src/spec/dspark/module-dflash.ts +5 -4
  261. package/src/spec/dspark/module.ts +3 -3
  262. package/src/spec/dspark/quantize-drafter.ts +1 -1
  263. package/src/spec/generate.ts +1 -1
  264. package/src/spec/glm52-mtp-source.ts +9 -1
  265. package/src/spec/ngram-source.ts +1 -1
  266. package/src/spec/prefix-state.ts +45 -0
  267. package/src/spec/qwen-mtp-source.ts +528 -0
  268. package/src/spec/serve-loop.ts +179 -213
  269. package/src/spec/source.ts +42 -10
  270. package/src/spec/two-model.ts +1 -1
  271. package/src/ssd-cache.ts +95 -24
  272. package/src/ssd-durability.ts +207 -0
  273. package/src/tool-call.ts +51 -14
  274. package/src/train/flash-cce.ts +14 -13
  275. package/src/train/forward.ts +38 -1
  276. package/src/train/loss.ts +3 -2
  277. package/src/train/prefix-shared.ts +5 -1
  278. package/src/train/segmented.ts +9 -6
  279. package/src/train/trainer.ts +9 -8
  280. package/src/vision/preprocess.ts +1 -1
  281. package/src/vision/prompt.ts +48 -2
  282. package/src/vision/qwen3vl-preprocess.ts +409 -0
  283. package/src/vision/qwen3vl-prompt.ts +196 -0
  284. package/src/vision/qwen3vl-tower.ts +555 -0
  285. package/src/vision/video-frames.ts +148 -0
  286. package/src/web/app.html +1 -1
  287. package/src/web/app.js +15 -6
  288. package/src/web/src/adapters-panel.ts +3 -3
  289. package/src/web/src/assistant.ts +2 -36
  290. package/src/web/src/chat.ts +16 -9
  291. package/src/web/src/composer.ts +4 -4
  292. package/src/web/src/hub.ts +1 -1
  293. package/src/web/src/markdown.ts +0 -0
  294. package/src/web/src/memory-panel.ts +1 -1
  295. package/src/web/src/model-picker.ts +1 -1
  296. package/src/web/src/palette.ts +1 -1
  297. package/src/web/src/protocol.ts +3 -39
  298. package/src/web/src/sessions.ts +1 -1
  299. package/src/web/src/ui-catalog.ts +4 -18
  300. package/src/web-assets.ts +47 -0
  301. package/src/weights.ts +134 -33
  302. package/docs/archive/mlx-bun-lab-report.html +0 -275
  303. package/docs/archive/wwdc-mlx-bun.html +0 -848
  304. package/docs/design/adapters-end-to-end.md +0 -205
  305. package/docs/design/batching-perf-path.md +0 -255
  306. package/docs/design/batching-v2-plan.md +0 -529
  307. package/docs/design/bucketing-stage.md +0 -197
  308. package/docs/design/compat-cli-surface-design.md +0 -230
  309. package/docs/design/docs-reorg-plan.md +0 -165
  310. package/docs/design/dspark-serving-program.md +0 -371
  311. package/docs/design/dspark-speculative-decoding.md +0 -268
  312. package/docs/design/grammar-spec-batching-integration.md +0 -301
  313. package/docs/design/memory-inference-path.md +0 -422
  314. package/docs/design/memory-synthesis.md +0 -287
  315. package/docs/design/memory-system.md +0 -237
  316. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  317. package/docs/design/omlx-adoption-map.md +0 -98
  318. package/docs/design/orpo-dynamic-lambda.md +0 -220
  319. package/docs/design/orpo-future-enhancements.md +0 -340
  320. package/docs/design/paged-kv-cache.md +0 -214
  321. package/docs/design/parallel-slots.md +0 -567
  322. package/docs/design/parity-tier-dag.md +0 -161
  323. package/docs/design/repo-cleanup-plan.md +0 -189
  324. package/docs/design/runtime-isolation.md +0 -263
  325. package/docs/design/segmented-backward-training.md +0 -543
  326. package/docs/design/spec-decode-larger-targets.md +0 -229
  327. package/docs/design/ssd-kv-cold-tier.md +0 -288
  328. package/docs/design/structured-output.md +0 -484
  329. package/docs/design/tauri-desktop-app.md +0 -127
  330. package/docs/design/the-dreaming-master-plan.md +0 -445
  331. package/docs/design/turboquant-kv.md +0 -235
  332. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  333. package/docs/design/web-chat-beat-matrix.md +0 -315
  334. package/docs/design/write-pipeline-entity.md +0 -468
  335. package/docs/investigations/curve-runs/bisector-route.json +0 -98
  336. package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
  337. package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
  338. package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
  339. package/docs/investigations/hlg-ab.html +0 -34
  340. package/docs/investigations/hlg-explorer.html +0 -238
  341. package/docs/investigations/hlg-figs/cliff.svg +0 -1
  342. package/docs/investigations/hlg-figs/cross.svg +0 -1
  343. package/docs/investigations/hlg-figs/edges.svg +0 -1
  344. package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
  345. package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
  346. package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
  347. package/docs/investigations/hlg-figs/terrain.svg +0 -1
  348. package/docs/investigations/hlg-report.html +0 -262
  349. package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
  350. package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  351. package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
  352. package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
  353. package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
  354. package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
  355. package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
  356. package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
  357. package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
  358. package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
  359. package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  360. package/docs/investigations/hlg-runs/hlg-map.json +0 -383
  361. package/docs/investigations/hlg-runs/run0-original.log +0 -159
  362. package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
  363. package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  364. package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  365. package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  366. package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  367. package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  368. package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  369. package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  370. package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
  371. package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
  372. package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
  373. package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
  374. package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
  375. package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  376. package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
  377. package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
  378. package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
  379. package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
  380. package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
  381. package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  382. package/docs/investigations/hlg-runs/runN-verify.log +0 -116
  383. package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  384. package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  385. package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  386. package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  387. package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  388. package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  389. package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
  390. package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  391. package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
  392. package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  393. package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  394. package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  395. package/docs/planning/IDEAS.md +0 -69
  396. package/docs/reference/embedding.md +0 -197
  397. package/docs/reference/features-matrix.md +0 -100
  398. package/docs/reference/orpo-quickstart.md +0 -135
  399. package/src/flags.ts +0 -13
  400. /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
  401. /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
  402. /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
  403. /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
  404. /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
  405. /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
  406. /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
  407. /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
  408. /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
  409. /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
  410. /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
  411. /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
  412. /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
  413. /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
  414. /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
  415. /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
  416. /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
  417. /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
  418. /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
  419. /package/docs/{planning → archive/planning}/journal.md +0 -0
  420. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  421. /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
  422. /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
  423. /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
  424. /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
  425. /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
  426. /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
  427. /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
- continuous-batching scheduler — a server-lane feature, not a library
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,192 +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
+ ```
197
+
198
+ ## Model profiles
199
+
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.
237
+
238
+ ```ts
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
264
+ ```
265
+
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).
294
+
295
+ ```ts
296
+ import { createModel, loadModelConfig, openModel, resolveModelProfile, Weights } from "mlx-bun";
297
+
298
+ // Preferred.
299
+ const model = await openModel(dir);
300
+
301
+ // Equivalent for safetensors artifacts, with each step visible.
302
+ const config = await loadModelConfig(dir);
303
+ const profile = resolveModelProfile(config);
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
51
317
 
52
- > **First run:** library consumers bypass the CLI's first-run step, so
53
- > call `await ensureNativeRuntime()` once before constructing a model on
54
- > a machine that may not have the MLX native runtime yet — it downloads
55
- > the sha256-verified native pack (MLX plus the direct-Colibri expert-I/O
56
- > helper) to `~/Library/Caches/mlx-bun/` and is
57
- > a no-op when the runtime is already present (beside the executable,
58
- > already cached, or installed via homebrew). `nativeRuntimeDir()`
59
- > returns the resolved directory, or `null` on a fresh machine.
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]!));
357
+ ```
358
+
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
+ ```
60
386
 
61
387
  ## generate(model, promptTokens, options) → Generation
62
388
 
63
- `Generation` is an `AsyncIterable<GeneratedToken>` with a `stats`
64
- property (`GenerateStats`, available after iteration completes — also
65
- 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.
66
397
 
67
398
  `GenerateOptions` (all optional):
68
399
 
69
400
  | option | default | notes |
70
401
  |---|---|---|
71
402
  | `maxTokens` | 512 | |
72
- | `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 |
73
407
  | `repetitionPenalty` / `repetitionContextSize` | off / 20 | |
408
+ | `presencePenalty` / `frequencyPenalty` (+ `…ContextSize`) | off | OpenAI-style, may be negative |
74
409
  | `eosTokenIds` | from config | includes the tool-handoff token |
75
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) |
76
413
  | `cache` | fresh | pre-warmed `Cache[]`; `cache[0].offset` tokens are treated as already prefilled. Caller keeps ownership |
77
- | `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 |
78
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 |
79
418
  | `adapters` | none | mounted LoRA adapter ids, applied for exactly this generation |
80
- | `promptEmbeddings` / `imageMask` | — | vision path (see `src/vision/`) |
81
- | `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 |
82
421
  | `grammar` | off | a compiled `GrammarController` (`src/grammar.ts`) for structured output — masks invalid tokens to `-inf` each step (L2-class, oMLX oracle) |
83
- | `turboQuant` | off | TurboQuant KV scheme (docs/design/turboquant-kv.md), a rotation-based alternative to uniform `kvBits`; mutually exclusive with `kvBits`/`kvConfig` |
84
- | `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 |
85
422
 
86
423
  `GenerateStats`: `promptTokens`, `cachedTokens`, `generatedTokens`,
87
- `prefillTps` / `prefillMs`, `decodeTps` / `decodeMs`, `cacheTokens`
88
- (the exact token sequence whose KV is in the cache — feed it to
89
- `PromptCache.put`). Timing semantics match mlx-lm: the prompt clock
90
- runs until the first token arrives (the prefill→decode boundary is
91
- prompt time), the decode clock from there. An optional `spec` field
92
- carries speculative-decoding telemetry (serve `--draft-model` path
93
- 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
94
431
  `draftedByPos` / `acceptedByPos` counts.
95
432
 
96
- ## Serving pieces
97
-
98
433
  ```ts
99
- import { loadContext, createServer } from "mlx-bun";
434
+ import { generate } from "mlx-bun";
100
435
 
101
- const ctx = await loadContext(dir, "my-model", { memoryBudgetBytes: 12e9 });
102
- const server = createServer(ctx, 8080, {
103
- promptCacheBytes: 2e9, // byte-capped LRU (never count-capped)
104
- kvQuant: undefined, // default: kv_config.json; "off" | "config" | bits
105
- 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,
106
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);
107
447
  ```
108
448
 
109
- `loadContext` refuses models that cannot serve any context within the
110
- budget (pre-GPU, mmap-only check). The server exposes OpenAI chat
111
- completions, Anthropic `/v1/messages`, OpenAI Responses, embeddings,
112
- adapters, and `/stats` — [server-api.md](./server-api.md).
449
+ ## Text embeddings
113
450
 
114
- 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.
115
462
 
116
463
  ```ts
117
- const ctx = await loadContext(dir, "glm-5.2", {
118
- glm: {
119
- contextTokens: 4096,
120
- maxGenerationTokens: 128,
121
- batchSize: 8,
122
- enableMtp: true, // default; native MTP routes requests serial+spec
123
- },
124
- });
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)));
125
475
  ```
126
476
 
127
- `openModel()` and `loadContext()` both select the streamed Colibri runtime;
128
- callers do not use `Weights.open()` or `createModel()` for this artifact.
129
- Direct `generate()` supports the full sampling/grammar/logprobs contract but
130
- does not itself mount a drafter; native MTP is a serving-context feature.
477
+ ## Model discovery (Registry)
131
478
 
132
- ## 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.
133
490
 
134
491
  ```ts
135
- import { createModel, loadTokenizer, isEmbeddingModel, embedMany } from "mlx-bun";
492
+ import { Registry } from "mlx-bun";
136
493
 
137
- const model = createModel(await Weights.open(dir), config); // Qwen3-Embedding
138
- if (!isEmbeddingModel(model)) throw new Error("not an embedding model");
139
- const tok = await loadTokenizer(dir);
140
-
141
- const [a, b] = embedMany(model, tok, ["the cat sat", "a kitten rested"]);
142
- // 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();
143
500
  ```
144
501
 
145
- `isEmbeddingModel` narrows a `RuntimeModel` to the plain-Qwen3 backbone (the
146
- only one exposing `embedPooled`: last-token hidden → L2-norm). `embedOne`/
147
- `embedMany` append the `<|endoftext|>` pooling token and, given an optional
148
- `instruction`, apply Qwen3-Embedding's query format. Same vectors the CLI
149
- (`mlx-bun embed`) and `/v1/embeddings` produce — bit-exact vs mlx-lm.
150
-
151
- ## Prompt cache (in-process prefix reuse)
502
+ ## Downloads
152
503
 
153
- > **Note:** `PromptCache`, `AdapterManager`, `saveKvCache`, and
154
- > `loadKvCache` are **not** re-exported from the public `"mlx-bun"`
155
- > entry point (`src/index.ts`). Import them from their internal paths
156
- > as shown below; this is intentional — they are implementation-level
157
- > 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.
158
513
 
159
514
  ```ts
160
- import { PromptCache } from "./src/prompt-cache";
161
- const cache = new PromptCache(2e9); // BYTE-capped LRU
162
- const hit = cache.take(promptIds, /*namespace*/ ""); // longest common prefix, trimmed
163
- const caches = hit?.caches ?? model.makeCache();
164
- const gen = generate(model, promptIds, { cache: caches });
165
- for await (const t of gen) { /* … */ }
166
- 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>
167
521
  ```
168
522
 
169
- Namespace entries by adapter spec when LoRA is in play (KV computed
170
- 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.
171
545
 
172
- ## 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.
173
604
 
174
605
  ```ts
175
- import { saveKvCache, loadKvCache } from "./src/kv-store";
176
- saveKvCache("/tmp/prefix.kv", tokens, caches); // page-aligned file
177
- const { tokens, caches } = loadKvCache("/tmp/prefix.kv", model, {
178
- 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",
179
614
  });
180
- // Restore streams one tensor at a time from a read-only mmap into MLX-owned
181
- // storage, drops each clean file-page range, and unmaps before returning.
182
- // Note: loadKvCache accepts anything with makeCache(): Cache[] — it validates
183
- // the entry count against the cache list makeCache() returns (not
184
- // model.layers.length, which is wrong for KV-shared models like e4b, whose
185
- // makeCache() returns donors only). RuntimeModel qualifies directly, no cast.
615
+ console.log(server.port);
616
+ // … later
617
+ server.stop();
186
618
  ```
187
619
 
188
- Format v3 supports plain, rotating, quantized, TurboQuant, SSM, and GLM-5.2
189
- checkpoint-native caches. GLM rows use `mla`, `mla-dsa`, and `mtp-mla` kinds and
190
- store only compressed latent/RoPE/DSA state—never reconstructed full K/V.
191
- `Glm52NativeMtpSource.clonePersistentCache()` returns an owned MTP snapshot;
192
- `restorePersistentCache()` adopts a validated restored `mtp-mla` row. Writes
193
- are page-aligned, hashed, atomic (`.tmp` + fsync + rename), and available in
194
- both synchronous and event-loop-yielding `saveKvCacheAsync` forms.
195
-
196
- ## Registry + fit (model discovery and memory math)
620
+ For GLM-5.2, pass its load-only resource controls under `glm`:
197
621
 
198
622
  ```ts
199
- import { Registry, fit, skuMatrix } from "mlx-bun";
200
-
201
- const reg = new Registry(); // bun:sqlite, scans the HF cache
202
- await reg.scan();
203
- const m = reg.resolve("12B"); // unique-match query, throws on ambiguity
204
- const report = fit(await loadModelConfig(m.path), m.sizeBytes, 8192,
205
- undefined, undefined, m.expertsBytes);
206
- // 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
+ });
207
631
  ```
208
632
 
209
- `fit` is calibrated per chip family (MoE-aware: decode reads active
210
- experts only). The server uses it for admission; `mlx-bun fit <query>`
211
- is the CLI face.
633
+ ## Internal pieces referenced above (not exported)
212
634
 
213
- ## 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.
214
643
 
215
644
  ```ts
216
- import { AdapterManager } from "./src/lora";
217
- const adapters = new AdapterManager(model);
218
- await adapters.mount("upper", "/path/to/adapter-dir"); // all-or-nothing validation
219
- 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
220
652
  ```
221
653
 
222
- Mount N adapters on one base; selection is per-generation; an
223
- unselected adapter costs nothing; base logits are byte-identical after
224
- 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).
225
656
 
226
657
  ## Memory & disposal rules
227
658
 
228
659
  - `MlxArray` wrappers have explicit `.dispose()` with a
229
660
  FinalizationRegistry backstop — dispose what you create in hot paths.
230
661
  - Caches you own (from `model.makeCache()` outside the prompt cache):
231
- dispose each via `c.dispose()` when done.
662
+ dispose each via `c.dispose()` when done. `Weights` you opened yourself:
663
+ `weights.dispose()`.
232
664
  - GPU OOM is **uncatchable** (mlx throws from a Metal completion
233
665
  handler → process death). Use `fit`/admission before generating, not
234
666
  try/catch around it.
235
667
  - Never read a typed array that native code wrote in a hot path; use
236
- 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.