@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
package/src/state/ssm.ts
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
// Gated DeltaNet recurrence for Qwen3.5 linear-attention layers.
|
|
2
|
+
//
|
|
3
|
+
// Port target: mlx_lm.models.gated_delta (compute_g + the `gated_delta_step`
|
|
4
|
+
// Metal kernel) and the recurrent-state cache. mlx-lm runs the GPU kernel by
|
|
5
|
+
// default (use_kernel = not training), and its float accumulation / simd_sum
|
|
6
|
+
// reduction order differ from the pure-ops fallback — so BIT-EXACT parity with
|
|
7
|
+
// mlx-lm requires the SAME kernel, dispatched with the SAME grid/threadgroup.
|
|
8
|
+
// We port the non-vectorized variant (g.ndim == 3). Padded prefill selects
|
|
9
|
+
// the oracle masked specialization; unpadded execution retains its kernel.
|
|
10
|
+
//
|
|
11
|
+
// Numerics (must match the reference dtypes exactly — mlx infers the kernel's
|
|
12
|
+
// pointer element types from the input arrays):
|
|
13
|
+
// q, k bf16 [B, T, Hk, Dk] (after inv_scale * rms_norm(., None))
|
|
14
|
+
// v bf16 [B, T, Hv, Dv]
|
|
15
|
+
// g f32 [B, T, Hv] (= exp(-exp(A_log_f32) * softplus(a+dt_bias)))
|
|
16
|
+
// beta bf16 [B, T, Hv] (= sigmoid(b))
|
|
17
|
+
// state_in f32 [B, Hv, Dv, Dk]
|
|
18
|
+
// y (out) bf16 [B, T, Hv, Dv] (InT)
|
|
19
|
+
// state_out f32 [B, Hv, Dv, Dk] (StT)
|
|
20
|
+
// GQA is handled inside the kernel (hk_idx = hv_idx / (Hv/Hk)); q/k stay at Hk.
|
|
21
|
+
|
|
22
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
23
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
24
|
+
import { materializeCopy } from "@mlx-bun/mlx/materialize";
|
|
25
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
26
|
+
import type { GatedDeltaCache, GatedDeltaParameters, Mask } from "../contracts/mlx/cache";
|
|
27
|
+
import { gatedDeltaUpdate } from "../kernels/delta/gated";
|
|
28
|
+
import { gatedDeltaState } from "../kernels/delta/state";
|
|
29
|
+
import { disposing } from "../layers/helpers";
|
|
30
|
+
import type { SsmPrefillPadding } from "./ssm-prefill-padding";
|
|
31
|
+
|
|
32
|
+
/** The causal convolution of `qkv` [B, S, convDim] over the stored `state`
|
|
33
|
+
* [B, K-1, convDim], copied from GatedDeltaNet's `#convolve` without the
|
|
34
|
+
* activation, which belongs to the block's glue (`layer.heads`). Returns the
|
|
35
|
+
* raw conv1d output and the next state: each row's last K-1 input columns,
|
|
36
|
+
* ending at `rowLengths[row]` when given, else at the window's end. Borrows
|
|
37
|
+
* its inputs. */
|
|
38
|
+
function convolve(qkv: MlxArray, state: MlxArray, weight: MlxArray, rowLengths?: readonly number[]): [MlxArray, MlxArray] {
|
|
39
|
+
const [, S, D] = qkv.shape as [number, number, number];
|
|
40
|
+
const input = ops.concatAxis([state, qkv], 1);
|
|
41
|
+
// MLX copy and contiguous can both alias the whole prefill buffer.
|
|
42
|
+
// Materialize only the tail so cache residency follows its logical size.
|
|
43
|
+
const tail = convTail(input, S, weight.shape[1]! - 1, rowLengths);
|
|
44
|
+
const conv = ops.conv1d(input, weight, 1, 0, 1, D);
|
|
45
|
+
input.dispose();
|
|
46
|
+
return [conv, tail];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function convTail(input: MlxArray, processed: number, nKeep: number, rowLengths?: readonly number[]): MlxArray {
|
|
50
|
+
const [B, , D] = input.shape as [number, number, number];
|
|
51
|
+
if (rowLengths) {
|
|
52
|
+
using indices = ops.fromInt32(rowLengths.flatMap(length =>
|
|
53
|
+
Array.from({ length: nKeep }, (_, position) => length + position)), [B, nKeep, 1]);
|
|
54
|
+
return ops.takeAlongAxis(input, indices, 1);
|
|
55
|
+
}
|
|
56
|
+
using view = input.slice([0, processed, 0], [B, processed + nKeep, D]);
|
|
57
|
+
return materializeCopy(view);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The recorded recurrence inputs of a verify window: the convolution inputs and
|
|
61
|
+
* the kernel's position-local inputs (the heads' k and v, the a and b
|
|
62
|
+
* projections), each [B, S, ...], and the layer they came from. The state after
|
|
63
|
+
* the first `keep` positions is the same arithmetic prefix whether or not the
|
|
64
|
+
* rest of the window was ever processed (the kernel walks positions in order;
|
|
65
|
+
* the convolution and the glue are per position), so a rejected tail needs no
|
|
66
|
+
* recomputation: the retained rows fold into the pre-round state. */
|
|
67
|
+
interface SsmWindowRows {
|
|
68
|
+
qkv: MlxArray;
|
|
69
|
+
k: MlxArray;
|
|
70
|
+
v: MlxArray;
|
|
71
|
+
a: MlxArray;
|
|
72
|
+
b: MlxArray;
|
|
73
|
+
layer: GatedDeltaParameters;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** One armed speculative verify round on an SSMCache: the pre-round state
|
|
77
|
+
* snapshot (retained refs — MLX arrays are immutable, so this is free) plus
|
|
78
|
+
* the window's recorded rows. `recurDecode`/`recurWindow` record them. */
|
|
79
|
+
export interface SsmSpecRound {
|
|
80
|
+
/** True from specRoundBegin() until the layer's forward records. */
|
|
81
|
+
armed: boolean;
|
|
82
|
+
prevConv: MlxArray | null;
|
|
83
|
+
prevRecurrent: MlxArray | null;
|
|
84
|
+
prevOffset: number;
|
|
85
|
+
prevOffsets: number[] | null;
|
|
86
|
+
/** The window's recorded rows; null until the verify forward records. */
|
|
87
|
+
rows: SsmWindowRows | null;
|
|
88
|
+
/** Window length S of the recorded verify forward. */
|
|
89
|
+
S: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Recurrent cache for a gated-DeltaNet layer — port of mlx-lm
|
|
93
|
+
* cache.ArraysCache(size=2): slot 0 = causal-conv state [B, K-1, conv_dim],
|
|
94
|
+
* slot 1 = recurrent state [B, Hv, Dv, Dk] f32. `recurDecode` and
|
|
95
|
+
* `recurWindow` own both slots and the kernels that update them
|
|
96
|
+
* (GatedDeltaCache); the DeltaNet block hands them its projections. `advance(N)` only
|
|
97
|
+
* tracks token count for B=1 single-stream (lengths / left_padding are
|
|
98
|
+
* batched-decode concerns). Not a key/value cache: no KV scheme converts it.
|
|
99
|
+
*
|
|
100
|
+
* A rolled-back speculative round leaves the snapshot in the slots and its
|
|
101
|
+
* retained window rows pending; they fold in (the state-only kernel over the
|
|
102
|
+
* recorded rows, and the convolution tail) the next time anything reads the
|
|
103
|
+
* state: the next forward, `state()`, the slots, row moves or persistence. The
|
|
104
|
+
* rollback itself builds no graph. */
|
|
105
|
+
export class SSMCache implements GatedDeltaCache {
|
|
106
|
+
#conv: MlxArray | null = null;
|
|
107
|
+
#recurrent: MlxArray | null = null;
|
|
108
|
+
/** Rows a rolled-back round retained, not yet folded into the slots, with each row's kept count. */
|
|
109
|
+
#pending: { rows: SsmWindowRows; keep: readonly number[] } | null = null;
|
|
110
|
+
offset = 0;
|
|
111
|
+
prefillPadding: SsmPrefillPadding | null = null;
|
|
112
|
+
/** Live speculative verify round; null outside a verify transaction. */
|
|
113
|
+
specRound: SsmSpecRound | null = null;
|
|
114
|
+
/** Per-row token coverage (batch lane only; null on serial B=1 caches,
|
|
115
|
+
* where `offset` IS the row's count). mlx-lm's ArraysCache tracks no
|
|
116
|
+
* offset at all (cache.py:594-722) — per-sequence coverage is the
|
|
117
|
+
* server's bookkeeping; ours lives here because prompt-cache put()
|
|
118
|
+
* keys on EXACT coverage (#putOrDispose offset === tokens.length) and
|
|
119
|
+
* merged rows carry different totals. Seeded by mergeRows, advanced in
|
|
120
|
+
* lockstep by advance(), row-filtered by filter(). */
|
|
121
|
+
offsets: number[] | null = null;
|
|
122
|
+
|
|
123
|
+
/** The causal-conv state, with any retained rows folded in. */
|
|
124
|
+
get conv(): MlxArray | null { this.#fold(); return this.#conv; }
|
|
125
|
+
set conv(value: MlxArray | null) { this.#fold(); this.#conv = value; }
|
|
126
|
+
/** The recurrent state, with any retained rows folded in. */
|
|
127
|
+
get recurrent(): MlxArray | null { this.#fold(); return this.#recurrent; }
|
|
128
|
+
set recurrent(value: MlxArray | null) { this.#fold(); this.#recurrent = value; }
|
|
129
|
+
|
|
130
|
+
signature(): string { return "ssm"; }
|
|
131
|
+
bytesPerToken(): number { return 0; }
|
|
132
|
+
get batchSize(): number { return this.offsets?.length ?? 1; }
|
|
133
|
+
|
|
134
|
+
/** Row `i`'s own token coverage (per-row when batched, `offset` serial). */
|
|
135
|
+
rowOffset(i: number): number {
|
|
136
|
+
return this.offsets ? this.offsets[i]! : this.offset;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Linear-attn layers don't go through the KV update path. */
|
|
140
|
+
updateAndFetch(): [MlxArray, MlxArray] {
|
|
141
|
+
throw new Error("SSMCache has no KV updateAndFetch (gated-DeltaNet layer)");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
makeMask(N: number, _windowSize: number | null): Mask {
|
|
145
|
+
const arr = this.prefillPadding?.makeMask(N) ?? null;
|
|
146
|
+
return { mode: arr ? "array" : "", arr };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
recurDecode(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters): MlxArray {
|
|
150
|
+
return this.#recur(qkv, a, b, layer, null);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
recurWindow(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters): MlxArray {
|
|
154
|
+
return this.#recur(qkv, a, b, layer, this.prefillPadding);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** GatedDeltaNet.forward from its projections to the recurrence output,
|
|
158
|
+
* copied: zero the padded positions, convolve over the stored conv state,
|
|
159
|
+
* run the block's glue, run the gated-delta kernel over the stored
|
|
160
|
+
* recurrent state, replace both states and advance. Inside an armed round
|
|
161
|
+
* the replaced states and the inputs go to the round instead of being
|
|
162
|
+
* disposed, and the round replays through `layer`. */
|
|
163
|
+
#recur(qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters,
|
|
164
|
+
padding: SsmPrefillPadding | null): MlxArray {
|
|
165
|
+
const [B, S, convDim] = qkv.shape as [number, number, number];
|
|
166
|
+
const nKeep = layer.convWeight.shape[1]! - 1;
|
|
167
|
+
this.#fold();
|
|
168
|
+
|
|
169
|
+
const spec = this.specRound;
|
|
170
|
+
if (spec) {
|
|
171
|
+
if (!spec.armed)
|
|
172
|
+
throw new Error("SSMCache: spec round already recorded this round");
|
|
173
|
+
spec.armed = false;
|
|
174
|
+
spec.S = S;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
using mask = padding?.makeMask(S) ?? null;
|
|
178
|
+
if (mask) {
|
|
179
|
+
using expanded = ops.reshape(mask, [B, S, 1]);
|
|
180
|
+
using zero = MlxArray.fromBytesCopy(new Uint8Array(qkv.dtype === Dtype.float32 ? 4 : 2), [], qkv.dtype);
|
|
181
|
+
qkv = disposing(qkv, ops.where(expanded, qkv, zero));
|
|
182
|
+
}
|
|
183
|
+
// Convolution keeps each row's last real-token tail, including empty rows.
|
|
184
|
+
const convState = this.#conv ?? ops.zeros([B, nKeep, convDim], qkv.dtype);
|
|
185
|
+
const [conv, newConv] = convolve(qkv, convState, layer.convWeight, padding?.convolutionLengths(S));
|
|
186
|
+
if (!this.#conv) convState.dispose();
|
|
187
|
+
if (spec) spec.prevConv = this.#conv;
|
|
188
|
+
else this.#conv?.dispose();
|
|
189
|
+
this.#conv = newConv;
|
|
190
|
+
|
|
191
|
+
const { q, k, v } = layer.heads(conv);
|
|
192
|
+
conv.dispose();
|
|
193
|
+
const [out, newState] = gatedDeltaUpdate(q, k, v, a, b, layer.aLog, layer.dtBias, this.#recurrent, mask);
|
|
194
|
+
q.dispose();
|
|
195
|
+
if (spec) {
|
|
196
|
+
spec.rows = { qkv, k, v, a, b, layer };
|
|
197
|
+
spec.prevRecurrent = this.#recurrent;
|
|
198
|
+
} else {
|
|
199
|
+
for (const array of [qkv, k, v, a, b]) array.dispose();
|
|
200
|
+
this.#recurrent?.dispose();
|
|
201
|
+
}
|
|
202
|
+
this.#recurrent = newState;
|
|
203
|
+
this.advance(S);
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Fold a rolled-back round's retained rows into the slots: each row's
|
|
208
|
+
* convolution tail ends at its kept count, and the state-only kernel walks the
|
|
209
|
+
* kept positions of the recorded k, v, a and b from the restored state. The
|
|
210
|
+
* values are the verify window's own, and the kernel's recurrence is the
|
|
211
|
+
* update kernel's, so both slots are bit for bit those of a call over only
|
|
212
|
+
* the retained positions. */
|
|
213
|
+
#fold(): void {
|
|
214
|
+
const pending = this.#pending;
|
|
215
|
+
if (!pending) return;
|
|
216
|
+
this.#pending = null;
|
|
217
|
+
const { rows, keep } = pending;
|
|
218
|
+
try {
|
|
219
|
+
const [B, , convDim] = rows.qkv.shape as [number, number, number];
|
|
220
|
+
const nKeep = rows.layer.convWeight.shape[1]! - 1;
|
|
221
|
+
const uniform = keep.every(count => count === keep[0]);
|
|
222
|
+
using zeros = this.#conv ? null : ops.zeros([B, nKeep, convDim], rows.qkv.dtype);
|
|
223
|
+
using input = ops.concatAxis([this.#conv ?? zeros!, rows.qkv], 1);
|
|
224
|
+
const conv = convTail(input, keep[0]!, nKeep, uniform ? undefined : keep);
|
|
225
|
+
let recurrent: MlxArray;
|
|
226
|
+
try { recurrent = gatedDeltaState(rows.k, rows.v, rows.a, rows.b, rows.layer.aLog, rows.layer.dtBias, this.#recurrent, keep); }
|
|
227
|
+
catch (error) { conv.dispose(); throw error; }
|
|
228
|
+
this.#conv?.dispose();
|
|
229
|
+
this.#recurrent?.dispose();
|
|
230
|
+
this.#conv = conv;
|
|
231
|
+
this.#recurrent = recurrent;
|
|
232
|
+
} finally { for (const array of [rows.qkv, rows.k, rows.v, rows.a, rows.b]) array.dispose(); }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
advance(n: number): void {
|
|
236
|
+
if (this.prefillPadding) { this.advanceRows(this.prefillPadding.advance(n)); return; }
|
|
237
|
+
this.offset += n;
|
|
238
|
+
// Batched rows step together (one forward advances every row), so the
|
|
239
|
+
// per-row counts move in lockstep; they DIFFER only in their merge-time
|
|
240
|
+
// seeds (each row's prompt length).
|
|
241
|
+
if (this.offsets) for (let i = 0; i < this.offsets.length; i++) this.offsets[i]! += n;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Methods may retain different verified prefixes for each active request. */
|
|
245
|
+
advanceRows(counts: readonly number[]): void {
|
|
246
|
+
if (!this.offsets) { this.offset += counts[0]!; return; }
|
|
247
|
+
for (let row = 0; row < this.offsets.length; row++) this.offsets[row]! += counts[row]!;
|
|
248
|
+
this.offset = Math.max(...this.offsets);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
state(): MlxArray[] {
|
|
252
|
+
this.#fold();
|
|
253
|
+
const out: MlxArray[] = [];
|
|
254
|
+
if (this.#conv) out.push(this.#conv);
|
|
255
|
+
if (this.#recurrent) out.push(this.#recurrent);
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
isTrimmable(): boolean {
|
|
260
|
+
return false; // recurrent state can't be trimmed per token — see specRound*
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
trim(_n: number): void {
|
|
264
|
+
throw new Error("SSMCache is not trimmable");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// --- speculative verify-round support -----------------------------------
|
|
268
|
+
// The serve loop arms a round before the verify forward and resolves it
|
|
269
|
+
// after the accept walk. The layer's forward, seeing an armed round, hands
|
|
270
|
+
// its replaced state slots to the round instead of disposing them and
|
|
271
|
+
// records the window's rows. Cache/interface contract: rollback(keep)
|
|
272
|
+
// restores the snapshot and retains the first `keep` window rows, which fold
|
|
273
|
+
// in bit-exactly before the state is next read; commit frees the snapshot
|
|
274
|
+
// and the recordings.
|
|
275
|
+
|
|
276
|
+
specRoundBegin(): void {
|
|
277
|
+
this.#dropSpecRound(); // defensive: a mid-round throw left a stale round
|
|
278
|
+
this.#fold();
|
|
279
|
+
this.specRound = {
|
|
280
|
+
armed: true,
|
|
281
|
+
prevConv: null,
|
|
282
|
+
prevRecurrent: null,
|
|
283
|
+
prevOffset: this.offset,
|
|
284
|
+
prevOffsets: this.offsets ? [...this.offsets] : null,
|
|
285
|
+
rows: null,
|
|
286
|
+
S: 0,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
specRoundCommit(): void {
|
|
291
|
+
this.#dropSpecRound();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
specRoundRollback(keep: number | readonly number[]): void {
|
|
295
|
+
const r = this.specRound;
|
|
296
|
+
if (!r) throw new Error("SSMCache.specRoundRollback without an armed round");
|
|
297
|
+
if (r.armed)
|
|
298
|
+
throw new Error("SSMCache.specRoundRollback before the verify forward recorded");
|
|
299
|
+
if (typeof keep === "number" ? keep < 0 || keep > r.S : keep.some(count => count < 0 || count > r.S))
|
|
300
|
+
throw new Error(`SSMCache.specRoundRollback keep=${keep} outside window S=${r.S}`);
|
|
301
|
+
if (!r.rows) throw new Error("SSMCache.specRoundRollback with no recorded window");
|
|
302
|
+
// Restore the pre-round snapshot (ownership moves back to the cache).
|
|
303
|
+
this.#conv?.dispose();
|
|
304
|
+
this.#recurrent?.dispose();
|
|
305
|
+
this.#conv = r.prevConv;
|
|
306
|
+
this.#recurrent = r.prevRecurrent;
|
|
307
|
+
r.prevConv = null;
|
|
308
|
+
r.prevRecurrent = null;
|
|
309
|
+
this.offset = r.prevOffset;
|
|
310
|
+
this.offsets = r.prevOffsets;
|
|
311
|
+
const rows = typeof keep === "number" ? Array<number>(r.rows.qkv.shape[0]!).fill(keep) : keep;
|
|
312
|
+
if (rows.some(count => count > 0)) {
|
|
313
|
+
// The kept rows move to the cache, which folds them in before the next read.
|
|
314
|
+
this.#pending = { rows: r.rows, keep: [...rows] };
|
|
315
|
+
r.rows = null;
|
|
316
|
+
}
|
|
317
|
+
if (typeof keep === "number") this.advance(keep);
|
|
318
|
+
else this.advanceRows(keep);
|
|
319
|
+
this.#dropSpecRound();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#dropSpecRound(): void {
|
|
323
|
+
const r = this.specRound;
|
|
324
|
+
if (!r) return;
|
|
325
|
+
r.prevConv?.dispose();
|
|
326
|
+
r.prevRecurrent?.dispose();
|
|
327
|
+
if (r.rows) for (const array of [r.rows.qkv, r.rows.k, r.rows.v, r.rows.a, r.rows.b]) array.dispose();
|
|
328
|
+
this.specRound = null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
dispose(): void {
|
|
332
|
+
this.#dropSpecRound();
|
|
333
|
+
const pending = this.#pending?.rows;
|
|
334
|
+
this.#pending = null;
|
|
335
|
+
if (pending) for (const array of [pending.qkv, pending.k, pending.v, pending.a, pending.b]) array.dispose();
|
|
336
|
+
this.#conv?.dispose();
|
|
337
|
+
this.#recurrent?.dispose();
|
|
338
|
+
this.#conv = null;
|
|
339
|
+
this.#recurrent = null;
|
|
340
|
+
this.prefillPadding = null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// --- batch-lane dynamic-B ops (BatchScheduler only) ----------------------
|
|
344
|
+
// Both state slots are plain [B, ...] tensors with no temporal axis and no
|
|
345
|
+
// left-padding, so the batched twins of mergeKVRows/filterKVRows collapse
|
|
346
|
+
// to B-axis concat/take. `offset` is bookkeeping only for SSM layers (RoPE
|
|
347
|
+
// reads the FULL-attention caches; the linear-attn forward never reads it),
|
|
348
|
+
// so merged rows carrying different token counts share the max; the exact
|
|
349
|
+
// per-row counts live in `offsets` (extraction's coverage key).
|
|
350
|
+
|
|
351
|
+
/** Merge a fully-prefilled solo row into a running batched cache (`prev`
|
|
352
|
+
* null ⇒ the solo row becomes the batch). The returned cache owns fresh
|
|
353
|
+
* arrays — or, when there is nothing to concat, arrays STOLEN from `solo`
|
|
354
|
+
* (nulled there so the scheduler's unconditional dispose of the solo
|
|
355
|
+
* caches stays safe). `prev`/`solo` disposal remains the caller's job. */
|
|
356
|
+
static mergeRows(prev: SSMCache | null, solo: SSMCache): SSMCache {
|
|
357
|
+
if (!solo.conv || !solo.recurrent)
|
|
358
|
+
throw new Error("SSMCache.mergeRows: solo row has no state (prefill first)");
|
|
359
|
+
const out = new SSMCache();
|
|
360
|
+
if (prev) {
|
|
361
|
+
if (!prev.conv || !prev.recurrent)
|
|
362
|
+
throw new Error("SSMCache.mergeRows: batched cache has no state");
|
|
363
|
+
out.conv = ops.concatAxis([prev.conv, solo.conv], 0);
|
|
364
|
+
out.recurrent = ops.concatAxis([prev.recurrent, solo.recurrent], 0);
|
|
365
|
+
} else {
|
|
366
|
+
out.conv = solo.conv;
|
|
367
|
+
out.recurrent = solo.recurrent;
|
|
368
|
+
solo.conv = null;
|
|
369
|
+
solo.recurrent = null;
|
|
370
|
+
}
|
|
371
|
+
out.offset = Math.max(prev?.offset ?? 0, solo.offset);
|
|
372
|
+
// Seed per-row coverage: an adopted serial `prev` (offsets null) is one
|
|
373
|
+
// row whose exact count is its scalar offset.
|
|
374
|
+
out.offsets = [...(prev ? prev.offsets ?? [prev.offset] : []), solo.offset];
|
|
375
|
+
return out;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Evict rows not in `keep` (ascending row indices), in place. */
|
|
379
|
+
filter(keep: number[]): void {
|
|
380
|
+
if (this.conv && this.recurrent) {
|
|
381
|
+
const idx = ops.fromInt32(keep, [keep.length]);
|
|
382
|
+
const conv = ops.takeAxis(this.conv, idx, 0);
|
|
383
|
+
const recurrent = ops.takeAxis(this.recurrent, idx, 0);
|
|
384
|
+
idx.dispose();
|
|
385
|
+
this.conv.dispose();
|
|
386
|
+
this.recurrent.dispose();
|
|
387
|
+
this.conv = conv;
|
|
388
|
+
this.recurrent = recurrent;
|
|
389
|
+
}
|
|
390
|
+
this.prefillPadding?.filter(keep);
|
|
391
|
+
if (this.offsets) {
|
|
392
|
+
this.offsets = keep.map((i) => this.offsets![i]!);
|
|
393
|
+
this.offset = Math.max(0, ...this.offsets);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
filterRows(keep: readonly number[]): void { this.filter([...keep]); }
|
|
398
|
+
|
|
399
|
+
/** Row `i` as a fresh SERIAL cache — port of mlx-lm ArraysCache.extract
|
|
400
|
+
* (cache.py:673-676: `cache.cache = [c[idx : idx + 1] for c in self.cache]`,
|
|
401
|
+
* a B-axis slice of every state slot). A proper subset gets compact storage
|
|
402
|
+
* so retaining one row does not pin the other rows. A singleton retains an
|
|
403
|
+
* independent handle over its complete allocation. Offset =
|
|
404
|
+
* the row's OWN coverage, not the shared max. */
|
|
405
|
+
extractRow(i: number): SSMCache {
|
|
406
|
+
if (!this.conv || !this.recurrent)
|
|
407
|
+
throw new Error("SSMCache.extractRow: empty cache");
|
|
408
|
+
const cut = (a: MlxArray): MlxArray => {
|
|
409
|
+
const lo = a.shape.map(() => 0);
|
|
410
|
+
lo[0] = i;
|
|
411
|
+
const hi = [...a.shape];
|
|
412
|
+
hi[0] = i + 1;
|
|
413
|
+
using view = a.slice(lo, hi);
|
|
414
|
+
return a.shape[0] === 1 ? ops.contiguous(view) : materializeCopy(view);
|
|
415
|
+
};
|
|
416
|
+
const out = new SSMCache();
|
|
417
|
+
out.conv = cut(this.conv);
|
|
418
|
+
out.recurrent = cut(this.recurrent);
|
|
419
|
+
out.offset = this.rowOffset(i);
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SpeculativeRowLayout } from "../contracts/mlx/cache";
|
|
2
|
+
|
|
3
|
+
/** The verification rows a storage's row-layout declaration names
|
|
4
|
+
* (`state/layout`'s table). */
|
|
5
|
+
export type TargetRowLayout = SpeculativeRowLayout;
|
|
6
|
+
|
|
7
|
+
export { targetRowLayoutFactory } from "./layout";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PromptCache, cacheBytes, type ColdTier } from "./prefix-cache";
|
|
2
|
+
import { cloneKvCaches, SpillQueue } from "./persistence";
|
|
3
|
+
import { SsdDurabilityCoordinator } from "./durability";
|
|
4
|
+
import { disposeResources } from "../runtime/resources";
|
|
5
|
+
import type { SsdCacheStore } from "./ssd-cache";
|
|
6
|
+
|
|
7
|
+
/** One cache with RAM and SSD storage. Inference sees the same put/take
|
|
8
|
+
* interface. Recency and capacity choose RAM residents; a pending write is
|
|
9
|
+
* never treated as a completed demotion. */
|
|
10
|
+
export class TieredPromptCache extends PromptCache {
|
|
11
|
+
protected override promoteRestores = true;
|
|
12
|
+
readonly spillQueue: SpillQueue;
|
|
13
|
+
readonly durability: SsdDurabilityCoordinator;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
maxBytes: number,
|
|
17
|
+
storage: Pick<SsdCacheStore, "storeAsync" | "hasDurablePrefix">,
|
|
18
|
+
cold: ColdTier,
|
|
19
|
+
clone = cloneKvCaches,
|
|
20
|
+
readonly writeBehind = true,
|
|
21
|
+
) {
|
|
22
|
+
super(maxBytes, null, cold, clone);
|
|
23
|
+
this.spillQueue = new SpillQueue(Infinity, cacheBytes, async item => {
|
|
24
|
+
return storage.hasDurablePrefix(item.tokens, item.ns) ||
|
|
25
|
+
await storage.storeAsync(item.tokens, item.caches, item.ns, undefined, item.attachments);
|
|
26
|
+
}, disposeResources, () => false);
|
|
27
|
+
this.durability = new SsdDurabilityCoordinator(this, this.spillQueue, clone,
|
|
28
|
+
(tokens, ns) => storage.hasDurablePrefix(tokens, ns), 0, 5_000, () => {
|
|
29
|
+
// Writing alone does not remove a RAM entry. Apply capacity and
|
|
30
|
+
// pressure policy after the completed writer releases its views.
|
|
31
|
+
this.evictToBudget();
|
|
32
|
+
this.reclaim();
|
|
33
|
+
});
|
|
34
|
+
this.canEvict = entry => {
|
|
35
|
+
if (storage.hasDurablePrefix(entry.tokens, entry.ns)) return true;
|
|
36
|
+
this.durability.schedule(entry.tokens, entry.ns, false);
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override put(...args: Parameters<PromptCache["put"]>): void {
|
|
42
|
+
super.put(...args);
|
|
43
|
+
if (this.writeBehind) this.durability.schedule(args[0], args[2]);
|
|
44
|
+
}
|
|
45
|
+
}
|