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
|
@@ -20,11 +20,17 @@
|
|
|
20
20
|
// temporal coordinates then ROLLED to the ring's physical layout — see
|
|
21
21
|
// buildBatchedRotatingMask (a column-by-column port of mlx-lm make_mask,
|
|
22
22
|
// including the `roll(shift=idx+1)`). Gated model-free against mlx-lm
|
|
23
|
-
// (tests/batched-rotating.test.ts) + end-to-end vs a long-context Gemma oracle.
|
|
23
|
+
// (tests/unit/batched-rotating.test.ts) + end-to-end vs a long-context Gemma oracle.
|
|
24
24
|
|
|
25
25
|
import { MlxArray } from "../mlx/array";
|
|
26
26
|
import * as ops from "../mlx/ops";
|
|
27
27
|
import { RotatingKVCache, type Cache, type Mask } from "./gemma4-base";
|
|
28
|
+
import { BatchedRotatingState } from "./batched-rotating-state";
|
|
29
|
+
import {
|
|
30
|
+
mergeStorageRows,
|
|
31
|
+
plainRowStorage,
|
|
32
|
+
temporalStorageView,
|
|
33
|
+
} from "./batched-row-storage";
|
|
28
34
|
|
|
29
35
|
const STEP = 256;
|
|
30
36
|
|
|
@@ -78,39 +84,46 @@ export function buildBatchedRotatingMask(
|
|
|
78
84
|
export class BatchedRotatingCache implements Cache {
|
|
79
85
|
keys: MlxArray | null = null;
|
|
80
86
|
values: MlxArray | null = null;
|
|
81
|
-
|
|
82
|
-
offsetArr: number[];
|
|
83
|
-
leftPad: number[];
|
|
84
|
-
#idx = 0;
|
|
85
|
-
#offset = 0; // scalar total tokens processed (mlx-lm _offset)
|
|
86
|
-
#rotated = false;
|
|
87
|
+
readonly #rows: BatchedRotatingState;
|
|
87
88
|
#ropeArr: MlxArray | null = null;
|
|
88
89
|
#ropeForOffset = -1;
|
|
89
90
|
readonly maxSize: number;
|
|
90
91
|
|
|
91
92
|
constructor(maxSize: number, leftPad: number[]) {
|
|
92
93
|
this.maxSize = maxSize;
|
|
93
|
-
this
|
|
94
|
-
this.offsetArr = leftPad.map((l) => -l);
|
|
94
|
+
this.#rows = new BatchedRotatingState(maxSize, leftPad);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/** Same signature as the serial RotatingKVCache — the scheduler's merge
|
|
98
|
+
* guards recognize a batched ring as "rotating-plain + RowBatchCache"
|
|
99
|
+
* (batch-scheduler #mergeJoiner prevRot branch); without this the next
|
|
100
|
+
* join sees "unknown", skips the prevRot rows, and replaces the batch's
|
|
101
|
+
* sliding-layer KV with a one-row cache (B≥3 collapse). Guard order
|
|
102
|
+
* everywhere is isRowBatchCache FIRST, so the shared string never
|
|
103
|
+
* misroutes a batched cache into a serial-only path. */
|
|
104
|
+
signature(): string { return "kv:rotating-plain"; }
|
|
105
|
+
|
|
106
|
+
get offsetArr(): number[] { return this.#rows.offsets; }
|
|
107
|
+
get leftPad(): number[] { return this.#rows.leftPad; }
|
|
108
|
+
get batchSize(): number { return this.#rows.batchSize; }
|
|
109
|
+
|
|
97
110
|
/** Current batch size — tracks filter() (which shrinks the per-row arrays). */
|
|
98
111
|
get #B(): number {
|
|
99
|
-
return this.
|
|
112
|
+
return this.#rows.batchSize;
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
/** mlx-lm uses `cache.offset` (the per-row array) for the scalar interface
|
|
103
116
|
* too; we expose the scalar total as `offset` and the per-row positions via
|
|
104
117
|
* `ropeOffsetArr` (the model's per-row RoPE path). */
|
|
105
118
|
get offset(): number {
|
|
106
|
-
return this.#
|
|
119
|
+
return this.#rows.totalOffset;
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
get ropeOffsetArr(): MlxArray {
|
|
110
|
-
if (this.#ropeArr && this.#ropeForOffset === this
|
|
123
|
+
if (this.#ropeArr && this.#ropeForOffset === this.offset) return this.#ropeArr;
|
|
111
124
|
this.#ropeArr?.dispose();
|
|
112
125
|
this.#ropeArr = MlxArray.fromInt32(Int32Array.from(this.offsetArr), [this.#B]);
|
|
113
|
-
this.#ropeForOffset = this
|
|
126
|
+
this.#ropeForOffset = this.offset;
|
|
114
127
|
return this.#ropeArr;
|
|
115
128
|
}
|
|
116
129
|
|
|
@@ -120,7 +133,7 @@ export class BatchedRotatingCache implements Cache {
|
|
|
120
133
|
mode: "array",
|
|
121
134
|
arr: buildBatchedRotatingMask(
|
|
122
135
|
this.#B, N, this.leftPad, this.maxSize, window,
|
|
123
|
-
this.#
|
|
136
|
+
this.#rows.ringIndex, this.offset, this.#rows.rotated,
|
|
124
137
|
),
|
|
125
138
|
};
|
|
126
139
|
}
|
|
@@ -131,7 +144,7 @@ export class BatchedRotatingCache implements Cache {
|
|
|
131
144
|
const vD = v.shape[3]!;
|
|
132
145
|
if (S !== 1)
|
|
133
146
|
throw new Error("BatchedRotatingCache supports N=1 decode updates only (solo-prefill then merge)");
|
|
134
|
-
const prev = this
|
|
147
|
+
const prev = this.offset;
|
|
135
148
|
|
|
136
149
|
// Grow the buffer (in STEP chunks) until it reaches maxSize.
|
|
137
150
|
if (!this.keys || (prev >= this.keys.shape[2]! && this.keys.shape[2]! < this.maxSize)) {
|
|
@@ -148,7 +161,7 @@ export class BatchedRotatingCache implements Cache {
|
|
|
148
161
|
this.keys = newK;
|
|
149
162
|
this.values = newV;
|
|
150
163
|
}
|
|
151
|
-
this.#
|
|
164
|
+
this.#rows.markGrown(prev);
|
|
152
165
|
}
|
|
153
166
|
|
|
154
167
|
// Trim any overshoot past maxSize (decrements left padding persistently).
|
|
@@ -160,34 +173,27 @@ export class BatchedRotatingCache implements Cache {
|
|
|
160
173
|
this.values!.dispose();
|
|
161
174
|
this.keys = tk;
|
|
162
175
|
this.values = tv;
|
|
163
|
-
this.#
|
|
164
|
-
this.leftPad = this.leftPad.map((x) => x - trimSize);
|
|
176
|
+
this.#rows.trimOvershoot(trimSize);
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
// Rotate when the write head reaches the end of the ring.
|
|
168
|
-
|
|
169
|
-
this.#rotated = true;
|
|
170
|
-
this.#idx = 0;
|
|
171
|
-
}
|
|
172
|
-
if (this.#rotated) this.leftPad = this.leftPad.map((x) => x - S);
|
|
180
|
+
const writeIndex = this.#rows.beginWrite(S);
|
|
173
181
|
|
|
174
182
|
// Write the new K/V at the ring head.
|
|
175
183
|
const [, , SK, DK] = this.keys!.shape as [number, number, number, number];
|
|
176
|
-
const k2 = ops.sliceUpdate(this.keys!, k, [0, 0,
|
|
177
|
-
const v2 = ops.sliceUpdate(this.values!, v, [0, 0,
|
|
184
|
+
const k2 = ops.sliceUpdate(this.keys!, k, [0, 0, writeIndex, 0], [B, H, writeIndex + S, DK]);
|
|
185
|
+
const v2 = ops.sliceUpdate(this.values!, v, [0, 0, writeIndex, 0], [B, H, writeIndex + S, vD]);
|
|
178
186
|
this.keys!.dispose();
|
|
179
187
|
this.values!.dispose();
|
|
180
188
|
this.keys = k2;
|
|
181
189
|
this.values = v2;
|
|
182
|
-
this.#
|
|
183
|
-
this.offsetArr = this.offsetArr.map((x) => x + S);
|
|
184
|
-
this.#idx += S;
|
|
190
|
+
this.#rows.commitWrite(S);
|
|
185
191
|
|
|
186
192
|
// Return the populated prefix (ring not yet full) or the whole buffer.
|
|
187
|
-
if (this
|
|
193
|
+
if (this.offset < this.maxSize) {
|
|
188
194
|
return [
|
|
189
|
-
this.keys.slice([0, 0, 0, 0], [B, H, this
|
|
190
|
-
this.values.slice([0, 0, 0, 0], [B, H, this
|
|
195
|
+
this.keys.slice([0, 0, 0, 0], [B, H, this.offset, DK]),
|
|
196
|
+
this.values.slice([0, 0, 0, 0], [B, H, this.offset, vD]),
|
|
191
197
|
];
|
|
192
198
|
}
|
|
193
199
|
return [
|
|
@@ -206,27 +212,10 @@ export class BatchedRotatingCache implements Cache {
|
|
|
206
212
|
/** Ring contents in temporal order, cut to the valid length (extract). */
|
|
207
213
|
temporalView(): [MlxArray, MlxArray] {
|
|
208
214
|
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (this.#idx === Sbuf) {
|
|
214
|
-
t = a.slice([0, 0, 0, 0], [B, H, Sbuf, D]);
|
|
215
|
-
} else if (this.#idx < this.#offset) {
|
|
216
|
-
const tail = a.slice([0, 0, this.#idx, 0], [B, H, Sbuf, D]);
|
|
217
|
-
const head = a.slice([0, 0, 0, 0], [B, H, this.#idx, D]);
|
|
218
|
-
t = ops.concatAxis([tail, head], 2);
|
|
219
|
-
tail.dispose();
|
|
220
|
-
head.dispose();
|
|
221
|
-
} else {
|
|
222
|
-
t = a.slice([0, 0, 0, 0], [B, H, this.#idx, D]);
|
|
223
|
-
}
|
|
224
|
-
const [, , , Dd] = t.shape as [number, number, number, number];
|
|
225
|
-
const cut = t.slice([0, 0, 0, 0], [B, H, valid, Dd]);
|
|
226
|
-
t.dispose();
|
|
227
|
-
return cut;
|
|
228
|
-
};
|
|
229
|
-
return [order(this.keys), order(this.values)];
|
|
215
|
+
return [
|
|
216
|
+
temporalStorageView(plainRowStorage, this.keys, this.#rows),
|
|
217
|
+
temporalStorageView(plainRowStorage, this.values, this.#rows),
|
|
218
|
+
];
|
|
230
219
|
}
|
|
231
220
|
|
|
232
221
|
/** mlx-lm `BatchRotatingKVCache.extract` (models/cache.py:1417): row `i`
|
|
@@ -242,34 +231,13 @@ export class BatchedRotatingCache implements Cache {
|
|
|
242
231
|
extractRow(i: number): RotatingKVCache | null {
|
|
243
232
|
if (!this.keys || !this.values) return null;
|
|
244
233
|
const pad = Math.max(0, this.leftPad[i]!);
|
|
245
|
-
const valid = Math.min(this.#offset, this.maxSize);
|
|
246
|
-
const cut = (a: MlxArray): MlxArray => {
|
|
247
|
-
const [, H, Sbuf, D] = a.shape as [number, number, number, number];
|
|
248
|
-
const row = a.slice([i, 0, 0, 0], [i + 1, H, Sbuf, D]);
|
|
249
|
-
// temporal order — same three cases as temporalView, single row.
|
|
250
|
-
let t: MlxArray;
|
|
251
|
-
if (this.#idx === Sbuf) {
|
|
252
|
-
t = row;
|
|
253
|
-
} else if (this.#idx < this.#offset) {
|
|
254
|
-
const tail = row.slice([0, 0, this.#idx, 0], [1, H, Sbuf, D]);
|
|
255
|
-
const head = row.slice([0, 0, 0, 0], [1, H, this.#idx, D]);
|
|
256
|
-
t = ops.concatAxis([tail, head], 2);
|
|
257
|
-
tail.dispose();
|
|
258
|
-
head.dispose();
|
|
259
|
-
row.dispose();
|
|
260
|
-
} else {
|
|
261
|
-
t = row.slice([0, 0, 0, 0], [1, H, this.#idx, D]);
|
|
262
|
-
row.dispose();
|
|
263
|
-
}
|
|
264
|
-
const cutV = t.slice([0, 0, pad, 0], [1, H, valid, D]);
|
|
265
|
-
t.dispose();
|
|
266
|
-
const own = ops.contiguous(cutV);
|
|
267
|
-
cutV.dispose();
|
|
268
|
-
return own;
|
|
269
|
-
};
|
|
270
234
|
const c = new RotatingKVCache(this.maxSize);
|
|
271
|
-
const k =
|
|
272
|
-
|
|
235
|
+
const k = temporalStorageView(plainRowStorage, this.keys, this.#rows, {
|
|
236
|
+
row: i, from: pad, copy: true,
|
|
237
|
+
});
|
|
238
|
+
const v = temporalStorageView(plainRowStorage, this.values, this.#rows, {
|
|
239
|
+
row: i, from: pad, copy: true,
|
|
240
|
+
});
|
|
273
241
|
c.restoreState(k, v, this.offsetArr[i]!, k.shape[2]!);
|
|
274
242
|
return c;
|
|
275
243
|
}
|
|
@@ -277,31 +245,27 @@ export class BatchedRotatingCache implements Cache {
|
|
|
277
245
|
/** Keep only `keep` rows along the batch axis (eviction). */
|
|
278
246
|
filter(keep: number[]): void {
|
|
279
247
|
if (this.keys && this.values) {
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
const v = ops.takeAxis(this.values, idxArr, 0);
|
|
283
|
-
idxArr.dispose();
|
|
248
|
+
const k = plainRowStorage.takeRows(this.keys, keep);
|
|
249
|
+
const v = plainRowStorage.takeRows(this.values, keep);
|
|
284
250
|
this.keys.dispose();
|
|
285
251
|
this.values.dispose();
|
|
286
252
|
this.keys = k;
|
|
287
253
|
this.values = v;
|
|
288
254
|
}
|
|
289
|
-
this.
|
|
290
|
-
this.leftPad = keep.map((i) => this.leftPad[i]!);
|
|
255
|
+
this.#rows.filter(keep);
|
|
291
256
|
this.releaseRopeArr();
|
|
292
257
|
}
|
|
293
258
|
|
|
259
|
+
filterRows(keep: readonly number[]): void { this.filter([...keep]); }
|
|
260
|
+
|
|
294
261
|
state(): MlxArray[] {
|
|
295
262
|
return this.keys && this.values ? [this.keys, this.values] : [];
|
|
296
263
|
}
|
|
297
264
|
isTrimmable(): boolean {
|
|
298
|
-
return this.#
|
|
265
|
+
return this.#rows.trimmable;
|
|
299
266
|
}
|
|
300
267
|
trim(n: number): void {
|
|
301
|
-
|
|
302
|
-
this.#offset -= k;
|
|
303
|
-
this.#idx -= k;
|
|
304
|
-
this.offsetArr = this.offsetArr.map((x) => x - k);
|
|
268
|
+
this.#rows.trim(n);
|
|
305
269
|
}
|
|
306
270
|
dispose(): void {
|
|
307
271
|
this.keys?.dispose();
|
|
@@ -322,24 +286,11 @@ export class BatchedRotatingCache implements Cache {
|
|
|
322
286
|
const width = Math.max(...lens, 0);
|
|
323
287
|
const leftPad = lens.map((l) => width - l);
|
|
324
288
|
const cache = new BatchedRotatingCache(maxSize, leftPad);
|
|
325
|
-
cache.
|
|
289
|
+
cache.#rows.restoreMerged(width, offsets);
|
|
326
290
|
if (width === 0) return cache;
|
|
327
291
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const [B, H, , D] = a.shape as [number, number, number, number];
|
|
331
|
-
const z = ops.zeros([B, H, pad, D], a.dtype);
|
|
332
|
-
const out = ops.concatAxis([z, a], 2);
|
|
333
|
-
z.dispose();
|
|
334
|
-
return out;
|
|
335
|
-
};
|
|
336
|
-
const ks = rows.map((r, i) => padTo(r.keys, leftPad[i]!));
|
|
337
|
-
const vs = rows.map((r, i) => padTo(r.values, leftPad[i]!));
|
|
338
|
-
cache.keys = ops.concatAxis(ks, 0);
|
|
339
|
-
cache.values = ops.concatAxis(vs, 0);
|
|
340
|
-
for (const a of [...ks, ...vs]) a.dispose();
|
|
341
|
-
cache.#idx = width;
|
|
342
|
-
cache.#offset = width;
|
|
292
|
+
cache.keys = mergeStorageRows(plainRowStorage, rows.map((row) => row.keys), leftPad);
|
|
293
|
+
cache.values = mergeStorageRows(plainRowStorage, rows.map((row) => row.values), leftPad);
|
|
343
294
|
return cache;
|
|
344
295
|
}
|
|
345
296
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { MlxArray } from "../mlx/array";
|
|
2
|
+
import * as ops from "../mlx/ops";
|
|
3
|
+
import { BatchedRotatingState } from "./batched-rotating-state";
|
|
4
|
+
|
|
5
|
+
export interface RowStorage<T> {
|
|
6
|
+
shape(value: T): readonly [number, number, number, number];
|
|
7
|
+
slice(value: T, batchFrom: number, batchTo: number, tokenFrom: number, tokenTo: number): T;
|
|
8
|
+
concatTokens(values: readonly T[]): T;
|
|
9
|
+
concatRows(values: readonly T[]): T;
|
|
10
|
+
padLeft(value: T, tokens: number): T;
|
|
11
|
+
takeRows(value: T, keep: readonly number[]): T;
|
|
12
|
+
copy(value: T): T;
|
|
13
|
+
dispose(value: T): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const plainRowStorage: RowStorage<MlxArray> = {
|
|
17
|
+
shape: (value) => value.shape as [number, number, number, number],
|
|
18
|
+
slice(value, batchFrom, batchTo, tokenFrom, tokenTo) {
|
|
19
|
+
const [, H, , D] = value.shape as [number, number, number, number];
|
|
20
|
+
return value.slice([batchFrom, 0, tokenFrom, 0], [batchTo, H, tokenTo, D]);
|
|
21
|
+
},
|
|
22
|
+
concatTokens: (values) => ops.concatAxis([...values], 2),
|
|
23
|
+
concatRows: (values) => ops.concatAxis([...values], 0),
|
|
24
|
+
padLeft(value, tokens) {
|
|
25
|
+
if (tokens === 0) return value.slice([0, 0, 0, 0], value.shape as number[]);
|
|
26
|
+
const [B, H, , D] = value.shape as [number, number, number, number];
|
|
27
|
+
const zeros = ops.zeros([B, H, tokens, D], value.dtype);
|
|
28
|
+
const out = ops.concatAxis([zeros, value], 2);
|
|
29
|
+
zeros.dispose();
|
|
30
|
+
return out;
|
|
31
|
+
},
|
|
32
|
+
takeRows(value, keep) {
|
|
33
|
+
const indices = MlxArray.fromInt32(Int32Array.from(keep), [keep.length]);
|
|
34
|
+
const out = ops.takeAxis(value, indices, 0);
|
|
35
|
+
indices.dispose();
|
|
36
|
+
return out;
|
|
37
|
+
},
|
|
38
|
+
copy: (value) => ops.copyOf(value),
|
|
39
|
+
dispose: (value) => value.dispose(),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const quantizedRowStorage: RowStorage<ops.QuantizedTensor> = {
|
|
43
|
+
shape: (value) => value.packed.shape as [number, number, number, number],
|
|
44
|
+
slice(value, batchFrom, batchTo, tokenFrom, tokenTo) {
|
|
45
|
+
const cut = (array: MlxArray): MlxArray => {
|
|
46
|
+
const [, H, , D] = array.shape as [number, number, number, number];
|
|
47
|
+
return array.slice([batchFrom, 0, tokenFrom, 0], [batchTo, H, tokenTo, D]);
|
|
48
|
+
};
|
|
49
|
+
return { packed: cut(value.packed), scales: cut(value.scales), biases: cut(value.biases) };
|
|
50
|
+
},
|
|
51
|
+
concatTokens(values) {
|
|
52
|
+
return {
|
|
53
|
+
packed: ops.concatAxis(values.map((value) => value.packed), 2),
|
|
54
|
+
scales: ops.concatAxis(values.map((value) => value.scales), 2),
|
|
55
|
+
biases: ops.concatAxis(values.map((value) => value.biases), 2),
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
concatRows(values) {
|
|
59
|
+
return {
|
|
60
|
+
packed: ops.concatAxis(values.map((value) => value.packed), 0),
|
|
61
|
+
scales: ops.concatAxis(values.map((value) => value.scales), 0),
|
|
62
|
+
biases: ops.concatAxis(values.map((value) => value.biases), 0),
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
padLeft(value, tokens) {
|
|
66
|
+
const pad = (array: MlxArray): MlxArray => plainRowStorage.padLeft(array, tokens);
|
|
67
|
+
return { packed: pad(value.packed), scales: pad(value.scales), biases: pad(value.biases) };
|
|
68
|
+
},
|
|
69
|
+
takeRows(value, keep) {
|
|
70
|
+
const take = (array: MlxArray): MlxArray => plainRowStorage.takeRows(array, keep);
|
|
71
|
+
return { packed: take(value.packed), scales: take(value.scales), biases: take(value.biases) };
|
|
72
|
+
},
|
|
73
|
+
copy(value) {
|
|
74
|
+
return {
|
|
75
|
+
packed: ops.copyOf(value.packed),
|
|
76
|
+
scales: ops.copyOf(value.scales),
|
|
77
|
+
biases: ops.copyOf(value.biases),
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
dispose(value) {
|
|
81
|
+
value.packed.dispose();
|
|
82
|
+
value.scales.dispose();
|
|
83
|
+
value.biases.dispose();
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export function mergeStorageRows<T>(
|
|
88
|
+
storage: RowStorage<T>,
|
|
89
|
+
rows: readonly T[],
|
|
90
|
+
leftPad: readonly number[],
|
|
91
|
+
): T {
|
|
92
|
+
const padded = rows.map((row, index) => storage.padLeft(row, leftPad[index]!));
|
|
93
|
+
try {
|
|
94
|
+
return storage.concatRows(padded);
|
|
95
|
+
} finally {
|
|
96
|
+
for (const row of padded) storage.dispose(row);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** De-roll storage into temporal order, then cut one row or the whole batch. */
|
|
101
|
+
export function temporalStorageView<T>(
|
|
102
|
+
storage: RowStorage<T>,
|
|
103
|
+
value: T,
|
|
104
|
+
state: BatchedRotatingState,
|
|
105
|
+
options: { row?: number; from?: number; to?: number; copy?: boolean } = {},
|
|
106
|
+
): T {
|
|
107
|
+
const [batch, , tokens] = storage.shape(value);
|
|
108
|
+
const batchFrom = options.row ?? 0;
|
|
109
|
+
const batchTo = options.row === undefined ? batch : options.row + 1;
|
|
110
|
+
const parts = state.temporalRanges(tokens).map(([from, to]) =>
|
|
111
|
+
storage.slice(value, batchFrom, batchTo, from, to)
|
|
112
|
+
);
|
|
113
|
+
const ordered = parts.length === 1 ? parts[0]! : storage.concatTokens(parts);
|
|
114
|
+
if (parts.length > 1) for (const part of parts) storage.dispose(part);
|
|
115
|
+
const cut = storage.slice(
|
|
116
|
+
ordered,
|
|
117
|
+
0,
|
|
118
|
+
batchTo - batchFrom,
|
|
119
|
+
options.from ?? 0,
|
|
120
|
+
options.to ?? state.validLength,
|
|
121
|
+
);
|
|
122
|
+
storage.dispose(ordered);
|
|
123
|
+
if (!options.copy) return cut;
|
|
124
|
+
const owned = storage.copy(cut);
|
|
125
|
+
storage.dispose(cut);
|
|
126
|
+
return owned;
|
|
127
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compiled decode step (Phase A of docs/
|
|
1
|
+
// Compiled decode step (Phase A of docs/archive/investigations/optimization_plan.md).
|
|
2
2
|
//
|
|
3
3
|
// The single-token decode graph is identical token-to-token except for
|
|
4
4
|
// integer state (RoPE offset, cache write position, active length). This
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
import { MlxArray } from "../mlx/array";
|
|
34
34
|
import { CompiledFunction } from "../mlx/compile";
|
|
35
35
|
import * as ops from "../mlx/ops";
|
|
36
|
+
import { runtimeValue } from "../runtime-config";
|
|
36
37
|
import {
|
|
37
38
|
Gemma4Model,
|
|
38
39
|
KVCache,
|
|
@@ -207,7 +208,7 @@ function slotDesc(c: AnyCache, plan: DecodeStepPlan): SlotDesc {
|
|
|
207
208
|
* inside quantizedSdpa's dispatch). Offsets/capacities don't — they're
|
|
208
209
|
* array values or shapeless dims. */
|
|
209
210
|
function closureKey(descs: SlotDesc[]): string {
|
|
210
|
-
const flags = `nf=${
|
|
211
|
+
const flags = `nf=${runtimeValue("MLX_BUN_NO_FUSED_SDPA") === "1" ? 1 : 0}`;
|
|
211
212
|
return descs.map((d) => `${d.kind}:${d.groupSize}:${d.bits}`).join(",") + "|" + flags;
|
|
212
213
|
}
|
|
213
214
|
|
|
@@ -555,7 +556,7 @@ export class CompiledDecode {
|
|
|
555
556
|
anyCaches
|
|
556
557
|
.map((c, i) => (phases[i] === "ring" ? `${slotDesc(c, plans[i]!).kind}:${slotDesc(c, plans[i]!).groupSize}:${slotDesc(c, plans[i]!).bits}` : "js"))
|
|
557
558
|
.join(",") +
|
|
558
|
-
`|nf=${
|
|
559
|
+
`|nf=${runtimeValue("MLX_BUN_NO_FUSED_SDPA") === "1" ? 1 : 0}`;
|
|
559
560
|
if (this.#broken.has(key)) throw new Error(`compiled decode: known-broken closure ${key}`);
|
|
560
561
|
|
|
561
562
|
let closures = this.#segClosures.get(key);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cleanupFailure, disposeResources } from "../engine/resources";
|
|
1
2
|
// DiffusionGemma-26B-A4B-it (model_type "diffusion_gemma") — the first
|
|
2
3
|
// NON-autoregressive model in the codebase. Instead of a left-to-right AR loop
|
|
3
4
|
// it runs an ENCODER prefill over the prompt (builds a KV cache) and then a
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
// Ported verbatim from optiq/vlm/_mlxvlm/models/diffusion_gemma/language.py.
|
|
11
12
|
// Reuses mlx-bun's quantized primitives (QuantizedLinear/Embedding/SwitchLinear,
|
|
12
13
|
// RMSNorm, KVCache/RotatingKVCache) from gemma4-base. Architecture deltas vs
|
|
13
|
-
// gemma4 (verified against the checkpoint, see docs/design/
|
|
14
|
+
// gemma4 (verified against the checkpoint, see docs/design/generic-model-support.md):
|
|
14
15
|
// - attention scale=1.0, NO attention softcap (only final-logit softcap 30 fp32)
|
|
15
16
|
// - QK/V-norm POST-proj / PRE-RoPE; v_norm is RMSNormNoScale; V gets no RoPE
|
|
16
17
|
// - full-attention layers (5,11,17,23,29) reuse k as v (no v_proj), hd=512 kv=2,
|
|
@@ -626,8 +627,8 @@ export class DiffusionGemmaModel {
|
|
|
626
627
|
/** Prefill from a pre-spliced image+text prompt → populated cache. */
|
|
627
628
|
prefillVision(splicedIds: number[], pixels: MlxArray): Cache[] {
|
|
628
629
|
const cache = this.makeCache();
|
|
629
|
-
this.encodeVisionPrefill(splicedIds, pixels, cache);
|
|
630
|
-
|
|
630
|
+
try { this.encodeVisionPrefill(splicedIds, pixels, cache); return cache; }
|
|
631
|
+
catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
|
|
631
632
|
}
|
|
632
633
|
|
|
633
634
|
/** One KVCache (full) / RotatingKVCache (sliding) per layer — the encoder
|
|
@@ -899,16 +900,15 @@ export class DiffusionGemmaModel {
|
|
|
899
900
|
* loop reuses it across all steps of a canvas block). */
|
|
900
901
|
prefill(promptIds: number[]): Cache[] {
|
|
901
902
|
const cache = this.makeCache();
|
|
902
|
-
this.encodePrompt(promptIds, cache);
|
|
903
|
-
|
|
903
|
+
try { this.encodePrompt(promptIds, cache); return cache; }
|
|
904
|
+
catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
|
|
904
905
|
}
|
|
905
906
|
|
|
906
907
|
/** One denoising-step decoder pass -> softcapped logits [1, canvas, vocab]. */
|
|
907
908
|
decoderLogits(canvasArr: MlxArray, cache: Cache[], scEmbeddings: MlxArray | null): MlxArray {
|
|
908
909
|
const h = this.decodeCanvasArr(canvasArr, cache, null, scEmbeddings);
|
|
909
|
-
|
|
910
|
-
h.dispose();
|
|
911
|
-
return logits;
|
|
910
|
+
try { return this.logitsFromHidden(h); }
|
|
911
|
+
finally { h.dispose(); }
|
|
912
912
|
}
|
|
913
913
|
|
|
914
914
|
/** Dequantized embedding table (once per call), for self-conditioning soft
|
package/src/model/factory.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// Model construction
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// cache-signature guard), so the choice is always safe.
|
|
1
|
+
// Model construction consumes one declared profile. Exact artifact profiles
|
|
2
|
+
// outrank family profiles; dedicated/generated graphs outrank the universal
|
|
3
|
+
// fallback. Request-level methods are resolved elsewhere and are never changed
|
|
4
|
+
// by this module.
|
|
6
5
|
|
|
7
6
|
import { loadModelConfig, type ModelConfig } from "../config";
|
|
8
7
|
import { totalmem } from "node:os";
|
|
@@ -24,13 +23,56 @@ import {
|
|
|
24
23
|
type Glm52MemoryPlan,
|
|
25
24
|
} from "./glm52-memory";
|
|
26
25
|
import { UniversalDenseModel } from "./universal/dense";
|
|
27
|
-
import { genericArgsFor
|
|
28
|
-
import {
|
|
26
|
+
import { genericArgsFor } from "./universal/archs";
|
|
27
|
+
import {
|
|
28
|
+
ModelImplementationRegistry,
|
|
29
|
+
type ModelImplementation,
|
|
30
|
+
type ModelImplementationProvider,
|
|
31
|
+
} from "./implementation";
|
|
32
|
+
import {
|
|
33
|
+
assertResolvedModelProfile,
|
|
34
|
+
resolveModelProfile,
|
|
35
|
+
type ResolvedModelProfile,
|
|
36
|
+
type ResolveModelProfileOptions,
|
|
37
|
+
type ModelGraph,
|
|
38
|
+
} from "./profile";
|
|
29
39
|
|
|
30
40
|
export type RuntimeModel =
|
|
31
41
|
| Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model | Qwen3MoeModel
|
|
32
42
|
| DiffusionGemmaModel | Glm52Model | UniversalDenseModel;
|
|
33
43
|
|
|
44
|
+
function residentImplementation(
|
|
45
|
+
id: string,
|
|
46
|
+
graph: ModelGraph,
|
|
47
|
+
create: ModelImplementation<Weights, RuntimeModel>["create"],
|
|
48
|
+
): ModelImplementation<Weights, RuntimeModel> {
|
|
49
|
+
return { id, graph, loader: "safetensors",
|
|
50
|
+
loop: graph === "diffusion-gemma" ? "diffusion" : "autoregressive", create };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Engine-owned registrations. Exact quant profiles can name additional
|
|
54
|
+
* implementations in a composed registry, without changing sessions or files. */
|
|
55
|
+
export const MLX_MODEL_IMPLEMENTATIONS = new ModelImplementationRegistry<Weights, RuntimeModel>([
|
|
56
|
+
residentImplementation("diffusion-gemma", "diffusion-gemma", (weights, config) => new DiffusionGemmaModel(weights, config)),
|
|
57
|
+
residentImplementation("minicpm5", "minicpm5", (weights, config) => new MiniCPM5Model(weights, config)),
|
|
58
|
+
residentImplementation("qwen3.5", "qwen3.5", (weights, config) => new Qwen35Model(weights, config)),
|
|
59
|
+
residentImplementation("qwen3-moe", "qwen3-moe", (weights, config) => new Qwen3MoeModel(weights, config)),
|
|
60
|
+
residentImplementation("qwen3", "qwen3", (weights, config) => new Qwen3Model(weights, config)),
|
|
61
|
+
residentImplementation("gemma4", "gemma4", (weights, config) => new Gemma4Model(weights, config)),
|
|
62
|
+
residentImplementation("gemma4-generated", "gemma4", (weights, config) => {
|
|
63
|
+
const Graph = GENERATED.get(configFingerprint(config));
|
|
64
|
+
if (!Graph) throw new Error("no generated Gemma graph for this config; refusing to fall back");
|
|
65
|
+
return new Graph(weights, config);
|
|
66
|
+
}),
|
|
67
|
+
residentImplementation("universal-dense", "universal-dense", (weights, config) =>
|
|
68
|
+
new UniversalDenseModel(weights, config, genericArgsFor(config)!)),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
export interface ModelOpenOptions<Model = RuntimeModel> extends Glm52RuntimeOpenOptions {
|
|
72
|
+
readonly profiles?: ResolveModelProfileOptions;
|
|
73
|
+
readonly implementations?: ModelImplementationProvider<Weights, Model>;
|
|
74
|
+
}
|
|
75
|
+
|
|
34
76
|
export interface Glm52RuntimeOpenOptions {
|
|
35
77
|
/** Whole-process ceiling. Defaults to the smaller of the validated 25 GiB
|
|
36
78
|
* preset and physical RAM. */
|
|
@@ -97,55 +139,51 @@ export async function openGlm52RuntimeModel(
|
|
|
97
139
|
* model.safetensors.index.json, so it must bypass Weights.open and use its
|
|
98
140
|
* dedicated header catalog/tensor source.
|
|
99
141
|
*/
|
|
100
|
-
export
|
|
142
|
+
export function openModel<Model>(
|
|
101
143
|
modelDir: string,
|
|
102
|
-
options:
|
|
103
|
-
): Promise<
|
|
144
|
+
options: ModelOpenOptions<Model> & { readonly implementations: ModelImplementationProvider<Weights, Model> },
|
|
145
|
+
): Promise<Model>;
|
|
146
|
+
export function openModel(modelDir: string, options?: ModelOpenOptions): Promise<RuntimeModel>;
|
|
147
|
+
export async function openModel<Model>(
|
|
148
|
+
modelDir: string,
|
|
149
|
+
options: ModelOpenOptions<Model> = {},
|
|
150
|
+
): Promise<Model | RuntimeModel> {
|
|
104
151
|
const config = await loadModelConfig(modelDir);
|
|
105
|
-
|
|
152
|
+
const profile = resolveModelProfile(config, options.profiles);
|
|
153
|
+
if (profile.profile.execution.loader === "colibri") {
|
|
154
|
+
if (options.implementations || profile.profile.execution.implementation !== undefined)
|
|
155
|
+
throw new Error("custom Colibri implementations require a streamed loader binding; refusing to fall back");
|
|
106
156
|
return (await openGlm52RuntimeModel(modelDir, options)).model;
|
|
107
|
-
|
|
157
|
+
}
|
|
158
|
+
// Resolve before opening weights; missing or incompatible code allocates nothing.
|
|
159
|
+
const implementation = (options.implementations ?? MLX_MODEL_IMPLEMENTATIONS).select(config, profile);
|
|
160
|
+
const weights = await Weights.open(modelDir);
|
|
161
|
+
try { return implementation.create(weights, config, profile); }
|
|
162
|
+
catch (error) { weights.dispose(); throw error; }
|
|
108
163
|
}
|
|
109
164
|
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
export function createModel(
|
|
114
|
-
|
|
115
|
-
|
|
165
|
+
/** Construct the binding named by a validated profile. A supplied registry can
|
|
166
|
+
* return any backend-owned interface; the compatibility default returns the
|
|
167
|
+
* legacy model union. The caller retains ownership of the supplied weights. */
|
|
168
|
+
export function createModel<Model>(
|
|
169
|
+
weights: Weights, config: ModelConfig, resolved: ResolvedModelProfile | undefined,
|
|
170
|
+
implementations: ModelImplementationProvider<Weights, Model>,
|
|
171
|
+
): Model;
|
|
172
|
+
export function createModel(
|
|
173
|
+
weights: Weights, config: ModelConfig, resolved?: ResolvedModelProfile,
|
|
174
|
+
implementations?: ModelImplementationProvider<Weights, RuntimeModel>,
|
|
175
|
+
): RuntimeModel;
|
|
176
|
+
export function createModel<Model>(
|
|
177
|
+
weights: Weights,
|
|
178
|
+
config: ModelConfig,
|
|
179
|
+
resolved: ResolvedModelProfile = resolveModelProfile(config),
|
|
180
|
+
implementations: ModelImplementationProvider<Weights, Model> | ModelImplementationProvider<Weights, RuntimeModel> = MLX_MODEL_IMPLEMENTATIONS,
|
|
181
|
+
): Model | RuntimeModel {
|
|
182
|
+
assertResolvedModelProfile(config, resolved);
|
|
183
|
+
if (resolved.profile.execution.loader === "colibri")
|
|
116
184
|
throw new Error(
|
|
117
185
|
"glm_moe_dsa uses the direct Colibri container; construct it with " +
|
|
118
186
|
"openModel(modelDir) or Glm52Model.open(modelDir)",
|
|
119
187
|
);
|
|
120
|
-
|
|
121
|
-
// DiffusionGemma is non-autoregressive — generate() detects it and routes to
|
|
122
|
-
// the denoising engine instead of the AR decode loop. It exposes the shared
|
|
123
|
-
// RuntimeModel surface (config/weightsBytes/loraState/makeCache) with the
|
|
124
|
-
// AR-only methods as throwing stubs (never called on this model).
|
|
125
|
-
if (isDiffusionGemmaConfig(config)) return new DiffusionGemmaModel(weights, config);
|
|
126
|
-
if (isMiniCPM5Config(config)) return new MiniCPM5Model(weights, config);
|
|
127
|
-
if (isQwen35Config(config)) return new Qwen35Model(weights, config);
|
|
128
|
-
if (isQwen3MoeConfig(config)) return new Qwen3MoeModel(weights, config);
|
|
129
|
-
if (isQwen3Config(config)) return new Qwen3Model(weights, config);
|
|
130
|
-
// 2. gemma4*: fingerprint-matched generated specialization, else monolith.
|
|
131
|
-
// Always prefer the model-specific generated file when its config
|
|
132
|
-
// fingerprint matches — the parity regime is selected by the OUTPUT-changing
|
|
133
|
-
// flags (kv-quant), which the generated forward already gates, NOT by
|
|
134
|
-
// detouring an optimized model through the generic monolith.
|
|
135
|
-
if (config.modelType.startsWith("gemma4")) {
|
|
136
|
-
const cls = GENERATED.get(configFingerprint(config)) ?? Gemma4Model;
|
|
137
|
-
return new cls(weights, config);
|
|
138
|
-
}
|
|
139
|
-
// 3. Generic Tier-0 fallback: the universal-dense descriptor table
|
|
140
|
-
// (replaces the old hard "llama = MiniCPM5 only" / unknown-type throws).
|
|
141
|
-
const generic = genericArgsFor(config);
|
|
142
|
-
if (generic) return new UniversalDenseModel(weights, config, generic);
|
|
143
|
-
// 4. Reject, naming the arch (post-remap) and the declared surface.
|
|
144
|
-
const arch = remapModelType(config.modelType);
|
|
145
|
-
throw new Error(
|
|
146
|
-
`unsupported model_type "${config.modelType}"` +
|
|
147
|
-
(arch !== config.modelType ? ` (mlx-lm remaps it to "${arch}")` : "") +
|
|
148
|
-
` — targeted: gemma4*, diffusion_gemma, glm_moe_dsa, qwen3_5, qwen3, qwen3_moe, MiniCPM5;` +
|
|
149
|
-
` generic (Tier-0): ${[...GENERIC_MODEL_TYPES].sort().join(", ")}`,
|
|
150
|
-
);
|
|
188
|
+
return implementations.select(config, resolved).create(weights, config, resolved);
|
|
151
189
|
}
|
package/src/model/fingerprint.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Config fingerprint for generated-model dispatch (docs/
|
|
1
|
+
// Config fingerprint for generated-model dispatch (docs/archive/investigations/optimization_plan.md
|
|
2
2
|
// Phase C): a stable hash over every config field that changes the
|
|
3
3
|
// decode graph structure, plus the kv_config quant layout. A generated
|
|
4
4
|
// specialization is used only when the fingerprint of the loaded config
|