@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,734 @@
|
|
|
1
|
+
// Whisper FAST execution path — the optimized graphs behind the same
|
|
2
|
+
// decoding loop as the faithful port (packages/inference/src/models/whisper/model.ts /
|
|
3
|
+
// packages/inference/src/transcription/whisper/decode.ts).
|
|
4
|
+
// Gated token-exact against the faithful path on the fixture clips
|
|
5
|
+
// (`02d723a:tests/parity/whisper-fast.test.ts`); never bit-exact by design.
|
|
6
|
+
//
|
|
7
|
+
// What it changes (the reference-engine techniques):
|
|
8
|
+
// S1 fused SDPA (mx.fast.scaled_dot_product_attention) for encoder
|
|
9
|
+
// self-attention, decoder self-attention (causal prefill, cached
|
|
10
|
+
// steps) and cross-attention — whisper.cpp's flash_attn.
|
|
11
|
+
// S3 cross-attention K/V for ALL decoder layers projected by ONE matmul
|
|
12
|
+
// pair per window (concatenated weights), stored head-split
|
|
13
|
+
// [B, H, 1500, hd] once — whisper.cpp's kv_cross / vLLM's
|
|
14
|
+
// encoder-prefill-only cross KV.
|
|
15
|
+
// S2 the single-token decoder step — embed → 4 layers → ln → logits →
|
|
16
|
+
// logit filters — is ONE mx.compile'd, shapeless closure replayed
|
|
17
|
+
// natively (packages/mlx/src/compile.ts; the LLM compiled-decode pattern).
|
|
18
|
+
// Per-step state crosses as small int32 arrays (position, last two
|
|
19
|
+
// tokens, last timestamp, at-sample-begin) so the graph never
|
|
20
|
+
// changes; the growing self-KV enters as inputs (concat in-graph).
|
|
21
|
+
// GELU: the four separate ops instead of the compiled fused kernel the
|
|
22
|
+
// faithful path needs for bit-exactness (measured equal; fewer traces).
|
|
23
|
+
// S4 logit filters (suppress sets, blank, timestamp rules incl. the
|
|
24
|
+
// openai monotonic rule and the timestamp-probability rule) are
|
|
25
|
+
// device-side broadcast ops inside that closure — no 200 KB host
|
|
26
|
+
// mask upload per step.
|
|
27
|
+
// Beam rows attend to the single encoder output through broadcasting;
|
|
28
|
+
// cache reorders are `take` on axis 0 between steps (openai/whisper.cpp
|
|
29
|
+
// seq_cp equivalent).
|
|
30
|
+
|
|
31
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
32
|
+
import { CompiledFunction } from "@mlx-bun/mlx/compile";
|
|
33
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
34
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
35
|
+
import type { FastFilterConfig, WhisperDims, WhisperFastCache, WhisperFastPath as WhisperFastPathContract } from "../../contracts/mlx/whisper";
|
|
36
|
+
|
|
37
|
+
/** Numerical operations needed by the fused path; model loading stays outside. */
|
|
38
|
+
export interface WhisperFastModel {
|
|
39
|
+
readonly dims: WhisperDims;
|
|
40
|
+
readonly dtype: Dtype;
|
|
41
|
+
weight(name: string): MlxArray;
|
|
42
|
+
weightT(name: string): MlxArray;
|
|
43
|
+
layerNorm(input: MlxArray, prefix: string): MlxArray;
|
|
44
|
+
linear(input: MlxArray, prefix: string, bias?: boolean): MlxArray;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Cache layout of the fast path: head-split self K/V [B, H, T, hd] per
|
|
48
|
+
* layer; cross K/V [Bc, H, 1500, hd] per layer (Bc = 1 for one window). */
|
|
49
|
+
export class FastKvCache implements WhisperFastCache {
|
|
50
|
+
k: MlxArray[] = [];
|
|
51
|
+
v: MlxArray[] = [];
|
|
52
|
+
crossK: MlxArray[] = [];
|
|
53
|
+
crossV: MlxArray[] = [];
|
|
54
|
+
get offset(): number {
|
|
55
|
+
return this.k[0]?.shape[2] ?? 0;
|
|
56
|
+
}
|
|
57
|
+
rearrange(indices: number[]): void {
|
|
58
|
+
if (indices.every((x, i) => x === i)) return;
|
|
59
|
+
const idx = ops.fromInt32(indices, [indices.length]);
|
|
60
|
+
for (let l = 0; l < this.k.length; l++) {
|
|
61
|
+
const nk = ops.takeAxis(this.k[l]!, idx, 0);
|
|
62
|
+
const nv = ops.takeAxis(this.v[l]!, idx, 0);
|
|
63
|
+
this.k[l]!.dispose();
|
|
64
|
+
this.v[l]!.dispose();
|
|
65
|
+
this.k[l] = nk;
|
|
66
|
+
this.v[l] = nv;
|
|
67
|
+
}
|
|
68
|
+
idx.dispose();
|
|
69
|
+
}
|
|
70
|
+
dispose(): void {
|
|
71
|
+
for (const a of [...this.k, ...this.v, ...this.crossK, ...this.crossV]) a.dispose();
|
|
72
|
+
this.k = []; this.v = []; this.crossK = []; this.crossV = [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const dispose = (old: MlxArray, next: MlxArray): MlxArray => {
|
|
77
|
+
old.dispose();
|
|
78
|
+
return next;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export class WhisperFastPath implements WhisperFastPathContract {
|
|
82
|
+
readonly model: WhisperFastModel;
|
|
83
|
+
readonly #H: number;
|
|
84
|
+
readonly #hd: number;
|
|
85
|
+
readonly #L: number;
|
|
86
|
+
/** Encoder per-layer fused [D, 3D] q/k/v weightᵀ and [3D] bias (k has none). */
|
|
87
|
+
readonly #encQkv = new Map<number, { wT: MlxArray; b: MlxArray }>();
|
|
88
|
+
#crossWkT: MlxArray | null = null;
|
|
89
|
+
#crossWvT: MlxArray | null = null;
|
|
90
|
+
#crossBv: MlxArray | null = null;
|
|
91
|
+
#encPos: MlxArray | null = null;
|
|
92
|
+
readonly #steps = new Map<string, CompiledFunction>();
|
|
93
|
+
#negInf: MlxArray | null = null;
|
|
94
|
+
|
|
95
|
+
constructor(model: WhisperFastModel) {
|
|
96
|
+
this.model = model;
|
|
97
|
+
this.#H = model.dims.nTextHead;
|
|
98
|
+
this.#hd = model.dims.nTextState / model.dims.nTextHead;
|
|
99
|
+
this.#L = model.dims.nTextLayer;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
makeCache(): FastKvCache {
|
|
103
|
+
return new FastKvCache();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- helpers ------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
#split(x: MlxArray, B: number, T: number, H: number, hd: number): MlxArray {
|
|
109
|
+
let h = ops.reshape(x, [B, T, H, hd]);
|
|
110
|
+
h = dispose(h, ops.transposeAxes(h, [0, 2, 1, 3]));
|
|
111
|
+
return h;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#merge(x: MlxArray, B: number, T: number, D: number): MlxArray {
|
|
115
|
+
let h = ops.transposeAxes(x, [0, 2, 1, 3]);
|
|
116
|
+
h = dispose(h, ops.reshape(h, [B, T, D]));
|
|
117
|
+
return h;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Attention with fused SDPA; k/v may carry batch 1 against B queries
|
|
121
|
+
* (broadcast materialized for the fused kernel only when needed). */
|
|
122
|
+
#attend(qh: MlxArray, kh: MlxArray, vh: MlxArray, causal: boolean): MlxArray {
|
|
123
|
+
const scale = this.#hd ** -0.5;
|
|
124
|
+
const B = qh.shape[0]!;
|
|
125
|
+
if (kh.shape[0] === B) return ops.sdpa(qh, kh, vh, scale, causal ? "causal" : "");
|
|
126
|
+
// beams over one encoder output: plain matmul attention broadcasts
|
|
127
|
+
// the batch for free; the fused kernel would need B copies.
|
|
128
|
+
const kT = ops.transposeAxes(kh, [0, 1, 3, 2]);
|
|
129
|
+
const s = ops.scalarLike(scale, qh);
|
|
130
|
+
let qk = ops.matmul(qh, kT);
|
|
131
|
+
kT.dispose();
|
|
132
|
+
qk = dispose(qk, ops.mul(qk, s));
|
|
133
|
+
s.dispose();
|
|
134
|
+
const w = ops.softmaxAxis(qk, -1, true);
|
|
135
|
+
qk.dispose();
|
|
136
|
+
const o = ops.matmul(w, vh);
|
|
137
|
+
w.dispose();
|
|
138
|
+
return o;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- encoder ------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
/** One gemm for q/k/v instead of three (whisper.cpp fuses these too). */
|
|
144
|
+
#encoderQkv(layer: number): { wT: MlxArray; b: MlxArray } {
|
|
145
|
+
let e = this.#encQkv.get(layer);
|
|
146
|
+
if (e) return e;
|
|
147
|
+
const m = this.model;
|
|
148
|
+
const p = `encoder.blocks.${layer}.attn`;
|
|
149
|
+
const wT = ops.concatAxis([m.weightT(`${p}.query.weight`), m.weightT(`${p}.key.weight`), m.weightT(`${p}.value.weight`)], 1);
|
|
150
|
+
const zeroB = ops.zeros([m.dims.nAudioState], m.dtype);
|
|
151
|
+
const b = ops.concatAxis([m.weight(`${p}.query.bias`), zeroB, m.weight(`${p}.value.bias`)], 0);
|
|
152
|
+
zeroB.dispose();
|
|
153
|
+
ops.evalAll([wT, b]);
|
|
154
|
+
e = { wT, b };
|
|
155
|
+
this.#encQkv.set(layer, e);
|
|
156
|
+
return e;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#sinusoids(): MlxArray {
|
|
160
|
+
if (this.#encPos) return this.#encPos;
|
|
161
|
+
const { nAudioCtx: length, nAudioState: channels } = this.model.dims;
|
|
162
|
+
const half = channels / 2;
|
|
163
|
+
const inc = Math.log(10000) / (half - 1);
|
|
164
|
+
const ar = ops.arange(0, half, 1, Dtype.int32);
|
|
165
|
+
const negInc = MlxArray.fromFloat32(new Float32Array([-inc]), []);
|
|
166
|
+
let inv = ops.mul(ar, negInc);
|
|
167
|
+
ar.dispose(); negInc.dispose();
|
|
168
|
+
inv = dispose(inv, ops.exp(inv));
|
|
169
|
+
inv = dispose(inv, ops.reshape(inv, [1, half]));
|
|
170
|
+
let pos = ops.arange(0, length, 1, Dtype.int32);
|
|
171
|
+
pos = dispose(pos, ops.reshape(pos, [length, 1]));
|
|
172
|
+
const scaled = ops.mul(pos, inv);
|
|
173
|
+
pos.dispose(); inv.dispose();
|
|
174
|
+
const s = ops.sin(scaled);
|
|
175
|
+
const c = ops.cos(scaled);
|
|
176
|
+
scaled.dispose();
|
|
177
|
+
let pe = ops.concatAxis([s, c], 1);
|
|
178
|
+
s.dispose(); c.dispose();
|
|
179
|
+
pe = dispose(pe, pe.astype(this.model.dtype));
|
|
180
|
+
this.#encPos = pe;
|
|
181
|
+
return pe;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** AudioEncoder with fused SDPA: mel [B, 3000, n_mels] → [B, 1500, D].
|
|
185
|
+
* `audioCtx` (Lab, whisper.cpp `-ac`): encode only the first
|
|
186
|
+
* 2·audioCtx mel frames → [B, audioCtx, D]; the positional table is
|
|
187
|
+
* truncated to match. Cheaper for short clips, NOT the trained context. */
|
|
188
|
+
encode(mel: MlxArray, audioCtx: number | null = null): MlxArray {
|
|
189
|
+
const m = this.model;
|
|
190
|
+
const d = m.dims;
|
|
191
|
+
const H = d.nAudioHead;
|
|
192
|
+
const hd = d.nAudioState / H;
|
|
193
|
+
let input = mel;
|
|
194
|
+
let ownInput = false;
|
|
195
|
+
if (audioCtx !== null && audioCtx < d.nAudioCtx) {
|
|
196
|
+
input = mel.slice([0, 0, 0], [mel.shape[0]!, 2 * audioCtx, mel.shape[2]!]);
|
|
197
|
+
ownInput = true;
|
|
198
|
+
}
|
|
199
|
+
let x = ops.conv1d(input, m.weight("encoder.conv1.weight"), 1, 1);
|
|
200
|
+
if (ownInput) input.dispose();
|
|
201
|
+
x = dispose(x, ops.add(x, m.weight("encoder.conv1.bias")));
|
|
202
|
+
x = dispose(x, ops.geluPrecise(x));
|
|
203
|
+
x = dispose(x, ops.conv1d(x, m.weight("encoder.conv2.weight"), 2, 1));
|
|
204
|
+
x = dispose(x, ops.add(x, m.weight("encoder.conv2.bias")));
|
|
205
|
+
x = dispose(x, ops.geluPrecise(x));
|
|
206
|
+
const pe = this.#sinusoids();
|
|
207
|
+
if (x.shape[1] !== d.nAudioCtx) {
|
|
208
|
+
const peT = pe.slice([0, 0], [x.shape[1]!, d.nAudioState]);
|
|
209
|
+
x = dispose(x, ops.add(x, peT));
|
|
210
|
+
peT.dispose();
|
|
211
|
+
} else x = dispose(x, ops.add(x, pe));
|
|
212
|
+
const [B, T, D] = x.shape as [number, number, number];
|
|
213
|
+
// Measured dead ends (M1 Max, 2026-09-15, interleaved A/B, median of 4):
|
|
214
|
+
// padding MLP rows to 1536 for gemm tile alignment (isolated gemm +25 %,
|
|
215
|
+
// whole encoder 0 %) and the unfused-vs-compiled GELU (0 %). The encoder
|
|
216
|
+
// is bound by its fp16 gemms at ~7–8 TFLOPS; ~290 ms per window here
|
|
217
|
+
// vs mlx-whisper's 298.
|
|
218
|
+
for (let i = 0; i < d.nAudioLayer; i++) {
|
|
219
|
+
const p = `encoder.blocks.${i}`;
|
|
220
|
+
const ln = m.layerNorm(x, `${p}.attn_ln`);
|
|
221
|
+
const { wT, b } = this.#encoderQkv(i);
|
|
222
|
+
const qkv = ops.addmm(b, ln, wT);
|
|
223
|
+
ln.dispose();
|
|
224
|
+
const q = qkv.slice([0, 0, 0], [B, T, D]);
|
|
225
|
+
const k = qkv.slice([0, 0, D], [B, T, 2 * D]);
|
|
226
|
+
const v = qkv.slice([0, 0, 2 * D], [B, T, 3 * D]);
|
|
227
|
+
qkv.dispose();
|
|
228
|
+
const qh = this.#split(q, B, T, H, hd); q.dispose();
|
|
229
|
+
const kh = this.#split(k, B, T, H, hd); k.dispose();
|
|
230
|
+
const vh = this.#split(v, B, T, H, hd); v.dispose();
|
|
231
|
+
let o = ops.sdpa(qh, kh, vh, hd ** -0.5, "");
|
|
232
|
+
qh.dispose(); kh.dispose(); vh.dispose();
|
|
233
|
+
o = dispose(o, this.#merge(o, B, T, D));
|
|
234
|
+
o = dispose(o, m.linear(o, `${p}.attn.out`));
|
|
235
|
+
x = dispose(x, ops.add(x, o));
|
|
236
|
+
o.dispose();
|
|
237
|
+
let h = m.layerNorm(x, `${p}.mlp_ln`);
|
|
238
|
+
h = dispose(h, m.linear(h, `${p}.mlp1`));
|
|
239
|
+
h = dispose(h, ops.geluPrecise(h));
|
|
240
|
+
h = dispose(h, m.linear(h, `${p}.mlp2`));
|
|
241
|
+
x = dispose(x, ops.add(x, h));
|
|
242
|
+
h.dispose();
|
|
243
|
+
}
|
|
244
|
+
x = dispose(x, m.layerNorm(x, "encoder.ln_post"));
|
|
245
|
+
return x;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// --- cross K/V (once per window) ---------------------------------------------
|
|
249
|
+
|
|
250
|
+
#crossWeights(): { wkT: MlxArray; wvT: MlxArray; bv: MlxArray } {
|
|
251
|
+
if (!this.#crossWkT) {
|
|
252
|
+
const m = this.model;
|
|
253
|
+
const ks: MlxArray[] = [];
|
|
254
|
+
const vs: MlxArray[] = [];
|
|
255
|
+
const bs: MlxArray[] = [];
|
|
256
|
+
for (let l = 0; l < this.#L; l++) {
|
|
257
|
+
ks.push(m.weightT(`decoder.blocks.${l}.cross_attn.key.weight`));
|
|
258
|
+
vs.push(m.weightT(`decoder.blocks.${l}.cross_attn.value.weight`));
|
|
259
|
+
bs.push(m.weight(`decoder.blocks.${l}.cross_attn.value.bias`));
|
|
260
|
+
}
|
|
261
|
+
this.#crossWkT = ops.concatAxis(ks, 1); // [D, L·D]
|
|
262
|
+
this.#crossWvT = ops.concatAxis(vs, 1);
|
|
263
|
+
this.#crossBv = ops.concatAxis(bs, 0); // [L·D]
|
|
264
|
+
ops.evalAll([this.#crossWkT, this.#crossWvT, this.#crossBv]);
|
|
265
|
+
}
|
|
266
|
+
return { wkT: this.#crossWkT!, wvT: this.#crossWvT!, bv: this.#crossBv! };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Project the encoder output into every decoder layer's cross K/V with
|
|
270
|
+
* one matmul pair; fills `cache.crossK/V` head-split. */
|
|
271
|
+
crossKv(features: MlxArray, cache: FastKvCache): void {
|
|
272
|
+
const { wkT, wvT, bv } = this.#crossWeights();
|
|
273
|
+
const [Bc, Tc] = features.shape as [number, number, number];
|
|
274
|
+
const D = this.model.dims.nTextState;
|
|
275
|
+
const kAll = ops.matmul(features, wkT);
|
|
276
|
+
const vAll = ops.addmm(bv, features, wvT);
|
|
277
|
+
for (let l = 0; l < this.#L; l++) {
|
|
278
|
+
const k = kAll.slice([0, 0, l * D], [Bc, Tc, (l + 1) * D]);
|
|
279
|
+
const v = vAll.slice([0, 0, l * D], [Bc, Tc, (l + 1) * D]);
|
|
280
|
+
const kh = this.#split(k, Bc, Tc, this.#H, this.#hd);
|
|
281
|
+
const vh = this.#split(v, Bc, Tc, this.#H, this.#hd);
|
|
282
|
+
k.dispose(); v.dispose();
|
|
283
|
+
// materialize once so every step reads contiguous tensors
|
|
284
|
+
cache.crossK.push(dispose(kh, ops.contiguous(kh)));
|
|
285
|
+
cache.crossV.push(dispose(vh, ops.contiguous(vh)));
|
|
286
|
+
}
|
|
287
|
+
kAll.dispose();
|
|
288
|
+
vAll.dispose();
|
|
289
|
+
ops.evalAll([...cache.crossK, ...cache.crossV]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// --- decoder layers ---------------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
/** One decoder layer. `selfK/selfV` are the cache prefix (or null);
|
|
295
|
+
* returns the new full K/V for the caller to store. */
|
|
296
|
+
#layer(
|
|
297
|
+
x: MlxArray, l: number, selfK: MlxArray | null, selfV: MlxArray | null,
|
|
298
|
+
crossK: MlxArray, crossV: MlxArray, causal: boolean,
|
|
299
|
+
): { x: MlxArray; k: MlxArray; v: MlxArray } {
|
|
300
|
+
const m = this.model;
|
|
301
|
+
const p = `decoder.blocks.${l}`;
|
|
302
|
+
const [B, T, D] = x.shape as [number, number, number];
|
|
303
|
+
const ln = m.layerNorm(x, `${p}.attn_ln`);
|
|
304
|
+
const q = m.linear(ln, `${p}.attn.query`);
|
|
305
|
+
const k = m.linear(ln, `${p}.attn.key`, false);
|
|
306
|
+
const v = m.linear(ln, `${p}.attn.value`);
|
|
307
|
+
ln.dispose();
|
|
308
|
+
const qh = this.#split(q, B, T, this.#H, this.#hd); q.dispose();
|
|
309
|
+
let kh = this.#split(k, B, T, this.#H, this.#hd); k.dispose();
|
|
310
|
+
let vh = this.#split(v, B, T, this.#H, this.#hd); v.dispose();
|
|
311
|
+
if (selfK && selfV) {
|
|
312
|
+
kh = dispose(kh, ops.concatAxis([selfK, kh], 2));
|
|
313
|
+
vh = dispose(vh, ops.concatAxis([selfV, vh], 2));
|
|
314
|
+
}
|
|
315
|
+
let o = this.#attend(qh, kh, vh, causal);
|
|
316
|
+
qh.dispose();
|
|
317
|
+
o = dispose(o, this.#merge(o, B, T, D));
|
|
318
|
+
o = dispose(o, m.linear(o, `${p}.attn.out`));
|
|
319
|
+
let h = ops.add(x, o);
|
|
320
|
+
o.dispose();
|
|
321
|
+
const cln = m.layerNorm(h, `${p}.cross_attn_ln`);
|
|
322
|
+
const cq = m.linear(cln, `${p}.cross_attn.query`);
|
|
323
|
+
cln.dispose();
|
|
324
|
+
const cqh = this.#split(cq, B, T, this.#H, this.#hd); cq.dispose();
|
|
325
|
+
let co = this.#attend(cqh, crossK, crossV, false);
|
|
326
|
+
cqh.dispose();
|
|
327
|
+
co = dispose(co, this.#merge(co, B, T, D));
|
|
328
|
+
co = dispose(co, m.linear(co, `${p}.cross_attn.out`));
|
|
329
|
+
h = dispose(h, ops.add(h, co));
|
|
330
|
+
co.dispose();
|
|
331
|
+
let f = m.layerNorm(h, `${p}.mlp_ln`);
|
|
332
|
+
f = dispose(f, m.linear(f, `${p}.mlp1`));
|
|
333
|
+
f = dispose(f, ops.geluPrecise(f));
|
|
334
|
+
f = dispose(f, m.linear(f, `${p}.mlp2`));
|
|
335
|
+
h = dispose(h, ops.add(h, f));
|
|
336
|
+
f.dispose();
|
|
337
|
+
return { x: h, k: kh, v: vh };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#embed(tokens: MlxArray, offsetIdx: MlxArray): MlxArray {
|
|
341
|
+
const m = this.model;
|
|
342
|
+
const T = tokens.shape[1]!;
|
|
343
|
+
let x = ops.takeAxis(m.weight("decoder.token_embedding.weight"), tokens, 0);
|
|
344
|
+
// positional rows [offset, offset+T) gathered by an index array so the
|
|
345
|
+
// graph stays position-agnostic
|
|
346
|
+
let pos: MlxArray;
|
|
347
|
+
if (T === 1) pos = ops.takeAxis(m.weight("decoder.positional_embedding"), offsetIdx, 0); // [1, D]
|
|
348
|
+
else {
|
|
349
|
+
const ar = ops.arange(0, T, 1, Dtype.int32);
|
|
350
|
+
const idx = ops.add(ar, offsetIdx);
|
|
351
|
+
ar.dispose();
|
|
352
|
+
pos = ops.takeAxis(m.weight("decoder.positional_embedding"), idx, 0); // [T, D]
|
|
353
|
+
idx.dispose();
|
|
354
|
+
}
|
|
355
|
+
x = dispose(x, ops.add(x, pos));
|
|
356
|
+
pos.dispose();
|
|
357
|
+
return x;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#logits(x: MlxArray): MlxArray {
|
|
361
|
+
const m = this.model;
|
|
362
|
+
let h = m.layerNorm(x, "decoder.ln");
|
|
363
|
+
h = dispose(h, ops.matmul(h, m.weightT("decoder.token_embedding.weight")));
|
|
364
|
+
return h;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Eager multi-token prefill (initial tokens): fills the self-KV cache and
|
|
368
|
+
* returns logits [B, T, V] in the weight dtype. */
|
|
369
|
+
prefill(tokens: MlxArray, cache: FastKvCache): MlxArray {
|
|
370
|
+
const offsetIdx = ops.fromInt32([cache.offset], [1]);
|
|
371
|
+
let x = this.#embed(tokens, offsetIdx);
|
|
372
|
+
offsetIdx.dispose();
|
|
373
|
+
for (let l = 0; l < this.#L; l++) {
|
|
374
|
+
const r = this.#layer(x, l, cache.k[l] ?? null, cache.v[l] ?? null, cache.crossK[l]!, cache.crossV[l]!, true);
|
|
375
|
+
x.dispose();
|
|
376
|
+
x = r.x;
|
|
377
|
+
cache.k[l]?.dispose();
|
|
378
|
+
cache.v[l]?.dispose();
|
|
379
|
+
cache.k[l] = r.k;
|
|
380
|
+
cache.v[l] = r.v;
|
|
381
|
+
}
|
|
382
|
+
const logits = this.#logits(x);
|
|
383
|
+
x.dispose();
|
|
384
|
+
return logits;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// --- compiled single-token step -----------------------------------------------------
|
|
388
|
+
|
|
389
|
+
#negInfArr(): MlxArray {
|
|
390
|
+
return this.#negInf ??= MlxArray.fromFloat32(new Float32Array([-Infinity]), []);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** Device-side logit filters (see header). `state` rows: lastTok,
|
|
394
|
+
* penultTok (-1 = none), lastTs (-1 = none) as int32 [B]; atBegin int32 [1]. */
|
|
395
|
+
#filters(logits: MlxArray, cfg: FastFilterConfig, state: { lastTok: MlxArray; penultTok: MlxArray; lastTs: MlxArray; atBegin: MlxArray }): MlxArray {
|
|
396
|
+
const V = cfg.nVocab;
|
|
397
|
+
const B = logits.shape[0]!;
|
|
398
|
+
const negInf = this.#negInfArr();
|
|
399
|
+
const zero = ops.scalarLike(0, logits);
|
|
400
|
+
const maskConst = new Float32Array(V);
|
|
401
|
+
for (const id of cfg.suppressIds) maskConst[id] = -Infinity;
|
|
402
|
+
if (cfg.useTimestampRules) maskConst[cfg.noTimestamps] = -Infinity;
|
|
403
|
+
const constMask = MlxArray.fromFloat32(maskConst, [1, V]);
|
|
404
|
+
let base = ops.add(logits, constMask);
|
|
405
|
+
constMask.dispose();
|
|
406
|
+
const atBegin = ops.greater(state.atBegin, ops.fromInt32([0], [1])); // [1] bool
|
|
407
|
+
if (cfg.blankIds) {
|
|
408
|
+
const blank = new Float32Array(V);
|
|
409
|
+
for (const id of cfg.blankIds) blank[id] = -Infinity;
|
|
410
|
+
const blankMask = MlxArray.fromFloat32(blank, [1, V]);
|
|
411
|
+
const bm = ops.where(atBegin, blankMask, zero);
|
|
412
|
+
blankMask.dispose();
|
|
413
|
+
base = dispose(base, ops.add(base, bm));
|
|
414
|
+
bm.dispose();
|
|
415
|
+
}
|
|
416
|
+
if (!cfg.useTimestampRules) {
|
|
417
|
+
atBegin.dispose(); zero.dispose();
|
|
418
|
+
return base;
|
|
419
|
+
}
|
|
420
|
+
const idx = ops.arange(0, V, 1, Dtype.int32);
|
|
421
|
+
const idx2 = ops.reshape(idx, [1, V]);
|
|
422
|
+
idx.dispose();
|
|
423
|
+
const c = (n: number) => ops.fromInt32([n], [1]);
|
|
424
|
+
const tsBegin = c(cfg.timestampBegin);
|
|
425
|
+
const eot = c(cfg.eot);
|
|
426
|
+
const minusOne = c(-1);
|
|
427
|
+
const one = c(1);
|
|
428
|
+
const col = (a: MlxArray) => ops.reshape(a, [B, 1]);
|
|
429
|
+
const lastTok = col(state.lastTok);
|
|
430
|
+
const penultTok = col(state.penultTok);
|
|
431
|
+
const lastTs = col(state.lastTs);
|
|
432
|
+
const lastWas = ops.greaterEqual(lastTok, tsBegin); // [B,1]
|
|
433
|
+
const penultNone = ops.less(penultTok, c(0));
|
|
434
|
+
const penultGe = ops.greaterEqual(penultTok, tsBegin);
|
|
435
|
+
const penultWas = ops.logicalOr(penultNone, penultGe);
|
|
436
|
+
const notPenult = ops.logicalNot(penultWas);
|
|
437
|
+
const idxGeTs = ops.greaterEqual(idx2, tsBegin); // [1,V]
|
|
438
|
+
const idxLtEot = ops.less(idx2, eot);
|
|
439
|
+
const idxLtTs = ops.less(idx2, tsBegin);
|
|
440
|
+
// m1: pairs rule
|
|
441
|
+
const a1 = ops.logicalAnd(lastWas, penultWas);
|
|
442
|
+
const a2 = ops.logicalAnd(lastWas, notPenult);
|
|
443
|
+
const m1a = ops.logicalAnd(a1, idxGeTs);
|
|
444
|
+
const m1b = ops.logicalAnd(a2, idxLtEot);
|
|
445
|
+
let m = ops.logicalOr(m1a, m1b);
|
|
446
|
+
// m2: monotonic timestamps
|
|
447
|
+
const hasTs = ops.greaterEqual(lastTs, c(0));
|
|
448
|
+
const lastTsPlus = ops.add(lastTs, one);
|
|
449
|
+
const tsLast = ops.where(a2, lastTs, lastTsPlus);
|
|
450
|
+
const idxLtLast = ops.less(idx2, tsLast);
|
|
451
|
+
const m2a = ops.logicalAnd(hasTs, idxGeTs);
|
|
452
|
+
const m2 = ops.logicalAnd(m2a, idxLtLast);
|
|
453
|
+
m = dispose(m, ops.logicalOr(m, m2));
|
|
454
|
+
// m3: at sample begin — timestamps only, capped by max_initial_timestamp
|
|
455
|
+
let m3 = idxLtTs;
|
|
456
|
+
let capMask: MlxArray | null = null;
|
|
457
|
+
if (cfg.maxInitialTimestampIndex !== null) {
|
|
458
|
+
const lastAllowed = c(cfg.timestampBegin + cfg.maxInitialTimestampIndex);
|
|
459
|
+
capMask = ops.greater(idx2, lastAllowed);
|
|
460
|
+
lastAllowed.dispose();
|
|
461
|
+
m3 = ops.logicalOr(idxLtTs, capMask);
|
|
462
|
+
}
|
|
463
|
+
const m3b = ops.logicalAnd(atBegin, m3);
|
|
464
|
+
m = dispose(m, ops.logicalOr(m, m3b));
|
|
465
|
+
const ruleMask = ops.where(m, negInf, zero);
|
|
466
|
+
// probability rule on the PRE-rule logits (the oracle's `logits`)
|
|
467
|
+
// (no Slice inside a shapeless compile — MLX cannot infer its output
|
|
468
|
+
// shape; mask the complementary halves to -inf instead)
|
|
469
|
+
const lse = ops.logsumexpAxis(base, -1, true);
|
|
470
|
+
const logprobs = ops.sub(base, lse);
|
|
471
|
+
const tsPart = ops.where(idxGeTs, logprobs, negInf);
|
|
472
|
+
const tsLp = ops.logsumexpAxis(tsPart, -1, true);
|
|
473
|
+
const textPart = ops.where(idxLtTs, logprobs, negInf);
|
|
474
|
+
const maxText = ops.maxAxis(textPart, -1, true);
|
|
475
|
+
const cond = ops.greater(tsLp, maxText); // [B,1]
|
|
476
|
+
const condMask = ops.logicalAnd(cond, idxLtTs);
|
|
477
|
+
const probMask = ops.where(condMask, negInf, zero);
|
|
478
|
+
let out = ops.add(base, ruleMask);
|
|
479
|
+
out = dispose(out, ops.add(out, probMask));
|
|
480
|
+
for (const a of [base, atBegin, zero, idx2, tsBegin, eot, minusOne, one, lastTok, penultTok, lastTs, lastWas, penultNone,
|
|
481
|
+
penultGe, penultWas, notPenult, idxGeTs, idxLtEot, idxLtTs, a1, a2, m1a, m1b, m, hasTs, lastTsPlus, tsLast, idxLtLast,
|
|
482
|
+
m2a, m2, m3b, ruleMask, lse, logprobs, tsPart, tsLp, textPart, maxText, cond, condMask, probMask]) a.dispose();
|
|
483
|
+
if (capMask) { capMask.dispose(); m3.dispose(); }
|
|
484
|
+
return out;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Greedy variant: the compiled graph also picks argmax(filtered) and its
|
|
488
|
+
* log-prob, so the next step can consume the token ARRAY without a host
|
|
489
|
+
* round trip (S5 pipelining: the host reads the previous step's token
|
|
490
|
+
* while the GPU runs the current one — the oracle's async_eval lag). */
|
|
491
|
+
#greedyStep(B: number, Bc: number, cfg: FastFilterConfig): CompiledFunction {
|
|
492
|
+
const key = `greedy:${B}:${Bc}:${cfg.suppressIds.join(",")}:${cfg.blankIds?.join(",") ?? "-"}:${cfg.useTimestampRules}:${cfg.maxInitialTimestampIndex}`;
|
|
493
|
+
let fn = this.#steps.get(key);
|
|
494
|
+
if (fn) return fn;
|
|
495
|
+
const L = this.#L;
|
|
496
|
+
fn = new CompiledFunction((inputs) => {
|
|
497
|
+
// inputs: [tok [B] int32, offsetIdx [1], lastTok [B], penultTok [B], lastTs [B], K.., V.., CK.., CV..]
|
|
498
|
+
const [tokFlat, offsetIdx, lastTok, penultTok, lastTs] = inputs as [MlxArray, MlxArray, MlxArray, MlxArray, MlxArray];
|
|
499
|
+
const K = inputs.slice(5, 5 + L);
|
|
500
|
+
const Vv = inputs.slice(5 + L, 5 + 2 * L);
|
|
501
|
+
const CK = inputs.slice(5 + 2 * L, 5 + 3 * L);
|
|
502
|
+
const CV = inputs.slice(5 + 3 * L, 5 + 4 * L);
|
|
503
|
+
const tok = ops.reshape(tokFlat, [B, 1]);
|
|
504
|
+
let x = this.#embed(tok, offsetIdx);
|
|
505
|
+
tok.dispose();
|
|
506
|
+
const newK: MlxArray[] = [];
|
|
507
|
+
const newV: MlxArray[] = [];
|
|
508
|
+
for (let l = 0; l < L; l++) {
|
|
509
|
+
const r = this.#layer(x, l, K[l]!, Vv[l]!, CK[l]!, CV[l]!, false);
|
|
510
|
+
x.dispose();
|
|
511
|
+
x = r.x;
|
|
512
|
+
newK.push(r.k);
|
|
513
|
+
newV.push(r.v);
|
|
514
|
+
}
|
|
515
|
+
const logits16 = this.#logits(x);
|
|
516
|
+
x.dispose();
|
|
517
|
+
const V = cfg.nVocab;
|
|
518
|
+
let last = ops.reshape(logits16, [B, V]);
|
|
519
|
+
logits16.dispose();
|
|
520
|
+
last = dispose(last, last.astype(Dtype.float32));
|
|
521
|
+
const atBegin = ops.fromInt32([0], [1]); // steps ≥ 1 are never at sample_begin
|
|
522
|
+
const filtered = this.#filters(last, cfg, { lastTok, penultTok, lastTs, atBegin });
|
|
523
|
+
atBegin.dispose();
|
|
524
|
+
let next = ops.argmaxAxis(filtered, -1);
|
|
525
|
+
next = dispose(next, next.astype(Dtype.int32));
|
|
526
|
+
const lse = ops.logsumexpAxis(filtered, -1, true);
|
|
527
|
+
const logprobs = ops.sub(filtered, lse);
|
|
528
|
+
lse.dispose();
|
|
529
|
+
const idx = ops.reshape(next, [B, 1]);
|
|
530
|
+
let lp = ops.takeAlongAxis(logprobs, idx, 1);
|
|
531
|
+
idx.dispose();
|
|
532
|
+
logprobs.dispose();
|
|
533
|
+
lp = dispose(lp, ops.reshape(lp, [B]));
|
|
534
|
+
// timestamp state for the NEXT step: last := next, penult := last,
|
|
535
|
+
// lastTs := next if it is a timestamp else unchanged
|
|
536
|
+
const tsBegin = ops.fromInt32([cfg.timestampBegin], [1]);
|
|
537
|
+
const isTs = ops.greaterEqual(next, tsBegin);
|
|
538
|
+
const newLastTs = ops.where(isTs, next, lastTs);
|
|
539
|
+
tsBegin.dispose();
|
|
540
|
+
isTs.dispose();
|
|
541
|
+
filtered.dispose();
|
|
542
|
+
return [last, next, lp, newLastTs, ...newK, ...newV];
|
|
543
|
+
}, true);
|
|
544
|
+
this.#steps.set(key, fn);
|
|
545
|
+
return fn;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Pipelined greedy step: consumes/produces token + timestamp-state ARRAYS.
|
|
549
|
+
* Returns owned arrays; the caller reads `next` lazily (one-step lag). */
|
|
550
|
+
greedyStep(
|
|
551
|
+
st: { tok: MlxArray; lastTok: MlxArray; penultTok: MlxArray; lastTs: MlxArray },
|
|
552
|
+
cache: FastKvCache, cfg: FastFilterConfig,
|
|
553
|
+
): { pre: MlxArray; next: MlxArray; lp: MlxArray; lastTs: MlxArray } {
|
|
554
|
+
const B = st.tok.shape[0]!;
|
|
555
|
+
const Bc = cache.crossK[0]!.shape[0]!;
|
|
556
|
+
const fn = this.#greedyStep(B, Bc, cfg);
|
|
557
|
+
const offsetIdx = ops.fromInt32([cache.offset], [1]);
|
|
558
|
+
const outs = fn.apply([st.tok, offsetIdx, st.lastTok, st.penultTok, st.lastTs, ...cache.k, ...cache.v, ...cache.crossK, ...cache.crossV]);
|
|
559
|
+
offsetIdx.dispose();
|
|
560
|
+
const L = this.#L;
|
|
561
|
+
for (let l = 0; l < L; l++) {
|
|
562
|
+
cache.k[l]!.dispose();
|
|
563
|
+
cache.v[l]!.dispose();
|
|
564
|
+
cache.k[l] = outs[4 + l]!;
|
|
565
|
+
cache.v[l] = outs[4 + L + l]!;
|
|
566
|
+
}
|
|
567
|
+
return { pre: outs[0]!, next: outs[1]!, lp: outs[2]!, lastTs: outs[3]! };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** Beam variant: top-(k) indices/values of the filtered log-probs come
|
|
571
|
+
* out of the graph (one host readback per step, no partition ops outside). */
|
|
572
|
+
#beamStep(B: number, Bc: number, k: number, cfg: FastFilterConfig): CompiledFunction {
|
|
573
|
+
const key = `beam${k}:${B}:${Bc}:${cfg.suppressIds.join(",")}:${cfg.blankIds?.join(",") ?? "-"}:${cfg.useTimestampRules}:${cfg.maxInitialTimestampIndex}`;
|
|
574
|
+
let fn = this.#steps.get(key);
|
|
575
|
+
if (fn) return fn;
|
|
576
|
+
const L = this.#L;
|
|
577
|
+
fn = new CompiledFunction((inputs) => {
|
|
578
|
+
const [tokFlat, offsetIdx, lastTok, penultTok, lastTs] = inputs as [MlxArray, MlxArray, MlxArray, MlxArray, MlxArray];
|
|
579
|
+
const K = inputs.slice(5, 5 + L);
|
|
580
|
+
const Vv = inputs.slice(5 + L, 5 + 2 * L);
|
|
581
|
+
const CK = inputs.slice(5 + 2 * L, 5 + 3 * L);
|
|
582
|
+
const CV = inputs.slice(5 + 3 * L, 5 + 4 * L);
|
|
583
|
+
const tok = ops.reshape(tokFlat, [B, 1]);
|
|
584
|
+
let x = this.#embed(tok, offsetIdx);
|
|
585
|
+
tok.dispose();
|
|
586
|
+
const newK: MlxArray[] = [];
|
|
587
|
+
const newV: MlxArray[] = [];
|
|
588
|
+
for (let l = 0; l < L; l++) {
|
|
589
|
+
const r = this.#layer(x, l, K[l]!, Vv[l]!, CK[l]!, CV[l]!, false);
|
|
590
|
+
x.dispose();
|
|
591
|
+
x = r.x;
|
|
592
|
+
newK.push(r.k);
|
|
593
|
+
newV.push(r.v);
|
|
594
|
+
}
|
|
595
|
+
const logits16 = this.#logits(x);
|
|
596
|
+
x.dispose();
|
|
597
|
+
const V = cfg.nVocab;
|
|
598
|
+
let last = ops.reshape(logits16, [B, V]);
|
|
599
|
+
logits16.dispose();
|
|
600
|
+
last = dispose(last, last.astype(Dtype.float32));
|
|
601
|
+
const atBegin = ops.fromInt32([0], [1]);
|
|
602
|
+
const filtered = this.#filters(last, cfg, { lastTok, penultTok, lastTs, atBegin });
|
|
603
|
+
atBegin.dispose();
|
|
604
|
+
const lse = ops.logsumexpAxis(filtered, -1, true);
|
|
605
|
+
const logprobs = ops.sub(filtered, lse);
|
|
606
|
+
lse.dispose();
|
|
607
|
+
filtered.dispose();
|
|
608
|
+
// top-k: sort descending is overkill; argpartition on the negation
|
|
609
|
+
// then gather. (Slice with constant bounds is fine here: its output
|
|
610
|
+
// shape does not depend on a varying dimension… but shapeless
|
|
611
|
+
// compile still refuses Slice, so use take with a constant index.)
|
|
612
|
+
const neg = ops.neg(logprobs);
|
|
613
|
+
const part = ops.argpartitionAxis(neg, k - 1, -1);
|
|
614
|
+
neg.dispose();
|
|
615
|
+
const firstK = ops.arange(0, k, 1, Dtype.int32);
|
|
616
|
+
let idx = ops.takeAxis(part, firstK, 1); // [B, k]
|
|
617
|
+
part.dispose();
|
|
618
|
+
firstK.dispose();
|
|
619
|
+
idx = dispose(idx, idx.astype(Dtype.int32));
|
|
620
|
+
const vals = ops.takeAlongAxis(logprobs, idx, 1);
|
|
621
|
+
logprobs.dispose();
|
|
622
|
+
return [last, idx, vals, ...newK, ...newV];
|
|
623
|
+
}, true);
|
|
624
|
+
this.#steps.set(key, fn);
|
|
625
|
+
return fn;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** One compiled beam step: returns the pre-filter logits and the top-k
|
|
629
|
+
* (indices, log-probs) per row; updates the cache in place. */
|
|
630
|
+
beamStep(
|
|
631
|
+
tokens: number[], cache: FastKvCache, cfg: FastFilterConfig, k: number,
|
|
632
|
+
state: { lastTok: number[]; penultTok: number[]; lastTs: number[] },
|
|
633
|
+
): { pre: MlxArray; idx: MlxArray; vals: MlxArray } {
|
|
634
|
+
const B = tokens.length;
|
|
635
|
+
const Bc = cache.crossK[0]!.shape[0]!;
|
|
636
|
+
const fn = this.#beamStep(B, Bc, k, cfg);
|
|
637
|
+
const tok = ops.fromInt32(tokens, [B]);
|
|
638
|
+
const offsetIdx = ops.fromInt32([cache.offset], [1]);
|
|
639
|
+
const lastTok = ops.fromInt32(state.lastTok, [B]);
|
|
640
|
+
const penultTok = ops.fromInt32(state.penultTok, [B]);
|
|
641
|
+
const lastTs = ops.fromInt32(state.lastTs, [B]);
|
|
642
|
+
const outs = fn.apply([tok, offsetIdx, lastTok, penultTok, lastTs, ...cache.k, ...cache.v, ...cache.crossK, ...cache.crossV]);
|
|
643
|
+
for (const a of [tok, offsetIdx, lastTok, penultTok, lastTs]) a.dispose();
|
|
644
|
+
const L = this.#L;
|
|
645
|
+
for (let l = 0; l < L; l++) {
|
|
646
|
+
cache.k[l]!.dispose();
|
|
647
|
+
cache.v[l]!.dispose();
|
|
648
|
+
cache.k[l] = outs[3 + l]!;
|
|
649
|
+
cache.v[l] = outs[3 + L + l]!;
|
|
650
|
+
}
|
|
651
|
+
// Compiled outputs may be strided views; host readback walks memory
|
|
652
|
+
// linearly, so materialize the small top-k arrays.
|
|
653
|
+
const idx = ops.contiguous(outs[1]!);
|
|
654
|
+
const vals = ops.contiguous(outs[2]!);
|
|
655
|
+
outs[1]!.dispose();
|
|
656
|
+
outs[2]!.dispose();
|
|
657
|
+
return { pre: outs[0]!, idx, vals };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Compiled step for a fixed (B, Bc, filter config) signature. Inputs:
|
|
661
|
+
* [tok [B,1], offsetIdx [1], lastTok [B], penultTok [B], lastTs [B],
|
|
662
|
+
* atBegin [1], K_0..K_{L-1}, V_0.., CK_0.., CV_0..]. Outputs:
|
|
663
|
+
* [preLogits [B,V] f32, filtered [B,V] f32, newK_0.., newV_0..]. */
|
|
664
|
+
#step(B: number, Bc: number, cfg: FastFilterConfig): CompiledFunction {
|
|
665
|
+
const key = `${B}:${Bc}:${cfg.suppressIds.join(",")}:${cfg.blankIds?.join(",") ?? "-"}:${cfg.useTimestampRules}:${cfg.maxInitialTimestampIndex}`;
|
|
666
|
+
let fn = this.#steps.get(key);
|
|
667
|
+
if (fn) return fn;
|
|
668
|
+
const L = this.#L;
|
|
669
|
+
fn = new CompiledFunction((inputs) => {
|
|
670
|
+
const [tok, offsetIdx, lastTok, penultTok, lastTs, atBegin] = inputs as [MlxArray, MlxArray, MlxArray, MlxArray, MlxArray, MlxArray];
|
|
671
|
+
const K = inputs.slice(6, 6 + L);
|
|
672
|
+
const Vv = inputs.slice(6 + L, 6 + 2 * L);
|
|
673
|
+
const CK = inputs.slice(6 + 2 * L, 6 + 3 * L);
|
|
674
|
+
const CV = inputs.slice(6 + 3 * L, 6 + 4 * L);
|
|
675
|
+
let x = this.#embed(tok, offsetIdx);
|
|
676
|
+
const newK: MlxArray[] = [];
|
|
677
|
+
const newV: MlxArray[] = [];
|
|
678
|
+
for (let l = 0; l < L; l++) {
|
|
679
|
+
const r = this.#layer(x, l, K[l]!, Vv[l]!, CK[l]!, CV[l]!, false);
|
|
680
|
+
x.dispose();
|
|
681
|
+
x = r.x;
|
|
682
|
+
newK.push(r.k);
|
|
683
|
+
newV.push(r.v);
|
|
684
|
+
}
|
|
685
|
+
const logits16 = this.#logits(x);
|
|
686
|
+
x.dispose();
|
|
687
|
+
const V = cfg.nVocab;
|
|
688
|
+
let last = ops.reshape(logits16, [B, V]);
|
|
689
|
+
logits16.dispose();
|
|
690
|
+
last = dispose(last, last.astype(Dtype.float32));
|
|
691
|
+
const filtered = this.#filters(last, cfg, { lastTok, penultTok, lastTs, atBegin });
|
|
692
|
+
return [last, filtered, ...newK, ...newV];
|
|
693
|
+
}, true);
|
|
694
|
+
this.#steps.set(key, fn);
|
|
695
|
+
return fn;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** Run one compiled decode step; updates `cache` in place. Returns the
|
|
699
|
+
* pre-filter and filtered last-position logits [B, V] f32 (owned). */
|
|
700
|
+
step(
|
|
701
|
+
tokens: number[], cache: FastKvCache, cfg: FastFilterConfig,
|
|
702
|
+
state: { lastTok: number[]; penultTok: number[]; lastTs: number[]; atBegin: boolean },
|
|
703
|
+
): { pre: MlxArray; filtered: MlxArray } {
|
|
704
|
+
const B = tokens.length;
|
|
705
|
+
const Bc = cache.crossK[0]!.shape[0]!;
|
|
706
|
+
const fn = this.#step(B, Bc, cfg);
|
|
707
|
+
const tok = ops.fromInt32(tokens, [B, 1]);
|
|
708
|
+
const offsetIdx = ops.fromInt32([cache.offset], [1]);
|
|
709
|
+
const lastTok = ops.fromInt32(state.lastTok, [B]);
|
|
710
|
+
const penultTok = ops.fromInt32(state.penultTok, [B]);
|
|
711
|
+
const lastTs = ops.fromInt32(state.lastTs, [B]);
|
|
712
|
+
const atBegin = ops.fromInt32([state.atBegin ? 1 : 0], [1]);
|
|
713
|
+
const outs = fn.apply([tok, offsetIdx, lastTok, penultTok, lastTs, atBegin, ...cache.k, ...cache.v, ...cache.crossK, ...cache.crossV]);
|
|
714
|
+
for (const a of [tok, offsetIdx, lastTok, penultTok, lastTs, atBegin]) a.dispose();
|
|
715
|
+
const L = this.#L;
|
|
716
|
+
for (let l = 0; l < L; l++) {
|
|
717
|
+
cache.k[l]!.dispose();
|
|
718
|
+
cache.v[l]!.dispose();
|
|
719
|
+
cache.k[l] = outs[2 + l]!;
|
|
720
|
+
cache.v[l] = outs[2 + L + l]!;
|
|
721
|
+
}
|
|
722
|
+
return { pre: outs[0]!, filtered: outs[1]! };
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
dispose(): void {
|
|
726
|
+
for (const e of this.#encQkv.values()) { e.wT.dispose(); e.b.dispose(); }
|
|
727
|
+
this.#encQkv.clear();
|
|
728
|
+
this.#crossWkT?.dispose(); this.#crossWvT?.dispose(); this.#crossBv?.dispose();
|
|
729
|
+
this.#encPos?.dispose();
|
|
730
|
+
this.#negInf?.dispose();
|
|
731
|
+
for (const f of this.#steps.values()) f.dispose();
|
|
732
|
+
this.#steps.clear();
|
|
733
|
+
}
|
|
734
|
+
}
|