@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
package/README.md
CHANGED
|
@@ -1,3 +1,1476 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mlx-bun/inference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Composable inference components built on [`@mlx-bun/mlx`](../mlx/README.md).
|
|
4
|
+
Callers choose artifacts and graphs, own state, and invoke inference directly.
|
|
5
|
+
The package includes the kernels, the layers that bind them, the entire graphs
|
|
6
|
+
selected by the loader for each composition, the cache legos, input processing,
|
|
7
|
+
sampling, generation, embeddings, transcription, and optional execution.
|
|
8
|
+
|
|
9
|
+
## Contracts and composition
|
|
10
|
+
|
|
11
|
+
The root API composes the lower layers for loading and generation. Component
|
|
12
|
+
subpaths remain available for direct use and custom graphs. See the
|
|
13
|
+
[architecture](../../ARCHITECTURE.md) for dependency direction and ownership.
|
|
14
|
+
|
|
15
|
+
`contracts/portable` contains platform-free inference interfaces and can be consumed
|
|
16
|
+
without Bun or MLX types. `contracts/mlx` describes tensor and state interactions
|
|
17
|
+
using MLX types. `contracts` exports both. Use the portable entry when sharing
|
|
18
|
+
inference output or scheduling types with application and browser code.
|
|
19
|
+
Application protocols and job contracts migrate with their owning apps.
|
|
20
|
+
|
|
21
|
+
Paged attention accepts numerical storage; adapter mounting accepts named LoRA
|
|
22
|
+
targets; prompt preparation accepts encoder interfaces. Callers can supply their
|
|
23
|
+
own implementations without subclassing a concrete model or cache.
|
|
24
|
+
|
|
25
|
+
Each graph declares its construction facts (`GraphDeclaration`, portable; a model
|
|
26
|
+
exposes it as `graphCapabilities`): its method, media
|
|
27
|
+
input, compiled decode, sparse attention, hidden-layer taps for
|
|
28
|
+
drafts, a draft head its checkpoint carries (`nativeDraft`), whether long prompts prefill in workspace-bounded
|
|
29
|
+
chunks, and what its verifier qualifies (adapters, logprobs, external tokens,
|
|
30
|
+
grammar proposals). None names a cache format: the composition's caches serve
|
|
31
|
+
every method, and the planner never reads how they store keys and values.
|
|
32
|
+
Pooled embedding support is the presence of `embedPooled` itself; graphs supply no
|
|
33
|
+
separate `embeddings` flag. `graphCapabilitiesOf` derives that boolean in the
|
|
34
|
+
portable `GraphCapabilities` description consumed by the gateway. `isEmbeddingModel`
|
|
35
|
+
narrows the graph to its required pooling operation, and the existing
|
|
36
|
+
`embedOne`/`embedMany` entry points consume it directly. The architecture ratchet
|
|
37
|
+
rejects independent pooling flags and reads of the retired declaration field.
|
|
38
|
+
`MlxDeclaredGraph` names the operations behind the other promises (`embed`, `bindMediaInput`,
|
|
39
|
+
`mediaEncoders`, `pixelInput`, `draftTarget`, `nativeDraftHead`, `trainable`,
|
|
40
|
+
`compiledDecodeStep` and `releaseCompiledDecode`, `expertResidency`) and the facts its
|
|
41
|
+
composition declares (`verifyRoundCosts`, `knownSpans`, `mixed`), and
|
|
42
|
+
`declaredGraph` in `models/capabilities` checks the pairing once when composition
|
|
43
|
+
binds the graph: an undeclared graph, malformed pooling operation, or media
|
|
44
|
+
declaration without its operation is refused. The gateway plans from the
|
|
45
|
+
declarations, the request and dynamic state
|
|
46
|
+
(batch membership, cache contents, cancellation), and
|
|
47
|
+
`MlxGatewayBinding.capabilities` reports what it resolved. Supporting a new graph
|
|
48
|
+
means declaring and implementing; `execution/` and the app engine need no edit.
|
|
49
|
+
The batch group and the autoregressive binding take the structural `MlxTokenGraph`
|
|
50
|
+
(`models/graph`: config, caches, the named phases, and the declared ports), not
|
|
51
|
+
the registry's `RuntimeModel` union. A graph's compiled decode step lives with that
|
|
52
|
+
graph (`models/gemma4/compiled-decode`) and reaches them through `compiledDecodeStep`;
|
|
53
|
+
it drives caches through the `CompiledDecodeCache` protocol (`contracts/mlx/cache`,
|
|
54
|
+
implemented by the KV caches in `state/`) and names no cache class. Every cache
|
|
55
|
+
lego has that form except direct pages the kernel reads, which declare none
|
|
56
|
+
(`pagedDecodeCompiles` in `state/paged/cache`). The autoregressive binding resolves
|
|
57
|
+
replay eligibility from the paging and KV configuration once; requests with active
|
|
58
|
+
adapters use the ordinary declared operations.
|
|
59
|
+
The architecture gate rejects concrete model imports, model-class `instanceof`,
|
|
60
|
+
model-type and `architectures` checks, family predicates and model-scoped flags there,
|
|
61
|
+
in the app's engine, server and CLI, and in `@mlx-bun/training`. What those need to
|
|
62
|
+
know about a model is declared in its family record and read through its profile
|
|
63
|
+
(`models/profile`: `embeddingDeclarationFor`, `sentinelDeclarationFor`,
|
|
64
|
+
`mediaTokenDeclarationFor`, `generationDefaultsFor`, `chatTemplateFallbackFor`,
|
|
65
|
+
`trainingDefaultsFor`) or returned by its opened runtime (`models/runtime`,
|
|
66
|
+
`models/memory-plan`); token text resolves to
|
|
67
|
+
ids against the tokenizer in use (`input/special-tokens`). One record per model
|
|
68
|
+
family (`models/family`, listed in `models/families`) holds the family's `accepts`
|
|
69
|
+
rule, its graph id (an open string), tier, loader, loop, fidelity, engine
|
|
70
|
+
capabilities, roles and those declarations; support tiers, profiles, engine
|
|
71
|
+
capabilities, implementation loader/loop and fit's expert count derive from the list.
|
|
72
|
+
A new model is a record in `<family>/family.ts`, an entry in `MODEL_FAMILIES` and its
|
|
73
|
+
native implementation in `models/factory`; the records import no graph class, so
|
|
74
|
+
resolving a profile never loads MLX. The gate keeps model-type comparisons and family
|
|
75
|
+
predicates inside the registry, the family directories and the artifact readers
|
|
76
|
+
(`artifacts/config-dialects`, which spell how a `model_type`'s `config.json` is laid
|
|
77
|
+
out). `trainable`
|
|
78
|
+
(`TrainableGraph`) declares what the trainer consumes: the quantized
|
|
79
|
+
`lmHead` for fused linear-CE heads, `segmented` backward (how a range of layers
|
|
80
|
+
runs and which reused K/V crosses segment boundaries), `prefixShared` forwards,
|
|
81
|
+
`gradCheckpoint`, the `flashAttention` constraint, and the `denoising` objective.
|
|
82
|
+
The trainer names the declaration a graph lacks. The loader resolves module configuration once
|
|
83
|
+
(`contracts/portable/composition`, `models/composition`) from the caller's request
|
|
84
|
+
and the machine: KV scheme and paging, scheduler row cap and prompt chunk size,
|
|
85
|
+
draft method and depth, sampling and request policy. The file names its kernel
|
|
86
|
+
and compilation choices. Alternative layer forms remain importable for callers
|
|
87
|
+
writing their own graphs. Each request names its adapter file; its trained delta
|
|
88
|
+
adds to the outputs of the projections it targets, under that request's scheduler
|
|
89
|
+
context, independently of the file's projection kernel choices.
|
|
90
|
+
|
|
91
|
+
## Numerical and execution policy
|
|
92
|
+
|
|
93
|
+
The inherited fidelity policy is L1 by default: bit-exact numerics against the
|
|
94
|
+
pinned mlx-lm oracle for matching artifacts, inputs, and settings. L2 covers
|
|
95
|
+
schemes with a different oracle, including mlx-optiq mixed-KV, and requires
|
|
96
|
+
bit-exact comparison to that scheme's reference. Lab paths without an oracle
|
|
97
|
+
need numerical/quality evaluation and a paired A/B before becoming defaults.
|
|
98
|
+
These are correctness obligations; the outstanding real-weight verification is
|
|
99
|
+
tracked in the [refactor plan](../../PLAN.md#verify-the-migrated-library).
|
|
100
|
+
They do not make application sampling or serving defaults identical to mlx-lm.
|
|
101
|
+
|
|
102
|
+
Execution policy treats default memory estimates as advisory. Attempt the
|
|
103
|
+
requested work without rejecting it or clamping output tokens solely because
|
|
104
|
+
of a predicted memory estimate. Explicit user limits, queue capacity, and actual
|
|
105
|
+
layout requirements still apply; release owned resources on failure. Application
|
|
106
|
+
composition must preserve this policy when binding the optional execution layer.
|
|
107
|
+
|
|
108
|
+
### External parity evidence
|
|
109
|
+
|
|
110
|
+
On 2026-09-24, inference at `5527989` and main at `02d723a` independently matched
|
|
111
|
+
the external mlx-lm reference for MiniCPM5-1B-OptiQ-4bit, snapshot
|
|
112
|
+
`664aabaed233c653f82716d8dc822234d0091f78`. On an Apple M1 Max with 32 GiB RAM,
|
|
113
|
+
macOS 27.0 (26A428), and Bun 1.4.2, all 100 greedy tokens and all 13,056,000
|
|
114
|
+
float32 logits were byte-identical; all logits were finite. The prompt was
|
|
115
|
+
`The capital of France is` (six matching tokenizer IDs), batch one, default
|
|
116
|
+
unquantized KV, with no `MLX_BUN_*` overrides and no EOS early stopping.
|
|
117
|
+
|
|
118
|
+
The external environment used Python 3.13.5, MLX/MLX-Metal 0.32.2 and mlx-lm
|
|
119
|
+
0.31.3. Reference generation used the unchanged Python body from
|
|
120
|
+
`02d723a:scripts/regen/minicpm5.ts`; model-file and per-step reference hashes
|
|
121
|
+
were checked. The reference manifest SHA-256 is
|
|
122
|
+
`e20d64193328d5dfe1c4b9681651730b35b2eeb2f5152b0ae3d8fb50b05dfd5f`.
|
|
123
|
+
The [MiniCPM5 parity test](tests/parity/minicpm5-parity.test.ts) is that harness:
|
|
124
|
+
public root, model, and scoring imports, forwarding the prompt once and each
|
|
125
|
+
selected token thereafter with the same live cache (plain KV from the model).
|
|
126
|
+
It requires byte-identical finite logits and the greedy token at all 100 steps.
|
|
127
|
+
Opt in with all of `MLX_BUN_TEST_MINICPM5_MODEL`, `MLX_BUN_TEST_MINICPM5_REFERENCE`
|
|
128
|
+
(the directory holding `minicpm5-parity.json` and the 100 step blobs) and
|
|
129
|
+
`MLX_BUN_TEST_MINICPM5_REFERENCE_SHA256`; absent all three it skips, any other
|
|
130
|
+
combination fails. Before native libraries load it verifies the manifest hash, the
|
|
131
|
+
pinned model files and shards by content (relocated copies pass) and every blob;
|
|
132
|
+
the runtime must match the reference's MLX version and GPU architecture.
|
|
133
|
+
This covers that path only, not other models, batching, quantized KV,
|
|
134
|
+
snapshot restore, long contexts, or speed. Raw outputs and the reference
|
|
135
|
+
remain external; Python is not a project dependency.
|
|
136
|
+
|
|
137
|
+
The [Gemma4 parity test](tests/parity/gemma4-parity.test.ts) restores main's
|
|
138
|
+
Gemma4 12B OptiQ-4bit oracle consumer on the same terms (batch one, plain KV
|
|
139
|
+
from the model's `makeCache`, the artifact's `kv_config.json` not applied). Its
|
|
140
|
+
reference is main's `scripts/regen/parity.ts` output: the prompt
|
|
141
|
+
`The capital of France is`, 100 greedy IDs and the first four full-vocabulary
|
|
142
|
+
float32 vectors. Through `createModel` it checks the 100-token greedy trajectory
|
|
143
|
+
and those four vectors byte for byte; a separate 12-step control does the same
|
|
144
|
+
through `new Gemma4Model`. Every step must be finite and select the reference
|
|
145
|
+
token. This is not 100-step full-vector parity. The loader's Gemma 4 table
|
|
146
|
+
builds the written 12B graph only for the `kv_config` composition it serves, so
|
|
147
|
+
for plain KV (the default composition) `createModel` builds `Gemma4Model` too,
|
|
148
|
+
and the test asserts that. Opt in with all of
|
|
149
|
+
`MLX_BUN_TEST_GEMMA4_MODEL`, `MLX_BUN_TEST_GEMMA4_REFERENCE` (the directory
|
|
150
|
+
holding `gemma4-parity.json`, the producer's unchanged `parity.json` and the four
|
|
151
|
+
step blobs) and `MLX_BUN_TEST_GEMMA4_REFERENCE_SHA256`. Before native libraries
|
|
152
|
+
load it verifies the manifest hash; the producer provenance (script hash, main
|
|
153
|
+
revision, installed mlx, mlx-lm and mlx-optiq source hashes and versions, and the
|
|
154
|
+
pinned `parity.json`, which must agree with the manifest); the tokenizer, config,
|
|
155
|
+
index and exactly its shards by content; and every blob. The runtime must match
|
|
156
|
+
the reference's MLX version and GPU architecture. Main's retained capture
|
|
157
|
+
(`02d723a:goldens/apple-m1-max/parity.json`) has no tokenizer, index, shard or
|
|
158
|
+
producer-source pins, so it cannot qualify. The reviewed run in
|
|
159
|
+
[#202](https://github.com/joshuarossi/mlx-bun/pull/202) used a fresh, fully pinned
|
|
160
|
+
capture from main `02d723a` and passed both consumer paths at `e7288425` on an
|
|
161
|
+
Apple M1 Max (MLX 0.32.2, mlx-lm 0.31.3), with input pins unchanged. This qualifies
|
|
162
|
+
the plain-KV scope above; the written graph's unrolled `kv_config` path and
|
|
163
|
+
performance remain separate checks.
|
|
164
|
+
|
|
165
|
+
The [written Gemma graph test](tests/parity/gemma4-written.test.ts) is that
|
|
166
|
+
unrolled-path check, ported from `02d723a:tests/parity/generated-parity.test.ts`
|
|
167
|
+
for each registered fingerprint (12B, e4b, 26B-A4B). With
|
|
168
|
+
`MLX_BUN_TEST_GEMMA4_GRAPH_MODEL` naming an artifact that ships `kv_config.json`, it
|
|
169
|
+
requires the `kv_config` composition (`--kv-quant config`) to select the written
|
|
170
|
+
graph and the default composition `Gemma4Model`; byte-identical vectors against
|
|
171
|
+
`Gemma4Model` over caches converted to `kv_config` before a prompt past the
|
|
172
|
+
sliding window; identical 24-token greedy trajectories under the `kv_config`
|
|
173
|
+
composition, each graph reading its own composed caches (stop tokens off, so
|
|
174
|
+
every trajectory is full length), uncompiled, where from the second forward on
|
|
175
|
+
every listed cache answers from its quantized storage, and with compiled decode;
|
|
176
|
+
and identical vectors over plain caches. This is identity between the written
|
|
177
|
+
graph and the family's generic graph within this tree, not an oracle claim. On 2026-09-28 it
|
|
178
|
+
passed on the M1 Max for mlx-community gemma-4-e4b-it-OptiQ-4bit (`98d7dc6a`; 24
|
|
179
|
+
unrolled forwards uncompiled, 2 compiled) and gemma-4-12B-it-OptiQ-4bit
|
|
180
|
+
(`5b110106`; 24 uncompiled, 1 compiled). The 26B-A4B cell was not run. A final
|
|
181
|
+
cell runs two different rows through one forward over `kv_config` caches and
|
|
182
|
+
requires bytes equal to the generic graph's; it caught the committed e4b graph slicing
|
|
183
|
+
its per-layer inputs for one row, and passes on e4b and 12B after regeneration
|
|
184
|
+
(2026-09-29, M1 Max).
|
|
185
|
+
|
|
186
|
+
The three Gemma files are written directly under `models/gemma4/`. Each names
|
|
187
|
+
its sliding/full attention, KV donors, per-layer inputs, activation and expert
|
|
188
|
+
operations in order. Changes to one file are checked by that file's numerical
|
|
189
|
+
gates. A shared block or kernel change reaches the files that import it.
|
|
190
|
+
|
|
191
|
+
The identity tests compare each specialized file with its family's generic graph
|
|
192
|
+
at maxDiff 0 where both serve the same operation. Real-weight parity suites and
|
|
193
|
+
app-path digests (`scripts/digest.ts`) hold the served numbers; recorded synthetic
|
|
194
|
+
digests (`scripts/synthetic-digests.ts`) cover cases without an artifact. There is
|
|
195
|
+
no source generator, parallel input description or generator drift test.
|
|
196
|
+
|
|
197
|
+
On 2026-09-27 UTC, the rotating live-window correction (source `5ec1f4ae`) was
|
|
198
|
+
checked on the same M1 Max (MLX 0.32.2, pinned native library) against selections
|
|
199
|
+
made independently of `temporalView`, main `02d723a`, and pinned optiq 0.2.7.
|
|
200
|
+
Gemma4 e2b and a custom graph over unchanged Llama-3.2-3B weights (alternating
|
|
201
|
+
sliding layers, window 8; not a published model) matched main's B1 logits byte for
|
|
202
|
+
byte in 13 cases. In 14 late-join and initial two-row cases, at both tail-split
|
|
203
|
+
settings, every projection, token and valid state equaled the same batching code
|
|
204
|
+
reading rows through the independent selection, and merged rows equaled their own
|
|
205
|
+
B1 state; before the correction, late joins without a tail split and initial
|
|
206
|
+
two-row batches merged the oldest window. Gemma4 e4b donor attention (plain and
|
|
207
|
+
affine) and the deterministic assistant-drafter chain
|
|
208
|
+
(`gemma-4-E4B-it-assistant-bf16` `844e008e`) equaled their independent references
|
|
209
|
+
byte for byte through prefill, a verify block, a rollback and a decode; below the
|
|
210
|
+
window the chain equaled main's. For DiffusionGemma 26B-A4B with a 1,500-token
|
|
211
|
+
prompt, the decoder-selected encoder K/V of all 30 layers and the full first-pass
|
|
212
|
+
logits equaled the pinned optiq reference byte for byte, and the opt-in real-weight
|
|
213
|
+
test passed with measured main references. This covers these paths only, not
|
|
214
|
+
stochastic speculative verification, other models, performance or full-candidate
|
|
215
|
+
qualification. Raw evidence remains external.
|
|
216
|
+
|
|
217
|
+
Three opt-in consumers repeat these checks. The
|
|
218
|
+
[rotating-join test](tests/execution/rotating-join.test.ts) takes
|
|
219
|
+
`MLX_BUN_TEST_ROTATING_JOIN_MODEL` (a Gemma4 artifact with sliding layers) and, for a
|
|
220
|
+
custom graph over a Llama-family artifact's unchanged weights,
|
|
221
|
+
`MLX_BUN_TEST_ROTATING_JOIN_WINDOW`; it runs late joins and two-row preparation with
|
|
222
|
+
the prompt tail split off and on, each against the same run reading rows through an
|
|
223
|
+
independent newest-window selection, and compares merged rows with their solo state.
|
|
224
|
+
The [Gemma4 assistant test](tests/parity/gemma4-assistant.test.ts) takes
|
|
225
|
+
`MLX_BUN_TEST_ASSISTANT_TARGET` and `MLX_BUN_TEST_ASSISTANT_DRAFT` and compares donor
|
|
226
|
+
attention and deterministic draft chains with independently selected donors past the
|
|
227
|
+
window. Its separate generation consumer uses the real assistant provider at B1,
|
|
228
|
+
depth 2, with plain KV below the sliding window, and compares greedy output with
|
|
229
|
+
ordinary generation. It checks complete forward blocks and retained K/V against independent direct-graph replay, including
|
|
230
|
+
acceptance, rejection and continuation; this is not main or external-oracle parity.
|
|
231
|
+
On 2026-09-27 UTC, this consumer passed at `9be1c241` on an M1 Max with 32 GiB
|
|
232
|
+
(macOS 27.0, Bun 1.4.2), using e4b `98d7dc6a` and assistant `844e008e`. Its 67
|
|
233
|
+
output tokens matched ordinary generation; 44 rounds included 24 accepted
|
|
234
|
+
proposals and 39 genuine target-disagreement rounds. All 45 forward blocks and
|
|
235
|
+
the retained state matched the direct replay byte for byte. This does not qualify
|
|
236
|
+
B>1, persistence or performance. Captures and their content pins remain external.
|
|
237
|
+
The [DiffusionGemma window test](tests/parity/diffusion-gemma-window.test.ts)
|
|
238
|
+
takes `MLX_BUN_TEST_DIFFUSION_WINDOW_MODEL`, `MLX_BUN_TEST_DIFFUSION_WINDOW_REFERENCE`
|
|
239
|
+
(a directory holding `manifest.json` and raw tensors, produced outside this
|
|
240
|
+
repository) and `MLX_BUN_TEST_DIFFUSION_WINDOW_REFERENCE_SHA256`; the manifest pins
|
|
241
|
+
the inputs, artifact files, producing runtime and every tensor. The artifact, inputs,
|
|
242
|
+
geometry and reference tensors are verified before native libraries load; the
|
|
243
|
+
producing runtime (MLX version, GPU architecture) is checked right after the native
|
|
244
|
+
import, and each produced tensor's shape and dtype before its bytes. Each of these
|
|
245
|
+
tests skips only when none of its settings is present.
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
### Speech and embedding parity
|
|
249
|
+
|
|
250
|
+
Three opt-in consumers, ported from main `02d723a`, compare Whisper, Silero VAD and
|
|
251
|
+
Qwen3-Embedding with real weights against both the external oracle and main.
|
|
252
|
+
They skip only when none of their settings is present; any other combination
|
|
253
|
+
fails, and every reference and model pin is verified before native libraries load.
|
|
254
|
+
The oracle producers are main's unchanged scripts, run outside this repository in
|
|
255
|
+
their own environments: `scripts/oracle/gen-whisper-golden.py` and
|
|
256
|
+
`gen-silero-golden.py` (mlx-whisper 0.4.3 and silero-vad 6.2.1, MLX 0.32.2, in
|
|
257
|
+
`~/Code/mlx-whisper-oracle/.venv`) and `gen-qwen3-embed-golden.py` (mlx-lm 0.31.3,
|
|
258
|
+
MLX 0.32.2, `~/Code/mlx-lm/.venv-mlx-0.32.2-macos14`). The main side is
|
|
259
|
+
[`scripts/main-speech-reference.ts`](scripts/main-speech-reference.ts), run from a
|
|
260
|
+
main checkout (`whisper`, `vad` and `embed` commands; see its header): it imports
|
|
261
|
+
main's own modules by path, changes nothing there, and writes JSON outside this
|
|
262
|
+
repository. Nothing produced is committed.
|
|
263
|
+
|
|
264
|
+
- [Whisper](tests/parity/whisper-parity.test.ts): `MLX_BUN_TEST_WHISPER_MODEL`
|
|
265
|
+
(the mlx-community snapshot), `_AUDIO` (speech-fox.wav and jfk.wav), `_REFERENCE`
|
|
266
|
+
(the oracle's `whisper.json` and blobs plus `main-whisper.json`),
|
|
267
|
+
`_REFERENCE_SHA256` and `_MAIN_SHA256`. Against the oracle: mel and window-0
|
|
268
|
+
encoder output bit for bit, window-0 per-step logits bit for bit on the faithful
|
|
269
|
+
graph, identical transcripts, segment tokens, timestamps and seeks. Against main:
|
|
270
|
+
whole results and encoder hashes identical for the faithful and the fast path,
|
|
271
|
+
greedy and beam 5, word timestamps and a run fed one second at a time. The fast
|
|
272
|
+
path is held to the oracle within two tokens per clip, the bound main recorded.
|
|
273
|
+
The openai/whisper-large-v3-turbo tokenizer must be in the Hugging Face cache.
|
|
274
|
+
- [Silero VAD](tests/parity/silero-vad-parity.test.ts): `MLX_BUN_TEST_VAD_MODEL`
|
|
275
|
+
(`ggml-silero-v6.2.0.bin`), `_AUDIO` (adds chirp-1s6.wav), `_REFERENCE`
|
|
276
|
+
(`silero-vad.json` and `main-vad.json`), `_REFERENCE_SHA256`, `_MAIN_SHA256`.
|
|
277
|
+
Probabilities against the torch reference within 5e-3 (the ggml weights are fp16)
|
|
278
|
+
and identical segments under both parameter sets; against main, probabilities,
|
|
279
|
+
100 ms-streamed probabilities and segments bit for bit. The oracle's noise clip
|
|
280
|
+
uses numpy's generator and is not compared.
|
|
281
|
+
- [Qwen3-Embedding](tests/parity/embedding-parity.test.ts): `MLX_BUN_TEST_EMBED_MODEL`,
|
|
282
|
+
`_REFERENCE` (`meta.json`, `hidden.bin`, `pooled.bin`, `main-embed.json`),
|
|
283
|
+
`_REFERENCE_SHA256`, `_MAIN_SHA256`. Hidden states and the pooled vector for the
|
|
284
|
+
oracle's ids bit for bit, the tokenizer's ids equal to the oracle's, and `embedMany`
|
|
285
|
+
vectors and token counts bit for bit with main for four texts, raw and with a
|
|
286
|
+
query instruction; it also checks the embed call counter.
|
|
287
|
+
|
|
288
|
+
The app repeats the served surfaces with the same references.
|
|
289
|
+
[`transcription-parity.test.ts`](../../apps/mlx-bun/tests/engine/transcription-parity.test.ts)
|
|
290
|
+
(`MLX_BUN_TEST_NATIVE=1`, `MLX_BUN_APP_TEST_WHISPER_MODEL`, `MLX_BUN_TEST_WHISPER_AUDIO`,
|
|
291
|
+
`_REFERENCE`, `_REFERENCE_SHA256`) requires the transcription-only server's
|
|
292
|
+
transcripts to equal the oracle's for the same audio across multipart and JSON
|
|
293
|
+
requests, beam search with vocabulary, SRT, translation, event streaming, VAD-gated
|
|
294
|
+
sessions and the error path. [`embeddings-parity.test.ts`](../../apps/mlx-bun/tests/engine/embeddings-parity.test.ts)
|
|
295
|
+
(`MLX_BUN_APP_TEST_EMBED_MODEL` and the embedding references) requires `/v1/embeddings`
|
|
296
|
+
to return the oracle's and main's vectors bit for bit. The
|
|
297
|
+
[voice test](../../apps/mlx-bun/tests/engine/voice.test.ts) covers the CLIs and the
|
|
298
|
+
chat-server companion on synthesized speech at word level.
|
|
299
|
+
|
|
300
|
+
On 2026-09-29 all of these passed on an Apple M1 Max with 32 GiB, macOS 27.0.1
|
|
301
|
+
(26A434), Bun 1.4.2, native MLX 0.32.2, with this tree on `e878baaa` plus the tests
|
|
302
|
+
above and main at `02d723a`. Whisper: 36 of 36 (7 oracle cases, 21 main runs,
|
|
303
|
+
3 clips), including every mel, encoder output and per-step logit vector bit for bit
|
|
304
|
+
with the oracle; the fast path differed from the oracle by 0 tokens except 2 on
|
|
305
|
+
`long-noprev-en` (the recorded bound), and fast and faithful beam 5 agreed exactly.
|
|
306
|
+
VAD: 4 of 4 clips; the largest difference from torch was 2.8e-3 (jfk) and main matched
|
|
307
|
+
bit for bit. Embedding: 4 of 4; 35,840 hidden values and 2,560 pooled values identical
|
|
308
|
+
to mlx-lm, and main's vectors identical. App: 6 of 6 transcription and 3 of 3 embedding
|
|
309
|
+
route tests. Artifacts: whisper-large-v3-turbo `a4aaeec0` (weights SHA-256
|
|
310
|
+
`951ed3fc1203e6a62467abb2144a96ce7eafca8fa77e3704fdb8635ff3e7f8a6`), tokenizer from
|
|
311
|
+
openai/whisper-large-v3-turbo `41f01f3f`, Silero `ggml-silero-v6.2.0.bin` (SHA-256
|
|
312
|
+
`2aa269b785eeb53a82983a20501ddf7c1d9c48e33ab63a41391ac6c9f7fb6987`),
|
|
313
|
+
Qwen3-Embedding-4B-4bit-DWQ `b5d88f1f` (weights SHA-256
|
|
314
|
+
`cd373d0bc76a3464a69b036ac6beca217a0d668bdf901ff3b05a7d7997d2d607`). Reference
|
|
315
|
+
SHA-256s: whisper.json `348fb1ff2097f6b9b57b236293a67fd6da7c5eedaa886f793866eaba4a0aa965`,
|
|
316
|
+
main-whisper.json `e307ac92f636ac03a4ae95c39b8a1ae5d1cb09c6b42644a79979205281037a67`,
|
|
317
|
+
silero-vad.json `e961872b71f458894658f2f43c2eab6112c4517edaa9f49f5ff8fbc500460a51`,
|
|
318
|
+
main-vad.json `f9194f6d70c030f59f95e049b39e019c658e6486e75c274e79a96c830650a38d`,
|
|
319
|
+
meta.json `cb63915245d57da88f7df0ac088fd19ac249bcebd1829e4b25840163cf9059b3`,
|
|
320
|
+
main-embed.json `85e13387bda46cf9c2aff8181b8dc575cce926a5bf90ff5293211287412f08a1`.
|
|
321
|
+
Inputs were main's `speech-fox.wav` and `chirp-1s6.wav` fixtures and whisper.cpp's
|
|
322
|
+
`jfk.wav`; the mel comparison confirms the same samples. A deliberate change of the
|
|
323
|
+
attention scale made 13 Whisper tests fail, so the comparisons discriminate.
|
|
324
|
+
This covers these checkpoints and clips only: no other Whisper size, no non-WAV
|
|
325
|
+
input (AudioToolbox decoding), no oracle for word timestamps or the streaming run
|
|
326
|
+
(main only), no Whisper batching, and no performance. Raw references remain external.
|
|
327
|
+
|
|
328
|
+
### Packed Qwen Trellis parity
|
|
329
|
+
|
|
330
|
+
[`tests/parity/qwen-trellis-parity.test.ts`](tests/parity/qwen-trellis-parity.test.ts)
|
|
331
|
+
compares the 11.99 GB `qwen38-trellis-global-exit5-h39-q4b-v2` and its folded
|
|
332
|
+
MTP companion with pre-refactor main. Stock mlx-lm cannot read this packed
|
|
333
|
+
artifact. Produce the reference with
|
|
334
|
+
[`scripts/main-qwen-trellis-reference.ts`](scripts/main-qwen-trellis-reference.ts),
|
|
335
|
+
passing `--main <main-checkout> --target <snapshot> --draft <companion> --out
|
|
336
|
+
<external-reference.json>` and main's native library through `MLX_BUN_LIBMLXC`.
|
|
337
|
+
The producer does not modify main. Its reference pins model metadata and every
|
|
338
|
+
weight shard by content, the main revision, MLX version and GPU architecture.
|
|
339
|
+
Set all of `MLX_BUN_TEST_QWEN_TRELLIS_TARGET`,
|
|
340
|
+
`MLX_BUN_TEST_QWEN_TRELLIS_DRAFT`, `MLX_BUN_TEST_QWEN_TRELLIS_REFERENCE` and
|
|
341
|
+
`MLX_BUN_TEST_QWEN_TRELLIS_REFERENCE_SHA256`, then run:
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
bun --no-env-file test packages/inference/tests/parity/qwen-trellis-parity.test.ts
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The schema-2 reference independently records 96 complete last-position float32
|
|
348
|
+
vectors and greedy tokens across three prompts, plus three 64-token native MTP
|
|
349
|
+
runs. Each MTP run records emitted tokens, every target input, speculation
|
|
350
|
+
counters and the full logits at its actual native heads. Separate teacher-forced
|
|
351
|
+
controls record one-row and verify-window vectors, CPU first-maximum IDs, exact
|
|
352
|
+
maximum sets and cache positions before, after and after rollback.
|
|
353
|
+
|
|
354
|
+
The consumer requires equality with those main controls and literal equality
|
|
355
|
+
of native MTP tokens, target inputs, counters and head vectors. Every emitted
|
|
356
|
+
token must belong to the exact maximum set of its actual head. This preserves
|
|
357
|
+
native selection when an exact tie makes the CPU's first maximum a different
|
|
358
|
+
token. Window-versus-one-row KL checks remain separate checks of the existing
|
|
359
|
+
arithmetic; they do not relax equality against main. The producer records its
|
|
360
|
+
source revision and script content hashes, including whether its operational
|
|
361
|
+
checkout was dirty. Produce references from an isolated, pinned main checkout
|
|
362
|
+
on the same device as the candidate and retain the independent capture's hash.
|
|
363
|
+
|
|
364
|
+
The MTP scope is the shared gateway with batch capacity one, depth two, plain
|
|
365
|
+
KV, greedy sampling and stopping tokens disabled. These checks cover the named
|
|
366
|
+
artifact and geometry, not B>1, quantized KV, stochastic sampling, intelligence
|
|
367
|
+
or performance. Refactor run results and their source and artifact pins belong
|
|
368
|
+
in the PR and external run directory.
|
|
369
|
+
|
|
370
|
+
The existing runtime-report consumer also matches full logits, all live cache
|
|
371
|
+
planes and continuation in 18 cases (contexts 0/64/512 × append lengths
|
|
372
|
+
1/3/4/8/16/128, fixed prefix chunk 256, plain KV), including serialized-state
|
|
373
|
+
restoration. Main's report SHA-256 is
|
|
374
|
+
`8bb68cc58d3aad035111b10e48102595a2c00f91c1b640cc7577da19dae71e54`. Main runs this
|
|
375
|
+
comparison with its compiled decode off, and this tree's runtime oracle runs no
|
|
376
|
+
compiled decode; the legacy reference's omitted configuration provenance is accepted
|
|
377
|
+
explicitly after checking its invocation. Captures and logs remain outside Git.
|
|
378
|
+
|
|
379
|
+
### Repeatable runtime comparison
|
|
380
|
+
|
|
381
|
+
The source-checkout tool `bun packages/inference/scripts/runtime-oracle.ts --help`
|
|
382
|
+
explains the plan schema and `emit`/`compare` commands. It hashes complete logits,
|
|
383
|
+
live cache planes and one-token continuation; `compare` is CPU-only. Supply local
|
|
384
|
+
weights and external reference reports. No Python environment or reference data
|
|
385
|
+
is installed by this repository. The opt-in test uses `MLX_BUN_PARITY_PLAN` and
|
|
386
|
+
`MLX_BUN_PARITY_REFERENCE`; it skips only when none of its settings (those two,
|
|
387
|
+
`MLX_BUN_PARITY_TIMEOUT_MS`, `MLX_BUN_PARITY_ALLOW_UNRECORDED_CONFIG`,
|
|
388
|
+
`MLX_BUN_PARITY_REFERENCE_SHA256`) is set, and a partial or blank opt-in fails.
|
|
389
|
+
`MLX_BUN_PARITY_REFERENCE_SHA256` pins a published reference revision: the report's
|
|
390
|
+
bytes must match before the worker starts. Legacy reports require explicit
|
|
391
|
+
`--allow-unrecorded-config` (test: `MLX_BUN_PARITY_ALLOW_UNRECORDED_CONFIG=1`), after
|
|
392
|
+
verifying their environment separately. New reports record runtime overrides,
|
|
393
|
+
source/harness/native hashes, machine and plan; `--hash-weights` adds weight hashes.
|
|
394
|
+
|
|
395
|
+
The same consumer covers Qwen Trellis and Gemma. Packed Qwen Trellis has the
|
|
396
|
+
measured comparison below; Gemma still needs a recorded run with this consumer.
|
|
397
|
+
Produce references outside this repository with the unchanged producers at main
|
|
398
|
+
`02d723a`, sequentially on the comparison machine. Stock mlx-lm architectures
|
|
399
|
+
(Gemma4 e2b/e4b/26B-A4B, Gemma2, Llama, MiniCPM5) use
|
|
400
|
+
`MLX_BUN_TEST_RUNTIME_ORACLE=1 HF_HUB_OFFLINE=1 python scripts/oracle/check-runtime.py plan.json reference.json`
|
|
401
|
+
in the pinned oracle environment; it registers no OptiQ architectures, so the 12B
|
|
402
|
+
`gemma4_unified` artifact keeps its dedicated consumer above. Packed Qwen Trellis
|
|
403
|
+
has no external oracle (mlx-lm cannot load it), so main is the reference:
|
|
404
|
+
`MLX_BUN_TEST_RUNTIME_ORACLE=1 MLX_BUN_COMPILED_DECODE=0 bun --no-env-file tests/support/runtime-oracle-worker.ts plan.json reference.json`
|
|
405
|
+
in the main checkout (main's own switches; this tree reads neither, and its
|
|
406
|
+
runtime oracle runs no compiled decode). Neither producer records provenance or applies `kv`, so
|
|
407
|
+
compare plain-KV plans with the legacy acceptance and pin the report:
|
|
408
|
+
`MLX_BUN_PARITY_PLAN=plan.json MLX_BUN_PARITY_REFERENCE=reference.json MLX_BUN_PARITY_REFERENCE_SHA256=<sha256> MLX_BUN_PARITY_ALLOW_UNRECORDED_CONFIG=1 bun --no-env-file test packages/inference/tests/parity/runtime-oracle.test.ts`.
|
|
409
|
+
Useful plans keep main's IDs and vary geometry: for Trellis, contexts 0/64/512 ×
|
|
410
|
+
lengths 1/3/4/8/16/128 with prefix chunk 256 cover the M≤4, M5–15 and M≥16
|
|
411
|
+
dispatch profiles; for Gemma4 e2b/e4b, a context past the 512-token window (for
|
|
412
|
+
example 0/64/600 × 1/8/128, chunk 128) covers sliding-window wrap; for MiniCPM5
|
|
413
|
+
beyond the recorded cases, a longer multi-chunk context with `restore: true` adds
|
|
414
|
+
persisted-state continuation. Mixed KV (`kv: "artifact"`) has no reference producer
|
|
415
|
+
until the mixed-KV reference contract in [PLAN](../../PLAN.md#verify-the-migrated-library)
|
|
416
|
+
is confirmed.
|
|
417
|
+
|
|
418
|
+
On 2026-09-25 UTC, `6b0fd69` matched main `02d723a` and its unchanged external
|
|
419
|
+
`02d723a:scripts/oracle/check-runtime.py` for the same MiniCPM snapshot above. All nine
|
|
420
|
+
cases (contexts 0/64/320 × append lengths 1/8/128, prefix chunk 128, batch one)
|
|
421
|
+
matched bit-for-bit for full logits, prefix/append state and continuation logits/state.
|
|
422
|
+
The machine was the same M1 Max; Python was 3.14.5, MLX/MLX-Metal 0.32.2,
|
|
423
|
+
mlx-lm 0.31.3. Runtime overrides were only compiled decode off (main's switch,
|
|
424
|
+
which that tree also read) and `MLX_BUN_TEST_RUNTIME_ORACLE=1`; references ran
|
|
425
|
+
sequentially with HF offline.
|
|
426
|
+
The weight SHA-256 was
|
|
427
|
+
`88bb686ed4a28f7c2065e27aabef7669f84961ac47c83efe2d003436c179e2e4`;
|
|
428
|
+
the Python report SHA-256 was
|
|
429
|
+
`bc034fd8f6aec39ec9f90c1aeeed1caed713efc58453e85f4cfc32b6046c86d5`.
|
|
430
|
+
This covers plain KV and chunked prefix continuation, not mixed KV, saved-state
|
|
431
|
+
restoration, compiled decode, sliding-window wrap, other families or performance.
|
|
432
|
+
Raw reports remain outside Git; the broader PLAN verification item stays open.
|
|
433
|
+
|
|
434
|
+
The plan can additionally request `kv: "artifact"` for the checkpoint's existing
|
|
435
|
+
mixed-KV policy and `restore: true` for persisted state and continuation checks.
|
|
436
|
+
Conversion happens after each forward, never on empty state. Restore verifies
|
|
437
|
+
tensor integrity, the saved token prefix, live planes and continuation against
|
|
438
|
+
the uninterrupted path; it does not restore sampling or execution sessions.
|
|
439
|
+
Temporary checkpoints are removed after each case.
|
|
440
|
+
|
|
441
|
+
Oracle goldens and recorded comparisons are published outside this repository;
|
|
442
|
+
run the tool against a pinned golden revision or regenerate one. A mixed-KV
|
|
443
|
+
comparison must use the documented per-path composition (stock mlx-lm attention
|
|
444
|
+
for one query, OptiQ tiled attention for multi-query input), not every default of
|
|
445
|
+
`optiq serve`, whose fused install also changes single-query decode.
|
|
446
|
+
|
|
447
|
+
### Compiled decode verification
|
|
448
|
+
|
|
449
|
+
The opt-in `tests/parity/compiled-decode.test.ts` accepts local checkpoint paths
|
|
450
|
+
through `MLX_BUN_COMPILED_GEMMA_E4B` and `MLX_BUN_COMPILED_GEMMA12B`. Run it with
|
|
451
|
+
`bun --no-env-file test packages/inference/tests/parity/compiled-decode.test.ts`
|
|
452
|
+
from the root. Each unset path skips that family; a supplied invalid path fails.
|
|
453
|
+
The checkpoints must include their existing mixed-KV configuration. No model or
|
|
454
|
+
reference data is downloaded.
|
|
455
|
+
|
|
456
|
+
The test compares native full-logit bytes for identical fixed tokens with plain
|
|
457
|
+
and artifact-configured KV, both before and after the sliding window fills.
|
|
458
|
+
Greedy trajectories and a dense Gemma mid-segment failure (which ends its generation:
|
|
459
|
+
the compiled step has no fallback) use main's original tokenizer-rendered prompts
|
|
460
|
+
(12B targets 600/1100 tokens; e4b targets 700).
|
|
461
|
+
Those checks require actual compiled-step activation and zero unexpected retraces.
|
|
462
|
+
The forced-token full-logit matrix uses deterministic IDs independently of EOS. These are
|
|
463
|
+
compiled-versus-ordinary checks, not an external-oracle or performance claim.
|
|
464
|
+
Runtime compilation overrides stay inside the test; this adds no application option.
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
### Scheduler continuation and per-path checks
|
|
468
|
+
|
|
469
|
+
The opt-in [continuation test](tests/parity/ordinary-continuation.test.ts) takes
|
|
470
|
+
`MLX_BUN_TEST_CONTINUATION_MODEL=/cached/checkpoint`. It compares uninterrupted
|
|
471
|
+
and interrupted/restarted B1/B4 generation, including pending tokens, seeded
|
|
472
|
+
sampling history, byte-identical checkpoint planes, and actual restored-row
|
|
473
|
+
counts. `MLX_BUN_TEST_CONTINUATION_ADAPTER=/cached/adapter` adds adapter-context
|
|
474
|
+
and cache-namespace isolation without bundled fixtures; with it,
|
|
475
|
+
`MLX_BUN_TEST_CONTINUATION_IGNORED_DRAFT=1` binds a two-model draft whose rows
|
|
476
|
+
fail if opened and requires the adapter rows to match a draftless adapter
|
|
477
|
+
control, tokens and checkpoint planes. The existing KV matrix
|
|
478
|
+
uses `MLX_BUN_TEST_CONTINUATION_KV=bf16|4|8|per-layer|turbo`,
|
|
479
|
+
`MLX_BUN_TEST_CONTINUATION_KV_START=0` (or `prompt+N`), and
|
|
480
|
+
`MLX_BUN_TEST_CONTINUATION_INTERRUPT=6` (6–15).
|
|
481
|
+
The test uses the actual capability planner. With an adapter and positive affine
|
|
482
|
+
start it also checks grouped admission, an event-driven late join, cancellation
|
|
483
|
+
against a matched stop control, queued base-context isolation and reuse after
|
|
484
|
+
drain. Mounting must change full logits, and unmounting must restore the base.
|
|
485
|
+
Full-attention universal graphs accept ordinary adapter requests with uniform
|
|
486
|
+
or per-layer delayed affine KV and adapter-aware continuation. Paired B1
|
|
487
|
+
Qwen2.5-0.5B acceptance without a configured draft matched main for uniform
|
|
488
|
+
KV4/KV8 and partial mixed KV, greedy/seeded sampling, and fresh-process
|
|
489
|
+
continuation before and after conversion: full logits, every valid cache plane,
|
|
490
|
+
adapter identity and unmount restoration. Separate candidate B2/B4 tests without
|
|
491
|
+
a configured draft passed joins, cancellation, queued adapter/base isolation
|
|
492
|
+
and reuse after drain.
|
|
493
|
+
|
|
494
|
+
Gemma4 adapter rows take ordinary generation checkpoints. Paired B1 checks, recorded
|
|
495
|
+
while adapter rows still bypassed paging (so on plain KV),
|
|
496
|
+
on Gemma4 e2b OptiQ-4bit (snapshot `b0162532`, MacBookPro18,2, MLX 0.32.2)
|
|
497
|
+
matched main `02d723a` at capacity 1 against continuous capacity 8 for 16 tokens,
|
|
498
|
+
greedy and seeded sampling, and fresh-process restoration across both settings.
|
|
499
|
+
Full logits and all 15 donor KV states matched; all 35 layer outputs and the
|
|
500
|
+
final norm matched by hash. The nonzero adapter changed logits and unmounting
|
|
501
|
+
restored the base. This covers one active row within the sliding window, not grouped rows, paged numerics,
|
|
502
|
+
hard-kill durability, external-oracle parity or performance.
|
|
503
|
+
|
|
504
|
+
An adapter request whose draft cannot serve target adapters (the two-model
|
|
505
|
+
provider on any graph; any provider on a softcap graph, which never speculates
|
|
506
|
+
with adapters) decodes ordinarily, ignores the draft and, without fill, grammar
|
|
507
|
+
or logprobs, takes generation checkpoints like a draftless adapter request.
|
|
508
|
+
The continuation test's ignored-draft opt-in passed with a synthetic nonzero
|
|
509
|
+
q/v adapter on gemma-2-2b-it-4bit (with fresh-process restore) and on
|
|
510
|
+
Llama-3.2-3B-Instruct-4bit with a custom 4-token window (M1 Max, bf16 KV).
|
|
511
|
+
|
|
512
|
+
With `MLX_BUN_TEST_CONTINUATION_KV=turbo`, every saved and restored checkpoint
|
|
513
|
+
after conversion (immediately with a start of 0) must hold TurboQuant's exact
|
|
514
|
+
inventory: five encoded planes per full-attention cache (K indices, float16 K
|
|
515
|
+
scales and zeros per 32-wide group, packed uint8 V, float16 V scales) at the
|
|
516
|
+
scheme's bits, the fused decode when `MLX_BUN_TURBOQUANT_FUSED_DECODE` is unset,
|
|
517
|
+
a plain ring per sliding cache, every KV layer at the checkpoint's offset, and
|
|
518
|
+
no reuse floor with a start of 0. Before a positive start converts, checkpoints
|
|
519
|
+
are still plain. Paired B1 checks on Gemma4
|
|
520
|
+
e4b OptiQ-4bit (snapshot `98d7dc6a`, MacBookPro18,2, MLX 0.32.2) with
|
|
521
|
+
server-wide TurboQuant k8v3 from token 0 matched main `02d723a` through each
|
|
522
|
+
side's continuous gateway at capacity 8 for 16 tokens, greedy and seeded
|
|
523
|
+
sampling: full logits, all 42 layer outputs and the final norm by hash, and all
|
|
524
|
+
24 donor states at every forward (20 plain sliding rings; the 4 full donors
|
|
525
|
+
plain on the prefill forward and TurboQuant, all five planes, from the next),
|
|
526
|
+
checkpoint saves at 4, 8 and 12, an interruption at 10 and a fresh-process
|
|
527
|
+
resume from the durable record at 8. Within this tree, the continuation test
|
|
528
|
+
passed on the same artifact at B1 and B4 with TurboQuant from token 0: seeded
|
|
529
|
+
sampling (temperature 0.7, seed 42 plus the row, repetition penalty 1.1) for
|
|
530
|
+
16 tokens, interrupted at 6 with durable checkpoints at 4, then restored in the
|
|
531
|
+
same process and in a fresh one; every restored record held 12 tokens over 24
|
|
532
|
+
caches of 2 or 5 planes. The main-paired evidence is B1 only; a wrapped sliding
|
|
533
|
+
ring, a delayed TurboQuant start, hard-kill durability, external-oracle parity
|
|
534
|
+
and performance are not claimed.
|
|
535
|
+
|
|
536
|
+
MiniCPM5 and these universal graphs also ignore a configured draft for adapter
|
|
537
|
+
requests, as main did; requested logprobs remain available. Main also ignored
|
|
538
|
+
supplied fill for them, which the shared fill method now serves (the pairing
|
|
539
|
+
below predates that). Without fill/logprobs/grammar, ordinary checkpoints remain eligible. Ignoring the draft
|
|
540
|
+
does not invoke the provider during a request; loading an artifact-backed draft
|
|
541
|
+
still performs its existing compatibility probe. Paired B1 acceptance on cached
|
|
542
|
+
Qwen2.5-0.5B and MiniCPM5-1B compared six scenarios per model, each with a matched
|
|
543
|
+
draftless adapter control, over 16 generated tokens. The scenarios cover
|
|
544
|
+
TwoModel and Ngram, uniform KV4/KV8 and partial mixed KV, ignored strict/verify/echo
|
|
545
|
+
fill, requested logprobs, and greedy or seeded sampling. Full logits and all valid
|
|
546
|
+
KV planes matched main and controls; request-time draft/fill counters stayed zero,
|
|
547
|
+
nonzero adapters changed logits, and physical unmount restored base logits.
|
|
548
|
+
The no-fill scenarios retained plain or encoded checkpoints and matched
|
|
549
|
+
uninterrupted output after graceful cancellation and fresh-process restoration.
|
|
550
|
+
This does not qualify every combination or multi-row configured-draft requests.
|
|
551
|
+
|
|
552
|
+
Only the first cold decode/resume token may have signed versus unsigned int32
|
|
553
|
+
metadata; its raw bytes, value and computed state must match exactly. Main
|
|
554
|
+
ignored supplied fill on these graphs without adapters or a configured draft
|
|
555
|
+
too, having no affine append binding; the planner then chose ordinary
|
|
556
|
+
decoding, which the pairing below qualified, and now serves it through the
|
|
557
|
+
shared fill method. Paired B1 acceptance on cached MiniCPM5-1B and Qwen2.5-0.5B matched main and
|
|
558
|
+
no-fill controls for supplied strict/echo fill, unseeded greedy sampling and
|
|
559
|
+
KV4 starting at token 8. Each request generated 16 tokens across conversion;
|
|
560
|
+
all 17 forward states, 16 full-vocabulary projections and valid cache planes
|
|
561
|
+
matched, and every fill hook stayed unused. Main planned fill and disabled it
|
|
562
|
+
inside generation; the planner of that pairing reported `fill-incompatible-with-request`
|
|
563
|
+
and selected ordinary continuous decoding. This pair does not qualify B2 or
|
|
564
|
+
cancellation.
|
|
565
|
+
Requests that supply fill remain ineligible for generation checkpoints.
|
|
566
|
+
Speculation, grammar proposals and fill are planned over delayed affine KV as
|
|
567
|
+
over any other scheme. A graph whose layers softcap their attention scores
|
|
568
|
+
(Gemma2) composes the softcapped read (`state/softcap-attention`) at construction
|
|
569
|
+
in place of the cache's own read: it fetches each window as plain keys and values
|
|
570
|
+
from every storage (plain as stored, TurboQuant decoded, affine dequantized, paged
|
|
571
|
+
gathered) and attends with the manual composition. That attention runs unchanged over the dequantized keys and values (dequantize and
|
|
572
|
+
read, not a quantized kernel), so affine KV serves its ordinary, grammar,
|
|
573
|
+
speculative and fill rows, with checkpoints, before and after each row's
|
|
574
|
+
transition; main threw at the first forward past it. No artifact
|
|
575
|
+
holds those numbers, so the native Gemma2 suite (`MLX_BUN_GEMMA2_NATIVE=1`)
|
|
576
|
+
gates every phase at maxDiff 0 against the plain storage the graph composes
|
|
577
|
+
without a scheme, its fetched window requantized by mlx past the transition, and
|
|
578
|
+
holds the descriptor and mask cases to recorded synthetic digests
|
|
579
|
+
(`tests/models/synthetic-digest.ts`, fixtures under `tests/fixtures/synthetic-digests/`,
|
|
580
|
+
`scripts/synthetic-digests.ts record|check`, recorded per GPU architecture). Direct grammar jump commits its spans over any storage
|
|
581
|
+
through the shared span method, as main's jump did: one maintenance
|
|
582
|
+
call, then one unsplit forward of the pending token and the forced span, plain
|
|
583
|
+
across the start and over the converted layers after it. On 2026-09-28 UTC (M1 Max, MLX 0.32.2) the opt-in
|
|
584
|
+
`tests/parity/affine-grammar-spans.test.ts` (`MLX_BUN_TEST_AFFINE_SPANS_MODEL`)
|
|
585
|
+
passed on cached Gemma2-2B-4bit `2c715097` with KV4: below the transition, B1
|
|
586
|
+
spans matched direct jump-forward generation in tokens, matcher history and
|
|
587
|
+
every projection's bytes (a row crossing it was then refused, which this tree
|
|
588
|
+
no longer does); an interleaved peer matched its solo run and the
|
|
589
|
+
group served again.
|
|
590
|
+
|
|
591
|
+
On 2026-09-28 UTC, cached Gemma2-2B-4bit snapshot `2c715097` (26 full-attention
|
|
592
|
+
layers) passed bounded TurboQuant k8v3 acceptance on an M1 Max, MLX 0.32.2 and
|
|
593
|
+
Bun 1.4.2. [#210](https://github.com/joshuarossi/mlx-bun/pull/210) qualified
|
|
594
|
+
ordinary decoding, fresh-process continuation and HTTP cancellation/recovery
|
|
595
|
+
against main, with separate same-geometry candidate batching controls.
|
|
596
|
+
[#212](https://github.com/joshuarossi/mlx-bun/pull/212), tested at `e783c7b4`,
|
|
597
|
+
matched main `02d723a` byte for byte in full-vocabulary logits and all valid cache
|
|
598
|
+
planes for immediate and delayed conversion with real choice/JSON grammars.
|
|
599
|
+
The checks cover unsplit forced spans followed by sampling, callback stop within
|
|
600
|
+
a committed span, actual prefix reuse with conversion before the suffix,
|
|
601
|
+
cancellation with an active peer, and same-scheduler recovery. Graph calls remain
|
|
602
|
+
B1 and interleaved; this does not qualify stacked B2 grammar numerics, other
|
|
603
|
+
artifacts, an external TurboQuant oracle or performance. Captures and input pins
|
|
604
|
+
remain outside Git.
|
|
605
|
+
|
|
606
|
+
Universal graphs
|
|
607
|
+
with sliding layers take the same ordinary delayed path when their bound
|
|
608
|
+
attention reads encoded KV. Qwen3 and Qwen3-MoE attend affine KV as mlx-lm's
|
|
609
|
+
`scaled_dot_product_attention` does (quantized SDPA over a quantized cache) and
|
|
610
|
+
take the same encoded-read path: affine KV, immediate or delayed, batches,
|
|
611
|
+
speculates and takes generation checkpoints across its conversion. On 2026-09-28 UTC (M1 Max,
|
|
612
|
+
MLX 0.32.2), cached Qwen3-4B-Instruct-2507-4bit `50d42775` matched mlx-lm
|
|
613
|
+
0.31.3's `generate_step` order bit for bit in six greedy steps' full logits
|
|
614
|
+
through direct generation with KV8 and KV4 at starts 0, 8 and 14 (a one-off
|
|
615
|
+
comparison); the opt-in spans test and the ordinary-continuation test (KV8 from
|
|
616
|
+
0, KV4 from prompt+4, KV8 from prompt+2) passed on it; and a grouped n-gram
|
|
617
|
+
draft over immediate KV8 and KV4 reproduced ordinary greedy output. No
|
|
618
|
+
Qwen3-MoE artifact was run.
|
|
619
|
+
On encoded-read universal graphs, Qwen3 and MiniCPM5, whose verifiers qualify
|
|
620
|
+
grammar proposals, grammar jump is planned as verified proposals over every KV
|
|
621
|
+
scheme; bound directly, the span method commits spans over their delayed affine
|
|
622
|
+
rows with no dense-read requirement, before and after conversion.
|
|
623
|
+
The same opt-in real-weight spans test passed on cached MiniCPM5-1B-OptiQ-4bit
|
|
624
|
+
`664aabae` and Qwen2.5-0.5B-Instruct-4bit `a5339a41` with KV4: B1 spans matched
|
|
625
|
+
direct generation in tokens, matcher history and every projection's bytes, both
|
|
626
|
+
below the transition and for a row converting three tokens into decode and
|
|
627
|
+
continuing over converted layers; an interleaved peer matched its solo run. On 2026-09-27 UTC (M1 Max, MLX 0.32.2) at `a9b60646`,
|
|
628
|
+
a custom graph over unchanged Llama-3.2-3B-Instruct-4bit weights with window 8
|
|
629
|
+
(not a published model) matched main at B1 in full logits and all
|
|
630
|
+
valid cache planes for bf16, immediate affine, KV4 and KV8 converting in decode
|
|
631
|
+
after the ring wrapped, and KV4 converting in prefill at token 32. Gateway and
|
|
632
|
+
direct execution agreed at B1 and B2, with rows converting separately, a
|
|
633
|
+
prefill-converted joiner, a cancelled row's survivor and same-group reuse
|
|
634
|
+
matching a fresh group; the continuation test restored B1 and B4 rows in a
|
|
635
|
+
fresh process for KV4 and KV8. Gemma4's delayed rotating rows share the
|
|
636
|
+
single-row adoption: on e4b OptiQ-4bit, one-block and wrapped singletons matched
|
|
637
|
+
main through conversion (hidden states, logits, valid planes and
|
|
638
|
+
physical ring order), as did the speculative target layout and two rows in both
|
|
639
|
+
orders at the same batch width; main-produced wrapped checkpoints loaded into
|
|
640
|
+
both trees gave identical adoption and continuation, and RAM and disk restores
|
|
641
|
+
agreed. A restored wrapped ring's continuation can still differ from the live
|
|
642
|
+
one ([State and attention](#state-and-attention)). Captures and their content
|
|
643
|
+
pins remain external. These checks do not cover cross-version checkpoint
|
|
644
|
+
files, hard-kill durability, an external oracle or performance.
|
|
645
|
+
|
|
646
|
+
Grammar, grammar proposals and supplied fill on these graphs have a separate
|
|
647
|
+
opt-in consumer, the
|
|
648
|
+
[sliding-window grammar and fill test](tests/parity/sliding-grammar-fill.test.ts).
|
|
649
|
+
It takes `MLX_BUN_TEST_SLIDING_GRAMMAR_FILL_MODEL` (a Llama-family artifact) and
|
|
650
|
+
`MLX_BUN_TEST_SLIDING_GRAMMAR_FILL_WINDOW` (at least 4) and builds the same custom
|
|
651
|
+
graph as the rotating-join and continuation consumers. The reference is this
|
|
652
|
+
tree's direct token-by-token generation; the gateway runs its placement path
|
|
653
|
+
(plan, `methodRequest` or row sampling, the binding's group), with every case past
|
|
654
|
+
the window:
|
|
655
|
+
- Grammar (JSON schema, choice, EBNF): at B1, tokens, every forward's IDs and
|
|
656
|
+
valid K/V, and every projection's complete logits. At B2, prepared together
|
|
657
|
+
or joining late, each row's tokens.
|
|
658
|
+
- With the composition's `grammarJump` on: the speculative group verifies the matcher's
|
|
659
|
+
forced string instead of committing it. Tokens equal direct token-by-token
|
|
660
|
+
generation at B1 and B2, rejected proposals leave the ring, and proposals are
|
|
661
|
+
both accepted and rejected.
|
|
662
|
+
- Supplied strict and echo fill is applied through the shared fill binding and
|
|
663
|
+
by direct `generate`: strict rows restate the fill-off control, and echo and
|
|
664
|
+
scripted proposals (one accepted, one rejected after the ring wrapped) are
|
|
665
|
+
verified, so output equals the control while the session records injected and
|
|
666
|
+
accepted tokens. B2 pairs start together and late (a strict row joining beside
|
|
667
|
+
a wrapped echo row, whose first multi-position verify forward reads a ring the
|
|
668
|
+
joiner's one in-place write has rotated).
|
|
669
|
+
- For each of these shapes: a B2 row cancelled at its third token beside a
|
|
670
|
+
surviving peer, then reuse of the drained group.
|
|
671
|
+
|
|
672
|
+
Run it with
|
|
673
|
+
`MLX_BUN_TEST_SLIDING_GRAMMAR_FILL_MODEL=/Llama-3.2-3B-Instruct-4bit MLX_BUN_TEST_SLIDING_GRAMMAR_FILL_WINDOW=8 bun --no-env-file test packages/inference/tests/parity/sliding-grammar-fill.test.ts`.
|
|
674
|
+
|
|
675
|
+
The [padded-prefill test](tests/parity/padded-prefill-model.test.ts) takes
|
|
676
|
+
`MLX_BUN_TEST_PADDED_PREFILL_MODEL` and `MLX_BUN_TEST_PADDED_PREFILL_REFERENCE`.
|
|
677
|
+
The external JSON report is `{ runtime, configSha256, rows }`, where `rows` is
|
|
678
|
+
the output of the unchanged Python body in
|
|
679
|
+
[`02d723a:tests/parity/padded-prefill-model.test.ts` lines 9–56](https://github.com/joshuarossi/mlx-bun/blob/02d723a/tests/parity/padded-prefill-model.test.ts#L9-L56): prompts, padding side, chunk counts,
|
|
680
|
+
recurrent-state hashes, four per-row logit hashes, and row offsets. The test
|
|
681
|
+
requires all 18 cases (36 with `MLX_BUN_TEST_PADDED_PREFILL_WIDE=1`), then compares
|
|
682
|
+
the full-vocabulary float32 logit slice at the last prompt position for each row
|
|
683
|
+
and each of three continuation steps via SHA-256, plus recurrent state.
|
|
684
|
+
`runtime` is the MLX core version (for example, `0.32.2`), not the mlx-lm version. Generate the reference outside this repository on the same machine with
|
|
685
|
+
the pinned oracle and identical weights; this test never starts Python.
|
|
686
|
+
`MLX_BUN_TEST_PADDED_FULL_LAYOUT=affine|turbo`,
|
|
687
|
+
`MLX_BUN_TEST_SPECULATIVE_ROTATING_LAYOUT=1`, or
|
|
688
|
+
`MLX_BUN_TEST_PADDED_ROTATING_LAYOUT=1` exercise the corresponding pre-conversion
|
|
689
|
+
cache layouts against the same plain-KV reference, not quantized arithmetic.
|
|
690
|
+
|
|
691
|
+
The [Gemma2 batching test](tests/models/universal/gemma2-batching.test.ts) runs
|
|
692
|
+
synthetic mask checks with `MLX_BUN_GEMMA2_NATIVE=1`; add
|
|
693
|
+
`MLX_BUN_GEMMA2_MODEL=/cached/gemma2` for real B1/B2/B4 continuous lifecycle checks.
|
|
694
|
+
Supplying a model or reference without the native opt-in, a reference without a
|
|
695
|
+
model, or an invalid model config fails before native initialization.
|
|
696
|
+
`MLX_BUN_GEMMA2_REFERENCE=/external/report.json` adds full-logit and KV hashes
|
|
697
|
+
against same-shaped pinned Python runs. The reference uses upstream Gemma2
|
|
698
|
+
and `BatchKVCache`, adding only a GQA axis to the upstream row mask. B1 is
|
|
699
|
+
compared exactly with direct execution; B>1 uses the same-shaped reference,
|
|
700
|
+
since quantized matmul dispatch can change numerical results across batch sizes.
|
|
701
|
+
The app gateway's real Gemma2 placement test uses the same
|
|
702
|
+
`MLX_BUN_GEMMA2_NATIVE=1` opt-in: importing the graph loads MLX even though
|
|
703
|
+
that test only checks placement and allocates no tensors. Native-blocked CPU
|
|
704
|
+
runs skip this check; synthetic gateway tests remain CPU-only.
|
|
705
|
+
|
|
706
|
+
With the composition's `grammarJump` on, Gemma2 plain-KV ordinary requests preserve
|
|
707
|
+
main's direct forced spans through the shared scheduler: one graph append for
|
|
708
|
+
`[pending, ...forced]`, with no sampling of forced IDs. Graphs already using
|
|
709
|
+
verified grammar proposals retain that behavior. Paired greedy Gemma2-2B checks
|
|
710
|
+
against main passed a terminating choice and nonterminal JSON schema, including
|
|
711
|
+
full-vocabulary logits, all 26 valid KV layers, masks and publication order.
|
|
712
|
+
Separate real-gateway checks passed callback stop inside a committed span,
|
|
713
|
+
retention and restoration of the complete prefix, fixed-suffix continuation,
|
|
714
|
+
mid-span cancellation with an active peer, and same-scheduler recovery. Graph
|
|
715
|
+
calls remain B1; this is not stacked batching or an all-model qualification.
|
|
716
|
+
The shared gateway owns prefix transfer and, as on main's shared path, returns
|
|
717
|
+
an empty `stats.cacheTokens`; direct generation retains its exact cache history.
|
|
718
|
+
No full-stats, HTTP cancellation, abort-last/abort-plus-stop, oracle or performance
|
|
719
|
+
identity is claimed by these checks.
|
|
720
|
+
|
|
721
|
+
Shared Gemma2 qualifies plain KV, including grammar-constrained and adapter
|
|
722
|
+
requests; the opt-in [constrained Gemma2 test](../../apps/mlx-bun/tests/engine/gemma2-constrained.test.ts)
|
|
723
|
+
compares them exactly with direct generation. Plain-KV fill uses the shared fill
|
|
724
|
+
binding: with `MLX_BUN_GEMMA2_MODEL`, the batching test replays main's fill
|
|
725
|
+
(one-position assert appends, one verify forward per echo span, trim on rejection)
|
|
726
|
+
and requires B1 logits bit for bit, then B2/B3/B4 tokens through joins and
|
|
727
|
+
cancellation, observing each capacity as the batch's high water. Fill also runs
|
|
728
|
+
inside a request's adapter context; the constrained test's grouped case checks
|
|
729
|
+
four-row adapter groups, partition from base rows, an event-driven join and a
|
|
730
|
+
replacement, a cancellation against a same-count stop control, and no leak. Plain-KV
|
|
731
|
+
generation continuation uses the shared continuation binding once persistence is
|
|
732
|
+
configured; the opt-in [Gemma2 continuation test](../../apps/mlx-bun/tests/engine/gemma2-continuation.test.ts)
|
|
733
|
+
runs it through the app's serving composition: interval snapshots, a durable
|
|
734
|
+
record surviving cancellation, its restore after a fresh engine, and cleanup on
|
|
735
|
+
completion. Plain-KV two-model speculation uses the shared speculative group
|
|
736
|
+
with a second loaded instance as the draft; the batching test's opt-in two-model
|
|
737
|
+
case checks B1 determinism, main's gate that greedy grammar with speculation
|
|
738
|
+
equals greedy grammar alone, a ragged joined group (its measured target batch),
|
|
739
|
+
cancellation, and a follow-on request that reproduces the fresh B1 run. N-gram
|
|
740
|
+
lookup drafting uses the same group; its opt-in case observes rounds where a
|
|
741
|
+
proposing row verifies beside an empty, right-padded peer and rounds where no row
|
|
742
|
+
proposes, swaps the padded peer's content without changing the target's forwards,
|
|
743
|
+
proposals, tokens or logits (greedy and seeded), cancels a ragged third row against
|
|
744
|
+
a same-count stop control, and checks main's grammar gate. Draft providers whose
|
|
745
|
+
rows tap target hidden layers keep a typed refusal (this graph declares no taps);
|
|
746
|
+
KV schemes and paging are its composition's legos, read through the softcapped
|
|
747
|
+
read above.
|
|
748
|
+
Its full-attention policy remains the pinned mlx-lm policy documented in the
|
|
749
|
+
[architecture descriptor](src/models/universal/archs.ts).
|
|
750
|
+
A plain-KV adapter request with a configured TwoModel or Ngram draft uses
|
|
751
|
+
ordinary continuous decoding and ignores both draft and fill, as main did.
|
|
752
|
+
This is ordinary decoding, not adapter speculation support. Paired B1 greedy
|
|
753
|
+
acceptance against main passed with a synthetic nonzero adapter on Gemma2-2B:
|
|
754
|
+
full logits and valid KV match with either draft configured, with or without
|
|
755
|
+
fill and logprobs; draft and fill stay unused, and physical unmount restores
|
|
756
|
+
the base computation. This does not qualify multi-row adapter/draft requests.
|
|
757
|
+
|
|
758
|
+
A plain-KV request without adapters keeps two-model or n-gram speculation when
|
|
759
|
+
fill is supplied, but ignores the fill session (strict, verify or echo), as main
|
|
760
|
+
did. Logprobs instead select ordinary decoding and ignore both draft and fill.
|
|
761
|
+
Paired B1 greedy acceptance on Gemma2-2B matched main and each fill-off control:
|
|
762
|
+
full target/draft logits, valid KV, proposals, continuing commits, output and
|
|
763
|
+
logprobs; genuine drafting ran when selected, and every fill hook stayed unused.
|
|
764
|
+
Main omits its terminal speculative commit; the candidate's terminal state was
|
|
765
|
+
checked against its verified prefix, without a main-terminal parity claim.
|
|
766
|
+
This does not qualify B>1, external-oracle parity or performance, or enable
|
|
767
|
+
speculative fill.
|
|
768
|
+
|
|
769
|
+
The [Trellis variant test](tests/parity/trellis-shared-m.test.ts) takes
|
|
770
|
+
`MLX_BUN_TEST_TRELLIS_MODEL=/cached/packed-qwen`. It preserves main's variant
|
|
771
|
+
comparison: `MLX_BUN_TRELLIS_AB_VARIANT` (default 7) against
|
|
772
|
+
`MLX_BUN_TRELLIS_AB_BASELINE` (default 6). Use variant 13 against baseline 12
|
|
773
|
+
for the optimized expansion path. `TrellisLinear` passes only variant 13, so the
|
|
774
|
+
test runs the generic Qwen3.5 graph with MLPs it builds over the loaded weights,
|
|
775
|
+
each passing its variant explicitly. M=1–5 compares complete logits, recurrent/KV
|
|
776
|
+
state and continuation; variant 13 also checks M=16/128/512 with the last-position
|
|
777
|
+
head. Six alternating-order blocks also screen the last-position head at
|
|
778
|
+
M=1–5/8/9/16, M=32 for variants 11–13, and M=128/512 for variant 13.
|
|
779
|
+
These are exact within-artifact kernel-variant checks, not a Python oracle
|
|
780
|
+
or timing claim.
|
|
781
|
+
|
|
782
|
+
For variant 13, the `applegpu_g13s` single-row bf16 gate/up kernel at
|
|
783
|
+
5120 → 17408, matching 2/3/4-bit widths, T=256 and L=12 shares a 16 KiB
|
|
784
|
+
threadgroup codebook across sixteen output rows. Its values retain the unrefined
|
|
785
|
+
float32 decoder contract, scale multiplication, accumulation order and activation
|
|
786
|
+
tail. At the same qualified single-row bf16 geometry, mixed-width gate/up and the
|
|
787
|
+
2/3/4-bit down kernels construct the decoder's integer value through exact
|
|
788
|
+
float32 mantissa bits before the unchanged reciprocal multiply. This preserves
|
|
789
|
+
scale multiplication and reduction order. The qualified down kernels also
|
|
790
|
+
generate a 16 KiB float32 threadgroup codebook, using 128/512/256 threads for
|
|
791
|
+
2/3/4-bit weights respectively. All 128 partial sums and their reduction order
|
|
792
|
+
remain unchanged. Other shapes and shared multi-row kernels keep their existing paths.
|
|
793
|
+
|
|
794
|
+
These representations are required kernel arguments: `fusedGateUpSwiglu`'s
|
|
795
|
+
`codebook`, `fusedGateUpSwigluMixed`'s `exactBits` and `trellisScatter`'s
|
|
796
|
+
`ScatterRepresentation` (`bits` for the balanced 3-bit down, `genericBits` for
|
|
797
|
+
2/4-bit down, `floatCodebook`). `qualifiedRepresentations(architecture)` turns
|
|
798
|
+
them all on for `applegpu_g13s` and off elsewhere; `TrellisLinear` resolves it
|
|
799
|
+
once at construction and passes the stored result. Passing `false` selects the
|
|
800
|
+
original inline decoding and conversions for A/B runs; disable the float codebook
|
|
801
|
+
as well when comparing the down representations.
|
|
802
|
+
The [kernel test](tests/kernels/trellis-gateup-codebook.test.ts) checks all 4096
|
|
803
|
+
codebook values, all 1021 possible integer decoder values and fused outputs;
|
|
804
|
+
the [artifact test](tests/parity/trellis-gateup-codebook.test.ts)
|
|
805
|
+
uses `MLX_BUN_TEST_TRELLIS_MODEL` to compare complete finite logits, live cache state
|
|
806
|
+
and greedy continuation, running the generic Qwen3.5 graph with MLPs that pass each
|
|
807
|
+
representation explicitly. The [standard-request benchmark](../../apps/mlx-bun/scripts/bench-trellis-gateup.ts)
|
|
808
|
+
provides `--help`, requires local weights and an external output path, and runs
|
|
809
|
+
one fixed prompt with 256 greedy tokens and MTP off.
|
|
810
|
+
|
|
811
|
+
Run each file with `bun --no-env-file test <file>` and exclusive GPU access.
|
|
812
|
+
Unset required paths skip before native imports; supplied invalid model paths
|
|
813
|
+
or a partial padded-prefill opt-in fail. Open acceptance work lives in
|
|
814
|
+
[PLAN](../../PLAN.md#verify-the-migrated-library).
|
|
815
|
+
They extend rather than repeat [#61](https://github.com/joshuarossi/mlx-bun/pull/61)
|
|
816
|
+
(MiniCPM/Gemma plain and mixed state restore; Trellis against main),
|
|
817
|
+
[#62](https://github.com/joshuarossi/mlx-bun/pull/62) (Gemma e4b window-wrap state
|
|
818
|
+
restore), and [#64](https://github.com/joshuarossi/mlx-bun/pull/64) (compiled
|
|
819
|
+
Gemma e4b/12B). Those completed comparisons do not cover scheduler sampling
|
|
820
|
+
recovery, padded cohorts, or this real-model Trellis variant matrix.
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
## Direct library use
|
|
824
|
+
|
|
825
|
+
See [Qwen3 loading and generation](examples/qwen3-generate.ts). Run it from the
|
|
826
|
+
repository root with `bun packages/inference/examples/qwen3-generate.ts <checkpoint-directory> "Hello"`.
|
|
827
|
+
It loads the local tokenizer, streams tokens from the supplied graph, and releases
|
|
828
|
+
weights after generation completes.
|
|
829
|
+
|
|
830
|
+
Use the graph matching your checkpoint, or compose your own operations and supply
|
|
831
|
+
an explicit binding. The root export contains common loading and generation
|
|
832
|
+
helpers; the subpaths below expose individual components.
|
|
833
|
+
|
|
834
|
+
## Trellis weight expansion
|
|
835
|
+
|
|
836
|
+
See the runnable [Trellis expansion example](examples/trellis-expand.ts). Its
|
|
837
|
+
`expandWeights` function borrows codes and scales and returns an owned lazy tensor.
|
|
838
|
+
`bun packages/inference/examples/trellis-expand.ts` demonstrates the layout with
|
|
839
|
+
small generated inputs, without a model download.
|
|
840
|
+
|
|
841
|
+
The caller supplies uint32 packed codes, one floating-point scale per stored
|
|
842
|
+
row, and matching geometry. The eligibility helper checks the kernel's supported
|
|
843
|
+
geometry and output dtype; callers remain responsible for matching tensor
|
|
844
|
+
shapes and dtypes. This kernel supports the existing 1MAD codebook with L=12,
|
|
845
|
+
256-symbol blocks, and 2-, 3-, or 4-bit codes. Output is the stored matrix in
|
|
846
|
+
bfloat16, shaped `[rows, cols]`. For axis 0, that is the transposed weight layout.
|
|
847
|
+
|
|
848
|
+
The existing interleaved 3-bit layout uses `blockInterleave: 2` and codes shaped
|
|
849
|
+
`[cols / 512, rows, 48]`. Row-major codes use `[rows, cols * k / 32]`.
|
|
850
|
+
|
|
851
|
+
Expansion is lazy. The caller decides when to evaluate the result and owns its
|
|
852
|
+
array handle. The kernel does not load checkpoints or choose a model.
|
|
853
|
+
|
|
854
|
+
## Packed operations
|
|
855
|
+
|
|
856
|
+
All operations are imported from `@mlx-bun/inference/kernels/trellis`.
|
|
857
|
+
They borrow input arrays and return an owned, lazy output array.
|
|
858
|
+
|
|
859
|
+
| Operation | Input and purpose |
|
|
860
|
+
| --- | --- |
|
|
861
|
+
| `trellisReduce(x, codes, scales, geometry, variant)` | Axis-1 projection; `x` is `[M, inFeatures]`, M=1..4 |
|
|
862
|
+
| `trellisScatter(x, codes, scales, geometry, variant, useSharedScatterCodebook, representation)` | Axis-0 projection; same input shape and row budget |
|
|
863
|
+
| `fusedGateUpSwiglu(x, gate, up, variant, codebook)` | Matching axis-1 gate/up geometry and bit width; preserves leading input dimensions, with 1..4 total rows |
|
|
864
|
+
| `fusedGateUpSwigluMixed(x, gate, up, variant, tail, exactBits)` | Matching axis-1 geometry with independent bit widths; `tail` is `"fused"` or `"split"` |
|
|
865
|
+
| `gateUpReduceRow(x, gate, up, variant, codebook)` / `gateUpReduceRows(x, gate, up, variant)` | `fusedGateUpSwiglu`'s one-row / 2..4 shared-row kernel alone |
|
|
866
|
+
| `scatterRow` / `balancedScatterRow(x2, codes, scales, geometry, variant, representation)` | `trellisScatter`'s one-row kernel alone; balanced takes 3-bit codes at T 256, the other every other width |
|
|
867
|
+
| `scatterRows(x2, codes, scales, geometry, variant)` / `balancedScatterRows(..., sharedCodebook)` | `trellisScatter`'s 2..4 shared-row kernels alone, the same split |
|
|
868
|
+
| `expandTrellis(codes, scales, geometry, dtype, variant)` | General expansion, and variant 13's vector form |
|
|
869
|
+
| `tiledTrellisPrefill(x, codes, scales, geometry)` | Axis-1 packed prefill, M=5..32 |
|
|
870
|
+
| `splitKTrellisPrefill(x, codes, scales, geometry)` | Axis-0 packed prefill, M=5..8 |
|
|
871
|
+
| `wideTrellisPrefill(x, codes, scales, geometry)` | Axis-1 prefill following MLX's GemvWide arithmetic, M=5..15 |
|
|
872
|
+
|
|
873
|
+
`gate` and `up` are `TrellisWeights`: `{ codes, scales, geometry }`.
|
|
874
|
+
Callers supply compatible tensors and choose the variant explicitly. Existing
|
|
875
|
+
variant numbers are preserved: 6 uses the original packed f32 code/scale path;
|
|
876
|
+
13 adds the shared-work and vector-expansion forms. Variant 4 is
|
|
877
|
+
the inherited timing-only path and does not compute decoded weights correctly.
|
|
878
|
+
No application environment flags are read by these operations.
|
|
879
|
+
|
|
880
|
+
For prefill, use the corresponding `*Eligible(geometry, rowCount, dtype)` helper
|
|
881
|
+
to preserve the existing dispatch profile; the wide and tiled helpers also take
|
|
882
|
+
`widePrefill` from `qualifiedRepresentations(architecture)`, which a caller
|
|
883
|
+
resolves once. Wide prefill additionally requires caller-proven aligned,
|
|
884
|
+
row-contiguous input. These profiles and numerical
|
|
885
|
+
contracts are unchanged; they are not universal dispatch rules for every shape.
|
|
886
|
+
|
|
887
|
+
Single-purpose kernels from the same entry point: each runs one algorithm for
|
|
888
|
+
one row form and one code layout, takes no options beyond the mixed-width
|
|
889
|
+
SwiGLU tail, and checks its inputs. The factored kernels decode each weight's
|
|
890
|
+
code value y and apply the row scale (with 1/147.8) once per output, or fold it
|
|
891
|
+
into each input row's activation; the matrix kernels run on the simdgroup matrix
|
|
892
|
+
unit. Both reassociate relative to variant 6 and agree with it to bf16
|
|
893
|
+
precision, not bit for bit. The codebook kernels look each code value up in a
|
|
894
|
+
threadgroup table and keep variant 13's one-row output bits.
|
|
895
|
+
|
|
896
|
+
| Operation | Input and purpose |
|
|
897
|
+
| --- | --- |
|
|
898
|
+
| `gateUpFactoredRow(x, gate, up)` / `gateUpFactoredRows(x, gate, up)` | Same-width axis-1 gate/up/SwiGLU; one row / 2..4 rows sharing each decoded weight |
|
|
899
|
+
| `mixedGateUpFactoredRows(x, gate, up)` | Different-width gate/up, 1..4 rows; MLX's compiled-swiglu tail |
|
|
900
|
+
| `downFactoredRow` / `downFactoredRows(x2, codes, scales, geometry)` | Axis-0 projection over row-major codes; one row / 2..4 rows |
|
|
901
|
+
| `downK3InterleavedFactoredRow` / `downK3InterleavedFactoredRows(...)` | The same over 3-bit block-interleaved codes (T 256, L 12) |
|
|
902
|
+
| `gateUpMma(x, gate, up)` | Gate/up/SwiGLU for 1..8 rows; widths may differ |
|
|
903
|
+
| `gateUpCodebookRow(x, gate, up, tail)` | One-row gate/up/SwiGLU through a threadgroup codebook; widths may differ; the reduce kernels' bits |
|
|
904
|
+
| `downCodebookRow` / `downK3InterleavedCodebookRow(x2, codes, scales, geometry)` | One-row axis-0 projection through a threadgroup codebook, row-major 2/4-bit / 3-bit interleaved codes; the scatter kernels' bits |
|
|
905
|
+
| `downMma` / `downK3InterleavedMma(x2, codes, scales, geometry)` | Axis-0 projection for 1..8 rows, row-major / 3-bit interleaved codes |
|
|
906
|
+
| `gateUpMmaWide(x, gate, up)` | Gate/up/SwiGLU for any number of rows, each decoded fragment feeding several 8-row blocks (threadgroup codebook, activations staged in threadgroup memory); each row the bits `gateUpMma` gives its block of 8 |
|
|
907
|
+
| `downMmaWide` / `downK3InterleavedMmaWide(x2, codes, scales, geometry)` | Axis-0 projection for any number of rows through a threadgroup codebook, each decoded fragment feeding up to three 8-row blocks in the pass the rows plan (`downMmaWidePass` / `downK3InterleavedMmaWidePass` take the pass, `DOWN_ONE_BLOCK` for 1..8 rows); each row the bits `downMma` gives its block of 8 |
|
|
908
|
+
| `gateUpCodebookMma(x, gate, up)` | Gate/up/SwiGLU for 1..8 rows, each code value read from a threadgroup codebook (the M1 Max form); `gateUpMma`'s bits |
|
|
909
|
+
|
|
910
|
+
Outside the Trellis family, `kernels/quantization/affine{3,4}-{rows,mma}` run
|
|
911
|
+
3- and 4-bit group-64 affine projections for 2..8 rows (shared decode) and
|
|
912
|
+
1..8 rows (matrix unit), and `affine-split-mma` any number of rows of either width,
|
|
913
|
+
in blocks of 8, on the matrix unit with output tiles sharing each activation
|
|
914
|
+
fragment and K split across the threadgroup (the M1 Max form); `affine-row` is one
|
|
915
|
+
row of either width bit for bit MLX's one-row quantized matmul (its qmv's
|
|
916
|
+
arithmetic, each threadgroup computing the activation chunks once for 64 rows);
|
|
917
|
+
`kernels/attention/kvq4-decode` is one-row attention
|
|
918
|
+
over a 4-bit group-64 KV cache with head dim 256, reading each packed row once
|
|
919
|
+
for all query heads of its KV head, the tokens per threadgroup the caller's (512 on
|
|
920
|
+
the M4 Pro, 128 on the M1 Max); `kernels/attention/multi-query` is causal
|
|
921
|
+
flash decoding for 2..8 query rows over a BF16 cache. `kernels/ane` holds the
|
|
922
|
+
Neural Engine programs (`linear`) and the GPU kernels that fill their buffers
|
|
923
|
+
(`fill`). The layers in `layers/trellis-gate-up`, `layers/trellis-down`,
|
|
924
|
+
`layers/trellis-codebook-gate-up`, `layers/trellis-codebook-down`, `layers/affine-verify-linear`, `layers/kv4-decode-attention` and
|
|
925
|
+
`layers/ane-prefill-split` bind loaded weights to these kernels, one kernel per
|
|
926
|
+
layer.
|
|
927
|
+
|
|
928
|
+
## Artifact, input, and layer APIs
|
|
929
|
+
|
|
930
|
+
- `@mlx-bun/inference/artifacts`: model configuration, safetensors metadata,
|
|
931
|
+
and lazy native weight loading through `Weights.open(directory)`. The caller
|
|
932
|
+
supplies the local artifact directory and any graph-specific weight view.
|
|
933
|
+
- `@mlx-bun/inference/artifacts/auxiliary-files`: `copyAuxFiles(source, output)`
|
|
934
|
+
copies tokenizer, template, and other non-weight checkpoint files when present,
|
|
935
|
+
without loading MLX.
|
|
936
|
+
- `@mlx-bun/inference/input`: `loadTokenizer(directory)` consumes the existing
|
|
937
|
+
Hugging Face tokenizer files; `ChatTemplate.load(directory)` loads the template.
|
|
938
|
+
- `@mlx-bun/inference/layers`: quantized linear and embedding layers, RMSNorm,
|
|
939
|
+
and `TrellisLinear`, which composes the standalone Trellis kernels at decode
|
|
940
|
+
variant 13 and retains the existing row-count dispatch and dense expansion.
|
|
941
|
+
`ExpandedTrellisLinear` is the 8-bit affine carrier a caller constructs explicitly.
|
|
942
|
+
- `@mlx-bun/inference/layers/lora`: inference-time LoRA state and weights.
|
|
943
|
+
- `@mlx-bun/inference/runtime/config`: immutable execution settings and scoped
|
|
944
|
+
overrides for the `MLX_BUN_*` keys the library still reads (process settings,
|
|
945
|
+
diagnostics and test opt-ins; the website's configuration reference lists each
|
|
946
|
+
read site). A model-level option is a composition field instead (above).
|
|
947
|
+
|
|
948
|
+
These modules do not download models or start services. Tensor handles returned
|
|
949
|
+
by `Weights.tensor()` are borrowed from the weights owner; release them through
|
|
950
|
+
`Weights.release()`, `releaseShard()`, or `dispose()`. Layer outputs are owned
|
|
951
|
+
by the caller.
|
|
952
|
+
|
|
953
|
+
## Concrete graphs
|
|
954
|
+
|
|
955
|
+
Import a graph directly, provide its weights and configuration, and own its state:
|
|
956
|
+
|
|
957
|
+
See [explicit forward passes and state ownership](examples/qwen3-forward.ts).
|
|
958
|
+
Run `bun packages/inference/examples/qwen3-forward.ts <checkpoint-directory> "[1,2,3]"`
|
|
959
|
+
with token IDs from your checkpoint’s tokenizer. The example forwards the prompt,
|
|
960
|
+
selects a token, continues with the same cache, and disposes cache before weights.
|
|
961
|
+
|
|
962
|
+
The direct graph imports currently include `models/gemma4`,
|
|
963
|
+
`models/gemma4/graphs`, `models/minicpm5`, `models/qwen3`, `models/qwen3-moe`,
|
|
964
|
+
`models/qwen3_5`, `models/qwen38-27b-trellis-m4pro`, `models/qwen38-27b-trellis-m1max`,
|
|
965
|
+
and `models/universal`.
|
|
966
|
+
`models/glm52`, `models/diffusion-gemma`, and `models/whisper` provide the other
|
|
967
|
+
graph families.
|
|
968
|
+
`@mlx-bun/inference/models` exposes the profile/implementation registry, the
|
|
969
|
+
composition resolver and loading helpers: `createModel` loads the graph
|
|
970
|
+
its family's selection table (`MLX_GRAPH_TABLES`) recommends for the composition,
|
|
971
|
+
else the family's generic graph. Direct graph
|
|
972
|
+
constructors remain available. Runtimes
|
|
973
|
+
that plan memory before opening weights (streamed experts) are opened with
|
|
974
|
+
`openPlannedRuntime` from generic `RuntimeOpenOptions` and return a generic
|
|
975
|
+
`MemoryPlan` and telemetry; `planRuntimeMemory` (`models/memory-plan`, native-free)
|
|
976
|
+
returns the plan from artifact headers, or null for models without one. A graph
|
|
977
|
+
that carries its own draft head declares it (`GraphCapabilities.nativeDraft`), and
|
|
978
|
+
`DraftProviderRegistry.native(graph)` builds the provider.
|
|
979
|
+
|
|
980
|
+
`models/qwen38-27b-trellis-m4pro` and `models/qwen38-27b-trellis-m1max` are
|
|
981
|
+
separate written files for `mlx-bun/Qwen3.8-27B-Trellis-3.2bpw`. Each names all 64
|
|
982
|
+
layers and the literal projection and MLP forms for its row bindings. M4 Pro
|
|
983
|
+
keeps its factored row and matrix-unit kernels. M1 Max names its threadgroup
|
|
984
|
+
codebook forms at one row, codebook matrix-unit forms at two to eight rows,
|
|
985
|
+
wide matrix-unit forms at nine to 256 rows, and expanded GPU forms above that.
|
|
986
|
+
Its prompt projection forms also retain the measured dequantized matmuls.
|
|
987
|
+
Its one-row binding fuses the attention residual addition with the MLP's
|
|
988
|
+
normalization, the recurrent normalization with its float32 SiLU gate, and
|
|
989
|
+
the convolution's SiLU with Q/K normalization and scaling. These retain MLX's
|
|
990
|
+
intermediate dtype rounding and reduction order. The
|
|
991
|
+
[normalization tests](tests/kernels/normalization-fusion.test.ts) compare raw
|
|
992
|
+
and compiled forms bit for bit, including changing shapes and layer weights;
|
|
993
|
+
the [DeltaNet heads tests](tests/kernels/delta-heads.test.ts) cover the fused
|
|
994
|
+
activation, Q/K scales, V output, and strided convolution inputs.
|
|
995
|
+
|
|
996
|
+
The files declare the Neural Engine program ranges directly: M4 Pro uses
|
|
997
|
+
640..1024, 1025..1536 and 1537..2048 positions; M1 Max uses fifteen capacities
|
|
998
|
+
from 256 to 2048, beginning at 224 positions. The scheduler indexes those ranges
|
|
999
|
+
for one-sequence pieces, and installation compiles the listed programs. Each
|
|
1000
|
+
file also states its measured verification costs and draft-gate settings.
|
|
1001
|
+
|
|
1002
|
+
The loader recommends a file from its artifact, device and module-configuration
|
|
1003
|
+
keys. These are defaults: callers can explicitly choose a differently tuned file,
|
|
1004
|
+
a portable or device-tuned generic family graph, or supply an ordinary load function to `openModel`'s
|
|
1005
|
+
`graph` option. A custom function needs no family registration and returns the
|
|
1006
|
+
operations its caller needs, including a graph with prefill and scoring alone.
|
|
1007
|
+
The code-authoring rules do not restrict those choices.
|
|
1008
|
+
|
|
1009
|
+
For explicit state and tensor operations, compose the cache legos with
|
|
1010
|
+
`composeGraphCaches(graph, configuration)` or use the loader-bound `makeCache`.
|
|
1011
|
+
Use `MlxPhaseDispatch` to index prompt ranges and exact-row `decode`, `verify`
|
|
1012
|
+
and serving `logits` tables. Chunk-specific ranges preserve a file's distinct
|
|
1013
|
+
chunk and tail operations when it declares them. Known appends use whole-span ranges, preserving
|
|
1014
|
+
arbitrarily long accepted grammar spans without imposing the verifier's row cap.
|
|
1015
|
+
Scoring heads, media and denoising consumers index their own declared ranges. Each file declares verification costs and known-span operations
|
|
1016
|
+
where measured. `examples/qwen3-forward.ts` runs a prompt and a decode through
|
|
1017
|
+
these public components. Dispose each cache and returned array when finished.
|
|
1018
|
+
|
|
1019
|
+
Shared dense/quantized layers, activations, normalization, and RoPE live under
|
|
1020
|
+
`layers/`; architecture assembly lives under `models/<family>/`. DeltaNet
|
|
1021
|
+
kernels remain independently importable through `kernels/delta`.
|
|
1022
|
+
|
|
1023
|
+
## Native expert I/O
|
|
1024
|
+
|
|
1025
|
+
GLM's expert I/O support is built from `native/expert-io.c` with
|
|
1026
|
+
`bun run --filter @mlx-bun/inference build:native`. The resulting dylib lives in
|
|
1027
|
+
`dist/native/` and is included in the package archive. `prepack` rejects missing
|
|
1028
|
+
artifacts. An existing build can be staged with `bun run stage:native <directory>`
|
|
1029
|
+
inside this package. Loading uses the bundled library, or the caller's explicit
|
|
1030
|
+
`libraryPath` / `MLX_BUN_EXPERT_IO_DYLIB` override.
|
|
1031
|
+
|
|
1032
|
+
`artifacts/experts` owns native I/O bindings, residency, and usage accounting;
|
|
1033
|
+
`artifacts/glm52` owns direct-container and quantized-weight loading. Numeric
|
|
1034
|
+
streamed expert kernels are independently available through `kernels/glm52`.
|
|
1035
|
+
|
|
1036
|
+
## State and attention
|
|
1037
|
+
|
|
1038
|
+
`@mlx-bun/inference/state` exposes the cache legos (plain, affine, rotating,
|
|
1039
|
+
TurboQuant, recurrent, paged and GLM's latent caches, each owning its storage and
|
|
1040
|
+
its read), their row layouts, KV maintenance, the cache service, cloning, and
|
|
1041
|
+
persistence. The caller creates and owns the state.
|
|
1042
|
+
`@mlx-bun/inference/contracts` holds the shared cache and ownership interfaces.
|
|
1043
|
+
A rotating cache's `temporalView()` is its live window: the newest
|
|
1044
|
+
`min(offset, maxSize)` positions in chronological order, including right after a
|
|
1045
|
+
multi-token write leaves the ring oversized. A request's own rotating cache and
|
|
1046
|
+
its batched, speculative and aligned layouts select the same window.
|
|
1047
|
+
A single solo rotating row adopted into a shared layout keeps its physical
|
|
1048
|
+
columns, ring phase and offset, so it attends over its keys in the solo cache's
|
|
1049
|
+
order and later writes land where the solo cache's would; several rows are
|
|
1050
|
+
aligned in temporal order behind their left padding. `extractRow`, like mlx-lm's
|
|
1051
|
+
`BatchRotatingKVCache.extract`, and persistence store a row in temporal order,
|
|
1052
|
+
so a restored wrapped ring starts a new physical phase: its continuation can
|
|
1053
|
+
differ from the live row's, in main as well. RAM and disk restores agreed in the
|
|
1054
|
+
checks below.
|
|
1055
|
+
|
|
1056
|
+
- `state/`: storage layout, positions, row membership, precision transitions,
|
|
1057
|
+
snapshots, and persistence. `persistence.worker.js` performs CPU disk I/O.
|
|
1058
|
+
Every storage a graph's caches (or their precision maintenance, or paging) produce
|
|
1059
|
+
declares its three row layouts together in `state/layout`'s table, each implementing
|
|
1060
|
+
the `BatchableCache` port: `ownedCacheLayout` for ordinary continuous decoding,
|
|
1061
|
+
`prefillCacheLayout` for prefill cohorts and `targetCacheLayout` for verification
|
|
1062
|
+
rows, which hold the speculative transaction (`SpeculativeRowLayout`). The
|
|
1063
|
+
[cache contract test](tests/state/cache-contract.test.ts) runs every lego through
|
|
1064
|
+
every read, these layouts, the transaction, snapshots and SSD. The
|
|
1065
|
+
batch group only calls `mergeRows`, `filterRows` and `extractRow` on it and names no
|
|
1066
|
+
storage family; the gate rejects `execution/` importing a concrete cache module.
|
|
1067
|
+
A request's own cache is a row source for its layout, not a batch: an adopted lone row is the
|
|
1068
|
+
first source of the layout's first `mergeRows`.
|
|
1069
|
+
- `layers/quantized-attention.ts`: the affine attention kernels (mlx-lm's unfused
|
|
1070
|
+
port, OptiQ's tiled port, the folded and grouped-heads forms). An affine lego
|
|
1071
|
+
takes its kernel set at construction (`state/affine-attention`: unfused or
|
|
1072
|
+
tiled-causal as the composition names, or the opt-in always-tiled set).
|
|
1073
|
+
- `layers/folded-attention.ts`: bf16 causal windows of up to eight queries that MLX's
|
|
1074
|
+
fused SDPA hands to its unfused fallback (more than 32 query vectors per KV head),
|
|
1075
|
+
with GQA folded into rows: that fallback bit for bit, each KV head's keys and values
|
|
1076
|
+
read once instead of once per query head. A bf16 storage reads its windows with the
|
|
1077
|
+
read its composition names (`KVCache.windowRead`: `sdpaRead`, or `foldedWindowRead`),
|
|
1078
|
+
and its row layouts and clones with the same.
|
|
1079
|
+
- `kernels/turboquant/`: packing, rotation, codebooks, and packed decode kernels.
|
|
1080
|
+
- `kernels/delta/gated.ts`: DeltaNet kernels; recurrent storage and its
|
|
1081
|
+
`recurDecode`/`recurWindow` live in `state/ssm.ts`. The training cache
|
|
1082
|
+
(`TrainingSSMCache`, the training forward) implements the same two calls with
|
|
1083
|
+
a backward attached that differentiates an ops recomputation of the recurrence.
|
|
1084
|
+
- `state/paged/`: the paged lego (`--paged-kv`) and its persistence codec.
|
|
1085
|
+
The numerical attention implementation lives in `kernels/attention/paged.ts`.
|
|
1086
|
+
|
|
1087
|
+
Public kernel imports are `@mlx-bun/inference/kernels/turboquant`,
|
|
1088
|
+
`@mlx-bun/inference/kernels/delta`, and
|
|
1089
|
+
`@mlx-bun/inference/kernels/attention/paged`. Paged state is available through
|
|
1090
|
+
`@mlx-bun/inference/state/paged`.
|
|
1091
|
+
|
|
1092
|
+
The opt-in [Gemma4 paged test](tests/parity/gemma4-paged.test.ts) takes
|
|
1093
|
+
`MLX_BUN_TEST_PAGED_MODEL` (any Gemma4 artifact) and places requests through the
|
|
1094
|
+
gateway binding's plan, state policy and execution group. Gathered bf16 pages must
|
|
1095
|
+
equal plain KV at B1 and B3 with blocks 16 and 256 (tokens, every sampled vector,
|
|
1096
|
+
each retired row's valid K/V), the bit-exact contract. Over bf16, KV4 and KV8
|
|
1097
|
+
pages it also requires the direct reader to serve decode, each call within 2^-4
|
|
1098
|
+
of the gathered reader's output scale over the same query and pages (Lab
|
|
1099
|
+
numerics; greedy tokens are reported, not compared, since that difference can
|
|
1100
|
+
flip a low-margin step), abort-versus-stop survivor identity with recovery on
|
|
1101
|
+
the drained group, and RAM then fresh-SSD-store reuse of pages of the same
|
|
1102
|
+
encoding and reader. It passes on Gemma4 E4B and 12B. Run it from the root with
|
|
1103
|
+
`MLX_BUN_TEST_PAGED_MODEL=/gemma4/snapshot bun --no-env-file test packages/inference/tests/parity/gemma4-paged.test.ts`.
|
|
1104
|
+
|
|
1105
|
+
## Source ownership
|
|
1106
|
+
|
|
1107
|
+
All kernel files below live under `src/kernels/trellis/`.
|
|
1108
|
+
|
|
1109
|
+
| File | Responsibility |
|
|
1110
|
+
| --- | --- |
|
|
1111
|
+
| `codebook.ts` | Shared 1MAD Metal helpers, host LUT, and decoder variant mapping |
|
|
1112
|
+
| `geometry.ts` | Trellis geometry and borrowed weight types |
|
|
1113
|
+
| `reduce.ts` | Axis-1 packed matvec and its shared-row variant |
|
|
1114
|
+
| `scatter.ts` | Axis-0 packed matvec, balanced/shared variants, and partial reduction |
|
|
1115
|
+
| `gate-up.ts` | Same-width fused gate/up SwiGLU |
|
|
1116
|
+
| `mixed-gate-up.ts` | Independent-width gate/up and its two activation tails |
|
|
1117
|
+
| `expand.ts` | General stored-matrix expansion and vector dispatch |
|
|
1118
|
+
| `vector-expand.ts` | Four-weight vector expansion |
|
|
1119
|
+
| `tiled-prefill.ts` | Axis-1 tiled prefill |
|
|
1120
|
+
| `splitk-prefill.ts` | Axis-0 split-K prefill and ordered reduction |
|
|
1121
|
+
| `wide-prefill.ts` | GemvWide-compatible prefill and hardware eligibility |
|
|
1122
|
+
| `index.ts` | Public Trellis imports |
|
|
1123
|
+
|
|
1124
|
+
Tests in `tests/kernels/` cover the host decoding reference, variant equivalence,
|
|
1125
|
+
activation tails, packing, strides, and existing prefill comparisons against MLX.
|
|
1126
|
+
The wide-prefill native comparison runs only on supported hardware.
|
|
1127
|
+
|
|
1128
|
+
The MLX runtime libraries belong to `@mlx-bun/mlx`; this package bundles its
|
|
1129
|
+
expert I/O and video helpers. Set up
|
|
1130
|
+
that package's native artifacts, then run `bun run typecheck` and `bun run test`
|
|
1131
|
+
from the repository root.
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
## Sampling, embeddings, and adapters
|
|
1135
|
+
|
|
1136
|
+
`@mlx-bun/inference/sampling` exposes `makeSampler`, `makeLogitsProcessors`,
|
|
1137
|
+
`makeStepSampler`, and the individual top-p/top-k/min-p/XTC and tone-curve
|
|
1138
|
+
operations. `sampling/types.ts` owns options; `filters.ts`, `processors.ts`,
|
|
1139
|
+
`hlg.ts`, and `curve.ts` own transformations; `step.ts` owns token history and
|
|
1140
|
+
per-step sampling; `extras.ts` owns captured log-probability readback and disposal.
|
|
1141
|
+
The caller supplies scores and chooses options. Returned arrays belong to the caller.
|
|
1142
|
+
|
|
1143
|
+
`sampling/grammar` compiles constraints against the caller's loaded tokenizer
|
|
1144
|
+
using the existing xgrammar dependency. Await `ready()`, apply the mask to logits,
|
|
1145
|
+
accept the sampled token, and dispose the controller when finished. The normalized
|
|
1146
|
+
argmax and token-bitmask kernels are exposed through `kernels/sampling`.
|
|
1147
|
+
|
|
1148
|
+
`embeddings` provides the pooled-embedding helpers: `embedOne`, `embedMany`,
|
|
1149
|
+
`embeddingTerminatorId` and `withInstruction`. Any graph declaring `embeddings` and
|
|
1150
|
+
providing `embedPooled` works; the pooling token comes from the graph's profile
|
|
1151
|
+
(`embeddingDeclarationFor`, resolved with the tokenizer). Supply the graph and tokenizer explicitly. `adapters`
|
|
1152
|
+
provides `AdapterManager` for loading and applying existing mlx-lm and PEFT LoRA
|
|
1153
|
+
artifacts to a caller-owned graph; adapter weight/state types are also exported.
|
|
1154
|
+
|
|
1155
|
+
## Generation
|
|
1156
|
+
|
|
1157
|
+
The [generation example](examples/qwen3-generate.ts) iterates over emitted tokens
|
|
1158
|
+
and returns the final generation statistics. It uses the same public generation
|
|
1159
|
+
API available through the root and `@mlx-bun/inference/generation`.
|
|
1160
|
+
|
|
1161
|
+
`generate` uses the graph supplied by the caller. `generateAutoregressive` accepts
|
|
1162
|
+
an explicit `MlxAutoregressiveBinding` from `generation/bindings/autoregressive`, including
|
|
1163
|
+
caller-defined graph operations, state construction, and optional compiled decode.
|
|
1164
|
+
`generateDenoising` accepts a denoising binding; `generation/diffusion` also exposes
|
|
1165
|
+
`denoiseSync` and `denoiseAsync` directly. No service or model selection is involved.
|
|
1166
|
+
|
|
1167
|
+
`generation/autoregressive.ts` owns prefill and token iteration;
|
|
1168
|
+
`generation/diffusion.ts` owns canvas denoising; `generation/result.ts` owns the
|
|
1169
|
+
async iterator and final stats. `generation/scopes.ts` owns adapter,
|
|
1170
|
+
wired-memory, and expert-usage lifetimes. Existing cancellation and early-return
|
|
1171
|
+
cleanup behavior is preserved. `generation/fill` exposes the existing optional
|
|
1172
|
+
fill session and proposal interfaces.
|
|
1173
|
+
|
|
1174
|
+
## Speech and vision
|
|
1175
|
+
|
|
1176
|
+
- `input/audio`: WAV parsing, AudioToolbox decoding, transcoding, mel features,
|
|
1177
|
+
and the existing Whisper tokenizer. `loadWhisperTokenizer` reads local HF
|
|
1178
|
+
artifacts; it does not download them.
|
|
1179
|
+
- `transcription`: Whisper decoding, long-form and streaming transcription,
|
|
1180
|
+
word timing, and text/SRT/VTT formatting. Callers supply the Whisper graph,
|
|
1181
|
+
tokenizer, and audio samples. `transcription/format` exposes the
|
|
1182
|
+
text/SRT/VTT/verbose-JSON formatters alone, without loading native MLX, for
|
|
1183
|
+
HTTP layers that only shape responses.
|
|
1184
|
+
- `models/audio/conformer`: the audio encoder graph. `transcription/silero-vad`:
|
|
1185
|
+
the voice-activity gate transcription runs before Whisper.
|
|
1186
|
+
- `input/vision`: image decoding/preprocessing, multimodal prompt assembly,
|
|
1187
|
+
and video frames. Qwen preprocessing and prompt assembly have separate
|
|
1188
|
+
`input/vision/qwen3vl` and `input/vision/qwen3vl-prompt` imports.
|
|
1189
|
+
- `models/vision/siglip`, `models/vision/qwen3vl`, and `models/vision/unified`:
|
|
1190
|
+
concrete vision encoders.
|
|
1191
|
+
|
|
1192
|
+
The AVFoundation frame extractor is built by `build:native` and bundled beside
|
|
1193
|
+
expert I/O in `dist/native`. It needs no runtime download or compilation.
|
|
1194
|
+
`MLX_BUN_FRAME_EXTRACT` remains an explicit override. AudioToolbox and `afconvert`
|
|
1195
|
+
use macOS system facilities. Optional encoder caching lives in `state/encoder-cache`;
|
|
1196
|
+
media fetching keeps the existing destination, size, and timeout controls.
|
|
1197
|
+
`MediaFetchPolicy.allowPrivate` permits LAN destinations; callers may supply
|
|
1198
|
+
`privateDestinationHint` when their UI needs a specific configuration instruction.
|
|
1199
|
+
|
|
1200
|
+
## Speculative generation
|
|
1201
|
+
|
|
1202
|
+
`bun --no-env-file scripts/bench-mtp.ts --help` describes the paired MTP
|
|
1203
|
+
performance runner. It consumes this package's production batch-one grouped
|
|
1204
|
+
method with local target and companion artifacts, pins their content, warms
|
|
1205
|
+
each proposal depth, and reverses variant order on alternating repetitions.
|
|
1206
|
+
Reports stay outside the checkout. It records accepted draft proposals and
|
|
1207
|
+
delivered tokens separately: depth two permits at most two accepted drafts,
|
|
1208
|
+
while a fully accepted round can deliver three tokens including its bonus.
|
|
1209
|
+
The optimization score is total delivered tokens divided by total step time;
|
|
1210
|
+
accepted drafts per step time, request/first-token/decode time, per-position
|
|
1211
|
+
acceptance, peak MLX memory and output token identity are also recorded.
|
|
1212
|
+
|
|
1213
|
+
Separate diagnostic runs report draft, verification, sampling and commit wall
|
|
1214
|
+
time. They disable device-first overlap and evaluate retained state through
|
|
1215
|
+
the target and Qwen MTP materialization hooks, including commit work that would
|
|
1216
|
+
otherwise execute in the next round. Their throughput is not a production
|
|
1217
|
+
performance estimate. These checks cover plain KV, greedy sampling and B1;
|
|
1218
|
+
kernel candidates still need their numerical/state gates and serving checks.
|
|
1219
|
+
|
|
1220
|
+
The standalone `scripts/bench/build-q4-native.sh` probe builds an offline Metal
|
|
1221
|
+
library and a native MLX `Primitive` with `output_shapes`, bypassing
|
|
1222
|
+
`fast.metal_kernel`'s shapeless limitation. It reads packed affine Q4 directly
|
|
1223
|
+
and shares decoded weights across verification rows. This is a lab experiment,
|
|
1224
|
+
not a runtime dispatch choice or a Trellis decoder. Its exact Q4-to-half and
|
|
1225
|
+
BF16-to-FP32 conversions do not establish reduction-order parity; the executable
|
|
1226
|
+
reports equality and error against MLX separately from projection timings.
|
|
1227
|
+
Build inputs must match the installed MLX version. The script's usage lists
|
|
1228
|
+
the required MLX source, Metal C++ headers, native libraries and external output
|
|
1229
|
+
directory. Offline compilation needs Xcode's Metal Toolchain component;
|
|
1230
|
+
`--runtime-compile` instead uses macOS's compiler through the native Metal
|
|
1231
|
+
backend, still without `fast.metal_kernel`. The probe checks all 16 Q4 codes
|
|
1232
|
+
and all 65,280 finite BF16 bit patterns before measuring projections. Append
|
|
1233
|
+
`mma` to the executable's arguments to measure its register-only matrix-product
|
|
1234
|
+
candidate instead of its shared-weight scalar reduction. Both are experiments;
|
|
1235
|
+
the current runtime dispatch is unchanged.
|
|
1236
|
+
|
|
1237
|
+
`generation/speculative` exposes the draft contract and the assistant, two-model,
|
|
1238
|
+
Qwen/GLM MTP, DFlash, DFlash 2, DeepSpec, and n-gram proposal providers. A provider
|
|
1239
|
+
drafts only through its grouped rows (`DraftProvider.grouped`), in the gateway's
|
|
1240
|
+
speculative group; one request is a group of one row, and there is no per-request
|
|
1241
|
+
draft source. DFlash 2 (`incoai/Qwen3.8-27B-DFlash2`) drafts a whole block in one pass from
|
|
1242
|
+
five tapped target layers, with
|
|
1243
|
+
its projections quantized at load to the width the registry passes (4 bits). `bindMlxGateway(model, { provider,
|
|
1244
|
+
numDraftTokens, adaptiveDepth })` drafts `numDraftTokens` every round, or with
|
|
1245
|
+
`adaptiveDepth` chooses each round's count (up to that, none included) from the decayed
|
|
1246
|
+
acceptance at each draft position and the graph's declared `verifyRoundCosts`, weighing a
|
|
1247
|
+
round of B sequences by the rows it verifies, B(k + 1), against a step without drafts over
|
|
1248
|
+
the same B rows (past the widest measured forward, at that forward's cost per row). The app
|
|
1249
|
+
passes `numDraftTokens` as the drafter's own width, or, for an adaptive drafter of a kind
|
|
1250
|
+
the graph lists in `adaptiveDraftWidths` (the M1 Max graph: an MTP head, 4), the width
|
|
1251
|
+
measured there; the composition's `draftDepth` records the drafter's own. A round
|
|
1252
|
+
without drafts is the graph's decode step, pipelined as plain decode is: built from the
|
|
1253
|
+
previous step's samples on the device before they are read, its input dropped again
|
|
1254
|
+
(`bindRowStepRollback`) if that sample ends its row. A run of such rounds probes at
|
|
1255
|
+
intervals that double while drafting keeps not paying: a drafter whose rows draft without
|
|
1256
|
+
committing (`preview`: the DFlash 2 block, or the Qwen MTP head's first draft under a round of
|
|
1257
|
+
its own cache that it rolls back) proposes from a step's input and the steps that follow compare
|
|
1258
|
+
its proposals with their samples; any other drafter verifies one draft. A graph whose composition measured them
|
|
1259
|
+
declares the gate's constants per drafter kind (`adaptiveDraftGates`: the prior's acceptance and weight, the decay per
|
|
1260
|
+
round and per step without drafts, the margin, the probe intervals and depth; the M1 Max graph, for DFlash 2 and the
|
|
1261
|
+
MTP head, counts that keep 95% per observing round and 93% per step without drafts, DFlash 2 probing one draft deep);
|
|
1262
|
+
other graphs and kinds keep the gate's defaults. A target graph whose residual stream
|
|
1263
|
+
carries a TurboQuant R1 fold declares it on its draft target (`residualBasis`: the R1 seed and the
|
|
1264
|
+
final-norm gain the fold moved into the head); the drafter loads unrotated and applies it when a draft group binds.
|
|
1265
|
+
A graph whose composition measured them declares the kernels of a drafter's own 4-bit projections per width class
|
|
1266
|
+
(`draftProjections`; the M1 Max graph: the affine row kernel at two to three rows, the split matrix-unit kernel from
|
|
1267
|
+
four, MLX's quantized matmul at one); the DFlash 2 drafter builds them when its first group binds and
|
|
1268
|
+
otherwise keeps its own rule (the simdgroup matrix unit at five to eight rows, MLX's quantized matmul otherwise).
|
|
1269
|
+
A graph whose composition measured it declares the target's frequency-ranked draft vocabulary on its draft head
|
|
1270
|
+
(`DraftProjection.vocabulary`: the M1 Max graph, the artifact's `mtp/draft_vocab.json`, those rows of its head on the
|
|
1271
|
+
head's own layers, gathered when a drafter first reads them); DFlash 2 then projects onto those rows and selects among
|
|
1272
|
+
them, the target still verifying over its full head.
|
|
1273
|
+
A library caller speculates as the app does: `binding.plan` with `hasDraft`, then
|
|
1274
|
+
`binding.methodRequest` and `submit` on `binding.createBatchGroup({ maxBatch: 1 })`
|
|
1275
|
+
(`@mlx-bun/training/dspark`'s calibration runs this way).
|
|
1276
|
+
`generation/speculative/binding` exports the target operations the groups bind
|
|
1277
|
+
(`bindSpeculativeTargetModel`); they do not require a concrete model class.
|
|
1278
|
+
Each request's draft selection is resolved when the binding binds the request and
|
|
1279
|
+
keys its group: argmax under the composition's `greedyDraft`, else the draft vocabulary
|
|
1280
|
+
the provider declares for the target (`draftVocabulary`, the Qwen MTP companion's
|
|
1281
|
+
`draft_vocab.json`) when the request's draft sampler reproduces on it, else each
|
|
1282
|
+
row's sampler over the full head. A drafter's rows bind one selection operation
|
|
1283
|
+
when they open and their draft loop calls it; the assistant and DeepSpec drafters
|
|
1284
|
+
bind their head's selection at construction the same way.
|
|
1285
|
+
`generation/speculative/loader` (a file of `models/speculative`) loads a `dspark.json`
|
|
1286
|
+
checkpoint (`loadDsparkDrafter`) and also exports the drafter class and its config types,
|
|
1287
|
+
which `@mlx-bun/training/dspark` uses to produce those checkpoints.
|
|
1288
|
+
|
|
1289
|
+
`DraftProviderRegistry` (`generation/speculative/draft-registry`) is how a server
|
|
1290
|
+
selects and loads a draft: each provider kind registers `detect(artifact)` (files
|
|
1291
|
+
only, never MLX) and `load(request)`, and `defaultDraftProviders()` registers the
|
|
1292
|
+
built-in kinds (`dspark`, `deepspec`, `assistant`, `mtp`, `two-model` as the
|
|
1293
|
+
catch-all, `ngram` by name only). An application uses the default, extends a copy
|
|
1294
|
+
with `register`, or hands `loadContext` a ready provider (`draftProvider`); it never
|
|
1295
|
+
names a provider class, and the architecture gate rejects one there. A graph whose
|
|
1296
|
+
checkpoint carries its own draft head declares it as `GraphCapabilities.nativeDraft`
|
|
1297
|
+
(GLM-5.2's MTP row); `registry.native(graph)` binds the registered provider.
|
|
1298
|
+
Providers read the target through capability-named ports on `TargetView`
|
|
1299
|
+
(`assistantRows`, `hiddenLayerTaps`, `recurrentMtp`) and refuse a target lacking
|
|
1300
|
+
one with `target graph does not provide <port>`.
|
|
1301
|
+
|
|
1302
|
+
Draft graphs live in `models/gemma4/assistant`, `models/qwen/mtp`, `models/glm52/mtp`,
|
|
1303
|
+
and `models/speculative/*`. Drafters are models: `models/drafters` holds their artifact
|
|
1304
|
+
conventions and `models/drafter-loaders` their lazy loaders (`loadAssistantDrafter`, `loadDeepspecDrafter`,
|
|
1305
|
+
`loadDsparkDrafter`, `loadQwenMtpModule`), and hands the sources the ports of
|
|
1306
|
+
`contracts/mlx/drafter`; a graph that declares `nativeDraft` also provides
|
|
1307
|
+
`nativeDraftHead()`. Sources and the packages above (quantization included) never name
|
|
1308
|
+
a drafter class. Proposal sources live in `generation/speculative/sources`;
|
|
1309
|
+
verification and acceptance belong to `generation/speculative`; batched draft work
|
|
1310
|
+
belongs to `generation/speculative/bindings`; draft checkpoints belong to `state/speculative`.
|
|
1311
|
+
Existing sampling, rejection, rollback, and kernel behavior is preserved.
|
|
1312
|
+
One intentional correction: grouped speculation maintains a reused target prefix
|
|
1313
|
+
before its first forward. A round without drafts maintains only before its append,
|
|
1314
|
+
so under a delayed KV scheme a stored prefix can still owe the conversion its last
|
|
1315
|
+
append reached. Main read that prefix unconverted in its first batched
|
|
1316
|
+
suffix chunk.
|
|
1317
|
+
|
|
1318
|
+
Grouped speculation binds any provider whose operations the target meets: batchable
|
|
1319
|
+
caches, row layouts for verification and rollback, and a forward that captures the
|
|
1320
|
+
hidden layers the provider taps. A provider whose rows tap target layers declares
|
|
1321
|
+
them with `targetTapLayers(target)` on its grouped provider; the binding resolves
|
|
1322
|
+
the list once for the bound target, rows opened for it must tap exactly that list,
|
|
1323
|
+
and placement refuses the provider when the target forward cannot capture those
|
|
1324
|
+
layers. The post-final-norm sentinel (index = layer count) is the forward output.
|
|
1325
|
+
|
|
1326
|
+
The [Qwen MTP group-prefix test](tests/parity/qwen-mtp-group-prefix.test.ts) takes
|
|
1327
|
+
`MLX_BUN_TEST_MTP_TARGET` and `MLX_BUN_TEST_MTP_DRAFT` (both or neither) and runs
|
|
1328
|
+
the Trellis target with its folded MTP draft through the gateway binding at B2,
|
|
1329
|
+
depth 2, plain KV and EOS disabled: two unequal prompts, the first retiring by a
|
|
1330
|
+
stop, an abort or a consumer failure, greedy and seeded. It observes every verify
|
|
1331
|
+
round and each row's committed target state, attributed to its request through
|
|
1332
|
+
joins and filters, and checks within this tree the exact committed inventory,
|
|
1333
|
+
accepted and rejected proposals while both rows are active, retirement outcomes,
|
|
1334
|
+
and that a stopped or finished request publishes one generated checkpoint equal
|
|
1335
|
+
to its terminal committed state while a cancelled or failed one publishes none.
|
|
1336
|
+
Both requests then continue from those checkpoints, twice from RAM and once in a
|
|
1337
|
+
fresh process from the flushed SSD store, with identical records. Main parity is
|
|
1338
|
+
external evidence; B>2, KV quantization, HTTP and performance are not covered.
|
|
1339
|
+
|
|
1340
|
+
The [grouped speculation test](tests/parity/speculative-group.test.ts) covers
|
|
1341
|
+
each built-in draft provider over its target with plain, affine, TurboQuant or
|
|
1342
|
+
per-layer KV. It takes `MLX_BUN_TEST_SPEC_TARGET`, `MLX_BUN_TEST_SPEC_KIND`
|
|
1343
|
+
(`ngram|two-model|assistant|mtp|dspark|deepspec|glm-mtp`) and
|
|
1344
|
+
`MLX_BUN_TEST_SPEC_DRAFT` (none for n-gram or `glm-mtp`), and optionally
|
|
1345
|
+
`MLX_BUN_TEST_SPEC_KV` (`bf16|4|8|turbo|config`),
|
|
1346
|
+
`MLX_BUN_TEST_SPEC_KV_START`, `MLX_BUN_TEST_SPEC_DEPTH`, `MLX_BUN_TEST_SPEC_WINDOW`
|
|
1347
|
+
(the custom sliding-window graph over a Llama-family target) and
|
|
1348
|
+
`MLX_BUN_TEST_SPEC_ADAPTER`. Providers load through `defaultDraftProviders()`, as
|
|
1349
|
+
the app's model host loads them. Requests go through placement, `methodRequest` and
|
|
1350
|
+
the binding's group: B1 repeating exactly, B4 cohorts that prefill together and repeat,
|
|
1351
|
+
stop/failure/abort retirement at exact counts, late and prefill joins, prompt
|
|
1352
|
+
snapshots with their draft attachment, generated prefixes restored from RAM and
|
|
1353
|
+
a fresh SSD store after a provider reload, and, with an adapter, a live adapter
|
|
1354
|
+
whose B2/B4 rows with the draft configured equal B1 controls, context
|
|
1355
|
+
partitioning, failure cleanup and adapter-isolated prefix reuse. Adapter rows
|
|
1356
|
+
speculate only through providers that support target adapters. It has run for
|
|
1357
|
+
n-gram, two-model and assistant providers; MTP, DSpark, DeepSpec and GLM
|
|
1358
|
+
native MTP have not.
|
|
1359
|
+
For example:
|
|
1360
|
+
`MLX_BUN_TEST_SPEC_TARGET=/gemma4-e4b MLX_BUN_TEST_SPEC_KIND=assistant MLX_BUN_TEST_SPEC_DRAFT=/e4b-assistant MLX_BUN_TEST_SPEC_KV=4 bun --no-env-file test packages/inference/tests/parity/speculative-group.test.ts`.
|
|
1361
|
+
|
|
1362
|
+
`glm-mtp` selects GLM-5.2's checkpoint-native MTP (`--mtp on`), mounted as the
|
|
1363
|
+
app's model host mounts it: the Colibri runtime opened with the MTP tier planned
|
|
1364
|
+
for one drafting lane, and the provider the graph declares
|
|
1365
|
+
(`GraphCapabilities.nativeDraft`) at the plan's draft depth unless `MLX_BUN_TEST_SPEC_DEPTH` overrides it. The same checks run as for the
|
|
1366
|
+
other providers, except the KV matrix: this test takes plain KV only and no custom
|
|
1367
|
+
window for this kind (the latent cache's affine, TurboQuant and paged legos are
|
|
1368
|
+
gated over synthetic weights by `tests/models/glm52/` and the cache contract test,
|
|
1369
|
+
not here):
|
|
1370
|
+
`MLX_BUN_TEST_SPEC_TARGET=/GLM-5.2 MLX_BUN_TEST_SPEC_KIND=glm-mtp bun --no-env-file test packages/inference/tests/parity/speculative-group.test.ts`.
|
|
1371
|
+
|
|
1372
|
+
`state` also exposes the byte-limited `PromptCache`, retention policies, row state,
|
|
1373
|
+
and checkpoint attachments. The caller owns cache lifetime and reuse namespaces.
|
|
1374
|
+
|
|
1375
|
+
## Optional execution and persistence
|
|
1376
|
+
|
|
1377
|
+
`execution` exposes `createInferenceEngine`, method adapters, cancellation,
|
|
1378
|
+
admission, and continuous batching. Supply an execution planner and its graph
|
|
1379
|
+
bindings; the engine manages in-process session lifetimes and bounded output.
|
|
1380
|
+
It opens no network listener. `createAutoregressiveMethod` and
|
|
1381
|
+
`createDenoisingMethod` adapt the direct methods when a consumer needs sessions;
|
|
1382
|
+
speculation runs only in the batch group. `MlxBatchExecutionGroup` owns batched rows;
|
|
1383
|
+
`ExecutionCoordinator` and `driveExecutionGroup` coordinate its work.
|
|
1384
|
+
|
|
1385
|
+
DiffusionGemma requests share that group through `execution/denoising-group`.
|
|
1386
|
+
This is interleaved execution, not stacked canvases: each row keeps its own
|
|
1387
|
+
encoder state, canvas, feedback and MLX key sequence, and the group advances one
|
|
1388
|
+
row by one bounded unit per iteration in round-robin order. The first unit is the
|
|
1389
|
+
whole prompt prefill and first canvas draw; each later unit is one denoising step.
|
|
1390
|
+
A row publishes only its finished result, and its decode time ends when that result
|
|
1391
|
+
is computed. Rows borrow one dequantized embedding table, which the group releases
|
|
1392
|
+
after the last row's run closes. Each unit runs with only its row's adapters
|
|
1393
|
+
active. An image request passes its pixels as the row's prefill input: only the
|
|
1394
|
+
first unit reads them (the vision encoder runs there), and the request owner
|
|
1395
|
+
releases them after the row settles. Grammar, draft, logprobs, logits processors,
|
|
1396
|
+
fill requests are refused with typed plan reasons; its caches are the legos the composition's KV scheme and paging name. `DenoisingKeys` reproduces MLX's
|
|
1397
|
+
global key sequence per request, so denoising never reads or reseeds the process
|
|
1398
|
+
key. The [denoising tests](tests/generation/denoising-binding.test.ts) take
|
|
1399
|
+
`MLX_BUN_DIFFUSION_MODEL=/cached/diffusiongemma` for the real-weight check and
|
|
1400
|
+
optionally `MLX_BUN_DIFFUSION_REFERENCE`, comma-separated trajectories in main's
|
|
1401
|
+
`goldens/diffusion/gen*.json` or `goldens/diffusion/vision.json` format. An image
|
|
1402
|
+
reference must have nonempty output and require more than one denoising step, so
|
|
1403
|
+
its cancellation check interrupts unfinished image work. With the measured main
|
|
1404
|
+
image reference, the real-weight check passed image cancellation beside a live
|
|
1405
|
+
text survivor and image/text reuse on the same group, preserving survivor and
|
|
1406
|
+
recovery tokens and checking state/table disposal calls and caller-owned pixels.
|
|
1407
|
+
The disposal checks do not establish allocator leak freedom. Separate paired B1
|
|
1408
|
+
HTTP acceptance against main passed image SSE disconnect after the first decoder
|
|
1409
|
+
step, server cancellation, prepared-pixel release, drain and same-server image/text
|
|
1410
|
+
recovery. Both clients received headers and the initial role frame, with no canvas
|
|
1411
|
+
tokens published before cancellation. Recovery tokens, messages, finish, usage and
|
|
1412
|
+
prepared inputs matched controls and main. Concurrent HTTP rows, full logits/KV
|
|
1413
|
+
planes, external-oracle parity and performance remain unqualified.
|
|
1414
|
+
|
|
1415
|
+
`execution/fit` estimates whether a model fits a machine at a context length:
|
|
1416
|
+
resident weights (bytes the caller supplies, such as a registry's), KV bytes from
|
|
1417
|
+
`state/kv-scheme`, and the prefill transient, against RAM × `WIRED_FRACTION` or an
|
|
1418
|
+
explicit budget. `fit` also solves the maximum safe context and predicts decode
|
|
1419
|
+
tokens per second from memory bandwidth; `skuMatrix` repeats it across Apple Silicon
|
|
1420
|
+
configurations. Estimates are advisory, and the entry imports without MLX. See the
|
|
1421
|
+
runnable [fit example](examples/fit-model.ts):
|
|
1422
|
+
`bun packages/inference/examples/fit-model.ts <checkpoint-directory> [context-tokens]`.
|
|
1423
|
+
|
|
1424
|
+
`state/cache-service` composes the RAM prompt cache, the KV budget, the saved-state
|
|
1425
|
+
store and paging into one service per loaded model,
|
|
1426
|
+
`createKvCacheService(graph, options)`, constructed with its budget (the RAM cap,
|
|
1427
|
+
`kvBudgetBytes`, a saved-state root with its shared byte budget, and the graph's
|
|
1428
|
+
paging): the batch group takes it as `cache` and only acquires a request's state
|
|
1429
|
+
(the prompt tokens it already covers, 0 for a miss), publishes state back,
|
|
1430
|
+
offers continuation checkpoints and releases the request; it never sees what is
|
|
1431
|
+
resident, evicted or saved. The service resumes a replayable generation under the
|
|
1432
|
+
key its continuation request names, with the request's sampler, pending token,
|
|
1433
|
+
adapter and cache identities. `report()` is what it holds. `SsdCacheStore`,
|
|
1434
|
+
`TieredPromptCache` and `SsdDurabilityCoordinator` remain for callers composing
|
|
1435
|
+
their own parts. Applications choose their own storage paths,
|
|
1436
|
+
capacity, scheduler settings, and shutdown lifecycle. A store's directory is
|
|
1437
|
+
`<dir>/<configFingerprint>/`; the app's fingerprint joins architecture, KV scheme,
|
|
1438
|
+
binding compatibility and the hub's resolved model-directory identity. HF snapshot
|
|
1439
|
+
revisions have separate directories; startup does not hash weights. If weights are
|
|
1440
|
+
replaced in place, clear saved KV for a fresh run. `scan()` skips, and
|
|
1441
|
+
never deletes, files whose header names another identity; only its own corrupt
|
|
1442
|
+
files and `.tmp` orphans are removed. Saved KV is disposable; changes to the
|
|
1443
|
+
store identity rebuild the cache.
|
|
1444
|
+
|
|
1445
|
+
## Scoring
|
|
1446
|
+
|
|
1447
|
+
`scoreDecisionAnswers` takes a structural `DecisionScoringGraph`, prompt tokens,
|
|
1448
|
+
and complete candidate token sequences. It teacher-forces each candidate, applies
|
|
1449
|
+
the head only to answer-predicting rows, and normalizes average response token
|
|
1450
|
+
log likelihoods across candidates. It returns `meanLogLikelihoods` and their
|
|
1451
|
+
softmax `probabilities`. No sampling or generation loop runs. Include the
|
|
1452
|
+
answer terminator in each candidate; it counts in the average. An optional `execute` callback schedules
|
|
1453
|
+
each candidate separately; direct consumers can run under their own graph lease.
|
|
1454
|
+
Prepared media uses the graph's `MlxPromptInput` to feed the supplied tokens at
|
|
1455
|
+
their positions. Every candidate owns fresh caches, released before it settles.
|
|
1456
|
+
|
|
1457
|
+
`sequenceLogProbabilities` and `logitsFromHiddenPadM` are the existing DPO
|
|
1458
|
+
response-mask reduction and DPO/ORPO head geometry, now owned here and consumed by
|
|
1459
|
+
training. The extraction preserves operation order and differentiability. API
|
|
1460
|
+
schemas, answer text encoding, confidence, and wire responses belong to the app.
|
|
1461
|
+
|
|
1462
|
+
`scoring` exposes `forwardSequence` / `forwardSequenceHidden` for full-sequence
|
|
1463
|
+
logits and hidden states, including the existing padded-batch masks. The original
|
|
1464
|
+
`trainForward` names remain aliases for compatibility. `evalPpl` computes
|
|
1465
|
+
perplexity over caller-provided token rows; `klPerToken` compares supplied logits.
|
|
1466
|
+
Neither requires an evaluation dataset registry or runner; the app's
|
|
1467
|
+
`mlx-bun perplexity` drives `evalPpl` over a local text/JSONL file. Tool-call
|
|
1468
|
+
parsing is available from `input`; template/schema fill compilation lives in
|
|
1469
|
+
`generation/fill`.
|
|
1470
|
+
|
|
1471
|
+
Standalone app bundles keep the expert-I/O library, the Neural Engine bridge
|
|
1472
|
+
and the frame extractor beside the executable. Explicit
|
|
1473
|
+
`MLX_BUN_EXPERT_IO_DYLIB` / `MLX_BUN_ANE_DYLIB` / `MLX_BUN_FRAME_EXTRACT`
|
|
1474
|
+
overrides take precedence; ordinary source/package execution resolves the
|
|
1475
|
+
package's `dist/native` directory. The app bundle verification covers relocation
|
|
1476
|
+
without loading native numerical code.
|