@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,212 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
3
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
4
|
+
import { materializeCopy } from "@mlx-bun/mlx/materialize";
|
|
5
|
+
import { FullPrefillPadding } from "./full-prefill-padding";
|
|
6
|
+
import type { Mask, PrefillPadding } from "../contracts/mlx/cache";
|
|
7
|
+
import { cleanupFailure, disposeResources } from "../runtime/resources";
|
|
8
|
+
|
|
9
|
+
/** Borrowed tensor planes with shared token positions. Plane shapes may differ
|
|
10
|
+
* in head width and dtype. Every plane uses [row, head, token, field]. */
|
|
11
|
+
export interface KvTensorRowView {
|
|
12
|
+
readonly planes: readonly MlxArray[];
|
|
13
|
+
readonly rowOffsets: readonly number[];
|
|
14
|
+
readonly leftPad: readonly number[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Position and membership operations shared by packed KV representations.
|
|
18
|
+
* A codec supplies tensor planes; this owner never encodes or decodes them. */
|
|
19
|
+
export class KvTensorRows implements KvTensorRowView {
|
|
20
|
+
planes: MlxArray[] = [];
|
|
21
|
+
rowOffsets: number[] = [];
|
|
22
|
+
leftPad: number[] = [];
|
|
23
|
+
#rope: MlxArray | undefined;
|
|
24
|
+
#beforeRound: number[] | undefined;
|
|
25
|
+
readonly #padding = new FullPrefillPadding();
|
|
26
|
+
|
|
27
|
+
restorePrefillEnds(ends: readonly number[] | undefined): void { this.#padding.restoreEnds(ends); }
|
|
28
|
+
preparePrefill(padding: PrefillPadding): void {
|
|
29
|
+
this.#padding.prepare(this, padding); this.#positionsChanged();
|
|
30
|
+
}
|
|
31
|
+
finalizePrefill(): void {
|
|
32
|
+
const next = this.#padding.finalize(this.planes, this);
|
|
33
|
+
if (next) this.#replace(next, this.rowOffsets, this.leftPad);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Snapshot logical validity without changing physical row alignment. */
|
|
37
|
+
captureDonorValidity(): Pick<import("../contracts/mlx/cache").KvDonorRows, "offsets" | "starts" | "ends"> {
|
|
38
|
+
const offsets = this.rowOffsets.map((offset, row) => Math.max(0, this.#padding.validOffset(offset, row)));
|
|
39
|
+
return { offsets, starts: [...this.leftPad], ends: offsets.map((offset, row) => offset + this.leftPad[row]!) };
|
|
40
|
+
}
|
|
41
|
+
get offset(): number { return Math.max(0, ...this.#ends()); }
|
|
42
|
+
get batchSize(): number | null { return this.rowOffsets.length || null; }
|
|
43
|
+
#ends(): number[] { return this.rowOffsets.map((offset, row) => offset + this.leftPad[row]!); }
|
|
44
|
+
get ropeOffsetArr(): MlxArray | undefined {
|
|
45
|
+
if (this.leftPad.every(pad => pad === 0) && this.rowOffsets.every(offset => offset === this.rowOffsets[0])) return undefined;
|
|
46
|
+
return this.#rope ??= ops.fromInt32(this.rowOffsets, [this.rowOffsets.length]);
|
|
47
|
+
}
|
|
48
|
+
#positionsChanged(): void { this.#rope?.dispose(); this.#rope = undefined; }
|
|
49
|
+
bytesPerToken(): number {
|
|
50
|
+
return this.planes.length && this.rowOffsets.length ? this.planes.reduce((bytes, plane) => bytes + plane.nbytes, 0) /
|
|
51
|
+
(this.rowOffsets.length * this.planes[0]!.shape[2]!) : 0;
|
|
52
|
+
}
|
|
53
|
+
trim(count: number): void {
|
|
54
|
+
this.rowOffsets = this.rowOffsets.map(offset => offset - count); this.#positionsChanged();
|
|
55
|
+
}
|
|
56
|
+
specRoundBegin(): void { this.#beforeRound = [...this.rowOffsets]; }
|
|
57
|
+
specRoundCommit(): void { this.#beforeRound = undefined; }
|
|
58
|
+
specRoundRollback(keep: number | readonly number[]): void {
|
|
59
|
+
this.rowOffsets = this.#beforeRound!.map((offset, row) => offset + (typeof keep === "number" ? keep : keep[row]!));
|
|
60
|
+
this.#beforeRound = undefined; this.#positionsChanged();
|
|
61
|
+
}
|
|
62
|
+
makeMask(N: number, window: number | null): Mask {
|
|
63
|
+
const B = this.rowOffsets.length, ends = this.#ends(), S = this.offset + N;
|
|
64
|
+
if (window === null && this.leftPad.every(pad => pad === 0) && ends.every(end => end === ends[0]))
|
|
65
|
+
return { mode: N === 1 ? "" : "causal", arr: null };
|
|
66
|
+
using starts = ops.fromInt32(ends, [B, 1, 1, 1]);
|
|
67
|
+
using relative = ops.arange(0, N, 1, Dtype.int32);
|
|
68
|
+
using queries = ops.reshape(relative, [1, 1, N, 1]);
|
|
69
|
+
using position = ops.add(starts, queries);
|
|
70
|
+
using indices = ops.arange(0, S, 1, Dtype.int32);
|
|
71
|
+
using keys = ops.reshape(indices, [1, 1, 1, S]);
|
|
72
|
+
using pads = ops.fromInt32(this.leftPad, [B, 1, 1, 1]);
|
|
73
|
+
using causal = ops.lessEqual(keys, position);
|
|
74
|
+
using valid = ops.greaterEqual(keys, pads);
|
|
75
|
+
const mask = ops.logicalAnd(causal, valid);
|
|
76
|
+
if (window === null) return { mode: "array", arr: mask };
|
|
77
|
+
using windowSize = ops.fromInt32([window], []);
|
|
78
|
+
using limit = ops.add(keys, windowSize);
|
|
79
|
+
using within = ops.less(position, limit);
|
|
80
|
+
try { return { mode: "array", arr: ops.logicalAnd(mask, within) }; }
|
|
81
|
+
finally { mask.dispose(); }
|
|
82
|
+
}
|
|
83
|
+
#replace(planes: MlxArray[], offsets: number[], pads: number[]): void {
|
|
84
|
+
const previous = this.planes;
|
|
85
|
+
this.planes = planes; this.rowOffsets = offsets; this.leftPad = pads;
|
|
86
|
+
this.#positionsChanged(); disposeResources(previous);
|
|
87
|
+
}
|
|
88
|
+
append(inputs: readonly MlxArray[]): void {
|
|
89
|
+
const B = this.rowOffsets.length, N = inputs[0]!.shape[2]!, ends = this.#ends();
|
|
90
|
+
const needed = Math.max(...ends) + N, capacity = Math.ceil(needed / 256) * 256;
|
|
91
|
+
const common = ends.every(end => end === ends[0]);
|
|
92
|
+
using positions = common ? null : ops.fromInt32(ends.flatMap(end =>
|
|
93
|
+
Array.from({ length: N }, (_, position) => end + position)), [B, 1, N, 1]);
|
|
94
|
+
const next: MlxArray[] = [];
|
|
95
|
+
try {
|
|
96
|
+
for (const [index, input] of inputs.entries()) {
|
|
97
|
+
const old = this.planes[index];
|
|
98
|
+
let base: MlxArray;
|
|
99
|
+
if (!old) base = ops.zeros([B, input.shape[1]!, capacity, input.shape[3]!], input.dtype);
|
|
100
|
+
else if (old.shape[2]! >= needed) base = old;
|
|
101
|
+
else {
|
|
102
|
+
using padding = ops.zeros([B, old.shape[1]!, capacity - old.shape[2]!, old.shape[3]!], old.dtype);
|
|
103
|
+
base = ops.concatAxis([old, padding], 2);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const updated = common
|
|
107
|
+
? ops.sliceUpdate(base, input, [0, 0, ends[0]!, 0], [B, base.shape[1]!, ends[0]! + N, base.shape[3]!])
|
|
108
|
+
: ops.putAlongAxis(base, positions!, input, 2);
|
|
109
|
+
next.push(updated);
|
|
110
|
+
} finally { if (base !== old) base.dispose(); }
|
|
111
|
+
}
|
|
112
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources(next)); }
|
|
113
|
+
this.#replace(next, this.rowOffsets.map(offset => offset + N), this.leftPad);
|
|
114
|
+
}
|
|
115
|
+
mergeRows(rows: readonly KvTensorRowView[]): void {
|
|
116
|
+
const next: MlxArray[] = [], held: MlxArray[] = [];
|
|
117
|
+
let offsets: number[], pads: number[];
|
|
118
|
+
try {
|
|
119
|
+
if (rows.length === 1) {
|
|
120
|
+
const row = rows[0]!;
|
|
121
|
+
for (const plane of row.planes) next.push(ops.copyOf(plane));
|
|
122
|
+
offsets = [...row.rowOffsets]; pads = [...row.leftPad];
|
|
123
|
+
} else {
|
|
124
|
+
const widths = rows.map(row => Math.max(0, ...row.rowOffsets.map((offset, i) => offset + row.leftPad[i]!)));
|
|
125
|
+
const width = Math.max(0, ...widths);
|
|
126
|
+
if (width) {
|
|
127
|
+
const prototype = rows.find((_, index) => widths[index]! > 0)!;
|
|
128
|
+
for (const [field, geometry] of prototype.planes.entries()) {
|
|
129
|
+
const pieces = rows.map((row, index) => {
|
|
130
|
+
const active = widths[index]!;
|
|
131
|
+
if (!active) {
|
|
132
|
+
const empty = ops.zeros([row.rowOffsets.length, geometry.shape[1]!, width, geometry.shape[3]!], geometry.dtype);
|
|
133
|
+
held.push(empty); return empty;
|
|
134
|
+
}
|
|
135
|
+
const plane = row.planes[field]!;
|
|
136
|
+
const view = plane.slice([0, 0, 0, 0], [row.rowOffsets.length, plane.shape[1]!, active, plane.shape[3]!]);
|
|
137
|
+
held.push(view);
|
|
138
|
+
if (active === width) return view;
|
|
139
|
+
using padding = ops.zeros([row.rowOffsets.length, plane.shape[1]!, width - active, plane.shape[3]!], plane.dtype);
|
|
140
|
+
const padded = ops.concatAxis([padding, view], 2); held.push(padded); return padded;
|
|
141
|
+
});
|
|
142
|
+
next.push(ops.concatAxis(pieces, 0));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
offsets = rows.flatMap(row => [...row.rowOffsets]);
|
|
146
|
+
pads = rows.flatMap((row, index) => row.leftPad.map(pad => pad + width - widths[index]!));
|
|
147
|
+
}
|
|
148
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources(next)); }
|
|
149
|
+
finally { disposeResources(held); }
|
|
150
|
+
this.#replace(next, offsets, pads);
|
|
151
|
+
}
|
|
152
|
+
/** Preserve caller-owned physical positions when changing representation.
|
|
153
|
+
* Live tensor bytes are copied; padding never participates in a codec. */
|
|
154
|
+
alignRows(leftPad: readonly number[]): void {
|
|
155
|
+
if (leftPad.every((pad, row) => pad === this.leftPad[row])) return;
|
|
156
|
+
const width = Math.max(0, ...this.rowOffsets.map((offset, row) => offset + leftPad[row]!));
|
|
157
|
+
const rows: MlxArray[][] = [], planes: MlxArray[] = [];
|
|
158
|
+
try {
|
|
159
|
+
for (let row = 0; row < this.rowOffsets.length; row++) rows.push(this.extractRow(row));
|
|
160
|
+
for (let field = 0; field < this.planes.length; field++) {
|
|
161
|
+
const parts: MlxArray[] = [];
|
|
162
|
+
try {
|
|
163
|
+
for (let row = 0; row < rows.length; row++) {
|
|
164
|
+
const geometry = this.planes[field]!, live = rows[row]![field];
|
|
165
|
+
using before = ops.zeros([1, geometry.shape[1]!, leftPad[row]!, geometry.shape[3]!], geometry.dtype);
|
|
166
|
+
using after = ops.zeros([1, geometry.shape[1]!, width - leftPad[row]! - this.rowOffsets[row]!, geometry.shape[3]!], geometry.dtype);
|
|
167
|
+
parts.push(ops.concatAxis(live ? [before, live, after] : [before, after], 2));
|
|
168
|
+
}
|
|
169
|
+
planes.push(ops.concatAxis(parts, 0));
|
|
170
|
+
} finally { disposeResources(parts); }
|
|
171
|
+
}
|
|
172
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources(planes)); }
|
|
173
|
+
finally { disposeResources(rows.flat()); }
|
|
174
|
+
this.#replace(planes, [...this.rowOffsets], [...leftPad]);
|
|
175
|
+
}
|
|
176
|
+
filterRows(keep: readonly number[]): void {
|
|
177
|
+
if (!keep.length) { this.dispose(); return; }
|
|
178
|
+
const next: MlxArray[] = [];
|
|
179
|
+
using indices = this.planes.length ? ops.fromInt32([...keep], [keep.length]) : null;
|
|
180
|
+
const pads = keep.map(row => this.leftPad[row]!);
|
|
181
|
+
const sharedPadding = Math.min(...pads);
|
|
182
|
+
try {
|
|
183
|
+
for (const plane of this.planes) {
|
|
184
|
+
const selected = ops.takeAxis(plane, indices!, 0);
|
|
185
|
+
if (!sharedPadding) next.push(selected);
|
|
186
|
+
else {
|
|
187
|
+
try { next.push(selected.slice([0, 0, sharedPadding, 0], [...selected.shape])); }
|
|
188
|
+
finally { selected.dispose(); }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (error) { return cleanupFailure(error, () => disposeResources(next)); }
|
|
193
|
+
this.#replace(next, keep.map(row => this.rowOffsets[row]!), pads.map(pad => pad - sharedPadding));
|
|
194
|
+
this.#padding.filter(keep);
|
|
195
|
+
}
|
|
196
|
+
extractRow(row: number): MlxArray[] {
|
|
197
|
+
const start = this.leftPad[row]!, count = this.rowOffsets[row]!, result: MlxArray[] = [];
|
|
198
|
+
if (!count) return result;
|
|
199
|
+
try {
|
|
200
|
+
for (const plane of this.planes) {
|
|
201
|
+
using view = plane.slice([row, 0, start, 0], [row + 1, plane.shape[1]!, start + count, plane.shape[3]!]);
|
|
202
|
+
result.push(materializeCopy(view));
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
205
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources(result)); }
|
|
206
|
+
}
|
|
207
|
+
dispose(): void {
|
|
208
|
+
const previous = this.planes;
|
|
209
|
+
this.planes = []; this.rowOffsets = []; this.leftPad = []; this.#beforeRound = undefined; this.#padding.clear();
|
|
210
|
+
this.#positionsChanged(); disposeResources(previous);
|
|
211
|
+
}
|
|
212
|
+
}
|
package/src/state/kv.ts
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
3
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
4
|
+
import type { AttentionRead,BidirectionalAttentionCache,BlockAttentionCache,Cache,CommittedAttentionCache,CompiledDecodeCache,DecodeSlot,DecodeStepInputs,DecodeStepPlan,DecodeTrace,EncoderAttentionCache,KvDonorRows,Mask } from "../contracts/mlx/cache";
|
|
5
|
+
import { bidirMask, createCausalMask } from "../kernels/attention/masks";
|
|
6
|
+
import { QuantizedKVCache } from "./quantized-kv";
|
|
7
|
+
import { AttentionMasks, causalLease, encoderWindowMask, sdpaRead, unmaskedLease, withLease, type DenseRead } from "./attention-read";
|
|
8
|
+
import type { AffineKernelSet } from "./affine-attention";
|
|
9
|
+
import { uncomposedTurboQuantKernels, type TurboQuantKernels } from "./turboquant-attention";
|
|
10
|
+
|
|
11
|
+
export type { AffineKernelSet };
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/** KV cache — port of mlx-lm cache.py KVCache: preallocated in steps of
|
|
15
|
+
* 256 along the sequence axis, updated in place via slice_update.
|
|
16
|
+
*
|
|
17
|
+
* Reads: each named read appends through `updateAndFetch` and attends the
|
|
18
|
+
* fetched prefix with the stock fused SDPA, the call every graph makes after
|
|
19
|
+
* `updateAndFetch` today, a window through `windowRead` (that SDPA unless the
|
|
20
|
+
* composition names a read computing its bits otherwise). Decode passes no
|
|
21
|
+
* mask and a window the fused causal mask; at one query the two compute the
|
|
22
|
+
* same bits, so a one-row window needs no case of its own. This cache has no
|
|
23
|
+
* sliding window (graphs compose `RotatingKVCache` for sliding layers), so
|
|
24
|
+
* only the bidirectional and encoder reads build a mask array. */
|
|
25
|
+
export class KVCache implements CompiledDecodeCache, CommittedAttentionCache, BidirectionalAttentionCache, EncoderAttentionCache, BlockAttentionCache {
|
|
26
|
+
static readonly STEP = 256;
|
|
27
|
+
/** Set only by compiled-decode trace adapters (see Cache). */
|
|
28
|
+
readonly ropeOffsetArr?: MlxArray;
|
|
29
|
+
keys: MlxArray | null = null;
|
|
30
|
+
values: MlxArray | null = null;
|
|
31
|
+
offset = 0;
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
/** The per-forward masks of the model this cache belongs to. Row layouts
|
|
35
|
+
* made from this cache (`state/layout`) share them. */
|
|
36
|
+
readonly masks = new AttentionMasks(),
|
|
37
|
+
/** The TurboQuant read of the graph that built this cache: a TurboQuant
|
|
38
|
+
* conversion of this storage (`TurboQuantKVCache.fromKVCache`, the delayed
|
|
39
|
+
* TurboQuant rows) attends with it, and copies of this cache carry it.
|
|
40
|
+
* Qwen 3.5's `makeCache` passes `denseTurboQuantKernels`, Gemma 4's and
|
|
41
|
+
* MiniCPM5's `deferredTurboQuantKernels`; a graph that reads TurboQuant
|
|
42
|
+
* storage only through the deprecated fetches passes none. */
|
|
43
|
+
readonly turboQuantKernels: TurboQuantKernels = uncomposedTurboQuantKernels,
|
|
44
|
+
/** The read of a causal window (`appendWindow`, `appendCommitted`): the
|
|
45
|
+
* stock fused SDPA, or the read the composition names
|
|
46
|
+
* (`foldedWindowRead`, bit for bit the same). Row layouts made from this
|
|
47
|
+
* storage and its clones read their windows with it. */
|
|
48
|
+
readonly windowRead: DenseRead = sdpaRead,
|
|
49
|
+
) {}
|
|
50
|
+
|
|
51
|
+
signature(): string { return "kv:plain"; }
|
|
52
|
+
|
|
53
|
+
appendDecode(k: MlxArray, v: MlxArray): AttentionRead {
|
|
54
|
+
const [keys, values] = this.updateAndFetch(k, v);
|
|
55
|
+
return sdpaRead(keys, values, unmaskedLease);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
appendWindow(k: MlxArray, v: MlxArray): AttentionRead {
|
|
59
|
+
const [keys, values] = this.updateAndFetch(k, v);
|
|
60
|
+
return this.windowRead(keys, values, causalLease);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A bf16 committed span reads exactly as a window does. */
|
|
64
|
+
appendCommitted(k: MlxArray, v: MlxArray): AttentionRead {
|
|
65
|
+
return this.appendWindow(k, v);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Gemma 4's vision prefill read: the causal mask OR'd with the image block
|
|
69
|
+
* overlay (`bidirMask`), then the fused SDPA with that array mask. One mask
|
|
70
|
+
* per forward: every layer passes the same `bidirectional` tensor. */
|
|
71
|
+
appendBidirectional(k: MlxArray, v: MlxArray, bidirectional: MlxArray): AttentionRead {
|
|
72
|
+
const L = k.shape[2]!;
|
|
73
|
+
const mask = this.masks.lease("bidirectional:none", `${L}`, () => bidirMask(L, null, bidirectional), bidirectional);
|
|
74
|
+
return withLease(mask, held => {
|
|
75
|
+
const [keys, values] = this.updateAndFetch(k, v);
|
|
76
|
+
return sdpaRead(keys, values, held);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** DiffusionGemma's vision encoder read on a full-attention layer: the
|
|
81
|
+
* append `updateAndFetch` makes, then the fused SDPA under the encoder
|
|
82
|
+
* window's mask (`encoderWindowMask`), one per forward for every layer. */
|
|
83
|
+
appendEncoderWindow(k: MlxArray, v: MlxArray, bidirectional: MlxArray): AttentionRead {
|
|
84
|
+
return withLease(encoderWindowMask(this.masks, k.shape[2]!, bidirectional), mask => {
|
|
85
|
+
const [keys, values] = this.updateAndFetch(k, v);
|
|
86
|
+
return sdpaRead(keys, values, mask);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** DiffusionGemma's canvas read on a full-attention layer: the stored
|
|
91
|
+
* positions in order, then the block's keys and values, with no mask. */
|
|
92
|
+
readBlock(k: MlxArray, v: MlxArray): AttentionRead {
|
|
93
|
+
const [storedK, storedV] = this.temporalView();
|
|
94
|
+
let keys: MlxArray, values: MlxArray;
|
|
95
|
+
try {
|
|
96
|
+
keys = ops.concatAxis([storedK, k], 2);
|
|
97
|
+
try { values = ops.concatAxis([storedV, v], 2); }
|
|
98
|
+
catch (error) { keys.dispose(); throw error; }
|
|
99
|
+
} finally { storedK.dispose(); storedV.dispose(); }
|
|
100
|
+
return sdpaRead(keys, values, unmaskedLease);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
bytesPerToken(): number {
|
|
104
|
+
const steps = this.keys?.shape[2] ?? 0;
|
|
105
|
+
return steps > 0 ? (this.keys!.nbytes + this.values!.nbytes) / steps : 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
|
|
109
|
+
const prev = this.offset;
|
|
110
|
+
const L = k.shape[2]!;
|
|
111
|
+
if (!this.keys || prev + L > this.keys.shape[2]!) {
|
|
112
|
+
const [B, H, , D] = k.shape as [number, number, number, number];
|
|
113
|
+
const vD = v.shape[3]!;
|
|
114
|
+
const nSteps = Math.floor((KVCache.STEP + L - 1) / KVCache.STEP);
|
|
115
|
+
const newK = ops.zeros([B, H, nSteps * KVCache.STEP, D], k.dtype);
|
|
116
|
+
const newV = ops.zeros([B, H, nSteps * KVCache.STEP, vD], v.dtype);
|
|
117
|
+
if (this.keys && this.values) {
|
|
118
|
+
let oldK = this.keys;
|
|
119
|
+
let oldV = this.values;
|
|
120
|
+
if (prev % KVCache.STEP !== 0) {
|
|
121
|
+
const trimK = oldK.slice([0, 0, 0, 0], [B, H, prev, D]);
|
|
122
|
+
const trimV = oldV.slice([0, 0, 0, 0], [B, H, prev, vD]);
|
|
123
|
+
oldK.dispose();
|
|
124
|
+
oldV.dispose();
|
|
125
|
+
oldK = trimK;
|
|
126
|
+
oldV = trimV;
|
|
127
|
+
}
|
|
128
|
+
this.keys = ops.concatAxis([oldK, newK], 2);
|
|
129
|
+
this.values = ops.concatAxis([oldV, newV], 2);
|
|
130
|
+
for (const a of [oldK, oldV, newK, newV]) a.dispose();
|
|
131
|
+
} else {
|
|
132
|
+
this.keys = newK;
|
|
133
|
+
this.values = newV;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.offset += L;
|
|
138
|
+
const [B, H, S, D] = this.keys!.shape as [number, number, number, number];
|
|
139
|
+
const vD = this.values!.shape[3]!;
|
|
140
|
+
const k2 = ops.sliceUpdate(this.keys!, k, [0, 0, prev, 0], [B, H, this.offset, D]);
|
|
141
|
+
const v2 = ops.sliceUpdate(this.values!, v, [0, 0, prev, 0], [B, H, this.offset, vD]);
|
|
142
|
+
this.keys!.dispose();
|
|
143
|
+
this.values!.dispose();
|
|
144
|
+
this.keys = k2;
|
|
145
|
+
this.values = v2;
|
|
146
|
+
return [
|
|
147
|
+
this.keys.slice([0, 0, 0, 0], [B, H, this.offset, D]),
|
|
148
|
+
this.values.slice([0, 0, 0, 0], [B, H, this.offset, vD]),
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
makeMask(N: number, windowSize: number | null): Mask {
|
|
153
|
+
if (N === 1) return { mode: "", arr: null };
|
|
154
|
+
// Windowless multi-token chunks get the STRING "causal" at ANY offset —
|
|
155
|
+
// mlx-lm cache.py:114-125 create_attention_mask does exactly this, and
|
|
156
|
+
// mx.fast SDPA aligns the string bottom-right, i.e. the same values as
|
|
157
|
+
// our materialized matrix. Shipping the array instead (the old
|
|
158
|
+
// offset>0 branch) forced a materialize-and-add mask path: a bool
|
|
159
|
+
// [N, offset+N] built PER CHUNK and read by every full-attention layer
|
|
160
|
+
// — the e4b 16k prefill gap vs mlx-lm (2026-07-06b, hd=512 layers
|
|
161
|
+
// where the fallback dispatch can't block-skip an array mask).
|
|
162
|
+
if (windowSize === null) return { mode: "causal", arr: null };
|
|
163
|
+
if (this.offset === 0 && N <= windowSize) return { mode: "causal", arr: null };
|
|
164
|
+
return { mode: "array", arr: createCausalMask(N, this.offset, windowSize) };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Arrays to eval to materialize cache state (prefill chunk boundary). */
|
|
168
|
+
state(): MlxArray[] {
|
|
169
|
+
return this.keys && this.values ? [this.keys, this.values] : [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
isTrimmable(): boolean {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
trim(n: number): void {
|
|
177
|
+
this.offset = Math.max(0, this.offset - n);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Chronological (K, V) view sliced to offset (drafter donor read). */
|
|
181
|
+
captureDonorRows(): KvDonorRows {
|
|
182
|
+
const [keys, values] = this.temporalView();
|
|
183
|
+
const B = keys.shape[0]!, width = keys.shape[2]!;
|
|
184
|
+
return { keys, values, offsets: Array(B).fill(this.offset),
|
|
185
|
+
starts: Array(B).fill(0), ends: Array(B).fill(width) };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
temporalView(): [MlxArray, MlxArray] {
|
|
189
|
+
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
190
|
+
const [B, H, , D] = this.keys.shape as [number, number, number, number];
|
|
191
|
+
const vD = this.values.shape[3]!;
|
|
192
|
+
return [
|
|
193
|
+
this.keys.slice([0, 0, 0, 0], [B, H, this.offset, D]),
|
|
194
|
+
this.values.slice([0, 0, 0, 0], [B, H, this.offset, vD]),
|
|
195
|
+
];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Compiled decode: host-side bookkeeping for a 1-token step — the
|
|
199
|
+
* growth/trim half of updateAndFetch (L=1), without the write. */
|
|
200
|
+
prepareDecodeStep(): DecodeStepPlan {
|
|
201
|
+
const prev = this.offset;
|
|
202
|
+
if (!this.keys || !this.values) throw new Error("compiled decode on an empty cache");
|
|
203
|
+
if (prev + 1 > this.keys.shape[2]!) {
|
|
204
|
+
const [B, H, , D] = this.keys.shape as [number, number, number, number];
|
|
205
|
+
const vD = this.values.shape[3]!;
|
|
206
|
+
let oldK = this.keys;
|
|
207
|
+
let oldV = this.values;
|
|
208
|
+
if (prev % KVCache.STEP !== 0) {
|
|
209
|
+
const trimK = oldK.slice([0, 0, 0, 0], [B, H, prev, D]);
|
|
210
|
+
const trimV = oldV.slice([0, 0, 0, 0], [B, H, prev, vD]);
|
|
211
|
+
oldK.dispose();
|
|
212
|
+
oldV.dispose();
|
|
213
|
+
oldK = trimK;
|
|
214
|
+
oldV = trimV;
|
|
215
|
+
}
|
|
216
|
+
const newK = ops.zeros([B, H, KVCache.STEP, D], oldK.dtype);
|
|
217
|
+
const newV = ops.zeros([B, H, KVCache.STEP, vD], oldV.dtype);
|
|
218
|
+
this.keys = ops.concatAxis([oldK, newK], 2);
|
|
219
|
+
this.values = ops.concatAxis([oldV, newV], 2);
|
|
220
|
+
for (const a of [oldK, oldV, newK, newV]) a.dispose();
|
|
221
|
+
}
|
|
222
|
+
return { fetch: "concat", writePos: prev, activeLen: prev };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
decodePhase(): DecodeStepPlan["fetch"] { return "concat"; }
|
|
226
|
+
|
|
227
|
+
decodeSlot(_plan: DecodeStepPlan): DecodeSlot { return concatDecodeSlot(this.offset); }
|
|
228
|
+
|
|
229
|
+
decodeInputs(plan: DecodeStepPlan, step: DecodeStepInputs): MlxArray[] {
|
|
230
|
+
return [step.activeView(this.keys!, plan.activeLen), step.activeView(this.values!, plan.activeLen)];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
commitDecodeStep(_slot: DecodeSlot, outputs: MlxArray[]): MlxArray[] {
|
|
234
|
+
return this.writeDecodeStep(outputs[0]!, outputs[1]!);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Compiled decode: the write half (same sliceUpdate as updateAndFetch).
|
|
238
|
+
* Takes ownership of kNew/vNew; returns the arrays to async-eval with
|
|
239
|
+
* the step (the updated buffers). */
|
|
240
|
+
writeDecodeStep(kNew: MlxArray, vNew: MlxArray): MlxArray[] {
|
|
241
|
+
const prev = this.offset;
|
|
242
|
+
const [B, H, , D] = this.keys!.shape as [number, number, number, number];
|
|
243
|
+
const vD = this.values!.shape[3]!;
|
|
244
|
+
const k2 = ops.sliceUpdate(this.keys!, kNew, [0, 0, prev, 0], [B, H, prev + 1, D]);
|
|
245
|
+
const v2 = ops.sliceUpdate(this.values!, vNew, [0, 0, prev, 0], [B, H, prev + 1, vD]);
|
|
246
|
+
this.keys!.dispose();
|
|
247
|
+
this.values!.dispose();
|
|
248
|
+
kNew.dispose();
|
|
249
|
+
vNew.dispose();
|
|
250
|
+
this.keys = k2;
|
|
251
|
+
this.values = v2;
|
|
252
|
+
this.offset = prev + 1;
|
|
253
|
+
return [k2, v2];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Adopt persisted state (takes ownership of the arrays). */
|
|
257
|
+
restoreState(keys: MlxArray, values: MlxArray, offset: number): void {
|
|
258
|
+
this.dispose();
|
|
259
|
+
this.keys = keys;
|
|
260
|
+
this.values = values;
|
|
261
|
+
this.offset = offset;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Port of mlx-lm KVCache.to_quantized: quantize the whole buffer
|
|
265
|
+
* (padding included — it's overwritten before being read). The converted
|
|
266
|
+
* cache attends with the kernel set `kernels` builds for its bits, group
|
|
267
|
+
* size and this cache's activation dtype. */
|
|
268
|
+
toQuantized(groupSize: number, bits: number, kernels: AffineKernelSet): QuantizedKVCache {
|
|
269
|
+
const q = new QuantizedKVCache(groupSize, bits, kernels(bits, groupSize, this.keys?.dtype ?? Dtype.bfloat16), this.masks);
|
|
270
|
+
q.offset = this.offset;
|
|
271
|
+
if (this.keys && this.values) {
|
|
272
|
+
q.keys = ops.quantize(this.keys, groupSize, bits);
|
|
273
|
+
q.values = ops.quantize(this.values, groupSize, bits);
|
|
274
|
+
}
|
|
275
|
+
this.dispose();
|
|
276
|
+
return q;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
dispose(): void {
|
|
280
|
+
this.keys?.dispose();
|
|
281
|
+
this.values?.dispose();
|
|
282
|
+
this.keys = this.values = null;
|
|
283
|
+
this.offset = 0;
|
|
284
|
+
this.masks.clear();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The growing plain cache inside a compiled decode trace (also a rotating
|
|
289
|
+
* cache before its window fills): fetch = concat(active prefix, new row); the
|
|
290
|
+
* new row is a closure output for the write after the step. `offset` is set for
|
|
291
|
+
* completeness only: at L=1 every makeMask returns mode "" and the RoPE offset
|
|
292
|
+
* rides `ropeOffsetArr`, so the baked value cannot leak into the graph. */
|
|
293
|
+
class TracedConcatKVCache extends KVCache implements DecodeTrace {
|
|
294
|
+
override readonly ropeOffsetArr: MlxArray;
|
|
295
|
+
outs: MlxArray[] = [];
|
|
296
|
+
constructor(
|
|
297
|
+
offset: number,
|
|
298
|
+
readonly activeK: MlxArray,
|
|
299
|
+
readonly activeV: MlxArray,
|
|
300
|
+
ropeOffsetArr: MlxArray,
|
|
301
|
+
) {
|
|
302
|
+
super();
|
|
303
|
+
this.offset = offset;
|
|
304
|
+
this.ropeOffsetArr = ropeOffsetArr;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
override updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
|
|
308
|
+
// same-shape reshape: an owned alias that survives the caller's
|
|
309
|
+
// dispose of k/v, so the row can be a closure output
|
|
310
|
+
this.outs = [ops.reshape(k, k.shape), ops.reshape(v, v.shape)];
|
|
311
|
+
return [
|
|
312
|
+
ops.concatAxis([this.activeK, k], 2),
|
|
313
|
+
ops.concatAxis([this.activeV, v], 2),
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** The concat-fetch slot of a plain cache at `offset` (KVCache, and RotatingKVCache before the window fills). */
|
|
319
|
+
export function concatDecodeSlot(offset: number): DecodeSlot {
|
|
320
|
+
return {
|
|
321
|
+
key: "p-cat:0:0", fetch: "concat", inputs: 2, outputs: 2,
|
|
322
|
+
trace: (inputs, ropeOffset) => new TracedConcatKVCache(offset, inputs[0]!, inputs[1]!, ropeOffset),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { KV4_DECODE_BLOCK } from "../kernels/attention/kvq4-decode";
|
|
2
|
+
import { kv4DecodeAttention } from "../layers/kv4-decode-attention";
|
|
3
|
+
import { foldedQuantizedSdpa } from "../layers/quantized-attention";
|
|
4
|
+
import { unfusedKernel, type AffineKernels } from "./affine-attention";
|
|
5
|
+
import { AttentionMasks } from "./attention-read";
|
|
6
|
+
import { QuantizedKVCache } from "./quantized-kv";
|
|
7
|
+
|
|
8
|
+
/** The kernels of the Qwen3.8-27B Trellis compositions' full-attention layers
|
|
9
|
+
* for one sequence: the fused one-row KV4 decode kernel (`kv4DecodeAttention`,
|
|
10
|
+
* each packed key and value row read once for every query head of its KV head,
|
|
11
|
+
* `decodeBlock` tokens per threadgroup: the M4 Pro's 512 by default, the M1
|
|
12
|
+
* Max's 128 as its composition names) and the folded-GQA window kernel
|
|
13
|
+
* (`foldedQuantizedSdpa`, one quantized matmul per KV head for its query rows,
|
|
14
|
+
* causal over the last L positions). Neither reads the mask, and both take one
|
|
15
|
+
* row (B = 1), head dim 256 and an even GQA factor. `multiRow` is the
|
|
16
|
+
* composition's affine set, which a row layout reads with whenever it holds
|
|
17
|
+
* more than one row or one behind left padding: the old graph served several
|
|
18
|
+
* sequences on the generic path. A masked window on serial storage takes the
|
|
19
|
+
* unfused port, which this storage never needs (its mask is plain causal). */
|
|
20
|
+
export function kv4Head256Kernels(multiRow: AffineKernels, decodeBlock = KV4_DECODE_BLOCK): AffineKernels {
|
|
21
|
+
return {
|
|
22
|
+
decode: (q, keys, values, scale) => kv4DecodeAttention(q, keys, values, scale, 64, 4, decodeBlock),
|
|
23
|
+
window: (q, keys, values, scale) => foldedQuantizedSdpa(q, keys, values, scale, 64, 4),
|
|
24
|
+
maskedWindow: unfusedKernel(64, 4),
|
|
25
|
+
multiRow,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The 4-bit group-64 head-dim-256 full-attention storage the M4 Pro Qwen3.8
|
|
30
|
+
* composition reads, quantized from its first append: `QuantizedKVCache`
|
|
31
|
+
* storage with `kv4Head256Kernels`. The served lego is the bf16-first
|
|
32
|
+
* `Bf16FirstKv4Head256Cache` (`state/bf16-first-kv.ts`), whose converted
|
|
33
|
+
* storage reads with these kernels; this cache is the quantized-from-token-zero
|
|
34
|
+
* composition. The committed read is `QuantizedKVCache`'s.
|
|
35
|
+
*
|
|
36
|
+
* Each read is bit-identical to the kernel the old graph injected for one
|
|
37
|
+
* sequence; neither kernel is bit-identical to the unfused port (f32 online
|
|
38
|
+
* softmax, regrouped rows: bf16-level). A one-row window takes the folded
|
|
39
|
+
* kernel, where the old graph's one-row plan took the KV4 decode kernel. Row
|
|
40
|
+
* layouts made from this cache keep its kernels, `multiRow` included. */
|
|
41
|
+
export class Kv4Head256Cache extends QuantizedKVCache {
|
|
42
|
+
/** `multiRow`: the composition's affine kernels for 4-bit group 64. */
|
|
43
|
+
constructor(multiRow: AffineKernels, masks = new AttentionMasks()) {
|
|
44
|
+
super(64, 4, kv4Head256Kernels(multiRow), masks);
|
|
45
|
+
}
|
|
46
|
+
}
|