mlx-bun 0.2.0 → 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 -588
- package/docs/README.md +91 -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/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- 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 +2 -2
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +394 -159
- package/docs/reference/distribution.md +321 -182
- package/docs/reference/environment.md +348 -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 +104 -6
- package/docs/reference/server-api.md +886 -425
- package/docs/reference/server-config.md +638 -330
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +9 -4
- 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 +58 -25
- 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 +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 +1 -1
- package/src/generate.ts +656 -268
- 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/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-store.ts +109 -49
- 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/{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 +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 +9 -10
- 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/batched-mask.ts +8 -2
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +2 -2
- package/src/model/batched-rotating.ts +1 -1
- package/src/model/compiled-decode.ts +1 -1
- 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/gemma4-base.ts +26 -9
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +2 -2
- package/src/model/generated/gemma4-26b.ts +2 -2
- package/src/model/generated/gemma4-e4b.ts +2 -2
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +6 -8
- 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_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +140 -56
- 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-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/prompt-cache.ts +96 -53
- 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 +4 -4
- 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 +154 -193
- 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 +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/request-plan.ts +19 -15
- 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 +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +317 -2649
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +8 -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 +4 -3
- 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 +1 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +166 -69
- package/src/spec/serve-loop.ts +154 -156
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +80 -12
- package/src/ssd-durability.ts +25 -13
- 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 +5 -3
- 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
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// TurboQuant KV-cache codec —
|
|
1
|
+
// TurboQuant KV-cache codec — reference operations ported op-for-op
|
|
2
2
|
// from the vendored vllm-metal reference
|
|
3
3
|
// (lab/repro/vllm-metal-turboquant/turboquant_reference.py). See
|
|
4
|
-
// docs/design/turboquant
|
|
5
|
-
// goldens/turboquant.json (via tests/turboquant-ops.test.ts) for the
|
|
4
|
+
// docs/design/turboquant.md for the algorithm writeup and
|
|
5
|
+
// goldens/turboquant.json (via tests/research/turboquant-ops.test.ts) for the
|
|
6
6
|
// bit-exactness oracle. Quantization groups run along the LAST axis
|
|
7
7
|
// (head_dim) only — never the token axis, per the standing invariant.
|
|
8
8
|
//
|
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
type TurboQuantHeadDim, type TurboQuantValueBits,
|
|
28
28
|
} from "./turboquant-tables";
|
|
29
29
|
|
|
30
|
+
import { tryJointDecodePackedKv, type PackedKvArrays } from "./turboquant-kv-decode";
|
|
31
|
+
import { tryDecodePackedKvInverse256 } from "./turboquant-kv-inverse";
|
|
32
|
+
import { isShapelessTracing } from "./compile";
|
|
33
|
+
|
|
30
34
|
type S = MlxHandle;
|
|
31
35
|
|
|
32
36
|
const BLOCK_SIZE = 32;
|
|
@@ -421,6 +425,32 @@ export function decodeValuesRotated(
|
|
|
421
425
|
return outBf16;
|
|
422
426
|
}
|
|
423
427
|
|
|
428
|
+
/** Optional exact packed K/V decode. Inputs stay borrowed; the caller owns
|
|
429
|
+
* the returned pair. The eager path keeps the reference inverse rotation. */
|
|
430
|
+
export function tryDecodePackedKv(
|
|
431
|
+
inputs: PackedKvArrays, kBits: number, vBits: number, headDim: number,
|
|
432
|
+
deferV = false, s: S = gpuStream,
|
|
433
|
+
): [MlxArray, MlxArray] | null {
|
|
434
|
+
// Reject unsupported value widths before requesting their centroid table.
|
|
435
|
+
if (![2, 3, 4, 5, 8].includes(vBits)) return null;
|
|
436
|
+
if (!deferV && headDim === 256 && kBits === 8 && vBits === 3 &&
|
|
437
|
+
s === gpuStream && !isShapelessTracing() && inputs[0].shape[2]! >= 8192) {
|
|
438
|
+
const decoded = tryDecodePackedKvInverse256(
|
|
439
|
+
inputs, centroidsFor(vBits), signArray(256), kBits, vBits, headDim, s,
|
|
440
|
+
);
|
|
441
|
+
if (decoded) return decoded;
|
|
442
|
+
}
|
|
443
|
+
const decoded = tryJointDecodePackedKv(inputs, centroidsFor(vBits), kBits, vBits, headDim, deferV, s);
|
|
444
|
+
if (!decoded || deferV) return decoded;
|
|
445
|
+
const [key, rotated] = decoded;
|
|
446
|
+
try {
|
|
447
|
+
const value = fwht(rotated, false, s);
|
|
448
|
+
try { return [key, value.astype(Dtype.bfloat16, s)]; }
|
|
449
|
+
finally { value.dispose(); }
|
|
450
|
+
} catch (error) { key.dispose(); throw error; }
|
|
451
|
+
finally { rotated.dispose(); }
|
|
452
|
+
}
|
|
453
|
+
|
|
424
454
|
/** Undo the value rotation on an attention output computed against
|
|
425
455
|
* decodeValuesRotated values. f32 through the transform (matching the
|
|
426
456
|
* eager decode path's precision), returned in the input's dtype. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// GENERATED by scripts/regen
|
|
2
|
-
// Regenerate: bun scripts/regen
|
|
1
|
+
// GENERATED by scripts/regen.ts turboquant — DO NOT EDIT.
|
|
2
|
+
// Regenerate: bun scripts/regen.ts turboquant
|
|
3
3
|
//
|
|
4
4
|
// Source: lab/repro/vllm-metal-turboquant/turboquant_reference.py (vendored
|
|
5
5
|
// vllm-metal oracle, see PROVENANCE.md in that directory), executed under
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Companion to src/train/forward.ts's buildBatchedPadMask, which builds the
|
|
4
4
|
// PREFILL mask ([B,1,L,L], offset 0, right-padded — the training case, already
|
|
5
|
-
// parity-proven by tests/train-batch-e2e.test.ts). This module covers the
|
|
5
|
+
// parity-proven by tests/parity/train-batch-e2e.test.ts). This module covers the
|
|
6
6
|
// DECODE case for batched serving: left-padded rows sharing one growing
|
|
7
7
|
// [B,H,S,D] KV buffer, where a step forwards N new query tokens at a nonzero
|
|
8
8
|
// offset. (The two builders should be consolidated when batched prefill is
|
|
9
|
-
// wired into the serving path — phase S1a, docs/design/
|
|
9
|
+
// wired into the serving path — phase S1a, docs/design/batching.md.)
|
|
10
10
|
//
|
|
11
11
|
// Why left-padding for decode: with rows right-aligned in the KV buffer, every
|
|
12
12
|
// row's next write lands in the SAME column, so one advancing offset serves
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// thing the mask must encode is (a) causality, (b) each row's left padding,
|
|
16
16
|
// and (c) the sliding window, if any.
|
|
17
17
|
|
|
18
|
+
import { cacheSignature } from "./gemma4-base";
|
|
18
19
|
import { MlxArray } from "../mlx/array";
|
|
19
20
|
import * as ops from "../mlx/ops";
|
|
20
21
|
import { createCausalMask, KVCache, type Cache, type Mask } from "./gemma4-base";
|
|
@@ -92,6 +93,11 @@ export class BatchedDecodeMaskCache implements Cache {
|
|
|
92
93
|
#ropeArr: MlxArray | null = null;
|
|
93
94
|
#ropeForOffset = -1;
|
|
94
95
|
|
|
96
|
+
/** A decorator, not a storage kind: never claims the inner cache's
|
|
97
|
+
* identity (isPlainKvCache(wrapper) must stay false — it is not a
|
|
98
|
+
* KVCache), but records what it wraps for diagnostics/persistence. */
|
|
99
|
+
signature(): string { return `kv:batched-mask:${cacheSignature(this.inner)}`; }
|
|
100
|
+
|
|
95
101
|
constructor(
|
|
96
102
|
private readonly inner: Cache,
|
|
97
103
|
private readonly B: number,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// (maybeQuantizeKv at chunk boundaries — see BatchScheduler.#quantizeSolo),
|
|
18
18
|
// so a row's quantized bytes are bit-exact vs serial `--kv-quant config` by
|
|
19
19
|
// construction; this module only re-arranges those bytes across the batch
|
|
20
|
-
// axis. Gates: tests/batched-kv-quant-parity.test.ts.
|
|
20
|
+
// axis. Gates: tests/parity/batched-kv-quant-parity.test.ts.
|
|
21
21
|
|
|
22
22
|
import { MlxArray } from "../mlx/array";
|
|
23
23
|
import * as ops from "../mlx/ops";
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
// every step; it self-tracks per-row offset/leftPad and the scalar ring
|
|
23
23
|
// state. N=1 decode updates only (rows solo-prefill serially, then merge).
|
|
24
24
|
//
|
|
25
|
-
// Gates: tests/batched-rotating-quant.test.ts (model-free byte-identity vs
|
|
25
|
+
// Gates: tests/unit/batched-rotating-quant.test.ts (model-free byte-identity vs
|
|
26
26
|
// the serial oracle per row, through ring wrap, B=1 and B=2) + the gemma
|
|
27
|
-
// scheduler gate in tests/batched-kv-quant-parity.test.ts.
|
|
27
|
+
// scheduler gate in tests/parity/batched-kv-quant-parity.test.ts.
|
|
28
28
|
|
|
29
29
|
import { MlxArray } from "../mlx/array";
|
|
30
30
|
import * as ops from "../mlx/ops";
|
|
@@ -20,7 +20,7 @@
|
|
|
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";
|
|
@@ -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
|
|
@@ -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
|
@@ -24,16 +24,55 @@ import {
|
|
|
24
24
|
} from "./glm52-memory";
|
|
25
25
|
import { UniversalDenseModel } from "./universal/dense";
|
|
26
26
|
import { genericArgsFor } from "./universal/archs";
|
|
27
|
+
import {
|
|
28
|
+
ModelImplementationRegistry,
|
|
29
|
+
type ModelImplementation,
|
|
30
|
+
type ModelImplementationProvider,
|
|
31
|
+
} from "./implementation";
|
|
27
32
|
import {
|
|
28
33
|
assertResolvedModelProfile,
|
|
29
34
|
resolveModelProfile,
|
|
30
35
|
type ResolvedModelProfile,
|
|
36
|
+
type ResolveModelProfileOptions,
|
|
37
|
+
type ModelGraph,
|
|
31
38
|
} from "./profile";
|
|
32
39
|
|
|
33
40
|
export type RuntimeModel =
|
|
34
41
|
| Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model | Qwen3MoeModel
|
|
35
42
|
| DiffusionGemmaModel | Glm52Model | UniversalDenseModel;
|
|
36
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
|
+
|
|
37
76
|
export interface Glm52RuntimeOpenOptions {
|
|
38
77
|
/** Whole-process ceiling. Defaults to the smaller of the validated 25 GiB
|
|
39
78
|
* preset and physical RAM. */
|
|
@@ -100,47 +139,51 @@ export async function openGlm52RuntimeModel(
|
|
|
100
139
|
* model.safetensors.index.json, so it must bypass Weights.open and use its
|
|
101
140
|
* dedicated header catalog/tensor source.
|
|
102
141
|
*/
|
|
103
|
-
export
|
|
142
|
+
export function openModel<Model>(
|
|
104
143
|
modelDir: string,
|
|
105
|
-
options:
|
|
106
|
-
): 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> {
|
|
107
151
|
const config = await loadModelConfig(modelDir);
|
|
108
|
-
const profile = resolveModelProfile(config);
|
|
109
|
-
if (profile.profile.execution.loader === "colibri")
|
|
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");
|
|
110
156
|
return (await openGlm52RuntimeModel(modelDir, options)).model;
|
|
111
|
-
|
|
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; }
|
|
112
163
|
}
|
|
113
164
|
|
|
114
|
-
/** Construct the
|
|
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;
|
|
115
172
|
export function createModel(
|
|
173
|
+
weights: Weights, config: ModelConfig, resolved?: ResolvedModelProfile,
|
|
174
|
+
implementations?: ModelImplementationProvider<Weights, RuntimeModel>,
|
|
175
|
+
): RuntimeModel;
|
|
176
|
+
export function createModel<Model>(
|
|
116
177
|
weights: Weights,
|
|
117
178
|
config: ModelConfig,
|
|
118
179
|
resolved: ResolvedModelProfile = resolveModelProfile(config),
|
|
119
|
-
|
|
180
|
+
implementations: ModelImplementationProvider<Weights, Model> | ModelImplementationProvider<Weights, RuntimeModel> = MLX_MODEL_IMPLEMENTATIONS,
|
|
181
|
+
): Model | RuntimeModel {
|
|
120
182
|
assertResolvedModelProfile(config, resolved);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
// DiffusionGemma is non-autoregressive. The profile pairs this graph with
|
|
129
|
-
// the denoising loop; generate() recognizes the constructed model surface.
|
|
130
|
-
case "diffusion-gemma": return new DiffusionGemmaModel(weights, config);
|
|
131
|
-
case "minicpm5": return new MiniCPM5Model(weights, config);
|
|
132
|
-
case "qwen3.5": return new Qwen35Model(weights, config);
|
|
133
|
-
case "qwen3-moe": return new Qwen3MoeModel(weights, config);
|
|
134
|
-
case "qwen3": return new Qwen3Model(weights, config);
|
|
135
|
-
case "gemma4": {
|
|
136
|
-
// Generated specializations remain exact config matches. Output-changing
|
|
137
|
-
// request methods stay independently resolved and cannot be fallbacks.
|
|
138
|
-
const cls = execution.specialization === "generated"
|
|
139
|
-
? GENERATED.get(configFingerprint(config))!
|
|
140
|
-
: Gemma4Model;
|
|
141
|
-
return new cls(weights, config);
|
|
142
|
-
}
|
|
143
|
-
case "universal-dense":
|
|
144
|
-
return new UniversalDenseModel(weights, config, genericArgsFor(config)!);
|
|
145
|
-
}
|
|
183
|
+
if (resolved.profile.execution.loader === "colibri")
|
|
184
|
+
throw new Error(
|
|
185
|
+
"glm_moe_dsa uses the direct Colibri container; construct it with " +
|
|
186
|
+
"openModel(modelDir) or Glm52Model.open(modelDir)",
|
|
187
|
+
);
|
|
188
|
+
return implementations.select(config, resolved).create(weights, config, resolved);
|
|
146
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
|
|
@@ -545,7 +545,7 @@ export function flashBackward(
|
|
|
545
545
|
// (dK_j[d] → [kv_row][d]); no post-transpose. (An earlier "fix" transposed
|
|
546
546
|
// it on the false belief the buffer was swapped — that transpose is only the
|
|
547
547
|
// identity at Tkv==D and corrupted dK for Tkv≠D; removed after
|
|
548
|
-
//
|
|
548
|
+
// flash-dkv-debug.ts (deleted 2026-08-23; git history) showed the raw output is correct.)
|
|
549
549
|
return [dq[0]!, dkv[0]!, dkv[1]!];
|
|
550
550
|
} finally {
|
|
551
551
|
Dvec.dispose();
|
|
@@ -621,8 +621,8 @@ export function flashAttention(
|
|
|
621
621
|
// - This flash kernel is the L2 path: a port of mlx-optiq's
|
|
622
622
|
// flash_attention_metal (O(L) backward memory). Its parity oracle is OPTIQ,
|
|
623
623
|
// not finite differences. Confirmed: optiq's flash dK == ops.sdpa to f16
|
|
624
|
-
// (scripts/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
|
|
625
|
-
// (tests/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
|
|
624
|
+
// (scripts/oracle/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
|
|
625
|
+
// (tests/unit/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
|
|
626
626
|
//
|
|
627
627
|
// Two real port bugs were fixed to reach that parity (the original diverged
|
|
628
628
|
// ~100% from optiq on dK): a spurious dK transpose in flashBackward (corrupted
|
package/src/model/gemma4-base.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// gemma4-base — the config-INDEPENDENT machinery shared by every
|
|
2
|
-
// architecture variant (docs/
|
|
2
|
+
// architecture variant (docs/archive/investigations/optimization_plan.md Phase B: pure code
|
|
3
3
|
// movement out of the gemma4.ts monolith; every definition here is
|
|
4
4
|
// verbatim from it). Cache classes, quantized-KV SDPA, masks, the
|
|
5
5
|
// quantized primitives + LoRA machinery, and small graph helpers live
|
|
@@ -13,6 +13,7 @@ import type { Weights } from "../weights";
|
|
|
13
13
|
import { MlxArray } from "../mlx/array";
|
|
14
14
|
import { Dtype } from "../mlx/ffi";
|
|
15
15
|
import * as ops from "../mlx/ops";
|
|
16
|
+
import { quantizedMatmulRows } from "../mlx/quantized-rows";
|
|
16
17
|
import { expertOffloadArray } from "../expert-offload";
|
|
17
18
|
import * as tq from "../mlx/turboquant-ops";
|
|
18
19
|
import { runtimeValue } from "../runtime-config";
|
|
@@ -155,8 +156,10 @@ export class QuantizedLinear {
|
|
|
155
156
|
return this._scales.shape[0]!;
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
forward(x: MlxArray): MlxArray {
|
|
159
|
-
let out =
|
|
159
|
+
forward(x: MlxArray, independentRows = false): MlxArray {
|
|
160
|
+
let out = independentRows ? quantizedMatmulRows(
|
|
161
|
+
x, this._w, this._scales, this._biases, this._spec,
|
|
162
|
+
) : ops.quantizedMatmul(
|
|
160
163
|
x,
|
|
161
164
|
this._w,
|
|
162
165
|
this._scales,
|
|
@@ -263,8 +266,13 @@ export type SharedKv =
|
|
|
263
266
|
|
|
264
267
|
export interface Cache {
|
|
265
268
|
offset: number;
|
|
266
|
-
/** Stable storage identity for compatibility guards and persistence.
|
|
267
|
-
|
|
269
|
+
/** Stable storage identity for compatibility guards and persistence.
|
|
270
|
+
* REQUIRED: every capability predicate (isPlainKvCache, isRotating*,
|
|
271
|
+
* kv-store codecs) is a string compare on this value, so a missing
|
|
272
|
+
* override used to fail OPEN into wrong routing (PRs #42/#43:
|
|
273
|
+
* BatchedRotatingCache shipped without one and batched joins dropped
|
|
274
|
+
* running rows). Wrappers report their own kind plus the inner kind. */
|
|
275
|
+
signature(): string;
|
|
268
276
|
/** Physical cache storage consumed by one additional token for one row.
|
|
269
277
|
* Recurrent caches return 0 because their state does not grow with the
|
|
270
278
|
* sequence. Optional for stateless/training adapters that are never
|
|
@@ -303,7 +311,7 @@ export interface Cache {
|
|
|
303
311
|
}
|
|
304
312
|
|
|
305
313
|
export function cacheSignature(cache: Cache | undefined): string {
|
|
306
|
-
return cache
|
|
314
|
+
return cache ? cache.signature() : "unknown";
|
|
307
315
|
}
|
|
308
316
|
|
|
309
317
|
export interface RowBatchCache extends Cache {
|
|
@@ -1499,7 +1507,7 @@ export class RotatingQuantizedKVCache implements Cache {
|
|
|
1499
1507
|
/** One TurboQuant-encoded (K, V) storage tuple — the 5 arrays kv-store.ts
|
|
1500
1508
|
* and toQuantized/fromKVCache pass around. Not `ops.QuantizedTensor`
|
|
1501
1509
|
* (mlx's affine int4/int8 scheme): this is the rotation + Lloyd-Max
|
|
1502
|
-
* layout (docs/design/turboquant
|
|
1510
|
+
* layout (docs/design/turboquant.md), asymmetric-affine for keys,
|
|
1503
1511
|
* FWHT+Lloyd-Max for values. Field order is the kv-store.ts tensor-slot
|
|
1504
1512
|
* contract — do not reorder without updating snapshotCache/loadKvCache. */
|
|
1505
1513
|
export interface TurboQuantTensor {
|
|
@@ -1518,7 +1526,7 @@ export const disposeTurboQuant = (t: TurboQuantTensor): void => {
|
|
|
1518
1526
|
t.vScales.dispose();
|
|
1519
1527
|
};
|
|
1520
1528
|
|
|
1521
|
-
/** TurboQuant KV cache — v1 (docs/design/turboquant
|
|
1529
|
+
/** TurboQuant KV cache — v1 (docs/design/turboquant.md): dequantize-
|
|
1522
1530
|
* on-fetch. Deliberately does NOT subclass KVCache/RotatingKVCache (or
|
|
1523
1531
|
* QuantizedKVCache) — a novel class fails every generated-file
|
|
1524
1532
|
* `#matches()` guard and every batching `instanceof` allow-list, so it
|
|
@@ -1548,6 +1556,8 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1548
1556
|
offset = 0;
|
|
1549
1557
|
/** Validated lazily on first update, once head_dim is known. */
|
|
1550
1558
|
#headDim: number | null = null;
|
|
1559
|
+
/** Experimental operation selection, captured once for this cache. */
|
|
1560
|
+
readonly #fusedDecode = process.env.MLX_BUN_TURBOQUANT_FUSED_DECODE === "1";
|
|
1551
1561
|
|
|
1552
1562
|
constructor(readonly kBits: number, readonly vBits: number) {}
|
|
1553
1563
|
|
|
@@ -1626,6 +1636,13 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1626
1636
|
const [B, H, , D] = a.shape as [number, number, number, number];
|
|
1627
1637
|
return a.slice([0, 0, 0, 0], [B, H, upTo, D]);
|
|
1628
1638
|
};
|
|
1639
|
+
if (this.#fusedDecode) {
|
|
1640
|
+
const inputs = [cut(t.kIdx), cut(t.kScales), cut(t.kZeros), cut(t.vPacked), cut(t.vScales)] as const;
|
|
1641
|
+
try {
|
|
1642
|
+
const decoded = tq.tryDecodePackedKv(inputs, this.kBits, this.vBits, headDim, deferV);
|
|
1643
|
+
if (decoded) return decoded;
|
|
1644
|
+
} finally { for (const input of inputs) input.dispose(); }
|
|
1645
|
+
}
|
|
1629
1646
|
const kIdxCut = cut(t.kIdx);
|
|
1630
1647
|
const kIdxUnpacked = this.kBits < 8 ? tq.unpackBits(kIdxCut, this.kBits, headDim) : kIdxCut;
|
|
1631
1648
|
if (kIdxUnpacked !== kIdxCut) kIdxCut.dispose();
|
|
@@ -2080,7 +2097,7 @@ export function quantizedSdpaTiled(
|
|
|
2080
2097
|
function fusedSdpaSupported(q: MlxArray, mask: Mask, groupSize: number, bits: number): boolean {
|
|
2081
2098
|
// Escape hatch mirroring optiq serve's --no-fused-kv: forces the
|
|
2082
2099
|
// stock unfused path everywhere. Also the A/B lever for
|
|
2083
|
-
// scripts/bench-fused-prefill.
|
|
2100
|
+
// scripts/bench-levers.ts fused-prefill. Read per call (cheap next to the
|
|
2084
2101
|
// FFI work) so tests and paired A/B harnesses can flip it in-process.
|
|
2085
2102
|
if (runtimeValue("MLX_BUN_NO_FUSED_SDPA") === "1") return false;
|
|
2086
2103
|
if (bits !== 4 && bits !== 8) return false;
|
package/src/model/gemma4.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { Weights } from "../weights";
|
|
|
23
23
|
import { MlxArray } from "../mlx/array";
|
|
24
24
|
import { Dtype } from "../mlx/ffi";
|
|
25
25
|
import * as ops from "../mlx/ops";
|
|
26
|
-
import { isExpertTracing, recordRouting } from "../expert-trace";
|
|
26
|
+
import { isExpertTracing, recordRouting } from "../lab/expert-trace/expert-trace";
|
|
27
27
|
|
|
28
28
|
// The shared, config-independent machinery lives in gemma4-base.ts
|
|
29
29
|
// (Phase B extraction); this file keeps the architecture-specific
|
|
@@ -56,7 +56,7 @@ import { Checkpoint } from "../mlx/checkpoint";
|
|
|
56
56
|
import { flashAttention, getTrainingAttn, flashSupported } from "./flash-attention";
|
|
57
57
|
import { unrotateValues as tqUnrotateValues } from "../mlx/turboquant-ops";
|
|
58
58
|
import { CompiledFunction } from "../mlx/compile";
|
|
59
|
-
import { flagOn } from "../
|
|
59
|
+
import { flagOn } from "../runtime-config";
|
|
60
60
|
|
|
61
61
|
/** Verbatim port of mlx_lm/models/gemma4_text.py:
|
|
62
62
|
* `@partial(mx.compile, shapeless=True) def geglu(gate, x): return nn.gelu_approx(gate) * x`
|
|
@@ -242,7 +242,7 @@ class Attention {
|
|
|
242
242
|
/** A number offset takes the static fast::rope; an array offset (set by
|
|
243
243
|
* compiled-decode trace adapters) takes the dynamic variant — same
|
|
244
244
|
* kernel, offset read from the array (bit-exactness asserted in
|
|
245
|
-
* tests/compile.test.ts). */
|
|
245
|
+
* tests/unit/compile.test.ts). */
|
|
246
246
|
rope(x: MlxArray, offset: number | MlxArray): MlxArray {
|
|
247
247
|
return typeof offset === "number"
|
|
248
248
|
? ops.rope(x, this.headDim, this.ropeBase, offset, this.ropeFreqs)
|
|
@@ -370,7 +370,7 @@ class Attention {
|
|
|
370
370
|
// verified route: its dQ/dK/dV vjps are exact (finite-difference checked;
|
|
371
371
|
// it's what mlx-lm's tuner differentiates). The two historical bugs were
|
|
372
372
|
// in THIS flash kernel (spurious dK transpose, divergent dQ barrier),
|
|
373
|
-
// both fixed and gated by tests/flash-attention.test.ts vs the ops.sdpa
|
|
373
|
+
// both fixed and gated by tests/unit/flash-attention.test.ts vs the ops.sdpa
|
|
374
374
|
// oracle — see flash-attention.ts's routing comment. Flash also remains
|
|
375
375
|
// Gemma-guarded in the trainer (e4b >=2K SIGTRAP not yet re-validated).
|
|
376
376
|
// Full layers → window 0 (pure causal); sliding layers → their window.
|
|
@@ -885,7 +885,7 @@ export class Gemma4Model {
|
|
|
885
885
|
* which attend bidirectionally among themselves (use_bidirectional_attention:
|
|
886
886
|
* "vision" — text stays causal; audio prompts pass NO bidir mask, and a
|
|
887
887
|
* mixed image+audio prompt drops the image overlay too, §3.3 Q1 of
|
|
888
|
-
* docs/design/
|
|
888
|
+
* docs/design/generic-model-support.md). `ids` ([1, L], the spliced token ids)
|
|
889
889
|
* is required for per-layer-input models (e2b/e4b): multimodal soft-token
|
|
890
890
|
* positions get token 0's per-layer embedding, and their tower content
|
|
891
891
|
* enters only through the projection term (the merged hidden). Matches
|
|
@@ -1015,7 +1015,7 @@ export class Gemma4Model {
|
|
|
1015
1015
|
return h;
|
|
1016
1016
|
}
|
|
1017
1017
|
|
|
1018
|
-
// --- Segmented-backward support (docs/design/
|
|
1018
|
+
// --- Segmented-backward support (docs/design/orpo-training.md
|
|
1019
1019
|
// §4 Phase B). These are ADDITIVE — forwardLayers is untouched. The segmented
|
|
1020
1020
|
// driver builds masks + per-layer inputs ONCE, then drives runLayerRange per
|
|
1021
1021
|
// segment, threading the KV-shared donor K/V across segment boundaries.
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: 9f812d2eb461fcbe
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: c9dd67ed5a525231
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: 418e9adc386ea67c
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
package/src/model/glm52-dsa.ts
CHANGED
|
@@ -291,7 +291,7 @@ function getDsaRankKeyKernel(): MetalKernel {
|
|
|
291
291
|
? ~bits
|
|
292
292
|
: (bits ^ 0x80000000u);
|
|
293
293
|
const uint descending = ~ascending;
|
|
294
|
-
keys[i] =
|
|
294
|
+
keys[i] = descending;
|
|
295
295
|
`,
|
|
296
296
|
});
|
|
297
297
|
}
|
|
@@ -308,7 +308,8 @@ function getDsaContractOrderKeyKernel(): MetalKernel {
|
|
|
308
308
|
const uint i = thread_position_in_grid.x;
|
|
309
309
|
const uint position = selected_positions[i];
|
|
310
310
|
const uint threshold_class = selected_scores[i] > threshold ? 0u : 1u;
|
|
311
|
-
|
|
311
|
+
// MLX axis lengths are signed int32; positions leave the high bit free.
|
|
312
|
+
keys[i] = (threshold_class << 31) | position;
|
|
312
313
|
`,
|
|
313
314
|
});
|
|
314
315
|
}
|
|
@@ -318,8 +319,8 @@ function getDsaContractOrderKeyKernel(): MetalKernel {
|
|
|
318
319
|
/**
|
|
319
320
|
* Deterministic on-device equivalent of `selectDsaThresholdTiesF32`.
|
|
320
321
|
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
322
|
+
* Stable sorting of the first uint32 key orders scores descending and keeps
|
|
323
|
+
* equal-score positions ascending, including threshold ties. The second key
|
|
323
324
|
* restores Colibri's observable two-scan order: all scores strictly above the
|
|
324
325
|
* threshold in position order, followed by threshold ties in position order.
|
|
325
326
|
* The caller owns the returned arrays and must call `dispose()` once the last
|
|
@@ -340,18 +341,21 @@ export function selectGlm52DsaDevice(
|
|
|
340
341
|
? scores
|
|
341
342
|
: scores.astype(Dtype.float32);
|
|
342
343
|
const [rankKeys] = getDsaRankKeyKernel().apply([scoresF32], {
|
|
343
|
-
outputs: [{ shape: [contextLength], dtype: Dtype.
|
|
344
|
+
outputs: [{ shape: [contextLength], dtype: Dtype.uint32 }],
|
|
344
345
|
grid: [contextLength, 1, 1],
|
|
345
346
|
threadGroup: [Math.min(256, contextLength), 1, 1],
|
|
346
347
|
});
|
|
347
|
-
|
|
348
|
-
|
|
348
|
+
// Stable argsort retains ascending positions for equal score bits, so the
|
|
349
|
+
// ranking key needs no position field. MLX 0.31.2's GPU argpartition also
|
|
350
|
+
// sorts fully, but does not promise this tie order in its public contract.
|
|
351
|
+
const rankedPositions = ops.argsortAxis(rankKeys!, 0);
|
|
352
|
+
const selectedUnordered = rankedPositions.slice([0], [topK]);
|
|
349
353
|
const selectedScores = ops.takeAlongAxis(scoresF32, selectedUnordered, 0);
|
|
350
354
|
const threshold = ops.minAxis(selectedScores, 0, false);
|
|
351
355
|
const [orderKeys] = getDsaContractOrderKeyKernel().apply(
|
|
352
356
|
[selectedScores, selectedUnordered, threshold],
|
|
353
357
|
{
|
|
354
|
-
outputs: [{ shape: [topK], dtype: Dtype.
|
|
358
|
+
outputs: [{ shape: [topK], dtype: Dtype.uint32 }],
|
|
355
359
|
grid: [topK, 1, 1],
|
|
356
360
|
threadGroup: [Math.min(256, topK), 1, 1],
|
|
357
361
|
},
|
|
@@ -360,7 +364,7 @@ export function selectGlm52DsaDevice(
|
|
|
360
364
|
const positions = ops.takeAlongAxis(selectedUnordered, order, 0);
|
|
361
365
|
|
|
362
366
|
rankKeys!.dispose();
|
|
363
|
-
|
|
367
|
+
rankedPositions.dispose();
|
|
364
368
|
selectedUnordered.dispose();
|
|
365
369
|
selectedScores.dispose();
|
|
366
370
|
orderKeys!.dispose();
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type Glm52Config,
|
|
5
5
|
} from "./glm52-config";
|
|
6
6
|
import { ColibriGlm52Container } from "./glm52-container";
|
|
7
|
-
import type { MemoryPlan } from "../
|
|
7
|
+
import type { MemoryPlan } from "../admission-plan";
|
|
8
8
|
|
|
9
9
|
export const GLM52_G5_GIB = 1024 ** 3;
|
|
10
10
|
export const GLM52_G5_DEFAULT_MACHINE_BYTES = 32 * GLM52_G5_GIB;
|