@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,343 @@
|
|
|
1
|
+
// Sampling — port of mlx-lm's sample_utils.make_sampler /
|
|
2
|
+
// make_logits_processors (temperature, top-p, top-k, min-p, XTC,
|
|
3
|
+
// repetition/presence/frequency penalties, logit bias).
|
|
4
|
+
// All filtering happens on-device; only the chosen token id crosses to JS.
|
|
5
|
+
// Seeded: each step derives a fresh key from (seed, step) so runs are
|
|
6
|
+
// reproducible without sharing global RNG state. (mlx-lm draws XTC's
|
|
7
|
+
// uniform and the categorical from one global stream; we split the
|
|
8
|
+
// per-step key into (xtc, categorical) subkeys instead — deterministic,
|
|
9
|
+
// and the XTC-off path is unchanged.)
|
|
10
|
+
|
|
11
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
12
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
13
|
+
import { runtimeFlag } from "../runtime/config";
|
|
14
|
+
import { normalizedArgmax } from "../kernels/sampling/normalized-argmax";
|
|
15
|
+
import { disposeStepExtras } from "./extras";
|
|
16
|
+
import { applyTopKRows,applyTopPRows } from "./window-filters";
|
|
17
|
+
import { logitsHistoryLimit,makeLogitsProcessors } from "./processors";
|
|
18
|
+
import { makeSampler,stepKey,toLogprobs } from "./sampler";
|
|
19
|
+
import { DeviceStepSampler,DeviceStepSamplerConfig,NumberStepSampler,NumberStepSamplerConfig,StepExtras,StepSample,StepSamplerOptions } from "./types";
|
|
20
|
+
|
|
21
|
+
/** Whether scores can use stateless greedy sampling across independent rows. */
|
|
22
|
+
export function isPlainGreedy(
|
|
23
|
+
opts: StepSamplerOptions,
|
|
24
|
+
hasProcessors = makeLogitsProcessors(opts).length > 0,
|
|
25
|
+
): boolean {
|
|
26
|
+
return (opts.temperature ?? 0) === 0 && !opts.curve && !hasProcessors && !opts.grammar;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function makeStepSampler(
|
|
30
|
+
options: StepSamplerOptions,
|
|
31
|
+
config: DeviceStepSamplerConfig,
|
|
32
|
+
): DeviceStepSampler;
|
|
33
|
+
export function makeStepSampler(
|
|
34
|
+
options: StepSamplerOptions,
|
|
35
|
+
config: NumberStepSamplerConfig,
|
|
36
|
+
): NumberStepSampler;
|
|
37
|
+
export function makeStepSampler(
|
|
38
|
+
options: StepSamplerOptions,
|
|
39
|
+
config: DeviceStepSamplerConfig | NumberStepSamplerConfig,
|
|
40
|
+
): DeviceStepSampler | NumberStepSampler {
|
|
41
|
+
const sampler = config.sampler ?? makeSampler(options);
|
|
42
|
+
const processors = makeLogitsProcessors(options);
|
|
43
|
+
const grammar = options.grammar ?? null;
|
|
44
|
+
const captureSelected = config.captureSelectedLogprob === true;
|
|
45
|
+
const captureTop = Math.max(0, config.captureTopLogprobs ?? 0);
|
|
46
|
+
const capture = captureSelected || captureTop > 0;
|
|
47
|
+
const greedyWithoutMetadata = (options.temperature ?? 0) === 0 && !options.curve && !capture && !config.sampler;
|
|
48
|
+
const historyLimit = logitsHistoryLimit(options);
|
|
49
|
+
let history: MlxArray | null = null;
|
|
50
|
+
|
|
51
|
+
const seedHistory = (tokens: readonly number[]): void => {
|
|
52
|
+
if (historyLimit === 0) return;
|
|
53
|
+
if (tokens.length === 0) {
|
|
54
|
+
history?.dispose();
|
|
55
|
+
history = null;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const recent = tokens.slice(Math.max(0, tokens.length - historyLimit));
|
|
59
|
+
const next = ops.fromInt32(recent, [recent.length]);
|
|
60
|
+
const previous = history;
|
|
61
|
+
history = next;
|
|
62
|
+
previous?.dispose();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Takes ownership of next. Trim before concatenation so the appended array
|
|
66
|
+
// never exceeds the largest active finite window.
|
|
67
|
+
const appendHistory = (next: MlxArray): void => {
|
|
68
|
+
const previous = history;
|
|
69
|
+
if (!previous || next.size >= historyLimit) {
|
|
70
|
+
history = next;
|
|
71
|
+
previous?.dispose();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const keep = Math.min(previous.size, historyLimit - next.size);
|
|
75
|
+
let tail: MlxArray | null = null;
|
|
76
|
+
try {
|
|
77
|
+
if (keep < previous.size) tail = previous.slice([previous.size - keep], [previous.size]);
|
|
78
|
+
history = ops.concatAxis([tail ?? previous, next], 0);
|
|
79
|
+
previous.dispose();
|
|
80
|
+
} finally {
|
|
81
|
+
tail?.dispose();
|
|
82
|
+
next.dispose();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const commitDevice = (token: MlxArray): void => {
|
|
87
|
+
if (historyLimit === 0) return;
|
|
88
|
+
appendHistory(ops.reshape(token, [1]));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const commitNumbers = (tokens: readonly number[]): void => {
|
|
92
|
+
if (tokens.length === 0 || historyLimit === 0) return;
|
|
93
|
+
const recent = tokens.slice(Math.max(0, tokens.length - historyLimit));
|
|
94
|
+
appendHistory(ops.fromInt32(recent, [recent.length]));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Borrows `scores` [1, V]; returns them processed over `tokens` (the history the processors read), owned unless
|
|
98
|
+
// no processor replaced them.
|
|
99
|
+
const applyProcessors = (tokens: MlxArray | null, scores: MlxArray): MlxArray => {
|
|
100
|
+
let current = scores;
|
|
101
|
+
try {
|
|
102
|
+
for (const processor of processors) {
|
|
103
|
+
const next = processor(tokens, current);
|
|
104
|
+
if (current !== scores && next !== current) current.dispose();
|
|
105
|
+
current = next;
|
|
106
|
+
}
|
|
107
|
+
return current;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (current !== scores) current.dispose();
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const sampleDevice = (input: MlxArray, step: number): StepSample<MlxArray> => {
|
|
115
|
+
const vocab = input.shape[input.shape.length - 1]!;
|
|
116
|
+
let current = input.shape.length === 2 ? input : ops.reshape(input, [1, vocab]);
|
|
117
|
+
let ownsCurrent = current !== input;
|
|
118
|
+
let logprobs: MlxArray | null = null;
|
|
119
|
+
let token: MlxArray | null = null;
|
|
120
|
+
let extras: StepExtras | null = null;
|
|
121
|
+
try {
|
|
122
|
+
if (processors.length) {
|
|
123
|
+
const next = applyProcessors(history, current);
|
|
124
|
+
if (ownsCurrent && next !== current) current.dispose();
|
|
125
|
+
current = next;
|
|
126
|
+
ownsCurrent = current !== input;
|
|
127
|
+
}
|
|
128
|
+
if (grammar && !grammar.isTerminated) {
|
|
129
|
+
const next = grammar.applyMask(current);
|
|
130
|
+
if (ownsCurrent && next !== current) current.dispose();
|
|
131
|
+
current = next;
|
|
132
|
+
ownsCurrent = current !== input;
|
|
133
|
+
}
|
|
134
|
+
if (greedyWithoutMetadata)
|
|
135
|
+
return { token: normalizedArgmax(current), extras: null };
|
|
136
|
+
logprobs = toLogprobs(current);
|
|
137
|
+
if (ownsCurrent) current.dispose();
|
|
138
|
+
current = input;
|
|
139
|
+
ownsCurrent = false;
|
|
140
|
+
token = sampler(logprobs, step);
|
|
141
|
+
|
|
142
|
+
if (capture) {
|
|
143
|
+
extras = { sel: null, topIdx: null, topVals: null };
|
|
144
|
+
if (captureSelected) {
|
|
145
|
+
const idx = ops.reshape(token, [1, 1]);
|
|
146
|
+
try {
|
|
147
|
+
extras.sel = ops.takeAlongAxis(logprobs, idx, -1);
|
|
148
|
+
} finally {
|
|
149
|
+
idx.dispose();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (captureTop > 0) {
|
|
153
|
+
const k = Math.min(captureTop, vocab);
|
|
154
|
+
let neg: MlxArray | null = null;
|
|
155
|
+
let partition: MlxArray | null = null;
|
|
156
|
+
let view: MlxArray | null = null;
|
|
157
|
+
try {
|
|
158
|
+
neg = ops.neg(logprobs);
|
|
159
|
+
partition = ops.argpartitionAxis(neg, k - 1, -1);
|
|
160
|
+
view = partition.slice([0, 0], [1, k]);
|
|
161
|
+
extras.topIdx = ops.contiguous(view);
|
|
162
|
+
extras.topVals = ops.takeAlongAxis(logprobs, extras.topIdx, -1);
|
|
163
|
+
} finally {
|
|
164
|
+
neg?.dispose();
|
|
165
|
+
partition?.dispose();
|
|
166
|
+
view?.dispose();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
logprobs.dispose();
|
|
172
|
+
logprobs = null;
|
|
173
|
+
const result = { token, extras };
|
|
174
|
+
token = null;
|
|
175
|
+
extras = null;
|
|
176
|
+
return result;
|
|
177
|
+
} finally {
|
|
178
|
+
if (ownsCurrent) current.dispose();
|
|
179
|
+
logprobs?.dispose();
|
|
180
|
+
token?.dispose();
|
|
181
|
+
disposeStepExtras(extras);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (config.initialHistory) seedHistory(config.initialHistory);
|
|
186
|
+
|
|
187
|
+
// Per-position sampling for a verify window: with no grammar or capture, position p's draw depends only on its
|
|
188
|
+
// scores, its step key and its processor history, which is this request's history extended by the window's inputs
|
|
189
|
+
// up to p (the drafts the walk accepted before reaching p). Every position of the window can then sit in ONE device
|
|
190
|
+
// graph and read back together. Row p reproduces sample(scores[p], steps[p]) exactly, processors included: they run
|
|
191
|
+
// per position on its own [1, V] row over that history, as sample() runs them.
|
|
192
|
+
// The plain filter chain (top-p -> top-k -> temperature -> categorical) can run
|
|
193
|
+
// its filters once over the whole window. Other samplers keep per-row calls.
|
|
194
|
+
const windowFilters = !config.sampler && !options.curve && options.hlg?.enabled !== true &&
|
|
195
|
+
(options.minP ?? 0) === 0 && (options.xtcProbability ?? 0) === 0 && (options.temperature ?? 0) > 0 &&
|
|
196
|
+
runtimeFlag("MLX_BUN_SAMPLER_WINDOW_FILTERS", true)
|
|
197
|
+
? { temperature: options.temperature!, topP: options.topP ?? 0, topK: options.topK ?? 0, seed: options.seed ?? 0 }
|
|
198
|
+
: null;
|
|
199
|
+
const positional = !grammar && !capture
|
|
200
|
+
? {
|
|
201
|
+
sample(scores: MlxArray, steps: readonly number[], inputs?: MlxArray): MlxArray {
|
|
202
|
+
if (scores.shape.length !== 2 || scores.shape[0] !== steps.length)
|
|
203
|
+
throw new Error(`positional sampling expects [${steps.length}, V] scores, got [${scores.shape.join(", ")}]`);
|
|
204
|
+
if (historyLimit > 0 && inputs?.size !== steps.length)
|
|
205
|
+
throw new Error(`positional sampling with processor history needs the window's ${steps.length} inputs`);
|
|
206
|
+
const vocab = scores.shape[1]!;
|
|
207
|
+
const tokens: MlxArray[] = [];
|
|
208
|
+
const processed: MlxArray[] = [];
|
|
209
|
+
let packed: MlxArray | null = null, stacked: MlxArray | null = null;
|
|
210
|
+
try {
|
|
211
|
+
// Each position's scores after its processors, over the history sample() would hold there.
|
|
212
|
+
const rowScores = (position: number): MlxArray => {
|
|
213
|
+
const row = scores.slice([position, 0], [position + 1, vocab]);
|
|
214
|
+
if (!processors.length) return row;
|
|
215
|
+
let extended: MlxArray | null = null;
|
|
216
|
+
try {
|
|
217
|
+
if (historyLimit > 0 && position > 0) {
|
|
218
|
+
using drafts = inputs!.slice([1], [position + 1]);
|
|
219
|
+
extended = history ? ops.concatAxis([history, drafts], 0) : ops.contiguous(drafts);
|
|
220
|
+
}
|
|
221
|
+
const next = applyProcessors(extended ?? history, row);
|
|
222
|
+
if (next !== row) row.dispose();
|
|
223
|
+
return next;
|
|
224
|
+
} catch (error) { row.dispose(); throw error; }
|
|
225
|
+
finally { extended?.dispose(); }
|
|
226
|
+
};
|
|
227
|
+
if (windowFilters && steps.length > 1) {
|
|
228
|
+
// One filter chain over [W, V] (bit-identical per row to the [1, V]
|
|
229
|
+
// chain, packages/inference/tests/sampling/window.test.ts), then each row's own
|
|
230
|
+
// keyed draw on its [1, V] slice: that shape keeps MLX's inverse-CDF
|
|
231
|
+
// categorical, which the draft's coupled draw relies on.
|
|
232
|
+
if (processors.length) {
|
|
233
|
+
for (let position = 0; position < steps.length; position++) processed.push(rowScores(position));
|
|
234
|
+
stacked = ops.concatAxis(processed, 0);
|
|
235
|
+
}
|
|
236
|
+
using logprobs = toLogprobs(stacked ?? scores);
|
|
237
|
+
let current = logprobs, owned: MlxArray | null = null;
|
|
238
|
+
try {
|
|
239
|
+
if (windowFilters.topP > 0 && windowFilters.topP < 1) { owned = applyTopPRows(current, windowFilters.topP); current = owned; }
|
|
240
|
+
if (windowFilters.topK > 0) {
|
|
241
|
+
const next = applyTopKRows(current, windowFilters.topK);
|
|
242
|
+
owned?.dispose(); owned = next; current = next;
|
|
243
|
+
}
|
|
244
|
+
for (const [position, step] of steps.entries()) {
|
|
245
|
+
using row = current.slice([position, 0], [position + 1, vocab]);
|
|
246
|
+
using scaled = ops.mulScalar(row, 1 / windowFilters.temperature);
|
|
247
|
+
using key = stepKey(windowFilters.seed, step);
|
|
248
|
+
tokens.push(ops.randomCategorical(scaled, key));
|
|
249
|
+
}
|
|
250
|
+
} finally { owned?.dispose(); }
|
|
251
|
+
} else
|
|
252
|
+
for (const [position, step] of steps.entries()) {
|
|
253
|
+
using row = rowScores(position);
|
|
254
|
+
if (greedyWithoutMetadata) { tokens.push(normalizedArgmax(row)); continue; }
|
|
255
|
+
using logprobs = toLogprobs(row);
|
|
256
|
+
tokens.push(sampler(logprobs, step));
|
|
257
|
+
}
|
|
258
|
+
if (tokens.length === 1) return tokens.pop()!;
|
|
259
|
+
packed = ops.concatAxis(tokens, 0);
|
|
260
|
+
const result = packed;
|
|
261
|
+
packed = null;
|
|
262
|
+
return result;
|
|
263
|
+
} finally {
|
|
264
|
+
packed?.dispose();
|
|
265
|
+
stacked?.dispose();
|
|
266
|
+
for (const array of [...tokens, ...processed]) array.dispose();
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
commit(tokens: readonly number[]): void {
|
|
270
|
+
if (config.historyUpdate === "after-sample") commitNumbers(tokens);
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
: undefined;
|
|
274
|
+
|
|
275
|
+
const unread = !grammar && !capture
|
|
276
|
+
? {
|
|
277
|
+
sample(scores: MlxArray, step: number): MlxArray {
|
|
278
|
+
const { token } = sampleDevice(scores, step);
|
|
279
|
+
if (config.historyUpdate === "after-sample") commitDevice(token);
|
|
280
|
+
return token;
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
: undefined;
|
|
284
|
+
|
|
285
|
+
const common = {
|
|
286
|
+
unread,
|
|
287
|
+
independent: isPlainGreedy(options, processors.length > 0) && !capture && !config.sampler
|
|
288
|
+
? independentGreedySampling : undefined,
|
|
289
|
+
positional,
|
|
290
|
+
isPlainGreedy: isPlainGreedy(options, processors.length > 0),
|
|
291
|
+
capturesLogprobs: capture,
|
|
292
|
+
needsHistory: historyLimit > 0,
|
|
293
|
+
seedHistory,
|
|
294
|
+
commitDevice,
|
|
295
|
+
commitNumbers,
|
|
296
|
+
dispose: () => {
|
|
297
|
+
history?.dispose();
|
|
298
|
+
history = null;
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
if (config.tokenRepresentation === "device") {
|
|
303
|
+
return {
|
|
304
|
+
...common,
|
|
305
|
+
sample(logits, step) {
|
|
306
|
+
const result = sampleDevice(logits, step);
|
|
307
|
+
if (config.historyUpdate === "after-sample") commitDevice(result.token);
|
|
308
|
+
return result;
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
...common,
|
|
315
|
+
async sample(logits, step) {
|
|
316
|
+
if (grammar && !grammar.isTerminated) await grammar.ready();
|
|
317
|
+
const result = sampleDevice(logits, step);
|
|
318
|
+
let tokenArray: MlxArray | null = result.token;
|
|
319
|
+
try {
|
|
320
|
+
// Read the sampler's integer result directly. Adding an astype here
|
|
321
|
+
// would append work behind the next dispatched decode step.
|
|
322
|
+
const token = ops.itemUint32(tokenArray);
|
|
323
|
+
tokenArray.dispose();
|
|
324
|
+
tokenArray = null;
|
|
325
|
+
if (config.acceptGrammar && grammar && !config.eosTokenIds?.includes(token))
|
|
326
|
+
grammar.accept(token);
|
|
327
|
+
if (config.historyUpdate === "after-sample") commitNumbers([token]);
|
|
328
|
+
return { token, extras: result.extras };
|
|
329
|
+
} catch (error) {
|
|
330
|
+
disposeStepExtras(result.extras);
|
|
331
|
+
throw error;
|
|
332
|
+
} finally {
|
|
333
|
+
tokenArray?.dispose();
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Stateless sampling across rows or verification positions. Preserve the
|
|
340
|
+
* normalized-score argmax, including rounding-created ties. */
|
|
341
|
+
export const independentGreedySampling = Object.freeze({
|
|
342
|
+
sample: normalizedArgmax,
|
|
343
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Sampling — port of mlx-lm's sample_utils.make_sampler /
|
|
2
|
+
// make_logits_processors (temperature, top-p, top-k, min-p, XTC,
|
|
3
|
+
// repetition/presence/frequency penalties, logit bias).
|
|
4
|
+
// All filtering happens on-device; only the chosen token id crosses to JS.
|
|
5
|
+
// Seeded: each step derives a fresh key from (seed, step) so runs are
|
|
6
|
+
// reproducible without sharing global RNG state. (mlx-lm draws XTC's
|
|
7
|
+
// uniform and the categorical from one global stream; we split the
|
|
8
|
+
// per-step key into (xtc, categorical) subkeys instead — deterministic,
|
|
9
|
+
// and the XTC-off path is unchanged.)
|
|
10
|
+
|
|
11
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
12
|
+
import type { SampledToken,SamplingSession } from "../contracts/portable/sampling";
|
|
13
|
+
import { type CurveParams } from "./curve";
|
|
14
|
+
|
|
15
|
+
/** Resolved HLG sampling config (the user-facing knobs). The mid gain is NOT
|
|
16
|
+
* here — it folds from temperature (m = 1/T) when the sampler is built. */
|
|
17
|
+
export interface HlgConfig {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
width: number;
|
|
20
|
+
shoulder: number;
|
|
21
|
+
toe: number;
|
|
22
|
+
pivot?: "top" | "entropy" | "median";
|
|
23
|
+
pivotOffset: number;
|
|
24
|
+
/** Use the LITERAL HLG OETF (gamma below 1/12, log above) instead of the
|
|
25
|
+
* parametric toe/mid/shoulder curve. Ignores gain/width/shoulder/toe/pivot. */
|
|
26
|
+
oetf?: boolean;
|
|
27
|
+
/** Use the LITERAL HLG EOTF (inverse OETF → OOTF gamma) — the decode/display
|
|
28
|
+
* direction (suppresses the tail). Ignores the parametric knobs. */
|
|
29
|
+
eotf?: boolean;
|
|
30
|
+
/** EOTF only: nominal peak display luminance L_W (cd/m²) → OOTF gamma.
|
|
31
|
+
* Default 1000 (γ = 1.2). Lower = flatter (more diverse), higher = sharper. */
|
|
32
|
+
lw?: number;
|
|
33
|
+
/** Use the FULL HLG chain: OETF signal shape → OOTF scaling (applyHlgPipeline).
|
|
34
|
+
* γ from `lw`. Ignores the parametric knobs. */
|
|
35
|
+
pipeline?: boolean;
|
|
36
|
+
/** Pipeline only: α = max brightness — the top logit the OOTF scales everything
|
|
37
|
+
* under ("the most confidence we want"). Default 5. For the shaper, reused as
|
|
38
|
+
* `out_scale` (default 12). */
|
|
39
|
+
maxBrightness?: number;
|
|
40
|
+
/** Use the user-specified HLGShaper (windowed anchor → piecewise OETF with a
|
|
41
|
+
* cubic suppress-toe → OOTF → ×out_scale). `lw`→L_W, `maxBrightness`→out_scale. */
|
|
42
|
+
shaper?: boolean;
|
|
43
|
+
/** Shaper only: W = nats of headroom below the top logit that span the curve
|
|
44
|
+
* (`x = clamp((ℓ−ℓmax)/W + 1, 0, 1)`). Default 10. */
|
|
45
|
+
window?: number;
|
|
46
|
+
/** Shaper only: s_m — mid sharpness (how hard above-median tokens are elevated). */
|
|
47
|
+
sM?: number;
|
|
48
|
+
/** Shaper only: A — shoulder compression (how hard top-end confidence is tamed). */
|
|
49
|
+
shoulderA?: number;
|
|
50
|
+
/** Shaper only: target top-to-reference logit gap (out_scale auto-derives from this
|
|
51
|
+
* so it's decoupled from W). Default 15. */
|
|
52
|
+
targetGap?: number;
|
|
53
|
+
/** Shaper only: reference token nats below the top for the auto out_scale. Default 4. */
|
|
54
|
+
refGap?: number;
|
|
55
|
+
/** Shaper only: curve geometry (the pivots / toe / mid power). Default to the
|
|
56
|
+
* HLGShaper constants (xM 0.55, yM 0.5, xFloor 0.2, yFloor 0.18, p 2.0) when unset. */
|
|
57
|
+
xM?: number; yM?: number; xFloor?: number; yFloor?: number; p?: number;
|
|
58
|
+
/** Optional explicit mid gain m. Default: folds from temperature (m = 1/T),
|
|
59
|
+
* so temperature stays the contrast knob. An explicit value decouples mid
|
|
60
|
+
* contrast from temperature — needed to probe mid-boost while holding the
|
|
61
|
+
* model's recommended temperature fixed. */
|
|
62
|
+
gain?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface SamplerOptions {
|
|
66
|
+
temperature?: number;
|
|
67
|
+
topP?: number;
|
|
68
|
+
topK?: number;
|
|
69
|
+
/** min-p: keep tokens whose probability ≥ minP · p(top token). mlx-lm's
|
|
70
|
+
* `min_p`. Applied after top-p, before XTC/top-k (reference chain order). */
|
|
71
|
+
minP?: number;
|
|
72
|
+
/** Minimum number of tokens min-p may never filter out. Default 1. */
|
|
73
|
+
minTokensToKeep?: number;
|
|
74
|
+
/** XTC ("exclude top choices"): with this probability per step, drop every
|
|
75
|
+
* token above the threshold EXCEPT the least likely of them. */
|
|
76
|
+
xtcProbability?: number;
|
|
77
|
+
/** Probability a token must exceed to be an XTC removal candidate. [0, 0.5]. */
|
|
78
|
+
xtcThreshold?: number;
|
|
79
|
+
/** Token ids XTC never removes (mlx-lm's server passes EOS + "\n"). */
|
|
80
|
+
xtcSpecialTokens?: number[];
|
|
81
|
+
seed?: number;
|
|
82
|
+
/** HLG tone-curve sampling. When enabled, replaces temperature's flat slope
|
|
83
|
+
* with the piecewise curve (temperature becomes the mid gain). Off/undefined
|
|
84
|
+
* ⇒ the plain temperature path, unchanged. `02d723a:docs/archive/hlg-sampling.md`. */
|
|
85
|
+
hlg?: HlgConfig;
|
|
86
|
+
/** v2 log-prob transfer-curve sampler. When set, the drawn monotone curve
|
|
87
|
+
* REPLACES temperature+softmax entirely (stochastic, seeded) — see
|
|
88
|
+
* packages/inference/src/sampling/curve.ts. Identity curve ≡ temperature 1. */
|
|
89
|
+
curve?: CurveParams;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface LogitsProcessorOptions {
|
|
93
|
+
/** Additive per-token-id logit bias, applied before any penalty
|
|
94
|
+
* (mlx-lm's `logit_bias`: {tokenId: bias}). */
|
|
95
|
+
logitBias?: Record<number, number>;
|
|
96
|
+
repetitionPenalty?: number;
|
|
97
|
+
repetitionContextSize?: number;
|
|
98
|
+
/** OpenAI-style presence penalty: subtracted ONCE from a token's logit if it
|
|
99
|
+
* occurred at all in the recent window. May be negative. */
|
|
100
|
+
presencePenalty?: number;
|
|
101
|
+
presenceContextSize?: number;
|
|
102
|
+
/** OpenAI-style frequency penalty: subtracted once PER OCCURRENCE of the
|
|
103
|
+
* token in the recent window. May be negative. */
|
|
104
|
+
frequencyPenalty?: number;
|
|
105
|
+
frequencyContextSize?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** logprobs [1, V] → sampled token array (uint32, shape [1]). */
|
|
109
|
+
export type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
|
|
110
|
+
|
|
111
|
+
/** (deviceTokens [n] | null, logits [1, V]) → logits [1, V]. */
|
|
112
|
+
export type LogitsProcessor = (tokens: MlxArray | null, logits: MlxArray) => MlxArray;
|
|
113
|
+
|
|
114
|
+
/** Optional arrays captured from the same post-processor logprobs used to
|
|
115
|
+
* sample a token. The caller controls readback timing and owns disposal. */
|
|
116
|
+
export interface StepExtras {
|
|
117
|
+
sel: MlxArray | null;
|
|
118
|
+
topIdx: MlxArray | null;
|
|
119
|
+
topVals: MlxArray | null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type StepSample<T> = SampledToken<T, StepExtras>;
|
|
123
|
+
|
|
124
|
+
interface StepGrammar {
|
|
125
|
+
readonly isTerminated: boolean;
|
|
126
|
+
ready(): Promise<void>;
|
|
127
|
+
applyMask(logits: MlxArray): MlxArray;
|
|
128
|
+
accept(token: number): void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface StepSamplerOptions extends SamplerOptions, LogitsProcessorOptions {
|
|
132
|
+
grammar?: StepGrammar | null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface StepSamplerConfigBase {
|
|
136
|
+
/** The grammar owner already awaited ready(), or this sampler must do it. */
|
|
137
|
+
grammarWait: "external" | "before-sample";
|
|
138
|
+
/** Update processor history in sample(), or let the decode loop commit later. */
|
|
139
|
+
historyUpdate: "after-sample" | "manual";
|
|
140
|
+
initialHistory?: readonly number[];
|
|
141
|
+
captureSelectedLogprob?: boolean;
|
|
142
|
+
captureTopLogprobs?: number;
|
|
143
|
+
/** Eager-number mode can advance grammar after sampling. */
|
|
144
|
+
acceptGrammar?: boolean;
|
|
145
|
+
eosTokenIds?: readonly number[];
|
|
146
|
+
/** Reuse a sampler already shared with a speculative draft source. */
|
|
147
|
+
sampler?: Sampler;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface DeviceStepSamplerConfig extends StepSamplerConfigBase {
|
|
151
|
+
tokenRepresentation: "device";
|
|
152
|
+
grammarWait: "external";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface NumberStepSamplerConfig extends StepSamplerConfigBase {
|
|
156
|
+
tokenRepresentation: "number";
|
|
157
|
+
grammarWait: "before-sample";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export type DeviceStepSampler = SamplingSession<MlxArray, MlxArray, StepSample<MlxArray>>;
|
|
161
|
+
export type NumberStepSampler = SamplingSession<MlxArray, MlxArray, Promise<StepSample<number>>>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Row-batched top-p / top-k filtering for a verify window.
|
|
2
|
+
//
|
|
3
|
+
// The accept walk samples every position of a speculative window. Each position
|
|
4
|
+
// runs the same filter chain over the full vocabulary: exp, argsort, gather,
|
|
5
|
+
// cumsum, inverse permutation, compare (top-p), then argpartition and a scatter
|
|
6
|
+
// (top-k). Done per position that is W independent chains of full-vocabulary
|
|
7
|
+
// kernels. Every one of those ops is row-wise along the last axis, so the chain
|
|
8
|
+
// over [W, V] computes each row with the same arithmetic as the [1, V] chain.
|
|
9
|
+
// Only the FILTERS batch: each row is still drawn separately with its own step
|
|
10
|
+
// key on a [1, V] slice, because MLX's categorical takes its inverse-CDF path
|
|
11
|
+
// only for that shape, and the draft is coupled to exactly that draw.
|
|
12
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
13
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
14
|
+
|
|
15
|
+
function negInfLike(a: MlxArray): MlxArray {
|
|
16
|
+
return ops.scalarLike(-Infinity, a);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** apply_top_p over rows: logprobs [W, V] -> logprobs with the tail at -inf. */
|
|
20
|
+
export function applyTopPRows(lp: MlxArray, topP: number): MlxArray {
|
|
21
|
+
const V = lp.shape[1]!;
|
|
22
|
+
using probs = ops.exp(lp);
|
|
23
|
+
using sortedIdx = ops.argsortAxis(lp, -1);
|
|
24
|
+
using sortedProbs = ops.takeAlongAxis(probs, sortedIdx, -1);
|
|
25
|
+
using cum = ops.cumsum(sortedProbs, -1);
|
|
26
|
+
using zerosIdx = ops.zeros(sortedIdx.shape, sortedIdx.dtype);
|
|
27
|
+
using ar = ops.arange(0, V, 1, sortedIdx.dtype);
|
|
28
|
+
using arRow = ops.reshape(ar, [1, V]);
|
|
29
|
+
using arRows = ops.add(zerosIdx, arRow); // integer broadcast: exact
|
|
30
|
+
using inverse = ops.putAlongAxis(zerosIdx, sortedIdx, arRows, -1);
|
|
31
|
+
using cumOrig = ops.takeAlongAxis(cum, inverse, -1);
|
|
32
|
+
using threshold = ops.scalarLike(1 - topP, cumOrig);
|
|
33
|
+
using keep = ops.less(threshold, cumOrig);
|
|
34
|
+
using ninf = negInfLike(lp);
|
|
35
|
+
return ops.where(keep, lp, ninf);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** apply_top_k over rows: all but each row's k highest logprobs -> -inf. */
|
|
39
|
+
export function applyTopKRows(lp: MlxArray, topK: number): MlxArray {
|
|
40
|
+
const [W, V] = lp.shape as [number, number];
|
|
41
|
+
using negLp = ops.neg(lp);
|
|
42
|
+
using part = ops.argpartitionAxis(negLp, topK - 1, -1);
|
|
43
|
+
using maskIdx = part.slice([0, topK], [W, V]);
|
|
44
|
+
using ninf = negInfLike(lp);
|
|
45
|
+
return ops.putAlongAxis(lp, maskIdx, ninf, -1);
|
|
46
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { MlxArray } from "@mlx-bun/mlx/array";
|
|
2
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
3
|
+
import type { Cache } from "../contracts/mlx/cache";
|
|
4
|
+
import type { MlxHeadForward, MlxSequenceForward } from "../contracts/mlx/graph";
|
|
5
|
+
import type { MlxPromptInput } from "../contracts/mlx/media";
|
|
6
|
+
import { logitsFromHiddenPadM, sequenceLogProbabilities } from "./response-logps";
|
|
7
|
+
|
|
8
|
+
/** The operations a closed-answer decision consumes. The graph and its weights
|
|
9
|
+
* are borrowed; each call owns fresh state. No sampling or generation is required. */
|
|
10
|
+
export interface DecisionScoringGraph {
|
|
11
|
+
makeCache(): Cache[];
|
|
12
|
+
forward: MlxSequenceForward;
|
|
13
|
+
logits: MlxHeadForward;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Teacher-forced conditional likelihoods for complete supplied answers.
|
|
17
|
+
* Each forward reads [prompt; answer[:-1]]. Only hidden rows that predict answer
|
|
18
|
+
* tokens reach the head: prompt targets are masked out. Average log p(token | prefix)
|
|
19
|
+
* over each answer (negative mean NLL), then softmax across candidate averages. No vocabulary restriction
|
|
20
|
+
* changes a token's likelihood. Include a terminator in each answer to score
|
|
21
|
+
* complete, mutually exclusive responses (especially when one prefixes another).
|
|
22
|
+
*
|
|
23
|
+
* Inputs and graph are borrowed; every candidate gets fresh caches, released with
|
|
24
|
+
* its intermediates on success, error or cancellation. The caller serializes
|
|
25
|
+
* shared graph access. This is inference, with no training or reference model.
|
|
26
|
+
* The average includes the terminator and excludes every prompt token. */
|
|
27
|
+
export async function scoreDecisionAnswers(graph: DecisionScoringGraph, prompt: readonly number[],
|
|
28
|
+
candidates: readonly (readonly number[])[], options: { signal?: AbortSignal; input?: MlxPromptInput;
|
|
29
|
+
/** Run one candidate's owned work at a scheduler boundary. Direct library
|
|
30
|
+
* callers may omit this when they already own exclusive graph access. */
|
|
31
|
+
execute?: <T>(work: () => Promise<T>) => Promise<T>;
|
|
32
|
+
} = {}): Promise<{
|
|
33
|
+
probabilities: number[]; meanLogLikelihoods: number[];
|
|
34
|
+
}> {
|
|
35
|
+
options.signal?.throwIfAborted();
|
|
36
|
+
if (!prompt.length) throw new RangeError("a decision prompt must contain tokens");
|
|
37
|
+
if (!candidates.length || candidates.some(answer => !answer.length || answer.some(id => !Number.isSafeInteger(id) || id < 0)))
|
|
38
|
+
throw new RangeError("a decision requires nonempty candidate token sequences");
|
|
39
|
+
const meanLogLikelihoods: number[] = [];
|
|
40
|
+
for (const answer of candidates) {
|
|
41
|
+
options.signal?.throwIfAborted();
|
|
42
|
+
const score = await (options.execute ?? (work => work()))(async () => {
|
|
43
|
+
options.signal?.throwIfAborted();
|
|
44
|
+
const caches = graph.makeCache();
|
|
45
|
+
try {
|
|
46
|
+
const tokens = [...prompt, ...answer.slice(0, -1)];
|
|
47
|
+
using ids = ops.fromInt32(tokens, [1, tokens.length]);
|
|
48
|
+
using hidden = options.input ? mediaResponseHidden(options.input, prompt, answer, caches, options.signal)
|
|
49
|
+
: graph.forward(ids, caches, tokens.length);
|
|
50
|
+
const [batch, length, width] = hidden.shape;
|
|
51
|
+
if (batch !== 1 || length !== (options.input ? answer.length : tokens.length) || !width)
|
|
52
|
+
throw new Error("decision forward returned an invalid hidden shape");
|
|
53
|
+
// Prediction at prompt.length - 1 targets answer[0], not prompt's last token.
|
|
54
|
+
using response = hidden.slice([0, options.input ? 0 : prompt.length - 1, 0], [1, length, width]);
|
|
55
|
+
using logits = logitsFromHiddenPadM({ logitsFromHidden: graph.logits }, response);
|
|
56
|
+
const vocab = logits.shape.at(-1)!;
|
|
57
|
+
if (answer.some(id => id >= vocab)) throw new RangeError("decision token id is outside the graph vocabulary");
|
|
58
|
+
// The DPO reduction sees only supervised rows; equivalent to a zero mask
|
|
59
|
+
// on all earlier prompt positions, without projecting them to vocabulary.
|
|
60
|
+
using logp = sequenceLogProbabilities(logits, [[prompt.at(-1)!, ...answer]], [[0, ...answer.map(() => 1)]]);
|
|
61
|
+
const score = logp.toFloat32()[0]! / answer.length;
|
|
62
|
+
options.signal?.throwIfAborted();
|
|
63
|
+
if (!Number.isFinite(score)) throw new Error("decision answer has a nonfinite log likelihood");
|
|
64
|
+
return score;
|
|
65
|
+
} finally {
|
|
66
|
+
const errors: unknown[] = [];
|
|
67
|
+
for (const cache of caches) { try { cache.dispose(); } catch (error) { errors.push(error); } }
|
|
68
|
+
if (errors.length) throw new AggregateError(errors, "decision cache cleanup failed");
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
meanLogLikelihoods.push(score);
|
|
72
|
+
await new Promise<void>(resolve => setImmediate(resolve));
|
|
73
|
+
}
|
|
74
|
+
options.signal?.throwIfAborted();
|
|
75
|
+
const maximum = Math.max(...meanLogLikelihoods), weights = meanLogLikelihoods.map(value => Math.exp(value - maximum));
|
|
76
|
+
const total = weights.reduce((sum, value) => sum + value, 0);
|
|
77
|
+
return { meanLogLikelihoods, probabilities: weights.map(value => value / total) };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Media ports expose the prompt tip and position-aware continuation. Feed
|
|
81
|
+
* supplied answer tokens through those operations, collecting their prediction
|
|
82
|
+
* rows; no sampling, generated ids, or generation state is involved. */
|
|
83
|
+
function mediaResponseHidden(input: MlxPromptInput, prompt: readonly number[], answer: readonly number[],
|
|
84
|
+
caches: Cache[], signal?: AbortSignal): MlxArray {
|
|
85
|
+
const rows: MlxArray[] = [];
|
|
86
|
+
try {
|
|
87
|
+
using ids = ops.fromInt32([...prompt], [1, prompt.length]);
|
|
88
|
+
rows.push(input.prefill(ids, caches, 0, "tail", prompt.length));
|
|
89
|
+
for (let i = 0; i < answer.length - 1; i++) {
|
|
90
|
+
signal?.throwIfAborted();
|
|
91
|
+
using token = ops.fromInt32([answer[i]!], [1, 1]);
|
|
92
|
+
rows.push(input.prefill(token, caches, prompt.length + i, "decode", 1));
|
|
93
|
+
}
|
|
94
|
+
return ops.concatAxis(rows, 1);
|
|
95
|
+
} finally { for (const row of rows) row.dispose(); }
|
|
96
|
+
}
|