@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,658 @@
|
|
|
1
|
+
// The Qwen 3.5 decoder blocks (port target: mlx_lm.models.qwen3_5 and the
|
|
2
|
+
// qwen3_next Attention/MLP/RMSNormGated it reuses): the gated DeltaNet block,
|
|
3
|
+
// the gated full-attention block, the two MLP blocks and the layer composing
|
|
4
|
+
// them. Each block is built once from the weights and the quant table and reads
|
|
5
|
+
// its state through the cache contract for the phase its caller names
|
|
6
|
+
// (`ReadPhase`). No block inspects its input to choose a phase, builds a mask
|
|
7
|
+
// or chooses an attention kernel: the cache was composed with its read.
|
|
8
|
+
|
|
9
|
+
import type { ModelConfig } from "../../artifacts/config";
|
|
10
|
+
import type { Weights } from "../../artifacts/weights";
|
|
11
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
12
|
+
import { Dtype } from "@mlx-bun/mlx/ffi";
|
|
13
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
14
|
+
import { CompiledFunction } from "@mlx-bun/mlx/compile";
|
|
15
|
+
import { TrellisLinear, fusedGateUpEligible, fusedGateUpSwiglu } from "../../layers/trellis-linear";
|
|
16
|
+
import { disposing } from "../../layers/helpers";
|
|
17
|
+
import { compiledSwiglu } from "../../layers/swiglu";
|
|
18
|
+
import { QuantizedLinear } from "../../layers/quantized-linear";
|
|
19
|
+
import { RMSNorm } from "../../layers/normalization";
|
|
20
|
+
import { CompiledGatedRMSNorm, CompiledResidualRMSNorm } from "../../layers/compiled-normalization";
|
|
21
|
+
import { CompiledSiluRMSHeads } from "../../layers/delta-heads";
|
|
22
|
+
import type { AttentionRead, Cache, CommittedAttentionCache, GatedDeltaCache, GatedDeltaHeads, GatedDeltaParameters } from "../../contracts/mlx/cache";
|
|
23
|
+
import { applyInterleavedRope } from "../../layers/qwen-mrope";
|
|
24
|
+
import type { MropeForwardState } from "../../contracts/mlx/positions";
|
|
25
|
+
import type { ReadPhase } from "../graph";
|
|
26
|
+
|
|
27
|
+
const PREFIX = "language_model";
|
|
28
|
+
|
|
29
|
+
/** The attention read each phase names. A committed span reads through
|
|
30
|
+
* `appendCommitted`, so the cache must be composed for committed appends. */
|
|
31
|
+
const appendFor: Readonly<Record<ReadPhase, (cache: Cache, k: MlxArray, v: MlxArray) => AttentionRead>> = {
|
|
32
|
+
decode: (cache, k, v) => (cache as CommittedAttentionCache).appendDecode(k, v),
|
|
33
|
+
window: (cache, k, v) => (cache as CommittedAttentionCache).appendWindow(k, v),
|
|
34
|
+
committed: (cache, k, v) => (cache as CommittedAttentionCache).appendCommitted(k, v),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** The recurrence each phase names. A committed span is a window for the
|
|
38
|
+
* recurrence. */
|
|
39
|
+
const recurFor: Readonly<Record<ReadPhase,
|
|
40
|
+
(cache: GatedDeltaCache, qkv: MlxArray, a: MlxArray, b: MlxArray, layer: GatedDeltaParameters) => MlxArray>> = {
|
|
41
|
+
decode: (cache, qkv, a, b, layer) => cache.recurDecode(qkv, a, b, layer),
|
|
42
|
+
window: (cache, qkv, a, b, layer) => cache.recurWindow(qkv, a, b, layer),
|
|
43
|
+
committed: (cache, qkv, a, b, layer) => cache.recurWindow(qkv, a, b, layer),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** The projection arithmetic each phase names. A committed span projects each
|
|
47
|
+
* position with one-row arithmetic (`QuantizedLinear`'s independent rows), the
|
|
48
|
+
* bits of appending the span one token at a time; decode and window rows share
|
|
49
|
+
* one matmul. */
|
|
50
|
+
const projectRowsApart: Readonly<Record<ReadPhase, boolean>> = { decode: false, window: false, committed: true };
|
|
51
|
+
|
|
52
|
+
// ── Compiled activations ─────────────────────────────────────────────────────
|
|
53
|
+
// The oracle (mlx_lm/models/activations.py + qwen3_next.py) wraps BOTH swiglu
|
|
54
|
+
// activations in `@partial(mx.compile, shapeless=True)`. We match it: every
|
|
55
|
+
// activation site below (the MLP swiglu, the RMSNormGated `_precise_swiglu`, and
|
|
56
|
+
// the conv `nn.silu`) runs through a compiled closure unconditionally, so the
|
|
57
|
+
// dispatched kernel set matches the oracle op-for-op (= mlx-lm, bit-exact). Traced
|
|
58
|
+
// once (shapeless), replayed thereafter; autograd-safe (mx.compile threads VJP
|
|
59
|
+
// through the traced graph). `compiledSwiglu` lives in layers/swiglu.
|
|
60
|
+
|
|
61
|
+
/** qwen3_next.py `_precise_swiglu(h, gate, x)`:
|
|
62
|
+
* gate = nn.silu(gate.astype(f32)); x = x.astype(f32); (gate*x).astype(h.dtype)
|
|
63
|
+
* Used by Qwen3NextRMSNormGated. Inputs: (h=hidden for the out dtype, gate=z,
|
|
64
|
+
* x=rms_norm(hidden)). mx.compile fuses the two casts + silu + mul + cast. */
|
|
65
|
+
let _preciseSwigluClosure: CompiledFunction | null = null;
|
|
66
|
+
export function compiledPreciseSwiglu(h: MlxArray, gate: MlxArray, x: MlxArray): MlxArray {
|
|
67
|
+
if (!_preciseSwigluClosure) {
|
|
68
|
+
_preciseSwigluClosure = new CompiledFunction((inputs) => {
|
|
69
|
+
const hh = inputs[0]!, g = inputs[1]!, xx = inputs[2]!;
|
|
70
|
+
const gf = g.astype(Dtype.float32);
|
|
71
|
+
const sig = ops.sigmoid(gf);
|
|
72
|
+
const silu = ops.mul(gf, sig); gf.dispose(); sig.dispose();
|
|
73
|
+
const xf = xx.astype(Dtype.float32);
|
|
74
|
+
const prod = ops.mul(silu, xf); silu.dispose(); xf.dispose();
|
|
75
|
+
const out = prod.astype(hh.dtype); prod.dispose();
|
|
76
|
+
return [out];
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return _preciseSwigluClosure.apply([h, gate, x])[0]!;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** mlx.nn.silu is `@partial(mx.compile, shapeless=True) def silu(x): x*sigmoid(x)`
|
|
83
|
+
* — a COMPILED kernel, not a standalone sigmoid+mul. Used for the conv
|
|
84
|
+
* activation `nn.silu(conv1d(...))`, matching mlx-lm's fused BV2ISigmoid…_V_. */
|
|
85
|
+
let _siluClosure: CompiledFunction | null = null;
|
|
86
|
+
export function compiledSilu(x: MlxArray): MlxArray {
|
|
87
|
+
if (!_siluClosure) {
|
|
88
|
+
_siluClosure = new CompiledFunction((inputs) => {
|
|
89
|
+
const xx = inputs[0]!;
|
|
90
|
+
const sig = ops.sigmoid(xx);
|
|
91
|
+
const out = ops.mul(xx, sig); sig.dispose(); // x * sigmoid(x)
|
|
92
|
+
return [out];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return _siluClosure.apply([x])[0]!;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// NOTE: the attention output gate is `self.o_proj(output * mx.sigmoid(gate))`
|
|
99
|
+
// INLINE in mlx-lm (qwen3_next.py) — NOT @mx.compile. It is intentionally kept
|
|
100
|
+
// as a standalone sigmoid + multiply in Qwen3Attention.forward so the dispatched
|
|
101
|
+
// kernel set matches the reference; there is no compiled-output-gate helper.
|
|
102
|
+
|
|
103
|
+
/** A projection the blocks can run: affine-quantized here, dense or quantized
|
|
104
|
+
* in the MTP companion. Only QuantizedLinear reads `independentRows`. */
|
|
105
|
+
export interface AttentionLinear { forward(x: MlxArray, independentRows?: boolean): MlxArray }
|
|
106
|
+
export interface QwenAttentionProjections<L extends AttentionLinear> {
|
|
107
|
+
readonly qProj: L; readonly kProj: L; readonly vProj: L; readonly oProj: L;
|
|
108
|
+
}
|
|
109
|
+
export interface QwenDeltaProjections<L extends AttentionLinear> {
|
|
110
|
+
readonly inProjQkv: L; readonly inProjZ: L; readonly inProjB: L; readonly inProjA: L; readonly outProj: L;
|
|
111
|
+
}
|
|
112
|
+
export type AttentionLinearLoader<L extends AttentionLinear> = (weights: Weights, path: string, config: ModelConfig) => L;
|
|
113
|
+
|
|
114
|
+
/** Gated-DeltaNet linear-attention block (mlx-lm GatedDeltaNet): projections,
|
|
115
|
+
* then the recurrence its cache runs (`GatedDeltaCache`), then the gated norm
|
|
116
|
+
* with z and the output projection. `loadLinear` chooses the projection layer
|
|
117
|
+
* (default QuantizedLinear); the graph is the same. */
|
|
118
|
+
export class GatedDeltaNet<L extends AttentionLinear = QuantizedLinear> {
|
|
119
|
+
/** Per-model seam; null retains the oracle graph (weightless rms_norm, then a
|
|
120
|
+
* scalar multiply). When set, the scale rides in as the norm's weight: MLX's
|
|
121
|
+
* kernel writes `w * T(x * inv)`, the same bf16 product, in one kernel. bf16 only. */
|
|
122
|
+
qkScale: { readonly q: MlxArray; readonly k: MlxArray } | null = null;
|
|
123
|
+
readonly inProjQkv: L;
|
|
124
|
+
readonly inProjZ: L;
|
|
125
|
+
readonly inProjB: L;
|
|
126
|
+
readonly inProjA: L;
|
|
127
|
+
readonly outProj: L;
|
|
128
|
+
readonly convWeight: MlxArray;
|
|
129
|
+
readonly aLog: MlxArray;
|
|
130
|
+
readonly dtBias: MlxArray;
|
|
131
|
+
readonly normWeight: MlxArray;
|
|
132
|
+
readonly eps: number;
|
|
133
|
+
readonly numKHeads: number;
|
|
134
|
+
readonly numVHeads: number;
|
|
135
|
+
readonly headKDim: number;
|
|
136
|
+
readonly headVDim: number;
|
|
137
|
+
readonly keyDim: number;
|
|
138
|
+
readonly valueDim: number;
|
|
139
|
+
readonly convKernel: number;
|
|
140
|
+
/** What this block lends its recurrent cache on every call: its recurrence
|
|
141
|
+
* weights and the glue between the convolution and the recurrence. */
|
|
142
|
+
readonly recurrence: GatedDeltaParameters;
|
|
143
|
+
|
|
144
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string,
|
|
145
|
+
loadLinear: AttentionLinearLoader<L> = QuantizedLinear.load as unknown as AttentionLinearLoader<L>,
|
|
146
|
+
projections?: QwenDeltaProjections<L>) {
|
|
147
|
+
const t = config.text;
|
|
148
|
+
this.numKHeads = t.linearNumKeyHeads;
|
|
149
|
+
this.numVHeads = t.linearNumValueHeads;
|
|
150
|
+
this.headKDim = t.linearKeyHeadDim;
|
|
151
|
+
this.headVDim = t.linearValueHeadDim;
|
|
152
|
+
this.keyDim = this.headKDim * this.numKHeads;
|
|
153
|
+
this.valueDim = this.headVDim * this.numVHeads;
|
|
154
|
+
this.convKernel = t.linearConvKernelDim;
|
|
155
|
+
this.eps = t.rmsNormEps;
|
|
156
|
+
this.inProjQkv = projections?.inProjQkv ?? loadLinear(weights, `${prefix}.in_proj_qkv`, config);
|
|
157
|
+
this.inProjZ = projections?.inProjZ ?? loadLinear(weights, `${prefix}.in_proj_z`, config);
|
|
158
|
+
this.inProjB = projections?.inProjB ?? loadLinear(weights, `${prefix}.in_proj_b`, config);
|
|
159
|
+
this.inProjA = projections?.inProjA ?? loadLinear(weights, `${prefix}.in_proj_a`, config);
|
|
160
|
+
this.outProj = projections?.outProj ?? loadLinear(weights, `${prefix}.out_proj`, config);
|
|
161
|
+
this.convWeight = weights.tensor(`${prefix}.conv1d.weight`);
|
|
162
|
+
this.aLog = weights.tensor(`${prefix}.A_log`);
|
|
163
|
+
this.dtBias = weights.tensor(`${prefix}.dt_bias`);
|
|
164
|
+
this.normWeight = weights.tensor(`${prefix}.norm.weight`);
|
|
165
|
+
this.recurrence = Object.freeze({
|
|
166
|
+
convWeight: this.convWeight, aLog: this.aLog, dtBias: this.dtBias,
|
|
167
|
+
heads: (convolved: MlxArray) => this.heads(convolved),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The glue between the convolution and the recurrence: silu, the split into
|
|
172
|
+
* q, k and v heads, and the q/k norms and scale (inv_scale = head_k_dim ** -0.5;
|
|
173
|
+
* q *= inv_scale², k *= inv_scale), folded into the norm weight when
|
|
174
|
+
* `qkScale` is set. Borrows `convolved`; the cache owns the heads. */
|
|
175
|
+
protected heads(convolved: MlxArray): GatedDeltaHeads {
|
|
176
|
+
const [B, S] = convolved.shape as [number, number, number];
|
|
177
|
+
const convOut = compiledSilu(convolved);
|
|
178
|
+
const [qFlat, kFlat, vFlat] = ops.split(
|
|
179
|
+
convOut, [this.keyDim, 2 * this.keyDim], -1,
|
|
180
|
+
) as [MlxArray, MlxArray, MlxArray];
|
|
181
|
+
convOut.dispose();
|
|
182
|
+
let q = ops.reshape(qFlat, [B, S, this.numKHeads, this.headKDim]);
|
|
183
|
+
qFlat.dispose();
|
|
184
|
+
let k = ops.reshape(kFlat, [B, S, this.numKHeads, this.headKDim]);
|
|
185
|
+
kFlat.dispose();
|
|
186
|
+
const v = disposing(vFlat, ops.reshape(vFlat, [B, S, this.numVHeads, this.headVDim]));
|
|
187
|
+
const invScale = Math.pow(this.headKDim, -0.5);
|
|
188
|
+
const folded = this.qkScale && q.dtype === Dtype.bfloat16 ? this.qkScale : null;
|
|
189
|
+
q = disposing(q, ops.rmsNorm(q, folded?.q ?? null, 1e-6));
|
|
190
|
+
if (!folded) q = disposing(q, ops.mulScalar(q, invScale * invScale));
|
|
191
|
+
k = disposing(k, ops.rmsNorm(k, folded?.k ?? null, 1e-6));
|
|
192
|
+
if (!folded) k = disposing(k, ops.mulScalar(k, invScale));
|
|
193
|
+
return { q, k, v };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** `x` [B, S, hidden] (borrowed) to an owned [B, S, hidden], reading `cache`
|
|
197
|
+
* for `phase`. */
|
|
198
|
+
forward(x: MlxArray, cache: GatedDeltaCache, phase: ReadPhase): MlxArray {
|
|
199
|
+
const [B, S] = x.shape as [number, number, number];
|
|
200
|
+
const rowsApart = projectRowsApart[phase];
|
|
201
|
+
const prof = (globalThis as Record<string, unknown>).__deltaProf as
|
|
202
|
+
Record<string, number>
|
|
203
|
+
| undefined;
|
|
204
|
+
|
|
205
|
+
const t0 = prof ? performance.now() : 0;
|
|
206
|
+
const qkv = this.inProjQkv.forward(x, rowsApart); // [B,S,convDim]
|
|
207
|
+
let z = this.inProjZ.forward(x, rowsApart);
|
|
208
|
+
z = disposing(z, ops.reshape(z, [B, S, this.numVHeads, this.headVDim]));
|
|
209
|
+
const b = this.inProjB.forward(x, rowsApart); // [B,S,numVHeads]
|
|
210
|
+
const a = this.inProjA.forward(x, rowsApart);
|
|
211
|
+
if (prof) { ops.evalAll([qkv, z, b, a]); prof.proj = (prof.proj ?? 0) + performance.now() - t0; }
|
|
212
|
+
const tr = prof ? performance.now() : 0;
|
|
213
|
+
|
|
214
|
+
// The cache consumes qkv, a and b: convolution, glue and recurrence.
|
|
215
|
+
const out = recurFor[phase](cache, qkv, a, b, this.recurrence);
|
|
216
|
+
if (prof) { ops.evalAll([out]); prof.recur = (prof.recur ?? 0) + performance.now() - tr; }
|
|
217
|
+
const to = prof ? performance.now() : 0;
|
|
218
|
+
|
|
219
|
+
// RMSNormGated: silu(z_f32) * rms_norm(out)_f32, cast back to out dtype.
|
|
220
|
+
const gated = this.rmsNormGated(out, z);
|
|
221
|
+
out.dispose();
|
|
222
|
+
z.dispose();
|
|
223
|
+
const merged = ops.reshape(gated, [B, S, this.valueDim]);
|
|
224
|
+
gated.dispose();
|
|
225
|
+
const result = this.outProj.forward(merged, rowsApart);
|
|
226
|
+
merged.dispose();
|
|
227
|
+
if (prof) { ops.evalAll([result]); prof.out = (prof.out ?? 0) + performance.now() - to; }
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
protected rmsNormGated(hidden: MlxArray, gate: MlxArray): MlxArray {
|
|
232
|
+
const xn = ops.rmsNorm(hidden, this.normWeight, this.eps); // bf16
|
|
233
|
+
// Oracle: _precise_swiglu(hidden, gate, xn) — ALWAYS one @mx.compile kernel
|
|
234
|
+
// (matches mlx-lm; no unfused silu+mul+cast).
|
|
235
|
+
const res = compiledPreciseSwiglu(hidden, gate, xn);
|
|
236
|
+
xn.dispose();
|
|
237
|
+
return res;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** How an attention block reads its cache, fixed when the block is built.
|
|
242
|
+
* `read` appends `k` and `v` (borrowed) for `phase` and returns the read the
|
|
243
|
+
* block attends once and disposes. */
|
|
244
|
+
export interface AttentionReadStrategy {
|
|
245
|
+
read(cache: Cache, phase: ReadPhase, k: MlxArray, v: MlxArray): AttentionRead;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The cache's own read for the phase: the default strategy. */
|
|
249
|
+
export const cacheRead: AttentionReadStrategy = Object.freeze({
|
|
250
|
+
read: (cache: Cache, phase: ReadPhase, k: MlxArray, v: MlxArray) => appendFor[phase](cache, k, v),
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
/** Full (softmax) attention with output gate + q/k norm + partial RoPE.
|
|
254
|
+
* `loadLinear` chooses the projection type (the MTP companion loads dense or
|
|
255
|
+
* quantized heads); `reads` is how the block reads its cache (the cache's own
|
|
256
|
+
* read unless a graph injects its kernels). The graph itself is the same. */
|
|
257
|
+
export class Qwen3Attention<L extends AttentionLinear = QuantizedLinear> {
|
|
258
|
+
readonly qProj: L;
|
|
259
|
+
readonly kProj: L;
|
|
260
|
+
readonly vProj: L;
|
|
261
|
+
readonly oProj: L;
|
|
262
|
+
readonly qNorm: RMSNorm;
|
|
263
|
+
readonly kNorm: RMSNorm;
|
|
264
|
+
readonly nHeads: number;
|
|
265
|
+
readonly nKvHeads: number;
|
|
266
|
+
readonly headDim: number;
|
|
267
|
+
readonly scale: number;
|
|
268
|
+
readonly ropeDims: number;
|
|
269
|
+
readonly ropeBase: number;
|
|
270
|
+
|
|
271
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string,
|
|
272
|
+
// The default projection type L is QuantizedLinear (the loader's own type).
|
|
273
|
+
loadLinear: AttentionLinearLoader<L> = QuantizedLinear.load as unknown as AttentionLinearLoader<L>,
|
|
274
|
+
readonly reads: AttentionReadStrategy = cacheRead,
|
|
275
|
+
projections?: QwenAttentionProjections<L>) {
|
|
276
|
+
const t = config.text;
|
|
277
|
+
this.nHeads = t.numAttentionHeads;
|
|
278
|
+
this.nKvHeads = t.numKeyValueHeads;
|
|
279
|
+
this.headDim = t.headDim;
|
|
280
|
+
this.scale = Math.pow(this.headDim, -0.5);
|
|
281
|
+
this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
|
|
282
|
+
this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
|
|
283
|
+
this.qProj = projections?.qProj ?? loadLinear(weights, `${prefix}.q_proj`, config);
|
|
284
|
+
this.kProj = projections?.kProj ?? loadLinear(weights, `${prefix}.k_proj`, config);
|
|
285
|
+
this.vProj = projections?.vProj ?? loadLinear(weights, `${prefix}.v_proj`, config);
|
|
286
|
+
this.oProj = projections?.oProj ?? loadLinear(weights, `${prefix}.o_proj`, config);
|
|
287
|
+
this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
|
|
288
|
+
this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** `x` [B, L, hidden] (borrowed) to an owned [B, L, hidden], appending to
|
|
292
|
+
* `cache` and attending through the read `phase` names. Media forwards
|
|
293
|
+
* supply their own positions in `mrope`. */
|
|
294
|
+
forward(x: MlxArray, cache: Cache, phase: ReadPhase, mrope: MropeForwardState | null = null): MlxArray {
|
|
295
|
+
return mrope ? this.forwardMedia(x, cache, phase, mrope) : this.forwardText(x, cache, phase);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
forwardText(x: MlxArray, cache: Cache, phase: ReadPhase): MlxArray {
|
|
299
|
+
const offArr = cache.ropeOffsetArr;
|
|
300
|
+
const position = offArr
|
|
301
|
+
? (value: MlxArray) => ops.ropeDynamic(value, this.ropeDims, this.ropeBase, offArr, null)
|
|
302
|
+
: (value: MlxArray) => ops.rope(value, this.ropeDims, this.ropeBase, cache.offset, null);
|
|
303
|
+
return this.forwardPositioned(x, cache, phase, position);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
forwardMedia(x: MlxArray, cache: Cache, phase: ReadPhase, mrope: MropeForwardState): MlxArray {
|
|
307
|
+
return this.forwardPositioned(x, cache, phase, value => applyInterleavedRope(value, mrope));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
protected forwardPositioned(x: MlxArray, cache: Cache, phase: ReadPhase, position: (value: MlxArray) => MlxArray): MlxArray {
|
|
311
|
+
const [B, L] = x.shape as [number, number, number];
|
|
312
|
+
const rowsApart = projectRowsApart[phase];
|
|
313
|
+
// Diagnostic sub-phase timing (same switch as GatedDeltaNet): projections
|
|
314
|
+
// and RoPE, cache append and attention, gated output. Barriers only when set.
|
|
315
|
+
const prof = (globalThis as Record<string, unknown>).__deltaProf as Record<string, number> | undefined;
|
|
316
|
+
let tp = prof ? performance.now() : 0;
|
|
317
|
+
const lap = (key: string, arrays: MlxArray[]) => {
|
|
318
|
+
if (!prof) return;
|
|
319
|
+
ops.evalAll(arrays);
|
|
320
|
+
const now = performance.now();
|
|
321
|
+
prof[key] = (prof[key] ?? 0) + now - tp;
|
|
322
|
+
tp = now;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// q_proj emits 2× head_dim per head → split into queries + gate.
|
|
326
|
+
const qp = this.qProj.forward(x, rowsApart);
|
|
327
|
+
const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
|
|
328
|
+
const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
|
|
329
|
+
qpr.dispose();
|
|
330
|
+
const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
|
|
331
|
+
|
|
332
|
+
let k = this.kProj.forward(x, rowsApart);
|
|
333
|
+
let v = this.vProj.forward(x, rowsApart);
|
|
334
|
+
|
|
335
|
+
// q/k norm over head_dim BEFORE transpose (reference order).
|
|
336
|
+
let q = this.qNorm.forward(qHeads);
|
|
337
|
+
qHeads.dispose();
|
|
338
|
+
q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3]));
|
|
339
|
+
k = disposing(k, ops.reshape(k, [B, L, this.nKvHeads, this.headDim]));
|
|
340
|
+
k = disposing(k, this.kNorm.forward(k));
|
|
341
|
+
k = disposing(k, ops.transposeAxes(k, [0, 2, 1, 3]));
|
|
342
|
+
v = disposing(v, ops.reshape(v, [B, L, this.nKvHeads, this.headDim]));
|
|
343
|
+
v = disposing(v, ops.transposeAxes(v, [0, 2, 1, 3]));
|
|
344
|
+
|
|
345
|
+
// Batched decode: the row layout exposes each row's REAL position as
|
|
346
|
+
// ropeOffsetArr (rows have different prompt lengths); the dynamic-offset
|
|
347
|
+
// kernel is the same fast::rope, bit-exact vs the static form
|
|
348
|
+
// (`02d723a:tests/unit/compile.test.ts`). Single rows: scalar offset.
|
|
349
|
+
// Media forwards supply their own 3D interleaved positions. The reference
|
|
350
|
+
// never uses the fused fast-rope kernel when position_ids are supplied,
|
|
351
|
+
// so this IS the oracle's own arithmetic. Text-only stays on ops.rope.
|
|
352
|
+
q = disposing(q, position(q));
|
|
353
|
+
k = disposing(k, position(k));
|
|
354
|
+
|
|
355
|
+
lap("attnProj", [q, k, v, gate]);
|
|
356
|
+
const read = this.reads.read(cache, phase, k, v);
|
|
357
|
+
k.dispose();
|
|
358
|
+
v.dispose();
|
|
359
|
+
let attn: MlxArray;
|
|
360
|
+
try { attn = read.attend(q, this.scale); } finally { read.dispose(); }
|
|
361
|
+
q.dispose();
|
|
362
|
+
lap("attnSdpa", [attn]);
|
|
363
|
+
|
|
364
|
+
const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
|
|
365
|
+
attn.dispose();
|
|
366
|
+
const merged = ops.reshape(attnT, [B, L, -1]);
|
|
367
|
+
attnT.dispose();
|
|
368
|
+
// qwen3_next.py:158 return self.o_proj(output * mx.sigmoid(gate))
|
|
369
|
+
// INLINE, not @mx.compile — copy the exact ops.
|
|
370
|
+
const sig = ops.sigmoid(gate);
|
|
371
|
+
gate.dispose();
|
|
372
|
+
const gated = ops.mul(merged, sig);
|
|
373
|
+
merged.dispose();
|
|
374
|
+
sig.dispose();
|
|
375
|
+
const out = this.oProj.forward(gated, rowsApart);
|
|
376
|
+
gated.dispose();
|
|
377
|
+
lap("attnOut", [out]);
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Text positions are selected by the text phase, never by media state. */
|
|
383
|
+
export class Qwen3TextAttention<L extends AttentionLinear = QuantizedLinear> extends Qwen3Attention<L> {
|
|
384
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, projections: QwenAttentionProjections<L>, reads: AttentionReadStrategy = cacheRead) {
|
|
385
|
+
super(weights, config, prefix, undefined, reads, projections);
|
|
386
|
+
}
|
|
387
|
+
override forward(x: MlxArray, cache: Cache, phase: ReadPhase): MlxArray {
|
|
388
|
+
return this.forwardText(x, cache, phase);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** Media phases bind the interleaved positions variant explicitly. */
|
|
393
|
+
export class Qwen3MediaAttention<L extends AttentionLinear = QuantizedLinear> extends Qwen3Attention<L> {
|
|
394
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, projections: QwenAttentionProjections<L>, reads: AttentionReadStrategy = cacheRead) {
|
|
395
|
+
super(weights, config, prefix, undefined, reads, projections);
|
|
396
|
+
}
|
|
397
|
+
override forward(x: MlxArray, cache: Cache, phase: ReadPhase, mrope: MropeForwardState | null = null): MlxArray {
|
|
398
|
+
return this.forwardMedia(x, cache, phase, mrope!);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Recurrence block with every projection named at its call site. */
|
|
403
|
+
export class QwenGatedDeltaNet<L extends AttentionLinear = QuantizedLinear> extends GatedDeltaNet<L> {
|
|
404
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, projections: QwenDeltaProjections<L>) {
|
|
405
|
+
super(weights, config, prefix, undefined, projections);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** The same recurrence and projections with a fused normalization/gate tail. */
|
|
410
|
+
export class QwenFusedGatedDeltaNet<L extends AttentionLinear = QuantizedLinear> extends QwenGatedDeltaNet<L> {
|
|
411
|
+
readonly #gatedNorm: CompiledGatedRMSNorm;
|
|
412
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, projections: QwenDeltaProjections<L>) {
|
|
413
|
+
super(weights, config, prefix, projections);
|
|
414
|
+
this.#gatedNorm = new CompiledGatedRMSNorm(this.normWeight, this.eps);
|
|
415
|
+
}
|
|
416
|
+
protected override rmsNormGated(hidden: MlxArray, gate: MlxArray): MlxArray {
|
|
417
|
+
return this.#gatedNorm.forward(hidden, gate);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** Fused convolution activation/Q/K norms as well as the normalization/gate tail. */
|
|
422
|
+
export class QwenFusedHeadsDeltaNet<L extends AttentionLinear = QuantizedLinear> extends QwenFusedGatedDeltaNet<L> {
|
|
423
|
+
readonly #headOperation: CompiledSiluRMSHeads;
|
|
424
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, projections: QwenDeltaProjections<L>) {
|
|
425
|
+
super(weights, config, prefix, projections);
|
|
426
|
+
this.#headOperation = new CompiledSiluRMSHeads(this.numKHeads, this.numVHeads, this.headVDim);
|
|
427
|
+
}
|
|
428
|
+
protected override heads(convolved: MlxArray): GatedDeltaHeads {
|
|
429
|
+
if (!this.qkScale) throw new Error("QwenFusedHeadsDeltaNet requires folded Q/K scales");
|
|
430
|
+
const [q, k, v] = this.#headOperation.forward(convolved, this.qkScale.q, this.qkScale.k);
|
|
431
|
+
return { q, k, v };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** The width classes whose rows the fused Trellis gate/up kernel takes (up to `TRELLIS_MATVEC_MAX_M` rows). */
|
|
436
|
+
const FUSED_GATE_UP_WIDTHS: ReadonlySet<number> = new Set<number>([1, 2, 3, 4]);
|
|
437
|
+
|
|
438
|
+
/** The MLP over Trellis-coded projections (a packed-Trellis artifact's
|
|
439
|
+
* `mode: "trellis"` entries). Its input is the post-attention norm's output,
|
|
440
|
+
* which RMSNorm writes as a fresh aligned row-contiguous array: the gate and up
|
|
441
|
+
* projections take that layout as given, so the Trellis prefill matches the
|
|
442
|
+
* native small-prefill matmul without an evaluation. */
|
|
443
|
+
export class TrellisMlp {
|
|
444
|
+
readonly kind = "trellis";
|
|
445
|
+
/** Gate and up share the geometry the fused gate/up kernel takes. */
|
|
446
|
+
readonly #fusedGateUp: boolean;
|
|
447
|
+
|
|
448
|
+
constructor(readonly gate: TrellisLinear, readonly up: TrellisLinear, readonly down: TrellisLinear) {
|
|
449
|
+
this.#fusedGateUp = fusedGateUpEligible(gate, up);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** `x` [.., hidden] (borrowed, row-contiguous) to an owned [.., hidden].
|
|
453
|
+
* Every phase computes the same rows; `width` is the class of `x`'s rows,
|
|
454
|
+
* named by the caller. */
|
|
455
|
+
forward(x: MlxArray, _phase: ReadPhase, width: number): MlxArray {
|
|
456
|
+
// Packed-trellis decode (up to four rows): gate, up and the swiglu in ONE
|
|
457
|
+
// kernel — x read once, no gate/up vectors materialized. This Lab path
|
|
458
|
+
// retains the packed activation arithmetic documented in
|
|
459
|
+
// `02d723a:docs/design/turboquant.md`.
|
|
460
|
+
if (this.#fusedGateUp && FUSED_GATE_UP_WIDTHS.has(width)) {
|
|
461
|
+
const hidden = fusedGateUpSwiglu(x, this.gate, this.up);
|
|
462
|
+
const out = this.down.forward(hidden);
|
|
463
|
+
hidden.dispose();
|
|
464
|
+
return out;
|
|
465
|
+
}
|
|
466
|
+
const g = this.gate.forward(x, true);
|
|
467
|
+
const u = this.up.forward(x, true);
|
|
468
|
+
// Oracle: down_proj(swiglu(gate_proj(x), up_proj(x))) — swiglu ALWAYS compiled
|
|
469
|
+
// (mlx-lm's @mx.compile swiglu; no unfused silu+mul, matches its kernel set).
|
|
470
|
+
const hidden = compiledSwiglu(g, u);
|
|
471
|
+
g.dispose();
|
|
472
|
+
u.dispose();
|
|
473
|
+
const out = this.down.forward(hidden);
|
|
474
|
+
hidden.dispose();
|
|
475
|
+
return out;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** The MLP over affine-quantized projections. */
|
|
480
|
+
export class AffineMlp {
|
|
481
|
+
readonly kind = "affine";
|
|
482
|
+
|
|
483
|
+
constructor(readonly gate: QuantizedLinear, readonly up: QuantizedLinear, readonly down: QuantizedLinear) {}
|
|
484
|
+
|
|
485
|
+
/** `x` [.., hidden] (borrowed) to an owned [.., hidden], with the projection
|
|
486
|
+
* arithmetic `phase` names; every width class takes the same kernels. */
|
|
487
|
+
forward(x: MlxArray, phase: ReadPhase, _width: number): MlxArray {
|
|
488
|
+
const rowsApart = projectRowsApart[phase];
|
|
489
|
+
const g = this.gate.forward(x, rowsApart);
|
|
490
|
+
const u = this.up.forward(x, rowsApart);
|
|
491
|
+
// Oracle: down_proj(swiglu(gate_proj(x), up_proj(x))) — swiglu ALWAYS compiled.
|
|
492
|
+
const hidden = compiledSwiglu(g, u);
|
|
493
|
+
g.dispose();
|
|
494
|
+
u.dispose();
|
|
495
|
+
const out = this.down.forward(hidden, rowsApart);
|
|
496
|
+
hidden.dispose();
|
|
497
|
+
return out;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export type QwenMlp = TrellisMlp | AffineMlp;
|
|
502
|
+
|
|
503
|
+
/** The MLP block the written-down quant table names for `prefix`: Trellis when
|
|
504
|
+
* its gate, up and down entries are `mode: "trellis"`, affine when none is.
|
|
505
|
+
* A table that mixes the two in one MLP has no block here and is refused. */
|
|
506
|
+
export function loadQwenMlp(weights: Weights, config: ModelConfig, prefix: string, representations?: import("../../kernels/trellis/index").TrellisRepresentations): QwenMlp {
|
|
507
|
+
const paths = [`${prefix}.gate_proj`, `${prefix}.up_proj`, `${prefix}.down_proj`] as const;
|
|
508
|
+
const trellis = paths.map(path => TrellisLinear.isTrellis(config, path));
|
|
509
|
+
if (trellis.every(Boolean)) {
|
|
510
|
+
// M4 Pro measurements support the 27B down projection at M3/4. The
|
|
511
|
+
// operation also checks variant, dtype and packed-code layout eligibility.
|
|
512
|
+
const sharedScatterCodebook = config.text.hiddenSize === 5120 && config.text.intermediateSize === 17408;
|
|
513
|
+
return new TrellisMlp(TrellisLinear.load(weights, paths[0], config, false, representations), TrellisLinear.load(weights, paths[1], config, false, representations),
|
|
514
|
+
TrellisLinear.load(weights, paths[2], config, sharedScatterCodebook, representations));
|
|
515
|
+
}
|
|
516
|
+
if (!trellis.some(Boolean))
|
|
517
|
+
return new AffineMlp(QuantizedLinear.load(weights, paths[0], config), QuantizedLinear.load(weights, paths[1], config),
|
|
518
|
+
QuantizedLinear.load(weights, paths[2], config));
|
|
519
|
+
throw new Error(`${prefix}: the quant table codes ${paths.filter((_, i) => trellis[i]).join(", ")} as Trellis and ` +
|
|
520
|
+
"the rest affine; a Qwen 3.5 MLP block takes all three projections in one representation");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export class Qwen3Layer {
|
|
524
|
+
readonly isLinear: boolean;
|
|
525
|
+
readonly linearAttn: GatedDeltaNet | null = null;
|
|
526
|
+
readonly selfAttn: Qwen3Attention | null = null;
|
|
527
|
+
readonly mlp: QwenMlp;
|
|
528
|
+
readonly inputNorm: RMSNorm;
|
|
529
|
+
readonly postAttnNorm: RMSNorm;
|
|
530
|
+
|
|
531
|
+
constructor(weights: Weights, config: ModelConfig, layerIdx: number, representations?: import("../../kernels/trellis/index").TrellisRepresentations) {
|
|
532
|
+
const prefix = `${PREFIX}.model.layers.${layerIdx}`;
|
|
533
|
+
this.isLinear = (layerIdx + 1) % config.text.fullAttentionInterval !== 0;
|
|
534
|
+
if (this.isLinear)
|
|
535
|
+
this.linearAttn = new GatedDeltaNet(weights, config, `${prefix}.linear_attn`);
|
|
536
|
+
else this.selfAttn = new Qwen3Attention(weights, config, `${prefix}.self_attn`);
|
|
537
|
+
this.mlp = loadQwenMlp(weights, config, `${prefix}.mlp`, representations);
|
|
538
|
+
this.inputNorm = new RMSNorm(weights.tensor(`${prefix}.input_layernorm.weight`), config.text.rmsNormEps);
|
|
539
|
+
this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** One layer over `x` [.., hidden] (borrowed) for `phase`; `width` is the class of `x`'s rows, which picks the
|
|
543
|
+
* MLP's kernels. */
|
|
544
|
+
forward(x: MlxArray, cache: Cache, phase: ReadPhase, mrope: MropeForwardState | null, width: number): MlxArray {
|
|
545
|
+
using h = this.forwardAttn(x, cache, phase, mrope);
|
|
546
|
+
return this.forwardMlp(h, phase, width);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
forwardAttn(x: MlxArray, cache: Cache, phase: ReadPhase, mrope: MropeForwardState | null = null): MlxArray {
|
|
550
|
+
using xn = this.inputNorm.forward(x);
|
|
551
|
+
using r = this.isLinear
|
|
552
|
+
? this.linearAttn!.forward(xn, cache as GatedDeltaCache, phase)
|
|
553
|
+
: this.selfAttn!.forward(xn, cache, phase, mrope);
|
|
554
|
+
return ops.add(x, r);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
forwardMlp(h: MlxArray, phase: ReadPhase, width: number): MlxArray {
|
|
558
|
+
using hn = this.postAttnNorm.forward(h);
|
|
559
|
+
// Diagnostic (same switch as the attention/DeltaNet sub-phases): settle the
|
|
560
|
+
// pending attention block first so the MLP is timed alone.
|
|
561
|
+
const prof = (globalThis as Record<string, unknown>).__deltaProf as Record<string, number> | undefined;
|
|
562
|
+
let t0 = 0;
|
|
563
|
+
if (prof) { ops.evalAll([hn]); t0 = performance.now(); }
|
|
564
|
+
// RMSNorm allocates an aligned row-contiguous output, the layout the
|
|
565
|
+
// Trellis MLP takes as given.
|
|
566
|
+
using m = this.mlp.forward(hn, phase, width);
|
|
567
|
+
if (prof) { ops.evalAll([m]); prof.mlp = (prof.mlp ?? 0) + performance.now() - t0; }
|
|
568
|
+
return ops.add(h, m);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/** A recurrent decoder layer whose mixer and MLP are named by its graph. */
|
|
573
|
+
export class QwenDeltaLayer {
|
|
574
|
+
readonly inputNorm: RMSNorm;
|
|
575
|
+
readonly postAttnNorm: RMSNorm;
|
|
576
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string,
|
|
577
|
+
readonly mixer: { forward(x: MlxArray, cache: GatedDeltaCache, phase: ReadPhase): MlxArray },
|
|
578
|
+
readonly mlp: { forward(x: MlxArray): MlxArray }) {
|
|
579
|
+
this.inputNorm = new RMSNorm(weights.tensor(`${prefix}.input_layernorm.weight`), config.text.rmsNormEps);
|
|
580
|
+
this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
|
|
581
|
+
}
|
|
582
|
+
forwardAttn(x: MlxArray, cache: Cache, phase: ReadPhase): MlxArray {
|
|
583
|
+
using normed = this.inputNorm.forward(x);
|
|
584
|
+
using mixed = this.mixer.forward(normed, cache as GatedDeltaCache, phase);
|
|
585
|
+
return ops.add(x, mixed);
|
|
586
|
+
}
|
|
587
|
+
forwardMlp(x: MlxArray): MlxArray {
|
|
588
|
+
using normed = this.postAttnNorm.forward(x);
|
|
589
|
+
using hidden = this.mlp.forward(normed);
|
|
590
|
+
return ops.add(x, hidden);
|
|
591
|
+
}
|
|
592
|
+
forward(x: MlxArray, cache: Cache, phase: ReadPhase): MlxArray {
|
|
593
|
+
using hidden = this.forwardAttn(x, cache, phase);
|
|
594
|
+
return this.forwardMlp(hidden);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Full-attention decoder layer; text and media mixers are separate bindings. */
|
|
599
|
+
export class QwenAttentionLayer {
|
|
600
|
+
readonly inputNorm: RMSNorm;
|
|
601
|
+
readonly postAttnNorm: RMSNorm;
|
|
602
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string,
|
|
603
|
+
readonly mixer: { forward(x: MlxArray, cache: Cache, phase: ReadPhase, positions?: MropeForwardState | null): MlxArray },
|
|
604
|
+
readonly mlp: { forward(x: MlxArray): MlxArray }) {
|
|
605
|
+
this.inputNorm = new RMSNorm(weights.tensor(`${prefix}.input_layernorm.weight`), config.text.rmsNormEps);
|
|
606
|
+
this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
|
|
607
|
+
}
|
|
608
|
+
forwardAttn(x: MlxArray, cache: Cache, phase: ReadPhase, positions: MropeForwardState | null = null): MlxArray {
|
|
609
|
+
using normed = this.inputNorm.forward(x);
|
|
610
|
+
using mixed = this.mixer.forward(normed, cache, phase, positions);
|
|
611
|
+
return ops.add(x, mixed);
|
|
612
|
+
}
|
|
613
|
+
forwardMlp(x: MlxArray): MlxArray {
|
|
614
|
+
using normed = this.postAttnNorm.forward(x);
|
|
615
|
+
using hidden = this.mlp.forward(normed);
|
|
616
|
+
return ops.add(x, hidden);
|
|
617
|
+
}
|
|
618
|
+
forward(x: MlxArray, cache: Cache, phase: ReadPhase, positions: MropeForwardState | null = null): MlxArray {
|
|
619
|
+
using hidden = this.forwardAttn(x, cache, phase, positions);
|
|
620
|
+
return this.forwardMlp(hidden);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** A residual/normalization kernel between the recurrent mixer and its MLP. */
|
|
625
|
+
export class QwenResidualDeltaLayer extends QwenDeltaLayer {
|
|
626
|
+
readonly #residualNorm: CompiledResidualRMSNorm;
|
|
627
|
+
constructor(...args: ConstructorParameters<typeof QwenDeltaLayer>) {
|
|
628
|
+
super(...args);
|
|
629
|
+
this.#residualNorm = new CompiledResidualRMSNorm(this.postAttnNorm.weight!, this.postAttnNorm.eps);
|
|
630
|
+
}
|
|
631
|
+
override forward(x: MlxArray, cache: Cache, phase: ReadPhase): MlxArray {
|
|
632
|
+
using normed = this.inputNorm.forward(x);
|
|
633
|
+
using mixed = this.mixer.forward(normed, cache as GatedDeltaCache, phase);
|
|
634
|
+
const [residual, normalized] = this.#residualNorm.forward(x, mixed);
|
|
635
|
+
try {
|
|
636
|
+
using hidden = this.mlp.forward(normalized);
|
|
637
|
+
return ops.add(residual, hidden);
|
|
638
|
+
} finally { residual.dispose(); normalized.dispose(); }
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** The corresponding residual/normalization kernel after full attention. */
|
|
643
|
+
export class QwenResidualAttentionLayer extends QwenAttentionLayer {
|
|
644
|
+
readonly #residualNorm: CompiledResidualRMSNorm;
|
|
645
|
+
constructor(...args: ConstructorParameters<typeof QwenAttentionLayer>) {
|
|
646
|
+
super(...args);
|
|
647
|
+
this.#residualNorm = new CompiledResidualRMSNorm(this.postAttnNorm.weight!, this.postAttnNorm.eps);
|
|
648
|
+
}
|
|
649
|
+
override forward(x: MlxArray, cache: Cache, phase: ReadPhase, positions: MropeForwardState | null = null): MlxArray {
|
|
650
|
+
using normed = this.inputNorm.forward(x);
|
|
651
|
+
using mixed = this.mixer.forward(normed, cache, phase, positions);
|
|
652
|
+
const [residual, normalized] = this.#residualNorm.forward(x, mixed);
|
|
653
|
+
try {
|
|
654
|
+
using hidden = this.mlp.forward(normalized);
|
|
655
|
+
return ops.add(residual, hidden);
|
|
656
|
+
} finally { residual.dispose(); normalized.dispose(); }
|
|
657
|
+
}
|
|
658
|
+
}
|