@mlx-bun/inference 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +1475 -2
- package/THIRD_PARTY_NOTICES.md +3404 -0
- package/dist/native/libmlx_bun_ane.dylib +0 -0
- package/dist/native/libmlx_bun_expert_io.dylib +0 -0
- package/dist/native/mlx-bun-frame-extract +0 -0
- package/examples/fit-model.ts +25 -0
- package/examples/qwen3-forward.ts +42 -0
- package/examples/qwen3-generate.ts +28 -0
- package/examples/trellis-expand.ts +23 -0
- package/package.json +129 -4
- package/src/adapters/available.ts +110 -0
- package/src/adapters/index.ts +3 -0
- package/src/adapters/manager.ts +316 -0
- package/src/artifacts/auxiliary-files.ts +45 -0
- package/src/artifacts/config-dialects.ts +94 -0
- package/src/artifacts/config.ts +341 -0
- package/src/artifacts/draft-vocabulary.ts +30 -0
- package/src/artifacts/expert-offload-build.ts +75 -0
- package/src/artifacts/expert-offload.ts +89 -0
- package/src/artifacts/experts/index.ts +3 -0
- package/src/artifacts/experts/io.ts +386 -0
- package/src/artifacts/experts/residency.ts +1134 -0
- package/src/artifacts/experts/usage.ts +415 -0
- package/src/artifacts/fingerprint.ts +44 -0
- package/src/artifacts/glm52/container.ts +305 -0
- package/src/artifacts/glm52/expert-layout.ts +249 -0
- package/src/artifacts/glm52/index.ts +7 -0
- package/src/artifacts/glm52/kernel-layout.ts +124 -0
- package/src/artifacts/glm52/layout.ts +152 -0
- package/src/artifacts/glm52/memory.ts +500 -0
- package/src/artifacts/glm52/resident-weights.ts +135 -0
- package/src/artifacts/glm52/weights.ts +503 -0
- package/src/artifacts/glm52-config.ts +262 -0
- package/src/artifacts/identity.ts +94 -0
- package/src/artifacts/index.ts +15 -0
- package/src/artifacts/mmap.ts +90 -0
- package/src/artifacts/safetensors-writer.ts +249 -0
- package/src/artifacts/safetensors.ts +164 -0
- package/src/artifacts/turboquant-fold.ts +42 -0
- package/src/artifacts/weight-audit.ts +43 -0
- package/src/artifacts/weights.ts +255 -0
- package/src/contracts/index.ts +2 -0
- package/src/contracts/mlx/attention.ts +8 -0
- package/src/contracts/mlx/cache.ts +510 -0
- package/src/contracts/mlx/checkpoint.ts +10 -0
- package/src/contracts/mlx/draft-checkpoint.ts +14 -0
- package/src/contracts/mlx/draft-projection.ts +27 -0
- package/src/contracts/mlx/draft-target.ts +84 -0
- package/src/contracts/mlx/drafter.ts +176 -0
- package/src/contracts/mlx/forward-work.ts +23 -0
- package/src/contracts/mlx/glm52-weights.ts +25 -0
- package/src/contracts/mlx/graph.ts +309 -0
- package/src/contracts/mlx/head.ts +6 -0
- package/src/contracts/mlx/index.ts +15 -0
- package/src/contracts/mlx/kv-codec.ts +9 -0
- package/src/contracts/mlx/media.ts +113 -0
- package/src/contracts/mlx/positions.ts +16 -0
- package/src/contracts/mlx/row-table.ts +2 -0
- package/src/contracts/mlx/trainable.ts +103 -0
- package/src/contracts/mlx/whisper.ts +96 -0
- package/src/contracts/portable/composition.ts +106 -0
- package/src/contracts/portable/denoising.ts +17 -0
- package/src/contracts/portable/execution.ts +79 -0
- package/src/contracts/portable/generation.ts +103 -0
- package/src/contracts/portable/graph.ts +80 -0
- package/src/contracts/portable/index.ts +15 -0
- package/src/contracts/portable/memory-plan.ts +26 -0
- package/src/contracts/portable/object-cache.ts +8 -0
- package/src/contracts/portable/prefill.ts +63 -0
- package/src/contracts/portable/prefix-cache.ts +33 -0
- package/src/contracts/portable/prompt.ts +2 -0
- package/src/contracts/portable/resources.ts +25 -0
- package/src/contracts/portable/rollback.ts +7 -0
- package/src/contracts/portable/sampling.ts +44 -0
- package/src/contracts/portable/scheduling.ts +54 -0
- package/src/contracts/portable/token-delivery.ts +4 -0
- package/src/contracts/portable/tsconfig.json +14 -0
- package/src/embeddings/index.ts +1 -0
- package/src/embeddings/text.ts +85 -0
- package/src/execution/admission.ts +59 -0
- package/src/execution/batch-group.ts +1240 -0
- package/src/execution/batch-types.ts +225 -0
- package/src/execution/cancellation.ts +38 -0
- package/src/execution/continuation-request.ts +45 -0
- package/src/execution/continuation-types.ts +10 -0
- package/src/execution/coordinator.ts +70 -0
- package/src/execution/denoising-group.ts +180 -0
- package/src/execution/engine.ts +56 -0
- package/src/execution/fill-group.ts +505 -0
- package/src/execution/fit.ts +184 -0
- package/src/execution/gateway-binding.ts +178 -0
- package/src/execution/grammar-group.ts +224 -0
- package/src/execution/index.ts +18 -0
- package/src/execution/methods.ts +75 -0
- package/src/execution/mixed-iteration.ts +96 -0
- package/src/execution/plan.ts +65 -0
- package/src/execution/prefill-cohort.ts +100 -0
- package/src/execution/prefill-rows.ts +263 -0
- package/src/execution/request-policy.ts +19 -0
- package/src/execution/row-sampling.ts +49 -0
- package/src/execution/scheduler.ts +92 -0
- package/src/execution/session.ts +247 -0
- package/src/execution/speculative-group.ts +797 -0
- package/src/execution/tasks.ts +44 -0
- package/src/generation/autoregressive.ts +840 -0
- package/src/generation/bindings/autoregressive.ts +104 -0
- package/src/generation/bindings/denoising.ts +60 -0
- package/src/generation/bindings/prefill-policy.ts +36 -0
- package/src/generation/checkpoint-identity.ts +60 -0
- package/src/generation/denoising.ts +85 -0
- package/src/generation/diffusion.ts +583 -0
- package/src/generation/fill/echo-index.ts +188 -0
- package/src/generation/fill/index.ts +3 -0
- package/src/generation/fill/proposal.ts +69 -0
- package/src/generation/fill/schema-rows.ts +506 -0
- package/src/generation/fill/session.ts +464 -0
- package/src/generation/fill/tool-boundary.ts +103 -0
- package/src/generation/fill-append.ts +58 -0
- package/src/generation/generate.ts +44 -0
- package/src/generation/grammar-step.ts +39 -0
- package/src/generation/index.ts +14 -0
- package/src/generation/phase-dispatch.ts +115 -0
- package/src/generation/prefill.ts +33 -0
- package/src/generation/result.ts +49 -0
- package/src/generation/scopes.ts +157 -0
- package/src/generation/speculative/acceptance.ts +41 -0
- package/src/generation/speculative/adaptive-draft-gate.ts +166 -0
- package/src/generation/speculative/bindings/assistant-rows.ts +102 -0
- package/src/generation/speculative/bindings/autoregressive-rows.ts +124 -0
- package/src/generation/speculative/bindings/binding.ts +106 -0
- package/src/generation/speculative/bindings/deepspec-rows.ts +33 -0
- package/src/generation/speculative/bindings/draft-target.ts +10 -0
- package/src/generation/speculative/bindings/glm52-mtp-rows.ts +128 -0
- package/src/generation/speculative/bindings/projected-context-rows.ts +78 -0
- package/src/generation/speculative/bindings/projected-draft-rows.ts +95 -0
- package/src/generation/speculative/bindings/qwen-mtp-rows.ts +336 -0
- package/src/generation/speculative/bindings/two-model-rows.ts +63 -0
- package/src/generation/speculative/cache-identity.ts +16 -0
- package/src/generation/speculative/draft-kind.ts +149 -0
- package/src/generation/speculative/draft-policy.ts +41 -0
- package/src/generation/speculative/draft-registry.ts +118 -0
- package/src/generation/speculative/draft-vocab.ts +110 -0
- package/src/generation/speculative/index.ts +17 -0
- package/src/generation/speculative/output.ts +49 -0
- package/src/generation/speculative/round.ts +307 -0
- package/src/generation/speculative/source.ts +144 -0
- package/src/generation/speculative/sources/assistant-source.ts +64 -0
- package/src/generation/speculative/sources/deepspec-source.ts +70 -0
- package/src/generation/speculative/sources/dflash-source.ts +84 -0
- package/src/generation/speculative/sources/dflash2-source.ts +86 -0
- package/src/generation/speculative/sources/glm52-mtp-source.ts +66 -0
- package/src/generation/speculative/sources/ngram-source.ts +165 -0
- package/src/generation/speculative/sources/qwen-mtp-source.ts +154 -0
- package/src/generation/speculative/sources/two-model.ts +75 -0
- package/src/generation/types.ts +194 -0
- package/src/index.ts +20 -0
- package/src/input/audio/audiotoolbox.ts +112 -0
- package/src/input/audio/decode.ts +133 -0
- package/src/input/audio/features.ts +202 -0
- package/src/input/audio/index.ts +6 -0
- package/src/input/audio/transcode.ts +67 -0
- package/src/input/audio/whisper-mel.ts +185 -0
- package/src/input/audio/whisper-tokenizer.ts +238 -0
- package/src/input/chat-template.ts +389 -0
- package/src/input/index.ts +6 -0
- package/src/input/media-fetch.ts +439 -0
- package/src/input/packed-mixed.ts +43 -0
- package/src/input/special-tokens.ts +75 -0
- package/src/input/token-groups.ts +49 -0
- package/src/input/tokenizer.ts +336 -0
- package/src/input/tool-call.ts +689 -0
- package/src/input/vision/index.ts +3 -0
- package/src/input/vision/preprocess.ts +247 -0
- package/src/input/vision/prompt.ts +398 -0
- package/src/input/vision/qwen3vl-preprocess.ts +409 -0
- package/src/input/vision/qwen3vl-prompt.ts +237 -0
- package/src/input/vision/video-frames.ts +100 -0
- package/src/kernels/ane/fill.ts +101 -0
- package/src/kernels/ane/linear.ts +416 -0
- package/src/kernels/attention/flash.ts +694 -0
- package/src/kernels/attention/kvq4-decode.ts +155 -0
- package/src/kernels/attention/masks.ts +40 -0
- package/src/kernels/attention/multi-query.ts +193 -0
- package/src/kernels/attention/paged.ts +130 -0
- package/src/kernels/delta/gated.ts +306 -0
- package/src/kernels/delta/index.ts +3 -0
- package/src/kernels/delta/silu-rms-heads.ts +69 -0
- package/src/kernels/delta/state.ts +74 -0
- package/src/kernels/gated-rms-norm.ts +64 -0
- package/src/kernels/glm52/layout.ts +37 -0
- package/src/kernels/glm52/streamed-experts.ts +360 -0
- package/src/kernels/logits.ts +38 -0
- package/src/kernels/quantization/affine-row.ts +154 -0
- package/src/kernels/quantization/affine-rows.ts +36 -0
- package/src/kernels/quantization/affine-split-mma.ts +144 -0
- package/src/kernels/quantization/affine3-mma.ts +117 -0
- package/src/kernels/quantization/affine3-rows.ts +103 -0
- package/src/kernels/quantization/affine4-mma.ts +113 -0
- package/src/kernels/quantization/affine4-rows.ts +102 -0
- package/src/kernels/residual-rms-norm.ts +65 -0
- package/src/kernels/sampling/index.ts +2 -0
- package/src/kernels/sampling/normalized-argmax.ts +116 -0
- package/src/kernels/sampling/token-bitmask.ts +52 -0
- package/src/kernels/softcap.ts +35 -0
- package/src/kernels/trellis/codebook.ts +123 -0
- package/src/kernels/trellis/down-codebook.ts +184 -0
- package/src/kernels/trellis/down-factored.ts +180 -0
- package/src/kernels/trellis/down-k3-interleaved-factored.ts +146 -0
- package/src/kernels/trellis/down-mma-wide.ts +195 -0
- package/src/kernels/trellis/down-mma.ts +145 -0
- package/src/kernels/trellis/expand.ts +42 -0
- package/src/kernels/trellis/gate-up-codebook-mma.ts +141 -0
- package/src/kernels/trellis/gate-up-codebook.ts +137 -0
- package/src/kernels/trellis/gate-up-factored.ts +197 -0
- package/src/kernels/trellis/gate-up-mma-wide.ts +147 -0
- package/src/kernels/trellis/gate-up-mma.ts +148 -0
- package/src/kernels/trellis/gate-up.ts +248 -0
- package/src/kernels/trellis/geometry.ts +22 -0
- package/src/kernels/trellis/index.ts +103 -0
- package/src/kernels/trellis/launch.ts +12 -0
- package/src/kernels/trellis/mixed-gate-up-factored.ts +119 -0
- package/src/kernels/trellis/mixed-gate-up.ts +146 -0
- package/src/kernels/trellis/qualified.ts +34 -0
- package/src/kernels/trellis/reduce.ts +150 -0
- package/src/kernels/trellis/scatter.ts +407 -0
- package/src/kernels/trellis/splitk-prefill.ts +152 -0
- package/src/kernels/trellis/tiled-prefill.ts +131 -0
- package/src/kernels/trellis/vector-expand.ts +58 -0
- package/src/kernels/trellis/wide-prefill.ts +89 -0
- package/src/kernels/turboquant/decode.ts +82 -0
- package/src/kernels/turboquant/index.ts +5 -0
- package/src/kernels/turboquant/inverse.ts +122 -0
- package/src/kernels/turboquant/ops.ts +860 -0
- package/src/kernels/turboquant/signs.ts +130 -0
- package/src/kernels/turboquant/tables.ts +124 -0
- package/src/layers/affine-verify-linear.ts +75 -0
- package/src/layers/ane-prefill-split.ts +283 -0
- package/src/layers/compiled-normalization.ts +41 -0
- package/src/layers/delta-heads.ts +23 -0
- package/src/layers/dense-embedding.ts +29 -0
- package/src/layers/dense-linear.ts +32 -0
- package/src/layers/dequantized-linear.ts +72 -0
- package/src/layers/folded-attention.ts +42 -0
- package/src/layers/geglu.ts +130 -0
- package/src/layers/helpers.ts +7 -0
- package/src/layers/index.ts +36 -0
- package/src/layers/kv4-decode-attention.ts +15 -0
- package/src/layers/layer-norm.ts +24 -0
- package/src/layers/loading.ts +92 -0
- package/src/layers/lora.ts +150 -0
- package/src/layers/normalization.ts +9 -0
- package/src/layers/prefix-layout.ts +33 -0
- package/src/layers/quantized-append-attention.ts +32 -0
- package/src/layers/quantized-attention.ts +353 -0
- package/src/layers/quantized-embedding.ts +39 -0
- package/src/layers/quantized-linear.ts +137 -0
- package/src/layers/quantized-switch-linear.ts +37 -0
- package/src/layers/qwen-append.ts +12 -0
- package/src/layers/qwen-mrope.ts +238 -0
- package/src/layers/rope-2d.ts +110 -0
- package/src/layers/rope.ts +389 -0
- package/src/layers/swiglu.ts +23 -0
- package/src/layers/trellis-codebook-down.ts +52 -0
- package/src/layers/trellis-codebook-gate-up.ts +42 -0
- package/src/layers/trellis-down.ts +117 -0
- package/src/layers/trellis-gate-up.ts +71 -0
- package/src/layers/trellis-linear.ts +211 -0
- package/src/layers/trellis-prompt.ts +102 -0
- package/src/layers/trellis-reduce-gate-up.ts +73 -0
- package/src/layers/trellis-scatter-down.ts +98 -0
- package/src/models/ane-install.ts +74 -0
- package/src/models/audio/conformer.ts +656 -0
- package/src/models/cache-composition.ts +44 -0
- package/src/models/capabilities.ts +89 -0
- package/src/models/chat-template.ts +10 -0
- package/src/models/composition.ts +107 -0
- package/src/models/diffusion-gemma/experts.ts +59 -0
- package/src/models/diffusion-gemma/family.ts +21 -0
- package/src/models/diffusion-gemma/model.ts +900 -0
- package/src/models/diffusion-gemma/vision.ts +549 -0
- package/src/models/drafter-loaders.ts +49 -0
- package/src/models/drafters.ts +39 -0
- package/src/models/factory.ts +351 -0
- package/src/models/families.ts +70 -0
- package/src/models/family.ts +125 -0
- package/src/models/gemma4/assistant-target.ts +46 -0
- package/src/models/gemma4/assistant.ts +327 -0
- package/src/models/gemma4/blocks.ts +755 -0
- package/src/models/gemma4/compiled-decode.ts +452 -0
- package/src/models/gemma4/family.ts +39 -0
- package/src/models/gemma4/gemma4-12b.ts +677 -0
- package/src/models/gemma4/gemma4-26b.ts +544 -0
- package/src/models/gemma4/gemma4-e4b.ts +599 -0
- package/src/models/gemma4/graphs.ts +147 -0
- package/src/models/gemma4/media.ts +88 -0
- package/src/models/gemma4/model.ts +860 -0
- package/src/models/gemma4/serving-blocks.ts +72 -0
- package/src/models/gemma4/trainable.ts +186 -0
- package/src/models/gemma4/written-blocks.ts +232 -0
- package/src/models/gemma4/written-forward.ts +159 -0
- package/src/models/gemma4/written-input.ts +29 -0
- package/src/models/glm52/atlas.ts +507 -0
- package/src/models/glm52/coupling.ts +388 -0
- package/src/models/glm52/dsa.ts +601 -0
- package/src/models/glm52/family.ts +20 -0
- package/src/models/glm52/mla.ts +812 -0
- package/src/models/glm52/model.ts +1127 -0
- package/src/models/glm52/moe.ts +223 -0
- package/src/models/glm52/mtp.ts +60 -0
- package/src/models/glm52/pilot.ts +537 -0
- package/src/models/glm52/reference.ts +445 -0
- package/src/models/glm52/residency.ts +557 -0
- package/src/models/glm52/streamed-experts.ts +447 -0
- package/src/models/graph.ts +59 -0
- package/src/models/implementation.ts +230 -0
- package/src/models/index.ts +15 -0
- package/src/models/media-input.ts +15 -0
- package/src/models/memory-plan.ts +39 -0
- package/src/models/minicpm5/family.ts +29 -0
- package/src/models/minicpm5/model.ts +394 -0
- package/src/models/minicpm5/trainable.ts +90 -0
- package/src/models/profile.ts +337 -0
- package/src/models/qwen/blocks.ts +658 -0
- package/src/models/qwen/checkpoint.ts +190 -0
- package/src/models/qwen/families.ts +57 -0
- package/src/models/qwen/media-input.ts +161 -0
- package/src/models/qwen/mtp.ts +101 -0
- package/src/models/qwen/qwen3-moe.ts +355 -0
- package/src/models/qwen/qwen3.ts +323 -0
- package/src/models/qwen/qwen38-27b-trellis-m1max.ts +1532 -0
- package/src/models/qwen/qwen38-27b-trellis-m4pro.ts +1092 -0
- package/src/models/qwen/qwen3_5.ts +433 -0
- package/src/models/qwen/written-layers.ts +285 -0
- package/src/models/row-table.ts +1 -0
- package/src/models/runtime.ts +73 -0
- package/src/models/speculative/deepspec-artifact.ts +10 -0
- package/src/models/speculative/deepspec.ts +744 -0
- package/src/models/speculative/dflash.ts +553 -0
- package/src/models/speculative/dflash2-artifact.ts +9 -0
- package/src/models/speculative/dflash2-operations.ts +1 -0
- package/src/models/speculative/dflash2.ts +576 -0
- package/src/models/speculative/loader.ts +29 -0
- package/src/models/support.ts +79 -0
- package/src/models/universal/archs.ts +365 -0
- package/src/models/universal/blocks.ts +479 -0
- package/src/models/universal/dense.ts +255 -0
- package/src/models/universal/family.ts +17 -0
- package/src/models/vision/qwen3vl.ts +566 -0
- package/src/models/vision/siglip.ts +520 -0
- package/src/models/vision/unified.ts +176 -0
- package/src/models/whisper/family.ts +19 -0
- package/src/models/whisper/fast.ts +734 -0
- package/src/models/whisper/model.ts +430 -0
- package/src/runtime/compiled-trace.ts +16 -0
- package/src/runtime/config.ts +77 -0
- package/src/runtime/expert-trace.ts +71 -0
- package/src/runtime/graph-forward.ts +65 -0
- package/src/runtime/integers.ts +6 -0
- package/src/runtime/native.ts +16 -0
- package/src/runtime/resources.ts +53 -0
- package/src/runtime/row-operations.ts +42 -0
- package/src/runtime/trace.ts +161 -0
- package/src/sampling/curve.ts +122 -0
- package/src/sampling/draft.ts +55 -0
- package/src/sampling/extras.ts +45 -0
- package/src/sampling/filters.ts +134 -0
- package/src/sampling/grammar.ts +630 -0
- package/src/sampling/hlg.ts +290 -0
- package/src/sampling/index.ts +9 -0
- package/src/sampling/processors.ts +142 -0
- package/src/sampling/sampler.ts +120 -0
- package/src/sampling/step.ts +343 -0
- package/src/sampling/types.ts +161 -0
- package/src/sampling/window-filters.ts +46 -0
- package/src/scoring/decisions.ts +96 -0
- package/src/scoring/full-sequence.ts +103 -0
- package/src/scoring/index.ts +9 -0
- package/src/scoring/kl.ts +29 -0
- package/src/scoring/perplexity.ts +165 -0
- package/src/scoring/response-logps.ts +65 -0
- package/src/state/affine-attention.ts +180 -0
- package/src/state/attachment.ts +68 -0
- package/src/state/attention-read.ts +215 -0
- package/src/state/batched-kv.ts +294 -0
- package/src/state/batched-mask.ts +445 -0
- package/src/state/batched-quant.ts +389 -0
- package/src/state/batched-quantized-kv.ts +209 -0
- package/src/state/batched-rotating-quant.ts +457 -0
- package/src/state/batched-rotating-state.ts +149 -0
- package/src/state/batched-rotating.ts +430 -0
- package/src/state/batched-row-storage.ts +179 -0
- package/src/state/batched-ssm.ts +54 -0
- package/src/state/batched-turboquant-kv.ts +144 -0
- package/src/state/bf16-first-kv.ts +663 -0
- package/src/state/cache-service.ts +592 -0
- package/src/state/capabilities.ts +65 -0
- package/src/state/checkpoint.ts +46 -0
- package/src/state/continuation-persistence.ts +126 -0
- package/src/state/decode-step-inputs.ts +31 -0
- package/src/state/decoded-kv-donor.ts +12 -0
- package/src/state/delayed-quantized-kv.ts +141 -0
- package/src/state/delayed-rotating-quantized-kv.ts +178 -0
- package/src/state/delayed-turboquant-kv.ts +123 -0
- package/src/state/durability.ts +214 -0
- package/src/state/encoder-cache.ts +26 -0
- package/src/state/full-kv-row-append.ts +33 -0
- package/src/state/full-kv-row-donor.ts +54 -0
- package/src/state/full-prefill-padding.ts +56 -0
- package/src/state/full-prefill-row.ts +112 -0
- package/src/state/full-transitioning-kv-rows.ts +70 -0
- package/src/state/glm52-cache.ts +783 -0
- package/src/state/grouped-heads-kv4-cache.ts +26 -0
- package/src/state/index.ts +38 -0
- package/src/state/kv-attention-view.ts +77 -0
- package/src/state/kv-budget.ts +14 -0
- package/src/state/kv-scheme.ts +315 -0
- package/src/state/kv-storage.ts +57 -0
- package/src/state/kv-tensor-rows.ts +212 -0
- package/src/state/kv.ts +324 -0
- package/src/state/kv4-head256-cache.ts +46 -0
- package/src/state/latent-legos.ts +895 -0
- package/src/state/layout.ts +277 -0
- package/src/state/leases.ts +26 -0
- package/src/state/paged/cache.ts +689 -0
- package/src/state/paged/codec.ts +36 -0
- package/src/state/paged/rows.ts +223 -0
- package/src/state/persistence-types.ts +173 -0
- package/src/state/persistence-worker.ts +83 -0
- package/src/state/persistence.ts +1036 -0
- package/src/state/persistence.worker.d.ts +3 -0
- package/src/state/persistence.worker.js +264 -0
- package/src/state/prefix-cache.ts +685 -0
- package/src/state/quantized-kv.ts +369 -0
- package/src/state/quantized-tensor.ts +26 -0
- package/src/state/request-policy.ts +48 -0
- package/src/state/retention-policy.ts +37 -0
- package/src/state/rollback.ts +131 -0
- package/src/state/rotating-kv-donor.ts +35 -0
- package/src/state/rotating-kv-layout.ts +341 -0
- package/src/state/rotating-kv.ts +529 -0
- package/src/state/rotating-quantized-kv.ts +616 -0
- package/src/state/rotating-row-transaction.ts +35 -0
- package/src/state/rows.ts +104 -0
- package/src/state/softcap-attention.ts +118 -0
- package/src/state/speculative/glm52-mtp-state.ts +38 -0
- package/src/state/speculative/qwen-mtp-state.ts +34 -0
- package/src/state/speculative-rotating-kv.ts +113 -0
- package/src/state/ssd-cache.ts +434 -0
- package/src/state/ssm-prefill-padding.ts +39 -0
- package/src/state/ssm.ts +422 -0
- package/src/state/target-layout.ts +7 -0
- package/src/state/tiered-prefix-cache.ts +45 -0
- package/src/state/training-cache.ts +300 -0
- package/src/state/transitioning-kv-rows.ts +267 -0
- package/src/state/turboquant-attention.ts +97 -0
- package/src/state/turboquant-codec.ts +90 -0
- package/src/state/turboquant-kv.ts +442 -0
- package/src/state/views.ts +26 -0
- package/src/transcription/index.ts +3 -0
- package/src/transcription/silero-vad.ts +379 -0
- package/src/transcription/whisper/decode.ts +773 -0
- package/src/transcription/whisper/format.ts +74 -0
- package/src/transcription/whisper/index.ts +4 -0
- package/src/transcription/whisper/timing.ts +274 -0
- package/src/transcription/whisper/transcribe.ts +394 -0
- package/src/transcription/whisper/types.ts +22 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { quantizedSdpaGroupedHeads } from "../layers/quantized-attention";
|
|
2
|
+
import { unfusedKernel, type AffineKernels } from "./affine-attention";
|
|
3
|
+
import { AttentionMasks } from "./attention-read";
|
|
4
|
+
import { BatchedQuantizedKVCache } from "./batched-quantized-kv";
|
|
5
|
+
|
|
6
|
+
const grouped: AffineKernels["window"] = (q, keys, values, scale, mask) =>
|
|
7
|
+
quantizedSdpaGroupedHeads(q, keys, values, scale, mask, 64, 4);
|
|
8
|
+
const kernels: AffineKernels = { decode: unfusedKernel(64, 4), window: grouped, maskedWindow: grouped };
|
|
9
|
+
|
|
10
|
+
/** The 4-bit group-64 batched full-attention cache of a depth-2 speculative
|
|
11
|
+
* verify composition on the M4 Pro GPU (applegpu_g16s): `BatchedQuantizedKVCache`
|
|
12
|
+
* whose window read is the grouped-heads key matmul
|
|
13
|
+
* (`quantizedSdpaGroupedHeads`, three GQA heads per batch, where MLX's native
|
|
14
|
+
* key matvec keeps exact arithmetic at 8192 or more keys) and whose decode read
|
|
15
|
+
* is the stock unfused port. Masks are the batched layout's.
|
|
16
|
+
*
|
|
17
|
+
* The window kernel's contract is the verify window: 3 queries per row, 24
|
|
18
|
+
* query heads, 4 KV heads, head dim 256, B <= 2, bf16 or f32 queries. Today
|
|
19
|
+
* `quantizedSdpaUnfused` applied it on that GPU at those shapes and 8192 or
|
|
20
|
+
* more keys, whenever the dispatch ran unfused; this cache applies it to every
|
|
21
|
+
* window, so it serves only a composition whose windows are all verify windows.
|
|
22
|
+
* Constructed explicitly; nothing selects it by device. */
|
|
23
|
+
export class GroupedHeadsKv4Cache extends BatchedQuantizedKVCache {
|
|
24
|
+
constructor(masks = new AttentionMasks()) { super(64, 4, kernels, masks); }
|
|
25
|
+
override makeEmptyBatch(): GroupedHeadsKv4Cache { return new GroupedHeadsKv4Cache(this.masks); }
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export * from "./kv";
|
|
2
|
+
export * from "./quantized-kv";
|
|
3
|
+
export { composedAffineKernels, tiledAffineKernels, tiledCausalAffineKernels, unfusedAffineKernels, type AffineKernels } from "./affine-attention";
|
|
4
|
+
export * from "./rotating-kv";
|
|
5
|
+
export * from "./rotating-quantized-kv";
|
|
6
|
+
export * from "./turboquant-kv";
|
|
7
|
+
export { deferredTurboQuantKernels, denseTurboQuantKernels, uncomposedTurboQuantKernels, type TurboQuantKernels } from "./turboquant-attention";
|
|
8
|
+
export * from "./turboquant-codec";
|
|
9
|
+
export * from "./ssm";
|
|
10
|
+
export * from "./glm52-cache";
|
|
11
|
+
export * from "./views";
|
|
12
|
+
export * from "./checkpoint";
|
|
13
|
+
export { createKvMaintenance, unchangedKv, type KvMaintenance } from "./layout";
|
|
14
|
+
export * from "./kv-scheme";
|
|
15
|
+
export * from "./persistence";
|
|
16
|
+
export * from "./batched-kv";
|
|
17
|
+
export * from "./batched-quantized-kv";
|
|
18
|
+
export * from "./batched-turboquant-kv";
|
|
19
|
+
export * from "./batched-rotating";
|
|
20
|
+
export * from "./batched-rotating-quant";
|
|
21
|
+
export * from "./bf16-first-kv";
|
|
22
|
+
export * from "./delayed-quantized-kv";
|
|
23
|
+
export * from "./delayed-turboquant-kv";
|
|
24
|
+
export * from "./delayed-rotating-quantized-kv";
|
|
25
|
+
export * from "./batched-ssm";
|
|
26
|
+
export * from "./speculative-rotating-kv";
|
|
27
|
+
export * from "./rollback";
|
|
28
|
+
export * from "./encoder-cache";
|
|
29
|
+
export * from "./prefix-cache";
|
|
30
|
+
export * from "./retention-policy";
|
|
31
|
+
export * from "./rows";
|
|
32
|
+
export * from "./attachment";
|
|
33
|
+
export * from "./ssd-cache";
|
|
34
|
+
export * from "./tiered-prefix-cache";
|
|
35
|
+
export * from "./durability";
|
|
36
|
+
export * from "./continuation-persistence";
|
|
37
|
+
export * from "./kv-budget";
|
|
38
|
+
export * from "./cache-service";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import type { Cache, KvAttentionView, KvDonorAttention } from "../contracts/mlx/cache";
|
|
4
|
+
import { disposeTriple } from "./quantized-tensor";
|
|
5
|
+
import { kvStorage } from "./kv-storage";
|
|
6
|
+
import { quantizedAppendAttention } from "../layers/quantized-append-attention";
|
|
7
|
+
import { viewAffineKernel, type AffineKernels } from "./affine-attention";
|
|
8
|
+
|
|
9
|
+
export function captureKvDonorAttention(cache: Cache): KvDonorAttention {
|
|
10
|
+
if (cache.captureDonorAttention) return cache.captureDonorAttention();
|
|
11
|
+
const view = cache.captureDonorRows!();
|
|
12
|
+
return {
|
|
13
|
+
width: view.keys.shape[2]!, dtype: view.keys.dtype,
|
|
14
|
+
offsets: view.offsets, starts: view.starts, ends: view.ends,
|
|
15
|
+
attend: (q, scale, mask) => ops.sdpa(q, view.keys, view.values, scale, mask.mode, mask.arr),
|
|
16
|
+
dispose() { view.keys.dispose(); view.values.dispose(); },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Q-only attention over fetched affine keys and values with the storage's
|
|
21
|
+
* kernel set (`viewAffineKernel`). Takes ownership of `keys` and `values`. */
|
|
22
|
+
export function quantizedDonorAttention(keys: ops.QuantizedTensor, values: ops.QuantizedTensor,
|
|
23
|
+
validity: Pick<KvDonorAttention, "offsets" | "starts" | "ends">, kernels: AffineKernels): KvDonorAttention {
|
|
24
|
+
return {
|
|
25
|
+
width: keys.packed.shape[2]!, dtype: keys.scales.dtype, ...validity,
|
|
26
|
+
attend: (q, scale, mask) => viewAffineKernel(kernels, q.shape[2]!, mask)(q, keys, values, scale, mask),
|
|
27
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Append `k` and `v` to `cache` and return its attention view. Plain storage
|
|
32
|
+
* reads with the stock SDPA; affine storage with `kernels`, the kernel set of
|
|
33
|
+
* the row layout reading it (`viewAffineKernel`). */
|
|
34
|
+
export function captureKvAttention(row: Cache, k: MlxArray, v: MlxArray, kernels: AffineKernels): KvAttentionView {
|
|
35
|
+
const cache = kvStorage(row);
|
|
36
|
+
if (cache.attentionState) return cache.attentionState.appendAndFetch(k, v);
|
|
37
|
+
const quantized = cache.quantizedAttention;
|
|
38
|
+
if (quantized) {
|
|
39
|
+
const { groupSize, bits } = quantized;
|
|
40
|
+
const [keys, values] = quantized.updateAndFetchQuantized(k, v);
|
|
41
|
+
return {
|
|
42
|
+
attend: (q, scale, mask, independentPositions) => independentPositions && q.shape[2]! > 1
|
|
43
|
+
? quantizedAppendAttention(q, keys, values, scale, groupSize, bits)
|
|
44
|
+
: viewAffineKernel(kernels, q.shape[2]!, mask)(q, keys, values, scale, mask),
|
|
45
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const [keys, values] = cache.updateAndFetch(k, v);
|
|
49
|
+
return {
|
|
50
|
+
attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
|
|
51
|
+
dispose() { keys.dispose(); values.dispose(); },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/** Takes ownership of already aligned per-row donor views. Each storage codec
|
|
57
|
+
* keeps its own attention arithmetic while the caller supplies one query batch. */
|
|
58
|
+
export function combineKvDonorAttention(rows: readonly KvDonorAttention[]): KvDonorAttention {
|
|
59
|
+
return { width: rows[0]!.width, dtype: rows[0]!.dtype,
|
|
60
|
+
offsets: rows.flatMap(row => [...row.offsets]), starts: rows.flatMap(row => [...row.starts]),
|
|
61
|
+
ends: rows.flatMap(row => [...row.ends]),
|
|
62
|
+
attend(q, scale, mask) {
|
|
63
|
+
const outputs: MlxArray[] = [];
|
|
64
|
+
try {
|
|
65
|
+
for (const [index, row] of rows.entries()) {
|
|
66
|
+
using query = q.slice([index, 0, 0, 0], [index + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
|
|
67
|
+
const from = mask.arr?.shape.map(() => 0), to = mask.arr ? [...mask.arr.shape] : undefined;
|
|
68
|
+
if (from && to && to.length === 4 && to[0]! > 1) { from[0] = index; to[0] = index + 1; }
|
|
69
|
+
using arr = mask.arr ? mask.arr.slice(from!, to!) : null;
|
|
70
|
+
outputs.push(row.attend(query, scale, { mode: mask.mode, arr }));
|
|
71
|
+
}
|
|
72
|
+
return outputs.length === 1 ? ops.contiguous(outputs[0]!) : ops.concatAxis(outputs, 0);
|
|
73
|
+
} finally { for (const output of outputs) output.dispose(); }
|
|
74
|
+
},
|
|
75
|
+
dispose() { for (const row of rows) row.dispose(); },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModelConfig } from "../artifacts/config";
|
|
2
|
+
import { kvBytesAt, KvScheme } from "./kv-scheme";
|
|
3
|
+
|
|
4
|
+
/** Project one batch row's worst-case KV bytes using the same per-layer
|
|
5
|
+
* scheme that the server-wide admission ceiling uses. */
|
|
6
|
+
export function batchRowKvBytes(
|
|
7
|
+
config: ModelConfig,
|
|
8
|
+
promptTokens: number,
|
|
9
|
+
maxTokens: number,
|
|
10
|
+
scheme?: KvScheme,
|
|
11
|
+
): number {
|
|
12
|
+
const tokens = promptTokens + maxTokens;
|
|
13
|
+
return scheme ? scheme.bytesAt(config, tokens) : kvBytesAt(config, tokens);
|
|
14
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import type { KvQuantSpec, ModelConfig, TurboQuantScheme } from "../artifacts/config";
|
|
2
|
+
import type { AffineKernelChoice, KvStorageScheme } from "../contracts/portable/composition";
|
|
3
|
+
|
|
4
|
+
const BF16_BYTES = 2;
|
|
5
|
+
|
|
6
|
+
export type KvQuantOverride = "off" | "config" | number | undefined;
|
|
7
|
+
|
|
8
|
+
export interface KvSchemeOptions {
|
|
9
|
+
kvBits?: number;
|
|
10
|
+
kvGroupSize?: number;
|
|
11
|
+
kvConfig?: KvQuantSpec[];
|
|
12
|
+
quantizedKvStart?: number;
|
|
13
|
+
turboQuant?: TurboQuantScheme;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ResolvedKvSchemeOptions extends Omit<KvSchemeOptions, "kvConfig" | "turboQuant"> {
|
|
17
|
+
readonly kvConfig?: readonly Readonly<KvQuantSpec>[];
|
|
18
|
+
readonly turboQuant?: Readonly<TurboQuantScheme>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type KvSchemeKind = "bf16" | "affine-uniform" | "affine-config" | "turbo";
|
|
22
|
+
|
|
23
|
+
/** How one attention layer stores its cache: every position (`full`), a rolling
|
|
24
|
+
* window (`sliding`), or a fixed-size recurrent state with no KV (`recurrent`). */
|
|
25
|
+
export type CacheLayerKind = "full" | "sliding" | "recurrent";
|
|
26
|
+
|
|
27
|
+
/** The cache layout the config declares for a layer. A layer the config does not
|
|
28
|
+
* list is full attention. The one place a layer-type spelling is read. */
|
|
29
|
+
export function cacheLayerKind(config: Pick<ModelConfig, "text">, layer: number): CacheLayerKind {
|
|
30
|
+
const type = config.text.layerTypes[layer];
|
|
31
|
+
return type === "linear_attention" ? "recurrent" : type === "sliding_attention" ? "sliding" : "full";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface KvGeometry {
|
|
35
|
+
fullBytesPerToken: number;
|
|
36
|
+
slidingBytesPerToken: number;
|
|
37
|
+
linearStateBytes: number;
|
|
38
|
+
window: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Bytes per stored affine-quantized KV element, including scale and bias. */
|
|
42
|
+
export function kvQuantBytesPerElement(bits: number, groupSize: number): number {
|
|
43
|
+
return bits / 8 + 4 / groupSize;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function kvGeometry(config: ModelConfig, options: KvSchemeOptions = {}): KvGeometry {
|
|
47
|
+
const text = config.text;
|
|
48
|
+
const byLayer = options.kvConfig?.length
|
|
49
|
+
? new Map(options.kvConfig.map((entry) => [entry.layerIdx, entry]))
|
|
50
|
+
: null;
|
|
51
|
+
const uniformBytes = options.kvBits
|
|
52
|
+
? kvQuantBytesPerElement(options.kvBits, options.kvGroupSize ?? 64)
|
|
53
|
+
: BF16_BYTES;
|
|
54
|
+
const elementBytes = (layer: number): number => {
|
|
55
|
+
const entry = byLayer?.get(layer);
|
|
56
|
+
return entry
|
|
57
|
+
? kvQuantBytesPerElement(entry.bits, entry.groupSize)
|
|
58
|
+
: byLayer ? BF16_BYTES : uniformBytes;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let linearLayers = 0;
|
|
62
|
+
let fullBytesPerToken = 0;
|
|
63
|
+
let slidingBytesPerToken = 0;
|
|
64
|
+
for (let layer = 0; layer < text.numHiddenLayers; layer++) {
|
|
65
|
+
const kind = cacheLayerKind(config, layer);
|
|
66
|
+
if (kind === "recurrent") {
|
|
67
|
+
linearLayers++;
|
|
68
|
+
} else if (kind === "sliding") {
|
|
69
|
+
slidingBytesPerToken +=
|
|
70
|
+
2 * text.numKeyValueHeads * text.headDim * elementBytes(layer);
|
|
71
|
+
} else {
|
|
72
|
+
fullBytesPerToken +=
|
|
73
|
+
2 * text.numGlobalKeyValueHeads * text.globalHeadDim * elementBytes(layer);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const convDim = 2 * text.linearNumKeyHeads * text.linearKeyHeadDim +
|
|
77
|
+
text.linearNumValueHeads * text.linearValueHeadDim;
|
|
78
|
+
const linearStateBytes = linearLayers === 0 ? 0 : linearLayers * (
|
|
79
|
+
text.linearNumValueHeads * text.linearValueHeadDim * text.linearKeyHeadDim * 4 +
|
|
80
|
+
Math.max(0, text.linearConvKernelDim - 1) * convDim * BF16_BYTES
|
|
81
|
+
);
|
|
82
|
+
return {
|
|
83
|
+
fullBytesPerToken,
|
|
84
|
+
slidingBytesPerToken,
|
|
85
|
+
linearStateBytes,
|
|
86
|
+
window: text.slidingWindow,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function kvBytesAt(
|
|
91
|
+
config: ModelConfig,
|
|
92
|
+
tokens: number,
|
|
93
|
+
options: KvSchemeOptions = {},
|
|
94
|
+
): number {
|
|
95
|
+
const value = kvGeometry(config, options);
|
|
96
|
+
return value.fullBytesPerToken * tokens +
|
|
97
|
+
value.slidingBytesPerToken * Math.min(tokens, value.window) +
|
|
98
|
+
value.linearStateBytes;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Head dims MLX 0.32.2's fused multi-query SDPA kernel serves for L > 8
|
|
102
|
+
* queries on M1–M4 GPUs (mlx/backend/metal/scaled_dot_product_attention.cpp
|
|
103
|
+
* `use_fallback`: 192 and 256 always fall back off-NAX; `has_fused_kernel`
|
|
104
|
+
* lists 64/72/80/96/128 for the full kernel). Every other head dim runs
|
|
105
|
+
* mlx/fast.cpp's composed fallback, which materializes the bf16 scores
|
|
106
|
+
* tensor [heads, L, S] plus a bool mask per full-attention layer. The
|
|
107
|
+
* decode kernel (L ≤ 8) fuses 256, so this term is a prefill-only cost. */
|
|
108
|
+
export const FUSED_SDPA_HEAD_DIMS: ReadonlySet<number> = new Set([64, 72, 80, 96, 128]);
|
|
109
|
+
const SDPA_SCORES_BYTES = 2; // bf16 scores in q's dtype
|
|
110
|
+
const SDPA_FUSED_MAX_QUERIES = 8;
|
|
111
|
+
|
|
112
|
+
/** Peak bytes of the SDPA-fallback scores tensor live during one prefill
|
|
113
|
+
* chunk of `chunk` queries against a `ctx`-token context: the last chunk
|
|
114
|
+
* sees the longest keys, and the lazy graph keeps one layer's scores live
|
|
115
|
+
* at a time. Zero when every attention layer's head dim fuses. Sliding
|
|
116
|
+
* layers see at most `window + L` keys. Admission-only (fit and the
|
|
117
|
+
* in-flight memory guard); nothing here changes what is computed. */
|
|
118
|
+
export function sdpaFallbackBytes(config: ModelConfig, chunk: number, ctx: number): number {
|
|
119
|
+
const text = config.text;
|
|
120
|
+
const heads = text.numAttentionHeads || 0;
|
|
121
|
+
const queries = Math.min(chunk, ctx);
|
|
122
|
+
if (heads <= 0 || queries <= SDPA_FUSED_MAX_QUERIES) return 0;
|
|
123
|
+
let peak = 0;
|
|
124
|
+
const layers = text.numHiddenLayers || text.layerTypes.length;
|
|
125
|
+
for (let layer = 0; layer < layers; layer++) {
|
|
126
|
+
const kind = cacheLayerKind(config, layer);
|
|
127
|
+
if (kind === "recurrent") continue;
|
|
128
|
+
const sliding = kind === "sliding" && text.slidingWindow > 0;
|
|
129
|
+
const headDim = sliding ? text.headDim : text.globalHeadDim;
|
|
130
|
+
if (FUSED_SDPA_HEAD_DIMS.has(headDim)) continue;
|
|
131
|
+
const keys = sliding ? Math.min(ctx, text.slidingWindow + queries) : ctx;
|
|
132
|
+
peak = Math.max(peak, heads * queries * keys * SDPA_SCORES_BYTES);
|
|
133
|
+
}
|
|
134
|
+
return peak;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The conversion start a scheme's caches keep bf16 for: the explicit
|
|
138
|
+
* `quantizedKvStart`, else immediately for a per-layer config and TurboQuant
|
|
139
|
+
* and after 5000 tokens for uniform bits (mlx-lm's default). */
|
|
140
|
+
function conversionStart(kind: KvSchemeKind, options: Readonly<ResolvedKvSchemeOptions>): number {
|
|
141
|
+
return options.quantizedKvStart ?? (kind === "affine-uniform" ? 5000 : 0);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The composition record's form of a scheme: what the graph's `makeCache`
|
|
145
|
+
* composes, with the affine read kernels and the conversion start (carried
|
|
146
|
+
* only when nonzero). */
|
|
147
|
+
function storageOf(kind: KvSchemeKind, options: Readonly<ResolvedKvSchemeOptions>, kernels: AffineKernelChoice): KvStorageScheme {
|
|
148
|
+
const tokens = conversionStart(kind, options);
|
|
149
|
+
const start = tokens > 0 ? { quantizedKvStart: tokens } : {};
|
|
150
|
+
switch (kind) {
|
|
151
|
+
case "bf16": return Object.freeze({ kind: "bf16" });
|
|
152
|
+
case "affine-uniform":
|
|
153
|
+
return Object.freeze({ kind: "affine", bits: options.kvBits!, groupSize: options.kvGroupSize ?? 64, ...start, kernels });
|
|
154
|
+
case "affine-config":
|
|
155
|
+
return Object.freeze({ kind: "affine-layers", layers: Object.freeze(options.kvConfig!.map(entry =>
|
|
156
|
+
Object.freeze({ layer: entry.layerIdx, bits: entry.bits, groupSize: entry.groupSize }))), ...start, kernels });
|
|
157
|
+
case "turbo":
|
|
158
|
+
return Object.freeze({ kind: "turbo", kBits: options.turboQuant!.kBits, vBits: options.turboQuant!.vBits, ...start });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** The kernels a scheme composes when `--fused-sdpa` is unset: OptiQ's
|
|
163
|
+
* tiled-causal rule for a per-layer config, mlx-lm's unfused port otherwise. */
|
|
164
|
+
function defaultKernels(kind: KvSchemeKind): AffineKernelChoice {
|
|
165
|
+
return kind === "affine-config" ? "tiled-causal" : "unfused";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export class KvScheme {
|
|
169
|
+
readonly kind: KvSchemeKind;
|
|
170
|
+
readonly options: Readonly<ResolvedKvSchemeOptions>;
|
|
171
|
+
/** The composition record's form (`Composition.kv`): the storage every
|
|
172
|
+
* attention cache of the graph composes and the read kernels of its affine
|
|
173
|
+
* caches. */
|
|
174
|
+
readonly storage: KvStorageScheme;
|
|
175
|
+
|
|
176
|
+
constructor(kind: KvSchemeKind, options: KvSchemeOptions, kernels: AffineKernelChoice = defaultKernels(kind)) {
|
|
177
|
+
this.kind = kind;
|
|
178
|
+
const kvConfig = options.kvConfig?.map((entry) =>
|
|
179
|
+
Object.freeze({ ...entry }),
|
|
180
|
+
);
|
|
181
|
+
const turboQuant = options.turboQuant
|
|
182
|
+
? Object.freeze({ ...options.turboQuant })
|
|
183
|
+
: undefined;
|
|
184
|
+
this.options = Object.freeze({
|
|
185
|
+
...options,
|
|
186
|
+
...(kvConfig ? { kvConfig: Object.freeze(kvConfig) } : {}),
|
|
187
|
+
...(turboQuant ? { turboQuant } : {}),
|
|
188
|
+
});
|
|
189
|
+
this.storage = storageOf(kind, this.options, kernels);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get quantized(): boolean {
|
|
193
|
+
return this.kind !== "bf16";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get cacheKey(): string {
|
|
197
|
+
if (this.kind === "turbo") {
|
|
198
|
+
const turbo = this.options.turboQuant!;
|
|
199
|
+
const start = this.options.quantizedKvStart ?? 0;
|
|
200
|
+
return `turbo-k${turbo.kBits}v${turbo.vBits}${start > 0 ? `-start${start}` : ""}`;
|
|
201
|
+
}
|
|
202
|
+
if (this.kind === "affine-uniform") {
|
|
203
|
+
const start = this.options.quantizedKvStart ?? 5000, group = this.options.kvGroupSize ?? 64;
|
|
204
|
+
return `kv${this.options.kvBits}${group !== 64 ? `-g${group}` : ""}${start > 0 ? `-start${start}` : ""}`;
|
|
205
|
+
}
|
|
206
|
+
if (this.kind === "affine-config") {
|
|
207
|
+
const start = this.options.quantizedKvStart ?? 0;
|
|
208
|
+
return `config${start > 0 ? `-start${start}` : ""}`;
|
|
209
|
+
}
|
|
210
|
+
return "bf16";
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get label(): string {
|
|
214
|
+
if (this.kind === "turbo") {
|
|
215
|
+
const turbo = this.options.turboQuant!;
|
|
216
|
+
return `turbo k${turbo.kBits}v${turbo.vBits}`;
|
|
217
|
+
}
|
|
218
|
+
if (this.kind === "affine-uniform") return `uniform-kv${this.options.kvBits}`;
|
|
219
|
+
if (this.kind === "affine-config") return "mixed (kv_config.json)";
|
|
220
|
+
return "bf16";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Configured attention storage, not live allocator occupancy. Recurrent
|
|
224
|
+
* layers retain their own state and never acquire an attention KV codec. */
|
|
225
|
+
describe(config: ModelConfig) {
|
|
226
|
+
const layers: Record<string, number> = {};
|
|
227
|
+
const attention = { global: 0, sliding_window: 0 };
|
|
228
|
+
let recurrent_layers = 0;
|
|
229
|
+
const perLayer = new Map(this.options.kvConfig?.map(entry => [entry.layerIdx, entry.bits]));
|
|
230
|
+
for (let layer = 0; layer < config.text.numHiddenLayers; layer++) {
|
|
231
|
+
const kind = cacheLayerKind(config, layer);
|
|
232
|
+
if (kind === "recurrent") { recurrent_layers++; continue; }
|
|
233
|
+
const sliding = kind === "sliding";
|
|
234
|
+
attention[sliding ? "sliding_window" : "global"]++;
|
|
235
|
+
const bits = this.kind === "affine-config" ? perLayer.get(layer)
|
|
236
|
+
: this.kind === "affine-uniform" ? this.options.kvBits : undefined;
|
|
237
|
+
const codec = this.kind === "turbo" && !sliding
|
|
238
|
+
? `turbo-k${this.options.turboQuant!.kBits}v${this.options.turboQuant!.vBits}`
|
|
239
|
+
: bits ? `kv${bits}` : "bf16";
|
|
240
|
+
layers[codec] = (layers[codec] ?? 0) + 1;
|
|
241
|
+
}
|
|
242
|
+
return { mode: this.label, layers, attention, recurrent_layers };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
bytesAt(config: ModelConfig, tokens: number): number {
|
|
246
|
+
// TurboQuant stays conservatively billed as bf16 until its packed layout
|
|
247
|
+
// exposes a stable projector.
|
|
248
|
+
return kvBytesAt(config, tokens, this.fitOptions);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Owned copies of the options for memory fit; TurboQuant fits as bf16. */
|
|
252
|
+
get fitOptions(): KvSchemeOptions {
|
|
253
|
+
if (this.kind === "turbo") return {};
|
|
254
|
+
const { kvConfig, turboQuant: _turbo, ...scalar } = this.options;
|
|
255
|
+
return { ...scalar, ...(kvConfig ? { kvConfig: kvConfig.map((entry) => ({ ...entry })) } : {}) };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Resolve the KV flags (`--kv-quant`, its TurboQuant spelling,
|
|
260
|
+
* `--quantized-kv-start`, `--fused-sdpa`) against the artifact's
|
|
261
|
+
* `kv_config.json`. `fusedSdpa` picks the affine read kernels (on:
|
|
262
|
+
* tiled-causal, off: unfused); unset, they follow the scheme's kind. */
|
|
263
|
+
export function resolveKvScheme(input: {
|
|
264
|
+
override?: KvQuantOverride;
|
|
265
|
+
turboQuant?: TurboQuantScheme;
|
|
266
|
+
config?: readonly KvQuantSpec[] | null;
|
|
267
|
+
missingConfig?: "bf16" | "error";
|
|
268
|
+
quantizedKvStart?: number;
|
|
269
|
+
fusedSdpa?: boolean;
|
|
270
|
+
}): KvScheme {
|
|
271
|
+
const start = input.quantizedKvStart;
|
|
272
|
+
if (start !== undefined && (!Number.isSafeInteger(start) || start < 0))
|
|
273
|
+
throw new Error("quantizedKvStart must be a nonnegative integer");
|
|
274
|
+
const kernels = (kind: KvSchemeKind): AffineKernelChoice =>
|
|
275
|
+
input.fusedSdpa === undefined ? defaultKernels(kind) : input.fusedSdpa ? "tiled-causal" : "unfused";
|
|
276
|
+
if (input.turboQuant) {
|
|
277
|
+
return new KvScheme("turbo", {
|
|
278
|
+
turboQuant: input.turboQuant,
|
|
279
|
+
quantizedKvStart: start ?? 0,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (input.override === "off" || input.override === undefined)
|
|
283
|
+
return new KvScheme("bf16", {});
|
|
284
|
+
if (input.override === "config") {
|
|
285
|
+
if (!input.config?.length) {
|
|
286
|
+
if (input.missingConfig === "error")
|
|
287
|
+
throw new Error("model has no kv_config.json (--kv-quant config)");
|
|
288
|
+
return new KvScheme("bf16", {});
|
|
289
|
+
}
|
|
290
|
+
return new KvScheme("affine-config", { kvConfig: [...input.config],
|
|
291
|
+
...(start === undefined ? {} : { quantizedKvStart: start }) }, kernels("affine-config"));
|
|
292
|
+
}
|
|
293
|
+
return new KvScheme("affine-uniform", {
|
|
294
|
+
kvBits: input.override,
|
|
295
|
+
quantizedKvStart: start ?? 0,
|
|
296
|
+
}, kernels("affine-uniform"));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** The scheme a composition record names (`Composition.kv`): the inverse of
|
|
300
|
+
* `KvScheme.storage`, for what describes, bills and keys the composed caches. */
|
|
301
|
+
export function kvSchemeOf(kv: KvStorageScheme): KvScheme {
|
|
302
|
+
switch (kv.kind) {
|
|
303
|
+
case "bf16": return new KvScheme("bf16", {});
|
|
304
|
+
case "affine":
|
|
305
|
+
return new KvScheme("affine-uniform", { kvBits: kv.bits, kvGroupSize: kv.groupSize,
|
|
306
|
+
quantizedKvStart: kv.quantizedKvStart ?? 0 }, kv.kernels);
|
|
307
|
+
case "affine-layers":
|
|
308
|
+
return new KvScheme("affine-config", { kvConfig: kv.layers.map(entry =>
|
|
309
|
+
({ layerIdx: entry.layer, bits: entry.bits, groupSize: entry.groupSize })),
|
|
310
|
+
quantizedKvStart: kv.quantizedKvStart ?? 0 }, kv.kernels);
|
|
311
|
+
case "turbo":
|
|
312
|
+
return new KvScheme("turbo", { turboQuant: { kBits: kv.kBits, vBits: kv.vBits },
|
|
313
|
+
quantizedKvStart: kv.quantizedKvStart ?? 0 });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// The storage-level reads and conversions state's own caches use on one
|
|
2
|
+
// another: a row layout fetching its serial rows' keys and values, a delayed
|
|
3
|
+
// lego converting the storage it holds, a donor capture reading packed or
|
|
4
|
+
// rotated storage. They are internal to `state/`. Graphs, layers and every
|
|
5
|
+
// other caller read a cache through the cache contract (`AttentionCache` and
|
|
6
|
+
// its named reads, `GatedDeltaCache`), which carries none of these.
|
|
7
|
+
|
|
8
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
9
|
+
import type { Dtype } from "@mlx-bun/mlx/ffi";
|
|
10
|
+
import type * as ops from "@mlx-bun/mlx/ops";
|
|
11
|
+
import type { Cache, KvAttentionView, Mask } from "../contracts/mlx/cache";
|
|
12
|
+
|
|
13
|
+
/** Affine-quantized storage: appends and hands back its packed keys and values. */
|
|
14
|
+
export interface QuantizedAttentionState {
|
|
15
|
+
readonly groupSize: number;
|
|
16
|
+
readonly bits: number;
|
|
17
|
+
updateAndFetchQuantized(k: MlxArray, v: MlxArray): [ops.QuantizedTensor, ops.QuantizedTensor];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** TurboQuant storage whose values stay in the codec's rotated domain until
|
|
21
|
+
* after attention. */
|
|
22
|
+
export interface RotatedValueAttentionState {
|
|
23
|
+
/** Capture a fetched value domain independently of later row changes. */
|
|
24
|
+
captureValueTransform?(): (output: MlxArray) => MlxArray;
|
|
25
|
+
updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Storage that appends once and hands every attention consumer the same view. */
|
|
29
|
+
export interface KvAttentionState {
|
|
30
|
+
appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A cache as `state/` sees its storage. */
|
|
34
|
+
export interface KvStorage extends Cache {
|
|
35
|
+
/** Appends `k` and `v` and returns the stored keys and values (owned views). */
|
|
36
|
+
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
|
|
37
|
+
/** The mask an `N`-position append would read under, from this storage's state. */
|
|
38
|
+
makeMask(N: number, windowSize: number | null): Mask;
|
|
39
|
+
/** Affine conversion of this storage. Logical position can differ from the
|
|
40
|
+
* physical write head in a padded rotating layout. `kernels` builds the
|
|
41
|
+
* kernel set the converted storage attends with (`AffineKernelSet`) for its
|
|
42
|
+
* bits, group size and activation dtype. */
|
|
43
|
+
readonly affineConversion?: { readonly offset: number;
|
|
44
|
+
toQuantized(groupSize: number, bits: number, kernels: (bits: number, groupSize: number, dtype: Dtype) => unknown): Cache };
|
|
45
|
+
/** TurboQuant conversion of this storage. */
|
|
46
|
+
readonly turboConversion?: { readonly offset: number; toTurboQuantized(kBits: number, vBits: number): Cache };
|
|
47
|
+
readonly attentionState?: KvAttentionState;
|
|
48
|
+
readonly rotatedValueAttention?: RotatedValueAttentionState;
|
|
49
|
+
readonly quantizedAttention?: QuantizedAttentionState;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `cache` as storage, for a caller inside `state/` holding a KV storage
|
|
53
|
+
* (every key/value storage here implements these reads; the contract does not
|
|
54
|
+
* promise them, so nothing outside `state/` may call them). */
|
|
55
|
+
export function kvStorage(cache: Cache): KvStorage {
|
|
56
|
+
return cache as KvStorage;
|
|
57
|
+
}
|