@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,249 @@
|
|
|
1
|
+
// Sharded safetensors writer: takes a list of named mlx arrays and writes
|
|
2
|
+
// them to disk as one or more `model*.safetensors` files via mlx's native
|
|
3
|
+
// saver (mlx_save_safetensors), matching the on-disk layout the loader
|
|
4
|
+
// (packages/inference/src/artifacts/weights.ts → ShardedSafetensors) reads back.
|
|
5
|
+
//
|
|
6
|
+
// Layout parity with mlx-lm / the MiniCPM5-OptiQ target:
|
|
7
|
+
// - single shard → `model.safetensors`, NO index file.
|
|
8
|
+
// - multiple shards → `model-00001-of-0000N.safetensors`, plus a
|
|
9
|
+
// `model.safetensors.index.json` { metadata:{total_size,
|
|
10
|
+
// total_parameters}, weight_map: <name → shard> }.
|
|
11
|
+
// - metadata map carries {"format":"mlx"} so the file round-trips through
|
|
12
|
+
// mlx_load_safetensors with the same provenance HF/mlx writes.
|
|
13
|
+
//
|
|
14
|
+
// Memory discipline: each array is eval'd immediately before insertion so its
|
|
15
|
+
// bytes are materialized one tensor at a time (the caller hands us already-
|
|
16
|
+
// disposed-after-write arrays), bounding peak memory to ~one tensor over the
|
|
17
|
+
// resident weight set rather than the whole model.
|
|
18
|
+
|
|
19
|
+
import { ptr } from "bun:ffi";
|
|
20
|
+
import { renameSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { C, takeMlxError } from "@mlx-bun/mlx/ffi";
|
|
23
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
24
|
+
|
|
25
|
+
const cstr = (s: string) => Buffer.from(s + "\0", "utf8");
|
|
26
|
+
|
|
27
|
+
/** Default greedy shard ceiling: 5 GiB (mlx-lm's MAX shard size). */
|
|
28
|
+
export const DEFAULT_SHARD_BYTES = 5 << 30;
|
|
29
|
+
|
|
30
|
+
export interface NamedTensor {
|
|
31
|
+
name: string;
|
|
32
|
+
array: MlxArray;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ShardInfo {
|
|
36
|
+
/** Filename written into outDir (e.g. "model.safetensors"). */
|
|
37
|
+
file: string;
|
|
38
|
+
/** Tensor names placed in this shard, in insertion order. */
|
|
39
|
+
names: string[];
|
|
40
|
+
/** Byte size of the tensors in this shard (sum of nbytes). */
|
|
41
|
+
bytes: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SafetensorsIndex {
|
|
45
|
+
metadata: { total_size: number; total_parameters: number };
|
|
46
|
+
weight_map: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface WriteResult {
|
|
50
|
+
shards: ShardInfo[];
|
|
51
|
+
totalSize: number;
|
|
52
|
+
totalParams: number;
|
|
53
|
+
/** The index object (also written to disk when shards.length > 1). */
|
|
54
|
+
index: SafetensorsIndex;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WriteOpts {
|
|
58
|
+
/** Greedy shard ceiling in bytes (default DEFAULT_SHARD_BYTES). */
|
|
59
|
+
shardBytes?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Insert one array into a native string→array map (key cstring, handle u64).
|
|
63
|
+
* Evals the array first so its bytes are materialized exactly here. */
|
|
64
|
+
function insertArray(map: bigint, name: string, array: MlxArray): void {
|
|
65
|
+
array.eval();
|
|
66
|
+
if (C.mlx_map_string_to_array_insert(map, ptr(cstr(name)), array.handle) !== 0)
|
|
67
|
+
throw new Error(`map insert ${name} failed: ${takeMlxError() ?? ""}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Build the {"format":"mlx"} metadata map. Caller frees it. */
|
|
71
|
+
function newMetaMap(): bigint {
|
|
72
|
+
const map = C.mlx_map_string_to_string_new();
|
|
73
|
+
if (C.mlx_map_string_to_string_insert(map, ptr(cstr("format")), ptr(cstr("mlx"))) !== 0)
|
|
74
|
+
throw new Error(`metadata insert failed: ${takeMlxError() ?? ""}`);
|
|
75
|
+
return map;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Pack the shard's tensors into a fresh native map and save to `path`. */
|
|
79
|
+
function saveShard(path: string, tensors: NamedTensor[]): void {
|
|
80
|
+
const arrMap = C.mlx_map_string_to_array_new();
|
|
81
|
+
const metaMap = newMetaMap();
|
|
82
|
+
try {
|
|
83
|
+
for (const { name, array } of tensors) insertArray(arrMap, name, array);
|
|
84
|
+
if (C.mlx_save_safetensors(ptr(cstr(path)), arrMap, metaMap) !== 0)
|
|
85
|
+
throw new Error(`mlx_save_safetensors(${path}) failed: ${takeMlxError() ?? ""}`);
|
|
86
|
+
} finally {
|
|
87
|
+
C.mlx_map_string_to_array_free(arrMap);
|
|
88
|
+
C.mlx_map_string_to_string_free(metaMap);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Shard filename for shard i of n (1-based, 5-digit, matches mlx-lm). */
|
|
93
|
+
function shardName(i: number, n: number): string {
|
|
94
|
+
if (n === 1) return "model.safetensors";
|
|
95
|
+
const pad = (x: number) => String(x).padStart(5, "0");
|
|
96
|
+
return `model-${pad(i + 1)}-of-${pad(n)}.safetensors`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Greedily bin tensors into shards no larger than `shardBytes`. A single
|
|
100
|
+
* tensor larger than the ceiling still gets its own shard (never split). */
|
|
101
|
+
function planShards(tensors: NamedTensor[], shardBytes: number): NamedTensor[][] {
|
|
102
|
+
const shards: NamedTensor[][] = [];
|
|
103
|
+
let cur: NamedTensor[] = [];
|
|
104
|
+
let curBytes = 0;
|
|
105
|
+
for (const t of tensors) {
|
|
106
|
+
const nb = t.array.nbytes;
|
|
107
|
+
if (cur.length > 0 && curBytes + nb > shardBytes) {
|
|
108
|
+
shards.push(cur);
|
|
109
|
+
cur = [];
|
|
110
|
+
curBytes = 0;
|
|
111
|
+
}
|
|
112
|
+
cur.push(t);
|
|
113
|
+
curBytes += nb;
|
|
114
|
+
}
|
|
115
|
+
if (cur.length > 0) shards.push(cur);
|
|
116
|
+
return shards.length > 0 ? shards : [[]];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Write `tensors` to `outDir` as sharded safetensors. Returns shard layout,
|
|
121
|
+
* total byte size, total parameter count, and the (single source of truth)
|
|
122
|
+
* index object. An index file is written only when there is more than one
|
|
123
|
+
* shard — matching the loader's expectation that a lone `model.safetensors`
|
|
124
|
+
* carries no sidecar index.
|
|
125
|
+
*/
|
|
126
|
+
export function writeShardedSafetensors(
|
|
127
|
+
outDir: string,
|
|
128
|
+
tensors: NamedTensor[],
|
|
129
|
+
opts: WriteOpts = {},
|
|
130
|
+
): WriteResult {
|
|
131
|
+
const shardBytes = opts.shardBytes ?? DEFAULT_SHARD_BYTES;
|
|
132
|
+
const plan = planShards(tensors, shardBytes);
|
|
133
|
+
const n = plan.length;
|
|
134
|
+
|
|
135
|
+
const shards: ShardInfo[] = [];
|
|
136
|
+
const weight_map: Record<string, string> = {};
|
|
137
|
+
let totalSize = 0;
|
|
138
|
+
let totalParams = 0;
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < n; i++) {
|
|
141
|
+
const group = plan[i]!;
|
|
142
|
+
const file = shardName(i, n);
|
|
143
|
+
let bytes = 0;
|
|
144
|
+
for (const t of group) {
|
|
145
|
+
bytes += t.array.nbytes;
|
|
146
|
+
totalParams += t.array.size;
|
|
147
|
+
weight_map[t.name] = file;
|
|
148
|
+
}
|
|
149
|
+
totalSize += bytes;
|
|
150
|
+
saveShard(join(outDir, file), group);
|
|
151
|
+
shards.push({ file, names: group.map((t) => t.name), bytes });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// weight_map sorted by name for a stable, diff-friendly index.
|
|
155
|
+
const sortedMap: Record<string, string> = {};
|
|
156
|
+
for (const name of Object.keys(weight_map).sort()) sortedMap[name] = weight_map[name]!;
|
|
157
|
+
|
|
158
|
+
const index: SafetensorsIndex = {
|
|
159
|
+
metadata: { total_size: totalSize, total_parameters: totalParams },
|
|
160
|
+
weight_map: sortedMap,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
if (n > 1) {
|
|
164
|
+
writeFileSync(
|
|
165
|
+
join(outDir, "model.safetensors.index.json"),
|
|
166
|
+
JSON.stringify(index, null, 2),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { shards, totalSize, totalParams, index };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Incremental sharded writer for model-scale streaming transforms (27B fold/
|
|
175
|
+
* quantize): tensors are added one at a time; when the current shard reaches
|
|
176
|
+
* the byte ceiling it is SAVED AND ITS ARRAYS DISPOSED immediately, so peak
|
|
177
|
+
* memory is one shard plus the tensor in flight — never the whole model
|
|
178
|
+
* (the whole-list writeShardedSafetensors path holds every array until the
|
|
179
|
+
* caller disposes them, which OOMs a 51 GB model on a 32 GB box).
|
|
180
|
+
*
|
|
181
|
+
* Shards are written under temp names (the final `-of-NNNNN` count is unknown
|
|
182
|
+
* until finish()) and renamed at finish, which also writes the index.
|
|
183
|
+
* add() takes ownership of the array (evaluates it now, disposes after save).
|
|
184
|
+
*/
|
|
185
|
+
export class ShardedWriter {
|
|
186
|
+
readonly #outDir: string;
|
|
187
|
+
readonly #shardBytes: number;
|
|
188
|
+
#cur: NamedTensor[] = [];
|
|
189
|
+
#curBytes = 0;
|
|
190
|
+
#done: { tmp: string; names: string[]; bytes: number }[] = [];
|
|
191
|
+
#totalParams = 0;
|
|
192
|
+
|
|
193
|
+
constructor(outDir: string, opts: WriteOpts = {}) {
|
|
194
|
+
this.#outDir = outDir;
|
|
195
|
+
this.#shardBytes = opts.shardBytes ?? DEFAULT_SHARD_BYTES;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
add(name: string, array: MlxArray): void {
|
|
199
|
+
array.eval();
|
|
200
|
+
const nb = array.nbytes;
|
|
201
|
+
if (this.#cur.length > 0 && this.#curBytes + nb > this.#shardBytes) this.#flush();
|
|
202
|
+
this.#cur.push({ name, array });
|
|
203
|
+
this.#curBytes += nb;
|
|
204
|
+
this.#totalParams += array.size;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#flush(): void {
|
|
208
|
+
if (this.#cur.length === 0) return;
|
|
209
|
+
const tmp = `model-shard-${String(this.#done.length + 1).padStart(5, "0")}.tmp.safetensors`;
|
|
210
|
+
saveShard(join(this.#outDir, tmp), this.#cur);
|
|
211
|
+
this.#done.push({
|
|
212
|
+
tmp,
|
|
213
|
+
names: this.#cur.map((t) => t.name),
|
|
214
|
+
bytes: this.#curBytes,
|
|
215
|
+
});
|
|
216
|
+
for (const t of this.#cur) t.array.dispose();
|
|
217
|
+
this.#cur = [];
|
|
218
|
+
this.#curBytes = 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
finish(): WriteResult {
|
|
222
|
+
this.#flush();
|
|
223
|
+
const n = this.#done.length;
|
|
224
|
+
const shards: ShardInfo[] = [];
|
|
225
|
+
const weight_map: Record<string, string> = {};
|
|
226
|
+
let totalSize = 0;
|
|
227
|
+
for (let i = 0; i < n; i++) {
|
|
228
|
+
const d = this.#done[i]!;
|
|
229
|
+
const file = shardName(i, n);
|
|
230
|
+
renameSync(join(this.#outDir, d.tmp), join(this.#outDir, file));
|
|
231
|
+
for (const name of d.names) weight_map[name] = file;
|
|
232
|
+
totalSize += d.bytes;
|
|
233
|
+
shards.push({ file, names: d.names, bytes: d.bytes });
|
|
234
|
+
}
|
|
235
|
+
const sortedMap: Record<string, string> = {};
|
|
236
|
+
for (const name of Object.keys(weight_map).sort()) sortedMap[name] = weight_map[name]!;
|
|
237
|
+
const index: SafetensorsIndex = {
|
|
238
|
+
metadata: { total_size: totalSize, total_parameters: this.#totalParams },
|
|
239
|
+
weight_map: sortedMap,
|
|
240
|
+
};
|
|
241
|
+
if (n > 1) {
|
|
242
|
+
writeFileSync(
|
|
243
|
+
join(this.#outDir, "model.safetensors.index.json"),
|
|
244
|
+
JSON.stringify(index, null, 2),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return { shards, totalSize, totalParams: this.#totalParams, index };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Safetensors reader: mmap the file (via libc — see mmap.ts for why not
|
|
2
|
+
// Bun.mmap), parse the JSON header, expose tensors as raw pointers into
|
|
3
|
+
// the mapping. No tensor bytes are ever copied; pages materialize only
|
|
4
|
+
// when something (the GPU) touches them.
|
|
5
|
+
//
|
|
6
|
+
// Format: [u64 LE header_len][header_len bytes of JSON][data...]
|
|
7
|
+
// Header: { "<name>": { dtype, shape, data_offsets: [begin, end] }, ... }
|
|
8
|
+
// with optional "__metadata__"; offsets are relative to the data section.
|
|
9
|
+
|
|
10
|
+
import { MmapFile } from "./mmap";
|
|
11
|
+
|
|
12
|
+
import type { SafetensorsDtype } from "@mlx-bun/mlx/array";
|
|
13
|
+
export type { SafetensorsDtype } from "@mlx-bun/mlx/array";
|
|
14
|
+
|
|
15
|
+
export const DTYPE_SIZE: Record<SafetensorsDtype, number> = {
|
|
16
|
+
BOOL: 1, U8: 1, I8: 1, U16: 2, I16: 2, U32: 4, I32: 4, U64: 8, I64: 8,
|
|
17
|
+
F16: 2, BF16: 2, F32: 4, F64: 8,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface TensorInfo {
|
|
21
|
+
name: string;
|
|
22
|
+
dtype: SafetensorsDtype;
|
|
23
|
+
shape: number[];
|
|
24
|
+
/** Byte range relative to the start of the data section. */
|
|
25
|
+
begin: number;
|
|
26
|
+
end: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SafetensorsFile {
|
|
30
|
+
readonly path: string;
|
|
31
|
+
readonly metadata: Record<string, string>;
|
|
32
|
+
readonly tensors: Map<string, TensorInfo>;
|
|
33
|
+
readonly mmap: MmapFile;
|
|
34
|
+
readonly #dataStart: number;
|
|
35
|
+
|
|
36
|
+
private constructor(
|
|
37
|
+
path: string,
|
|
38
|
+
mmap: MmapFile,
|
|
39
|
+
tensors: Map<string, TensorInfo>,
|
|
40
|
+
metadata: Record<string, string>,
|
|
41
|
+
dataStart: number,
|
|
42
|
+
) {
|
|
43
|
+
this.path = path;
|
|
44
|
+
this.mmap = mmap;
|
|
45
|
+
this.tensors = tensors;
|
|
46
|
+
this.metadata = metadata;
|
|
47
|
+
this.#dataStart = dataStart;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static open(path: string): SafetensorsFile {
|
|
51
|
+
const mmap = MmapFile.open(path);
|
|
52
|
+
if (mmap.size < 8) throw new Error(`${path}: too small for safetensors`);
|
|
53
|
+
|
|
54
|
+
const lenBytes = mmap.view(0, 8);
|
|
55
|
+
const headerLen = Number(
|
|
56
|
+
new DataView(lenBytes.buffer, lenBytes.byteOffset, 8).getBigUint64(0, true),
|
|
57
|
+
);
|
|
58
|
+
const dataStart = 8 + headerLen;
|
|
59
|
+
if (dataStart > mmap.size)
|
|
60
|
+
throw new Error(`${path}: header length ${headerLen} exceeds file size`);
|
|
61
|
+
|
|
62
|
+
const headerJson = new TextDecoder().decode(mmap.view(8, headerLen));
|
|
63
|
+
const header = JSON.parse(headerJson) as Record<
|
|
64
|
+
string,
|
|
65
|
+
{ dtype: SafetensorsDtype; shape: number[]; data_offsets: [number, number] }
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
const tensors = new Map<string, TensorInfo>();
|
|
69
|
+
let metadata: Record<string, string> = {};
|
|
70
|
+
for (const [name, entry] of Object.entries(header)) {
|
|
71
|
+
if (name === "__metadata__") {
|
|
72
|
+
metadata = entry as unknown as Record<string, string>;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const { dtype, shape, data_offsets: [begin, end] } = entry;
|
|
76
|
+
if (!(dtype in DTYPE_SIZE))
|
|
77
|
+
throw new Error(`${path}: tensor ${name} has unsupported dtype ${dtype}`);
|
|
78
|
+
const expected = shape.reduce((a, b) => a * b, 1) * DTYPE_SIZE[dtype];
|
|
79
|
+
if (end - begin !== expected)
|
|
80
|
+
throw new Error(
|
|
81
|
+
`${path}: tensor ${name} byte range ${end - begin} != shape ${JSON.stringify(shape)} × ${dtype}`,
|
|
82
|
+
);
|
|
83
|
+
if (dataStart + end > mmap.size)
|
|
84
|
+
throw new Error(`${path}: tensor ${name} extends past end of file`);
|
|
85
|
+
tensors.set(name, { name, dtype, shape, begin, end });
|
|
86
|
+
}
|
|
87
|
+
return new SafetensorsFile(path, mmap, tensors, metadata, dataStart);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#info(name: string): TensorInfo {
|
|
91
|
+
const t = this.tensors.get(name);
|
|
92
|
+
if (!t) throw new Error(`${this.path}: no tensor named ${name}`);
|
|
93
|
+
return t;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Raw pointer to the tensor's first byte (for zero-copy FFI handoff). */
|
|
97
|
+
pointer(name: string): number {
|
|
98
|
+
return this.mmap.pointer(this.#dataStart + this.#info(name).begin);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** JS view of a tensor's bytes (small tensors / tests only). */
|
|
102
|
+
view(name: string): Uint8Array {
|
|
103
|
+
const t = this.#info(name);
|
|
104
|
+
return this.mmap.view(this.#dataStart + t.begin, t.end - t.begin);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** A sharded model directory: index json + N safetensors files. */
|
|
109
|
+
export class ShardedSafetensors {
|
|
110
|
+
readonly files = new Map<string, SafetensorsFile>();
|
|
111
|
+
readonly tensorToFile = new Map<string, SafetensorsFile>();
|
|
112
|
+
|
|
113
|
+
static async open(modelDir: string): Promise<ShardedSafetensors> {
|
|
114
|
+
const self = new ShardedSafetensors();
|
|
115
|
+
const indexPath = `${modelDir}/model.safetensors.index.json`;
|
|
116
|
+
if (await Bun.file(indexPath).exists()) {
|
|
117
|
+
const index = (await Bun.file(indexPath).json()) as {
|
|
118
|
+
weight_map: Record<string, string>;
|
|
119
|
+
};
|
|
120
|
+
for (const [tensor, file] of Object.entries(index.weight_map)) {
|
|
121
|
+
let sf = self.files.get(file);
|
|
122
|
+
if (!sf) {
|
|
123
|
+
sf = SafetensorsFile.open(`${modelDir}/${file}`);
|
|
124
|
+
self.files.set(file, sf);
|
|
125
|
+
}
|
|
126
|
+
self.tensorToFile.set(tensor, sf);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
// Single-file artifacts: HF's model.safetensors, or mlx-whisper's
|
|
130
|
+
// weights.safetensors (mlx-community/whisper-*).
|
|
131
|
+
const file = (await Bun.file(`${modelDir}/model.safetensors`).exists())
|
|
132
|
+
? "model.safetensors"
|
|
133
|
+
: (await Bun.file(`${modelDir}/weights.safetensors`).exists())
|
|
134
|
+
? "weights.safetensors"
|
|
135
|
+
: "model.safetensors";
|
|
136
|
+
const sf = SafetensorsFile.open(`${modelDir}/${file}`);
|
|
137
|
+
self.files.set(file, sf);
|
|
138
|
+
for (const name of sf.tensors.keys()) self.tensorToFile.set(name, sf);
|
|
139
|
+
}
|
|
140
|
+
return self;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get tensorNames(): string[] {
|
|
144
|
+
return [...this.tensorToFile.keys()].sort();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#file(name: string): SafetensorsFile {
|
|
148
|
+
const sf = this.tensorToFile.get(name);
|
|
149
|
+
if (!sf) throw new Error(`no tensor named ${name}`);
|
|
150
|
+
return sf;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
info(name: string): TensorInfo {
|
|
154
|
+
return this.#file(name).tensors.get(name)!;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pointer(name: string): number {
|
|
158
|
+
return this.#file(name).pointer(name);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
view(name: string): Uint8Array {
|
|
162
|
+
return this.#file(name).view(name);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The TurboQuant fold record: `turboquant_fold.json`, the auxiliary file the
|
|
2
|
+
// Trellis quantizer (packages/quantize/src/trellis-quantizer.ts) writes beside a
|
|
3
|
+
// rotated checkpoint. It records the R1 rotation the weights were folded with
|
|
4
|
+
// and the final-norm gain the fold moved into `lm_head`. Records written before
|
|
5
|
+
// the quantizer recorded the gain carry no `final_gain`.
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
export const TURBOQUANT_FOLD_FILE = "turboquant_fold.json";
|
|
11
|
+
|
|
12
|
+
/** What a rotated checkpoint records about its fold. */
|
|
13
|
+
export interface TurboQuantFold {
|
|
14
|
+
/** Seed of R1's sign vector. */
|
|
15
|
+
readonly seed: number;
|
|
16
|
+
/** The residual stream carries the R1 rotation. */
|
|
17
|
+
readonly r1: boolean;
|
|
18
|
+
readonly hiddenSize: number;
|
|
19
|
+
/** `final_gain`: the effective final-norm gain γ (stored + 1 of the source's
|
|
20
|
+
* final norm) the fold moved into `lm_head`, one per hidden channel. Absent
|
|
21
|
+
* when the record does not carry it. */
|
|
22
|
+
readonly finalGain?: Float32Array;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The fold record in `modelDir`, or null when the artifact carries none.
|
|
26
|
+
* Throws when the record lacks a field or its gain does not span `hiddenSize`. */
|
|
27
|
+
export function readTurboQuantFold(modelDir: string): TurboQuantFold | null {
|
|
28
|
+
const path = join(modelDir, TURBOQUANT_FOLD_FILE);
|
|
29
|
+
if (!existsSync(path)) return null;
|
|
30
|
+
const raw = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
|
31
|
+
const { seed, r1, hiddenSize } = raw;
|
|
32
|
+
if (!Number.isInteger(seed)) throw new Error(`${path}: seed must be an integer (got ${String(seed)})`);
|
|
33
|
+
if (typeof r1 !== "boolean") throw new Error(`${path}: r1 must be a boolean (got ${String(r1)})`);
|
|
34
|
+
if (!Number.isInteger(hiddenSize) || (hiddenSize as number) <= 0)
|
|
35
|
+
throw new Error(`${path}: hiddenSize must be a positive integer (got ${String(hiddenSize)})`);
|
|
36
|
+
const fold = { seed: seed as number, r1, hiddenSize: hiddenSize as number };
|
|
37
|
+
if (raw.final_gain === undefined) return Object.freeze(fold);
|
|
38
|
+
const gain = raw.final_gain;
|
|
39
|
+
if (!Array.isArray(gain) || gain.length !== hiddenSize || !gain.every(value => typeof value === "number" && Number.isFinite(value)))
|
|
40
|
+
throw new Error(`${path}: final_gain must be ${String(hiddenSize)} finite numbers`);
|
|
41
|
+
return Object.freeze({ ...fold, finalGain: Float32Array.from(gain as number[]) });
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Universal-tier primitives: dense (unquantized) linear/embedding modules
|
|
2
|
+
// (mlx nn.Linear / nn.Embedding semantics), quantized-or-dense
|
|
3
|
+
// loader helpers, norm loaders, and the load-time weight audit.
|
|
4
|
+
//
|
|
5
|
+
// The quantized paths delegate to the proven QuantizedLinear/QuantizedEmbedding
|
|
6
|
+
// primitives (packages/inference/src/layers/); the
|
|
7
|
+
// dense paths are verbatim ports of mlx.nn.Linear (`mx.addmm(bias, x, W.T)`
|
|
8
|
+
// / `x @ W.T`) and mlx.nn.Embedding (`weight[ids]`, `x @ weight.T`).
|
|
9
|
+
|
|
10
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
11
|
+
import type { Weights } from "./weights";
|
|
12
|
+
|
|
13
|
+
/** Records every tensor a universal load consumes; `finish` diffs against
|
|
14
|
+
* the shard index so a descriptor mistake is a LOAD error (unconsumed /
|
|
15
|
+
* missing tensors named), never a silently-wrong model
|
|
16
|
+
* (`02d723a:docs/design/generic-model-support.md` §4.1). */
|
|
17
|
+
export class WeightAudit {
|
|
18
|
+
readonly consumed = new Set<string>();
|
|
19
|
+
|
|
20
|
+
use(name: string): void {
|
|
21
|
+
this.consumed.add(name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** `drop` = the arch's sanitize rules (tensors mlx-lm discards on load,
|
|
25
|
+
* e.g. rotary_emb.inv_freq, or lm_head.weight under tied embeddings). */
|
|
26
|
+
finish(weights: Weights, drop: RegExp[]): void {
|
|
27
|
+
const unconsumed = weights.tensorNames.filter(
|
|
28
|
+
(n) => !this.consumed.has(n) && !drop.some((re) => re.test(n)),
|
|
29
|
+
);
|
|
30
|
+
if (unconsumed.length > 0)
|
|
31
|
+
throw new Error(
|
|
32
|
+
`weight audit: ${unconsumed.length} tensor(s) in the checkpoint were not consumed ` +
|
|
33
|
+
`by the universal module (descriptor mismatch?): ${unconsumed.slice(0, 12).join(", ")}` +
|
|
34
|
+
(unconsumed.length > 12 ? ", …" : ""),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Load a tensor and record it in the audit. */
|
|
40
|
+
export function tensorUsed(weights: Weights, audit: WeightAudit, name: string): MlxArray {
|
|
41
|
+
audit.use(name);
|
|
42
|
+
return weights.tensor(name);
|
|
43
|
+
}
|