@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,316 @@
|
|
|
1
|
+
// Hot-swap mounted LoRA adapters — port of optiq/adapters/{mount,
|
|
2
|
+
// registry,resolver}.py (serving side; lora/apply.py is the training-
|
|
3
|
+
// side rank logic). Mount N adapters on one quantized base, select per
|
|
4
|
+
// request by id, never reload the base.
|
|
5
|
+
//
|
|
6
|
+
// Deviations from the reference, both deliberate:
|
|
7
|
+
// - No ContextVar/serve-pin: our generation queue is serialized, so the
|
|
8
|
+
// active adapter is a plain field (LoraState) set by generate().
|
|
9
|
+
// - Residual composition is mlx-lm LoRALinear / optiq apply.py
|
|
10
|
+
// (`y + (scale·z).astype(x.dtype)`), not mount.py's uncast f32 add —
|
|
11
|
+
// the cast form is what the adapters were trained behind.
|
|
12
|
+
|
|
13
|
+
import { MlxArray,cpuStream } from "@mlx-bun/mlx/array";
|
|
14
|
+
import { C } from "@mlx-bun/mlx/ffi";
|
|
15
|
+
import { transposeAxes } from "@mlx-bun/mlx/ops";
|
|
16
|
+
import { ptr,read } from "bun:ffi";
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
import { existsSync,statSync } from "node:fs";
|
|
19
|
+
import { resolve } from "node:path";
|
|
20
|
+
import { SafetensorsFile } from "../artifacts/safetensors";
|
|
21
|
+
import type { LoraState,LoraWeights } from "../layers/lora";
|
|
22
|
+
import { adapterWeightsFile,readAdapterScale } from "./available";
|
|
23
|
+
|
|
24
|
+
/** The adapter manager needs named mount points, not a concrete model family. */
|
|
25
|
+
export interface LoraTarget {
|
|
26
|
+
readonly inFeatures: number;
|
|
27
|
+
readonly outFeatures: number;
|
|
28
|
+
adapters?: Map<string, LoraWeights> | null;
|
|
29
|
+
loraState?: LoraState | null;
|
|
30
|
+
}
|
|
31
|
+
export interface AdapterTarget {
|
|
32
|
+
readonly prefixBase: string;
|
|
33
|
+
readonly loraState: LoraState;
|
|
34
|
+
loraTargets(): ReadonlyMap<string, LoraTarget>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cstr = (s: string) => Buffer.from(s + "\0", "utf8");
|
|
38
|
+
|
|
39
|
+
export interface AdapterInfo {
|
|
40
|
+
id: string;
|
|
41
|
+
path: string;
|
|
42
|
+
rank: number | null;
|
|
43
|
+
scale: number;
|
|
44
|
+
sizeBytes: number;
|
|
45
|
+
mountedLayers: number;
|
|
46
|
+
/** Adapter tensors for modules outside the 7 target suffixes
|
|
47
|
+
* (per-layer-input projections etc.) — skipped, like the reference. */
|
|
48
|
+
skippedTensors: number;
|
|
49
|
+
/** Actual resident bytes of this adapter's mounted lora_a/lora_b arrays
|
|
50
|
+
* (sum of MlxArray.nbytes — the real RAM cost while mounted, not the
|
|
51
|
+
* on-disk safetensors size). Web chat's adapter routing table shows this
|
|
52
|
+
* per loaded adapter instead of guessing
|
|
53
|
+
* from file size. */
|
|
54
|
+
ramBytes: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Read every tensor from an adapter safetensors file (small, f32) into
|
|
58
|
+
* materialized mlx arrays. Tensor names come from our header parser;
|
|
59
|
+
* arrays from mlx's native loader via the proven map-get pattern
|
|
60
|
+
* (weights.ts). The map and header mmap are freed before returning. */
|
|
61
|
+
export function loadAdapterTensors(file: string): Map<string, MlxArray> {
|
|
62
|
+
const sf = SafetensorsFile.open(file);
|
|
63
|
+
const names = [...sf.tensors.keys()];
|
|
64
|
+
sf.mmap.unmap();
|
|
65
|
+
|
|
66
|
+
const arrMapSlot = new BigUint64Array([C.mlx_map_string_to_array_new()]);
|
|
67
|
+
const metaMapSlot = new BigUint64Array([C.mlx_map_string_to_string_new()]);
|
|
68
|
+
const arrMapPtr = ptr(arrMapSlot);
|
|
69
|
+
const metaMapPtr = ptr(metaMapSlot);
|
|
70
|
+
const status = C.mlx_load_safetensors(arrMapPtr, metaMapPtr, ptr(cstr(file)), cpuStream);
|
|
71
|
+
C.mlx_map_string_to_string_free(read.u64(metaMapPtr, 0));
|
|
72
|
+
const mapHandle = read.u64(arrMapPtr, 0);
|
|
73
|
+
if (status !== 0) {
|
|
74
|
+
C.mlx_map_string_to_array_free(mapHandle);
|
|
75
|
+
throw new Error(`mlx_load_safetensors(${file}) failed`);
|
|
76
|
+
}
|
|
77
|
+
const out = new Map<string, MlxArray>();
|
|
78
|
+
try {
|
|
79
|
+
for (const name of names) {
|
|
80
|
+
const slot = new BigUint64Array([C.mlx_array_new()]);
|
|
81
|
+
const slotPtr = ptr(slot);
|
|
82
|
+
if (C.mlx_map_string_to_array_get(slotPtr, mapHandle, ptr(cstr(name))) !== 0)
|
|
83
|
+
throw new Error(`adapter tensor ${name} missing from native map`);
|
|
84
|
+
const arr = new MlxArray(read.u64(slotPtr, 0));
|
|
85
|
+
arr.eval(); // materialize — the map (and its Load refs) is freed below
|
|
86
|
+
out.set(name, arr);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
for (const a of out.values()) a.dispose();
|
|
90
|
+
C.mlx_map_string_to_array_free(mapHandle);
|
|
91
|
+
throw e;
|
|
92
|
+
}
|
|
93
|
+
C.mlx_map_string_to_array_free(mapHandle);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Parse an adapter selection spec: "sft" | "sft+dpo" | "sft,dpo"
|
|
98
|
+
* (reference mount.py stacking syntax — residuals sum in order). */
|
|
99
|
+
export function parseAdapterSpec(spec: string): string[] {
|
|
100
|
+
const sep = spec.includes("+") ? "+" : ",";
|
|
101
|
+
return spec.split(sep).map((s) => s.trim()).filter(Boolean);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class AdapterManager {
|
|
105
|
+
readonly #model: AdapterTarget;
|
|
106
|
+
readonly #mounted = new Map<string, AdapterInfo>();
|
|
107
|
+
/** Owned adapter arrays per id (disposed on unmount). */
|
|
108
|
+
readonly #arrays = new Map<string, MlxArray[]>();
|
|
109
|
+
readonly #revisions = new Map<string, string>();
|
|
110
|
+
|
|
111
|
+
constructor(model: AdapterTarget) {
|
|
112
|
+
this.#model = model;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Mount an adapter directory under `id`. Validates layer-name and
|
|
116
|
+
* rank/shape compatibility against the base BEFORE any weights are
|
|
117
|
+
* attached — a bad adapter fails here, never at request time
|
|
118
|
+
* (reference: resolver.py + mount.py add_adapter checks). */
|
|
119
|
+
async mount(id: string, dir: string): Promise<AdapterInfo> {
|
|
120
|
+
if (this.#mounted.has(id)) return this.#mounted.get(id)!;
|
|
121
|
+
if (id.includes("+") || id.includes(","))
|
|
122
|
+
throw new Error(`adapter id ${JSON.stringify(id)} may not contain '+' or ','`);
|
|
123
|
+
const path = resolve(dir);
|
|
124
|
+
if (!existsSync(path)) throw new Error(`adapter dir not found: ${path}`);
|
|
125
|
+
|
|
126
|
+
const weightsFile = adapterWeightsFile(path);
|
|
127
|
+
const { scale, rank: configRank, rsLora } = await readAdapterScale(path);
|
|
128
|
+
// Hash once at mount, before native allocation. Reusing an adapter name
|
|
129
|
+
// for different weights or scale must never reuse its prefix/checkpoint.
|
|
130
|
+
const digest = createHash("sha256").update(JSON.stringify({ scale, rank: configRank, rsLora }));
|
|
131
|
+
for await (const chunk of Bun.file(weightsFile).stream()) digest.update(chunk);
|
|
132
|
+
const revision = digest.digest("hex");
|
|
133
|
+
const tensors = loadAdapterTensors(weightsFile);
|
|
134
|
+
const targets = this.#model.loraTargets();
|
|
135
|
+
|
|
136
|
+
// Group adapter tensors into (modulePath → {a, b}), probing both the
|
|
137
|
+
// pure-LLM and VLM-wrapped prefixes like the reference _find_weight_pair.
|
|
138
|
+
const pairs = new Map<string, {
|
|
139
|
+
a?: MlxArray;
|
|
140
|
+
b?: MlxArray;
|
|
141
|
+
aIsPeft?: boolean;
|
|
142
|
+
bIsPeft?: boolean;
|
|
143
|
+
}>();
|
|
144
|
+
let skipped = 0;
|
|
145
|
+
const ourPrefix = `${this.#model.prefixBase}.layers.`;
|
|
146
|
+
const altPrefix = ourPrefix.startsWith("language_model.")
|
|
147
|
+
? ourPrefix.slice("language_model.".length)
|
|
148
|
+
: `language_model.${ourPrefix}`;
|
|
149
|
+
for (const [name, arr] of tensors) {
|
|
150
|
+
const m = name.match(/^(.*)\.(lora_a|lora_A|lora_b|lora_B)(\.weight)?$/);
|
|
151
|
+
if (!m) { skipped++; arr.dispose(); continue; }
|
|
152
|
+
let modulePath = m[1]!;
|
|
153
|
+
// Standard PEFT state dicts wrap the model path in
|
|
154
|
+
// `base_model.model.`. The remainder is the underlying HF module path
|
|
155
|
+
// (`model.layers...` or `language_model.model.layers...`).
|
|
156
|
+
if (modulePath.startsWith("base_model.model."))
|
|
157
|
+
modulePath = modulePath.slice("base_model.model.".length);
|
|
158
|
+
if (modulePath.startsWith(altPrefix))
|
|
159
|
+
modulePath = ourPrefix + modulePath.slice(altPrefix.length);
|
|
160
|
+
if (!targets.has(modulePath)) { skipped++; arr.dispose(); continue; }
|
|
161
|
+
const slot = pairs.get(modulePath) ?? {};
|
|
162
|
+
const peftName = m[3] === ".weight" || m[2] === "lora_A" || m[2] === "lora_B";
|
|
163
|
+
if (m[2]!.toLowerCase() === "lora_a") {
|
|
164
|
+
slot.a = arr;
|
|
165
|
+
slot.aIsPeft = peftName;
|
|
166
|
+
} else {
|
|
167
|
+
slot.b = arr;
|
|
168
|
+
slot.bIsPeft = peftName;
|
|
169
|
+
}
|
|
170
|
+
pairs.set(modulePath, slot);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Validate every pair against the base linear's dims before mounting
|
|
174
|
+
// anything (all-or-nothing: a bad adapter must not half-mount).
|
|
175
|
+
const validated: { linear: LoraTarget; lw: LoraWeights }[] = [];
|
|
176
|
+
const dispose = () => {
|
|
177
|
+
for (const { a, b } of pairs.values()) { a?.dispose(); b?.dispose(); }
|
|
178
|
+
};
|
|
179
|
+
try {
|
|
180
|
+
for (const [modulePath, pair] of pairs) {
|
|
181
|
+
const { a, b } = pair;
|
|
182
|
+
if (!a || !b)
|
|
183
|
+
throw new Error(`${modulePath}: adapter has only one of lora_a/lora_b`);
|
|
184
|
+
const linear = targets.get(modulePath)!;
|
|
185
|
+
if (a.shape.length !== 2 || b.shape.length !== 2)
|
|
186
|
+
throw new Error(
|
|
187
|
+
`${modulePath}: LoRA tensors must be rank-2; got ` +
|
|
188
|
+
`lora_a [${a.shape}] / lora_b [${b.shape}]`,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const internalLayout =
|
|
192
|
+
a.shape[0] === linear.inFeatures &&
|
|
193
|
+
b.shape[0] === a.shape[1] &&
|
|
194
|
+
b.shape[1] === linear.outFeatures;
|
|
195
|
+
const peftLayout =
|
|
196
|
+
a.shape[1] === linear.inFeatures &&
|
|
197
|
+
b.shape[1] === a.shape[0] &&
|
|
198
|
+
b.shape[0] === linear.outFeatures;
|
|
199
|
+
if (!internalLayout && !peftLayout)
|
|
200
|
+
throw new Error(
|
|
201
|
+
`${modulePath}: shape mismatch — lora_a [${a.shape}] / lora_b [${b.shape}] ` +
|
|
202
|
+
`vs base [in ${linear.inFeatures}, out ${linear.outFeatures}]. Expected ` +
|
|
203
|
+
`mlx-lm [in, rank] + [rank, out] or PEFT [rank, in] + [out, rank].`,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Full-rank square adapters satisfy both shape predicates. In that
|
|
207
|
+
// ambiguous case the PEFT spelling is authoritative; otherwise the
|
|
208
|
+
// unique compatible layout decides.
|
|
209
|
+
const usePeftLayout =
|
|
210
|
+
peftLayout && (!internalLayout || (pair.aIsPeft === true && pair.bIsPeft === true));
|
|
211
|
+
let mountedA = a;
|
|
212
|
+
let mountedB = b;
|
|
213
|
+
if (usePeftLayout) {
|
|
214
|
+
let transposedA: MlxArray | null = null;
|
|
215
|
+
let transposedB: MlxArray | null = null;
|
|
216
|
+
try {
|
|
217
|
+
transposedA = transposeAxes(a, [1, 0]);
|
|
218
|
+
transposedB = transposeAxes(b, [1, 0]);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
transposedA?.dispose();
|
|
221
|
+
transposedB?.dispose();
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
mountedA = transposedA;
|
|
225
|
+
mountedB = transposedB;
|
|
226
|
+
pair.a = mountedA;
|
|
227
|
+
pair.b = mountedB;
|
|
228
|
+
a.dispose();
|
|
229
|
+
b.dispose();
|
|
230
|
+
}
|
|
231
|
+
const rank = mountedA.shape[1]!;
|
|
232
|
+
// rsLoRA: effective per-layer scale is α/√rank (matches training).
|
|
233
|
+
const effScale = rsLora ? scale / Math.sqrt(rank) : scale;
|
|
234
|
+
validated.push({ linear, lw: { a: mountedA, b: mountedB, scale: effScale, rank } });
|
|
235
|
+
}
|
|
236
|
+
if (validated.length === 0)
|
|
237
|
+
throw new Error(
|
|
238
|
+
`failed to mount adapter ${JSON.stringify(id)}: no tensors match the ` +
|
|
239
|
+
`target modules (q/k/v/o/gate/up/down_proj). Check that the adapter ` +
|
|
240
|
+
`was trained for this base model.`,
|
|
241
|
+
);
|
|
242
|
+
} catch (e) {
|
|
243
|
+
dispose();
|
|
244
|
+
throw e;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const { linear, lw } of validated) {
|
|
248
|
+
(linear.adapters ??= new Map()).set(id, lw);
|
|
249
|
+
linear.loraState = this.#model.loraState;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const ramBytes = validated.reduce((sum, { lw }) => sum + lw.a.nbytes + lw.b.nbytes, 0);
|
|
253
|
+
const info: AdapterInfo = {
|
|
254
|
+
id,
|
|
255
|
+
path,
|
|
256
|
+
rank: configRank,
|
|
257
|
+
scale,
|
|
258
|
+
sizeBytes: statSync(weightsFile).size,
|
|
259
|
+
mountedLayers: validated.length,
|
|
260
|
+
skippedTensors: skipped,
|
|
261
|
+
ramBytes,
|
|
262
|
+
};
|
|
263
|
+
this.#mounted.set(id, info);
|
|
264
|
+
this.#revisions.set(id, revision);
|
|
265
|
+
this.#arrays.set(id, validated.flatMap(({ lw }) => [lw.a, lw.b]));
|
|
266
|
+
this.#model.loraState.refreshBindings();
|
|
267
|
+
return info;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Remove `id` from every linear (frees its arrays). The mount points
|
|
271
|
+
* stay in place for other adapters, like the reference. */
|
|
272
|
+
unmount(id: string): number {
|
|
273
|
+
if (!this.#mounted.delete(id)) return 0;
|
|
274
|
+
this.#revisions.delete(id);
|
|
275
|
+
let removed = 0;
|
|
276
|
+
for (const linear of this.#model.loraTargets().values()) {
|
|
277
|
+
if (linear.adapters?.delete(id)) removed++;
|
|
278
|
+
}
|
|
279
|
+
for (const a of this.#arrays.get(id) ?? []) a.dispose();
|
|
280
|
+
this.#arrays.delete(id);
|
|
281
|
+
if (this.#model.loraState.active.includes(id))
|
|
282
|
+
this.#model.loraState.active = this.#model.loraState.active.filter((x) => x !== id);
|
|
283
|
+
return removed;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Ordered, content-addressed namespace for the actual mounted composition. */
|
|
287
|
+
cacheNamespace(ids: readonly string[]): string {
|
|
288
|
+
return ids.map((id) => {
|
|
289
|
+
const revision = this.#revisions.get(id);
|
|
290
|
+
if (!revision) throw new Error(`adapter ${id} is no longer mounted`);
|
|
291
|
+
return `${id}@${revision}`;
|
|
292
|
+
}).join("+");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
list(): AdapterInfo[] {
|
|
296
|
+
return [...this.#mounted.values()];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
get(id: string): AdapterInfo | undefined {
|
|
300
|
+
return this.#mounted.get(id);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Resolve a request's adapter spec to validated ids ([] for none).
|
|
304
|
+
* Unknown ids are an error — selection must fail loudly, not no-op. */
|
|
305
|
+
resolveSpec(spec: string | null | undefined): string[] {
|
|
306
|
+
if (!spec || spec.toLowerCase() === "none") return [];
|
|
307
|
+
const ids = parseAdapterSpec(spec);
|
|
308
|
+
for (const id of ids)
|
|
309
|
+
if (!this.#mounted.has(id))
|
|
310
|
+
throw new Error(
|
|
311
|
+
`unknown adapter ${JSON.stringify(id)} — mounted: ` +
|
|
312
|
+
`${[...this.#mounted.keys()].join(", ") || "(none)"}`,
|
|
313
|
+
);
|
|
314
|
+
return ids;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Auxiliary checkpoint files: the non-weight files (tokenizer, chat template,
|
|
2
|
+
// generation config, …) a written model directory needs to stay loadable.
|
|
3
|
+
// Checkpoint producers (quantization, adapter fusion) copy them through verbatim.
|
|
4
|
+
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
/** Aux files copied through verbatim from the source dir when present. */
|
|
9
|
+
const AUX_FILES = [
|
|
10
|
+
"tokenizer.json",
|
|
11
|
+
"tokenizer_config.json",
|
|
12
|
+
"tokenizer.model",
|
|
13
|
+
"spiece.model",
|
|
14
|
+
"chat_template.jinja",
|
|
15
|
+
"generation_config.json",
|
|
16
|
+
"special_tokens_map.json",
|
|
17
|
+
"added_tokens.json",
|
|
18
|
+
"vocab.json",
|
|
19
|
+
"merges.txt",
|
|
20
|
+
"README.md",
|
|
21
|
+
"kv_config.json",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** Copy known aux files (and any `*.model`) from src → out when present. */
|
|
25
|
+
export async function copyAuxFiles(srcDir: string, outDir: string): Promise<void> {
|
|
26
|
+
const seen = new Set<string>();
|
|
27
|
+
for (const f of AUX_FILES) {
|
|
28
|
+
const src = join(srcDir, f);
|
|
29
|
+
if (existsSync(src)) {
|
|
30
|
+
await Bun.write(join(outDir, f), Bun.file(src));
|
|
31
|
+
seen.add(f);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Sweep any additional *.model tokenizer sidecars not in the known list.
|
|
35
|
+
try {
|
|
36
|
+
const { readdirSync } = await import("node:fs");
|
|
37
|
+
for (const f of readdirSync(srcDir)) {
|
|
38
|
+
if (f.endsWith(".model") && !seen.has(f)) {
|
|
39
|
+
await Bun.write(join(outDir, f), Bun.file(join(srcDir, f)));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// src dir unreadable — aux copy is best-effort.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// How a `config.json` differs from the plain Hugging Face layout, by `model_type`.
|
|
2
|
+
// A dialect is a reading rule for the artifact, not a statement about a model
|
|
3
|
+
// family (`models/families.ts` owns those): `loadModelConfig` applies exactly one
|
|
4
|
+
// and branches on its fields, never on a model type.
|
|
5
|
+
|
|
6
|
+
/** How `rope_parameters` is spelled. `map`: a per-attention-type map, absent means none.
|
|
7
|
+
* `theta`: as `map`, and a flat `rope_theta` stands in for a missing map.
|
|
8
|
+
* `flat`: one flat dict (`rope_theta`, `partial_rotary_factor`) for full attention. */
|
|
9
|
+
export type RopeLayout = "map" | "theta" | "flat";
|
|
10
|
+
|
|
11
|
+
export interface ConfigDialect {
|
|
12
|
+
/** Text-config fields the config omits: the architecture's own defaults. Fields present still win. */
|
|
13
|
+
readonly textDefaults?: () => Record<string, any>;
|
|
14
|
+
/** Geometry and stop tokens come from a converted container's own parser. */
|
|
15
|
+
readonly container?: "colibri";
|
|
16
|
+
readonly rope: RopeLayout;
|
|
17
|
+
/** `layer_types` when the config declares none: every layer full attention. */
|
|
18
|
+
readonly fullAttentionByDefault?: true;
|
|
19
|
+
/** Refuse a value this loader has not verified rather than mis-read it. */
|
|
20
|
+
readonly validate?: (text: Record<string, any>) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** DiffusionGemma's config.json ships only token ids + canvas_length + the
|
|
24
|
+
* quant map — the architecture dims live in optiq's `config.py` TextConfig
|
|
25
|
+
* defaults (and layer_types / rope_parameters are computed in __post_init__).
|
|
26
|
+
* We reproduce those defaults in snake_case so the generic parser below picks
|
|
27
|
+
* them up; any field actually present in config.json still overrides. Source:
|
|
28
|
+
* optiq/vlm/_mlxvlm/models/diffusion_gemma/config.py. */
|
|
29
|
+
function diffusionGemmaRawDefaults(): Record<string, any> {
|
|
30
|
+
const numLayers = 30;
|
|
31
|
+
const pattern = ["sliding_attention", "sliding_attention", "sliding_attention",
|
|
32
|
+
"sliding_attention", "sliding_attention", "full_attention"];
|
|
33
|
+
const layer_types = Array.from({ length: numLayers }, (_, i) => pattern[i % pattern.length]);
|
|
34
|
+
layer_types[numLayers - 1] = "full_attention"; // last forced full
|
|
35
|
+
return {
|
|
36
|
+
hidden_size: 2816,
|
|
37
|
+
num_hidden_layers: numLayers,
|
|
38
|
+
num_attention_heads: 16,
|
|
39
|
+
num_key_value_heads: 8,
|
|
40
|
+
num_global_key_value_heads: 2,
|
|
41
|
+
head_dim: 256,
|
|
42
|
+
global_head_dim: 512,
|
|
43
|
+
intermediate_size: 2112,
|
|
44
|
+
moe_intermediate_size: 704,
|
|
45
|
+
hidden_activation: "gelu_pytorch_tanh",
|
|
46
|
+
rms_norm_eps: 1e-6,
|
|
47
|
+
vocab_size: 262144,
|
|
48
|
+
max_position_embeddings: 262144,
|
|
49
|
+
sliding_window: 1024,
|
|
50
|
+
layer_types,
|
|
51
|
+
enable_moe_block: true,
|
|
52
|
+
num_experts: 128,
|
|
53
|
+
top_k_experts: 8,
|
|
54
|
+
final_logit_softcapping: 30.0,
|
|
55
|
+
tie_word_embeddings: true,
|
|
56
|
+
bos_token_id: 2,
|
|
57
|
+
eos_token_id: 1,
|
|
58
|
+
rope_parameters: {
|
|
59
|
+
sliding_attention: { rope_type: "default", rope_theta: 10000.0 },
|
|
60
|
+
full_attention: { rope_type: "proportional", partial_rotary_factor: 0.25, rope_theta: 1000000.0 },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Qwen3.8 adds output_gate_type ("swish"), which every implementation
|
|
66
|
+
* (transformers ground truth, mlx-lm pinned + main) currently ignores —
|
|
67
|
+
* the attention output gate is hardcoded o_proj(out·σ(gate)). Only accept
|
|
68
|
+
* values verified to mean that; an unknown value must fail at load, not
|
|
69
|
+
* silently mis-gate a future checkpoint where the field starts mattering. */
|
|
70
|
+
function verifyOutputGate(text: Record<string, any>): void {
|
|
71
|
+
if (text.output_gate_type != null && text.output_gate_type !== "swish")
|
|
72
|
+
throw new Error(
|
|
73
|
+
`qwen3_5: unverified output_gate_type "${text.output_gate_type}" (only "swish" ` +
|
|
74
|
+
`is verified to mean the standard sigmoid output gate)`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const PLAIN: ConfigDialect = Object.freeze({ rope: "map" });
|
|
79
|
+
const FLAT_THETA: ConfigDialect = Object.freeze({ rope: "theta" });
|
|
80
|
+
|
|
81
|
+
/** The reading rule for a config's `model_type`. Anything not listed reads as the plain layout. */
|
|
82
|
+
export function dialectFor(modelType: unknown): ConfigDialect {
|
|
83
|
+
if (modelType === "diffusion_gemma") return { rope: "map", textDefaults: diffusionGemmaRawDefaults };
|
|
84
|
+
if (modelType === "glm_moe_dsa") return { rope: "map", container: "colibri" };
|
|
85
|
+
// Qwen3.5 rope_parameters is a flat dict ({type, rope_theta, mrope_section,
|
|
86
|
+
// partial_rotary_factor}), not the gemma per-attention-type map — and
|
|
87
|
+
// type "default" means plain partial nn.RoPE (mrope_section ignored for text).
|
|
88
|
+
if (typeof modelType === "string" && modelType.startsWith("qwen3_5")) return { rope: "flat", validate: verifyOutputGate };
|
|
89
|
+
// Llama-style flat configs: a scalar rope_theta and no rope_parameters map.
|
|
90
|
+
if (modelType === "llama") return { rope: "theta", fullAttentionByDefault: true };
|
|
91
|
+
// Plain Qwen3 (Qwen3ForCausalLM, e.g. Qwen3-Embedding): handled like llama for rope.
|
|
92
|
+
if (modelType === "qwen3") return FLAT_THETA;
|
|
93
|
+
return PLAIN;
|
|
94
|
+
}
|