@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,566 @@
|
|
|
1
|
+
// Qwen3-VL vision tower (Qwen3.8's `vision_tower`) — faithful port
|
|
2
|
+
// of mlx-vlm qwen3_vl/vision.py at the qwen3_5 configuration: depth 27,
|
|
3
|
+
// hidden 1152, heads 16 (head_dim 72), patch 16, temporal 2, merge 2,
|
|
4
|
+
// intermediate 4304 with gelu_pytorch_tanh, learned 48×48 pos-embed grid
|
|
5
|
+
// bilinearly interpolated, 2D half/half rotary (dim 36) over full head_dim,
|
|
6
|
+
// pre-LN blocks with biased LayerNorms, merger LayerNorm→fc1→GELU(erf)→fc2
|
|
7
|
+
// to out_hidden 5120. deepstack is EMPTY for qwen3_5 and not ported.
|
|
8
|
+
//
|
|
9
|
+
// Weights: the artifact ships the tower as a bf16 sidecar
|
|
10
|
+
// (optiq/optiq_vision.safetensors, 333 tensors, MLX conv layout). The
|
|
11
|
+
// Conv3d patch embed has kernel == stride, so it is computed as one matmul
|
|
12
|
+
// against the weight flattened to the preprocessor's (c, t, py, px) row
|
|
13
|
+
// order — same values, GEMM accumulation (gate: tower output vs the mlx-vlm
|
|
14
|
+
// capture; e2e tokens are the decisive gate, the SigLIP precedent).
|
|
15
|
+
//
|
|
16
|
+
// Input rows arrive MERGE-BLOCK-MAJOR from qwen3vl-preprocess.ts; rope
|
|
17
|
+
// h/w positions and the pos-embed permutation are built in the same order.
|
|
18
|
+
|
|
19
|
+
import { ptr, read } from "bun:ffi";
|
|
20
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
+
import { tensorFingerprint } from "../../artifacts/fingerprint";
|
|
22
|
+
import { MlxArray, cpuStream } from "@mlx-bun/mlx/array";
|
|
23
|
+
import { C, Dtype } from "@mlx-bun/mlx/ffi";
|
|
24
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
25
|
+
import type { Qwen3VLPreprocessed } from "../../input/vision/qwen3vl-preprocess";
|
|
26
|
+
import {
|
|
27
|
+
QWEN3VL_MERGE_SIZE,
|
|
28
|
+
QWEN3VL_PATCH_SIZE,
|
|
29
|
+
QWEN3VL_TEMPORAL_PATCH_SIZE,
|
|
30
|
+
} from "../../input/vision/qwen3vl-preprocess";
|
|
31
|
+
|
|
32
|
+
const cstr = (s: string) => Buffer.from(s + "\0", "utf8");
|
|
33
|
+
|
|
34
|
+
const DEPTH = 27;
|
|
35
|
+
const HIDDEN = 1152;
|
|
36
|
+
const HEADS = 16;
|
|
37
|
+
const HEAD_DIM = HIDDEN / HEADS; // 72
|
|
38
|
+
const ROT_DIM = HEAD_DIM / 2; // 36 (VisionRotaryEmbedding dim)
|
|
39
|
+
const INTERMEDIATE = 4304;
|
|
40
|
+
const OUT_HIDDEN = 5120;
|
|
41
|
+
const POS_GRID = 48; // sqrt(num_position_embeddings 2304)
|
|
42
|
+
const ROPE_THETA = 10000;
|
|
43
|
+
|
|
44
|
+
function disposing(old: MlxArray, next: MlxArray): MlxArray {
|
|
45
|
+
old.dispose();
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface Block {
|
|
50
|
+
norm1W: MlxArray; norm1B: MlxArray;
|
|
51
|
+
norm2W: MlxArray; norm2B: MlxArray;
|
|
52
|
+
qkvW: MlxArray; qkvB: MlxArray;
|
|
53
|
+
projW: MlxArray; projB: MlxArray;
|
|
54
|
+
fc1W: MlxArray; fc1B: MlxArray;
|
|
55
|
+
fc2W: MlxArray; fc2B: MlxArray;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class Qwen3VLVisionTower {
|
|
59
|
+
#identity: string | undefined;
|
|
60
|
+
readonly #identityWeights: Array<[string, MlxArray]>;
|
|
61
|
+
|
|
62
|
+
/** Computed once, under native preparation. Exact weight content prevents
|
|
63
|
+
* cross-artifact SSD reuse even when model aliases and shapes are identical. */
|
|
64
|
+
get cacheIdentity(): string {
|
|
65
|
+
if (this.#identity) return this.#identity;
|
|
66
|
+
return this.#identity = tensorFingerprint(this.#identityWeights);
|
|
67
|
+
}
|
|
68
|
+
#patchW: MlxArray; // [1536, 1152] — flattened conv, already transposed
|
|
69
|
+
#patchB: MlxArray;
|
|
70
|
+
#posEmbed: MlxArray; // [2304, 1152]
|
|
71
|
+
#blocks: Block[];
|
|
72
|
+
#mergerNormW: MlxArray; #mergerNormB: MlxArray;
|
|
73
|
+
#mergerFc1W: MlxArray; #mergerFc1B: MlxArray;
|
|
74
|
+
#mergerFc2W: MlxArray; #mergerFc2B: MlxArray;
|
|
75
|
+
|
|
76
|
+
private constructor(w: Map<string, MlxArray>) {
|
|
77
|
+
this.#identityWeights = [...w];
|
|
78
|
+
const t = (n: string): MlxArray => {
|
|
79
|
+
const a = w.get(`vision_tower.${n}`);
|
|
80
|
+
if (!a) throw new Error(`vision sidecar missing tensor vision_tower.${n}`);
|
|
81
|
+
return a;
|
|
82
|
+
};
|
|
83
|
+
// Conv3d weight [1152, 2, 16, 16, 3] (out, kT, kH, kW, in) — used with
|
|
84
|
+
// the REAL conv3d op (kernel == stride): a value-equivalent GEMM rounds
|
|
85
|
+
// differently in bf16 and the ulp seeds diverge through 27 blocks.
|
|
86
|
+
this.#patchW = t("patch_embed.proj.weight");
|
|
87
|
+
this.#patchB = t("patch_embed.proj.bias");
|
|
88
|
+
this.#posEmbed = t("pos_embed.weight");
|
|
89
|
+
this.#blocks = [];
|
|
90
|
+
for (let i = 0; i < DEPTH; i++) {
|
|
91
|
+
const p = `blocks.${i}`;
|
|
92
|
+
this.#blocks.push({
|
|
93
|
+
norm1W: t(`${p}.norm1.weight`), norm1B: t(`${p}.norm1.bias`),
|
|
94
|
+
norm2W: t(`${p}.norm2.weight`), norm2B: t(`${p}.norm2.bias`),
|
|
95
|
+
qkvW: t(`${p}.attn.qkv.weight`), qkvB: t(`${p}.attn.qkv.bias`),
|
|
96
|
+
projW: t(`${p}.attn.proj.weight`), projB: t(`${p}.attn.proj.bias`),
|
|
97
|
+
fc1W: t(`${p}.mlp.linear_fc1.weight`), fc1B: t(`${p}.mlp.linear_fc1.bias`),
|
|
98
|
+
fc2W: t(`${p}.mlp.linear_fc2.weight`), fc2B: t(`${p}.mlp.linear_fc2.bias`),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
this.#mergerNormW = t("merger.norm.weight");
|
|
102
|
+
this.#mergerNormB = t("merger.norm.bias");
|
|
103
|
+
this.#mergerFc1W = t("merger.linear_fc1.weight");
|
|
104
|
+
this.#mergerFc1B = t("merger.linear_fc1.bias");
|
|
105
|
+
this.#mergerFc2W = t("merger.linear_fc2.weight");
|
|
106
|
+
this.#mergerFc2B = t("merger.linear_fc2.bias");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The 333 tensor names the tower models, by constructed name. */
|
|
110
|
+
static #tensorNames(): string[] {
|
|
111
|
+
const names: string[] = [
|
|
112
|
+
"vision_tower.patch_embed.proj.weight", "vision_tower.patch_embed.proj.bias",
|
|
113
|
+
"vision_tower.pos_embed.weight",
|
|
114
|
+
"vision_tower.merger.norm.weight", "vision_tower.merger.norm.bias",
|
|
115
|
+
"vision_tower.merger.linear_fc1.weight", "vision_tower.merger.linear_fc1.bias",
|
|
116
|
+
"vision_tower.merger.linear_fc2.weight", "vision_tower.merger.linear_fc2.bias",
|
|
117
|
+
];
|
|
118
|
+
for (let i = 0; i < DEPTH; i++) {
|
|
119
|
+
for (const nm of [
|
|
120
|
+
"norm1.weight", "norm1.bias", "norm2.weight", "norm2.bias",
|
|
121
|
+
"attn.qkv.weight", "attn.qkv.bias", "attn.proj.weight", "attn.proj.bias",
|
|
122
|
+
"mlp.linear_fc1.weight", "mlp.linear_fc1.bias",
|
|
123
|
+
"mlp.linear_fc2.weight", "mlp.linear_fc2.bias",
|
|
124
|
+
]) names.push(`vision_tower.blocks.${i}.${nm}`);
|
|
125
|
+
}
|
|
126
|
+
return names;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Checkpoint-generation naming for the tower. Pre-5.8 artifacts store
|
|
130
|
+
* `vision_tower.*`; the transformers-5.8 export stores `model.visual.*`
|
|
131
|
+
* (mlx-lm's text-only qwen3_5 drops BOTH prefixes in `Model.sanitize`, so
|
|
132
|
+
* the mapping between them is ours). Verified 2026-09-01 across
|
|
133
|
+
* Qwen3.8-27B-tqalloc-norot vs the staged pre-5.8 artifact: 331/333
|
|
134
|
+
* tensors byte-identical under the rename, patch_embed.proj.weight
|
|
135
|
+
* identical under the layout permutation below, and merger.linear_fc2
|
|
136
|
+
* differs only because the staged artifact is rotation-folded. */
|
|
137
|
+
static #sourceName(canonical: string, prefix: string): string {
|
|
138
|
+
return prefix + canonical.slice("vision_tower.".length);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** HF stores the Conv3d patch embed as (out, in, kT, kH, kW); MLX (and this
|
|
142
|
+
* tower) want (out, kT, kH, kW, in). Proven a pure permutation on the
|
|
143
|
+
* artifacts above — transpose(0,2,3,4,1) of the 5.8 tensor is bit-equal to
|
|
144
|
+
* the pre-5.8 one. */
|
|
145
|
+
static #fixPatchEmbed(w: Map<string, MlxArray>): void {
|
|
146
|
+
const key = "vision_tower.patch_embed.proj.weight";
|
|
147
|
+
const a = w.get(key);
|
|
148
|
+
if (!a || a.shape.length !== 5) return;
|
|
149
|
+
if (a.shape[4] === 3) return; // already MLX layout
|
|
150
|
+
const t = ops.transposeAxes(a, [0, 2, 3, 4, 1]);
|
|
151
|
+
const c = ops.contiguous(t);
|
|
152
|
+
t.dispose();
|
|
153
|
+
a.dispose();
|
|
154
|
+
w.set(key, c);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Load one safetensors file's map and move `wanted` tensors into `out`
|
|
158
|
+
* under their CANONICAL (`vision_tower.*`) names; `source` maps each
|
|
159
|
+
* canonical name to the stored one. Missing names are skipped; the map is
|
|
160
|
+
* always freed. */
|
|
161
|
+
static #takeFromFile(
|
|
162
|
+
path: string, wanted: string[], out: Map<string, MlxArray>,
|
|
163
|
+
source: (canonical: string) => string = (n) => n,
|
|
164
|
+
): void {
|
|
165
|
+
const arrMap = new BigUint64Array([C.mlx_map_string_to_array_new()]);
|
|
166
|
+
const metaMap = new BigUint64Array([C.mlx_map_string_to_string_new()]);
|
|
167
|
+
const arrMapPtr = ptr(arrMap);
|
|
168
|
+
const metaMapPtr = ptr(metaMap);
|
|
169
|
+
const status = C.mlx_load_safetensors(arrMapPtr, metaMapPtr, ptr(cstr(path)), cpuStream);
|
|
170
|
+
C.mlx_map_string_to_string_free(read.u64(metaMapPtr, 0));
|
|
171
|
+
const handle = read.u64(arrMapPtr, 0);
|
|
172
|
+
if (status !== 0) {
|
|
173
|
+
// The out-param map was allocated regardless — free it on the error
|
|
174
|
+
// path too (2026-08-18 review; one-shot leak, but a leak).
|
|
175
|
+
C.mlx_map_string_to_array_free(handle);
|
|
176
|
+
throw new Error(`failed to load ${path}`);
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
for (const name of wanted) {
|
|
180
|
+
if (out.has(name)) continue;
|
|
181
|
+
const slot = new BigUint64Array([C.mlx_array_new()]);
|
|
182
|
+
const slotPtr = ptr(slot);
|
|
183
|
+
if (C.mlx_map_string_to_array_get(slotPtr, handle, ptr(cstr(source(name)))) !== 0) {
|
|
184
|
+
C.mlx_array_free(read.u64(slotPtr, 0));
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
out.set(name, new MlxArray(read.u64(slotPtr, 0)));
|
|
188
|
+
}
|
|
189
|
+
} finally {
|
|
190
|
+
C.mlx_map_string_to_array_free(handle);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static load(modelDir: string): Qwen3VLVisionTower {
|
|
195
|
+
const names = Qwen3VLVisionTower.#tensorNames();
|
|
196
|
+
const weights = new Map<string, MlxArray>();
|
|
197
|
+
try {
|
|
198
|
+
const sidecar = `${modelDir}/optiq/optiq_vision.safetensors`;
|
|
199
|
+
if (existsSync(sidecar)) {
|
|
200
|
+
// OptiQ-convention sidecar (their artifacts + ours pre-2026-08-18).
|
|
201
|
+
Qwen3VLVisionTower.#takeFromFile(sidecar, names, weights);
|
|
202
|
+
} else {
|
|
203
|
+
// In-main fallback: the bf16 tower tensors live in the main
|
|
204
|
+
// safetensors shards (the mlx-vlm convention — one copy serves both
|
|
205
|
+
// ecosystems; sidecar duplication retired 2026-08-18). The 5.8-family
|
|
206
|
+
// export names them `model.visual.*` instead of `vision_tower.*`.
|
|
207
|
+
const idxPath = `${modelDir}/model.safetensors.index.json`;
|
|
208
|
+
let files: string[];
|
|
209
|
+
let prefixes = ["vision_tower.", "model.visual."];
|
|
210
|
+
if (existsSync(idxPath)) {
|
|
211
|
+
const idx = JSON.parse(readFileSync(idxPath, "utf8")) as
|
|
212
|
+
{ weight_map: Record<string, string> };
|
|
213
|
+
prefixes = prefixes.filter((p) =>
|
|
214
|
+
Qwen3VLVisionTower.#sourceName(names[0]!, p) in idx.weight_map);
|
|
215
|
+
files = [...new Set(prefixes.flatMap((p) => names
|
|
216
|
+
.map((n) => idx.weight_map[Qwen3VLVisionTower.#sourceName(n, p)])
|
|
217
|
+
.filter((f): f is string => !!f)))];
|
|
218
|
+
if (files.length === 0)
|
|
219
|
+
throw new Error(`no vision tower tensors in ${modelDir} (index has none)`);
|
|
220
|
+
} else {
|
|
221
|
+
files = ["model.safetensors"];
|
|
222
|
+
}
|
|
223
|
+
for (const f of files)
|
|
224
|
+
for (const p of prefixes)
|
|
225
|
+
Qwen3VLVisionTower.#takeFromFile(
|
|
226
|
+
`${modelDir}/${f}`, names, weights,
|
|
227
|
+
(n) => Qwen3VLVisionTower.#sourceName(n, p),
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
for (const name of names)
|
|
231
|
+
if (!weights.has(name))
|
|
232
|
+
throw new Error(`qwen vision weights missing tensor ${name} (sidecar or in-main)`);
|
|
233
|
+
Qwen3VLVisionTower.#fixPatchEmbed(weights);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
for (const [, a] of weights) a.dispose();
|
|
236
|
+
throw e;
|
|
237
|
+
}
|
|
238
|
+
return new Qwen3VLVisionTower(weights);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** One vision block: pre-LN attention (+residual), pre-LN MLP (+residual).
|
|
242
|
+
* Consumes `h`. */
|
|
243
|
+
#block(
|
|
244
|
+
h: MlxArray, blk: Block, cos: MlxArray, sin: MlxArray,
|
|
245
|
+
scale: number, n: number, segLen: number = n,
|
|
246
|
+
): MlxArray {
|
|
247
|
+
const xn = ops.layerNorm(h, blk.norm1W, blk.norm1B, 1e-6);
|
|
248
|
+
const qkv = this.#linear(xn, blk.qkvW, blk.qkvB); // [n, 3456]
|
|
249
|
+
xn.dispose();
|
|
250
|
+
const qkv4 = disposing(qkv, ops.reshape(qkv, [n, 3, HEADS, HEAD_DIM]));
|
|
251
|
+
const [qs, ks, vs] = ops.split(qkv4, [1, 2], 1) as [MlxArray, MlxArray, MlxArray];
|
|
252
|
+
qkv4.dispose();
|
|
253
|
+
let q = disposing(qs, ops.reshape(qs, [n, HEADS, HEAD_DIM]));
|
|
254
|
+
let k = disposing(ks, ops.reshape(ks, [n, HEADS, HEAD_DIM]));
|
|
255
|
+
const v = disposing(vs, ops.reshape(vs, [n, HEADS, HEAD_DIM]));
|
|
256
|
+
q = disposing(q, this.#applyRope(q, cos, sin));
|
|
257
|
+
k = disposing(k, this.#applyRope(k, cos, sin));
|
|
258
|
+
// [1, HEADS, n, HEAD_DIM] full (non-causal) attention — one image =
|
|
259
|
+
// one cu_seqlens segment, so no mask. The reference's ensure_fused_sdpa
|
|
260
|
+
// ZERO-PADS head_dim 72 → 80 (nearest fused-kernel width), runs fused
|
|
261
|
+
// sdpa there, and slices back; the padded kernel's accumulation is the
|
|
262
|
+
// parity-relevant arithmetic, so replicate it exactly.
|
|
263
|
+
const PAD_DIM = 80;
|
|
264
|
+
const padTo = (x: MlxArray): MlxArray => {
|
|
265
|
+
const z = ops.zeros([n, HEADS, PAD_DIM - HEAD_DIM], Dtype.bfloat16);
|
|
266
|
+
const p = ops.concatAxis([x, z], -1);
|
|
267
|
+
z.dispose();
|
|
268
|
+
return disposing(x, p);
|
|
269
|
+
};
|
|
270
|
+
const lift = (x: MlxArray): MlxArray => {
|
|
271
|
+
const p = padTo(x); // consumes x
|
|
272
|
+
const tr = ops.transposeAxes(p, [1, 0, 2]);
|
|
273
|
+
p.dispose();
|
|
274
|
+
return disposing(tr, ops.expandDims(tr, 0));
|
|
275
|
+
};
|
|
276
|
+
const qT = lift(q);
|
|
277
|
+
const kT = lift(k);
|
|
278
|
+
const vT = lift(v);
|
|
279
|
+
// cu_seqlens split: one sdpa per temporal segment (single segment for
|
|
280
|
+
// images), outputs concatenated in order — the reference's exact walk.
|
|
281
|
+
let attnP: MlxArray;
|
|
282
|
+
if (segLen === n) {
|
|
283
|
+
attnP = ops.sdpa(qT, kT, vT, scale, "", null);
|
|
284
|
+
} else {
|
|
285
|
+
const outs: MlxArray[] = [];
|
|
286
|
+
for (let s = 0; s < n; s += segLen) {
|
|
287
|
+
const qs = qT.slice([0, 0, s, 0], [1, HEADS, s + segLen, PAD_DIM]);
|
|
288
|
+
const ks = kT.slice([0, 0, s, 0], [1, HEADS, s + segLen, PAD_DIM]);
|
|
289
|
+
const vsS = vT.slice([0, 0, s, 0], [1, HEADS, s + segLen, PAD_DIM]);
|
|
290
|
+
outs.push(ops.sdpa(qs, ks, vsS, scale, "", null));
|
|
291
|
+
qs.dispose(); ks.dispose(); vsS.dispose();
|
|
292
|
+
}
|
|
293
|
+
attnP = ops.concatAxis(outs, 2);
|
|
294
|
+
for (const o of outs) o.dispose();
|
|
295
|
+
}
|
|
296
|
+
qT.dispose(); kT.dispose(); vT.dispose();
|
|
297
|
+
const attn = disposing(
|
|
298
|
+
attnP, attnP.slice([0, 0, 0, 0], [1, HEADS, n, HEAD_DIM]),
|
|
299
|
+
);
|
|
300
|
+
const attnT = disposing(attn, ops.transposeAxes(attn, [0, 2, 1, 3]));
|
|
301
|
+
const merged = disposing(attnT, ops.reshape(attnT, [n, HIDDEN]));
|
|
302
|
+
const proj = disposing(merged, this.#linear(merged, blk.projW, blk.projB));
|
|
303
|
+
h = disposing(h, disposing(proj, ops.add(h, proj)));
|
|
304
|
+
const mn = ops.layerNorm(h, blk.norm2W, blk.norm2B, 1e-6);
|
|
305
|
+
const f1 = disposing(mn, this.#linear(mn, blk.fc1W, blk.fc1B));
|
|
306
|
+
const act = disposing(f1, ops.geluApprox(f1)); // gelu_pytorch_tanh
|
|
307
|
+
const f2 = disposing(act, this.#linear(act, blk.fc2W, blk.fc2B));
|
|
308
|
+
return disposing(h, disposing(f2, ops.add(h, f2)));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** nn.Linear on bf16 — mx.addmm(bias, x, W^T), the reference's exact bias
|
|
312
|
+
* path (a separate matmul+add rounds differently and seeded the ±1-ulp
|
|
313
|
+
* block divergence found in the stage bisect). */
|
|
314
|
+
#linear(x: MlxArray, w: MlxArray, b: MlxArray): MlxArray {
|
|
315
|
+
const wT = ops.transposeAxes(w, [1, 0]);
|
|
316
|
+
const y = ops.addmm(b, x, wT);
|
|
317
|
+
wT.dispose();
|
|
318
|
+
return y;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Bilinearly interpolated learned pos embeds, in merge-block-major row
|
|
322
|
+
* order — port of fast_pos_embed_interpolate + the merge permutation. */
|
|
323
|
+
#interpolatedPosEmbeds(gridH: number, gridW: number): MlxArray {
|
|
324
|
+
const n = gridH * gridW;
|
|
325
|
+
const idx = [new Int32Array(n), new Int32Array(n), new Int32Array(n), new Int32Array(n)];
|
|
326
|
+
const wts = [new Float32Array(n), new Float32Array(n), new Float32Array(n), new Float32Array(n)];
|
|
327
|
+
const lin = (count: number): Float32Array => {
|
|
328
|
+
// mx.linspace(0, POS_GRID-1, count) in f32.
|
|
329
|
+
const outArr = new Float32Array(count);
|
|
330
|
+
if (count === 1) { outArr[0] = 0; return outArr; }
|
|
331
|
+
const step = Math.fround((POS_GRID - 1) / (count - 1));
|
|
332
|
+
for (let i = 0; i < count; i++) outArr[i] = Math.fround(i * step);
|
|
333
|
+
return outArr;
|
|
334
|
+
};
|
|
335
|
+
const hIdx = lin(gridH);
|
|
336
|
+
const wIdx = lin(gridW);
|
|
337
|
+
for (let h = 0; h < gridH; h++) {
|
|
338
|
+
const hf = Math.trunc(hIdx[h]!);
|
|
339
|
+
const hc = Math.min(hf + 1, POS_GRID - 1);
|
|
340
|
+
const dh = Math.fround(hIdx[h]! - hf);
|
|
341
|
+
for (let x = 0; x < gridW; x++) {
|
|
342
|
+
const wf = Math.trunc(wIdx[x]!);
|
|
343
|
+
const wc = Math.min(wf + 1, POS_GRID - 1);
|
|
344
|
+
const dw = Math.fround(wIdx[x]! - wf);
|
|
345
|
+
const i = h * gridW + x;
|
|
346
|
+
idx[0]![i] = hf * POS_GRID + wf;
|
|
347
|
+
idx[1]![i] = hf * POS_GRID + wc;
|
|
348
|
+
idx[2]![i] = hc * POS_GRID + wf;
|
|
349
|
+
idx[3]![i] = hc * POS_GRID + wc;
|
|
350
|
+
wts[0]![i] = Math.fround((1 - dh) * (1 - dw));
|
|
351
|
+
wts[1]![i] = Math.fround((1 - dh) * dw);
|
|
352
|
+
wts[2]![i] = Math.fround(dh * (1 - dw));
|
|
353
|
+
wts[3]![i] = Math.fround(dh * dw);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// pos_embeds[k] = pos_embed[idx_k] * w_k (bf16, like the reference's
|
|
357
|
+
// weight tensor cast); summed pairwise in reference order.
|
|
358
|
+
let acc: MlxArray | null = null;
|
|
359
|
+
for (let k = 0; k < 4; k++) {
|
|
360
|
+
const ids = MlxArray.fromInt32(idx[k]!, [n]);
|
|
361
|
+
const rows = ops.takeAxis(this.#posEmbed, ids, 0); // [n, 1152] bf16
|
|
362
|
+
ids.dispose();
|
|
363
|
+
const wf = MlxArray.fromFloat32(wts[k]!, [n, 1]);
|
|
364
|
+
const wb = disposing(wf, wf.astype(Dtype.bfloat16));
|
|
365
|
+
const term = ops.mul(rows, wb);
|
|
366
|
+
rows.dispose();
|
|
367
|
+
wb.dispose();
|
|
368
|
+
acc = acc ? disposing(acc, disposing(term, ops.add(acc, term))) : term;
|
|
369
|
+
}
|
|
370
|
+
// Merge-block-major permutation (rows currently h-major patch order).
|
|
371
|
+
const ms = QWEN3VL_MERGE_SIZE;
|
|
372
|
+
const permIdx = new Int32Array(n);
|
|
373
|
+
let r = 0;
|
|
374
|
+
for (let h1 = 0; h1 < gridH / ms; h1++)
|
|
375
|
+
for (let w1 = 0; w1 < gridW / ms; w1++)
|
|
376
|
+
for (let mh = 0; mh < ms; mh++)
|
|
377
|
+
for (let mw = 0; mw < ms; mw++)
|
|
378
|
+
permIdx[r++] = (h1 * ms + mh) * gridW + (w1 * ms + mw);
|
|
379
|
+
const pIds = MlxArray.fromInt32(permIdx, [n]);
|
|
380
|
+
const out = ops.takeAxis(acc!, pIds, 0);
|
|
381
|
+
pIds.dispose();
|
|
382
|
+
acc!.dispose();
|
|
383
|
+
return out;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** 2D rotary cos/sin [n, HEAD_DIM] f32 in merge-block-major order —
|
|
387
|
+
* port of rot_pos_emb + apply_rotary_pos_emb_vision's tile(·,2). */
|
|
388
|
+
#ropeCosSin(gridH: number, gridW: number): [MlxArray, MlxArray] {
|
|
389
|
+
const n = gridH * gridW;
|
|
390
|
+
const half = ROT_DIM / 2; // 18 inv freqs
|
|
391
|
+
// inv_freq on-device, the reference's exact graph — Metal powf is not
|
|
392
|
+
// correctly rounded, so a JS Math.pow emulation differs by 1 ulp on some
|
|
393
|
+
// entries (seen as 2.4e-7 in the freq gate before this).
|
|
394
|
+
const invFreq = (() => {
|
|
395
|
+
const ar = ops.arange(0, ROT_DIM, 2, Dtype.float32);
|
|
396
|
+
const ex = disposing(ar, ops.mulScalar(ar, 1 / ROT_DIM));
|
|
397
|
+
const theta = MlxArray.fromFloat32(Float32Array.from([ROPE_THETA]), [1]);
|
|
398
|
+
const p = ops.pow(theta, ex);
|
|
399
|
+
theta.dispose();
|
|
400
|
+
ex.dispose();
|
|
401
|
+
const one = MlxArray.fromFloat32(Float32Array.from([1]), [1]);
|
|
402
|
+
const inv = disposing(p, ops.div(one, p));
|
|
403
|
+
one.dispose();
|
|
404
|
+
const v = inv.toFloat32();
|
|
405
|
+
inv.dispose();
|
|
406
|
+
return v;
|
|
407
|
+
})();
|
|
408
|
+
const ms = QWEN3VL_MERGE_SIZE;
|
|
409
|
+
const freqs = new Float32Array(n * ROT_DIM);
|
|
410
|
+
let r = 0;
|
|
411
|
+
for (let h1 = 0; h1 < gridH / ms; h1++) {
|
|
412
|
+
for (let w1 = 0; w1 < gridW / ms; w1++) {
|
|
413
|
+
for (let mh = 0; mh < ms; mh++) {
|
|
414
|
+
for (let mw = 0; mw < ms; mw++) {
|
|
415
|
+
const hPos = h1 * ms + mh;
|
|
416
|
+
const wPos = w1 * ms + mw;
|
|
417
|
+
const base = r * ROT_DIM;
|
|
418
|
+
for (let i = 0; i < half; i++) {
|
|
419
|
+
freqs[base + i] = Math.fround(hPos * invFreq[i]!);
|
|
420
|
+
freqs[base + half + i] = Math.fround(wPos * invFreq[i]!);
|
|
421
|
+
}
|
|
422
|
+
r++;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const f = MlxArray.fromFloat32(freqs, [n, ROT_DIM]);
|
|
428
|
+
const cosH = ops.cos(f);
|
|
429
|
+
const sinH = ops.sin(f);
|
|
430
|
+
f.dispose();
|
|
431
|
+
// tile(·, (1, 2)) over the last dim → [n, HEAD_DIM].
|
|
432
|
+
const cos = disposing(cosH, ops.concatAxis([cosH, cosH], -1));
|
|
433
|
+
const sin = disposing(sinH, ops.concatAxis([sinH, sinH], -1));
|
|
434
|
+
return [cos, sin];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** (t·cos) + (rotate_half(t)·sin), f32 math, cast back to bf16. */
|
|
438
|
+
#applyRope(x: MlxArray, cos: MlxArray, sin: MlxArray): MlxArray {
|
|
439
|
+
// x [n, HEADS, HEAD_DIM]; cos/sin [n, 1, HEAD_DIM] broadcast over heads.
|
|
440
|
+
const half = HEAD_DIM / 2;
|
|
441
|
+
const n = x.shape[0]!;
|
|
442
|
+
const x1 = x.slice([0, 0, 0], [n, HEADS, half]);
|
|
443
|
+
const x2 = x.slice([0, 0, half], [n, HEADS, HEAD_DIM]);
|
|
444
|
+
const negX2 = ops.mulScalar(x2, -1);
|
|
445
|
+
x2.dispose();
|
|
446
|
+
const rot = disposing(negX2, ops.concatAxis([negX2, x1], -1));
|
|
447
|
+
x1.dispose();
|
|
448
|
+
const a = ops.mul(x, cos);
|
|
449
|
+
const b = disposing(rot, ops.mul(rot, sin));
|
|
450
|
+
const sum = ops.add(a, b);
|
|
451
|
+
a.dispose();
|
|
452
|
+
b.dispose();
|
|
453
|
+
return disposing(sum, sum.astype(Dtype.bfloat16));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Stage-instrumented encode for parity bisection: returns
|
|
457
|
+
* named f32 copies of each pipeline stage alongside the final output. */
|
|
458
|
+
encodeStages(pp: Qwen3VLPreprocessed): Map<string, Float32Array> {
|
|
459
|
+
const stages = new Map<string, Float32Array>();
|
|
460
|
+
const out = this.encode(pp, (name, arr) => {
|
|
461
|
+
const f = arr.astype(Dtype.float32);
|
|
462
|
+
stages.set(name, f.toFloat32());
|
|
463
|
+
f.dispose();
|
|
464
|
+
});
|
|
465
|
+
const f = out.astype(Dtype.float32);
|
|
466
|
+
stages.set("merged", f.toFloat32());
|
|
467
|
+
f.dispose();
|
|
468
|
+
out.dispose();
|
|
469
|
+
return stages;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** Parity-bisect helper: run ONLY the block chain (+ optional merger) on a
|
|
473
|
+
* caller-provided hidden grid — used to separate patch-embed seed ulp
|
|
474
|
+
* from block-math divergence in the stage gates. */
|
|
475
|
+
debugRunBlocks(
|
|
476
|
+
hF32: Float32Array, gridH: number, gridW: number, throughMerger = false,
|
|
477
|
+
): Float32Array {
|
|
478
|
+
const n = gridH * gridW;
|
|
479
|
+
const hf = MlxArray.fromFloat32(hF32, [n, HIDDEN]);
|
|
480
|
+
let h = disposing(hf, hf.astype(Dtype.bfloat16));
|
|
481
|
+
const [cosF, sinF] = this.#ropeCosSin(gridH, gridW);
|
|
482
|
+
const cos = disposing(cosF, ops.expandDims(cosF, 1));
|
|
483
|
+
const sin = disposing(sinF, ops.expandDims(sinF, 1));
|
|
484
|
+
const scale = Math.pow(HEAD_DIM, -0.5);
|
|
485
|
+
for (const blk of this.#blocks) h = this.#block(h, blk, cos, sin, scale, n);
|
|
486
|
+
cos.dispose();
|
|
487
|
+
sin.dispose();
|
|
488
|
+
if (throughMerger) {
|
|
489
|
+
const mnorm = disposing(h, ops.layerNorm(h, this.#mergerNormW, this.#mergerNormB, 1e-6));
|
|
490
|
+
const grouped = disposing(mnorm, ops.reshape(mnorm, [n / 4, HIDDEN * 4]));
|
|
491
|
+
const f1 = disposing(grouped, this.#linear(grouped, this.#mergerFc1W, this.#mergerFc1B));
|
|
492
|
+
const act = disposing(f1, ops.geluPrecise(f1));
|
|
493
|
+
h = disposing(act, this.#linear(act, this.#mergerFc2W, this.#mergerFc2B));
|
|
494
|
+
}
|
|
495
|
+
const f = disposing(h, h.astype(Dtype.float32));
|
|
496
|
+
const out = f.toFloat32();
|
|
497
|
+
f.dispose();
|
|
498
|
+
return out;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** Encode one preprocessed image → [imageTokens, 5120] bf16. */
|
|
502
|
+
encode(
|
|
503
|
+
pp: Qwen3VLPreprocessed,
|
|
504
|
+
tap?: (name: string, arr: MlxArray) => void,
|
|
505
|
+
): MlxArray {
|
|
506
|
+
const [gridT, gridH, gridW] = pp.gridThw;
|
|
507
|
+
const n = pp.rows;
|
|
508
|
+
const pvF = MlxArray.fromFloat32(pp.pixelValues, [n, pp.cols]);
|
|
509
|
+
const pv = disposing(pvF, pvF.astype(Dtype.bfloat16));
|
|
510
|
+
// PatchEmbed: rows (c,t,py,px) → [N, tps, ps, ps, C] NDHWC → Conv3d with
|
|
511
|
+
// kernel == stride — the reference's exact reshape/moveaxis/conv graph.
|
|
512
|
+
const tps = QWEN3VL_TEMPORAL_PATCH_SIZE;
|
|
513
|
+
const ps = QWEN3VL_PATCH_SIZE;
|
|
514
|
+
const r5 = disposing(pv, ops.reshape(pv, [n, 3, tps, ps, ps]));
|
|
515
|
+
const tr = disposing(r5, ops.transposeAxes(r5, [0, 2, 3, 4, 1]));
|
|
516
|
+
const ndhwc = disposing(tr, ops.contiguous(tr));
|
|
517
|
+
const convOut = disposing(
|
|
518
|
+
ndhwc, ops.conv3d(ndhwc, this.#patchW, [tps, ps, ps]),
|
|
519
|
+
);
|
|
520
|
+
let h = disposing(convOut, ops.reshape(convOut, [n, HIDDEN]));
|
|
521
|
+
h = disposing(h, ops.add(h, this.#patchB));
|
|
522
|
+
tap?.("patch", h);
|
|
523
|
+
// Per-temporal-group pos/rope embeds are identical — tile the (h,w)
|
|
524
|
+
// block gridT times (the reference tiles coords/pos_embed per frame).
|
|
525
|
+
const tile = (a: MlxArray): MlxArray => {
|
|
526
|
+
if (gridT === 1) return a;
|
|
527
|
+
const parts = new Array(gridT).fill(a);
|
|
528
|
+
return disposing(a, ops.concatAxis(parts, 0));
|
|
529
|
+
};
|
|
530
|
+
const pos = tile(this.#interpolatedPosEmbeds(gridH, gridW));
|
|
531
|
+
tap?.("pos", pos);
|
|
532
|
+
h = disposing(h, disposing(pos, ops.add(h, pos)));
|
|
533
|
+
|
|
534
|
+
const [cosF0, sinF0] = this.#ropeCosSin(gridH, gridW);
|
|
535
|
+
const cosF = tile(cosF0);
|
|
536
|
+
const sinF = tile(sinF0);
|
|
537
|
+
const cos = disposing(cosF, ops.expandDims(cosF, 1)); // [n,1,72]
|
|
538
|
+
const sin = disposing(sinF, ops.expandDims(sinF, 1));
|
|
539
|
+
|
|
540
|
+
const scale = Math.pow(HEAD_DIM, -0.5);
|
|
541
|
+
// Attention never crosses temporal groups (cu_seqlens = h·w per frame
|
|
542
|
+
// pair): images run one segment, videos gridT segments.
|
|
543
|
+
const segLen = gridH * gridW;
|
|
544
|
+
let blkIdx = 0;
|
|
545
|
+
for (const blk of this.#blocks) {
|
|
546
|
+
h = this.#block(h, blk, cos, sin, scale, n, segLen);
|
|
547
|
+
tap?.(`blk${blkIdx}`, h);
|
|
548
|
+
blkIdx++;
|
|
549
|
+
}
|
|
550
|
+
tap?.("blocks", h);
|
|
551
|
+
cos.dispose();
|
|
552
|
+
sin.dispose();
|
|
553
|
+
|
|
554
|
+
// Merger: LayerNorm(1152) → reshape 4× → fc1 → GELU(erf) → fc2.
|
|
555
|
+
const mnorm = disposing(h, ops.layerNorm(h, this.#mergerNormW, this.#mergerNormB, 1e-6));
|
|
556
|
+
const grouped = disposing(mnorm, ops.reshape(mnorm, [n / 4, HIDDEN * 4]));
|
|
557
|
+
const f1 = disposing(grouped, this.#linear(grouped, this.#mergerFc1W, this.#mergerFc1B));
|
|
558
|
+
const act = disposing(f1, ops.geluPrecise(f1)); // reference nn.GELU()
|
|
559
|
+
return disposing(act, this.#linear(act, this.#mergerFc2W, this.#mergerFc2B));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
dispose(): void {
|
|
563
|
+
// Sidecar arrays are process-pinned (house rule: mmap-backed weights are
|
|
564
|
+
// never handed a JS dtor); nothing to free eagerly.
|
|
565
|
+
}
|
|
566
|
+
}
|