@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,305 @@
|
|
|
1
|
+
// Header-only reader and validator for Colibri's converted GLM-5.2 artifact.
|
|
2
|
+
//
|
|
3
|
+
// Colibri emits ordinary safetensors shards named out-*, out-mtp-* and
|
|
4
|
+
// optionally out-idx-*. There is no model.safetensors.index.json. Tensor byte
|
|
5
|
+
// geometry is authoritative: converted 2-D tensors are flattened U8 payloads
|
|
6
|
+
// with companion F32 `<name>.qs` scales. Reading only the headers avoids
|
|
7
|
+
// mapping hundreds of GiB merely to validate the artifact.
|
|
8
|
+
|
|
9
|
+
import { closeSync, fstatSync, openSync, readSync, readdirSync } from "node:fs";
|
|
10
|
+
import { basename, join } from "node:path";
|
|
11
|
+
import type { Glm52Config } from "../glm52-config";
|
|
12
|
+
|
|
13
|
+
export type ColibriShardFamily = "main" | "mtp" | "indexer";
|
|
14
|
+
export type ColibriDtype =
|
|
15
|
+
| "BOOL" | "U8" | "I8" | "U16" | "I16" | "U32" | "I32" | "U64" | "I64"
|
|
16
|
+
| "F16" | "BF16" | "F32" | "F64";
|
|
17
|
+
|
|
18
|
+
const DTYPE_BYTES: Record<ColibriDtype, number> = {
|
|
19
|
+
BOOL: 1, U8: 1, I8: 1, U16: 2, I16: 2, U32: 4, I32: 4,
|
|
20
|
+
U64: 8, I64: 8, F16: 2, BF16: 2, F32: 4, F64: 8,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const GROUP_SIZES = [16, 32, 48, 64, 96, 128, 192, 256] as const;
|
|
24
|
+
// Match safetensors' own defensive ceiling: headers are metadata, never model
|
|
25
|
+
// payload. This prevents a corrupt multi-GiB shard from becoming a matching
|
|
26
|
+
// multi-GiB Bun allocation before JSON validation.
|
|
27
|
+
const MAX_SAFETENSORS_HEADER_BYTES = 100_000_000n;
|
|
28
|
+
|
|
29
|
+
interface HeaderTensor {
|
|
30
|
+
dtype: ColibriDtype;
|
|
31
|
+
shape: number[];
|
|
32
|
+
data_offsets: [number, number];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ColibriTensorInfo {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly dtype: ColibriDtype;
|
|
38
|
+
readonly shape: number[];
|
|
39
|
+
readonly file: string;
|
|
40
|
+
readonly family: ColibriShardFamily;
|
|
41
|
+
readonly begin: number;
|
|
42
|
+
readonly end: number;
|
|
43
|
+
readonly absoluteBegin: number;
|
|
44
|
+
readonly absoluteEnd: number;
|
|
45
|
+
readonly byteLength: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ColibriQuantTensorInfo {
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly outputRows: number;
|
|
51
|
+
readonly inputColumns: number;
|
|
52
|
+
readonly bits: 4 | 8;
|
|
53
|
+
readonly groupSize: number | null;
|
|
54
|
+
readonly weight: ColibriTensorInfo;
|
|
55
|
+
readonly scales: ColibriTensorInfo;
|
|
56
|
+
readonly coalescedNextWeight: string | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Glm52ContainerCapabilities {
|
|
60
|
+
readonly hasMtp: boolean;
|
|
61
|
+
readonly hasDsa: boolean;
|
|
62
|
+
readonly missingMtpTensors: string[];
|
|
63
|
+
readonly missingDsaTensors: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function product(shape: number[], label: string): number {
|
|
67
|
+
let value = 1;
|
|
68
|
+
for (const dim of shape) {
|
|
69
|
+
if (!Number.isSafeInteger(dim) || dim < 0)
|
|
70
|
+
throw new Error(`${label}: invalid shape ${JSON.stringify(shape)}`);
|
|
71
|
+
value *= dim;
|
|
72
|
+
if (!Number.isSafeInteger(value))
|
|
73
|
+
throw new Error(`${label}: shape product exceeds safe integer range`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readFully(fd: number, buffer: Uint8Array, position: number, label: string): void {
|
|
79
|
+
let offset = 0;
|
|
80
|
+
while (offset < buffer.byteLength) {
|
|
81
|
+
const got = readSync(fd, buffer, offset, buffer.byteLength - offset, position + offset);
|
|
82
|
+
if (got === 0)
|
|
83
|
+
throw new Error(`${label}: short read at ${position + offset}`);
|
|
84
|
+
offset += got;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function familyFor(filename: string): ColibriShardFamily | null {
|
|
89
|
+
if (/^out-mtp-\d+\.safetensors$/.test(filename)) return "mtp";
|
|
90
|
+
if (/^out-idx-\d+\.safetensors$/.test(filename)) return "indexer";
|
|
91
|
+
if (/^out-\d+\.safetensors$/.test(filename)) return "main";
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseHeader(path: string, family: ColibriShardFamily): ColibriTensorInfo[] {
|
|
96
|
+
const fd = openSync(path, "r");
|
|
97
|
+
try {
|
|
98
|
+
const stat = fstatSync(fd);
|
|
99
|
+
if (stat.size < 8) throw new Error(`${path}: too small for safetensors`);
|
|
100
|
+
const prefix = Buffer.allocUnsafe(8);
|
|
101
|
+
readFully(fd, prefix, 0, path);
|
|
102
|
+
const headerBig = prefix.readBigUInt64LE(0);
|
|
103
|
+
if (headerBig > MAX_SAFETENSORS_HEADER_BYTES) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`${path}: safetensors header ${headerBig} exceeds ` +
|
|
106
|
+
`${MAX_SAFETENSORS_HEADER_BYTES} byte limit`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (headerBig > BigInt(Number.MAX_SAFE_INTEGER))
|
|
110
|
+
throw new Error(`${path}: safetensors header is too large`);
|
|
111
|
+
const headerLength = Number(headerBig);
|
|
112
|
+
const dataStart = 8 + headerLength;
|
|
113
|
+
if (headerLength < 2 || dataStart > stat.size)
|
|
114
|
+
throw new Error(`${path}: invalid safetensors header length ${headerLength}`);
|
|
115
|
+
const bytes = Buffer.allocUnsafe(headerLength);
|
|
116
|
+
readFully(fd, bytes, 8, path);
|
|
117
|
+
let parsed: Record<string, HeaderTensor | Record<string, string>>;
|
|
118
|
+
try {
|
|
119
|
+
parsed = JSON.parse(bytes.toString("utf8"));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw new Error(`${path}: invalid safetensors JSON: ${String(error)}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const out: ColibriTensorInfo[] = [];
|
|
125
|
+
for (const [name, raw] of Object.entries(parsed)) {
|
|
126
|
+
if (name === "__metadata__") continue;
|
|
127
|
+
const entry = raw as HeaderTensor;
|
|
128
|
+
if (!entry || !(entry.dtype in DTYPE_BYTES) || !Array.isArray(entry.shape) ||
|
|
129
|
+
!Array.isArray(entry.data_offsets) || entry.data_offsets.length !== 2)
|
|
130
|
+
throw new Error(`${path}: malformed tensor entry ${name}`);
|
|
131
|
+
const [begin, end] = entry.data_offsets;
|
|
132
|
+
if (!Number.isSafeInteger(begin) || !Number.isSafeInteger(end) || begin < 0 || end < begin)
|
|
133
|
+
throw new Error(`${path}: tensor ${name} has invalid offsets ${String(entry.data_offsets)}`);
|
|
134
|
+
const elements = product(entry.shape, `${path}:${name}`);
|
|
135
|
+
const expected = elements * DTYPE_BYTES[entry.dtype];
|
|
136
|
+
if (end - begin !== expected)
|
|
137
|
+
throw new Error(
|
|
138
|
+
`${path}: tensor ${name} byte range ${end - begin} != ` +
|
|
139
|
+
`${JSON.stringify(entry.shape)} × ${entry.dtype}`,
|
|
140
|
+
);
|
|
141
|
+
if (dataStart + end > stat.size)
|
|
142
|
+
throw new Error(`${path}: tensor ${name} extends past end of shard`);
|
|
143
|
+
out.push({
|
|
144
|
+
name,
|
|
145
|
+
dtype: entry.dtype,
|
|
146
|
+
shape: [...entry.shape],
|
|
147
|
+
file: path,
|
|
148
|
+
family,
|
|
149
|
+
begin,
|
|
150
|
+
end,
|
|
151
|
+
absoluteBegin: dataStart + begin,
|
|
152
|
+
absoluteEnd: dataStart + end,
|
|
153
|
+
byteLength: end - begin,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
} finally {
|
|
158
|
+
closeSync(fd);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export class ColibriGlm52Container {
|
|
163
|
+
readonly tensors = new Map<string, ColibriTensorInfo>();
|
|
164
|
+
readonly files: ReadonlyArray<{ path: string; family: ColibriShardFamily }>;
|
|
165
|
+
|
|
166
|
+
private constructor(files: Array<{ path: string; family: ColibriShardFamily }>) {
|
|
167
|
+
this.files = files;
|
|
168
|
+
for (const { path, family } of files) {
|
|
169
|
+
for (const tensor of parseHeader(path, family)) {
|
|
170
|
+
const prior = this.tensors.get(tensor.name);
|
|
171
|
+
if (prior)
|
|
172
|
+
throw new Error(
|
|
173
|
+
`duplicate Colibri tensor ${tensor.name} in ${basename(prior.file)} ` +
|
|
174
|
+
`and ${basename(tensor.file)}`,
|
|
175
|
+
);
|
|
176
|
+
this.tensors.set(tensor.name, tensor);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (this.tensors.size === 0)
|
|
180
|
+
throw new Error("Colibri container has no tensors");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static open(modelDir: string): ColibriGlm52Container {
|
|
184
|
+
const files = readdirSync(modelDir)
|
|
185
|
+
.map((name) => ({ name, family: familyFor(name) }))
|
|
186
|
+
.filter((entry): entry is { name: string; family: ColibriShardFamily } =>
|
|
187
|
+
entry.family !== null)
|
|
188
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
189
|
+
.map(({ name, family }) => ({ path: join(modelDir, name), family }));
|
|
190
|
+
if (!files.some((entry) => entry.family === "main"))
|
|
191
|
+
throw new Error(`${modelDir}: no out-NNNNN.safetensors Colibri shards`);
|
|
192
|
+
return new ColibriGlm52Container(files);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
has(name: string): boolean {
|
|
196
|
+
return this.tensors.has(name);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
info(name: string): ColibriTensorInfo {
|
|
200
|
+
const info = this.tensors.get(name);
|
|
201
|
+
if (!info) throw new Error(`Colibri container: missing tensor ${name}`);
|
|
202
|
+
return info;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Validate a converted 2-D tensor and infer int4 group geometry from scales. */
|
|
206
|
+
quantized(name: string, outputRows: number, inputColumns: number): ColibriQuantTensorInfo {
|
|
207
|
+
if (!Number.isSafeInteger(outputRows) || outputRows <= 0 ||
|
|
208
|
+
!Number.isSafeInteger(inputColumns) || inputColumns <= 0)
|
|
209
|
+
throw new Error(`${name}: invalid logical shape [${outputRows},${inputColumns}]`);
|
|
210
|
+
const weight = this.info(name);
|
|
211
|
+
const scales = this.info(`${name}.qs`);
|
|
212
|
+
if (weight.dtype !== "U8")
|
|
213
|
+
throw new Error(`${name}: converted weight must be U8 (got ${weight.dtype})`);
|
|
214
|
+
if (scales.dtype !== "F32")
|
|
215
|
+
throw new Error(`${name}.qs: scales must be F32 (got ${scales.dtype})`);
|
|
216
|
+
|
|
217
|
+
const int8Bytes = outputRows * inputColumns;
|
|
218
|
+
const int4Bytes = outputRows * Math.ceil(inputColumns / 2);
|
|
219
|
+
let bits: 4 | 8;
|
|
220
|
+
if (weight.byteLength === int8Bytes) bits = 8;
|
|
221
|
+
else if (weight.byteLength === int4Bytes) bits = 4;
|
|
222
|
+
else {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`${name}: ${weight.byteLength} bytes do not encode logical ` +
|
|
225
|
+
`[${outputRows},${inputColumns}] as int8 (${int8Bytes}) or int4 (${int4Bytes})`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const scaleCount = scales.byteLength / 4;
|
|
230
|
+
let groupSize: number | null = null;
|
|
231
|
+
if (scaleCount !== outputRows) {
|
|
232
|
+
if (bits !== 4)
|
|
233
|
+
throw new Error(`${name}.qs: grouped scales are only valid for int4`);
|
|
234
|
+
groupSize = GROUP_SIZES.find(
|
|
235
|
+
(candidate) => scaleCount === outputRows * Math.ceil(inputColumns / candidate),
|
|
236
|
+
) ?? null;
|
|
237
|
+
if (groupSize === null)
|
|
238
|
+
throw new Error(
|
|
239
|
+
`${name}.qs: ${scaleCount} scales do not match per-row or a supported ` +
|
|
240
|
+
`group size for [${outputRows},${inputColumns}]`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let coalescedNextWeight: string | null = null;
|
|
245
|
+
for (const other of this.tensors.values()) {
|
|
246
|
+
if (other.dtype === "U8" && other.file === weight.file &&
|
|
247
|
+
other.absoluteBegin === weight.absoluteEnd) {
|
|
248
|
+
coalescedNextWeight = other.name;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name,
|
|
254
|
+
outputRows,
|
|
255
|
+
inputColumns,
|
|
256
|
+
bits,
|
|
257
|
+
groupSize,
|
|
258
|
+
weight,
|
|
259
|
+
scales,
|
|
260
|
+
coalescedNextWeight,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
capabilities(config: Glm52Config): Glm52ContainerCapabilities {
|
|
265
|
+
const mtpLayer = config.numHiddenLayers;
|
|
266
|
+
const mtpPrefix = `model.layers.${mtpLayer}`;
|
|
267
|
+
// G4 validates the full MTP family before enabling drafting. G2 records the
|
|
268
|
+
// architectural sentinels so a partial sidecar cannot masquerade as MTP.
|
|
269
|
+
const mtpRequired = config.numNextnPredictLayers > 0 ? [
|
|
270
|
+
`${mtpPrefix}.eh_proj.weight`,
|
|
271
|
+
`${mtpPrefix}.enorm.weight`,
|
|
272
|
+
`${mtpPrefix}.hnorm.weight`,
|
|
273
|
+
`${mtpPrefix}.shared_head.norm.weight`,
|
|
274
|
+
`${mtpPrefix}.input_layernorm.weight`,
|
|
275
|
+
`${mtpPrefix}.post_attention_layernorm.weight`,
|
|
276
|
+
`${mtpPrefix}.self_attn.q_a_proj.weight`,
|
|
277
|
+
`${mtpPrefix}.self_attn.kv_a_proj_with_mqa.weight`,
|
|
278
|
+
`${mtpPrefix}.mlp.gate.weight`,
|
|
279
|
+
`${mtpPrefix}.mlp.gate.e_score_correction_bias`,
|
|
280
|
+
] : [];
|
|
281
|
+
const missingMtpTensors = mtpRequired.filter((name) => !this.has(name));
|
|
282
|
+
|
|
283
|
+
const dsaRequired: string[] = [];
|
|
284
|
+
if (config.indexTopk > 0) {
|
|
285
|
+
for (let layer = 0; layer < config.numHiddenLayers; layer++) {
|
|
286
|
+
if (config.indexerTypes[layer] !== "full") continue;
|
|
287
|
+
const prefix = `model.layers.${layer}.self_attn.indexer`;
|
|
288
|
+
dsaRequired.push(
|
|
289
|
+
`${prefix}.wq_b.weight`,
|
|
290
|
+
`${prefix}.wk.weight`,
|
|
291
|
+
`${prefix}.weights_proj.weight`,
|
|
292
|
+
`${prefix}.k_norm.weight`,
|
|
293
|
+
`${prefix}.k_norm.bias`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const missingDsaTensors = dsaRequired.filter((name) => !this.has(name));
|
|
298
|
+
return {
|
|
299
|
+
hasMtp: mtpRequired.length > 0 && missingMtpTensors.length === 0,
|
|
300
|
+
hasDsa: dsaRequired.length > 0 && missingDsaTensors.length === 0,
|
|
301
|
+
missingMtpTensors,
|
|
302
|
+
missingDsaTensors,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ColibriGlm52Container,
|
|
3
|
+
ColibriQuantTensorInfo,
|
|
4
|
+
ColibriTensorInfo,
|
|
5
|
+
} from "./container";
|
|
6
|
+
import type { Glm52Config } from "../glm52-config";
|
|
7
|
+
import { positiveInteger } from "../../runtime/integers";
|
|
8
|
+
|
|
9
|
+
import { GLM52_EXPERT_SLOT_ALIGNMENT } from "../../kernels/glm52/layout";
|
|
10
|
+
export { GLM52_EXPERT_SLOT_ALIGNMENT };
|
|
11
|
+
export const GLM52_EXPERT_MAX_READ_SEGMENTS = 8;
|
|
12
|
+
|
|
13
|
+
export type Glm52ExpertProjection = "down" | "gate" | "up";
|
|
14
|
+
|
|
15
|
+
export interface Glm52ExpertProjectionLayout {
|
|
16
|
+
readonly projection: Glm52ExpertProjection;
|
|
17
|
+
readonly tensor: ColibriQuantTensorInfo;
|
|
18
|
+
readonly weightOffset: number;
|
|
19
|
+
readonly scaleOffset: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Glm52ExpertReadSegment {
|
|
23
|
+
readonly file: string;
|
|
24
|
+
readonly sourceOffset: number;
|
|
25
|
+
readonly destinationOffset: number;
|
|
26
|
+
readonly length: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Glm52ExpertSlotLayout {
|
|
30
|
+
readonly layer: number;
|
|
31
|
+
readonly expertId: number;
|
|
32
|
+
readonly bits: 4 | 8;
|
|
33
|
+
readonly groupSize: number | null;
|
|
34
|
+
readonly alignment: number;
|
|
35
|
+
readonly weightBytes: number;
|
|
36
|
+
readonly scaleBytes: number;
|
|
37
|
+
readonly scaleOffset: number;
|
|
38
|
+
readonly payloadBytes: number;
|
|
39
|
+
readonly slotBytes: number;
|
|
40
|
+
readonly projections: Readonly<Record<
|
|
41
|
+
Glm52ExpertProjection,
|
|
42
|
+
Glm52ExpertProjectionLayout
|
|
43
|
+
>>;
|
|
44
|
+
/**
|
|
45
|
+
* Positioned reads in canonical slot order. Adjacent tensor reads are
|
|
46
|
+
* coalesced only when both their source and destination ranges are adjacent
|
|
47
|
+
* in the same shard.
|
|
48
|
+
*/
|
|
49
|
+
readonly segments: readonly Glm52ExpertReadSegment[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type ExpertGeometry = Pick<
|
|
53
|
+
Glm52Config,
|
|
54
|
+
"hiddenSize" | "moeIntermediateSize" | "numRoutedExperts"
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
interface PrimitiveRead {
|
|
58
|
+
readonly tensor: ColibriTensorInfo;
|
|
59
|
+
readonly destinationOffset: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function alignUp(value: number, alignment: number): number {
|
|
63
|
+
const result = Math.ceil(value / alignment) * alignment;
|
|
64
|
+
if (!Number.isSafeInteger(result))
|
|
65
|
+
throw new Error("expert slot size exceeds the safe integer range");
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function coalesceReads(
|
|
70
|
+
reads: readonly PrimitiveRead[],
|
|
71
|
+
): Glm52ExpertReadSegment[] {
|
|
72
|
+
const segments: Glm52ExpertReadSegment[] = [];
|
|
73
|
+
for (const read of reads) {
|
|
74
|
+
const next: Glm52ExpertReadSegment = {
|
|
75
|
+
file: read.tensor.file,
|
|
76
|
+
sourceOffset: read.tensor.absoluteBegin,
|
|
77
|
+
destinationOffset: read.destinationOffset,
|
|
78
|
+
length: read.tensor.byteLength,
|
|
79
|
+
};
|
|
80
|
+
const prior = segments.at(-1);
|
|
81
|
+
if (
|
|
82
|
+
prior &&
|
|
83
|
+
prior.file === next.file &&
|
|
84
|
+
prior.sourceOffset + prior.length === next.sourceOffset &&
|
|
85
|
+
prior.destinationOffset + prior.length === next.destinationOffset
|
|
86
|
+
) {
|
|
87
|
+
segments[segments.length - 1] = {
|
|
88
|
+
...prior,
|
|
89
|
+
length: prior.length + next.length,
|
|
90
|
+
};
|
|
91
|
+
} else {
|
|
92
|
+
segments.push(next);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return segments;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Describe one routed expert as a fixed aligned native slot without opening or
|
|
100
|
+
* mapping any tensor payload.
|
|
101
|
+
*
|
|
102
|
+
* The slot order is deliberately independent of safetensors header order:
|
|
103
|
+
* down/gate/up packed weights followed by down/gate/up F32 scales. Every
|
|
104
|
+
* pointer-addressed component begins at a 16 KiB boundary: MLX/Metal cannot
|
|
105
|
+
* safely no-copy-wrap a merely sub-page-aligned host pointer. Source-adjacent
|
|
106
|
+
* components still coalesce when the required padding is zero.
|
|
107
|
+
*/
|
|
108
|
+
export function buildGlm52ExpertSlotLayout(
|
|
109
|
+
container: ColibriGlm52Container,
|
|
110
|
+
config: ExpertGeometry,
|
|
111
|
+
layer: number,
|
|
112
|
+
expertId: number,
|
|
113
|
+
alignment = GLM52_EXPERT_SLOT_ALIGNMENT,
|
|
114
|
+
): Glm52ExpertSlotLayout {
|
|
115
|
+
positiveInteger(config.hiddenSize, "hidden size");
|
|
116
|
+
positiveInteger(config.moeIntermediateSize, "MoE intermediate size");
|
|
117
|
+
positiveInteger(config.numRoutedExperts, "routed expert count");
|
|
118
|
+
if (!Number.isSafeInteger(layer) || layer < 0)
|
|
119
|
+
throw new Error("expert layer must be a non-negative safe integer");
|
|
120
|
+
if (
|
|
121
|
+
!Number.isSafeInteger(expertId) ||
|
|
122
|
+
expertId < 0 ||
|
|
123
|
+
expertId >= config.numRoutedExperts
|
|
124
|
+
) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`expert ID ${expertId} is outside [0,${config.numRoutedExperts})`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (
|
|
130
|
+
!Number.isSafeInteger(alignment) ||
|
|
131
|
+
alignment < 1 ||
|
|
132
|
+
(alignment & (alignment - 1)) !== 0
|
|
133
|
+
) {
|
|
134
|
+
throw new Error("expert slot alignment must be a positive power of two");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const prefix = `model.layers.${layer}.mlp.experts.${expertId}`;
|
|
138
|
+
const dimensions: Record<
|
|
139
|
+
Glm52ExpertProjection,
|
|
140
|
+
readonly [outputRows: number, inputColumns: number]
|
|
141
|
+
> = {
|
|
142
|
+
down: [config.hiddenSize, config.moeIntermediateSize],
|
|
143
|
+
gate: [config.moeIntermediateSize, config.hiddenSize],
|
|
144
|
+
up: [config.moeIntermediateSize, config.hiddenSize],
|
|
145
|
+
};
|
|
146
|
+
const suffix: Record<Glm52ExpertProjection, string> = {
|
|
147
|
+
down: "down_proj",
|
|
148
|
+
gate: "gate_proj",
|
|
149
|
+
up: "up_proj",
|
|
150
|
+
};
|
|
151
|
+
const order: readonly Glm52ExpertProjection[] = ["down", "gate", "up"];
|
|
152
|
+
|
|
153
|
+
const tensors = {} as Record<
|
|
154
|
+
Glm52ExpertProjection,
|
|
155
|
+
ColibriQuantTensorInfo
|
|
156
|
+
>;
|
|
157
|
+
for (const projection of order) {
|
|
158
|
+
const [outputRows, inputColumns] = dimensions[projection];
|
|
159
|
+
tensors[projection] = container.quantized(
|
|
160
|
+
`${prefix}.${suffix[projection]}.weight`,
|
|
161
|
+
outputRows,
|
|
162
|
+
inputColumns,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const bits = tensors.down.bits;
|
|
167
|
+
const groupSize = tensors.down.groupSize;
|
|
168
|
+
for (const projection of order) {
|
|
169
|
+
const tensor = tensors[projection];
|
|
170
|
+
if (tensor.bits !== bits || tensor.groupSize !== groupSize) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`${prefix}: routed expert projections must use one quantization layout`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let weightBytes = 0;
|
|
178
|
+
let cursor = 0;
|
|
179
|
+
const weightOffsets = {} as Record<Glm52ExpertProjection, number>;
|
|
180
|
+
for (const projection of order) {
|
|
181
|
+
cursor = alignUp(cursor, alignment);
|
|
182
|
+
weightOffsets[projection] = cursor;
|
|
183
|
+
weightBytes += tensors[projection].weight.byteLength;
|
|
184
|
+
cursor += tensors[projection].weight.byteLength;
|
|
185
|
+
}
|
|
186
|
+
if (!Number.isSafeInteger(weightBytes))
|
|
187
|
+
throw new Error(`${prefix}: expert weight bytes exceed the safe integer range`);
|
|
188
|
+
|
|
189
|
+
const scaleOffset = alignUp(cursor, alignment);
|
|
190
|
+
cursor = scaleOffset;
|
|
191
|
+
let scaleBytes = 0;
|
|
192
|
+
const scaleOffsets = {} as Record<Glm52ExpertProjection, number>;
|
|
193
|
+
for (const projection of order) {
|
|
194
|
+
cursor = alignUp(cursor, alignment);
|
|
195
|
+
scaleOffsets[projection] = cursor;
|
|
196
|
+
scaleBytes += tensors[projection].scales.byteLength;
|
|
197
|
+
cursor += tensors[projection].scales.byteLength;
|
|
198
|
+
}
|
|
199
|
+
const payloadBytes = cursor;
|
|
200
|
+
if (!Number.isSafeInteger(payloadBytes))
|
|
201
|
+
throw new Error(`${prefix}: expert payload bytes exceed the safe integer range`);
|
|
202
|
+
const slotBytes = alignUp(payloadBytes, alignment);
|
|
203
|
+
|
|
204
|
+
const primitiveReads: PrimitiveRead[] = [
|
|
205
|
+
...order.map((projection) => ({
|
|
206
|
+
tensor: tensors[projection].weight,
|
|
207
|
+
destinationOffset: weightOffsets[projection],
|
|
208
|
+
})),
|
|
209
|
+
...order.map((projection) => ({
|
|
210
|
+
tensor: tensors[projection].scales,
|
|
211
|
+
destinationOffset: scaleOffsets[projection],
|
|
212
|
+
})),
|
|
213
|
+
];
|
|
214
|
+
const segments = coalesceReads(primitiveReads);
|
|
215
|
+
if (segments.length > GLM52_EXPERT_MAX_READ_SEGMENTS) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`${prefix}: expert requires ${segments.length} read segments; maximum is ` +
|
|
218
|
+
`${GLM52_EXPERT_MAX_READ_SEGMENTS}`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const projections = {} as Record<
|
|
223
|
+
Glm52ExpertProjection,
|
|
224
|
+
Glm52ExpertProjectionLayout
|
|
225
|
+
>;
|
|
226
|
+
for (const projection of order) {
|
|
227
|
+
projections[projection] = {
|
|
228
|
+
projection,
|
|
229
|
+
tensor: tensors[projection],
|
|
230
|
+
weightOffset: weightOffsets[projection],
|
|
231
|
+
scaleOffset: scaleOffsets[projection],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
layer,
|
|
237
|
+
expertId,
|
|
238
|
+
bits,
|
|
239
|
+
groupSize,
|
|
240
|
+
alignment,
|
|
241
|
+
weightBytes,
|
|
242
|
+
scaleBytes,
|
|
243
|
+
scaleOffset,
|
|
244
|
+
payloadBytes,
|
|
245
|
+
slotBytes,
|
|
246
|
+
projections,
|
|
247
|
+
segments,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { positiveInteger } from "../../runtime/integers";
|
|
2
|
+
import { validateRange, type Glm52CanonicalMetalLayout, type Glm52CanonicalQ4MetalLayout, type Glm52CanonicalQ8MetalLayout } from "../../kernels/glm52/layout";
|
|
3
|
+
import { type Glm52ExpertSlotLayout } from "./expert-layout";
|
|
4
|
+
|
|
5
|
+
/** What differs between the canonical Q4 and Q8 slot layouts: the slot's bit
|
|
6
|
+
* width, the `Glm52ExpertSlotLayout` rejections' wording, and the element
|
|
7
|
+
* packing (Q4 packs two weights per byte in uint32 words and checks weight
|
|
8
|
+
* alignment; Q8 stores one signed int8 per weight). */
|
|
9
|
+
interface CanonicalSlotSpec {
|
|
10
|
+
readonly bits: 4 | 8;
|
|
11
|
+
readonly label: "Q4" | "Q8";
|
|
12
|
+
readonly bitsError: string;
|
|
13
|
+
readonly perRowError: string;
|
|
14
|
+
readonly weightsPerByte: 1 | 2;
|
|
15
|
+
readonly weightsMustBeWordAligned: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resolveCanonicalMetalLayout(
|
|
19
|
+
layout: Glm52ExpertSlotLayout,
|
|
20
|
+
spec: CanonicalSlotSpec,
|
|
21
|
+
): Glm52CanonicalMetalLayout {
|
|
22
|
+
if (layout.bits !== spec.bits) throw new Error(spec.bitsError);
|
|
23
|
+
if (layout.groupSize !== null) throw new Error(spec.perRowError);
|
|
24
|
+
|
|
25
|
+
const down = layout.projections.down;
|
|
26
|
+
const gate = layout.projections.gate;
|
|
27
|
+
const up = layout.projections.up;
|
|
28
|
+
const hiddenSize = positiveInteger(down.tensor.outputRows, "hidden size");
|
|
29
|
+
const intermediateSize = positiveInteger(
|
|
30
|
+
down.tensor.inputColumns,
|
|
31
|
+
"intermediate size",
|
|
32
|
+
);
|
|
33
|
+
const expected = [
|
|
34
|
+
[gate.tensor.outputRows, intermediateSize, "gate output rows"],
|
|
35
|
+
[gate.tensor.inputColumns, hiddenSize, "gate input columns"],
|
|
36
|
+
[up.tensor.outputRows, intermediateSize, "up output rows"],
|
|
37
|
+
[up.tensor.inputColumns, hiddenSize, "up input columns"],
|
|
38
|
+
] as const;
|
|
39
|
+
for (const [actual, wanted, label] of expected) {
|
|
40
|
+
if (actual !== wanted)
|
|
41
|
+
throw new Error(`${label} must be ${wanted}, got ${actual}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const slotBytes = positiveInteger(layout.slotBytes, "expert slot bytes");
|
|
45
|
+
for (const [projection, outputRows, inputColumns] of [
|
|
46
|
+
[down, hiddenSize, intermediateSize],
|
|
47
|
+
[gate, intermediateSize, hiddenSize],
|
|
48
|
+
[up, intermediateSize, hiddenSize],
|
|
49
|
+
] as const) {
|
|
50
|
+
const weightBytes = outputRows * inputColumns / spec.weightsPerByte;
|
|
51
|
+
const scaleBytes = outputRows * 4;
|
|
52
|
+
if (projection.tensor.weight.byteLength !== weightBytes) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`${projection.projection} ${spec.label} weight bytes must be ${weightBytes}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (projection.tensor.scales.byteLength !== scaleBytes) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${projection.projection} ${spec.label} scale bytes must be ${scaleBytes}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
validateRange(
|
|
63
|
+
projection.weightOffset,
|
|
64
|
+
weightBytes,
|
|
65
|
+
slotBytes,
|
|
66
|
+
`${projection.projection} weights`,
|
|
67
|
+
);
|
|
68
|
+
validateRange(
|
|
69
|
+
projection.scaleOffset,
|
|
70
|
+
scaleBytes,
|
|
71
|
+
slotBytes,
|
|
72
|
+
`${projection.projection} scales`,
|
|
73
|
+
);
|
|
74
|
+
if (spec.weightsMustBeWordAligned && (projection.weightOffset & 3) !== 0)
|
|
75
|
+
throw new Error(`${projection.projection} weights must be uint32-aligned`);
|
|
76
|
+
if ((projection.scaleOffset & 3) !== 0)
|
|
77
|
+
throw new Error(`${projection.projection} scales must be F32-aligned`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
hiddenSize,
|
|
82
|
+
intermediateSize,
|
|
83
|
+
slotBytes,
|
|
84
|
+
downWeightOffset: down.weightOffset,
|
|
85
|
+
gateWeightOffset: gate.weightOffset,
|
|
86
|
+
upWeightOffset: up.weightOffset,
|
|
87
|
+
downScaleOffset: down.scaleOffset,
|
|
88
|
+
gateScaleOffset: gate.scaleOffset,
|
|
89
|
+
upScaleOffset: up.scaleOffset,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolve and validate a production-compatible Q4 descriptor from the
|
|
95
|
+
* artifact-driven slot catalog. Colibri's main routed experts use one F32
|
|
96
|
+
* scale per output row (`groupSize === null`) and affine zero-point 8.
|
|
97
|
+
*/
|
|
98
|
+
export function glm52CanonicalQ4MetalLayout(
|
|
99
|
+
layout: Glm52ExpertSlotLayout,
|
|
100
|
+
): Glm52CanonicalQ4MetalLayout {
|
|
101
|
+
return resolveCanonicalMetalLayout(layout, {
|
|
102
|
+
bits: 4,
|
|
103
|
+
label: "Q4",
|
|
104
|
+
bitsError: "GLM Metal streamed experts require a Q4 slot",
|
|
105
|
+
perRowError: "GLM Metal streamed experts currently require per-row Q4 scales",
|
|
106
|
+
weightsPerByte: 2,
|
|
107
|
+
weightsMustBeWordAligned: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Resolve the signed two's-complement, per-output-row int8 layout used by
|
|
112
|
+
* GLM-5.2's native MTP routed experts. */
|
|
113
|
+
export function glm52CanonicalQ8MetalLayout(
|
|
114
|
+
layout: Glm52ExpertSlotLayout,
|
|
115
|
+
): Glm52CanonicalQ8MetalLayout {
|
|
116
|
+
return resolveCanonicalMetalLayout(layout, {
|
|
117
|
+
bits: 8,
|
|
118
|
+
label: "Q8",
|
|
119
|
+
bitsError: "GLM MTP Metal streamed experts require a Q8 slot",
|
|
120
|
+
perRowError: "GLM MTP Metal streamed experts require per-row Q8 scales",
|
|
121
|
+
weightsPerByte: 1,
|
|
122
|
+
weightsMustBeWordAligned: false,
|
|
123
|
+
});
|
|
124
|
+
}
|