mlx-bun 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -588
- package/docs/README.md +94 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
- package/docs/design/batching.md +1841 -0
- package/docs/design/decode-speed-program.md +2346 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +655 -334
- package/docs/design/kv-cache.md +824 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1990 -0
- package/docs/design/turboquant.md +2212 -0
- package/docs/design/unified-engine-frontier-plan.md +1833 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +50 -1
- package/docs/planning/release-notes-v0.4.0.md +45 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +3613 -0
- package/docs/reference/cli.md +434 -160
- package/docs/reference/distribution.md +328 -182
- package/docs/reference/environment.md +398 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +113 -6
- package/docs/reference/server-api.md +944 -429
- package/docs/reference/server-config.md +740 -338
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +158 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1790 -0
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +106 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +32 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +130 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +133 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/memory-client.ts +189 -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-cohort.ts +89 -0
- package/src/backends/mlx/prefill-rows.ts +200 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/request-state-policy.ts +41 -0
- package/src/backends/mlx/rollback.ts +89 -0
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +242 -0
- package/src/backends/mlx/speculative-group.ts +359 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +46 -0
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
- package/src/cli.ts +66 -58
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +60 -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/prefix-cache.ts +23 -0
- package/src/contracts/resources.ts +25 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +45 -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 +53 -0
- package/src/engine/resources.ts +53 -0
- package/src/engine/scheduler.ts +72 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- 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 +30 -16
- package/src/generate.ts +709 -417
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -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 +12 -12
- 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 +70 -12
- package/src/kv-store.ts +177 -63
- 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} +5 -5
- package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
- package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- 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 +1 -3
- package/src/mlx/array.ts +11 -11
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- 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/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +20 -6
- package/src/model/batched-quant.ts +9 -3
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +78 -17
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +65 -23
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +93 -0
- package/src/model/compiled-decode.ts +8 -2
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +55 -0
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +179 -80
- package/src/model/gemma4.ts +40 -32
- package/src/model/generated/gemma4-12b.ts +6 -3
- package/src/model/generated/gemma4-26b.ts +6 -3
- package/src/model/generated/gemma4-e4b.ts +6 -3
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/implementation.ts +69 -0
- package/src/model/kv-attention-view.ts +67 -0
- package/src/model/minicpm5.ts +17 -15
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +202 -78
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- 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/turboquant-codec.ts +83 -0
- package/src/model/universal/dense.ts +15 -14
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +110 -69
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +40 -29
- package/src/serve/batch-scheduler.ts +8 -1671
- 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 +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +183 -213
- 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/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +511 -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/request-plan.ts +23 -18
- package/src/serve/request-prep.ts +344 -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 +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +335 -2711
- package/src/spec/assistant-rows.ts +92 -0
- package/src/spec/assistant-source.ts +31 -55
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +16 -14
- package/src/spec/dflash-source.ts +36 -12
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +97 -122
- 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 +129 -172
- 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-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -137
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +93 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +219 -0
- package/src/spec/qwen-mtp-source.ts +147 -195
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +201 -201
- package/src/spec/source.ts +110 -13
- package/src/spec/two-model-rows.ts +61 -0
- package/src/spec/two-model.ts +44 -102
- package/src/ssd-cache.ts +99 -19
- package/src/ssd-durability.ts +36 -20
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +62 -60
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- 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 +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- 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 -318
- 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-serving-execution-seam.md +0 -81
- 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 -231
- 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 -415
- 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/turboquant-weights.md +0 -467
- 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/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { MlxArray } from "../../mlx/array";
|
|
2
|
+
import * as ops from "../../mlx/ops";
|
|
3
|
+
import type { BatchableCache, Cache, Mask } from "../../model/gemma4-base";
|
|
4
|
+
import { KvTensorRows } from "./kv-tensor-rows";
|
|
5
|
+
import { leaseCacheStates, minimumReusableOffset } from "./state-views";
|
|
6
|
+
import { withResource, disposeResources } from "../../engine/resources";
|
|
7
|
+
import { cloneKvCaches } from "../../kv-store";
|
|
8
|
+
|
|
9
|
+
/** Encoded layouts retain physical row alignment across precision changes. */
|
|
10
|
+
export interface TransitionedKvLayout extends BatchableCache {
|
|
11
|
+
alignRows?(leftPad: readonly number[]): void;
|
|
12
|
+
}
|
|
13
|
+
export interface SpeculativeTransitionedKvLayout extends TransitionedKvLayout {
|
|
14
|
+
specRoundBegin(): void;
|
|
15
|
+
specRoundCommit(): void;
|
|
16
|
+
specRoundRollback(keep: number | readonly number[]): void;
|
|
17
|
+
}
|
|
18
|
+
export interface KvRowTransition<Layout extends TransitionedKvLayout> {
|
|
19
|
+
readonly signature: string;
|
|
20
|
+
maintain(rows: Cache[]): void;
|
|
21
|
+
converted(row: Cache): boolean;
|
|
22
|
+
makeLayout(): Layout;
|
|
23
|
+
packRows?(layout: Layout, rows: readonly Cache[]): void;
|
|
24
|
+
prepareRows?(rows: Cache[]): Cache[];
|
|
25
|
+
extractRow?(row: Cache): Cache;
|
|
26
|
+
rollbackRow?(row: Cache, before: number, keep: number, preserveColumns: boolean): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export interface TransitioningKvPositions {
|
|
31
|
+
readonly rowOffsets: readonly number[];
|
|
32
|
+
readonly leftPad: readonly number[];
|
|
33
|
+
readonly offset: number;
|
|
34
|
+
readonly batchSize: number | null;
|
|
35
|
+
readonly ropeOffsetArr: MlxArray | undefined;
|
|
36
|
+
sync(rows: readonly Cache[], preserve: boolean): void;
|
|
37
|
+
filterRows(keep: readonly number[]): void;
|
|
38
|
+
makeMask(tokens: number, window: number | null): Mask;
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
|
41
|
+
export class FullKvPositions extends KvTensorRows implements TransitioningKvPositions {
|
|
42
|
+
sync(rows: readonly Cache[], preserve: boolean): void {
|
|
43
|
+
this.mergeRows(preserve
|
|
44
|
+
? [{ planes: [], rowOffsets: rows.map(row => row.offset), leftPad: [...this.leftPad] }]
|
|
45
|
+
: rows.map(row => ({ planes: [], rowOffsets: [row.offset], leftPad: [0] })));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Owns membership, committed positions and precision transitions. Codec and
|
|
50
|
+
* attention adapters own tensor access; scheduling and persistence use the
|
|
51
|
+
* existing batch and checkpoint contracts. Row conversion never examines an
|
|
52
|
+
* uncommitted speculative suffix. */
|
|
53
|
+
export abstract class TransitioningKvRows<Layout extends TransitionedKvLayout> implements BatchableCache {
|
|
54
|
+
protected readonly positions: TransitioningKvPositions;
|
|
55
|
+
protected rows: Cache[] = [];
|
|
56
|
+
protected packed?: Layout;
|
|
57
|
+
#prefilling = false;
|
|
58
|
+
|
|
59
|
+
readonly stateNeedsDispose = true;
|
|
60
|
+
constructor(readonly transition: KvRowTransition<Layout>, row?: Cache, positions: TransitioningKvPositions = new FullKvPositions()) {
|
|
61
|
+
this.positions = positions;
|
|
62
|
+
if (row) { this.rows = transition.prepareRows?.([row]) ?? [row]; this.syncPositions(); }
|
|
63
|
+
}
|
|
64
|
+
signature(): string { return this.transition.signature; }
|
|
65
|
+
get prefillMaintenance() { return this; }
|
|
66
|
+
beginPrefill(): void { this.#prefilling = true; }
|
|
67
|
+
commitPrefill(indices: readonly number[]): void {
|
|
68
|
+
if (this.packed) return;
|
|
69
|
+
for (const index of indices) {
|
|
70
|
+
const row = [this.rows[index]!];
|
|
71
|
+
try { this.transition.maintain(row); }
|
|
72
|
+
finally { this.rows[index] = row[0]!; }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
endPrefill(): void { this.#prefilling = false; }
|
|
76
|
+
get minimumReusableOffset(): number { return this.packed?.minimumReusableOffset ?? minimumReusableOffset(this.rows); }
|
|
77
|
+
get rowOffsets(): readonly number[] { return this.packed?.rowOffsets ?? this.positions.rowOffsets; }
|
|
78
|
+
get leftPad(): readonly number[] { return this.packed?.leftPad ?? this.positions.leftPad; }
|
|
79
|
+
get offset(): number { return this.packed?.offset ?? this.positions.offset; }
|
|
80
|
+
get batchSize(): number | null { return this.packed?.batchSize ?? this.positions.batchSize; }
|
|
81
|
+
get ropeOffsetArr(): MlxArray | undefined { return this.packed ? this.packed.ropeOffsetArr : this.positions.ropeOffsetArr; }
|
|
82
|
+
bytesPerToken(): number { return this.packed?.bytesPerToken?.() ?? Math.max(0, ...this.rows.map(row => row.bytesPerToken?.() ?? 0)); }
|
|
83
|
+
projectedBytes(tokens: number): number { return this.bytesPerToken() * tokens; }
|
|
84
|
+
makeMask(tokens: number, window: number | null): Mask { return (this.packed ?? this.positions).makeMask(tokens, window); }
|
|
85
|
+
isTrimmable(): boolean { return this.packed?.isTrimmable() ?? this.rows.every(row => row.isTrimmable()); }
|
|
86
|
+
trim(count: number): void {
|
|
87
|
+
if (this.packed) this.packed.trim(count);
|
|
88
|
+
else { for (const row of this.rows) row.trim(count); this.syncPositions(true); }
|
|
89
|
+
}
|
|
90
|
+
protected syncPositions(preserve = false): void { this.positions.sync(this.rows, preserve); }
|
|
91
|
+
protected advance(): void {
|
|
92
|
+
if (this.packed) return;
|
|
93
|
+
if (!this.#prefilling) this.transition.maintain(this.rows);
|
|
94
|
+
if (this.rows.length && this.rows.every(row => this.transition.converted(row))) {
|
|
95
|
+
const packed = this.transition.makeLayout();
|
|
96
|
+
try {
|
|
97
|
+
if (this.transition.packRows) this.transition.packRows(packed, this.rows);
|
|
98
|
+
else packed.mergeRows(this.rows);
|
|
99
|
+
packed.alignRows?.(this.positions.leftPad);
|
|
100
|
+
}
|
|
101
|
+
catch (error) { packed.dispose(); throw error; }
|
|
102
|
+
const previous = this.rows; this.rows = []; this.packed = packed;
|
|
103
|
+
this.positions.dispose(); disposeResources(previous);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
abstract updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
|
|
107
|
+
abstract makeEmptyBatch(): TransitioningKvRows<Layout>;
|
|
108
|
+
mergeRows(rows: readonly Cache[]): void {
|
|
109
|
+
const owned: Cache[] = [];
|
|
110
|
+
try {
|
|
111
|
+
for (const row of rows) {
|
|
112
|
+
if (row instanceof TransitioningKvRows) {
|
|
113
|
+
for (let index = 0; index < row.batchSize!; index++) owned.push(row.extractRow(index));
|
|
114
|
+
} else owned.push(...cloneKvCaches([row]));
|
|
115
|
+
}
|
|
116
|
+
} catch (error) { disposeResources(owned); throw error; }
|
|
117
|
+
let prepared: Cache[];
|
|
118
|
+
try { prepared = this.transition.prepareRows?.(owned) ?? owned; }
|
|
119
|
+
catch (error) { disposeResources(owned); throw error; }
|
|
120
|
+
this.dispose(); this.rows = prepared; this.syncPositions();
|
|
121
|
+
}
|
|
122
|
+
filterRows(keep: readonly number[]): void {
|
|
123
|
+
if (this.packed) { this.packed.filterRows(keep); return; }
|
|
124
|
+
const previous = this.rows; this.rows = keep.map(index => previous[index]!);
|
|
125
|
+
disposeResources(previous.filter((_, index) => !keep.includes(index))); this.positions.filterRows(keep);
|
|
126
|
+
}
|
|
127
|
+
extractRow(row: number): Cache { return this.packed?.extractRow(row) ?? (this.transition.extractRow?.(this.rows[row]!) ?? cloneKvCaches([this.rows[row]!])[0]!); }
|
|
128
|
+
state(): MlxArray[] {
|
|
129
|
+
return withResource(leaseCacheStates(this.packed ? [this.packed] : this.rows), arrays => {
|
|
130
|
+
const views: MlxArray[] = [];
|
|
131
|
+
try { for (const array of arrays) views.push(ops.contiguous(array)); return views; }
|
|
132
|
+
catch (error) { disposeResources(views); throw error; }
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
dispose(): void {
|
|
136
|
+
this.#prefilling = false;
|
|
137
|
+
const previous = this.rows; this.rows = [];
|
|
138
|
+
this.packed?.dispose(); this.packed = undefined; this.positions.dispose(); disposeResources(previous);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Rollback is a method capability, separate from ordinary cache membership. */
|
|
143
|
+
export abstract class SpeculativeTransitioningKvRows<Layout extends SpeculativeTransitionedKvLayout> extends TransitioningKvRows<Layout> {
|
|
144
|
+
#beforeRound?: number[];
|
|
145
|
+
specRoundBegin(): void {
|
|
146
|
+
this.advance(); this.#beforeRound = [...this.rowOffsets]; this.packed?.specRoundBegin();
|
|
147
|
+
}
|
|
148
|
+
specRoundCommit(): void {
|
|
149
|
+
this.packed?.specRoundCommit(); this.#beforeRound = undefined; this.advance();
|
|
150
|
+
}
|
|
151
|
+
specRoundRollback(keep: number | readonly number[]): void {
|
|
152
|
+
if (this.packed) this.packed.specRoundRollback(keep);
|
|
153
|
+
else {
|
|
154
|
+
for (const [index, row] of this.rows.entries()) {
|
|
155
|
+
const count = typeof keep === "number" ? keep : keep[index]!;
|
|
156
|
+
const before = this.#beforeRound![index]!;
|
|
157
|
+
if (this.transition.rollbackRow) this.transition.rollbackRow(row, before, count, this.rows.length > 1);
|
|
158
|
+
else row.trim(row.offset - before - count);
|
|
159
|
+
}
|
|
160
|
+
this.syncPositions(true);
|
|
161
|
+
}
|
|
162
|
+
this.#beforeRound = undefined; this.advance();
|
|
163
|
+
}
|
|
164
|
+
override dispose(): void { this.#beforeRound = undefined; super.dispose(); }
|
|
165
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -35,6 +35,14 @@ import {
|
|
|
35
35
|
} from "./runtime-config";
|
|
36
36
|
|
|
37
37
|
const argv = process.argv.slice(2);
|
|
38
|
+
// Private self-exec protocol for managed jobs in the single-file binary.
|
|
39
|
+
// Literal imports in job-entry keep the numerical runners inside the bundle.
|
|
40
|
+
if (argv[0] === "__job") {
|
|
41
|
+
const { runJobEntry } = await import("./jobs/job-entry");
|
|
42
|
+
await runJobEntry(argv[1]);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// The appliance path: naked `mlx-bun` (or only options, e.g.
|
|
39
47
|
// `mlx-bun --port 9000`) runs `serve` — first run downloads a model and
|
|
40
48
|
// opens the chat UI. `--help`/`--version` and explicit subcommands still win.
|
|
@@ -103,13 +111,20 @@ const SERVER_FLAGS = `Server options:
|
|
|
103
111
|
restarts (long-context agent TTFT drops from a
|
|
104
112
|
full re-prefill to a zero-copy mmap restore).
|
|
105
113
|
Off unless set.
|
|
106
|
-
--ssd-cache-max <GB> SSD tier byte cap
|
|
114
|
+
--ssd-cache-max <GB> Optional SSD tier byte cap; 0 = unlimited
|
|
115
|
+
[default: unlimited]
|
|
107
116
|
--ssd-demote-idle <sec> Demote prompt-cache entries idle this long to the
|
|
108
117
|
SSD tier, freeing their GPU memory (prefixes stay
|
|
109
118
|
reachable via zero-copy restore). 0 disables.
|
|
110
119
|
[default: 300 when --ssd-cache is on]
|
|
111
120
|
--ssd-cache-verify Verify tensor hashes on every restore (reads all
|
|
112
121
|
bytes eagerly — integrity paranoia only)
|
|
122
|
+
--generation-checkpoint <tokens>
|
|
123
|
+
Queue an in-flight generation snapshot to the SSD
|
|
124
|
+
cache every N emitted tokens. Repeating the same
|
|
125
|
+
request after restart replays and resumes it.
|
|
126
|
+
Requires --ssd-cache; supports ordinary requests
|
|
127
|
+
with shared cache layouts.
|
|
113
128
|
--isolate Run the inference engine as a CHILD process and
|
|
114
129
|
keep this process a pure UI/API proxy — the UI
|
|
115
130
|
stays instant under GPU load and survives engine
|
|
@@ -155,8 +170,8 @@ Model & quality:
|
|
|
155
170
|
auto-detected — a full same-tokenizer model
|
|
156
171
|
(mlx_lm.server parity), a Gemma "-assistant"
|
|
157
172
|
KV-borrowing drafter, or a DSpark checkpoint.
|
|
158
|
-
|
|
159
|
-
|
|
173
|
+
Supported draft providers use shared batched
|
|
174
|
+
execution; ordinary requests can batch too.
|
|
160
175
|
--draft-kind <kind> Override draft-artifact detection:
|
|
161
176
|
two-model | assistant | dspark | deepspec | mtp | ngram
|
|
162
177
|
(deepspec = DeepSeek's released Gemma4DSparkModel
|
|
@@ -181,7 +196,7 @@ Model & quality:
|
|
|
181
196
|
(bf16), 4 / 8 (uniform bits), or turbo[:k<bits>v
|
|
182
197
|
<bits>] (rotation-based TurboQuant — default
|
|
183
198
|
k8v3; kBits in {2,4,5,8}, vBits in {2,3,4,5,8};
|
|
184
|
-
docs/design/turboquant
|
|
199
|
+
docs/design/turboquant.md)
|
|
185
200
|
[default: off — quantized KV measured 5-20% slower
|
|
186
201
|
decode than bf16 at ≤16k (on mlx-lm too); it buys
|
|
187
202
|
memory headroom, so opt in when context is tight.
|
|
@@ -191,12 +206,11 @@ Model & quality:
|
|
|
191
206
|
only; sliding-window layers stay bf16)]
|
|
192
207
|
--paged-kv OPTIONAL vLLM-style paged KV storage (block pool +
|
|
193
208
|
gather before the stock SDPA; env MLX_BUN_PAGED_KV=1).
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
--kv-quant, --draft-model; bypasses the prompt
|
|
209
|
+
Gemma4-family, bf16; supports --batch N.
|
|
210
|
+
Refuses --kv-quant and --draft-model; bypasses the prompt
|
|
197
211
|
cache; runs uncompiled decode. Bit-exact with the
|
|
198
212
|
plain path; expect a small decode cost at batch=1
|
|
199
|
-
(the gather copy). docs/design/
|
|
213
|
+
(the gather copy). docs/design/kv-cache.md
|
|
200
214
|
[default: off]
|
|
201
215
|
--paged-kv-block-size <n> Tokens per KV block [default: 256 = the plain
|
|
202
216
|
cache's growth step]
|
|
@@ -212,13 +226,14 @@ Model & quality:
|
|
|
212
226
|
pass --temp 0 for its unset-request behavior)
|
|
213
227
|
[default: the model's generation_config.json]
|
|
214
228
|
--max-tokens <n> Completion cap when a request omits max_tokens
|
|
215
|
-
(mlx_lm.server flag;
|
|
216
|
-
[default: 128 GLM-5.2
|
|
217
|
-
|
|
229
|
+
(mlx_lm.server flag; --max-tokens 512 reproduces
|
|
230
|
+
its default) [default: 128 GLM-5.2 (memory-plan
|
|
231
|
+
reservation); otherwise none — a defaulted
|
|
232
|
+
request runs to EOS or the admitted context]
|
|
218
233
|
--hlg-sampling on|off Piecewise tone-curve sampling (HLG): rolls off the
|
|
219
234
|
top, boosts the mids, gentles the tail. Gain folds
|
|
220
235
|
from --temperature. [default: off]
|
|
221
|
-
See docs/
|
|
236
|
+
See docs/archive/hlg-sampling.md
|
|
222
237
|
--hlg-width <nats> HLG mid-region half-width [default: 4]
|
|
223
238
|
--hlg-shoulder <nats> HLG highlight rolloff scale [default: 4]
|
|
224
239
|
--hlg-toe <nats> HLG shadow rolloff scale [default: 6]
|
|
@@ -391,7 +406,7 @@ Usage: mlx-bun benchmark [query] [options]
|
|
|
391
406
|
|
|
392
407
|
Runs mlx-bun by itself (no other systems required) and records the
|
|
393
408
|
result to the eval DB. For quotable, cross-stack numbers use
|
|
394
|
-
|
|
409
|
+
bun scripts/bench-serve.ts all from the repo — it preflight-gates on an idle machine
|
|
395
410
|
and runs the mlx-lm/optiq comparison legs.
|
|
396
411
|
|
|
397
412
|
[query] Model to benchmark (--query <q> is accepted for the
|
|
@@ -475,7 +490,7 @@ Options:
|
|
|
475
490
|
--clear Archive the eval DB (timestamped backup) and start fresh
|
|
476
491
|
|
|
477
492
|
Table view: when, model, bench kind, KV mode, decode tok/s, TTFT,
|
|
478
|
-
peak memory, commit. Runs are written by
|
|
493
|
+
peak memory, commit. Runs are written by bun scripts/bench-serve.ts all.`,
|
|
479
494
|
|
|
480
495
|
train: `mlx-bun train — fine-tune a LoRA adapter on your data
|
|
481
496
|
|
|
@@ -825,14 +840,14 @@ function openChatUi(url: string, hostPort: string): void {
|
|
|
825
840
|
/** Resolve the decode ROUTE: a tier alias (--l1/--l2) sets the whole route,
|
|
826
841
|
* and an explicit per-fork flag (--kv-quant/--fused-sdpa/…) overrides the
|
|
827
842
|
* alias. Installs decode runtime options and returns the kv-quant mode. See
|
|
828
|
-
* docs/design/
|
|
843
|
+
* docs/design/unified-engine-frontier-plan.md + unified-engine-frontier-plan.md.
|
|
829
844
|
* Each tier is a GUARANTEE about which reference you reproduce bit-for-bit:
|
|
830
845
|
* --l1 bit-for-bit IDENTICAL to mlx-lm — drop-in replacement for mlx-lm
|
|
831
846
|
* (bf16 KV; THE naked default since 2026-07-05)
|
|
832
847
|
* --l2 bit-for-bit IDENTICAL to mlx-optiq — drop-in replacement for
|
|
833
848
|
* mlx-optiq: quantized KV per kv_config.json + fused N-tiled SDPA for
|
|
834
849
|
* L>1 + STOCK unfused L=1 decode (the optiq-golden composition,
|
|
835
|
-
* scripts/regen
|
|
850
|
+
* scripts/regen.ts kvq + regen-mixed-kv-goldens.ts).
|
|
836
851
|
* There is no --l3: output-changing experiments live in the Lab (env flags
|
|
837
852
|
* with a bench + expiry) until one beats the L1 baseline in a paired A/B —
|
|
838
853
|
* docs/design/unified-engine-frontier-plan.md §6-7. */
|
|
@@ -897,7 +912,7 @@ function applyDecodeRoute(): { kvQuant?: "off" | "config" | number; turboQuant?:
|
|
|
897
912
|
if (kv === "off") return { kvQuant: "off" };
|
|
898
913
|
if (kv === "config") return { kvQuant: "config" };
|
|
899
914
|
if (kv) {
|
|
900
|
-
// TurboQuant (docs/design/turboquant
|
|
915
|
+
// TurboQuant (docs/design/turboquant.md): a distinct axis from the
|
|
901
916
|
// affine kvQuant above — dequantize-on-fetch, stock unfused ops.sdpa,
|
|
902
917
|
// no kvQuant value at all (mutually exclusive with kvBits/kvConfig).
|
|
903
918
|
let turboQuant: TurboQuantScheme | null;
|
|
@@ -940,23 +955,41 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
|
|
|
940
955
|
if (kvBudgetGB > 0) serverOptions.kvBudgetBytes = kvBudgetGB * 1e9;
|
|
941
956
|
const pcRaw = opt("prompt-cache"); // null if absent; an explicit value (incl. 0) wins → `--prompt-cache 0` DISABLES the cache
|
|
942
957
|
if (pcRaw !== null) serverOptions.promptCacheBytes = Math.max(0, Number(pcRaw)) * 2 ** 30;
|
|
943
|
-
// Engine-child mode (
|
|
958
|
+
// Engine-child mode (docs/reference/server-config.md): listen on a unix socket
|
|
944
959
|
// instead of TCP — the isolated parent proxies here. Internal flag,
|
|
945
960
|
// spawned by --isolate; usable directly for socket-level integration.
|
|
946
961
|
const unixRaw = opt("unix");
|
|
947
962
|
if (unixRaw !== null) serverOptions.unixSocket = unixRaw;
|
|
948
|
-
// SSD cold tier (docs/design/
|
|
963
|
+
// SSD cold tier (docs/design/kv-cache.md) — off unless a dir is given.
|
|
949
964
|
const ssdDir = opt("ssd-cache");
|
|
950
965
|
if (ssdDir !== null) {
|
|
951
966
|
serverOptions.ssdCacheDir = ssdDir;
|
|
952
967
|
const maxRaw = opt("ssd-cache-max");
|
|
953
|
-
if (maxRaw !== null)
|
|
968
|
+
if (maxRaw !== null) {
|
|
969
|
+
const maxGb = Number(maxRaw);
|
|
970
|
+
if (!Number.isFinite(maxGb) || maxGb < 0) {
|
|
971
|
+
console.error(`--ssd-cache-max expects a number >= 0 (got "${maxRaw}")`);
|
|
972
|
+
process.exit(1);
|
|
973
|
+
}
|
|
974
|
+
serverOptions.ssdCacheMaxBytes = maxGb === 0
|
|
975
|
+
? Number.POSITIVE_INFINITY
|
|
976
|
+
: maxGb * 2 ** 30;
|
|
977
|
+
}
|
|
954
978
|
if (flag("ssd-cache-verify")) serverOptions.ssdCacheVerify = true;
|
|
955
979
|
const demoteRaw = opt("ssd-demote-idle");
|
|
956
980
|
if (demoteRaw !== null) serverOptions.ssdDemoteIdleSec = Math.max(0, Number(demoteRaw));
|
|
981
|
+
const checkpointRaw = opt("generation-checkpoint");
|
|
982
|
+
if (checkpointRaw !== null) {
|
|
983
|
+
const n = Number(checkpointRaw);
|
|
984
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
985
|
+
console.error(`--generation-checkpoint expects an integer >= 1 (got "${checkpointRaw}")`);
|
|
986
|
+
process.exit(1);
|
|
987
|
+
}
|
|
988
|
+
serverOptions.generationCheckpointTokens = n;
|
|
989
|
+
}
|
|
957
990
|
} else {
|
|
958
991
|
// Refuse-loudly house rule: sub-flags of an absent tier must not vanish.
|
|
959
|
-
for (const f of ["ssd-cache-max", "ssd-demote-idle"] as const)
|
|
992
|
+
for (const f of ["ssd-cache-max", "ssd-demote-idle", "generation-checkpoint"] as const)
|
|
960
993
|
if (opt(f) !== null) console.warn(`--${f} has no effect without --ssd-cache — ignored`);
|
|
961
994
|
if (flag("ssd-cache-verify"))
|
|
962
995
|
console.warn("--ssd-cache-verify has no effect without --ssd-cache — ignored");
|
|
@@ -979,16 +1012,11 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
|
|
|
979
1012
|
if (route.kvQuant !== undefined) serverOptions.kvQuant = route.kvQuant;
|
|
980
1013
|
if (route.turboQuant !== undefined) serverOptions.turboQuant = route.turboQuant;
|
|
981
1014
|
// --paged-kv (env: MLX_BUN_PAGED_KV=1): OPTIONAL vLLM-style block-pool KV
|
|
982
|
-
// storage, default off (docs/design/
|
|
983
|
-
//
|
|
984
|
-
// --batch N>1 is refused loudly by createServer rather than downgraded.
|
|
1015
|
+
// storage, default off (docs/design/kv-cache.md). The state policy owns
|
|
1016
|
+
// block pools and cache reuse; paging preserves the selected batch size.
|
|
985
1017
|
if (flag("paged-kv") || runtimeValue("MLX_BUN_PAGED_KV") === "1") {
|
|
986
1018
|
const bsRaw = opt("paged-kv-block-size");
|
|
987
1019
|
serverOptions.pagedKv = bsRaw !== null ? { blockSize: Number(bsRaw) } : {};
|
|
988
|
-
if (batchRaw === null) {
|
|
989
|
-
serverOptions.batch = 1;
|
|
990
|
-
console.log("[paged-kv] serial-only in v1 — pinning --batch 1");
|
|
991
|
-
}
|
|
992
1020
|
}
|
|
993
1021
|
// Bind loopback unless asked otherwise (mlx_lm.server parity); --host
|
|
994
1022
|
// 0.0.0.0 is the explicit opt-in for LAN exposure. The chat-UI open and
|
|
@@ -1026,7 +1054,7 @@ function serverRuntimeFlags(): { port: number; serverOptions: import("./server")
|
|
|
1026
1054
|
const maxTok = numFlag("max-tokens", 1, 10_000_000);
|
|
1027
1055
|
if (maxTok !== null) serverOptions.defaultMaxTokens = Math.floor(maxTok);
|
|
1028
1056
|
// HLG sampling — piecewise tone curve on the logprobs (default off). Knobs in
|
|
1029
|
-
// nats; the mid gain folds from --temperature. docs/
|
|
1057
|
+
// nats; the mid gain folds from --temperature. docs/archive/hlg-sampling.md.
|
|
1030
1058
|
if (onOff("hlg-sampling") === true) {
|
|
1031
1059
|
serverOptions.hlg = {
|
|
1032
1060
|
enabled: true,
|
|
@@ -1538,7 +1566,7 @@ switch (cmd) {
|
|
|
1538
1566
|
process.exit(1);
|
|
1539
1567
|
}
|
|
1540
1568
|
}
|
|
1541
|
-
// Runtime isolation (--isolate,
|
|
1569
|
+
// Runtime isolation (--isolate, docs/reference/server-config.md): THIS process
|
|
1542
1570
|
// becomes a pure reactor — it binds the TCP port and reverse-proxies to
|
|
1543
1571
|
// an engine CHILD (the full server on a unix socket, spawned from the
|
|
1544
1572
|
// same argv minus parent-only flags). Zero MLX calls in the parent: the
|
|
@@ -1580,7 +1608,7 @@ switch (cmd) {
|
|
|
1580
1608
|
};
|
|
1581
1609
|
const poolMaxRaw = opt("model-pool");
|
|
1582
1610
|
const poolMax = Math.max(1, Number(poolMaxRaw ?? 1) || 1);
|
|
1583
|
-
const { engine,
|
|
1611
|
+
const { engine, close } = startProxyServer({
|
|
1584
1612
|
port: rt.port,
|
|
1585
1613
|
...(rt.serverOptions.hostname ? { hostname: rt.serverOptions.hostname } : {}),
|
|
1586
1614
|
engine: {
|
|
@@ -1596,7 +1624,7 @@ switch (cmd) {
|
|
|
1596
1624
|
const shownHost0 = rt.serverOptions.hostname ?? "localhost";
|
|
1597
1625
|
console.log(style.dim(` isolated mode: engine child starting (socket ${sock})`));
|
|
1598
1626
|
console.log(` ${style.green("●")} ${style.bold("proxy up")} http://${shownHost0}:${rt.port} ${style.dim(`· ${m0.repoId} loading behind it · model pool ${poolMax}`)}`);
|
|
1599
|
-
const onExit = () => {
|
|
1627
|
+
const onExit = () => { void close().finally(() => process.exit(0)); };
|
|
1600
1628
|
process.on("SIGINT", onExit);
|
|
1601
1629
|
process.on("SIGTERM", onExit);
|
|
1602
1630
|
await engine.ready;
|
|
@@ -1613,25 +1641,6 @@ switch (cmd) {
|
|
|
1613
1641
|
const config = await loadModelConfig(m.path);
|
|
1614
1642
|
const report = fit(config, m.sizeBytes, 8192, undefined, undefined, m.expertsBytes);
|
|
1615
1643
|
sFit.done(`${style.bold(m.repoId)} ${style.dim(`· ${gb(m.sizeBytes)} · ~${report.predictedDecodeTps.toFixed(0)} tok/s predicted`)}${picked ? style.dim(" · auto-picked (override: mlx-bun serve <query>)") : ""}`);
|
|
1616
|
-
// Near-ceiling advisory (admission doctrine: advise, never refuse). A
|
|
1617
|
-
// model near the DEFAULT Metal wired ceiling (~75% of RAM) dies with an
|
|
1618
|
-
// UNCATCHABLE async-GPU-OOM on long prefills (M4 24 GB × 17 GB model,
|
|
1619
|
-
// 2026-08-20) — surface the standard remedy up front instead.
|
|
1620
|
-
{
|
|
1621
|
-
const { maxRecommendedWorkingSetSize } = await import("./mlx/ffi");
|
|
1622
|
-
const ceiling = maxRecommendedWorkingSetSize();
|
|
1623
|
-
const sysctlRaw = Bun.spawnSync(["sysctl", "-n", "iogpu.wired_limit_mb"]).stdout.toString().trim();
|
|
1624
|
-
const limitIsDefault = sysctlRaw === "0" || sysctlRaw === "";
|
|
1625
|
-
if (limitIsDefault && ceiling > 0 && m.sizeBytes > 0.8 * ceiling) {
|
|
1626
|
-
const ramGb = Number(Bun.spawnSync(["sysctl", "-n", "hw.memsize"]).stdout.toString().trim()) / 2 ** 30;
|
|
1627
|
-
const suggestMb = Math.floor((ramGb - 2.5) * 1024);
|
|
1628
|
-
console.error(style.dim(
|
|
1629
|
-
` ! ${gb(m.sizeBytes)} model vs ~${gb(ceiling)} default GPU ceiling — long prompts can\n` +
|
|
1630
|
-
` hit an uncatchable Metal OOM. If that happens, raise the limit first:\n` +
|
|
1631
|
-
` sudo sysctl iogpu.wired_limit_mb=${suggestMb} (resets on reboot)`,
|
|
1632
|
-
));
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
1644
|
const sNative = step("native runtime");
|
|
1636
1645
|
await ensureNative(sNative);
|
|
1637
1646
|
sNative.done("native runtime ready");
|
|
@@ -1651,9 +1660,8 @@ switch (cmd) {
|
|
|
1651
1660
|
}
|
|
1652
1661
|
// Speculative decoding (mlx_lm.server parity): --draft-model <path|query>
|
|
1653
1662
|
// resolves like the main model; --num-draft-tokens defaults to 3 (the
|
|
1654
|
-
// server's default upstream — mlx_lm.generate's is 2).
|
|
1655
|
-
//
|
|
1656
|
-
// is_batchable = draft is None).
|
|
1663
|
+
// server's default upstream — mlx_lm.generate's is 2). Qualified providers
|
|
1664
|
+
// use shared execution; ordinary requests retain their own placement.
|
|
1657
1665
|
const draftQuery = opt("draft-model");
|
|
1658
1666
|
let draftModelDir: string | undefined;
|
|
1659
1667
|
if (draftQuery !== null) {
|
|
@@ -1886,7 +1894,7 @@ switch (cmd) {
|
|
|
1886
1894
|
const promptTokens = Number(opt("prompt-tokens", "0"));
|
|
1887
1895
|
const kvMode = opt("kv-quant", "off")!;
|
|
1888
1896
|
console.log(` ${style.dim("measures THIS machine, OUR stack only — no other systems needed.")}`);
|
|
1889
|
-
console.log(` ${style.dim("numbers on a loaded machine are not quotable;
|
|
1897
|
+
console.log(` ${style.dim("numbers on a loaded machine are not quotable; bun scripts/bench-serve.ts all is the preflight-gated harness.")}`);
|
|
1890
1898
|
const sNative = step("native runtime");
|
|
1891
1899
|
await ensureNative(sNative);
|
|
1892
1900
|
sNative.done("native runtime ready");
|
|
@@ -1931,7 +1939,7 @@ switch (cmd) {
|
|
|
1931
1939
|
: kvMode !== "off" ? { kvBits: Number(kvMode), quantizedKvStart: 0 } : {};
|
|
1932
1940
|
|
|
1933
1941
|
// Warmup materializes weights so prefill timing measures prefill,
|
|
1934
|
-
// not lazy page-in (same protocol as
|
|
1942
|
+
// not lazy page-in (same protocol as bench.ts (deleted 2026-08-23; git history)).
|
|
1935
1943
|
const sWarm = step("warmup");
|
|
1936
1944
|
{
|
|
1937
1945
|
const wCache = model.makeCache();
|
|
@@ -2006,7 +2014,7 @@ switch (cmd) {
|
|
|
2006
2014
|
}
|
|
2007
2015
|
const { style } = await import("./tui");
|
|
2008
2016
|
if (rows.length === 0) {
|
|
2009
|
-
console.log("no eval runs recorded yet —
|
|
2017
|
+
console.log("no eval runs recorded yet — bun scripts/bench-serve.ts all writes them");
|
|
2010
2018
|
break;
|
|
2011
2019
|
}
|
|
2012
2020
|
const ago = (ts: number) => {
|
|
@@ -2078,7 +2086,7 @@ switch (cmd) {
|
|
|
2078
2086
|
// already run their own pi connect it with `mlx-bun harness pi`.
|
|
2079
2087
|
const OURS_VAL = new Set([
|
|
2080
2088
|
"--query", "-q", "--model", "--port", "--host", "--memory-budget", "--kv-budget", "--prompt-cache",
|
|
2081
|
-
"--ssd-cache", "--ssd-cache-max", "--ssd-demote-idle", "--kv-quant", "--unix",
|
|
2089
|
+
"--ssd-cache", "--ssd-cache-max", "--ssd-demote-idle", "--generation-checkpoint", "--kv-quant", "--unix",
|
|
2082
2090
|
"--batch", "--decode-concurrency", "--adapter", "--adapter-path",
|
|
2083
2091
|
"--draft-model", "--num-draft-tokens", "--draft-kind", "--ngram-max", "--ngram-min",
|
|
2084
2092
|
"--compiled-decode", "--compiled-activations", "--fused-sdpa", "--thinking",
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CompletionClient } from "./contracts/completion";
|
|
2
|
+
import type { EngineHost } from "./contracts/host";
|
|
3
|
+
|
|
4
|
+
export interface CompletionCall {
|
|
5
|
+
readonly body: Readonly<Record<string, unknown>>;
|
|
6
|
+
readonly route?: "chat/completions" | "completions";
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CompletionResponse {
|
|
11
|
+
choices: Array<{ text?: string; message?: { content?: string | null; [key: string]: unknown }; [key: string]: unknown }>;
|
|
12
|
+
usage?: Record<string, unknown>;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A borrowed transport. Direct services, HTTP and isolated workers receive
|
|
17
|
+
* the same request and return the same result; POSTs are never retried here. */
|
|
18
|
+
export function createCompletionClient(options: {
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
headers?: Readonly<Record<string, string>>;
|
|
21
|
+
host?: Pick<EngineHost<Request, Response>, "forward">;
|
|
22
|
+
}): CompletionClient<CompletionCall, CompletionResponse> {
|
|
23
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
24
|
+
const headers = { ...options.headers, "content-type": "application/json" };
|
|
25
|
+
const send = options.host ? options.host.forward.bind(options.host) : fetch;
|
|
26
|
+
return {
|
|
27
|
+
async complete({ body, route = "chat/completions", signal }) {
|
|
28
|
+
signal?.throwIfAborted();
|
|
29
|
+
const response = await send(new Request(`${baseUrl}/${route}`, {
|
|
30
|
+
method: "POST", headers, body: JSON.stringify({ ...body, stream: false }), signal,
|
|
31
|
+
}));
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const detail = await response.text();
|
|
34
|
+
throw new Error(`completion failed (${response.status}): ${detail}`);
|
|
35
|
+
}
|
|
36
|
+
const result = await response.json() as CompletionResponse;
|
|
37
|
+
if (!Array.isArray(result.choices)) throw new Error("completion response has no choices");
|
|
38
|
+
return result;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Adapts an in-process request handler. Closing waits for active handlers;
|
|
44
|
+
* a handler owns any response stream it returns. */
|
|
45
|
+
export function createDirectHost(
|
|
46
|
+
handler: (request: Request) => Promise<Response>, shutdown: () => Promise<void> = async () => {},
|
|
47
|
+
): EngineHost<Request, Response> {
|
|
48
|
+
let closed = false;
|
|
49
|
+
let closing: Promise<void> | undefined;
|
|
50
|
+
const active = new Set<Promise<Response>>();
|
|
51
|
+
return {
|
|
52
|
+
ready: Promise.resolve(),
|
|
53
|
+
forward(request) {
|
|
54
|
+
if (closed) return Promise.reject(new Error("engine host is closed"));
|
|
55
|
+
if (request.signal.aborted) return Promise.reject(request.signal.reason);
|
|
56
|
+
const work = Promise.resolve().then(() => handler(request));
|
|
57
|
+
active.add(work);
|
|
58
|
+
void work.then(() => active.delete(work), () => active.delete(work));
|
|
59
|
+
return work;
|
|
60
|
+
},
|
|
61
|
+
close() {
|
|
62
|
+
closed = true;
|
|
63
|
+
return closing ??= (async () => { await Promise.allSettled(active); await shutdown(); })();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -75,6 +75,17 @@ export interface QuantSpec {
|
|
|
75
75
|
bits: number;
|
|
76
76
|
groupSize: number;
|
|
77
77
|
mode: string;
|
|
78
|
+
/** `mode: "trellis"` (packed trellis-coded weights, src/model/trellis-linear.ts):
|
|
79
|
+
* bits = k per weight, groupSize = block T, plus the codec geometry. */
|
|
80
|
+
trellis?: TrellisSpec;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TrellisSpec {
|
|
84
|
+
L: number;
|
|
85
|
+
code: string;
|
|
86
|
+
/** 1 = coded along the input dim (stored [out, in·k/32]); 0 = along the
|
|
87
|
+
* output dim (stored [in, out·k/32], i.e. Wᵀ coded along its last axis). */
|
|
88
|
+
axis: 0 | 1;
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
export interface QuantizationConfig {
|
|
@@ -89,12 +100,12 @@ export interface KvQuantSpec {
|
|
|
89
100
|
groupSize: number;
|
|
90
101
|
}
|
|
91
102
|
|
|
92
|
-
/** TurboQuant scheme (docs/design/turboquant
|
|
103
|
+
/** TurboQuant scheme (docs/design/turboquant.md): rotation-based KV
|
|
93
104
|
* quantization, a distinct axis from the uniform/per-layer affine kvQuant
|
|
94
105
|
* above (composes with it in principle; v1 ships as a standalone CLI-only
|
|
95
106
|
* runtime lever, same class as uniform kvBits — see
|
|
96
107
|
* src/generate.ts maybeQuantizeKv). Valid kBits: {2,4,5,8}; vBits:
|
|
97
|
-
* {2,3,4,5,8} (docs/design/turboquant
|
|
108
|
+
* {2,3,4,5,8} (docs/design/turboquant.md's supported Lloyd-Max tables). */
|
|
98
109
|
export interface TurboQuantScheme {
|
|
99
110
|
kBits: number;
|
|
100
111
|
vBits: number;
|
|
@@ -162,11 +173,17 @@ export function parseQuantization(raw: Record<string, any> | undefined): Quantiz
|
|
|
162
173
|
for (const [key, value] of Object.entries(raw)) {
|
|
163
174
|
if (key === "bits" || key === "group_size" || key === "mode") continue;
|
|
164
175
|
if (typeof value === "object" && value !== null) {
|
|
165
|
-
|
|
176
|
+
const spec: QuantSpec = {
|
|
166
177
|
bits: (value as any).bits ?? def.bits,
|
|
167
178
|
groupSize: (value as any).group_size ?? def.groupSize,
|
|
168
179
|
mode: (value as any).mode ?? def.mode,
|
|
169
|
-
}
|
|
180
|
+
};
|
|
181
|
+
const tr = (value as any).trellis;
|
|
182
|
+
if (spec.mode === "trellis") {
|
|
183
|
+
if (!tr || typeof tr !== "object") throw new Error(`quantization.${key}: mode "trellis" needs a trellis {L, code, axis} block`);
|
|
184
|
+
spec.trellis = { L: Number(tr.L), code: String(tr.code ?? "1mad"), axis: tr.axis === 0 ? 0 : 1 };
|
|
185
|
+
}
|
|
186
|
+
perLayer.set(key, spec);
|
|
170
187
|
} else if (value === false) {
|
|
171
188
|
// mlx convention: `"layer": false` means not quantized
|
|
172
189
|
perLayer.set(key, { bits: 0, groupSize: 0, mode: "none" });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Cancellation } from "./generation";
|
|
2
|
+
|
|
3
|
+
/** Application-facing calls. The implementation owns tokenization, numerical
|
|
4
|
+
* method and transport; callers provide request data and consume results. */
|
|
5
|
+
export interface CompletionClient<Request, Result> {
|
|
6
|
+
complete(request: Request): Promise<Result>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface BatchCompletionClient<Request, Result> extends CompletionClient<Request, Result> {
|
|
10
|
+
/** Results preserve input order; the implementation chooses execution groups. */
|
|
11
|
+
completeBatch(requests: readonly Request[]): Promise<Result[]>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TaskClient<Request, Progress, Result> {
|
|
15
|
+
run(request: Request, report: (progress: Progress) => void, cancellation?: Cancellation): Promise<Result>;
|
|
16
|
+
}
|