@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,53 @@
|
|
|
1
|
+
import type { DisposableResource, PreparedStateChange, ResourceOwner } from "../contracts/portable/resources";
|
|
2
|
+
|
|
3
|
+
export function ownResource<T>(value: T, release: (value: T) => void): ResourceOwner<T> {
|
|
4
|
+
let held: { value: T } | undefined = { value };
|
|
5
|
+
const borrow = () => {
|
|
6
|
+
if (!held) throw new Error("resource ownership has ended");
|
|
7
|
+
return held.value;
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
borrow,
|
|
11
|
+
transfer() { const result = borrow(); held = undefined; return result; },
|
|
12
|
+
close() {
|
|
13
|
+
if (!held) return;
|
|
14
|
+
const result = held.value;
|
|
15
|
+
held = undefined;
|
|
16
|
+
release(result);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Attempt every release once. One failed destructor must not leak siblings. */
|
|
22
|
+
export function disposeResources(resources: Iterable<DisposableResource>): void {
|
|
23
|
+
const errors: unknown[] = [];
|
|
24
|
+
for (const resource of new Set(resources)) {
|
|
25
|
+
try { resource.dispose(); } catch (error) { errors.push(error); }
|
|
26
|
+
}
|
|
27
|
+
if (errors.length === 1) throw errors[0];
|
|
28
|
+
if (errors.length) throw new AggregateError(errors, "resource cleanup failed");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Used at failure boundaries to retain the execution error as well as cleanup. */
|
|
32
|
+
export function cleanupFailure(error: unknown, cleanup: () => void): never {
|
|
33
|
+
try { cleanup(); }
|
|
34
|
+
catch (cleanupError) { throw new AggregateError([error, cleanupError], "execution and cleanup failed"); }
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Complete every preparation before publishing any owner's replacement.
|
|
39
|
+
* Old state is released only after all owners have committed. */
|
|
40
|
+
export function applyStateChanges(prepare: readonly (() => PreparedStateChange)[]): void {
|
|
41
|
+
const changes: PreparedStateChange[] = [];
|
|
42
|
+
try {
|
|
43
|
+
for (const build of prepare) changes.push(build());
|
|
44
|
+
for (const change of changes) change.commit();
|
|
45
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources(changes.splice(0))); }
|
|
46
|
+
disposeResources(changes);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function withResource<T, R>(owner: Pick<ResourceOwner<T>, "borrow" | "close">, use: (value: T) => R): R {
|
|
50
|
+
try { return use(owner.borrow()); }
|
|
51
|
+
catch (error) { cleanupFailure(error, () => owner.close()); }
|
|
52
|
+
finally { owner.close(); }
|
|
53
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type RowTable<Operation> = Readonly<Partial<Record<number, Operation>> & { readonly default?: Operation }>;
|
|
2
|
+
type RowOperations<Operation> = RowTable<Operation> |
|
|
3
|
+
readonly { readonly from: number; readonly to: number; readonly operation: Operation }[];
|
|
4
|
+
|
|
5
|
+
/** Index authored exact rows, an explicit default, or authored contiguous ranges. */
|
|
6
|
+
export function rowOperationFor<Operation>(operations: readonly {readonly from:number;readonly to:number;readonly operation:Operation}[], rows:number):Operation;
|
|
7
|
+
export function rowOperationFor<Operation>(operations: RowTable<Operation>, rows:number):Operation;
|
|
8
|
+
export function rowOperationFor<Operation>(operations: RowOperations<Operation>, rows:number):Operation;
|
|
9
|
+
export function rowOperationFor<Operation>(operations: RowOperations<Operation>, rows: number): Operation {
|
|
10
|
+
let operation: Operation | undefined;
|
|
11
|
+
if (Number.isSafeInteger(rows) && rows > 0) {
|
|
12
|
+
operation = Array.isArray(operations)
|
|
13
|
+
? operations.find(range => rows >= range.from && rows <= range.to)?.operation
|
|
14
|
+
: (Object.hasOwn(operations, rows) ? (operations as RowTable<Operation>)[rows] : undefined)
|
|
15
|
+
?? (Object.hasOwn(operations, "default") ? (operations as RowTable<Operation>).default : undefined);
|
|
16
|
+
}
|
|
17
|
+
if (operation === undefined) throw new Error(`no declared operation for ${rows} rows`);
|
|
18
|
+
return operation;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Compatibility data helper. Its default is an ordinary inspectable own property. */
|
|
22
|
+
export function rowTableWithDefault<Operation>(entries: RowTable<Operation>, defaultOperation: Operation): RowTable<Operation> {
|
|
23
|
+
return Object.freeze({ ...entries, default: defaultOperation });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Map only authored entries, ranges and an explicitly present default. */
|
|
27
|
+
export function mapOperations<Input,Output>(operations: readonly {readonly from:number;readonly to:number;readonly operation:Input}[], bind:(operation:Input)=>Output): readonly {readonly from:number;readonly to:number;readonly operation:Output}[];
|
|
28
|
+
export function mapOperations<Input,Output>(operations: RowTable<Input>, bind:(operation:Input)=>Output):RowTable<Output>;
|
|
29
|
+
export function mapOperations<Input,Output>(operations: RowOperations<Input>, bind:(operation:Input)=>Output):RowOperations<Output>;
|
|
30
|
+
export function mapOperations<Input, Output>(operations: RowOperations<Input>,
|
|
31
|
+
bind: (operation: Input) => Output): RowOperations<Output> {
|
|
32
|
+
if (Array.isArray(operations))
|
|
33
|
+
return Object.freeze(operations.map(range => Object.freeze({ from: range.from, to: range.to, operation: bind(range.operation) })));
|
|
34
|
+
const source = operations as RowTable<Input>, entries: Partial<Record<number, Output>> & { default?: Output } = {};
|
|
35
|
+
for (const [key, operation] of Object.entries(source)) {
|
|
36
|
+
if (operation === undefined) continue;
|
|
37
|
+
const rows = Number(key);
|
|
38
|
+
if (key === "default") entries.default = bind(operation);
|
|
39
|
+
else if (Number.isSafeInteger(rows) && rows > 0 && String(rows) === key) entries[rows] = bind(operation);
|
|
40
|
+
}
|
|
41
|
+
return Object.freeze(entries);
|
|
42
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { runtimeValue } from "./config";
|
|
2
|
+
|
|
3
|
+
export const P2R_TRACE_PREFIX = "[p2r] ";
|
|
4
|
+
export const P2R_TRACE_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
export type P2RTraceOutcome = "success" | "error" | "abort";
|
|
7
|
+
|
|
8
|
+
export type P2RTracePhase =
|
|
9
|
+
| "request.body_parse"
|
|
10
|
+
| "request.prompt_prepare"
|
|
11
|
+
| "completion.total"
|
|
12
|
+
| "completion.placement"
|
|
13
|
+
| "engine.request_wait"
|
|
14
|
+
| "engine.admission_wait"
|
|
15
|
+
| "engine.mixed_forward"
|
|
16
|
+
| "cache.lookup_restore"
|
|
17
|
+
| "cache.prefetch"
|
|
18
|
+
| "prefill.total"
|
|
19
|
+
| "prefill.batch_setup"
|
|
20
|
+
| "prefill.chunk"
|
|
21
|
+
| "prefill.row_sync"
|
|
22
|
+
| "prefill.forward"
|
|
23
|
+
| "prefill.evaluate"
|
|
24
|
+
| "prefill.checkpoint"
|
|
25
|
+
| "prefill.project"
|
|
26
|
+
| "prefill.complete"
|
|
27
|
+
| "prefill.companion"
|
|
28
|
+
| "prefill.kv_maintenance"
|
|
29
|
+
| "token_zero.total"
|
|
30
|
+
| "token_zero.forward"
|
|
31
|
+
| "token_zero.head"
|
|
32
|
+
| "token_zero.sample"
|
|
33
|
+
| "response.token_route"
|
|
34
|
+
| "response.first_write"
|
|
35
|
+
| "response.final_write";
|
|
36
|
+
|
|
37
|
+
export type P2RTraceAttributes = Readonly<Record<
|
|
38
|
+
string,
|
|
39
|
+
string | number | boolean | null
|
|
40
|
+
>>;
|
|
41
|
+
|
|
42
|
+
export interface P2RTraceEvent {
|
|
43
|
+
phase: P2RTracePhase;
|
|
44
|
+
startMs: number;
|
|
45
|
+
durationMs: number;
|
|
46
|
+
attributes?: P2RTraceAttributes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface P2RTraceRecord {
|
|
50
|
+
version: typeof P2R_TRACE_VERSION;
|
|
51
|
+
traceId: string;
|
|
52
|
+
requestId: string;
|
|
53
|
+
route: string;
|
|
54
|
+
clock: "monotonic-ms";
|
|
55
|
+
outcome: P2RTraceOutcome;
|
|
56
|
+
/** Process-local monotonic origin; aligns concurrent requests in one server. */
|
|
57
|
+
startedAtMs?: number;
|
|
58
|
+
totalMs: number;
|
|
59
|
+
events: P2RTraceEvent[];
|
|
60
|
+
attributes?: P2RTraceAttributes;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type Clock = () => number;
|
|
64
|
+
type Emit = (record: P2RTraceRecord) => void;
|
|
65
|
+
|
|
66
|
+
export interface PromptResponseTraceOptions {
|
|
67
|
+
traceId: string;
|
|
68
|
+
requestId: string;
|
|
69
|
+
route: string;
|
|
70
|
+
clock?: Clock;
|
|
71
|
+
emit?: Emit;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Request-local diagnostic trace. Callers pass it explicitly because the
|
|
75
|
+
* continuous scheduler interleaves rows and cannot safely use ambient async
|
|
76
|
+
* context. No instance is created unless MLX_BUN_P2R_TRACE=1. */
|
|
77
|
+
export class PromptResponseTrace {
|
|
78
|
+
readonly #startedAt: number;
|
|
79
|
+
readonly #events: P2RTraceEvent[] = [];
|
|
80
|
+
readonly #clock: Clock;
|
|
81
|
+
readonly #emit: Emit;
|
|
82
|
+
#finished = false;
|
|
83
|
+
|
|
84
|
+
readonly traceId: string;
|
|
85
|
+
readonly requestId: string;
|
|
86
|
+
readonly route: string;
|
|
87
|
+
|
|
88
|
+
constructor(options: PromptResponseTraceOptions) {
|
|
89
|
+
this.traceId = options.traceId;
|
|
90
|
+
this.requestId = options.requestId;
|
|
91
|
+
this.route = options.route;
|
|
92
|
+
this.#clock = options.clock ?? performance.now.bind(performance);
|
|
93
|
+
this.#emit = options.emit ?? ((record) => {
|
|
94
|
+
console.error(`${P2R_TRACE_PREFIX}${JSON.stringify(record)}`);
|
|
95
|
+
});
|
|
96
|
+
this.#startedAt = this.#clock();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get finished(): boolean {
|
|
100
|
+
return this.#finished;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Start one span. The returned closer is idempotent so finally blocks and
|
|
104
|
+
* early exits cannot double-record a phase. */
|
|
105
|
+
begin(phase: P2RTracePhase, attributes?: P2RTraceAttributes): () => void {
|
|
106
|
+
const startedAt = this.#clock();
|
|
107
|
+
let closed = false;
|
|
108
|
+
return () => {
|
|
109
|
+
if (closed || this.#finished) return;
|
|
110
|
+
closed = true;
|
|
111
|
+
const endedAt = this.#clock();
|
|
112
|
+
this.#events.push({
|
|
113
|
+
phase,
|
|
114
|
+
startMs: startedAt - this.#startedAt,
|
|
115
|
+
durationMs: endedAt - startedAt,
|
|
116
|
+
...(attributes ? { attributes } : {}),
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
mark(phase: P2RTracePhase, attributes?: P2RTraceAttributes): void {
|
|
122
|
+
if (this.#finished) return;
|
|
123
|
+
this.#events.push({
|
|
124
|
+
phase,
|
|
125
|
+
startMs: this.#clock() - this.#startedAt,
|
|
126
|
+
durationMs: 0,
|
|
127
|
+
...(attributes ? { attributes } : {}),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
finish(
|
|
132
|
+
outcome: P2RTraceOutcome,
|
|
133
|
+
attributes?: P2RTraceAttributes,
|
|
134
|
+
): P2RTraceRecord | null {
|
|
135
|
+
if (this.#finished) return null;
|
|
136
|
+
this.#finished = true;
|
|
137
|
+
const totalMs = this.#clock() - this.#startedAt;
|
|
138
|
+
const record: P2RTraceRecord = {
|
|
139
|
+
version: P2R_TRACE_VERSION,
|
|
140
|
+
traceId: this.traceId,
|
|
141
|
+
requestId: this.requestId,
|
|
142
|
+
route: this.route,
|
|
143
|
+
clock: "monotonic-ms",
|
|
144
|
+
outcome,
|
|
145
|
+
startedAtMs: this.#startedAt,
|
|
146
|
+
totalMs,
|
|
147
|
+
events: [...this.#events].sort((a, b) => a.startMs - b.startMs),
|
|
148
|
+
...(attributes ? { attributes } : {}),
|
|
149
|
+
};
|
|
150
|
+
this.#emit(record);
|
|
151
|
+
return record;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function createPromptResponseTrace(
|
|
156
|
+
options: PromptResponseTraceOptions,
|
|
157
|
+
): PromptResponseTrace | undefined {
|
|
158
|
+
return runtimeValue("MLX_BUN_P2R_TRACE") === "1"
|
|
159
|
+
? new PromptResponseTrace(options)
|
|
160
|
+
: undefined;
|
|
161
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// curve.ts — the v2 log-prob transfer-curve sampler. Replaces the whole
|
|
2
|
+
// temperature+softmax stage with a drawn MONOTONE CUBIC curve in log-probability
|
|
3
|
+
// space:
|
|
4
|
+
//
|
|
5
|
+
// p_out_i = exp(curve(log p_in_i)) / Σ_j exp(curve(log p_in_j))
|
|
6
|
+
//
|
|
7
|
+
// `lp` reaching the sampler is already log p_in (toLogprobs = logits − logsumexp),
|
|
8
|
+
// so the curve maps it directly and an identity curve reproduces temperature 1.
|
|
9
|
+
// The curve is a Fritsch–Carlson monotone cubic Hermite (PCHIP) through N movable
|
|
10
|
+
// control points in nat log-prob space (DaVinci-curves style — the endpoints start
|
|
11
|
+
// at the corners but are draggable too). Smooth, never overshoots, monotone. A
|
|
12
|
+
// control point dragged below the diagonal suppresses those tokens; the gate is
|
|
13
|
+
// emergent from the curve shape, not a separate threshold.
|
|
14
|
+
//
|
|
15
|
+
// Reading the curve: at a given p_in, ABOVE the diagonal raises those tokens,
|
|
16
|
+
// BELOW lowers them (renorm makes the literal number an intent); the SLOPE across
|
|
17
|
+
// a region sets how evenly the tokens inside it spread (flat<1 even, steep>1 separated).
|
|
18
|
+
|
|
19
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
20
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
21
|
+
|
|
22
|
+
export interface CurvePoint { x_pct: number; y_pct: number; }
|
|
23
|
+
export interface CurveParams {
|
|
24
|
+
space?: "logprob";
|
|
25
|
+
points: CurvePoint[]; // ≥2 control points, all movable; first/last are the endpoints
|
|
26
|
+
monotonic?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Bottom-left default anchor / plot floor (p = 1e-6). */
|
|
30
|
+
export const CURVE_UMIN = Math.log(1e-6);
|
|
31
|
+
const L = (pt: CurvePoint): [number, number] => [Math.log(Math.max(pt.x_pct, 1e-9) / 100), Math.log(Math.max(pt.y_pct, 1e-9) / 100)];
|
|
32
|
+
|
|
33
|
+
/** Control points in nat log-prob space, sorted ascending in x. */
|
|
34
|
+
function knotArrays(p: CurveParams): { X: number[]; Y: number[] } {
|
|
35
|
+
const k = p.points.map(L).sort((a, b) => a[0] - b[0]);
|
|
36
|
+
return { X: k.map((q) => q[0]), Y: k.map((q) => q[1]) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Consecutive-point secants, top→bottom (region slopes; what the editor readout shows). */
|
|
40
|
+
export function curveSecants(p: CurveParams): number[] {
|
|
41
|
+
const { X, Y } = knotArrays(p);
|
|
42
|
+
const s: number[] = [];
|
|
43
|
+
for (let i = X.length - 1; i > 0; i--) s.push((Y[i]! - Y[i - 1]!) / (X[i]! - X[i - 1]!));
|
|
44
|
+
return s;
|
|
45
|
+
}
|
|
46
|
+
/** True iff every consecutive-point secant is ≥ 0 (monotone non-decreasing). */
|
|
47
|
+
export function isMonotone(p: CurveParams): boolean {
|
|
48
|
+
return p.points.length >= 2 && curveSecants(p).every((s) => s >= -1e-12);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface Seg { A: number; B: number; C: number; D: number } // cubic in u: A+Bu+Cu²+Du³
|
|
52
|
+
export interface Spline { X: number[]; Y: number[]; M: number[]; segs: Seg[] }
|
|
53
|
+
|
|
54
|
+
function cubicInU(x0: number, h: number, y0: number, y1: number, m0: number, m1: number): Seg {
|
|
55
|
+
const c0 = y0, c1 = h * m0;
|
|
56
|
+
const c2 = -3 * y0 - 2 * h * m0 + 3 * y1 - h * m1;
|
|
57
|
+
const c3 = 2 * y0 + h * m0 - 2 * y1 + h * m1;
|
|
58
|
+
const s = 1 / h, s2 = s * s, s3 = s2 * s;
|
|
59
|
+
let A = c0, B = 0, C = 0, D = 0;
|
|
60
|
+
B += c1 * s; A += -c1 * s * x0;
|
|
61
|
+
C += c2 * s2; B += -2 * x0 * c2 * s2; A += x0 * x0 * c2 * s2;
|
|
62
|
+
D += c3 * s3; C += -3 * x0 * c3 * s3; B += 3 * x0 * x0 * c3 * s3; A += -x0 * x0 * x0 * c3 * s3;
|
|
63
|
+
return { A, B, C, D };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Monotone cubic (Fritsch–Carlson) over the N control points. */
|
|
67
|
+
export function buildSpline(p: CurveParams): Spline {
|
|
68
|
+
const { X, Y } = knotArrays(p);
|
|
69
|
+
const n = X.length;
|
|
70
|
+
const Dsec: number[] = [];
|
|
71
|
+
for (let i = 0; i < n - 1; i++) Dsec.push((Y[i + 1]! - Y[i]!) / (X[i + 1]! - X[i]!));
|
|
72
|
+
const M = new Array<number>(n);
|
|
73
|
+
M[0] = Dsec[0]!; M[n - 1] = Dsec[n - 2]!;
|
|
74
|
+
for (let i = 1; i < n - 1; i++) {
|
|
75
|
+
if (Dsec[i - 1]! * Dsec[i]! <= 0) { M[i] = 0; continue; }
|
|
76
|
+
const h1 = X[i]! - X[i - 1]!, h2 = X[i + 1]! - X[i]!;
|
|
77
|
+
const w1 = 2 * h2 + h1, w2 = h2 + 2 * h1;
|
|
78
|
+
M[i] = (w1 + w2) / (w1 / Dsec[i - 1]! + w2 / Dsec[i]!);
|
|
79
|
+
}
|
|
80
|
+
const segs: Seg[] = [];
|
|
81
|
+
for (let i = 0; i < n - 1; i++) segs.push(cubicInU(X[i]!, X[i + 1]! - X[i]!, Y[i]!, Y[i + 1]!, M[i]!, M[i + 1]!));
|
|
82
|
+
return { X, Y, M, segs };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Scalar eval (editor preview / tests). Extrapolates linearly past both endpoints. */
|
|
86
|
+
export function evalSpline(sp: Spline, u: number): number {
|
|
87
|
+
const n = sp.X.length;
|
|
88
|
+
if (u < sp.X[0]!) return sp.Y[0]! + Math.max(sp.M[0]!, 1) * (u - sp.X[0]!); // deep tail: slope ≥1, stays gated
|
|
89
|
+
if (u > sp.X[n - 1]!) return sp.Y[n - 1]! + sp.M[n - 1]! * (u - sp.X[n - 1]!);
|
|
90
|
+
let i = sp.segs.length - 1;
|
|
91
|
+
while (i > 0 && u < sp.X[i]!) i--;
|
|
92
|
+
const s = sp.segs[i]!;
|
|
93
|
+
return s.A + s.B * u + s.C * u * u + s.D * u * u * u;
|
|
94
|
+
}
|
|
95
|
+
export function curveAt(p: CurveParams, u: number): number { return evalSpline(buildSpline(p), u); }
|
|
96
|
+
|
|
97
|
+
/** Apply the monotone-cubic transfer to normalized logprobs `lp` [.., V] (on-device). */
|
|
98
|
+
export function applyCurve(lp: MlxArray, p: CurveParams): MlxArray {
|
|
99
|
+
const sp = buildSpline(p);
|
|
100
|
+
const n = sp.X.length;
|
|
101
|
+
const tmp: MlxArray[] = [];
|
|
102
|
+
const k = <T extends MlxArray>(a: T): T => { tmp.push(a); return a; };
|
|
103
|
+
const C = (v: number): MlxArray => k(ops.scalarLike(v, lp));
|
|
104
|
+
const lp2 = k(ops.mul(lp, lp));
|
|
105
|
+
const lp3 = k(ops.mul(lp2, lp));
|
|
106
|
+
const seg = (s: Seg): MlxArray => k(ops.add(
|
|
107
|
+
k(ops.add(C(s.A), k(ops.mulScalar(lp, s.B)))),
|
|
108
|
+
k(ops.add(k(ops.mulScalar(lp2, s.C)), k(ops.mulScalar(lp3, s.D)))),
|
|
109
|
+
));
|
|
110
|
+
// below the bottom point: slope ≥ 1 (deep tail stays gated)
|
|
111
|
+
const lowSlope = Math.max(sp.M[0]!, 1);
|
|
112
|
+
let out: MlxArray = k(ops.add(C(sp.Y[0]! - lowSlope * sp.X[0]!), k(ops.mulScalar(lp, lowSlope))));
|
|
113
|
+
for (let i = 0; i < sp.segs.length; i++) {
|
|
114
|
+
const next = ops.where(k(ops.greaterEqual(lp, C(sp.X[i]!))), seg(sp.segs[i]!), out);
|
|
115
|
+
tmp.push(next); out = next;
|
|
116
|
+
}
|
|
117
|
+
// above the top point: linear extrapolation with the top tangent (if it was dragged left of 100%)
|
|
118
|
+
const aboveExt = k(ops.add(C(sp.Y[n - 1]! - sp.M[n - 1]! * sp.X[n - 1]!), k(ops.mulScalar(lp, sp.M[n - 1]!))));
|
|
119
|
+
const result = ops.where(k(ops.greaterEqual(lp, C(sp.X[n - 1]!))), aboveExt, out);
|
|
120
|
+
for (const a of tmp) a.dispose();
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Sampling support for the DSpark/DFlash drafter at temperature > 0: the
|
|
2
|
+
// drafter samples each position from the server sampler's exact
|
|
3
|
+
// top-p/top-k/temperature distribution (sampler.ts), using a seeded key stream
|
|
4
|
+
// that does not touch global RNG state. HLG/curve samplers are out of scope;
|
|
5
|
+
// the caller rejects them.
|
|
6
|
+
|
|
7
|
+
import { MlxArray } from "@mlx-bun/mlx/array";
|
|
8
|
+
import * as ops from "@mlx-bun/mlx/ops";
|
|
9
|
+
import { applyTopP, applyTopK, toLogprobs } from "./index";
|
|
10
|
+
import { stepKey } from "./sampler";
|
|
11
|
+
|
|
12
|
+
export interface DSparkSampleConfig {
|
|
13
|
+
/** temperature > 0 enables sampling; 0 ⇒ greedy (handled by the caller). */
|
|
14
|
+
temperature: number;
|
|
15
|
+
topP?: number;
|
|
16
|
+
topK?: number;
|
|
17
|
+
seed?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Seeded stream of mlx random keys — reproducible draws without sharing global
|
|
21
|
+
* RNG state: draw n is `stepKey(seed >>> 0, n)`. */
|
|
22
|
+
export class KeyStream {
|
|
23
|
+
#seed: number;
|
|
24
|
+
#ctr = 0;
|
|
25
|
+
constructor(seed = 0) { this.#seed = seed >>> 0; }
|
|
26
|
+
next(): MlxArray {
|
|
27
|
+
return stepKey(this.#seed, this.#ctr++);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Processed categorical logits for one position: logits [1,V] → masked +
|
|
33
|
+
* temperature-scaled logits [1,V] (the input to randomCategorical;
|
|
34
|
+
* softmax(·) is the sampling distribution). Mirrors makeSampler's top-p/top-k/
|
|
35
|
+
* temperature path exactly. temperature must be > 0.
|
|
36
|
+
*/
|
|
37
|
+
export function processLogits(logits: MlxArray, cfg: DSparkSampleConfig): MlxArray {
|
|
38
|
+
if (cfg.temperature <= 0) throw new Error("processLogits requires temperature > 0");
|
|
39
|
+
const lp = toLogprobs(logits); // [1,V]
|
|
40
|
+
let cur = lp;
|
|
41
|
+
const owned: MlxArray[] = [lp];
|
|
42
|
+
if (cfg.topP && cfg.topP > 0 && cfg.topP < 1) { cur = applyTopP(cur, cfg.topP); owned.push(cur); }
|
|
43
|
+
if (cfg.topK && cfg.topK > 0) { cur = applyTopK(cur, cfg.topK); owned.push(cur); }
|
|
44
|
+
const scaled = ops.mulScalar(cur, 1 / cfg.temperature);
|
|
45
|
+
for (const a of owned) a.dispose();
|
|
46
|
+
return scaled;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Sample one token id from processed logits [1,V] (∝ softmax). */
|
|
50
|
+
export function sampleToken(scaled: MlxArray, key: MlxArray): number {
|
|
51
|
+
const t = ops.randomCategorical(scaled, key); // [1] uint32
|
|
52
|
+
const id = ops.itemUint32(t);
|
|
53
|
+
t.dispose();
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { TokenLogprobs } from "../contracts/portable/generation";
|
|
13
|
+
import { StepExtras } from "./types";
|
|
14
|
+
|
|
15
|
+
export function stepExtrasArrays(extras: StepExtras | null): MlxArray[] {
|
|
16
|
+
return extras
|
|
17
|
+
? [extras.sel, extras.topIdx, extras.topVals].filter(
|
|
18
|
+
(array): array is MlxArray => array !== null,
|
|
19
|
+
)
|
|
20
|
+
: [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function disposeStepExtras(extras: StepExtras | null): void {
|
|
24
|
+
if (!extras) return;
|
|
25
|
+
extras.sel?.dispose();
|
|
26
|
+
extras.topIdx?.dispose();
|
|
27
|
+
extras.topVals?.dispose();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Read captured probabilities with their token, without adding casts behind
|
|
31
|
+
* the next decode step. Consumes the capture on success and failure. */
|
|
32
|
+
export function readStepExtras(extras: StepExtras | null): TokenLogprobs | undefined {
|
|
33
|
+
if (!extras) return undefined;
|
|
34
|
+
try {
|
|
35
|
+
const out: TokenLogprobs = {};
|
|
36
|
+
if (extras.sel) out.logprob = extras.sel.toFloat32Host()[0]!;
|
|
37
|
+
if (extras.topIdx && extras.topVals) {
|
|
38
|
+
const ids = extras.topIdx.toIntTokens();
|
|
39
|
+
const vals = extras.topVals.toFloat32Host();
|
|
40
|
+
out.top = Array.from(ids, (id, i) => ({ id, logprob: vals[i]! }))
|
|
41
|
+
.sort((a, b) => b.logprob - a.logprob);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
} finally { disposeStepExtras(extras); }
|
|
45
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
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 { Dtype } from "@mlx-bun/mlx/ffi";
|
|
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: keep the smallest set of tokens whose cumulative
|
|
20
|
+
* probability exceeds top_p; others → -inf. */
|
|
21
|
+
export function applyTopP(lp: MlxArray, topP: number): MlxArray {
|
|
22
|
+
const probs = ops.exp(lp);
|
|
23
|
+
const sortedIdx = ops.argsortAxis(lp, -1);
|
|
24
|
+
const sortedProbs = ops.takeAlongAxis(probs, sortedIdx, -1);
|
|
25
|
+
const cum = ops.cumsum(sortedProbs, -1);
|
|
26
|
+
// scatter arange back through sortedIdx to invert the permutation
|
|
27
|
+
const V = lp.shape[lp.shape.length - 1]!;
|
|
28
|
+
const zerosIdx = ops.zeros(sortedIdx.shape, sortedIdx.dtype);
|
|
29
|
+
const ar = ops.arange(0, V, 1, sortedIdx.dtype);
|
|
30
|
+
const arB = ops.reshape(ar, sortedIdx.shape);
|
|
31
|
+
const inverse = ops.putAlongAxis(zerosIdx, sortedIdx, arB, -1);
|
|
32
|
+
const cumOrig = ops.takeAlongAxis(cum, inverse, -1);
|
|
33
|
+
const threshold = ops.scalarLike(1 - topP, cumOrig);
|
|
34
|
+
const keep = ops.less(threshold, cumOrig); // cum > 1 - topP
|
|
35
|
+
const ninf = negInfLike(lp);
|
|
36
|
+
const out = ops.where(keep, lp, ninf);
|
|
37
|
+
for (const a of [probs, sortedIdx, sortedProbs, cum, zerosIdx, ar, arB, inverse, cumOrig, threshold, keep, ninf])
|
|
38
|
+
a.dispose();
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** apply_top_k: all but the k highest logprobs → -inf. */
|
|
43
|
+
export function applyTopK(lp: MlxArray, topK: number): MlxArray {
|
|
44
|
+
const V = lp.shape[lp.shape.length - 1]!;
|
|
45
|
+
const negLp = ops.neg(lp);
|
|
46
|
+
const part = ops.argpartitionAxis(negLp, topK - 1, -1);
|
|
47
|
+
const maskIdx = part.slice([0, topK], [1, V]);
|
|
48
|
+
const ninf = negInfLike(lp);
|
|
49
|
+
const out = ops.putAlongAxis(lp, maskIdx, ninf, -1);
|
|
50
|
+
for (const a of [negLp, part, maskIdx, ninf]) a.dispose();
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** apply_min_p: mask tokens whose probability is below minP · p(top token).
|
|
55
|
+
* Faithful to the reference (mlx-lm 0.31.3 sample_utils.apply_min_p): the
|
|
56
|
+
* threshold is computed in log space directly against the max logprob —
|
|
57
|
+
* `scaled_min_p = max(lp) + log(minP)` — no full sort; min_tokens_to_keep
|
|
58
|
+
* survivors are recovered with an argpartition on the top tail. */
|
|
59
|
+
export function applyMinP(lp: MlxArray, minP: number, minTokensToKeep = 1): MlxArray {
|
|
60
|
+
if (!(minP >= 0 && minP <= 1))
|
|
61
|
+
throw new Error(`minP has to be a float in the [0, 1] interval, but is ${minP}`);
|
|
62
|
+
if (!Number.isInteger(minTokensToKeep) || minTokensToKeep < 1)
|
|
63
|
+
throw new Error(`minTokensToKeep has to be a positive integer, but is ${minTokensToKeep}`);
|
|
64
|
+
|
|
65
|
+
const owned: MlxArray[] = [];
|
|
66
|
+
const k = <T extends MlxArray>(a: T): T => { owned.push(a); return a; };
|
|
67
|
+
|
|
68
|
+
// Mask tokens that have a probability less than max(p) * min_p
|
|
69
|
+
const top = k(ops.maxAxis(lp, -1, true));
|
|
70
|
+
const scaledMinP = k(ops.add(top, k(ops.scalarLike(Math.log(minP), top))));
|
|
71
|
+
let remove = k(ops.less(lp, scaledMinP));
|
|
72
|
+
|
|
73
|
+
// Ensure at least min_tokens_to_keep survive the filter
|
|
74
|
+
if (minTokensToKeep > 1) {
|
|
75
|
+
const V = lp.shape[lp.shape.length - 1]!;
|
|
76
|
+
// mx.argpartition(lp, kth=-min_tokens_to_keep): negative kth ≡ V - k
|
|
77
|
+
const part = k(ops.argpartitionAxis(lp, V - minTokensToKeep, -1));
|
|
78
|
+
const topIdx = k(part.slice([0, V - minTokensToKeep], [1, V]));
|
|
79
|
+
const falses = k(ops.zeros([1, minTokensToKeep], Dtype.bool));
|
|
80
|
+
remove = k(ops.putAlongAxis(remove, topIdx, falses, -1));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ninf = k(negInfLike(lp));
|
|
84
|
+
const out = ops.where(remove, ninf, lp);
|
|
85
|
+
for (const a of owned) a.dispose();
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** apply_xtc: with probability xtcProbability, remove every token whose
|
|
90
|
+
* probability exceeds the minimum above-threshold probability — i.e. drop
|
|
91
|
+
* the top choices, keeping the least likely token that still clears the
|
|
92
|
+
* threshold (plus everything below it). Special tokens are never removed.
|
|
93
|
+
* `key` seeds the per-step uniform draw (the reference uses the global RNG). */
|
|
94
|
+
export function applyXtc(
|
|
95
|
+
logits: MlxArray,
|
|
96
|
+
xtcProbability: number,
|
|
97
|
+
xtcThreshold: number,
|
|
98
|
+
xtcSpecialTokens: number[],
|
|
99
|
+
key: MlxArray | null,
|
|
100
|
+
): MlxArray {
|
|
101
|
+
if (!(xtcThreshold >= 0 && xtcThreshold <= 0.5))
|
|
102
|
+
throw new Error(`xtcThreshold has to be a float in the [0, 0.5] interval, but is ${xtcThreshold}`);
|
|
103
|
+
if (!(xtcProbability >= 0 && xtcProbability <= 1))
|
|
104
|
+
throw new Error(`xtcProbability has to be a float in the [0, 1] interval, but is ${xtcProbability}`);
|
|
105
|
+
|
|
106
|
+
const owned: MlxArray[] = [];
|
|
107
|
+
const k = <T extends MlxArray>(a: T): T => { owned.push(a); return a; };
|
|
108
|
+
|
|
109
|
+
const probs = k(ops.softmaxAxis(logits, -1, false));
|
|
110
|
+
// mask = probs > min(where(probs > threshold, probs, inf))
|
|
111
|
+
const thresh = k(ops.scalarLike(xtcThreshold, probs));
|
|
112
|
+
const above = k(ops.less(thresh, probs)); // probs > threshold
|
|
113
|
+
const inf = k(ops.scalarLike(Infinity, probs));
|
|
114
|
+
const aboveProbs = k(ops.where(above, probs, inf));
|
|
115
|
+
// min(x) = -max(-x) (no min reduction in the ops layer)
|
|
116
|
+
const minAbove = k(ops.neg(k(ops.maxAxis(k(ops.neg(aboveProbs)), -1, true))));
|
|
117
|
+
let mask = k(ops.less(minAbove, probs)); // probs > min above-threshold prob
|
|
118
|
+
if (xtcSpecialTokens.length > 0) {
|
|
119
|
+
const n = xtcSpecialTokens.length;
|
|
120
|
+
const idx = k(ops.fromInt32(xtcSpecialTokens, [1, n]));
|
|
121
|
+
const falses = k(ops.zeros([1, n], Dtype.bool));
|
|
122
|
+
mask = k(ops.putAlongAxis(mask, idx, falses, -1));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// where(uniform() > xtcProbability, logits, where(mask, -inf, logits))
|
|
126
|
+
const u = k(ops.randomUniform([1], Dtype.float32, 0, 1, key));
|
|
127
|
+
const pArr = k(ops.scalarLike(xtcProbability, u));
|
|
128
|
+
const skip = k(ops.less(pArr, u)); // u > xtcProbability → leave logits alone
|
|
129
|
+
const ninf = k(negInfLike(logits));
|
|
130
|
+
const culled = k(ops.where(mask, ninf, logits));
|
|
131
|
+
const out = ops.where(skip, logits, culled);
|
|
132
|
+
for (const a of owned) a.dispose();
|
|
133
|
+
return out;
|
|
134
|
+
}
|