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.
- package/README.md +71 -578
- package/bin/mlx-bun.mjs +1 -1
- package/docs/README.md +91 -0
- package/docs/{design → archive}/hlg-sampling.md +3 -3
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
- package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
- package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
- package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
- package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
- package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
- package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
- package/docs/design/batching.md +569 -0
- package/docs/design/decode-speed-program.md +3178 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +579 -334
- package/docs/design/kv-cache.md +680 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1627 -0
- package/docs/design/turboquant.md +2102 -0
- package/docs/design/unified-engine-frontier-plan.md +1831 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +34 -1
- package/docs/planning/release-notes-v0.3.0.md +35 -0
- package/docs/planning/video-series.md +421 -0
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +395 -149
- package/docs/reference/distribution.md +321 -181
- package/docs/reference/environment.md +348 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +580 -142
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +137 -6
- package/docs/reference/server-api.md +938 -422
- package/docs/reference/server-config.md +652 -320
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +10 -5
- package/src/admission-plan.ts +18 -0
- package/src/anthropic.ts +92 -3
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1646 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +47 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/gateway-binding.ts +71 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/memory-guard.ts +40 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/rollback.ts +51 -0
- package/src/backends/mlx/serial-executor.ts +273 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-views.ts +26 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/cli.ts +189 -61
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +49 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/resources.ts +18 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +51 -0
- package/src/engine/resources.ts +42 -0
- package/src/engine/scheduler.ts +67 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +263 -23
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +10 -79
- package/src/eval/tasks/bfcl.ts +2 -1
- package/src/eval/tasks/gsm8k.ts +2 -1
- package/src/eval/tasks/hashhop.ts +2 -1
- package/src/eval/tasks/humaneval.ts +5 -4
- package/src/eval/tasks/ifeval.ts +2 -1
- package/src/eval/tasks/mmlu.ts +2 -1
- package/src/expert-io.ts +3 -1
- package/src/expert-offload.ts +3 -1
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +24 -43
- package/src/generate.ts +740 -337
- package/src/grammar.ts +14 -8
- package/src/harness-pi.ts +15 -34
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +27 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +15 -14
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-scheme.ts +199 -0
- package/src/kv-store.ts +459 -234
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +7 -5
- package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +20 -4
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +32 -170
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +3 -4
- package/src/mlx/array.ts +9 -10
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +66 -22
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +64 -19
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/batched-mask.ts +9 -3
- package/src/model/batched-quant.ts +2 -2
- package/src/model/batched-rotating-quant.ts +45 -99
- package/src/model/batched-rotating-state.ts +73 -0
- package/src/model/batched-rotating.ts +57 -106
- package/src/model/batched-row-storage.ts +127 -0
- package/src/model/compiled-decode.ts +4 -3
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +87 -49
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/gemma4-base.ts +187 -17
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +52 -148
- package/src/model/generated/gemma4-26b.ts +34 -94
- package/src/model/generated/gemma4-e4b.ts +28 -76
- package/src/model/glm52-cache.ts +19 -9
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +28 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +7 -8
- package/src/model/profile.ts +443 -0
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta.ts +98 -2
- package/src/model/qwen3-mrope.ts +252 -0
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +336 -60
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/universal/dense.ts +1 -1
- package/src/model/universal/modules.ts +8 -1
- package/src/native/frame_extract.swift +140 -0
- package/src/native-pack.ts +15 -7
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +14 -320
- package/src/prompt-cache.ts +97 -54
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/atomic-output.ts +38 -0
- package/src/quantize/calibration.ts +2 -1
- package/src/quantize/config-writer.ts +16 -3
- package/src/quantize/index.ts +28 -1
- package/src/quantize/job.ts +11 -0
- package/src/quantize/quantizer.ts +265 -100
- package/src/quantize/rotate.ts +603 -0
- package/src/quantize/safetensors-writer.ts +79 -1
- package/src/quantize/sensitivity.ts +41 -59
- package/src/quantize/trellis.ts +586 -0
- package/src/quantize/weight-transform.ts +292 -0
- package/src/responses.ts +130 -3
- package/src/runtime-config.ts +81 -0
- package/src/sampler.ts +276 -4
- package/src/serve/admin-routes.ts +184 -0
- package/src/serve/aux-routes.ts +132 -0
- package/src/serve/batch-scheduler.ts +8 -1558
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +354 -0
- package/src/serve/completion-sink.ts +157 -0
- package/src/serve/discovery-routes.ts +204 -0
- package/src/serve/generation-gateway.ts +211 -243
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/kv-budget.ts +14 -0
- package/src/serve/lab-routes.ts +341 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +155 -0
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +512 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/prompt-response-trace.ts +147 -0
- package/src/serve/request-plan.ts +162 -0
- package/src/serve/request-prep.ts +346 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +101 -0
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +523 -3551
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +9 -8
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +2 -2
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +5 -4
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +9 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +528 -0
- package/src/spec/serve-loop.ts +179 -213
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +95 -24
- package/src/ssd-durability.ts +207 -0
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +14 -13
- package/src/train/forward.ts +38 -1
- package/src/train/loss.ts +3 -2
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +9 -6
- package/src/train/trainer.ts +9 -8
- package/src/vision/preprocess.ts +1 -1
- package/src/vision/prompt.ts +48 -2
- package/src/vision/qwen3vl-preprocess.ts +409 -0
- package/src/vision/qwen3vl-prompt.ts +196 -0
- package/src/vision/qwen3vl-tower.ts +555 -0
- package/src/vision/video-frames.ts +148 -0
- package/src/web/app.html +1 -1
- package/src/web/app.js +15 -6
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +16 -9
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +134 -33
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -268
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -189
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -288
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/investigations/hlg-ab.html +0 -34
- package/docs/investigations/hlg-explorer.html +0 -238
- package/docs/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/investigations/hlg-figs/cross.svg +0 -1
- package/docs/investigations/hlg-figs/edges.svg +0 -1
- package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/investigations/hlg-report.html +0 -262
- package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -100
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -13
- /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
- /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
- /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
- /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
- /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
- /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
- /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
- /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
- /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
- /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
- /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
- /package/docs/{planning → archive/planning}/journal.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
- /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
- /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /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`
|
|
7
|
-
`
|
|
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
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
49
|
-
runs its header-only exact process equation, then opens the bounded
|
|
50
|
-
resident/expert tiers; impossible plans fail before either tier is
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
>
|
|
59
|
-
>
|
|
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
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
|
81
|
-
| `logprobs` / `topLogprobs` | off | per-token logprob capture (mlx_lm.server parity):
|
|
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
|
-
|
|
89
|
-
`PromptCache.put`). Timing semantics match mlx-lm: the prompt clock
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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 {
|
|
434
|
+
import { generate } from "mlx-bun";
|
|
100
435
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
492
|
+
import { Registry } from "mlx-bun";
|
|
136
493
|
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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 {
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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 {
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
//
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const
|
|
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
|
-
|
|
223
|
-
|
|
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
|
|
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.
|