@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,341 @@
|
|
|
1
|
+
// Model config + quantization metadata reader for the load path.
|
|
2
|
+
// Parses HF config.json (Gemma 4 unified layout: text_config nested),
|
|
3
|
+
// the OptiQ per-layer quantization map, and kv_config.json when present.
|
|
4
|
+
|
|
5
|
+
import { dialectFor } from "./config-dialects";
|
|
6
|
+
import {
|
|
7
|
+
parseGlm52Config,
|
|
8
|
+
type Glm52Config,
|
|
9
|
+
} from "./glm52-config";
|
|
10
|
+
|
|
11
|
+
export interface RopeParams {
|
|
12
|
+
ropeTheta: number;
|
|
13
|
+
ropeType: string;
|
|
14
|
+
partialRotaryFactor: number;
|
|
15
|
+
/** ProportionalRoPE frequency multiplier (rope_utils: factor, default 1). */
|
|
16
|
+
factor: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TextConfig {
|
|
20
|
+
hiddenSize: number;
|
|
21
|
+
numHiddenLayers: number;
|
|
22
|
+
numAttentionHeads: number;
|
|
23
|
+
numKeyValueHeads: number;
|
|
24
|
+
headDim: number;
|
|
25
|
+
/** Global (full-attention) layers can use different KV geometry. */
|
|
26
|
+
numGlobalKeyValueHeads: number;
|
|
27
|
+
globalHeadDim: number;
|
|
28
|
+
attentionKEqV: boolean;
|
|
29
|
+
intermediateSize: number;
|
|
30
|
+
hiddenActivation: string;
|
|
31
|
+
rmsNormEps: number;
|
|
32
|
+
vocabSize: number;
|
|
33
|
+
maxPositionEmbeddings: number;
|
|
34
|
+
slidingWindow: number;
|
|
35
|
+
/** "sliding_attention" | "full_attention" per layer. */
|
|
36
|
+
layerTypes: string[];
|
|
37
|
+
/** e2b/e4b: per-layer input embedding width (0 = disabled). */
|
|
38
|
+
hiddenSizePerLayerInput: number;
|
|
39
|
+
vocabSizePerLayerInput: number;
|
|
40
|
+
/** Trailing layers that share KV with earlier same-type layers. */
|
|
41
|
+
numKvSharedLayers: number;
|
|
42
|
+
/** 26B-A4B MoE block: dense MLP + routed experts per layer. */
|
|
43
|
+
enableMoeBlock: boolean;
|
|
44
|
+
numExperts: number;
|
|
45
|
+
topKExperts: number;
|
|
46
|
+
moeIntermediateSize: number;
|
|
47
|
+
/** Qwen3-MoE sparse-block gating (qwen3_moe): a layer is MoE unless it is
|
|
48
|
+
* in `mlpOnlyLayers` and `(idx+1) % decoderSparseStep == 0` selects it.
|
|
49
|
+
* `normTopkProb` renormalizes the top-k gate weights. Defaults are inert
|
|
50
|
+
* for non-qwen3_moe families. */
|
|
51
|
+
decoderSparseStep: number;
|
|
52
|
+
mlpOnlyLayers: number[];
|
|
53
|
+
normTopkProb: boolean;
|
|
54
|
+
/** Qwen3.5 hybrid (gated-DeltaNet) geometry; 0/false for other families. */
|
|
55
|
+
linearNumValueHeads: number;
|
|
56
|
+
linearNumKeyHeads: number;
|
|
57
|
+
linearKeyHeadDim: number;
|
|
58
|
+
linearValueHeadDim: number;
|
|
59
|
+
linearConvKernelDim: number;
|
|
60
|
+
/** Every `fullAttentionInterval`-th layer is full-attention (Qwen3.5). */
|
|
61
|
+
fullAttentionInterval: number;
|
|
62
|
+
/** Qwen3.5 full-attention output gate: q_proj emits 2× and gates the output. */
|
|
63
|
+
attnOutputGate: boolean;
|
|
64
|
+
/** Rotary fraction of head_dim (Qwen3.5 full attention: 0.25 → 64 of 256). */
|
|
65
|
+
partialRotaryFactor: number;
|
|
66
|
+
ropeParameters: Record<string, RopeParams>;
|
|
67
|
+
finalLogitSoftcapping: number | null;
|
|
68
|
+
tieWordEmbeddings: boolean;
|
|
69
|
+
bosTokenId: number;
|
|
70
|
+
eosTokenId: number | number[];
|
|
71
|
+
/** DiffusionGemma: fixed denoising-canvas length (256). Undefined elsewhere. */
|
|
72
|
+
canvasLength?: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface QuantSpec {
|
|
76
|
+
bits: number;
|
|
77
|
+
groupSize: number;
|
|
78
|
+
mode: string;
|
|
79
|
+
/** `mode: "trellis"` (packed trellis-coded weights, packages/inference/src/layers/trellis-linear.ts):
|
|
80
|
+
* bits = k per weight, groupSize = block T, plus the codec geometry. */
|
|
81
|
+
trellis?: TrellisSpec;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface TrellisSpec {
|
|
85
|
+
L: number;
|
|
86
|
+
code: string;
|
|
87
|
+
/** 1 = coded along the input dim (stored [out, in·k/32]); 0 = along the
|
|
88
|
+
* output dim (stored [in, out·k/32], i.e. Wᵀ coded along its last axis). */
|
|
89
|
+
axis: 0 | 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface QuantizationConfig {
|
|
93
|
+
default: QuantSpec;
|
|
94
|
+
/** Per-module overrides keyed by module path (e.g. "language_model.model.embed_tokens"). */
|
|
95
|
+
perLayer: Map<string, QuantSpec>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface KvQuantSpec {
|
|
99
|
+
layerIdx: number;
|
|
100
|
+
bits: number;
|
|
101
|
+
groupSize: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** TurboQuant scheme (`02d723a:docs/design/turboquant.md`): rotation-based KV
|
|
105
|
+
* quantization, a distinct axis from the uniform/per-layer affine kvQuant
|
|
106
|
+
* above (composes with it in principle; v1 ships as a standalone CLI-only
|
|
107
|
+
* runtime lever, same class as uniform kvBits — see
|
|
108
|
+
* packages/inference/src/generation/autoregressive.ts maybeQuantizeKv). Valid
|
|
109
|
+
* kBits: {2,4,5,8}; vBits: {2,3,4,5,8} (`02d723a:docs/design/turboquant.md`'s
|
|
110
|
+
* supported Lloyd-Max tables). */
|
|
111
|
+
export interface TurboQuantScheme {
|
|
112
|
+
kBits: number;
|
|
113
|
+
vBits: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const TURBOQUANT_VALID_KBITS = [2, 4, 5, 8] as const;
|
|
117
|
+
export const TURBOQUANT_VALID_VBITS = [2, 3, 4, 5, 8] as const;
|
|
118
|
+
|
|
119
|
+
/** Parse `turbo` / `turbo:k<bits>v<bits>` (default k8v3). Returns null for
|
|
120
|
+
* non-turbo strings (caller falls through to the uniform/config parser);
|
|
121
|
+
* throws Error with a user-facing message for a malformed turbo spec. */
|
|
122
|
+
export function parseTurboQuantScheme(raw: string): TurboQuantScheme | null {
|
|
123
|
+
if (raw !== "turbo" && !raw.startsWith("turbo:")) return null;
|
|
124
|
+
if (raw === "turbo") return { kBits: 8, vBits: 3 };
|
|
125
|
+
const m = /^turbo:k(\d+)v(\d+)$/.exec(raw);
|
|
126
|
+
if (!m) throw new Error(`--kv-quant turbo spec must look like "turbo:k<bits>v<bits>" (got "${raw}")`);
|
|
127
|
+
const kBits = Number(m[1]);
|
|
128
|
+
const vBits = Number(m[2]);
|
|
129
|
+
if (!(TURBOQUANT_VALID_KBITS as readonly number[]).includes(kBits))
|
|
130
|
+
throw new Error(`--kv-quant turbo: kBits must be one of ${TURBOQUANT_VALID_KBITS.join(",")} (got ${kBits})`);
|
|
131
|
+
if (!(TURBOQUANT_VALID_VBITS as readonly number[]).includes(vBits))
|
|
132
|
+
throw new Error(`--kv-quant turbo: vBits must be one of ${TURBOQUANT_VALID_VBITS.join(",")} (got ${vBits})`);
|
|
133
|
+
return { kBits, vBits };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface ModelConfig {
|
|
137
|
+
modelDir: string;
|
|
138
|
+
modelType: string;
|
|
139
|
+
architectures: string[];
|
|
140
|
+
dtype: string;
|
|
141
|
+
text: TextConfig;
|
|
142
|
+
quantization: QuantizationConfig | null;
|
|
143
|
+
kvQuant: KvQuantSpec[] | null;
|
|
144
|
+
hasVisionSidecar: boolean;
|
|
145
|
+
eosTokenIds: number[];
|
|
146
|
+
raw: Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseRope(raw: Record<string, any>): Record<string, RopeParams> {
|
|
150
|
+
const out: Record<string, RopeParams> = {};
|
|
151
|
+
for (const [kind, p] of Object.entries(raw ?? {})) {
|
|
152
|
+
// transformers ≥5.10 configs mix flat scalars into the per-attention-type
|
|
153
|
+
// map (`rope_parameters: { full_attention: {...}, rope_theta: null,
|
|
154
|
+
// rope_type: "default", ... }` — the DeepSpec drafter ships this shape);
|
|
155
|
+
// only object entries are attention-type params.
|
|
156
|
+
if (!p || typeof p !== "object") continue;
|
|
157
|
+
out[kind] = {
|
|
158
|
+
ropeTheta: p.rope_theta,
|
|
159
|
+
ropeType: p.rope_type ?? "default",
|
|
160
|
+
partialRotaryFactor: p.partial_rotary_factor ?? 1.0,
|
|
161
|
+
factor: p.factor ?? 1.0,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function parseQuantization(raw: Record<string, any> | undefined): QuantizationConfig | null {
|
|
168
|
+
if (!raw) return null;
|
|
169
|
+
const def: QuantSpec = {
|
|
170
|
+
bits: raw.bits,
|
|
171
|
+
groupSize: raw.group_size,
|
|
172
|
+
mode: raw.mode ?? "affine",
|
|
173
|
+
};
|
|
174
|
+
const perLayer = new Map<string, QuantSpec>();
|
|
175
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
176
|
+
if (key === "bits" || key === "group_size" || key === "mode") continue;
|
|
177
|
+
if (typeof value === "object" && value !== null) {
|
|
178
|
+
const spec: QuantSpec = {
|
|
179
|
+
bits: (value as any).bits ?? def.bits,
|
|
180
|
+
groupSize: (value as any).group_size ?? def.groupSize,
|
|
181
|
+
mode: (value as any).mode ?? def.mode,
|
|
182
|
+
};
|
|
183
|
+
const tr = (value as any).trellis;
|
|
184
|
+
if (spec.mode === "trellis") {
|
|
185
|
+
if (!tr || typeof tr !== "object") throw new Error(`quantization.${key}: mode "trellis" needs a trellis {L, code, axis} block`);
|
|
186
|
+
spec.trellis = { L: Number(tr.L), code: String(tr.code ?? "1mad"), axis: tr.axis === 0 ? 0 : 1 };
|
|
187
|
+
}
|
|
188
|
+
perLayer.set(key, spec);
|
|
189
|
+
} else if (value === false) {
|
|
190
|
+
// mlx convention: `"layer": false` means not quantized
|
|
191
|
+
perLayer.set(key, { bits: 0, groupSize: 0, mode: "none" });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { default: def, perLayer };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Quant spec for a module path, or null if the module is unquantized. */
|
|
198
|
+
export function quantFor(q: QuantizationConfig | null, modulePath: string): QuantSpec | null {
|
|
199
|
+
if (!q) return null;
|
|
200
|
+
const spec = q.perLayer.get(modulePath) ?? q.default;
|
|
201
|
+
return spec.mode === "none" ? null : spec;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function loadModelConfig(modelDir: string): Promise<ModelConfig> {
|
|
205
|
+
const raw = (await Bun.file(`${modelDir}/config.json`).json()) as Record<string, any>;
|
|
206
|
+
const modelType = raw.model_type;
|
|
207
|
+
const dialect = dialectFor(modelType);
|
|
208
|
+
// The converted Colibri artifact keeps stop metadata split across the two
|
|
209
|
+
// HF files. Preserve the old config-only behavior for every other family;
|
|
210
|
+
// GLM-5.2 explicitly unions both lists through its dedicated parser.
|
|
211
|
+
let glm52: Glm52Config | null = null;
|
|
212
|
+
if (dialect.container === "colibri") {
|
|
213
|
+
const generationFile = Bun.file(`${modelDir}/generation_config.json`);
|
|
214
|
+
const generation = await generationFile.exists()
|
|
215
|
+
? await generationFile.json() as Record<string, any>
|
|
216
|
+
: null;
|
|
217
|
+
glm52 = parseGlm52Config(modelDir, raw, generation);
|
|
218
|
+
}
|
|
219
|
+
// Gemma 4 unified nests the LM config; plain text models keep it at top level.
|
|
220
|
+
// Some architectures keep it flat but omit all dims — backfill from their defaults.
|
|
221
|
+
const baseT = (raw.text_config ?? raw) as Record<string, any>;
|
|
222
|
+
const t = dialect.textDefaults ? { ...dialect.textDefaults(), ...baseT } : baseT;
|
|
223
|
+
const qwenRope = (t.rope_parameters ?? {}) as Record<string, any>;
|
|
224
|
+
dialect.validate?.(t);
|
|
225
|
+
|
|
226
|
+
const text: TextConfig = {
|
|
227
|
+
hiddenSize: glm52?.hiddenSize ?? t.hidden_size,
|
|
228
|
+
numHiddenLayers: glm52?.numHiddenLayers ?? t.num_hidden_layers,
|
|
229
|
+
numAttentionHeads: glm52?.numAttentionHeads ?? t.num_attention_heads,
|
|
230
|
+
numKeyValueHeads: glm52?.numKeyValueHeads ?? t.num_key_value_heads,
|
|
231
|
+
headDim: glm52?.qkHeadDim ?? t.head_dim ?? Math.floor(t.hidden_size / t.num_attention_heads),
|
|
232
|
+
numGlobalKeyValueHeads:
|
|
233
|
+
glm52?.numKeyValueHeads ?? t.num_global_key_value_heads ?? t.num_key_value_heads,
|
|
234
|
+
globalHeadDim:
|
|
235
|
+
glm52?.qkHeadDim ??
|
|
236
|
+
t.global_head_dim ??
|
|
237
|
+
t.head_dim ??
|
|
238
|
+
Math.floor(t.hidden_size / t.num_attention_heads),
|
|
239
|
+
attentionKEqV: t.attention_k_eq_v ?? false,
|
|
240
|
+
intermediateSize: glm52?.intermediateSize ?? t.intermediate_size,
|
|
241
|
+
hiddenActivation: t.hidden_activation ?? t.hidden_act ?? "gelu_pytorch_tanh",
|
|
242
|
+
rmsNormEps: glm52?.rmsNormEps ?? t.rms_norm_eps,
|
|
243
|
+
vocabSize: glm52?.vocabSize ?? t.vocab_size,
|
|
244
|
+
maxPositionEmbeddings:
|
|
245
|
+
glm52?.maxPositionEmbeddings ?? t.max_position_embeddings,
|
|
246
|
+
slidingWindow: t.sliding_window ?? 0,
|
|
247
|
+
layerTypes: glm52
|
|
248
|
+
? Array(glm52.numHiddenLayers).fill("full_attention")
|
|
249
|
+
: t.layer_types ?? (dialect.fullAttentionByDefault ? Array(t.num_hidden_layers).fill("full_attention") : []),
|
|
250
|
+
hiddenSizePerLayerInput: t.hidden_size_per_layer_input ?? 0,
|
|
251
|
+
vocabSizePerLayerInput:
|
|
252
|
+
t.vocab_size_per_layer_input ?? glm52?.vocabSize ?? t.vocab_size,
|
|
253
|
+
numKvSharedLayers: t.num_kv_shared_layers ?? 0,
|
|
254
|
+
enableMoeBlock: glm52 ? true : t.enable_moe_block ?? false,
|
|
255
|
+
numExperts: glm52?.numRoutedExperts ?? t.num_experts ?? 0,
|
|
256
|
+
// gemma4 uses `top_k_experts`; qwen3_moe uses `num_experts_per_tok`.
|
|
257
|
+
topKExperts:
|
|
258
|
+
glm52?.numExpertsPerToken ?? t.top_k_experts ?? t.num_experts_per_tok ?? 0,
|
|
259
|
+
moeIntermediateSize: glm52?.moeIntermediateSize ?? t.moe_intermediate_size ?? 0,
|
|
260
|
+
decoderSparseStep: glm52 ? 1 : t.decoder_sparse_step ?? 1,
|
|
261
|
+
mlpOnlyLayers: glm52
|
|
262
|
+
? Array.from({ length: glm52.firstKDenseReplace }, (_, layer) => layer)
|
|
263
|
+
: t.mlp_only_layers ?? [],
|
|
264
|
+
normTopkProb: glm52?.normTopkProb ?? t.norm_topk_prob ?? false,
|
|
265
|
+
linearNumValueHeads: t.linear_num_value_heads ?? 0,
|
|
266
|
+
linearNumKeyHeads: t.linear_num_key_heads ?? 0,
|
|
267
|
+
linearKeyHeadDim: t.linear_key_head_dim ?? 0,
|
|
268
|
+
linearValueHeadDim: t.linear_value_head_dim ?? 0,
|
|
269
|
+
linearConvKernelDim: t.linear_conv_kernel_dim ?? 0,
|
|
270
|
+
fullAttentionInterval: t.full_attention_interval ?? 0,
|
|
271
|
+
attnOutputGate: t.attn_output_gate ?? false,
|
|
272
|
+
partialRotaryFactor: glm52
|
|
273
|
+
? glm52.qkRopeHeadDim / glm52.qkHeadDim
|
|
274
|
+
: qwenRope.partial_rotary_factor ?? t.partial_rotary_factor ?? 1.0,
|
|
275
|
+
ropeParameters: glm52
|
|
276
|
+
? {
|
|
277
|
+
full_attention: {
|
|
278
|
+
ropeTheta: glm52.ropeTheta,
|
|
279
|
+
ropeType: "default",
|
|
280
|
+
partialRotaryFactor: glm52.qkRopeHeadDim / glm52.qkHeadDim,
|
|
281
|
+
factor: 1.0,
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
: dialect.rope === "flat"
|
|
285
|
+
? {
|
|
286
|
+
full_attention: {
|
|
287
|
+
ropeTheta: qwenRope.rope_theta ?? t.rope_theta ?? 10000,
|
|
288
|
+
ropeType: "default",
|
|
289
|
+
partialRotaryFactor: qwenRope.partial_rotary_factor ?? 1.0,
|
|
290
|
+
factor: 1.0,
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
: t.rope_parameters
|
|
294
|
+
? parseRope(t.rope_parameters)
|
|
295
|
+
: dialect.rope === "theta"
|
|
296
|
+
? {
|
|
297
|
+
full_attention: {
|
|
298
|
+
ropeTheta: t.rope_theta ?? 10000,
|
|
299
|
+
ropeType: "default",
|
|
300
|
+
partialRotaryFactor: 1.0,
|
|
301
|
+
factor: 1.0,
|
|
302
|
+
},
|
|
303
|
+
}
|
|
304
|
+
: {},
|
|
305
|
+
finalLogitSoftcapping: t.final_logit_softcapping ?? null,
|
|
306
|
+
tieWordEmbeddings: t.tie_word_embeddings ?? raw.tie_word_embeddings ?? false,
|
|
307
|
+
bosTokenId: t.bos_token_id ?? raw.bos_token_id,
|
|
308
|
+
eosTokenId: glm52?.eosTokenIds ?? t.eos_token_id ?? raw.eos_token_id,
|
|
309
|
+
canvasLength: raw.canvas_length ?? t.canvas_length,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
let kvQuant: KvQuantSpec[] | null = null;
|
|
313
|
+
if (await Bun.file(`${modelDir}/kv_config.json`).exists()) {
|
|
314
|
+
const kv = (await Bun.file(`${modelDir}/kv_config.json`).json()) as any[];
|
|
315
|
+
kvQuant = kv.map((e) => ({
|
|
316
|
+
layerIdx: e.layer_idx,
|
|
317
|
+
bits: e.bits,
|
|
318
|
+
groupSize: e.group_size,
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const eos = glm52?.eosTokenIds ?? raw.eos_token_id ?? text.eosTokenId;
|
|
323
|
+
return {
|
|
324
|
+
modelDir,
|
|
325
|
+
modelType,
|
|
326
|
+
architectures: raw.architectures ?? [],
|
|
327
|
+
dtype: raw.dtype ?? "bfloat16",
|
|
328
|
+
text,
|
|
329
|
+
// Colibri's config retains the SOURCE FP8 quantization_config, while the
|
|
330
|
+
// serving shards are converted U8 + `.qs` tensors whose int4/int8/group
|
|
331
|
+
// geometry is inferred and validated by Glm52Container. Advertising the
|
|
332
|
+
// source FP8 block as mlx affine quantization would be actively wrong.
|
|
333
|
+
quantization: glm52
|
|
334
|
+
? null
|
|
335
|
+
: parseQuantization(raw.quantization ?? raw.quantization_config),
|
|
336
|
+
kvQuant,
|
|
337
|
+
hasVisionSidecar: await Bun.file(`${modelDir}/optiq_vision.safetensors`).exists(),
|
|
338
|
+
eosTokenIds: Array.isArray(eos) ? eos : [eos],
|
|
339
|
+
raw,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// A frequency-ranked draft vocabulary: `draft_vocab.json`, `{"ids": [...]}`, the
|
|
2
|
+
// target token ids a drafter may propose (FR-Spec, Zhao et al. 2025). The Qwen3.8
|
|
3
|
+
// Trellis artifact ships one in its MTP companion directory (`mtp/`); the
|
|
4
|
+
// companion's drafts and, where a graph's composition declares it, a tapped
|
|
5
|
+
// drafter's select over those rows of the target head.
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
|
|
9
|
+
export const DRAFT_VOCAB_FILE = "draft_vocab.json";
|
|
10
|
+
|
|
11
|
+
const lists = new Map<string, readonly number[]>();
|
|
12
|
+
|
|
13
|
+
/** The list at `path`: distinct ids in ascending order, read once per path.
|
|
14
|
+
* Throws when the file holds no ids, an id is not a nonnegative integer, or one
|
|
15
|
+
* falls outside the target's `vocabSize`. */
|
|
16
|
+
export function readDraftVocabulary(path: string, vocabSize: number): readonly number[] {
|
|
17
|
+
let ids = lists.get(path);
|
|
18
|
+
if (!ids) {
|
|
19
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as { ids?: unknown };
|
|
20
|
+
if (!Array.isArray(parsed.ids) || parsed.ids.length === 0)
|
|
21
|
+
throw new Error(`draft vocabulary ${path}: expected {"ids":[...]}`);
|
|
22
|
+
const sorted = [...new Set(parsed.ids as number[])].sort((a, b) => a - b);
|
|
23
|
+
if (!sorted.every(id => Number.isInteger(id) && id >= 0))
|
|
24
|
+
throw new Error(`draft vocabulary ${path}: ids must be nonnegative integers`);
|
|
25
|
+
lists.set(path, ids = Object.freeze(sorted));
|
|
26
|
+
}
|
|
27
|
+
if (ids.at(-1)! >= vocabSize)
|
|
28
|
+
throw new Error(`draft vocabulary ${path}: id ${ids.at(-1)} outside the target vocabulary ${vocabSize}`);
|
|
29
|
+
return ids;
|
|
30
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Expert offload — BUILD. Produces the page-aligned offload file
|
|
2
|
+
// (experts.bin + manifest.json) that `activateExpertOffload` serves: each
|
|
3
|
+
// expert tensor's bytes are copied to a 16 KB-aligned offset so the GPU can
|
|
4
|
+
// gather it directly from a read-only file mmap (safetensors packs tensors at
|
|
5
|
+
// arbitrary offsets). Kept apart from the runtime module so the hot model path
|
|
6
|
+
// never imports the converter code.
|
|
7
|
+
|
|
8
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, writeFileSync, writeSync } from "node:fs";
|
|
9
|
+
import { ShardedSafetensors } from "./safetensors";
|
|
10
|
+
import type { OffloadEntry, OffloadManifest } from "./expert-offload";
|
|
11
|
+
|
|
12
|
+
const PAGE = 16384;
|
|
13
|
+
const alignUp = (n: number, a: number) => Math.ceil(n / a) * a;
|
|
14
|
+
const isExpertTensor = (n: string) => n.includes("switch_glu") || n.includes(".experts.");
|
|
15
|
+
const layerOf = (n: string) => Number(n.match(/layers\.(\d+)/)?.[1] ?? -1);
|
|
16
|
+
|
|
17
|
+
/** Reuse `<modelPath>/.mlx-bun-offload` when present and valid for this model,
|
|
18
|
+
* else build it. Returns the offload dir to pass to `activateExpertOffload`. */
|
|
19
|
+
export async function ensureOffloadFile(
|
|
20
|
+
modelPath: string,
|
|
21
|
+
onProgress?: (message: string) => void,
|
|
22
|
+
outDir = `${modelPath}/.mlx-bun-offload`,
|
|
23
|
+
): Promise<string> {
|
|
24
|
+
const bin = `${outDir}/experts.bin`;
|
|
25
|
+
const man = `${outDir}/manifest.json`;
|
|
26
|
+
if (existsSync(bin) && existsSync(man)) {
|
|
27
|
+
try {
|
|
28
|
+
const manifest = JSON.parse(readFileSync(man, "utf8")) as OffloadManifest;
|
|
29
|
+
if (manifest.model === modelPath && statSync(bin).size === manifest.totalBytes) {
|
|
30
|
+
onProgress?.(`reusing ${bin}`);
|
|
31
|
+
return outDir;
|
|
32
|
+
}
|
|
33
|
+
} catch { /* fall through to rebuild */ }
|
|
34
|
+
}
|
|
35
|
+
await buildOffloadFile(modelPath, outDir, onProgress);
|
|
36
|
+
return outDir;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Pack every expert tensor (layers below `layerCap`) into a page-aligned file. */
|
|
40
|
+
export async function buildOffloadFile(
|
|
41
|
+
modelPath: string,
|
|
42
|
+
outDir: string,
|
|
43
|
+
onProgress?: (message: string) => void,
|
|
44
|
+
layerCap = Infinity,
|
|
45
|
+
): Promise<OffloadManifest> {
|
|
46
|
+
const shards = await ShardedSafetensors.open(modelPath);
|
|
47
|
+
const names = shards.tensorNames
|
|
48
|
+
.filter(isExpertTensor)
|
|
49
|
+
.filter(name => layerOf(name) < layerCap)
|
|
50
|
+
.sort((a, b) => layerOf(a) - layerOf(b) || a.localeCompare(b));
|
|
51
|
+
if (!names.length) throw new Error(`${modelPath}: no expert tensors (not an MoE model?)`);
|
|
52
|
+
|
|
53
|
+
mkdirSync(outDir, { recursive: true });
|
|
54
|
+
const fd = openSync(`${outDir}/experts.bin`, "w");
|
|
55
|
+
const pad = Buffer.alloc(PAGE);
|
|
56
|
+
let offset = 0;
|
|
57
|
+
let count = 0;
|
|
58
|
+
const tensors: OffloadEntry[] = [];
|
|
59
|
+
try {
|
|
60
|
+
for (const name of names) {
|
|
61
|
+
const aligned = alignUp(offset, PAGE);
|
|
62
|
+
if (aligned > offset) { writeSync(fd, pad, 0, aligned - offset); offset = aligned; }
|
|
63
|
+
const info = shards.info(name);
|
|
64
|
+
const bytes = shards.view(name); // zero-copy view into the source mmap
|
|
65
|
+
writeSync(fd, bytes);
|
|
66
|
+
tensors.push({ name, offset, length: bytes.length, dtype: info.dtype, shape: info.shape });
|
|
67
|
+
offset += bytes.length;
|
|
68
|
+
if (++count % 30 === 0) onProgress?.(`packing experts ${(offset / 1e9).toFixed(1)} GB (${count}/${names.length})`);
|
|
69
|
+
}
|
|
70
|
+
} finally { closeSync(fd); }
|
|
71
|
+
const manifest: OffloadManifest = { page: PAGE, model: modelPath, totalBytes: offset, tensors };
|
|
72
|
+
writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest));
|
|
73
|
+
onProgress?.(`wrote ${(offset / 1e9).toFixed(2)} GB, ${tensors.length} tensors, ${new Set(names.map(layerOf)).size} layers`);
|
|
74
|
+
return manifest;
|
|
75
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// E1 expert offload — RUNTIME.
|
|
2
|
+
//
|
|
3
|
+
// Routes expert WEIGHT tensors through a read-only file mmap (clean,
|
|
4
|
+
// page-aligned pages that cost ~0 phys_footprint and that the GPU gathers
|
|
5
|
+
// directly — probes confirmed bit-exact + non-wiring) instead of the
|
|
6
|
+
// anonymous mlx_load_safetensors copy that counts against memory pressure.
|
|
7
|
+
//
|
|
8
|
+
// Activated two ways, both before model construction:
|
|
9
|
+
// - env MLX_BUN_EXPERT_OFFLOAD=<dir> (scripts / direct runs)
|
|
10
|
+
// - activateExpertOffload(dir) (CLI `--expert-offload`)
|
|
11
|
+
// The file is produced by packages/inference/src/artifacts/expert-offload-build.ts. Tensors not in the
|
|
12
|
+
// manifest (or when never activated) fall back to the resident path, so this
|
|
13
|
+
// is inert by default and safe on a partially-converted file.
|
|
14
|
+
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { MmapFile } from "./mmap";
|
|
17
|
+
import { MlxArray, SAFETENSORS_TO_MLX } from "@mlx-bun/mlx/array";
|
|
18
|
+
import type { SafetensorsDtype } from "./safetensors";
|
|
19
|
+
import { runtimeValue } from "../runtime/config";
|
|
20
|
+
|
|
21
|
+
export interface OffloadEntry {
|
|
22
|
+
name: string;
|
|
23
|
+
offset: number;
|
|
24
|
+
length: number;
|
|
25
|
+
dtype: SafetensorsDtype;
|
|
26
|
+
shape: number[];
|
|
27
|
+
}
|
|
28
|
+
export interface OffloadManifest {
|
|
29
|
+
page: number;
|
|
30
|
+
model: string;
|
|
31
|
+
totalBytes: number;
|
|
32
|
+
tensors: OffloadEntry[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let mm: MmapFile | null = null;
|
|
36
|
+
let manifest: Map<string, OffloadEntry> | null = null;
|
|
37
|
+
|
|
38
|
+
/** Open the offload file at `dir` (experts.bin + manifest.json) and route
|
|
39
|
+
* expert weights through it. Call BEFORE the model is constructed.
|
|
40
|
+
*
|
|
41
|
+
* Routing is process-global. The returned function restores the routing that
|
|
42
|
+
* was active before this call, so an owner that activated a file for one
|
|
43
|
+
* model can stop later loads from resolving against it. It never unmaps:
|
|
44
|
+
* weights already created from the mapping keep borrowing its bytes for the
|
|
45
|
+
* process lifetime (see `expertOffloadArray`). */
|
|
46
|
+
export function activateExpertOffload(dir: string): () => void {
|
|
47
|
+
// Resolve both halves before touching either global: a failed open leaves
|
|
48
|
+
// whatever routing was active untouched.
|
|
49
|
+
const parsed = JSON.parse(readFileSync(`${dir}/manifest.json`, "utf8")) as OffloadManifest;
|
|
50
|
+
const next = new Map(parsed.tensors.map((t) => [t.name, t]));
|
|
51
|
+
const mapping = MmapFile.open(`${dir}/experts.bin`, "ro");
|
|
52
|
+
const previous = { mm, manifest };
|
|
53
|
+
manifest = next;
|
|
54
|
+
mm = mapping;
|
|
55
|
+
process.stderr.write(`[expert-offload] mmap ${dir}/experts.bin (${parsed.tensors.length} tensors)\n`);
|
|
56
|
+
return () => { mm = previous.mm; manifest = previous.manifest; };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function isExpertOffload(): boolean {
|
|
60
|
+
return mm !== null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Zero-copy mmap-backed array for `name` if offload is active and the tensor
|
|
64
|
+
* is in the manifest; else null. The converter aligns every tensor to 16 KB
|
|
65
|
+
* and the mmap base is page-aligned, so the pointer is page-aligned —
|
|
66
|
+
* required for the GPU to gather from it.
|
|
67
|
+
*
|
|
68
|
+
* Lifetime: these arrays are created ONCE at model load
|
|
69
|
+
* (QuantizedSwitchLinear.load, packages/inference/src/layers/quantized-switch-linear.ts)
|
|
70
|
+
* and live as model weights;
|
|
71
|
+
* every decode's gather_qmm references them, so GPU command buffers retain
|
|
72
|
+
* the mlx buffer past any dispose and mlx may drop the LAST reference on
|
|
73
|
+
* the Metal completion thread. That's why this must be fromPointer (native
|
|
74
|
+
* free(NULL) dtor — safe from any thread), NOT the old fromView JSCallback
|
|
75
|
+
* pin (the 2026-07-06 deadlock class). The memory contract is trivially
|
|
76
|
+
* met: `mm` is module-level and never unmapped — the mapping owns the
|
|
77
|
+
* bytes for the process (clean file-backed pages, ~0 phys_footprint). */
|
|
78
|
+
export function expertOffloadArray(name: string): MlxArray | null {
|
|
79
|
+
if (!mm || !manifest) return null;
|
|
80
|
+
const e = manifest.get(name);
|
|
81
|
+
if (!e) return null;
|
|
82
|
+
// Bounds check the whole tensor (pointer() only checks the start).
|
|
83
|
+
if (e.offset + e.length > mm.size)
|
|
84
|
+
throw new Error(`expert-offload: ${name} [${e.offset}, +${e.length}) exceeds ${mm.path} (${mm.size} B)`);
|
|
85
|
+
return MlxArray.fromPointer(mm.pointer(e.offset), e.shape, SAFETENSORS_TO_MLX[e.dtype]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const expertOffloadPath = runtimeValue("MLX_BUN_EXPERT_OFFLOAD");
|
|
89
|
+
if (expertOffloadPath) activateExpertOffload(expertOffloadPath);
|