@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,165 @@
|
|
|
1
|
+
// NgramProvider — MODEL-FREE prompt-lookup speculative drafting behind the
|
|
2
|
+
// grouped draft rows (packages/inference/src/generation/speculative/source.ts).
|
|
3
|
+
// No weights, no caches, no tokenizer:
|
|
4
|
+
// drafts are copied from the request's own token stream. When the trailing
|
|
5
|
+
// k-gram of (prompt + emitted-so-far) has occurred earlier in that stream,
|
|
6
|
+
// the tokens that followed the earlier occurrence are proposed as the draft;
|
|
7
|
+
// the shared speculative group makes the result LOSSLESS by construction
|
|
8
|
+
// (drafts are only proposals — the target's own samples decide every emitted
|
|
9
|
+
// token, at any temperature; only the acceptance rate moves). Best case:
|
|
10
|
+
// agentic/RAG/code-edit traffic that re-emits spans already present in context.
|
|
11
|
+
//
|
|
12
|
+
// Prior art (ported, not invented): "Prompt Lookup Decoding" (Apoorv Saxena,
|
|
13
|
+
// github.com/apoorvumang/prompt-lookup-decoding) and vLLM's `ngram` proposer
|
|
14
|
+
// (vllm/v1 spec_decode ngram_proposer) — both match the LONGEST k-gram first
|
|
15
|
+
// (k = max..min) and take the FIRST occurrence scanning left-to-right; we
|
|
16
|
+
// follow that exactly. Defaults mirror Saxena (max_ngram_size=3,
|
|
17
|
+
// num_pred_tokens=10 → our --num-draft-tokens default for this kind).
|
|
18
|
+
//
|
|
19
|
+
// Token history: each row keeps the committed stream it has seen. Prefill
|
|
20
|
+
// appends the target's prompt chunks (to len-1 under the tail-split shape, the
|
|
21
|
+
// last prompt token being the first round's pending token); draft appends the
|
|
22
|
+
// row's pending token, then looks up; commit appends the accepted drafts.
|
|
23
|
+
// Invariant at matching time: history == prompt + every emitted token, ending
|
|
24
|
+
// at the pending token. A round with no match drafts nothing, which the group
|
|
25
|
+
// verifies as one plain target step, so ngram speculation is structurally never
|
|
26
|
+
// worse than plain decode by more than the host lookup and verification overhead.
|
|
27
|
+
|
|
28
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
29
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
30
|
+
import type { CheckpointAttachment } from "../../../contracts/mlx/checkpoint";
|
|
31
|
+
import type { DraftProvider, DraftRowGroup, DraftPrefillGroup, GroupedDraftProvider, DraftRowConstraints } from "../source";
|
|
32
|
+
import type { DraftRowCheckpoint } from "../../../contracts/mlx/draft-checkpoint";
|
|
33
|
+
import { applyStateChanges } from "../../../runtime/resources";
|
|
34
|
+
|
|
35
|
+
// Histories are append-only between prefill/restore replacements. Weak keys
|
|
36
|
+
// release derived indexes with retired rows; checkpoints still contain tokens
|
|
37
|
+
// only. Ordered positions preserve the original first-occurrence policy.
|
|
38
|
+
const ngramPositions = new WeakMap<readonly number[], { length: number; positions: Map<number, number[]> }>();
|
|
39
|
+
function proposeNgram(h: readonly number[], max: number, min: number, n: number): number[] {
|
|
40
|
+
let index = ngramPositions.get(h);
|
|
41
|
+
if (!index || h.length < index.length) {
|
|
42
|
+
index = { length: 0, positions: new Map() };
|
|
43
|
+
ngramPositions.set(h, index);
|
|
44
|
+
}
|
|
45
|
+
for (let i = index.length; i < h.length; i++) {
|
|
46
|
+
const token = h[i]!;
|
|
47
|
+
const positions = index.positions.get(token);
|
|
48
|
+
if (positions) positions.push(i); else index.positions.set(token, [i]);
|
|
49
|
+
}
|
|
50
|
+
index.length = h.length;
|
|
51
|
+
const L = h.length;
|
|
52
|
+
for (let k = Math.min(max, L - 1); k >= min; k--) {
|
|
53
|
+
const tailAt = L - k;
|
|
54
|
+
const positions = index.positions.get(h[tailAt]!) ?? [];
|
|
55
|
+
search: for (const i of positions) {
|
|
56
|
+
if (i + k >= L) break;
|
|
57
|
+
for (let j = 0; j < k; j++) if (h[i + j] !== h[tailAt + j]) continue search;
|
|
58
|
+
return h.slice(i + k, i + k + n);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const namespace = (max: number, min: number) => `ngram-history-v1:${max}:${min}`;
|
|
65
|
+
const captureHistory = (history: readonly number[], max: number, min: number): CheckpointAttachment => ({
|
|
66
|
+
schema: "ngram-history-v1", metadata: { max, min }, tensors: [ops.fromInt32([...history], [history.length])],
|
|
67
|
+
});
|
|
68
|
+
function restoreHistory(state: DraftRowCheckpoint, max: number, min: number): number[] {
|
|
69
|
+
const { attachment, processedTokens } = state;
|
|
70
|
+
if (attachment.schema !== "ngram-history-v1" || attachment.metadata.max !== max || attachment.metadata.min !== min ||
|
|
71
|
+
attachment.tensors.length !== 1 || attachment.tensors[0]!.shape.length !== 1 || attachment.tensors[0]!.shape[0] !== processedTokens)
|
|
72
|
+
throw new Error("ngram checkpoint does not match its token coverage or lookup policy");
|
|
73
|
+
return attachment.tensors[0]!.toIntTokens();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Each row owns only its committed token history. Verification and sampling
|
|
77
|
+
* remain in the shared method; target storage and scheduling are not needed. */
|
|
78
|
+
class NgramRows implements DraftRowGroup, DraftPrefillGroup {
|
|
79
|
+
readonly prefillMode = "tail-split" as const;
|
|
80
|
+
readonly tapLayers: readonly number[] = [];
|
|
81
|
+
readonly namespace: string;
|
|
82
|
+
#histories: number[][] = [];
|
|
83
|
+
#drafts: number[][] = [];
|
|
84
|
+
constructor(readonly max: number, readonly min: number, checkpoints: readonly (DraftRowCheckpoint | null)[],
|
|
85
|
+
readonly constraints?: DraftRowConstraints) {
|
|
86
|
+
this.namespace = namespace(max, min);
|
|
87
|
+
this.append(checkpoints);
|
|
88
|
+
}
|
|
89
|
+
get rowCount() { return this.#histories.length; }
|
|
90
|
+
prepareAppend(checkpoints: readonly (DraftRowCheckpoint | null)[]) {
|
|
91
|
+
let next = [...this.#histories, ...checkpoints.map(state => state ? restoreHistory(state, this.max, this.min) : [])];
|
|
92
|
+
return { commit: () => { const old = this.#histories; this.#histories = next; next = old; }, dispose: () => { next = []; } };
|
|
93
|
+
}
|
|
94
|
+
append(checkpoints: readonly (DraftRowCheckpoint | null)[]) { applyStateChanges([() => this.prepareAppend(checkpoints)]); }
|
|
95
|
+
filterRows(keep: readonly number[]) { this.#histories = keep.map(row => this.#histories[row]!); this.#drafts = []; }
|
|
96
|
+
prefill(tokens: MlxArray): void {
|
|
97
|
+
using packed = ops.contiguous(tokens);
|
|
98
|
+
const ids = packed.toIntTokens(), length = tokens.shape[1]!;
|
|
99
|
+
for (let row = 0; row < this.rowCount; row++) this.#histories[row]!.push(...ids.slice(row * length, (row + 1) * length));
|
|
100
|
+
}
|
|
101
|
+
materialize(): void {}
|
|
102
|
+
draft(pending: readonly number[], depth: number): number[][] | Promise<number[][]> {
|
|
103
|
+
if (this.constraints) {
|
|
104
|
+
for (const [row, history] of this.#histories.entries()) history.push(pending[row]!);
|
|
105
|
+
return Promise.all(this.#histories.map((_, row) => this.constraints!.propose(row, depth)))
|
|
106
|
+
.then(proposals => this.#drafts = proposals);
|
|
107
|
+
}
|
|
108
|
+
this.#drafts = this.#histories.map((history, row) => {
|
|
109
|
+
history.push(pending[row]!);
|
|
110
|
+
return proposeNgram(history, this.max, this.min, depth);
|
|
111
|
+
});
|
|
112
|
+
return this.#drafts;
|
|
113
|
+
}
|
|
114
|
+
commit(accepted: readonly number[]) {
|
|
115
|
+
for (let row = 0; row < this.rowCount; row++) this.#histories[row]!.push(...this.#drafts[row]!.slice(0, accepted[row]!));
|
|
116
|
+
this.#drafts = [];
|
|
117
|
+
}
|
|
118
|
+
capture(row: number): DraftRowCheckpoint {
|
|
119
|
+
const history = this.#histories[row]!;
|
|
120
|
+
return { processedTokens: history.length, attachment: captureHistory(history, this.max, this.min) };
|
|
121
|
+
}
|
|
122
|
+
dispose() { this.#histories = []; this.#drafts = []; }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface NgramOptions {
|
|
126
|
+
/** Longest suffix k-gram tried first (Saxena max_ngram_size). */
|
|
127
|
+
max?: number;
|
|
128
|
+
/** Shortest k-gram tried before giving up (vLLM prompt_lookup_min). */
|
|
129
|
+
min?: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export class NgramProvider implements DraftProvider {
|
|
133
|
+
readonly id = "ngram";
|
|
134
|
+
readonly weightsBytes = 0;
|
|
135
|
+
readonly max: number;
|
|
136
|
+
readonly min: number;
|
|
137
|
+
readonly grouped: GroupedDraftProvider = {
|
|
138
|
+
checkpointNamespace: () => namespace(this.max, this.min),
|
|
139
|
+
supportsTargetAdapters: true,
|
|
140
|
+
open: options => new NgramRows(this.max, this.min, options.checkpoints),
|
|
141
|
+
openPrefill: options => new NgramRows(this.max, this.min, options.checkpoints),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
constructor(opts: NgramOptions = {}) {
|
|
145
|
+
this.max = Math.max(1, opts.max ?? 3);
|
|
146
|
+
this.min = Math.max(1, Math.min(opts.min ?? 1, this.max));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
dispose(): void {}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Constraint candidates share committed-history ownership with prompt lookup.
|
|
153
|
+
* They never perform ngram lookup. */
|
|
154
|
+
export function constraintDraftProvider(): { id: string; grouped: GroupedDraftProvider } {
|
|
155
|
+
return {
|
|
156
|
+
id: "grammar",
|
|
157
|
+
grouped: {
|
|
158
|
+
checkpointNamespace: () => namespace(3, 1),
|
|
159
|
+
supportsTargetAdapters: true,
|
|
160
|
+
openPrefill: options => new NgramRows(3, 1, options.checkpoints),
|
|
161
|
+
open: options => new NgramRows(3, 1, options.checkpoints,
|
|
162
|
+
options.constraints ?? { propose: async () => [] }),
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Native Qwen3.8 MTP draft provider — the Qwen-TRAINED multi-step prediction
|
|
2
|
+
// head, split from the raw release's last shard and published as
|
|
3
|
+
// mlx-community/Qwen3.8-27B-MTP-* (model_type "qwen3_5_mtp", block_size 3).
|
|
4
|
+
//
|
|
5
|
+
// Reference implementation (locate + port + gate, don't design):
|
|
6
|
+
// mlx_vlm/speculative/drafters/qwen3_5_mtp/qwen3_5_mtp.py. Mechanism, per
|
|
7
|
+
// step: h = fc(concat(rms_emb(embed(token)), rms_hidden(hidden))) → ONE
|
|
8
|
+
// full-attention decoder layer (own KVCache, positions continue the target
|
|
9
|
+
// sequence) → norm → the TARGET's lm_head → next draft token; recursive for
|
|
10
|
+
// the block ("trained with multiple steps"). The drafter is NOT standalone:
|
|
11
|
+
// it binds the target's embed_tokens + lm_head (mtp_use_dedicated_embeddings
|
|
12
|
+
// false) and consumes the target's PRE-final-norm last-layer hidden (mlx-vlm
|
|
13
|
+
// captures it with skip_final_norm=True) — carried here by the provider's tap
|
|
14
|
+
// layer, NOT the target's post-norm output.
|
|
15
|
+
//
|
|
16
|
+
// Row convention (predict-2-ahead): drafter KV row at position p is built
|
|
17
|
+
// from (embed(token at position p+1), hidden at position p). Invariant
|
|
18
|
+
// across rounds: after commit, the drafter offset equals the next pending
|
|
19
|
+
// token's position minus zero — i.e. a draft always opens by building the
|
|
20
|
+
// pending token's row from the TARGET's TRUE hidden at the emitted position
|
|
21
|
+
// (held over from the verify tap). Computationally identical to mlx-vlm's
|
|
22
|
+
// commit-time "seed" append, just performed at the top of the next round.
|
|
23
|
+
//
|
|
24
|
+
// Companion projections may be dense or quantized according to their metadata.
|
|
25
|
+
// Published drafter norms are already sanitized to runtime layout; no +1.0 shift.
|
|
26
|
+
|
|
27
|
+
import { loadModelConfig, type ModelConfig } from "../../../artifacts/config";
|
|
28
|
+
import { Weights } from "../../../artifacts/weights";
|
|
29
|
+
import type { RecurrentMtpModule } from "../../../contracts/mlx/drafter";
|
|
30
|
+
import { DRAFT_CHECKPOINT_SCHEMA } from "../../../contracts/mlx/draft-checkpoint";
|
|
31
|
+
import { loadQwenMtpModule } from "../../../models/drafter-loaders";
|
|
32
|
+
import type { DraftProvider, DraftRowGroup, DraftPrefillGroup, DraftRowSampling, GroupedDraftProvider, RecurrentMtpTarget, TargetView } from "../source";
|
|
33
|
+
import { targetLacks } from "../source";
|
|
34
|
+
import type { DraftRowCheckpoint } from "../../../contracts/mlx/draft-checkpoint";
|
|
35
|
+
import { QwenMtpRows } from "../bindings/qwen-mtp-rows";
|
|
36
|
+
import { configuredDraftVocabulary } from "../draft-vocab";
|
|
37
|
+
import { type MtpRowState } from "../../../state/speculative/qwen-mtp-state";
|
|
38
|
+
import { captureQwenMtpState, restoreQwenMtpState } from "../../../state/speculative/qwen-mtp-state";
|
|
39
|
+
import { applyStateChanges, cleanupFailure, disposeResources } from "../../../runtime/resources";
|
|
40
|
+
import { artifactIdentity } from "../../../artifacts/identity";
|
|
41
|
+
import { configFingerprint } from "../../../artifacts/fingerprint";
|
|
42
|
+
import type { PreparedStateChange } from "../../../contracts/portable/resources";
|
|
43
|
+
|
|
44
|
+
/** MTP drafts from the target's last hidden layer, before its final norm. */
|
|
45
|
+
const mtpTapLayers = (target: RecurrentMtpTarget): readonly number[] => Object.freeze([target.layerCount - 1]);
|
|
46
|
+
|
|
47
|
+
export class QwenMtpProvider implements DraftProvider {
|
|
48
|
+
readonly grouped: GroupedDraftProvider = {
|
|
49
|
+
supportsExternalTokens: true,
|
|
50
|
+
targetTapLayers: view => mtpTapLayers(this.#target(view)),
|
|
51
|
+
// A list shipped beside the companion (or named by MLX_BUN_SPEC_DRAFT_VOCAB),
|
|
52
|
+
// for a target whose quantized head the rows can project a row subset of.
|
|
53
|
+
draftVocabulary: view => {
|
|
54
|
+
const head = this.#target(view).vocabularyHead;
|
|
55
|
+
return head ? configuredDraftVocabulary(head.vocabSize, this.#dir) : null;
|
|
56
|
+
},
|
|
57
|
+
checkpointNamespace: () => this.#checkpointNamespace,
|
|
58
|
+
open: options => this.#openRows(options.target, options.sampling, options.checkpoints),
|
|
59
|
+
openPrefill: options => this.#openRows(options.target, null, options.checkpoints),
|
|
60
|
+
};
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly weightsBytes: number;
|
|
63
|
+
readonly #module: RecurrentMtpModule;
|
|
64
|
+
readonly #config: ModelConfig;
|
|
65
|
+
readonly #resources: DisposableStack;
|
|
66
|
+
readonly #checkpointNamespace: string;
|
|
67
|
+
/** The companion's directory, where its optional draft vocabulary ships. */
|
|
68
|
+
readonly #dir: string;
|
|
69
|
+
|
|
70
|
+
private constructor(id: string, dir: string, config: ModelConfig, weightsBytes: number, module: RecurrentMtpModule, resources: DisposableStack, checkpointNamespace: string) {
|
|
71
|
+
this.id = id;
|
|
72
|
+
this.#dir = dir;
|
|
73
|
+
this.#config = config;
|
|
74
|
+
this.#module = module;
|
|
75
|
+
this.weightsBytes = weightsBytes;
|
|
76
|
+
this.#resources = resources;
|
|
77
|
+
this.#checkpointNamespace = checkpointNamespace;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static async load(dir: string): Promise<QwenMtpProvider> {
|
|
81
|
+
const config = await loadModelConfig(dir);
|
|
82
|
+
const weights = await Weights.open(dir);
|
|
83
|
+
using resources = new DisposableStack();
|
|
84
|
+
resources.defer(() => weights.dispose());
|
|
85
|
+
const weightsBytes = [...weights.shards.files.values()]
|
|
86
|
+
.reduce((a, f) => a + f.mmap.size, 0);
|
|
87
|
+
const module = await loadQwenMtpModule(weights, config, resources);
|
|
88
|
+
// Stable across restarts and distinct for differently folded/quantized
|
|
89
|
+
// companions. Hash once at provider load, outside inference execution.
|
|
90
|
+
const identity = await artifactIdentity(configFingerprint(config),
|
|
91
|
+
[...weights.shards.files].map(([name, shard]) => ({ name, path: shard.path })));
|
|
92
|
+
return new QwenMtpProvider(
|
|
93
|
+
dir.split("/").filter(Boolean).at(-1) ?? "qwen-mtp", dir,
|
|
94
|
+
config, weightsBytes, module, resources.move(), `${DRAFT_CHECKPOINT_SCHEMA.qwenMtp}:${identity}`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#target(view: TargetView): RecurrentMtpTarget {
|
|
99
|
+
if (this.#resources.disposed)
|
|
100
|
+
throw new Error("qwen MTP provider is disposed");
|
|
101
|
+
const target = view.recurrentMtp;
|
|
102
|
+
if (!target) throw targetLacks("recurrentMtp");
|
|
103
|
+
if (target.hiddenSize !== this.#config.text.hiddenSize) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`qwen MTP drafter hidden ${this.#config.text.hiddenSize} != target ` +
|
|
106
|
+
`${target.hiddenSize} — split from a different checkpoint?`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return target;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#openRows(view: TargetView, sampling: DraftRowSampling | null,
|
|
113
|
+
checkpoints: readonly (DraftRowCheckpoint | null)[]): DraftRowGroup & DraftPrefillGroup {
|
|
114
|
+
const target = this.#target(view);
|
|
115
|
+
const rows = new QwenMtpRows(target, this.#module, sampling, []);
|
|
116
|
+
const prepareAppend = (checkpoints: readonly (DraftRowCheckpoint | null)[]) => {
|
|
117
|
+
const states: Array<MtpRowState | null> = [];
|
|
118
|
+
let change: PreparedStateChange | undefined;
|
|
119
|
+
try {
|
|
120
|
+
for (const checkpoint of checkpoints) states.push(checkpoint ? restoreQwenMtpState(checkpoint) : null);
|
|
121
|
+
change = rows.prepareAppend(states);
|
|
122
|
+
disposeResources(states.splice(0).flatMap(state => state ? [state.cache, state.hidden] : []));
|
|
123
|
+
return change;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return cleanupFailure(error, () => disposeResources([
|
|
126
|
+
...states.flatMap(state => state ? [state.cache, state.hidden] : []), ...(change ? [change] : []),
|
|
127
|
+
]));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const append = (checkpoints: readonly (DraftRowCheckpoint | null)[]) => applyStateChanges([() => prepareAppend(checkpoints)]);
|
|
131
|
+
try { append(checkpoints); }
|
|
132
|
+
catch (error) { return cleanupFailure(error, () => rows.dispose()); }
|
|
133
|
+
return {
|
|
134
|
+
namespace: this.#checkpointNamespace, prefillMode: "full", tapLayers: mtpTapLayers(target),
|
|
135
|
+
get rowCount() { return rows.rowCount; },
|
|
136
|
+
append, prepareAppend, prefill: (tokens, context) => rows.prefill(tokens, context!), materialize: rows.materialize.bind(rows),
|
|
137
|
+
filterRows: rows.filterRows.bind(rows),
|
|
138
|
+
draft: rows.draft.bind(rows), draftDevice: rows.draftDevice.bind(rows), preview: rows.preview.bind(rows), commit: rows.commit.bind(rows),
|
|
139
|
+
consume: rows.consume.bind(rows),
|
|
140
|
+
capture(row) {
|
|
141
|
+
const state = rows.extractRow(row);
|
|
142
|
+
try { return captureQwenMtpState(state); }
|
|
143
|
+
finally { disposeResources([state.cache, state.hidden]); }
|
|
144
|
+
},
|
|
145
|
+
dispose: rows.dispose.bind(rows),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
dispose(): void {
|
|
150
|
+
// Release cached transpose views before their native weight maps. MLX
|
|
151
|
+
// retains buffers needed by outstanding GPU commands until completion.
|
|
152
|
+
this.#resources.dispose();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Two-model speculative drafting — mlx-lm parity (`mlx_lm.server
|
|
2
|
+
// --draft-model`). A full second model drafts autoregressively in the grouped
|
|
3
|
+
// draft rows (bindings/two-model-rows.ts); the shared speculative group
|
|
4
|
+
// verifies with the target. L1 oracle:
|
|
5
|
+
// mlx_lm.server with the same target/draft pair, greedy, token-for-token
|
|
6
|
+
// (spec-vs-spec — both batch the verify lm-head, so neither is bit-exact to
|
|
7
|
+
// stock decode at knife-edges).
|
|
8
|
+
//
|
|
9
|
+
// Faithfulness notes (read from the oracle venv's generate.py):
|
|
10
|
+
// - drafts are sampled with the REQUEST sampler (generate.py:593-601) —
|
|
11
|
+
// greedy drafting under a temperature>0 request is not parity;
|
|
12
|
+
// - draft-cache rewind is max(n - kAccept - 1, 0) (generate.py:589-591),
|
|
13
|
+
// with the all-accept re-feed of the last draft kept pending in the rows
|
|
14
|
+
// and joined with the next target token (generate.py:645-648);
|
|
15
|
+
// - logits processors do NOT run on draft steps (they're target-side).
|
|
16
|
+
|
|
17
|
+
import { loadModelConfig } from "../../../artifacts/config";
|
|
18
|
+
import { Weights } from "../../../artifacts/weights";
|
|
19
|
+
import { createModel, type RuntimeModel } from "../../../models/factory";
|
|
20
|
+
import type { DraftProvider, GroupedDraftProvider } from "../source";
|
|
21
|
+
import { standaloneDraftGroups } from "../bindings/two-model-rows";
|
|
22
|
+
import { bindMlxAutoregressiveGraph } from "../../bindings/autoregressive";
|
|
23
|
+
import { artifactIdentity } from "../../../artifacts/identity";
|
|
24
|
+
import { configFingerprint } from "../../../artifacts/fingerprint";
|
|
25
|
+
import { cleanupFailure, disposeResources } from "../../../runtime/resources";
|
|
26
|
+
|
|
27
|
+
export class TwoModelProvider implements DraftProvider {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly weightsBytes: number;
|
|
30
|
+
readonly grouped: GroupedDraftProvider;
|
|
31
|
+
|
|
32
|
+
private constructor(
|
|
33
|
+
readonly model: RuntimeModel,
|
|
34
|
+
private readonly weights: Weights,
|
|
35
|
+
id: string,
|
|
36
|
+
namespace: string,
|
|
37
|
+
) {
|
|
38
|
+
this.id = id;
|
|
39
|
+
this.grouped = standaloneDraftGroups(bindMlxAutoregressiveGraph(model), namespace);
|
|
40
|
+
this.weightsBytes = [...weights.shards.files.values()].reduce(
|
|
41
|
+
(a, f) => a + f.mmap.size,
|
|
42
|
+
0,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Load the draft model from a snapshot dir. Vocab-size mismatch with the
|
|
47
|
+
* target is a WARNING (mlx-lm parity, server.py:363-368); the tokenizer-
|
|
48
|
+
* family hard check lives at the server (it owns both tokenizers). */
|
|
49
|
+
static async load(modelDir: string, targetVocabSize?: number): Promise<TwoModelProvider> {
|
|
50
|
+
const config = await loadModelConfig(modelDir);
|
|
51
|
+
const weights = await Weights.open(modelDir);
|
|
52
|
+
let model: RuntimeModel | null = null;
|
|
53
|
+
try {
|
|
54
|
+
model = createModel(weights, config);
|
|
55
|
+
if (
|
|
56
|
+
targetVocabSize !== undefined &&
|
|
57
|
+
config.text.vocabSize !== targetVocabSize
|
|
58
|
+
)
|
|
59
|
+
console.warn(
|
|
60
|
+
`draft model vocab size ${config.text.vocabSize} != target ${targetVocabSize} — ` +
|
|
61
|
+
`speculation will accept nothing useful (mlx-lm warns identically)`,
|
|
62
|
+
);
|
|
63
|
+
const identity = await artifactIdentity(configFingerprint(config),
|
|
64
|
+
[...weights.shards.files].map(([name, shard]) => ({ name, path: shard.path })));
|
|
65
|
+
return new TwoModelProvider(model, weights, modelDir.split("/").filter(Boolean).at(-1)!,
|
|
66
|
+
`standalone-draft-v1:${identity}`);
|
|
67
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources([...(model ? [model] : []), weights])); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The draft graph releases what it built (constants, Neural Engine programs),
|
|
71
|
+
* then its weights go. */
|
|
72
|
+
dispose(): void {
|
|
73
|
+
disposeResources([this.model, this.weights]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// Streaming generation — port of mlx-lm's generate_step:
|
|
2
|
+
// - prefill in chunks; cache state evaluated per chunk (bounded transient
|
|
3
|
+
// memory), logits never computed for non-final prefill positions
|
|
4
|
+
// - decode pipelining via mx.async_eval: step n+1's graph is built and
|
|
5
|
+
// dispatched before step n's token is read back, so the GPU never idles
|
|
6
|
+
// on the JS round-trip
|
|
7
|
+
// - opt-in early token-zero yield reduces latency before that pipeline starts
|
|
8
|
+
// - sampling stays on-device; only the chosen token id crosses to JS
|
|
9
|
+
|
|
10
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
11
|
+
import { type Cache } from "../contracts/mlx/cache";
|
|
12
|
+
import type { TokenLogprobs } from "../contracts/portable/generation";
|
|
13
|
+
import type { PromptResponseTrace } from "../runtime/trace";
|
|
14
|
+
import type { GrammarController } from "../sampling/grammar";
|
|
15
|
+
import {
|
|
16
|
+
type LogitsProcessorOptions,
|
|
17
|
+
type SamplerOptions
|
|
18
|
+
} from "../sampling/index";
|
|
19
|
+
import {
|
|
20
|
+
type FillSession,
|
|
21
|
+
type FillStats
|
|
22
|
+
} from "./fill/session";
|
|
23
|
+
|
|
24
|
+
export interface GenerateOptions extends SamplerOptions, LogitsProcessorOptions {
|
|
25
|
+
/** Application session affinity; independent of numerical cache identity. */
|
|
26
|
+
cacheSessionId?: string;
|
|
27
|
+
/** Resolved host policy. Direct compatibility calls resolve from their
|
|
28
|
+
* captured binding when this is absent. */
|
|
29
|
+
decodePolicy?: Readonly<Pick<import("../contracts/portable/execution").ResolvedExecution, "compiledDecode" | "grammarJump">>;
|
|
30
|
+
/** Cooperatively cancel at prefill/decode boundaries; pending native work
|
|
31
|
+
* finishes before owned resources are released. */
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
maxTokens?: number;
|
|
34
|
+
eosTokenIds?: number[];
|
|
35
|
+
prefillChunkSize?: number;
|
|
36
|
+
/** Whether the request supplied its seed explicitly. Durable generation
|
|
37
|
+
* checkpoints use this to distinguish a reproducible user policy from the
|
|
38
|
+
* server-generated seed that must be recovered from checkpoint metadata. */
|
|
39
|
+
seedWasExplicit?: boolean;
|
|
40
|
+
/** Fired once during prefill, at `snapshotAt` tokens (default: the full
|
|
41
|
+
* prompt) and BEFORE any further KV is written — the one moment the
|
|
42
|
+
* caches hold exactly that prefix (post-wrap rings can't be rewound
|
|
43
|
+
* later). The server's prompt-boundary cache snapshot hangs here. */
|
|
44
|
+
onPrefillDone?: () => void;
|
|
45
|
+
/** Token count at which onPrefillDone fires (the STABLE cache boundary —
|
|
46
|
+
* chat prompts end in a generation primer, e.g. 12B's thought-channel
|
|
47
|
+
* tokens, that the NEXT turn's re-render does not contain; snapshotting
|
|
48
|
+
* there would make the entry untrimmable-and-divergent). Prefill is
|
|
49
|
+
* split at this index when it falls mid-prompt. */
|
|
50
|
+
snapshotAt?: number;
|
|
51
|
+
/** Resume an interrupted autoregressive generation from a durable KV
|
|
52
|
+
* checkpoint. `cache` must cover every token in `promptTokens` exactly;
|
|
53
|
+
* `initialPendingToken` is the already-sampled next token, so no cache
|
|
54
|
+
* rewind is required (important for recurrent/untrimmable layers). */
|
|
55
|
+
initialPendingToken?: number;
|
|
56
|
+
/** Number of completion tokens already emitted before this resumed run. */
|
|
57
|
+
initialGeneratedTokens?: number;
|
|
58
|
+
/** Original request prompt length used for protocol usage accounting when
|
|
59
|
+
* `promptTokens` also contains replayed completion tokens. */
|
|
60
|
+
originalPromptTokens?: number;
|
|
61
|
+
/** Periodic, safe decode-boundary checkpoint hook. The cache covers
|
|
62
|
+
* `cacheTokens`; `pendingToken` has been sampled but not emitted. */
|
|
63
|
+
checkpointEveryTokens?: number;
|
|
64
|
+
onDecodeCheckpoint?: (state: {
|
|
65
|
+
cacheTokens: number[];
|
|
66
|
+
caches: Cache[];
|
|
67
|
+
generatedTokens: number;
|
|
68
|
+
pendingToken: number;
|
|
69
|
+
}) => void | Promise<void>;
|
|
70
|
+
/** Pre-warmed KV caches (e.g. from the prompt cache). cache[0].offset
|
|
71
|
+
* prompt tokens are treated as already prefilled; only the suffix is
|
|
72
|
+
* forwarded. Caller keeps ownership — generate() will not dispose. */
|
|
73
|
+
cache?: Cache[];
|
|
74
|
+
/** Vision path: pre-merged (unscaled) input embeddings [1, L, hidden]
|
|
75
|
+
* covering the whole prompt; prefilled in one shot (no chunking).
|
|
76
|
+
* Caller keeps ownership. */
|
|
77
|
+
promptEmbeddings?: MlxArray;
|
|
78
|
+
/** bool [L] marking image tokens (bidirectional attention among them).
|
|
79
|
+
* MUST be unset when the prompt contains any audio — audio prompts run
|
|
80
|
+
* fully causal (`02d723a:docs/design/generic-model-support.md` §6.6). */
|
|
81
|
+
imageMask?: MlxArray;
|
|
82
|
+
/** bool [L] marking ALL multimodal soft tokens (image | audio) for
|
|
83
|
+
* per-layer-input id zeroing (e2b/e4b), decoupled from imageMask so
|
|
84
|
+
* audio-only prompts (no bidirectional mask) still zero their positions.
|
|
85
|
+
* When unset, zeroing falls back to imageMask (the legacy vision shape).
|
|
86
|
+
* Caller keeps ownership. */
|
|
87
|
+
multimodalMask?: MlxArray;
|
|
88
|
+
/** Mounted LoRA adapter ids to apply (resolved/validated by
|
|
89
|
+
* AdapterManager.resolveSpec). Residuals sum in order. Set on the
|
|
90
|
+
* model's LoraState for exactly the duration of this generation —
|
|
91
|
+
* a plain field, safe because the generation queue is serialized. */
|
|
92
|
+
adapters?: string[];
|
|
93
|
+
/** DiffusionGemma image-text-to-text: channel-first pixel_values [1,3,H,W].
|
|
94
|
+
* When set, `promptTokens` are the spliced (<|image|>-expanded) ids and the
|
|
95
|
+
* denoising engine prefills the merged vision features. Caller keeps ownership. */
|
|
96
|
+
visionPixels?: MlxArray;
|
|
97
|
+
/** Capture each emitted token's log-probability (mlx_lm.server `logprobs`).
|
|
98
|
+
* The distribution matches mlx-lm generate_step exactly: full-vocab
|
|
99
|
+
* log-softmax of the logits AFTER logits processors, BEFORE the sampler's
|
|
100
|
+
* temperature/top-p/top-k/min-p/XTC (generate.py L409-422). Off by default —
|
|
101
|
+
* the hot path pays nothing when unset. */
|
|
102
|
+
logprobs?: boolean;
|
|
103
|
+
/** Capture the top-k (token id, logprob) pairs per emitted token from the
|
|
104
|
+
* same distribution (mlx_lm.server `top_logprobs`). 0/unset = off. */
|
|
105
|
+
topLogprobs?: number;
|
|
106
|
+
/** Grammar-constrained decoding (packages/inference/src/sampling/grammar.ts): a compiled GrammarController
|
|
107
|
+
* that masks invalid tokens to -inf each step. L2-class (oMLX oracle). When
|
|
108
|
+
* set, the decode loop takes a slightly different shape: it eager-reads the
|
|
109
|
+
* token id (acceptToken needs a JS number, which the pipelined loop defers),
|
|
110
|
+
* advances the matcher, and awaits the async mask precompute (which overlaps
|
|
111
|
+
* the GPU forward). Non-grammar requests keep the fast pipelined loop. */
|
|
112
|
+
grammar?: GrammarController;
|
|
113
|
+
/** Token fast-forwarding (packages/inference/src/generation/fill/,
|
|
114
|
+
* `02d723a:docs/design/speculative-decoding.md` §7 "Token fast-forwarding"):
|
|
115
|
+
* a per-request table of DETERMINED token spans
|
|
116
|
+
* (compiled from the request's `tools` + the chat template). When the
|
|
117
|
+
* stream enters one, the engine appends the whole span itself with ONE
|
|
118
|
+
* multi-token forward and resumes sampling after it.
|
|
119
|
+
*
|
|
120
|
+
* This is context extension, NOT speculation: nothing is drafted, verified,
|
|
121
|
+
* or rolled back, and no comparison is made against what the model would
|
|
122
|
+
* have produced. Injection bypasses the sampler, which is a documented
|
|
123
|
+
* behavior-policy deviation at temperature > 0 — hence opt-in
|
|
124
|
+
* (MLX_BUN_FILL=strict) and the request refusals in shouldUseFill.
|
|
125
|
+
* Serial lane only: the batch scheduler and the spec loop never read it. */
|
|
126
|
+
fill?: FillSession;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface GenerateStats {
|
|
130
|
+
promptTokens: number;
|
|
131
|
+
/** An observed terminal cause takes precedence over the token-count limit. */
|
|
132
|
+
finishReason?: "stop" | "length";
|
|
133
|
+
/** Prompt tokens skipped via a pre-warmed cache. */
|
|
134
|
+
cachedTokens: number;
|
|
135
|
+
generatedTokens: number;
|
|
136
|
+
prefillTps: number;
|
|
137
|
+
decodeTps: number;
|
|
138
|
+
prefillMs: number;
|
|
139
|
+
decodeMs: number;
|
|
140
|
+
/** Exact token sequence whose KV now lives in the cache (prompt + every
|
|
141
|
+
* decoded token that was forwarded, including a trailing EOS the
|
|
142
|
+
* pipeline forwarded before reading it). For PromptCache.put(). */
|
|
143
|
+
cacheTokens: number[];
|
|
144
|
+
/** Speculative-decoding telemetry (serve --draft-model path only).
|
|
145
|
+
* draftedByPos/acceptedByPos: per-draft-position round counts (index =
|
|
146
|
+
* position within a round's block) — the per-position
|
|
147
|
+
* acceptance signal. */
|
|
148
|
+
spec?: {
|
|
149
|
+
drafted: number; accepted: number; targetCalls: number;
|
|
150
|
+
draftedByPos?: number[]; acceptedByPos?: number[];
|
|
151
|
+
rejected?: number; rounds?: number; acceptanceLengths?: number[];
|
|
152
|
+
tokensPerForward?: number; forwardsSaved?: number;
|
|
153
|
+
/** Round phase wall time, summed over the request's rounds. Present only
|
|
154
|
+
* under `MLX_BUN_SPEC_PHASE_TIMING=1`, which forces an evaluation at each
|
|
155
|
+
* phase boundary (diagnostic: it removes the overlap production decode
|
|
156
|
+
* relies on, so the sum is not production round time). */
|
|
157
|
+
phaseMs?: SpecPhaseMs;
|
|
158
|
+
};
|
|
159
|
+
/** Token fast-forwarding telemetry (MLX_BUN_FILL=strict). Present only when
|
|
160
|
+
* a fill table was actually armed for this generation. */
|
|
161
|
+
fill?: FillStats;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Wall milliseconds per speculative round phase (see GenerateStats.spec). */
|
|
165
|
+
export interface SpecPhaseMs {
|
|
166
|
+
/** draft(): the drafter's chain including its proposal readback. */
|
|
167
|
+
draft: number;
|
|
168
|
+
/** Target verify forward, forced to completion before sampling. */
|
|
169
|
+
verify: number;
|
|
170
|
+
/** Window sampling and its readback, plus the host accept walk. */
|
|
171
|
+
sample: number;
|
|
172
|
+
/** Target transaction resolve plus draft commit. Includes retained-state
|
|
173
|
+
* evaluation when the target/drafter supply diagnostic materialize hooks. */
|
|
174
|
+
commit: number;
|
|
175
|
+
rounds: number;
|
|
176
|
+
/** Target-forward component wall ms (`MLX_BUN_SPEC_LAYER_PROFILE=1`): per-op
|
|
177
|
+
* evaluation barriers inside the verify forward, so `verify` grows while set. */
|
|
178
|
+
layers?: Record<string, number>;
|
|
179
|
+
/** Graph nodes built per op name (`MLX_BUN_SPEC_OP_INVENTORY=1`), summed over rounds. */
|
|
180
|
+
verifyOps?: Record<string, number>;
|
|
181
|
+
draftOps?: Record<string, number>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface GenerateDiagnostics {
|
|
185
|
+
trace?: PromptResponseTrace;
|
|
186
|
+
mechanism?: "serial" | "continuous";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface GeneratedToken {
|
|
190
|
+
token: number;
|
|
191
|
+
index: number;
|
|
192
|
+
/** Present only when GenerateOptions.logprobs / topLogprobs requested it. */
|
|
193
|
+
logprobs?: TokenLogprobs;
|
|
194
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Common library entry points. Specialized components have explicit subpath imports.
|
|
2
|
+
export { loadModelConfig } from "./artifacts/config";
|
|
3
|
+
export { Weights } from "./artifacts/weights";
|
|
4
|
+
export { createModel, openModel, openGlm52RuntimeModel } from "./models/factory";
|
|
5
|
+
export type { RuntimeModel, ModelOpenOptions, Glm52RuntimeOpenOptions } from "./models/factory";
|
|
6
|
+
export { resolveComposition } from "./models/composition";
|
|
7
|
+
export type { CompositionRequest } from "./models/composition";
|
|
8
|
+
export type { Composition } from "./contracts/portable/composition";
|
|
9
|
+
export { ModelImplementationRegistry } from "./models/implementation";
|
|
10
|
+
export { loadTokenizer } from "./input/tokenizer";
|
|
11
|
+
export type { LoadedTokenizer } from "./input/tokenizer";
|
|
12
|
+
export { ChatTemplate } from "./input/chat-template";
|
|
13
|
+
export { generate, bindGeneration } from "./generation/generate";
|
|
14
|
+
export { generateAutoregressive } from "./generation/autoregressive";
|
|
15
|
+
export { generateDenoising } from "./generation/denoising";
|
|
16
|
+
export { Generation } from "./generation/result";
|
|
17
|
+
export type { GenerateOptions, GenerateStats, GeneratedToken } from "./generation/types";
|
|
18
|
+
export { embedOne, embedMany, embeddingTerminatorId, isEmbeddingModel, withInstruction } from "./embeddings/text";
|
|
19
|
+
export type { EmbedResult, EmbeddingGraph } from "./embeddings/text";
|
|
20
|
+
export { AdapterManager } from "./adapters/manager";
|