@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,137 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { LoraDelta, type LoraState, type LoraWeights } from "./lora";
|
|
4
|
+
import { quantFor,type ModelConfig } from "../artifacts/config";
|
|
5
|
+
import type { Weights } from "../artifacts/weights";
|
|
6
|
+
import { quantizedMatmulRows } from "../kernels/quantization/affine-rows";
|
|
7
|
+
|
|
8
|
+
/** Replaceable quantized payload. Inference sees this as read-only through
|
|
9
|
+
* QuantizedLinear getters; sensitivity analysis exchanges it atomically. */
|
|
10
|
+
export interface QuantizedLinearState {
|
|
11
|
+
w: MlxArray;
|
|
12
|
+
scales: MlxArray;
|
|
13
|
+
biases: MlxArray | null;
|
|
14
|
+
spec: ops.QuantSpec;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class QuantizedLinear {
|
|
18
|
+
readonly delta = new LoraDelta();
|
|
19
|
+
get adapters(): Map<string, LoraWeights> | null { return this.delta.adapters; }
|
|
20
|
+
set adapters(value: Map<string, LoraWeights> | null) { this.delta.adapters = value; }
|
|
21
|
+
get loraState(): LoraState | null { return this.delta.loraState; }
|
|
22
|
+
set loraState(value: LoraState | null) { this.delta.loraState = value; }
|
|
23
|
+
get dropoutId(): number { return this.delta.dropoutId; }
|
|
24
|
+
set dropoutId(value: number) { this.delta.dropoutId = value; }
|
|
25
|
+
private _w: MlxArray;
|
|
26
|
+
private _scales: MlxArray;
|
|
27
|
+
private _biases: MlxArray | null;
|
|
28
|
+
private _spec: ops.QuantSpec;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
w: MlxArray,
|
|
32
|
+
scales: MlxArray,
|
|
33
|
+
biases: MlxArray | null,
|
|
34
|
+
spec: ops.QuantSpec,
|
|
35
|
+
/** ADDITIVE bias term (`.bias`, mlx nn.QuantizedLinear's optional bias —
|
|
36
|
+
* qwen2 qkv, starcoder2, …). Distinct from `biases`, the quantization
|
|
37
|
+
* zero-points. Applied after the matmul, before any LoRA residual,
|
|
38
|
+
* exactly like mlx's QuantizedLinear.__call__. */
|
|
39
|
+
readonly bias: MlxArray | null = null,
|
|
40
|
+
) {
|
|
41
|
+
this._w = w;
|
|
42
|
+
this._scales = scales;
|
|
43
|
+
this._biases = biases;
|
|
44
|
+
this._spec = spec;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get w(): MlxArray { return this._w; }
|
|
48
|
+
get scales(): MlxArray { return this._scales; }
|
|
49
|
+
get biases(): MlxArray | null { return this._biases; }
|
|
50
|
+
get spec(): ops.QuantSpec { return this._spec; }
|
|
51
|
+
|
|
52
|
+
/** Atomically install a quantized payload and return the previous one.
|
|
53
|
+
* This is the sole mutation capability used by sensitivity sweeps. */
|
|
54
|
+
exchangeQuantizedState(next: QuantizedLinearState): QuantizedLinearState {
|
|
55
|
+
const previous = {
|
|
56
|
+
w: this._w,
|
|
57
|
+
scales: this._scales,
|
|
58
|
+
biases: this._biases,
|
|
59
|
+
spec: this._spec,
|
|
60
|
+
};
|
|
61
|
+
this._w = next.w;
|
|
62
|
+
this._scales = next.scales;
|
|
63
|
+
this._biases = next.biases;
|
|
64
|
+
this._spec = next.spec;
|
|
65
|
+
return previous;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static load(weights: Weights, path: string, config: ModelConfig): QuantizedLinear {
|
|
69
|
+
if (!weights.has(`${path}.scales`))
|
|
70
|
+
throw new Error(`${path}: expected quantized linear (no .scales tensor)`);
|
|
71
|
+
const spec = quantFor(config.quantization, path);
|
|
72
|
+
if (!spec) throw new Error(`${path}: no quant spec`);
|
|
73
|
+
return new QuantizedLinear(
|
|
74
|
+
weights.tensor(`${path}.weight`),
|
|
75
|
+
weights.tensor(`${path}.scales`),
|
|
76
|
+
weights.has(`${path}.biases`) ? weights.tensor(`${path}.biases`) : null,
|
|
77
|
+
spec,
|
|
78
|
+
weights.has(`${path}.bias`) ? weights.tensor(`${path}.bias`) : null,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** (in_features, out_features) from the quantized tensors (reference
|
|
83
|
+
* _infer_linear_shape: scales are [out, in/group_size]). */
|
|
84
|
+
get inFeatures(): number {
|
|
85
|
+
return this._scales.shape[1]! * this._spec.groupSize;
|
|
86
|
+
}
|
|
87
|
+
get outFeatures(): number {
|
|
88
|
+
return this._scales.shape[0]!;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
forward(x: MlxArray, independentRows = false): MlxArray {
|
|
92
|
+
let out = independentRows ? quantizedMatmulRows(
|
|
93
|
+
x, this._w, this._scales, this._biases, this._spec,
|
|
94
|
+
) : ops.quantizedMatmul(
|
|
95
|
+
x,
|
|
96
|
+
this._w,
|
|
97
|
+
this._scales,
|
|
98
|
+
this._biases,
|
|
99
|
+
this._spec,
|
|
100
|
+
true,
|
|
101
|
+
);
|
|
102
|
+
// Additive bias (mlx QuantizedLinear: `x = x + self["bias"]`), before
|
|
103
|
+
// the LoRA residual (LoRALinear calls the base linear bias-inclusive).
|
|
104
|
+
if (this.bias) { const previous = out; out = ops.add(out, this.bias); previous.dispose(); }
|
|
105
|
+
return this.delta.forward(x, out);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Stock affine projection. The weights and adapter target belong to base. */
|
|
110
|
+
export class QuantizedLinearPlain {
|
|
111
|
+
readonly #bias: (out: MlxArray) => MlxArray;
|
|
112
|
+
constructor(readonly base: QuantizedLinear) {
|
|
113
|
+
this.#bias = base.bias ? out => { const next = ops.add(out, base.bias!); out.dispose(); return next; } : out => out;
|
|
114
|
+
}
|
|
115
|
+
static load(weights: Weights, path: string, config: ModelConfig): QuantizedLinearPlain {
|
|
116
|
+
return new QuantizedLinearPlain(QuantizedLinear.load(weights, path, config));
|
|
117
|
+
}
|
|
118
|
+
forward(x: MlxArray): MlxArray {
|
|
119
|
+
const out = ops.quantizedMatmul(x, this.base.w, this.base.scales, this.base.biases, this.base.spec, true);
|
|
120
|
+
return this.base.delta.forward(x, this.#bias(out));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Each position uses independent one-row affine arithmetic. */
|
|
125
|
+
export class QuantizedLinearRows {
|
|
126
|
+
readonly #bias: (out: MlxArray) => MlxArray;
|
|
127
|
+
constructor(readonly base: QuantizedLinear) {
|
|
128
|
+
this.#bias = base.bias ? out => { const next = ops.add(out, base.bias!); out.dispose(); return next; } : out => out;
|
|
129
|
+
}
|
|
130
|
+
static load(weights: Weights, path: string, config: ModelConfig): QuantizedLinearRows {
|
|
131
|
+
return new QuantizedLinearRows(QuantizedLinear.load(weights, path, config));
|
|
132
|
+
}
|
|
133
|
+
forward(x: MlxArray): MlxArray {
|
|
134
|
+
const out = quantizedMatmulRows(x, this.base.w, this.base.scales, this.base.biases, this.base.spec);
|
|
135
|
+
return this.base.delta.forward(x, this.#bias(out));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import { quantFor,type ModelConfig } from "../artifacts/config";
|
|
4
|
+
import { expertOffloadArray } from "../artifacts/expert-offload";
|
|
5
|
+
import type { Weights } from "../artifacts/weights";
|
|
6
|
+
|
|
7
|
+
/** Port of switch_layers.QuantizedSwitchLinear (gather_qmm over stacked
|
|
8
|
+
* expert weights; rhs_indices selects the expert per row). */
|
|
9
|
+
export class QuantizedSwitchLinear {
|
|
10
|
+
constructor(
|
|
11
|
+
readonly w: MlxArray,
|
|
12
|
+
readonly scales: MlxArray,
|
|
13
|
+
readonly biases: MlxArray | null,
|
|
14
|
+
readonly spec: ops.QuantSpec,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
static load(weights: Weights, path: string, config: ModelConfig): QuantizedSwitchLinear {
|
|
18
|
+
if (!weights.has(`${path}.scales`))
|
|
19
|
+
throw new Error(`${path}: expected quantized switch linear (no .scales tensor)`);
|
|
20
|
+
const spec = quantFor(config.quantization, path);
|
|
21
|
+
if (!spec) throw new Error(`${path}: no quant spec`);
|
|
22
|
+
// Expert WEIGHT (the ~94% of expert bytes) comes from the page-aligned
|
|
23
|
+
// offload mmap when --expert-offload is active (else resident); scales/
|
|
24
|
+
// biases stay resident (small). Same bytes either way → bit-exact.
|
|
25
|
+
const wName = `${path}.weight`;
|
|
26
|
+
return new QuantizedSwitchLinear(
|
|
27
|
+
expertOffloadArray(wName) ?? weights.tensor(wName),
|
|
28
|
+
weights.tensor(`${path}.scales`),
|
|
29
|
+
weights.has(`${path}.biases`) ? weights.tensor(`${path}.biases`) : null,
|
|
30
|
+
spec,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
forward(x: MlxArray, indices: MlxArray, sortedIndices: boolean): MlxArray {
|
|
35
|
+
return ops.gatherQmm(x, this.w, this.scales, this.biases, indices, this.spec, sortedIndices);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// MLX 0.32.2, mlx/backend/metal/scaled_dot_product_attention.cpp (MIT):
|
|
2
|
+
// https://github.com/ml-explore/mlx/blob/v0.32.2/mlx/backend/metal/scaled_dot_product_attention.cpp
|
|
3
|
+
// For the qualified Qwen GQA-6/D256 graph on applegpu_g16s, one-pass/vector
|
|
4
|
+
// selection and two-pass block counts change after these inclusive KV lengths.
|
|
5
|
+
// A committed span must stay within one arithmetic regime to match M=1.
|
|
6
|
+
const ATTENTION_BOUNDARIES = [1023, 1024, 8192, 32768, 65536];
|
|
7
|
+
|
|
8
|
+
export function qwenAppendChunkSize(cacheTokens: number): number {
|
|
9
|
+
for (const end of ATTENTION_BOUNDARIES)
|
|
10
|
+
if (cacheTokens < end) return Math.min(4, end - cacheTokens);
|
|
11
|
+
return 4;
|
|
12
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Qwen3.5/3.8 multimodal RoPE (mRoPE) — the language-side position machinery
|
|
2
|
+
// for vision requests, port of mlx-vlm's qwen3_5 language rope:
|
|
3
|
+
// MRoPERotaryEmbedding (style "interleaved", mrope_section [11,11,10]) +
|
|
4
|
+
// get_rope_index + the rope_deltas decode continuation.
|
|
5
|
+
//
|
|
6
|
+
// TEXT-ONLY EQUIVALENCE: with all three position streams equal, interleaved
|
|
7
|
+
// selection collapses to plain partial RoPE — which is why the text path's
|
|
8
|
+
// ops.rope stays bit-exact untouched. This module only activates for
|
|
9
|
+
// requests whose prompt contains vision spans; while active, EVERY
|
|
10
|
+
// full-attention rope in the request (prefill AND decode) goes through the
|
|
11
|
+
// manual interleaved path, mirroring the reference (which never uses the
|
|
12
|
+
// fused fast-rope kernel when position_ids are supplied).
|
|
13
|
+
//
|
|
14
|
+
// Position semantics (get_rope_index, B=1 serial): text tokens advance all
|
|
15
|
+
// three streams together; an image span of grid (t, h/merge, w/merge) gets
|
|
16
|
+
// t/h/w grid positions offset by the current base; text after an image
|
|
17
|
+
// resumes at max+1. delta = (max position + 1) - sequence length; decode
|
|
18
|
+
// positions are offset + delta on all three streams.
|
|
19
|
+
|
|
20
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
21
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
22
|
+
import { MetalKernel } from "@mlx-bun/mlx/metal-kernel";
|
|
23
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
24
|
+
|
|
25
|
+
const MROPE_SECTION = [11, 11, 10] as const;
|
|
26
|
+
|
|
27
|
+
/** Interleaved frequency→stream selector (rope_utils
|
|
28
|
+
* _interleaved_position_selector): default t(0); indices 1,4,7,… take
|
|
29
|
+
* h(1) while < section[1]*3; indices 2,5,8,… take w(2) while < section[2]*3. */
|
|
30
|
+
export function interleavedSelector(freqDim: number): Int32Array {
|
|
31
|
+
const sel = new Int32Array(freqDim);
|
|
32
|
+
for (let dim = 1; dim <= 2; dim++) {
|
|
33
|
+
const offset = dim; // h starts at 1, w at 2
|
|
34
|
+
for (let i = offset; i < Math.min(MROPE_SECTION[dim]! * 3, freqDim); i += 3)
|
|
35
|
+
sel[i] = dim;
|
|
36
|
+
}
|
|
37
|
+
return sel;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** inv_freq for the language rope — computed on-device exactly like
|
|
41
|
+
* compute_inv_freq (Metal powf is not correctly rounded; a JS emulation
|
|
42
|
+
* drifts by 1 ulp on some entries). Returned as a live f32 device array
|
|
43
|
+
* (the apply kernel reads it directly). */
|
|
44
|
+
export function mropeInvFreq(rotaryDims: number, base: number): MlxArray {
|
|
45
|
+
const ar = ops.arange(0, rotaryDims, 2, Dtype.float32);
|
|
46
|
+
const ex = ops.mulScalar(ar, 1 / rotaryDims);
|
|
47
|
+
ar.dispose();
|
|
48
|
+
const theta = MlxArray.fromFloat32(Float32Array.from([base]), [1]);
|
|
49
|
+
const p = ops.pow(theta, ex);
|
|
50
|
+
theta.dispose();
|
|
51
|
+
ex.dispose();
|
|
52
|
+
const one = MlxArray.fromFloat32(Float32Array.from([1]), [1]);
|
|
53
|
+
const invA = ops.div(one, p);
|
|
54
|
+
one.dispose();
|
|
55
|
+
p.dispose();
|
|
56
|
+
return invA;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Effective positions [3,1,L] int32 for window [offset, offset+L) —
|
|
60
|
+
* prompt-grid positions while inside the prompt, offset+delta beyond it
|
|
61
|
+
* (the reference's rope_deltas continuation). */
|
|
62
|
+
export function buildMropePositions(
|
|
63
|
+
state: MropeRequestState, offset: number, L: number,
|
|
64
|
+
invFreq: MlxArray, rotaryDims: number,
|
|
65
|
+
): MropeForwardState {
|
|
66
|
+
return { posIds: mropePositionIds(state, offset, L), invFreq, rotaryDims };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Request-owned prompt positions; the returned native array belongs to the caller. */
|
|
70
|
+
export function mropePositionIds(state: MropeRequestState, offset: number, L: number): MlxArray {
|
|
71
|
+
const P = state.positions;
|
|
72
|
+
const promptLen = P[0].length;
|
|
73
|
+
const joint = new Int32Array(3 * L);
|
|
74
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
75
|
+
for (let l = 0; l < L; l++) {
|
|
76
|
+
const idx = offset + l;
|
|
77
|
+
joint[axis * L + l] = idx < promptLen ? P[axis]![idx]! : idx + state.delta;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return MlxArray.fromInt32(joint, [3, 1, L]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// The reference language-side apply is a CUSTOM METAL KERNEL (rope_utils
|
|
84
|
+
// _mrope_apply_kernel, pairing "half_split", position_ndim 3): per rotated
|
|
85
|
+
// pair it computes angle = pos * inv_freq in f32, cos/sin in-kernel, and
|
|
86
|
+
// writes x*c − x_pair*s / x_pair*c + x*s with ONE rounding to the tensor
|
|
87
|
+
// dtype. A bf16 cos/sin manual path is ~0.4% off per rotation and moved
|
|
88
|
+
// step-0 logits by ~1.0 — this verbatim port is the oracle's arithmetic.
|
|
89
|
+
const kernelCache = new Map<number, MetalKernel>();
|
|
90
|
+
function mropeKernel(rotaryDims: number): MetalKernel {
|
|
91
|
+
let k = kernelCache.get(rotaryDims);
|
|
92
|
+
if (k) return k;
|
|
93
|
+
const half = rotaryDims / 2;
|
|
94
|
+
const source = `
|
|
95
|
+
uint elem = thread_position_in_grid.x;
|
|
96
|
+
|
|
97
|
+
const int half_dim = ${half};
|
|
98
|
+
const int q_bsz = x_shape[0];
|
|
99
|
+
const int q_heads = x_shape[1];
|
|
100
|
+
const int q_len = x_shape[2];
|
|
101
|
+
const int q_dim = x_shape[3];
|
|
102
|
+
const int slots = half_dim + q_dim - ${rotaryDims};
|
|
103
|
+
const int work_size = q_bsz * q_heads * q_len * slots;
|
|
104
|
+
|
|
105
|
+
if (elem >= uint(work_size)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
int local = int(elem);
|
|
110
|
+
int slot = local % slots;
|
|
111
|
+
int tmp = local / slots;
|
|
112
|
+
int t = tmp % q_len;
|
|
113
|
+
tmp = tmp / q_len;
|
|
114
|
+
int h = tmp % q_heads;
|
|
115
|
+
int b = tmp / q_heads;
|
|
116
|
+
int base = ((b * q_heads + h) * q_len + t) * q_dim;
|
|
117
|
+
|
|
118
|
+
if (slot >= half_dim) {
|
|
119
|
+
int pass_d = ${rotaryDims} + slot - half_dim;
|
|
120
|
+
int pass_idx = base + pass_d;
|
|
121
|
+
x_out[pass_idx] = x[pass_idx];
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
int freq_idx = slot;
|
|
126
|
+
int d = freq_idx;
|
|
127
|
+
int pair_d = d + half_dim;
|
|
128
|
+
int axis = int(position_selector[freq_idx]);
|
|
129
|
+
float pos = static_cast<float>(position_ids[(axis * q_bsz + b) * q_len + t]);
|
|
130
|
+
float angle = pos * static_cast<float>(inv_freq[freq_idx]);
|
|
131
|
+
float c = metal::cos(angle);
|
|
132
|
+
float s = metal::sin(angle);
|
|
133
|
+
|
|
134
|
+
int idx = base + d;
|
|
135
|
+
float xv = static_cast<float>(x[idx]);
|
|
136
|
+
float xp = static_cast<float>(x[base + pair_d]);
|
|
137
|
+
x_out[idx] = static_cast<T>(xv * c - xp * s);
|
|
138
|
+
x_out[base + pair_d] = static_cast<T>(xp * c + xv * s);
|
|
139
|
+
`;
|
|
140
|
+
k = new MetalKernel({
|
|
141
|
+
name: `mrope_apply_half_split_${rotaryDims}_3d`,
|
|
142
|
+
inputNames: ["x", "position_ids", "inv_freq", "position_selector"],
|
|
143
|
+
outputNames: ["x_out"],
|
|
144
|
+
source,
|
|
145
|
+
ensureRowContiguous: true,
|
|
146
|
+
});
|
|
147
|
+
kernelCache.set(rotaryDims, k);
|
|
148
|
+
return k;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const selectorCache = new Map<number, MlxArray>();
|
|
152
|
+
function selectorArr(half: number): MlxArray {
|
|
153
|
+
let s = selectorCache.get(half);
|
|
154
|
+
if (!s) {
|
|
155
|
+
s = MlxArray.fromInt32(interleavedSelector(half), [half]);
|
|
156
|
+
selectorCache.set(half, s);
|
|
157
|
+
}
|
|
158
|
+
return s;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Apply interleaved mRoPE to one [B, heads, L, headDim] tensor via the
|
|
162
|
+
* reference's exact Metal kernel. `invFreq` is the f32 device array from
|
|
163
|
+
* mropeInvFreq. */
|
|
164
|
+
export function applyInterleavedRope(
|
|
165
|
+
x: MlxArray, fwd: MropeForwardState,
|
|
166
|
+
): MlxArray {
|
|
167
|
+
const rotaryDims = fwd.rotaryDims;
|
|
168
|
+
const [B, H, L, D] = x.shape as [number, number, number, number];
|
|
169
|
+
const half = rotaryDims / 2;
|
|
170
|
+
const slots = half + D - rotaryDims;
|
|
171
|
+
const work = B * H * L * slots;
|
|
172
|
+
const [out] = mropeKernel(rotaryDims).apply(
|
|
173
|
+
[x, fwd.posIds, fwd.invFreq, selectorArr(half)],
|
|
174
|
+
{
|
|
175
|
+
outputs: [{ shape: [B, H, L, D], dtype: x.dtype }],
|
|
176
|
+
grid: [work, 1, 1],
|
|
177
|
+
threadGroup: [256, 1, 1],
|
|
178
|
+
templateDtypes: { T: x.dtype },
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
return out!;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** get_rope_index port (B=1, no attention mask): positions per stream + the
|
|
185
|
+
* decode delta for a prompt with vision spans. `grids` are the per-image
|
|
186
|
+
* (t, h, w) grids in PATCH units and must appear in prompt order. */
|
|
187
|
+
export function qwenRopeIndex(
|
|
188
|
+
ids: readonly number[],
|
|
189
|
+
grids: readonly [number, number, number][],
|
|
190
|
+
mergeSize: number,
|
|
191
|
+
imageTokenId: number,
|
|
192
|
+
videoTokenId: number,
|
|
193
|
+
): MropeRequestState {
|
|
194
|
+
const L = ids.length;
|
|
195
|
+
const P: [Int32Array, Int32Array, Int32Array] =
|
|
196
|
+
[new Int32Array(L), new Int32Array(L), new Int32Array(L)];
|
|
197
|
+
let write = 0; // next output index (== st in the reference walk)
|
|
198
|
+
let base = 0; // st_idx: next position after everything placed so far
|
|
199
|
+
let gi = 0;
|
|
200
|
+
let i = 0;
|
|
201
|
+
while (i < L) {
|
|
202
|
+
const tok = ids[i]!;
|
|
203
|
+
if ((tok === imageTokenId || tok === videoTokenId) && gi < grids.length) {
|
|
204
|
+
const [t, h, w] = grids[gi]!;
|
|
205
|
+
gi++;
|
|
206
|
+
const gh = Math.trunc(h / mergeSize);
|
|
207
|
+
const gw = Math.trunc(w / mergeSize);
|
|
208
|
+
const span = t * gh * gw;
|
|
209
|
+
for (let k = 0; k < span; k++) {
|
|
210
|
+
const tt = Math.trunc(k / (gh * gw));
|
|
211
|
+
const rem = k % (gh * gw);
|
|
212
|
+
const hh = Math.trunc(rem / gw);
|
|
213
|
+
const ww = rem % gw;
|
|
214
|
+
P[0][write + k] = base + tt;
|
|
215
|
+
P[1][write + k] = base + hh;
|
|
216
|
+
P[2][write + k] = base + ww;
|
|
217
|
+
}
|
|
218
|
+
// Reference: next base = max position in the span + 1 = base +
|
|
219
|
+
// max(t, gh, gw) … computed as llm_pos_ids.max()+1.
|
|
220
|
+
base = base + Math.max(t, gh, gw);
|
|
221
|
+
write += span;
|
|
222
|
+
i += span;
|
|
223
|
+
} else {
|
|
224
|
+
P[0][write] = base;
|
|
225
|
+
P[1][write] = base;
|
|
226
|
+
P[2][write] = base;
|
|
227
|
+
base++;
|
|
228
|
+
write++;
|
|
229
|
+
i++;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (write !== L)
|
|
233
|
+
throw new Error(`qwenRopeIndex covered ${write} of ${L} tokens (grids misaligned?)`);
|
|
234
|
+
return { positions: P, delta: base - L };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
import { MropeForwardState,MropeRequestState } from "../contracts/mlx/positions";
|
|
238
|
+
export { type MropeForwardState,type MropeRequestState } from "../contracts/mlx/positions";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Two-dimensional rotary embedding of the Gemma vision towers (optiq's
|
|
2
|
+
// apply_multidimensional_rope): the head is split into one partition per
|
|
3
|
+
// spatial axis, and each partition rotates by its own axis position. The tables
|
|
4
|
+
// are built on device with the oracle's op sequence (arange -> power -> div ->
|
|
5
|
+
// cos/sin -> concat) so they are bit-identical to the reference's; a host-computed
|
|
6
|
+
// table rounds to bf16 differently and, applied to q and k in every layer,
|
|
7
|
+
// compounds.
|
|
8
|
+
|
|
9
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
10
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
11
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
12
|
+
|
|
13
|
+
const AXES = 2;
|
|
14
|
+
|
|
15
|
+
/** cos/sin for every position, [B, L, 1, headDim] (broadcast over heads). */
|
|
16
|
+
export interface Rope2dTables {
|
|
17
|
+
readonly cos: MlxArray;
|
|
18
|
+
readonly sin: MlxArray;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Tables for `positions` [B, L, 2] (integer or float grid coordinates, x then
|
|
23
|
+
* y), cast to the activation dtype the tables multiply. */
|
|
24
|
+
export function rope2dTables(positions: MlxArray, headDim: number, theta: number, dtype: Dtype): Rope2dTables {
|
|
25
|
+
const channelsPerAxis = 2 * Math.floor(headDim / (2 * AXES));
|
|
26
|
+
if (channelsPerAxis * AXES !== headDim) throw new Error(`2D RoPE needs a head dimension divisible by ${2 * AXES}, got ${headDim}`);
|
|
27
|
+
const half = channelsPerAxis / 2;
|
|
28
|
+
const [B, L] = positions.shape as [number, number, number];
|
|
29
|
+
|
|
30
|
+
// timescale = theta ** ((2 / channelsPerAxis) * arange(half))
|
|
31
|
+
const arange = ops.arange(0, half, 1, Dtype.float32);
|
|
32
|
+
const exponents = ops.mulScalar(arange, 2 / channelsPerAxis);
|
|
33
|
+
arange.dispose();
|
|
34
|
+
const base = ops.scalarLike(theta, exponents);
|
|
35
|
+
const timescale = ops.pow(base, exponents);
|
|
36
|
+
base.dispose();
|
|
37
|
+
exponents.dispose();
|
|
38
|
+
|
|
39
|
+
const cosParts: MlxArray[] = [];
|
|
40
|
+
const sinParts: MlxArray[] = [];
|
|
41
|
+
for (let axis = 0; axis < AXES; axis++) {
|
|
42
|
+
const column = positions.slice([0, 0, axis], [B, L, axis + 1]);
|
|
43
|
+
const coordinate = column.astype(Dtype.float32);
|
|
44
|
+
column.dispose();
|
|
45
|
+
const angle = ops.div(coordinate, timescale); // [B, L, half]
|
|
46
|
+
coordinate.dispose();
|
|
47
|
+
const cos = ops.cos(angle);
|
|
48
|
+
const sin = ops.sin(angle);
|
|
49
|
+
angle.dispose();
|
|
50
|
+
cosParts.push(ops.concatAxis([cos, cos], -1)); // [B, L, channelsPerAxis]
|
|
51
|
+
sinParts.push(ops.concatAxis([sin, sin], -1));
|
|
52
|
+
cos.dispose();
|
|
53
|
+
sin.dispose();
|
|
54
|
+
}
|
|
55
|
+
timescale.dispose();
|
|
56
|
+
|
|
57
|
+
const assemble = (parts: MlxArray[]): MlxArray => {
|
|
58
|
+
const full = ops.concatAxis(parts, -1); // [B, L, headDim]
|
|
59
|
+
for (const part of parts) part.dispose();
|
|
60
|
+
const shaped = ops.reshape(full, [B, L, 1, headDim]);
|
|
61
|
+
full.dispose();
|
|
62
|
+
const cast = shaped.astype(dtype);
|
|
63
|
+
shaped.dispose();
|
|
64
|
+
return cast;
|
|
65
|
+
};
|
|
66
|
+
const cos = assemble(cosParts);
|
|
67
|
+
const sin = assemble(sinParts);
|
|
68
|
+
return { cos, sin, dispose() { cos.dispose(); sin.dispose(); } };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** rotate_half applied within each axis partition of the head (not across the
|
|
72
|
+
* whole head): [-x2, x1] per partition. x: [..., headDim]. */
|
|
73
|
+
function partitionedRotateHalf(x: MlxArray): MlxArray {
|
|
74
|
+
const shape = x.shape;
|
|
75
|
+
const last = shape.length - 1;
|
|
76
|
+
const channelsPerAxis = shape[last]! / AXES;
|
|
77
|
+
const half = channelsPerAxis / 2;
|
|
78
|
+
const slice = (from: number, to: number): MlxArray => {
|
|
79
|
+
const start = shape.map(() => 0);
|
|
80
|
+
const stop = [...shape];
|
|
81
|
+
start[last] = from;
|
|
82
|
+
stop[last] = to;
|
|
83
|
+
return x.slice(start, stop);
|
|
84
|
+
};
|
|
85
|
+
const parts: MlxArray[] = [];
|
|
86
|
+
const scratch: MlxArray[] = [];
|
|
87
|
+
for (let axis = 0; axis < AXES; axis++) {
|
|
88
|
+
const origin = axis * channelsPerAxis;
|
|
89
|
+
const x1 = slice(origin, origin + half);
|
|
90
|
+
const x2 = slice(origin + half, origin + channelsPerAxis);
|
|
91
|
+
const negated = ops.neg(x2);
|
|
92
|
+
parts.push(ops.concatAxis([negated, x1], last));
|
|
93
|
+
scratch.push(x1, x2, negated);
|
|
94
|
+
}
|
|
95
|
+
const out = ops.concatAxis(parts, last);
|
|
96
|
+
for (const array of [...scratch, ...parts]) array.dispose();
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** x * cos + rotate_half(x) * sin. x: [B, L, H, headDim]; returns a new array. */
|
|
101
|
+
export function applyRope2d(x: MlxArray, tables: Rope2dTables): MlxArray {
|
|
102
|
+
const rotated = partitionedRotateHalf(x);
|
|
103
|
+
const direct = ops.mul(x, tables.cos);
|
|
104
|
+
const turned = ops.mul(rotated, tables.sin);
|
|
105
|
+
rotated.dispose();
|
|
106
|
+
const out = ops.add(direct, turned);
|
|
107
|
+
direct.dispose();
|
|
108
|
+
turned.dispose();
|
|
109
|
+
return out;
|
|
110
|
+
}
|