@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,155 @@
|
|
|
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 { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
5
|
+
|
|
6
|
+
// One-row decode attention over a 4-bit, group-64 affine KV cache, head dim
|
|
7
|
+
// 256: the shape of MLX's duplication-free GQA vector kernel
|
|
8
|
+
// (sdpa_vector_2pass_1_gqa: each simdgroup reads a K/V row once into registers
|
|
9
|
+
// and serves several query heads) with dequantize-on-load. Each lane owns 8 of
|
|
10
|
+
// the 256 dims: one 4-byte load of packed K (and V) per row plus its group's
|
|
11
|
+
// scale and bias. A threadgroup = one KV head × one block of BLOCK tokens
|
|
12
|
+
// (the caller's `block`), split across SUB = 4 token sub-chunks × 2 head groups
|
|
13
|
+
// (REP/2 heads each; threadgroup memory bounds SUB at 4 for D 256); the
|
|
14
|
+
// sub-chunks combine in threadgroup memory and a second kernel merges blocks.
|
|
15
|
+
// The cache length is an argument, not a specialization: one pipeline serves
|
|
16
|
+
// every decode step. K/V bytes per token vs bf16: ~0.28× (with scales). Online
|
|
17
|
+
// softmax in f32: agrees with the unfused path to bf16 precision, not bit for
|
|
18
|
+
// bit; the block partitions the softmax, so it is part of the numbers.
|
|
19
|
+
const SUB = 4;
|
|
20
|
+
/** The tokens per threadgroup of the M4 Pro composition, which measured it. */
|
|
21
|
+
export const KV4_DECODE_BLOCK = 512;
|
|
22
|
+
const PARTIAL_SOURCE = String.raw`
|
|
23
|
+
const uint lane = thread_index_in_simdgroup;
|
|
24
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
25
|
+
const uint hk = threadgroup_position_in_grid.y, block = threadgroup_position_in_grid.x;
|
|
26
|
+
const uint hg = sg % 2u, sc = sg / 2u; // head group, token sub-chunk
|
|
27
|
+
constexpr uint HPG = REP / 2; // heads per group
|
|
28
|
+
const uint N = kp_shape[2]; // cached positions: an argument, not a specialization
|
|
29
|
+
const uint t0 = block * (uint)BLOCK, tEnd = metal::min(N, t0 + (uint)BLOCK);
|
|
30
|
+
const uint sub = ((uint)BLOCK + (uint)SUB - 1u) / (uint)SUB;
|
|
31
|
+
const uint s0 = t0 + sc * sub, s1 = metal::min(tEnd, s0 + sub);
|
|
32
|
+
// Queries (pre-scaled), 8 dims per lane.
|
|
33
|
+
float qv[HPG][8];
|
|
34
|
+
#pragma clang loop unroll(full)
|
|
35
|
+
for (uint j = 0; j < HPG; ++j)
|
|
36
|
+
#pragma clang loop unroll(full)
|
|
37
|
+
for (uint i = 0; i < 8u; ++i) qv[j][i] = float(q[(hk * (uint)REP + hg * HPG + j) * (uint)D + lane * 8u + i]);
|
|
38
|
+
float m[HPG], l[HPG], o[HPG][8];
|
|
39
|
+
#pragma clang loop unroll(full)
|
|
40
|
+
for (uint j = 0; j < HPG; ++j) { m[j] = -INFINITY; l[j] = 0.0f; for (uint i = 0; i < 8u; ++i) o[j][i] = 0.0f; }
|
|
41
|
+
const uint g = lane / 8u; // this lane's 64-dim group
|
|
42
|
+
const ulong kph = (ulong)kp_strides[1], kpr = (ulong)kp_strides[2];
|
|
43
|
+
const ulong ksh = (ulong)ks_strides[1], ksr = (ulong)ks_strides[2];
|
|
44
|
+
const ulong vph = (ulong)vp_strides[1], vpr = (ulong)vp_strides[2];
|
|
45
|
+
const ulong vsh = (ulong)vs_strides[1], vsr = (ulong)vs_strides[2];
|
|
46
|
+
for (uint t = s0; t < s1; ++t) {
|
|
47
|
+
const uint kword = kp[(ulong)hk * kph + (ulong)t * kpr + lane];
|
|
48
|
+
const uint vword = vp[(ulong)hk * vph + (ulong)t * vpr + lane];
|
|
49
|
+
const float kscale = float(ks[(ulong)hk * ksh + (ulong)t * ksr + g]), kbias = float(kb[(ulong)hk * ksh + (ulong)t * ksr + g]);
|
|
50
|
+
const float vscale = float(vs[(ulong)hk * vsh + (ulong)t * vsr + g]), vbias = float(vb[(ulong)hk * vsh + (ulong)t * vsr + g]);
|
|
51
|
+
float kr[8], vr[8];
|
|
52
|
+
#pragma clang loop unroll(full)
|
|
53
|
+
for (uint i = 0; i < 8u; ++i) {
|
|
54
|
+
kr[i] = metal::fma(kscale, float((kword >> (4u * i)) & 15u), kbias);
|
|
55
|
+
vr[i] = metal::fma(vscale, float((vword >> (4u * i)) & 15u), vbias);
|
|
56
|
+
}
|
|
57
|
+
#pragma clang loop unroll(full)
|
|
58
|
+
for (uint j = 0; j < HPG; ++j) {
|
|
59
|
+
float score = 0.0f;
|
|
60
|
+
#pragma clang loop unroll(full)
|
|
61
|
+
for (uint i = 0; i < 8u; ++i) score = metal::fma(qv[j][i], kr[i], score);
|
|
62
|
+
score = metal::simd_sum(score);
|
|
63
|
+
const float mn = metal::max(m[j], score);
|
|
64
|
+
const float factor = metal::exp(m[j] - mn), e = metal::exp(score - mn);
|
|
65
|
+
m[j] = mn;
|
|
66
|
+
l[j] = l[j] * factor + e;
|
|
67
|
+
#pragma clang loop unroll(full)
|
|
68
|
+
for (uint i = 0; i < 8u; ++i) o[j][i] = metal::fma(o[j][i], factor, e * vr[i]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Combine the SUB token sub-chunks of each head in threadgroup memory.
|
|
72
|
+
threadgroup float osh[SUB * REP * D];
|
|
73
|
+
threadgroup float msh[SUB * REP], lsh[SUB * REP];
|
|
74
|
+
#pragma clang loop unroll(full)
|
|
75
|
+
for (uint j = 0; j < HPG; ++j) {
|
|
76
|
+
const uint h = hg * HPG + j, slot = sc * (uint)REP + h;
|
|
77
|
+
for (uint i = 0; i < 8u; ++i) osh[slot * (uint)D + lane * 8u + i] = o[j][i];
|
|
78
|
+
if (lane == 0u) { msh[slot] = m[j]; lsh[slot] = l[j]; }
|
|
79
|
+
}
|
|
80
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
81
|
+
for (uint e = thread_position_in_threadgroup.x; e < (uint)(REP * D); e += threads_per_threadgroup.x) {
|
|
82
|
+
const uint h = e / (uint)D, d = e % (uint)D;
|
|
83
|
+
float gmax = -INFINITY;
|
|
84
|
+
for (uint s = 0; s < (uint)SUB; ++s) gmax = metal::max(gmax, msh[s * (uint)REP + h]);
|
|
85
|
+
float acc = 0.0f, den = 0.0f;
|
|
86
|
+
for (uint s = 0; s < (uint)SUB; ++s) {
|
|
87
|
+
const float ms = msh[s * (uint)REP + h];
|
|
88
|
+
if (ms == -INFINITY) continue;
|
|
89
|
+
const float w = metal::exp(ms - gmax);
|
|
90
|
+
acc += w * osh[(s * (uint)REP + h) * (uint)D + d];
|
|
91
|
+
den += w * lsh[s * (uint)REP + h];
|
|
92
|
+
}
|
|
93
|
+
const ulong base = ((ulong)block * (uint)KVH + hk) * (uint)REP + h;
|
|
94
|
+
opart[base * (uint)D + d] = acc;
|
|
95
|
+
if (d == 0u) { mpart[base] = gmax; lpart[base] = den; }
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
const MERGE_SOURCE = String.raw`
|
|
100
|
+
const uint d = thread_position_in_grid.x, qv = thread_position_in_grid.y;
|
|
101
|
+
if (d >= (uint)D || qv >= (uint)(KVH * REP)) return;
|
|
102
|
+
const uint BLOCKS = mpart_shape[0];
|
|
103
|
+
float mmax = -INFINITY;
|
|
104
|
+
for (uint s = 0; s < BLOCKS; ++s) mmax = metal::max(mmax, mpart[(ulong)s * (uint)(KVH * REP) + qv]);
|
|
105
|
+
float num = 0.0f, den = 0.0f;
|
|
106
|
+
for (uint s = 0; s < BLOCKS; ++s) {
|
|
107
|
+
const ulong b = (ulong)s * (uint)(KVH * REP) + qv;
|
|
108
|
+
const float ms = mpart[b];
|
|
109
|
+
if (ms == -INFINITY) continue;
|
|
110
|
+
const float w = metal::exp(ms - mmax);
|
|
111
|
+
num += w * opart[b * (uint)D + d];
|
|
112
|
+
den += w * lpart[b];
|
|
113
|
+
}
|
|
114
|
+
out[(ulong)qv * (uint)D + d] = T(num / den);
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
let partial: MetalKernel | undefined, merge: MetalKernel | undefined;
|
|
118
|
+
|
|
119
|
+
/** One query row, head dim 256, a 4-bit group-64 cache, an even GQA factor. */
|
|
120
|
+
export function kvq4DecodeEligible(q: MlxArray, keys: ops.QuantizedTensor, groupSize: number, bits: number): boolean {
|
|
121
|
+
const [B, H, L, D] = q.shape as [number, number, number, number];
|
|
122
|
+
const KVH = keys.packed.shape[1]!;
|
|
123
|
+
return B === 1 && L === 1 && D === 256 && bits === 4 && groupSize === 64 && H % KVH === 0 && (H / KVH) % 2 === 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** q [1, H, 1, 256]; keys/values: 4-bit group-64 affine [1, KVH, N, ·] (any strides) → [1, H, 1, 256].
|
|
127
|
+
* `block`: tokens per threadgroup, a device's measured choice. */
|
|
128
|
+
export function kvq4DecodeSdpa(q: MlxArray, keys: ops.QuantizedTensor, values: ops.QuantizedTensor, scale: number,
|
|
129
|
+
block = KV4_DECODE_BLOCK): MlxArray {
|
|
130
|
+
const [, H, , D] = q.shape as [number, number, number, number];
|
|
131
|
+
const KVH = keys.packed.shape[1]!, N = keys.packed.shape[2]!, REP = H / KVH;
|
|
132
|
+
const blocks = Math.ceil(N / block);
|
|
133
|
+
partial ??= new MetalKernel({ name: "mlx_bun_kvq4_decode_partial", inputNames: ["q", "kp", "ks", "kb", "vp", "vs", "vb"],
|
|
134
|
+
outputNames: ["opart", "mpart", "lpart"], source: PARTIAL_SOURCE, ensureRowContiguous: false });
|
|
135
|
+
merge ??= new MetalKernel({ name: "mlx_bun_kvq4_decode_merge", inputNames: ["opart", "mpart", "lpart"],
|
|
136
|
+
outputNames: ["out"], source: MERGE_SOURCE, ensureRowContiguous: true });
|
|
137
|
+
using qs = ops.mulScalar(q, scale);
|
|
138
|
+
using qc = ops.contiguous(qs);
|
|
139
|
+
const [opart, mpart, lpart] = partial.apply([qc, keys.packed, keys.scales, keys.biases, values.packed, values.scales, values.biases], {
|
|
140
|
+
outputs: [{ shape: [blocks, KVH, REP, D], dtype: Dtype.float32 }, { shape: [blocks, KVH, REP], dtype: Dtype.float32 },
|
|
141
|
+
{ shape: [blocks, KVH, REP], dtype: Dtype.float32 }],
|
|
142
|
+
grid: [32 * 2 * SUB * blocks, KVH, 1], threadGroup: [32 * 2 * SUB, 1, 1],
|
|
143
|
+
templateDtypes: { T: q.dtype },
|
|
144
|
+
templateInts: { D, KVH, REP, BLOCK: block, SUB },
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
const [out] = merge.apply([opart!, mpart!, lpart!], {
|
|
148
|
+
outputs: [{ shape: [1, H, 1, D], dtype: q.dtype }],
|
|
149
|
+
grid: [D, KVH * REP, 1], threadGroup: [Math.min(256, D), 1, 1],
|
|
150
|
+
templateDtypes: { T: q.dtype },
|
|
151
|
+
templateInts: { D, KVH, REP },
|
|
152
|
+
});
|
|
153
|
+
return out!;
|
|
154
|
+
} finally { opart!.dispose(); mpart!.dispose(); lpart!.dispose(); }
|
|
155
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 { Mask } from "../../contracts/mlx/cache";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/** Port of base.py create_causal_mask (bool, [N, offset+N]). */
|
|
8
|
+
export function createCausalMask(N: number, offset: number, windowSize: number | null): MlxArray {
|
|
9
|
+
const rinds = ops.arange(0, offset + N, 1, Dtype.int32);
|
|
10
|
+
const lindsFlat = offset ? ops.arange(offset, offset + N, 1, Dtype.int32) : rinds;
|
|
11
|
+
const linds = ops.reshape(lindsFlat, [N, 1]);
|
|
12
|
+
const rindsB = ops.reshape(rinds, [1, offset + N]);
|
|
13
|
+
let mask = ops.greaterEqual(linds, rindsB);
|
|
14
|
+
if (windowSize !== null) {
|
|
15
|
+
const w = ops.fromInt32([windowSize], []);
|
|
16
|
+
const rPlusW = ops.add(rindsB, w);
|
|
17
|
+
const inWindow = ops.less(linds, rPlusW);
|
|
18
|
+
const combined = ops.logicalAnd(mask, inWindow);
|
|
19
|
+
for (const a of [w, rPlusW, inWindow, mask]) a.dispose();
|
|
20
|
+
mask = combined;
|
|
21
|
+
}
|
|
22
|
+
if (lindsFlat !== rinds) lindsFlat.dispose();
|
|
23
|
+
rinds.dispose();
|
|
24
|
+
linds.dispose();
|
|
25
|
+
rindsB.dispose();
|
|
26
|
+
return mask;
|
|
27
|
+
}
|
|
28
|
+
/** Causal(+window) mask OR'd with image×image bidirectional attention. */
|
|
29
|
+
export function bidirMask(L: number, windowSize: number | null, bidir: MlxArray): Mask {
|
|
30
|
+
const causal = createCausalMask(L, 0, windowSize);
|
|
31
|
+
const col = ops.reshape(bidir, [L, 1]);
|
|
32
|
+
const row = ops.reshape(bidir, [1, L]);
|
|
33
|
+
const outer = ops.logicalAnd(col, row);
|
|
34
|
+
col.dispose();
|
|
35
|
+
row.dispose();
|
|
36
|
+
const allow = ops.logicalOr(causal, outer);
|
|
37
|
+
causal.dispose();
|
|
38
|
+
outer.dispose();
|
|
39
|
+
return { mode: "array", arr: allow };
|
|
40
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
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 { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
5
|
+
|
|
6
|
+
// Multi-query flash-decoding: causal attention for a few query rows (speculative verify, L <= 8) over a long KV
|
|
7
|
+
// cache, reading every K and V row ONCE for all query vectors of a KV head.
|
|
8
|
+
// MLX's vector kernel accepts at most 32 query vectors per KV head (5 rows at
|
|
9
|
+
// GQA 6) and re-reads K/V per query row; above that it falls back to unfused
|
|
10
|
+
// attention. Here one threadgroup owns one KV head and one key split; its
|
|
11
|
+
// rep·L query vectors (padded to 8-row tiles, one simdgroup per tile) multiply
|
|
12
|
+
// each 32-key block staged once in threadgroup memory on the simdgroup matrix
|
|
13
|
+
// unit, with an online softmax per row. A second kernel merges the splits.
|
|
14
|
+
// Row (r, i) — head kv·rep + r, query i — sees keys j <= N - L + i.
|
|
15
|
+
// Not bit-identical to MLX's kernels (summation order); gate by KL.
|
|
16
|
+
const PARTIAL_SOURCE = String.raw`
|
|
17
|
+
const uint lane = thread_index_in_simdgroup;
|
|
18
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
19
|
+
const uint hk = threadgroup_position_in_grid.y;
|
|
20
|
+
const uint split = threadgroup_position_in_grid.x;
|
|
21
|
+
const uint fm = ((lane / 4u) & 4u) + ((lane / 2u) % 4u);
|
|
22
|
+
const uint fn = ((lane / 4u) & 2u) * 2u + (lane % 2u) * 2u;
|
|
23
|
+
threadgroup half kv[BK * D];
|
|
24
|
+
// Query tile: rows sg*8 .. sg*8+7 of the head group's rep·L vectors.
|
|
25
|
+
const bool tile = sg * 8u < (uint)NQ; // simdgroups past the tiles only stage K/V
|
|
26
|
+
const uint row = sg * 8u + fm; // this lane's A-fragment row
|
|
27
|
+
const bool rowOk = row < (uint)NQ;
|
|
28
|
+
const uint rr = rowOk ? row / (uint)L : 0u, qi = rowOk ? row % (uint)L : 0u;
|
|
29
|
+
const device T* qp = q + ((ulong)(hk * (uint)REP + rr) * (uint)L + qi) * (uint)D;
|
|
30
|
+
metal::simdgroup_half8x8 qf[D / 8];
|
|
31
|
+
#pragma clang loop unroll(full)
|
|
32
|
+
for (uint f = 0; f < (uint)D / 8u; ++f) {
|
|
33
|
+
qf[f].thread_elements()[0] = rowOk ? half(qp[8u * f + fn]) : half(0.0h);
|
|
34
|
+
qf[f].thread_elements()[1] = rowOk ? half(qp[8u * f + fn + 1u]) : half(0.0h);
|
|
35
|
+
}
|
|
36
|
+
metal::simdgroup_float8x8 o[D / 8];
|
|
37
|
+
#pragma clang loop unroll(full)
|
|
38
|
+
for (uint f = 0; f < (uint)D / 8u; ++f) o[f] = metal::make_filled_simdgroup_matrix<float, 8, 8>(0.0f);
|
|
39
|
+
float m = -INFINITY, l = 0.0f;
|
|
40
|
+
// Key range of this split; the row's causal limit.
|
|
41
|
+
const uint per = ((uint)N + (uint)SPLITS - 1u) / (uint)SPLITS;
|
|
42
|
+
const uint k0 = split * per, k1 = metal::min((uint)N, k0 + per);
|
|
43
|
+
const uint limit = (uint)N - (uint)L + qi; // keys <= limit are visible
|
|
44
|
+
const ulong hs = (ulong)k_strides[1], rs = (ulong)k_strides[2];
|
|
45
|
+
const ulong vhs = (ulong)v_strides[1], vrs = (ulong)v_strides[2];
|
|
46
|
+
for (uint kb = k0; kb < k1; kb += (uint)BK) {
|
|
47
|
+
// Stage K block [BK, D] as half.
|
|
48
|
+
for (uint i = thread_position_in_threadgroup.x; i < (uint)(BK * D / 8); i += threads_per_threadgroup.x) {
|
|
49
|
+
const uint j = (8u * i) / (uint)D, d = (8u * i) % (uint)D, key = kb + j;
|
|
50
|
+
threadgroup half* dst = kv + j * (uint)D + d;
|
|
51
|
+
if (key < k1) {
|
|
52
|
+
const device metal::vec<T, 4>* src = (const device metal::vec<T, 4>*)(k + (ulong)hk * hs + (ulong)key * rs + d);
|
|
53
|
+
const float4 a = float4(src[0]), b = float4(src[1]);
|
|
54
|
+
*(threadgroup half4*)dst = half4(a); *(threadgroup half4*)(dst + 4) = half4(b);
|
|
55
|
+
} else { *(threadgroup half4*)dst = half4(0.0h); *(threadgroup half4*)(dst + 4) = half4(0.0h); }
|
|
56
|
+
}
|
|
57
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
58
|
+
// S = Q · Kᵀ for BK keys: BK/8 fragments of [8 rows × 8 keys].
|
|
59
|
+
metal::simdgroup_float8x8 s[BK / 8];
|
|
60
|
+
metal::simdgroup_half8x8 p[BK / 8];
|
|
61
|
+
if (tile) {
|
|
62
|
+
#pragma clang loop unroll(full)
|
|
63
|
+
for (uint c = 0; c < (uint)BK / 8u; ++c) {
|
|
64
|
+
s[c] = metal::make_filled_simdgroup_matrix<float, 8, 8>(0.0f);
|
|
65
|
+
#pragma clang loop unroll(full)
|
|
66
|
+
for (uint f = 0; f < (uint)D / 8u; ++f) {
|
|
67
|
+
metal::simdgroup_half8x8 kt;
|
|
68
|
+
metal::simdgroup_load(kt, kv + (8u * c) * (uint)D + 8u * f, (ulong)D, ulong2(0, 0), true);
|
|
69
|
+
metal::simdgroup_multiply_accumulate(s[c], qf[f], kt, s[c]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Causal / range mask and online softmax for this lane's row fm.
|
|
73
|
+
float bmax = -INFINITY;
|
|
74
|
+
#pragma clang loop unroll(full)
|
|
75
|
+
for (uint c = 0; c < (uint)BK / 8u; ++c)
|
|
76
|
+
#pragma clang loop unroll(full)
|
|
77
|
+
for (uint e = 0; e < 2u; ++e) {
|
|
78
|
+
const uint key = kb + 8u * c + fn + e;
|
|
79
|
+
float v = s[c].thread_elements()[e];
|
|
80
|
+
if (!rowOk || key >= k1 || key > limit) v = -INFINITY;
|
|
81
|
+
s[c].thread_elements()[e] = v;
|
|
82
|
+
bmax = metal::max(bmax, v);
|
|
83
|
+
}
|
|
84
|
+
bmax = metal::max(bmax, metal::simd_shuffle_xor(bmax, 1u));
|
|
85
|
+
bmax = metal::max(bmax, metal::simd_shuffle_xor(bmax, 8u));
|
|
86
|
+
const float mnew = metal::max(m, bmax);
|
|
87
|
+
const float corr = mnew == -INFINITY ? 1.0f : metal::exp(m - mnew);
|
|
88
|
+
float bsum = 0.0f;
|
|
89
|
+
#pragma clang loop unroll(full)
|
|
90
|
+
for (uint c = 0; c < (uint)BK / 8u; ++c)
|
|
91
|
+
#pragma clang loop unroll(full)
|
|
92
|
+
for (uint e = 0; e < 2u; ++e) {
|
|
93
|
+
const float v = s[c].thread_elements()[e];
|
|
94
|
+
const float w = v == -INFINITY ? 0.0f : metal::exp(v - mnew);
|
|
95
|
+
bsum += w;
|
|
96
|
+
p[c].thread_elements()[e] = half(w);
|
|
97
|
+
}
|
|
98
|
+
bsum += metal::simd_shuffle_xor(bsum, 1u);
|
|
99
|
+
bsum += metal::simd_shuffle_xor(bsum, 8u);
|
|
100
|
+
l = l * corr + bsum;
|
|
101
|
+
m = mnew;
|
|
102
|
+
#pragma clang loop unroll(full)
|
|
103
|
+
for (uint f = 0; f < (uint)D / 8u; ++f) {
|
|
104
|
+
o[f].thread_elements()[0] *= corr;
|
|
105
|
+
o[f].thread_elements()[1] *= corr;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
109
|
+
// Stage V block [BK, D] as half; O += P · V.
|
|
110
|
+
for (uint i = thread_position_in_threadgroup.x; i < (uint)(BK * D / 8); i += threads_per_threadgroup.x) {
|
|
111
|
+
const uint j = (8u * i) / (uint)D, d = (8u * i) % (uint)D, key = kb + j;
|
|
112
|
+
threadgroup half* dst = kv + j * (uint)D + d;
|
|
113
|
+
if (key < k1) {
|
|
114
|
+
const device metal::vec<T, 4>* src = (const device metal::vec<T, 4>*)(v + (ulong)hk * vhs + (ulong)key * vrs + d);
|
|
115
|
+
const float4 a = float4(src[0]), b = float4(src[1]);
|
|
116
|
+
*(threadgroup half4*)dst = half4(a); *(threadgroup half4*)(dst + 4) = half4(b);
|
|
117
|
+
} else { *(threadgroup half4*)dst = half4(0.0h); *(threadgroup half4*)(dst + 4) = half4(0.0h); }
|
|
118
|
+
}
|
|
119
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
120
|
+
if (tile)
|
|
121
|
+
#pragma clang loop unroll(full)
|
|
122
|
+
for (uint f = 0; f < (uint)D / 8u; ++f)
|
|
123
|
+
#pragma clang loop unroll(full)
|
|
124
|
+
for (uint c = 0; c < (uint)BK / 8u; ++c) {
|
|
125
|
+
metal::simdgroup_half8x8 vb;
|
|
126
|
+
metal::simdgroup_load(vb, kv + (8u * c) * (uint)D + 8u * f, (ulong)D);
|
|
127
|
+
metal::simdgroup_multiply_accumulate(o[f], p[c], vb, o[f]);
|
|
128
|
+
}
|
|
129
|
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
|
130
|
+
}
|
|
131
|
+
// Partial results: unnormalized O, running max and sum per row.
|
|
132
|
+
if (!tile || !rowOk) return;
|
|
133
|
+
const ulong base = ((ulong)split * (uint)KVH + hk) * (uint)NQ + row;
|
|
134
|
+
#pragma clang loop unroll(full)
|
|
135
|
+
for (uint f = 0; f < (uint)D / 8u; ++f) {
|
|
136
|
+
opart[base * (uint)D + 8u * f + fn] = o[f].thread_elements()[0];
|
|
137
|
+
opart[base * (uint)D + 8u * f + fn + 1u] = o[f].thread_elements()[1];
|
|
138
|
+
}
|
|
139
|
+
if (fn == 0u) { mpart[base] = m; lpart[base] = l; }
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
const MERGE_SOURCE = String.raw`
|
|
143
|
+
const uint d = thread_position_in_grid.x; // 0..D-1
|
|
144
|
+
const uint qv = thread_position_in_grid.y; // 0..KVH·NQ-1
|
|
145
|
+
if (d >= (uint)D || qv >= (uint)(KVH * NQ)) return;
|
|
146
|
+
const uint hk = qv / (uint)NQ, row = qv % (uint)NQ;
|
|
147
|
+
float mmax = -INFINITY;
|
|
148
|
+
for (uint s = 0; s < (uint)SPLITS; ++s) mmax = metal::max(mmax, mpart[(ulong)s * (uint)(KVH * NQ) + qv]);
|
|
149
|
+
float num = 0.0f, den = 0.0f;
|
|
150
|
+
for (uint s = 0; s < (uint)SPLITS; ++s) {
|
|
151
|
+
const ulong b = (ulong)s * (uint)(KVH * NQ) + qv;
|
|
152
|
+
const float ms = mpart[b];
|
|
153
|
+
if (ms == -INFINITY) continue;
|
|
154
|
+
const float w = metal::exp(ms - mmax);
|
|
155
|
+
num += w * opart[b * (uint)D + d];
|
|
156
|
+
den += w * lpart[b];
|
|
157
|
+
}
|
|
158
|
+
const uint rr = row / (uint)L, qi = row % (uint)L;
|
|
159
|
+
out[((ulong)(hk * (uint)REP + rr) * (uint)L + qi) * (uint)D + d] = T(num / den);
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
let partial: MetalKernel | undefined, merge: MetalKernel | undefined;
|
|
163
|
+
const BK = 32;
|
|
164
|
+
|
|
165
|
+
/** q [1, H, L, D], keys/values [1, KVH, N, D] (any head/row strides) → [1, H, L, D]. */
|
|
166
|
+
export function multiQueryCausalSdpa(q: MlxArray, keys: MlxArray, values: MlxArray, scale: number): MlxArray {
|
|
167
|
+
const [, H, L, D] = q.shape as [number, number, number, number];
|
|
168
|
+
const KVH = keys.shape[1]!, N = keys.shape[2]!, REP = H / KVH, NQ = REP * L;
|
|
169
|
+
if (NQ > 64) throw new Error(`multiQueryCausalSdpa: ${NQ} query vectors per KV head exceed 64`);
|
|
170
|
+
const P = Math.max(1, Math.min(128, Math.ceil(N / 256))); // key splits
|
|
171
|
+
partial ??= new MetalKernel({ name: "mlx_bun_attn_multi_query_partial", inputNames: ["q", "k", "v"],
|
|
172
|
+
outputNames: ["opart", "mpart", "lpart"], source: PARTIAL_SOURCE, ensureRowContiguous: false });
|
|
173
|
+
merge ??= new MetalKernel({ name: "mlx_bun_attn_multi_query_merge", inputNames: ["opart", "mpart", "lpart"],
|
|
174
|
+
outputNames: ["out"], source: MERGE_SOURCE, ensureRowContiguous: true });
|
|
175
|
+
using qs = ops.mulScalar(q, scale); // softmax scale folded into q
|
|
176
|
+
using qc = ops.contiguous(qs);
|
|
177
|
+
const [opart, mpart, lpart] = partial.apply([qc, keys, values], {
|
|
178
|
+
outputs: [{ shape: [P, KVH, NQ, D], dtype: Dtype.float32 }, { shape: [P, KVH, NQ], dtype: Dtype.float32 },
|
|
179
|
+
{ shape: [P, KVH, NQ], dtype: Dtype.float32 }],
|
|
180
|
+
grid: [256 * P, KVH, 1], threadGroup: [256, 1, 1],
|
|
181
|
+
templateDtypes: { T: q.dtype },
|
|
182
|
+
templateInts: { L, D, N, KVH, REP, NQ, BK, SPLITS: P },
|
|
183
|
+
});
|
|
184
|
+
try {
|
|
185
|
+
const [out] = merge.apply([opart!, mpart!, lpart!], {
|
|
186
|
+
outputs: [{ shape: [1, H, L, D], dtype: q.dtype }],
|
|
187
|
+
grid: [D, KVH * NQ, 1], threadGroup: [Math.min(256, D), 1, 1],
|
|
188
|
+
templateDtypes: { T: q.dtype },
|
|
189
|
+
templateInts: { L, D, KVH, REP, NQ, SPLITS: P },
|
|
190
|
+
});
|
|
191
|
+
return out!;
|
|
192
|
+
} finally { opart!.dispose(); mpart!.dispose(); lpart!.dispose(); }
|
|
193
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
3
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
4
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
5
|
+
import type { KvAttentionView, Mask } from "../../contracts/mlx/cache";
|
|
6
|
+
import { disposeResources } from "../../runtime/resources";
|
|
7
|
+
/** Borrowed numerical storage. Allocation, appends, and eviction belong to state. */
|
|
8
|
+
export interface PagedAttentionStorage {
|
|
9
|
+
readonly keys: MlxArray;
|
|
10
|
+
readonly values: MlxArray;
|
|
11
|
+
readonly blockSize: number;
|
|
12
|
+
readonly headDim: number;
|
|
13
|
+
readonly vHeadDim: number;
|
|
14
|
+
readonly dtype: Dtype;
|
|
15
|
+
readonly quantization?: { bits: number; groupSize: number } | null;
|
|
16
|
+
readonly keyScales?: MlxArray | null;
|
|
17
|
+
readonly keyBiases?: MlxArray | null;
|
|
18
|
+
readonly valueScales?: MlxArray | null;
|
|
19
|
+
readonly valueBiases?: MlxArray | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Each SIMD group reduces one query/head over a bounded sequence partition.
|
|
23
|
+
// Only partial output vectors and normalizers materialize, never gathered KV.
|
|
24
|
+
const partialSpec = { name: "paged_attention_partial", ensureRowContiguous: true,
|
|
25
|
+
inputNames: ["q", "keys", "values", "table", "scale"], outputNames: ["out", "normal"], source: `
|
|
26
|
+
uint lane = thread_position_in_threadgroup.x;
|
|
27
|
+
uint hq = threadgroup_position_in_grid.y % HQ;
|
|
28
|
+
uint query = threadgroup_position_in_grid.y / HQ;
|
|
29
|
+
uint part = threadgroup_position_in_grid.z;
|
|
30
|
+
uint hk = hq / (HQ / HK);
|
|
31
|
+
float accum[(DV + 31) / 32];
|
|
32
|
+
for (uint j = 0; j < (DV + 31) / 32; ++j) accum[j] = 0;
|
|
33
|
+
float maximum = -INFINITY, total = 0;
|
|
34
|
+
uint length = uint(scale[1]);
|
|
35
|
+
uint count = (length + PART - 1) / PART;
|
|
36
|
+
uint end = min(uint(length - N + query + 1), (part + 1) * PART);
|
|
37
|
+
for (uint token = part * PART; token < end; ++token) {
|
|
38
|
+
uint physical = table[token / BS];
|
|
39
|
+
uint keyBase = ((physical * HK + hk) * BS + token % BS) * DK;
|
|
40
|
+
float score = 0;
|
|
41
|
+
for (uint d = lane; d < DK; d += 32)
|
|
42
|
+
score += float(q[(hq * N + query) * DK + d]) * float(keys[keyBase + d]);
|
|
43
|
+
score = simd_sum(score) * scale[0];
|
|
44
|
+
float next = max(maximum, score);
|
|
45
|
+
float previous = exp(maximum - next), weight = exp(score - next);
|
|
46
|
+
uint valueBase = ((physical * HK + hk) * BS + token % BS) * DV;
|
|
47
|
+
for (uint j = 0; j < (DV + 31) / 32; ++j) {
|
|
48
|
+
uint d = j * 32 + lane;
|
|
49
|
+
if (d < DV) accum[j] = accum[j] * previous + weight * float(values[valueBase + d]);
|
|
50
|
+
}
|
|
51
|
+
total = total * previous + weight; maximum = next;
|
|
52
|
+
}
|
|
53
|
+
uint base = ((hq * N + query) * count + part);
|
|
54
|
+
for (uint j = 0; j < (DV + 31) / 32; ++j) {
|
|
55
|
+
uint d = j * 32 + lane;
|
|
56
|
+
if (d < DV) out[base * DV + d] = accum[j];
|
|
57
|
+
}
|
|
58
|
+
if (lane == 0) { normal[base * 2] = maximum; normal[base * 2 + 1] = total; }
|
|
59
|
+
` };
|
|
60
|
+
const partial = new MetalKernel(partialSpec);
|
|
61
|
+
const quantizedPartial = new MetalKernel({ ...partialSpec, name: "paged_attention_affine_partial",
|
|
62
|
+
inputNames: [...partialSpec.inputNames, "ks", "kb", "vs", "vb"],
|
|
63
|
+
source: partialSpec.source
|
|
64
|
+
.replace("float(keys[keyBase + d])", "float(InT(float(ks[(keyBase+d)/GROUP]) * float((keys[(keyBase+d)/PACK] >> (((keyBase+d)%PACK)*BITS)) & ((1u<<BITS)-1u)) + float(kb[(keyBase+d)/GROUP])))")
|
|
65
|
+
.replace("float(values[valueBase + d])", "float(InT(float(vs[(valueBase+d)/GROUP]) * float((values[(valueBase+d)/PACK] >> (((valueBase+d)%PACK)*BITS)) & ((1u<<BITS)-1u)) + float(vb[(valueBase+d)/GROUP])))"),
|
|
66
|
+
});
|
|
67
|
+
const merge = new MetalKernel({ name: "paged_attention_merge", inputNames: ["parts", "normal"], outputNames: ["out"], source: `
|
|
68
|
+
uint d = thread_position_in_grid.x;
|
|
69
|
+
uint row = thread_position_in_grid.y;
|
|
70
|
+
if (d >= DV) return;
|
|
71
|
+
uint count = normal_shape[2];
|
|
72
|
+
float maximum = -INFINITY;
|
|
73
|
+
for (uint part = 0; part < count; ++part) maximum = max(maximum, normal[(row * count + part) * 2]);
|
|
74
|
+
float sum = 0, value = 0;
|
|
75
|
+
for (uint part = 0; part < count; ++part) {
|
|
76
|
+
uint base = row * count + part;
|
|
77
|
+
float weight = exp(normal[base * 2] - maximum);
|
|
78
|
+
sum += normal[base * 2 + 1] * weight;
|
|
79
|
+
value += parts[base * DV + d] * weight;
|
|
80
|
+
}
|
|
81
|
+
out[row * DV + d] = OutT(value / sum);
|
|
82
|
+
` });
|
|
83
|
+
|
|
84
|
+
/** Independent immutable view: later appends or row retirement cannot alter it. */
|
|
85
|
+
export function pagedAttentionView(pool: PagedAttentionStorage, blockTable: number[], length: number,
|
|
86
|
+
direct: boolean): KvAttentionView {
|
|
87
|
+
const keys = ops.contiguous(pool.keys), values = ops.contiguous(pool.values);
|
|
88
|
+
const table = MlxArray.fromInt32(Int32Array.from(blockTable), [blockTable.length]);
|
|
89
|
+
const bs = pool.blockSize, quantization = pool.quantization;
|
|
90
|
+
const extra = quantization ? [pool.keyScales!, pool.keyBiases!, pool.valueScales!, pool.valueBiases!].map(a => ops.contiguous(a)) : [];
|
|
91
|
+
const dkStored = pool.headDim, dvStored = pool.vHeadDim;
|
|
92
|
+
const dtype = pool.dtype;
|
|
93
|
+
return {
|
|
94
|
+
attend(q: MlxArray, scale: number, mask: Mask): MlxArray {
|
|
95
|
+
const [, hq, n, dk] = q.shape as [number, number, number, number];
|
|
96
|
+
const hk = keys.shape[1]!, dv = dvStored;
|
|
97
|
+
// Long prefill and explicit masks use the established attention oracle.
|
|
98
|
+
// This is shape dispatch within one attention interface, not scheduling.
|
|
99
|
+
if (!direct || n > 8 || mask.mode === "array") {
|
|
100
|
+
const gather = (tensor: MlxArray) => {
|
|
101
|
+
using picked = ops.takeAxis(tensor, table, 0);
|
|
102
|
+
using transposed = ops.transposeAxes(picked, [1, 0, 2, 3]);
|
|
103
|
+
using flat = ops.reshape(transposed, [1, hk, blockTable.length * bs, tensor.shape[3]!]);
|
|
104
|
+
return flat.slice([0, 0, 0, 0], [1, hk, length, tensor.shape[3]!]);
|
|
105
|
+
};
|
|
106
|
+
using k = gather(keys), v = gather(values);
|
|
107
|
+
if (!quantization) return ops.sdpa(q, k, v, scale, mask.mode, mask.arr);
|
|
108
|
+
using ks = gather(extra[0]!), kb = gather(extra[1]!), vs = gather(extra[2]!), vb = gather(extra[3]!);
|
|
109
|
+
using kd = ops.dequantize(k, ks, kb, { ...quantization, mode: "affine" });
|
|
110
|
+
using vd = ops.dequantize(v, vs, vb, { ...quantization, mode: "affine" });
|
|
111
|
+
return ops.sdpa(q, kd, vd, scale, mask.mode, mask.arr);
|
|
112
|
+
}
|
|
113
|
+
const part = 64, parts = Math.ceil(length / part);
|
|
114
|
+
using multiplier = MlxArray.fromFloat32(new Float32Array([scale, length]), [2]);
|
|
115
|
+
const [out, normal] = (quantization ? quantizedPartial : partial).apply([q, keys, values, table, multiplier, ...extra], {
|
|
116
|
+
outputs: [{ shape: [hq, n, parts, dv], dtype: Dtype.float32 }, { shape: [hq, n, parts, 2], dtype: Dtype.float32 }],
|
|
117
|
+
grid: [32, hq * n, parts], threadGroup: [32, 1, 1],
|
|
118
|
+
templateDtypes: quantization ? { InT: dtype } : {},
|
|
119
|
+
templateInts: { HQ: hq, HK: hk, N: n, DK: dkStored, DV: dv, BS: bs, PART: part,
|
|
120
|
+
...(quantization ? { BITS: quantization.bits, GROUP: quantization.groupSize, PACK: 32 / quantization.bits } : {}) },
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
return merge.apply([out!, normal!], { outputs: [{ shape: [1, hq, n, dv], dtype: q.dtype }],
|
|
124
|
+
grid: [Math.ceil(dv / 32) * 32, hq * n, 1], threadGroup: [32, 1, 1],
|
|
125
|
+
templateDtypes: { OutT: q.dtype }, templateInts: { DV: dv } })[0]!;
|
|
126
|
+
} finally { disposeResources([out!, normal!]); }
|
|
127
|
+
},
|
|
128
|
+
dispose() { disposeResources([keys, values, table, ...extra]); },
|
|
129
|
+
};
|
|
130
|
+
}
|