mlx-bun 0.2.0 → 0.4.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/README.md +71 -588
- package/docs/README.md +94 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
- package/docs/design/batching.md +1841 -0
- package/docs/design/decode-speed-program.md +2346 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +655 -334
- package/docs/design/kv-cache.md +824 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1990 -0
- package/docs/design/turboquant.md +2212 -0
- package/docs/design/unified-engine-frontier-plan.md +1833 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +50 -1
- package/docs/planning/release-notes-v0.4.0.md +45 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +3613 -0
- package/docs/reference/cli.md +434 -160
- package/docs/reference/distribution.md +328 -182
- package/docs/reference/environment.md +398 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +113 -6
- package/docs/reference/server-api.md +944 -429
- package/docs/reference/server-config.md +740 -338
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +158 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1790 -0
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +106 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +32 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +130 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +133 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill-cohort.ts +89 -0
- package/src/backends/mlx/prefill-rows.ts +200 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/request-state-policy.ts +41 -0
- package/src/backends/mlx/rollback.ts +89 -0
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +242 -0
- package/src/backends/mlx/speculative-group.ts +359 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +46 -0
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
- package/src/cli.ts +66 -58
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +60 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/prefix-cache.ts +23 -0
- package/src/contracts/resources.ts +25 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +45 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +53 -0
- package/src/engine/resources.ts +53 -0
- package/src/engine/scheduler.ts +72 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +30 -16
- package/src/generate.ts +709 -417
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-scheme.ts +70 -12
- package/src/kv-store.ts +177 -63
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
- package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +11 -11
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +20 -6
- package/src/model/batched-quant.ts +9 -3
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +78 -17
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +65 -23
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +93 -0
- package/src/model/compiled-decode.ts +8 -2
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +55 -0
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +179 -80
- package/src/model/gemma4.ts +40 -32
- package/src/model/generated/gemma4-12b.ts +6 -3
- package/src/model/generated/gemma4-26b.ts +6 -3
- package/src/model/generated/gemma4-e4b.ts +6 -3
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/implementation.ts +69 -0
- package/src/model/kv-attention-view.ts +67 -0
- package/src/model/minicpm5.ts +17 -15
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +202 -78
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/turboquant-codec.ts +83 -0
- package/src/model/universal/dense.ts +15 -14
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +110 -69
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +40 -29
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +183 -213
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +511 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +23 -18
- package/src/serve/request-prep.ts +344 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +335 -2711
- package/src/spec/assistant-rows.ts +92 -0
- package/src/spec/assistant-source.ts +31 -55
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +16 -14
- package/src/spec/dflash-source.ts +36 -12
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +97 -122
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +129 -172
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -137
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +93 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +219 -0
- package/src/spec/qwen-mtp-source.ts +147 -195
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +201 -201
- package/src/spec/source.ts +110 -13
- package/src/spec/two-model-rows.ts +61 -0
- package/src/spec/two-model.ts +44 -102
- package/src/ssd-cache.ts +99 -19
- package/src/ssd-durability.ts +36 -20
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +62 -60
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import { materializeCopy } from "../mlx/materialize";
|
|
3
|
+
import { withResource, disposeResources } from "../engine/resources";
|
|
4
|
+
import { leaseCacheState } from "../backends/mlx/state-views";
|
|
5
|
+
import { FullPrefillPadding } from "./full-prefill-padding";
|
|
6
|
+
import { KVCache, QuantizedKVCache, TurboQuantKVCache, type Cache, type Mask, type PrefillPadding } from "./gemma4-base";
|
|
7
|
+
|
|
8
|
+
type FullCache = KVCache | QuantizedKVCache | TurboQuantKVCache;
|
|
9
|
+
|
|
10
|
+
/** A physical full-attention row with separate valid-token coverage. Codecs
|
|
11
|
+
* consume the same physical tensors; padding never advances their threshold. */
|
|
12
|
+
export class FullPrefillRow implements Cache {
|
|
13
|
+
readonly padding = new FullPrefillPadding();
|
|
14
|
+
leftPadding = 0;
|
|
15
|
+
constructor(public inner: FullCache) {}
|
|
16
|
+
get minimumReusableOffset(): number { return (this.inner as Cache).minimumReusableOffset ?? 0; }
|
|
17
|
+
set minimumReusableOffset(value: number) { (this.inner as Cache).minimumReusableOffset = value; }
|
|
18
|
+
get positionOffset(): number { return this.inner.offset - this.leftPadding; }
|
|
19
|
+
get physicalLength(): number { return this.inner.offset; }
|
|
20
|
+
get offset(): number { return Math.max(0, this.padding.validOffset(this.positionOffset, 0)); }
|
|
21
|
+
get stateNeedsDispose(): boolean { return (this.inner as Cache).stateNeedsDispose ?? false; }
|
|
22
|
+
get affineConversion(): FullPrefillRow | undefined { return this.inner instanceof KVCache ? this : undefined; }
|
|
23
|
+
get turboConversion(): FullPrefillRow | undefined { return this.inner instanceof KVCache ? this : undefined; }
|
|
24
|
+
get quantizedAttention() { return (this.inner as Cache).quantizedAttention; }
|
|
25
|
+
get rotatedValueAttention() { return (this.inner as Cache).rotatedValueAttention; }
|
|
26
|
+
signature(): string { return this.inner.signature(); }
|
|
27
|
+
state(): MlxArray[] { return this.inner.state(); }
|
|
28
|
+
makeMask(tokens: number, window: number | null): Mask { return this.inner.makeMask(tokens, window); }
|
|
29
|
+
isTrimmable(): boolean { return true; }
|
|
30
|
+
trim(count: number): void { this.inner.trim(count); }
|
|
31
|
+
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.inner.updateAndFetch(k, v); }
|
|
32
|
+
bytesPerToken(): number { return (this.inner as Cache).bytesPerToken?.() ?? 0; }
|
|
33
|
+
preparePrefill(padding: PrefillPadding): void {
|
|
34
|
+
const positions = { rowOffsets: [this.positionOffset], leftPad: [this.leftPadding] };
|
|
35
|
+
this.padding.prepare(positions, padding);
|
|
36
|
+
this.leftPadding = positions.leftPad[0]!;
|
|
37
|
+
}
|
|
38
|
+
finalizePrefill(): void {
|
|
39
|
+
const positions = { rowOffsets: [this.positionOffset], leftPad: [this.leftPadding] };
|
|
40
|
+
const next = withResource(leaseCacheState(this.inner), state => this.padding.finalize(state, positions));
|
|
41
|
+
if (next) this.replace(next, this.inner.offset);
|
|
42
|
+
this.leftPadding = positions.leftPad[0]!;
|
|
43
|
+
}
|
|
44
|
+
removeLeftPadding(count: number): void {
|
|
45
|
+
if (!count) return;
|
|
46
|
+
const next: MlxArray[] = [];
|
|
47
|
+
try {
|
|
48
|
+
withResource(leaseCacheState(this.inner), state => {
|
|
49
|
+
for (const plane of state) next.push(plane.slice([0, 0, count, 0], [...plane.shape]));
|
|
50
|
+
});
|
|
51
|
+
this.replace(next, this.inner.offset - count); next.length = 0;
|
|
52
|
+
this.leftPadding -= count;
|
|
53
|
+
} finally { disposeResources(next); }
|
|
54
|
+
}
|
|
55
|
+
private restored(planes: MlxArray[], offset: number): FullCache {
|
|
56
|
+
const source = this.inner;
|
|
57
|
+
if (source instanceof KVCache) {
|
|
58
|
+
const result = new KVCache();
|
|
59
|
+
if (planes.length) result.restoreState(planes[0]!, planes[1]!, offset);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
if (source instanceof QuantizedKVCache) {
|
|
63
|
+
const result = new QuantizedKVCache(source.groupSize, source.bits);
|
|
64
|
+
if (planes.length) result.restoreState({ packed: planes[0]!, scales: planes[1]!, biases: planes[2]! },
|
|
65
|
+
{ packed: planes[3]!, scales: planes[4]!, biases: planes[5]! }, offset);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
const result = new TurboQuantKVCache(source.kBits, source.vBits);
|
|
69
|
+
if (planes.length) result.restoreState({ kIdx: planes[0]!, kScales: planes[1]!, kZeros: planes[2]!,
|
|
70
|
+
vPacked: planes[3]!, vScales: planes[4]! }, offset, source.headDim!);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
private replace(planes: MlxArray[], offset: number): void {
|
|
74
|
+
const next = this.restored(planes, offset), previous = this.inner;
|
|
75
|
+
(next as Cache).minimumReusableOffset = this.minimumReusableOffset;
|
|
76
|
+
this.inner = next; previous.dispose();
|
|
77
|
+
}
|
|
78
|
+
extract(): Cache {
|
|
79
|
+
const count = this.offset, planes: MlxArray[] = [];
|
|
80
|
+
try {
|
|
81
|
+
if (count) withResource(leaseCacheState(this.inner), state => {
|
|
82
|
+
for (const plane of state) {
|
|
83
|
+
using view = plane.slice([0, 0, this.leftPadding, 0], [1, plane.shape[1]!, this.leftPadding + count, plane.shape[3]!]);
|
|
84
|
+
planes.push(materializeCopy(view));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const result = this.restored(planes, count); planes.length = 0;
|
|
88
|
+
(result as Cache).minimumReusableOffset = this.minimumReusableOffset;
|
|
89
|
+
return result;
|
|
90
|
+
} finally { disposeResources(planes); }
|
|
91
|
+
}
|
|
92
|
+
toQuantized(groupSize: number, bits: number): FullPrefillRow {
|
|
93
|
+
this.inner = (this.inner as KVCache).toQuantized(groupSize, bits);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
toTurboQuantized(kBits: number, vBits: number): FullPrefillRow {
|
|
97
|
+
this.inner = TurboQuantKVCache.fromKVCache(this.inner as KVCache, kBits, vBits);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
dispose(): void { this.inner.dispose(); this.padding.clear(); }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function fullRowInner(row: Cache): Cache { return row instanceof FullPrefillRow ? row.inner : row; }
|
|
104
|
+
export function fullRowPadding(row: Cache): number { return row instanceof FullPrefillRow ? row.leftPadding : 0; }
|
|
105
|
+
export function fullRowPhysicalLength(row: Cache): number { return row instanceof FullPrefillRow ? row.physicalLength : row.offset; }
|
package/src/model/gemma4-base.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// gemma4-base — the config-INDEPENDENT machinery shared by every
|
|
2
|
-
// architecture variant (docs/
|
|
2
|
+
// architecture variant (docs/archive/investigations/optimization_plan.md Phase B: pure code
|
|
3
3
|
// movement out of the gemma4.ts monolith; every definition here is
|
|
4
4
|
// verbatim from it). Cache classes, quantized-KV SDPA, masks, the
|
|
5
5
|
// quantized primitives + LoRA machinery, and small graph helpers live
|
|
@@ -7,14 +7,19 @@
|
|
|
7
7
|
// DecoderLayer, Gemma4Model) stays in gemma4.ts and becomes the
|
|
8
8
|
// per-model generated code in Phase C.
|
|
9
9
|
|
|
10
|
+
import { decodedKvDonorAttention } from "./decoded-kv-donor";
|
|
10
11
|
import type { ModelConfig } from "../config";
|
|
11
12
|
import { quantFor } from "../config";
|
|
12
13
|
import type { Weights } from "../weights";
|
|
13
14
|
import { MlxArray } from "../mlx/array";
|
|
14
15
|
import { Dtype } from "../mlx/ffi";
|
|
15
16
|
import * as ops from "../mlx/ops";
|
|
17
|
+
import { quantizedMatmulRows } from "../mlx/quantized-rows";
|
|
16
18
|
import { expertOffloadArray } from "../expert-offload";
|
|
17
19
|
import * as tq from "../mlx/turboquant-ops";
|
|
20
|
+
import type { KvCodec } from "../backends/mlx/kv-codec";
|
|
21
|
+
import { TurboQuantCodec, disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
|
|
22
|
+
export { disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
|
|
18
23
|
import { runtimeValue } from "../runtime-config";
|
|
19
24
|
|
|
20
25
|
export type MaskMode = "" | "causal";
|
|
@@ -155,8 +160,10 @@ export class QuantizedLinear {
|
|
|
155
160
|
return this._scales.shape[0]!;
|
|
156
161
|
}
|
|
157
162
|
|
|
158
|
-
forward(x: MlxArray): MlxArray {
|
|
159
|
-
let out =
|
|
163
|
+
forward(x: MlxArray, independentRows = false): MlxArray {
|
|
164
|
+
let out = independentRows ? quantizedMatmulRows(
|
|
165
|
+
x, this._w, this._scales, this._biases, this._spec,
|
|
166
|
+
) : ops.quantizedMatmul(
|
|
160
167
|
x,
|
|
161
168
|
this._w,
|
|
162
169
|
this._scales,
|
|
@@ -246,9 +253,9 @@ export class QuantizedEmbedding {
|
|
|
246
253
|
|
|
247
254
|
/** Fetched KV handed from a donor layer to its sharers (and to the
|
|
248
255
|
* speculative drafter). Owned by forwardLayers for the pass.
|
|
249
|
-
* `offsetArr` is
|
|
250
|
-
*
|
|
251
|
-
*
|
|
256
|
+
* `offsetArr` is an owned pre-write position snapshot. Donor and sharing
|
|
257
|
+
* attention layers keep it until the fetched KV is released, even if the
|
|
258
|
+
* cache advances or replaces its borrowed row-position array. */
|
|
252
259
|
export type SharedKv =
|
|
253
260
|
| { kind: "plain"; keys: MlxArray; values: MlxArray; offset: number;
|
|
254
261
|
offsetArr?: MlxArray;
|
|
@@ -257,14 +264,95 @@ export type SharedKv =
|
|
|
257
264
|
* (tq.unrotateValues) after sdpa. Attention is linear in V, so the
|
|
258
265
|
* result is the same decode, just transformed once per query row
|
|
259
266
|
* instead of once per cached token. */
|
|
260
|
-
vRotated?: boolean
|
|
267
|
+
vRotated?: boolean;
|
|
268
|
+
restoreValues?: (output: MlxArray) => MlxArray }
|
|
261
269
|
| { kind: "quant"; keys: ops.QuantizedTensor; values: ops.QuantizedTensor;
|
|
262
|
-
offset: number; groupSize: number; bits: number; offsetArr?: MlxArray }
|
|
270
|
+
offset: number; groupSize: number; bits: number; offsetArr?: MlxArray }
|
|
271
|
+
| { kind: "view"; attention: KvAttentionView; offset: number; offsetArr?: MlxArray };
|
|
272
|
+
|
|
273
|
+
/** Retain an independent array handle for positions used across cache writes.
|
|
274
|
+
* The contiguous view preserves the integer values and shares their storage;
|
|
275
|
+
* callers release it with the fetched attention state. */
|
|
276
|
+
export function captureRopeOffsets(cache: Pick<Cache, "ropeOffsetArr">): MlxArray | undefined {
|
|
277
|
+
const offsets = cache.ropeOffsetArr;
|
|
278
|
+
return offsets ? ops.contiguous(offsets) : undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Quantized attention consumes a numerical storage port, independent of the
|
|
282
|
+
* concrete layout used for row positions, retention and persistence. */
|
|
283
|
+
export interface QuantizedAttentionState {
|
|
284
|
+
readonly groupSize: number;
|
|
285
|
+
readonly bits: number;
|
|
286
|
+
updateAndFetchQuantized(k: MlxArray, v: MlxArray): [ops.QuantizedTensor, ops.QuantizedTensor];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Values may stay in the codec's rotated domain until after attention. */
|
|
290
|
+
export interface RotatedValueAttentionState {
|
|
291
|
+
/** Capture a fetched value domain independently of later row changes. */
|
|
292
|
+
captureValueTransform?(): (output: MlxArray) => MlxArray;
|
|
293
|
+
updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** A captured numerical view supports multiple queries without appending KV
|
|
297
|
+
* again. It owns its tensor handles independently of later cache membership or
|
|
298
|
+
* precision changes. Query/mask inputs are borrowed; outputs are owned. */
|
|
299
|
+
export interface KvAttentionView {
|
|
300
|
+
attend(q: MlxArray, scale: number, mask: Mask): MlxArray;
|
|
301
|
+
dispose(): void;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Storage appends once and hands every attention consumer the same view. */
|
|
305
|
+
export interface KvAttentionState {
|
|
306
|
+
appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Owned read-only planes with validity separate from physical padding. */
|
|
310
|
+
export interface KvDonorRows {
|
|
311
|
+
readonly keys: MlxArray;
|
|
312
|
+
readonly values: MlxArray;
|
|
313
|
+
readonly offsets: readonly number[];
|
|
314
|
+
readonly starts: readonly number[];
|
|
315
|
+
readonly ends: readonly number[];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Captured Q-only attention hides plain or encoded donor storage. */
|
|
319
|
+
export interface KvDonorAttention extends KvAttentionView {
|
|
320
|
+
readonly width: number;
|
|
321
|
+
readonly dtype: Dtype;
|
|
322
|
+
readonly offsets: readonly number[];
|
|
323
|
+
readonly starts: readonly number[];
|
|
324
|
+
readonly ends: readonly number[];
|
|
325
|
+
}
|
|
263
326
|
|
|
264
327
|
export interface Cache {
|
|
328
|
+
captureDonorRows?(): KvDonorRows;
|
|
329
|
+
captureDonorAttention?(): KvDonorAttention;
|
|
330
|
+
/** A preparation method commits each request's own precision boundary.
|
|
331
|
+
* Splitting a forward for a sibling must not move that boundary. */
|
|
332
|
+
readonly prefillMaintenance?: {
|
|
333
|
+
beginPrefill(): void;
|
|
334
|
+
commitPrefill(rows: readonly number[]): void;
|
|
335
|
+
endPrefill(): void;
|
|
336
|
+
};
|
|
337
|
+
/** Optional representation-owned affine conversion. Logical position can
|
|
338
|
+
* differ from the physical write head in a padded rotating layout. */
|
|
339
|
+
readonly affineConversion?: { readonly offset: number; toQuantized(groupSize: number, bits: number): Cache };
|
|
340
|
+
readonly turboConversion?: { readonly offset: number; toTurboQuantized(kBits: number, vBits: number): Cache };
|
|
341
|
+
readonly attentionState?: KvAttentionState;
|
|
342
|
+
/** Earliest prefix whose retained representation can resume generation.
|
|
343
|
+
* Irreversible transitions may preserve bytes while invalidating older
|
|
344
|
+
* precision boundaries. Reuse policy must not trim below this offset. */
|
|
345
|
+
minimumReusableOffset?: number;
|
|
346
|
+
readonly rotatedValueAttention?: RotatedValueAttentionState;
|
|
347
|
+
readonly quantizedAttention?: QuantizedAttentionState;
|
|
265
348
|
offset: number;
|
|
266
|
-
/** Stable storage identity for compatibility guards and persistence.
|
|
267
|
-
|
|
349
|
+
/** Stable storage identity for compatibility guards and persistence.
|
|
350
|
+
* REQUIRED: every capability predicate (isPlainKvCache, isRotating*,
|
|
351
|
+
* kv-store codecs) is a string compare on this value, so a missing
|
|
352
|
+
* override used to fail OPEN into wrong routing (PRs #42/#43:
|
|
353
|
+
* BatchedRotatingCache shipped without one and batched joins dropped
|
|
354
|
+
* running rows). Wrappers report their own kind plus the inner kind. */
|
|
355
|
+
signature(): string;
|
|
268
356
|
/** Physical cache storage consumed by one additional token for one row.
|
|
269
357
|
* Recurrent caches return 0 because their state does not grow with the
|
|
270
358
|
* sequence. Optional for stateless/training adapters that are never
|
|
@@ -303,7 +391,7 @@ export interface Cache {
|
|
|
303
391
|
}
|
|
304
392
|
|
|
305
393
|
export function cacheSignature(cache: Cache | undefined): string {
|
|
306
|
-
return cache
|
|
394
|
+
return cache ? cache.signature() : "unknown";
|
|
307
395
|
}
|
|
308
396
|
|
|
309
397
|
export interface RowBatchCache extends Cache {
|
|
@@ -356,6 +444,19 @@ export interface BatchableCache extends Cache {
|
|
|
356
444
|
projectedBytes(tokens: number): number;
|
|
357
445
|
}
|
|
358
446
|
|
|
447
|
+
/** Padding is interpreted by cache geometry, independently of scheduling.
|
|
448
|
+
* Left padding initializes an empty group; lengths count input columns before
|
|
449
|
+
* trailing padding. Finalize before ordinary decode or publishing checkpoints. */
|
|
450
|
+
export interface PrefillPadding {
|
|
451
|
+
readonly leftPadding?: readonly number[];
|
|
452
|
+
readonly lengths: readonly number[];
|
|
453
|
+
readonly rightPadding?: readonly number[];
|
|
454
|
+
}
|
|
455
|
+
export interface PaddedPrefillCache extends Cache {
|
|
456
|
+
preparePrefill(padding: PrefillPadding): void;
|
|
457
|
+
finalizePrefill(): void;
|
|
458
|
+
}
|
|
459
|
+
|
|
359
460
|
export function isBatchableCache(cache: Cache): cache is BatchableCache {
|
|
360
461
|
const candidate = cache as Partial<BatchableCache>;
|
|
361
462
|
return (
|
|
@@ -479,6 +580,13 @@ export class KVCache implements Cache {
|
|
|
479
580
|
}
|
|
480
581
|
|
|
481
582
|
/** Chronological (K, V) view sliced to offset (drafter donor read). */
|
|
583
|
+
captureDonorRows(): KvDonorRows {
|
|
584
|
+
const [keys, values] = this.temporalView();
|
|
585
|
+
const B = keys.shape[0]!, width = keys.shape[2]!;
|
|
586
|
+
return { keys, values, offsets: Array(B).fill(this.offset),
|
|
587
|
+
starts: Array(B).fill(0), ends: Array(B).fill(width) };
|
|
588
|
+
}
|
|
589
|
+
|
|
482
590
|
temporalView(): [MlxArray, MlxArray] {
|
|
483
591
|
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
484
592
|
const [B, H, , D] = this.keys.shape as [number, number, number, number];
|
|
@@ -570,6 +678,7 @@ export class KVCache implements Cache {
|
|
|
570
678
|
* quantization is NYI upstream; sliding layers are window-capped
|
|
571
679
|
* anyway). Attention dispatches to quantizedSdpa for these. */
|
|
572
680
|
export class QuantizedKVCache implements Cache {
|
|
681
|
+
minimumReusableOffset?: number;
|
|
573
682
|
static readonly STEP = 256;
|
|
574
683
|
/** Set only by compiled-decode trace adapters (see Cache). */
|
|
575
684
|
readonly ropeOffsetArr?: MlxArray;
|
|
@@ -579,6 +688,8 @@ export class QuantizedKVCache implements Cache {
|
|
|
579
688
|
|
|
580
689
|
constructor(readonly groupSize: number, readonly bits: number) {}
|
|
581
690
|
|
|
691
|
+
get quantizedAttention(): QuantizedAttentionState { return this; }
|
|
692
|
+
|
|
582
693
|
signature(): string { return `kv:quant:${this.bits}:${this.groupSize}`; }
|
|
583
694
|
|
|
584
695
|
bytesPerToken(): number {
|
|
@@ -766,6 +877,16 @@ export class QuantizedKVCache implements Cache {
|
|
|
766
877
|
/** Chronological (K, V) triples sliced to offset — the quantized twin of
|
|
767
878
|
* KVCache.temporalView (batched merge/extend/filter reads). Caller owns
|
|
768
879
|
* the returned views. */
|
|
880
|
+
captureDonorAttention(): KvDonorAttention {
|
|
881
|
+
const [keys, values] = this.temporalView();
|
|
882
|
+
const B = keys.packed.shape[0]!, width = keys.packed.shape[2]!;
|
|
883
|
+
return { width, dtype: keys.scales.dtype, offsets: Array(B).fill(this.offset),
|
|
884
|
+
starts: Array(B).fill(0), ends: Array(B).fill(width),
|
|
885
|
+
attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, this.groupSize, this.bits),
|
|
886
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
|
|
769
890
|
temporalView(): [ops.QuantizedTensor, ops.QuantizedTensor] {
|
|
770
891
|
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
771
892
|
const cut = (t: ops.QuantizedTensor): ops.QuantizedTensor => {
|
|
@@ -790,6 +911,7 @@ export class QuantizedKVCache implements Cache {
|
|
|
790
911
|
* entries, so decode attends over at most the window. RoPE offsets use
|
|
791
912
|
* the true position; masks use the buffer-clamped offset. */
|
|
792
913
|
export class RotatingKVCache implements Cache {
|
|
914
|
+
declare minimumReusableOffset?: number;
|
|
793
915
|
static readonly STEP = 256;
|
|
794
916
|
/** Set only by compiled-decode trace adapters (see Cache). */
|
|
795
917
|
readonly ropeOffsetArr?: MlxArray;
|
|
@@ -1057,6 +1179,13 @@ export class RotatingKVCache implements Cache {
|
|
|
1057
1179
|
|
|
1058
1180
|
/** Chronological (K, V) view, valid length min(offset, maxSize)
|
|
1059
1181
|
* (port of optiq kv_view._read_cache_temporal). */
|
|
1182
|
+
captureDonorRows(): KvDonorRows {
|
|
1183
|
+
const [keys, values] = this.temporalView();
|
|
1184
|
+
const B = keys.shape[0]!, width = keys.shape[2]!;
|
|
1185
|
+
return { keys, values, offsets: Array(B).fill(this.offset),
|
|
1186
|
+
starts: Array(B).fill(0), ends: Array(B).fill(width) };
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1060
1189
|
temporalView(): [MlxArray, MlxArray] {
|
|
1061
1190
|
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
1062
1191
|
const tk = this.#temporalOrder(this.keys);
|
|
@@ -1125,6 +1254,8 @@ export const disposeTriple = (t: ops.QuantizedTensor): void => {
|
|
|
1125
1254
|
* + SDPA patches are unnecessary here: our SharedKv carries
|
|
1126
1255
|
* groupSize/bits through the donor→sharer plumbing explicitly. */
|
|
1127
1256
|
export class RotatingQuantizedKVCache implements Cache {
|
|
1257
|
+
get quantizedAttention(): QuantizedAttentionState { return this; }
|
|
1258
|
+
declare minimumReusableOffset?: number;
|
|
1128
1259
|
static readonly STEP = 256;
|
|
1129
1260
|
/** Set only by compiled-decode trace adapters (see Cache). */
|
|
1130
1261
|
readonly ropeOffsetArr?: MlxArray;
|
|
@@ -1382,6 +1513,16 @@ export class RotatingQuantizedKVCache implements Cache {
|
|
|
1382
1513
|
/** Chronological (K, V) triples cut to the valid window — the quantized
|
|
1383
1514
|
* twin of RotatingKVCache.temporalView (batched merge reads, Phase 3
|
|
1384
1515
|
* milestone 2). Caller owns the returned views. */
|
|
1516
|
+
captureDonorAttention(): KvDonorAttention {
|
|
1517
|
+
const [keys, values] = this.temporalView();
|
|
1518
|
+
const B = keys.packed.shape[0]!, width = keys.packed.shape[2]!;
|
|
1519
|
+
return { width, dtype: keys.scales.dtype, offsets: Array(B).fill(this.offset),
|
|
1520
|
+
starts: Array(B).fill(0), ends: Array(B).fill(width),
|
|
1521
|
+
attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, this.groupSize, this.bits),
|
|
1522
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1385
1526
|
temporalView(): [ops.QuantizedTensor, ops.QuantizedTensor] {
|
|
1386
1527
|
if (!this.keys || !this.values) throw new Error("cache is empty");
|
|
1387
1528
|
const valid = Math.min(this.offset, this.maxSize);
|
|
@@ -1496,29 +1637,7 @@ export class RotatingQuantizedKVCache implements Cache {
|
|
|
1496
1637
|
}
|
|
1497
1638
|
}
|
|
1498
1639
|
|
|
1499
|
-
/**
|
|
1500
|
-
* and toQuantized/fromKVCache pass around. Not `ops.QuantizedTensor`
|
|
1501
|
-
* (mlx's affine int4/int8 scheme): this is the rotation + Lloyd-Max
|
|
1502
|
-
* layout (docs/design/turboquant-kv.md), asymmetric-affine for keys,
|
|
1503
|
-
* FWHT+Lloyd-Max for values. Field order is the kv-store.ts tensor-slot
|
|
1504
|
-
* contract — do not reorder without updating snapshotCache/loadKvCache. */
|
|
1505
|
-
export interface TurboQuantTensor {
|
|
1506
|
-
kIdx: MlxArray;
|
|
1507
|
-
kScales: MlxArray;
|
|
1508
|
-
kZeros: MlxArray;
|
|
1509
|
-
vPacked: MlxArray;
|
|
1510
|
-
vScales: MlxArray;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
export const disposeTurboQuant = (t: TurboQuantTensor): void => {
|
|
1514
|
-
t.kIdx.dispose();
|
|
1515
|
-
t.kScales.dispose();
|
|
1516
|
-
t.kZeros.dispose();
|
|
1517
|
-
t.vPacked.dispose();
|
|
1518
|
-
t.vScales.dispose();
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
/** TurboQuant KV cache — v1 (docs/design/turboquant-kv.md): dequantize-
|
|
1640
|
+
/** TurboQuant KV cache — v1 (docs/design/turboquant.md): dequantize-
|
|
1522
1641
|
* on-fetch. Deliberately does NOT subclass KVCache/RotatingKVCache (or
|
|
1523
1642
|
* QuantizedKVCache) — a novel class fails every generated-file
|
|
1524
1643
|
* `#matches()` guard and every batching `instanceof` allow-list, so it
|
|
@@ -1537,6 +1656,7 @@ export const disposeTurboQuant = (t: TurboQuantTensor): void => {
|
|
|
1537
1656
|
* a full-window dequant every step; the deferred-InvFWHT trick is a
|
|
1538
1657
|
* documented non-goal until the quality gate passes. */
|
|
1539
1658
|
export class TurboQuantKVCache implements Cache {
|
|
1659
|
+
minimumReusableOffset = 0;
|
|
1540
1660
|
readonly stateNeedsDispose = true;
|
|
1541
1661
|
static readonly STEP = 256;
|
|
1542
1662
|
/** Set only by compiled-decode trace adapters (see Cache) — TurboQuant
|
|
@@ -1548,9 +1668,23 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1548
1668
|
offset = 0;
|
|
1549
1669
|
/** Validated lazily on first update, once head_dim is known. */
|
|
1550
1670
|
#headDim: number | null = null;
|
|
1671
|
+
/** Experimental operation selection, captured once for this cache. */
|
|
1672
|
+
readonly #codec: KvCodec<TurboQuantTensor>;
|
|
1551
1673
|
|
|
1552
|
-
constructor(readonly kBits: number, readonly vBits: number) {
|
|
1674
|
+
constructor(readonly kBits: number, readonly vBits: number) {
|
|
1675
|
+
this.#codec = new TurboQuantCodec(kBits, vBits, process.env.MLX_BUN_TURBOQUANT_FUSED_DECODE === "1");
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
captureDonorRows(): KvDonorRows {
|
|
1679
|
+
if (!this.#kv || this.#headDim === null) throw new Error("cache is empty");
|
|
1680
|
+
const [keys, values] = this.#decode(this.#kv, this.offset, this.#headDim, false);
|
|
1681
|
+
const B = keys.shape[0]!;
|
|
1682
|
+
return { keys, values, offsets: Array(B).fill(this.offset),
|
|
1683
|
+
starts: Array(B).fill(0), ends: Array(B).fill(this.offset) };
|
|
1684
|
+
}
|
|
1685
|
+
captureDonorAttention(): KvDonorAttention { return decodedKvDonorAttention(this.captureDonorRows()); }
|
|
1553
1686
|
|
|
1687
|
+
get rotatedValueAttention(): RotatedValueAttentionState { return this; }
|
|
1554
1688
|
signature(): string { return `kv:turboquant:${this.kBits}:${this.vBits}`; }
|
|
1555
1689
|
|
|
1556
1690
|
bytesPerToken(): number {
|
|
@@ -1602,48 +1736,9 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1602
1736
|
return this.#kv ? this.#kv.kIdx.shape[2]! : 0;
|
|
1603
1737
|
}
|
|
1604
1738
|
|
|
1605
|
-
|
|
1606
|
-
#encode(k: MlxArray, v: MlxArray): TurboQuantTensor {
|
|
1607
|
-
const signed = this.kBits === 8;
|
|
1608
|
-
const kEnc = tq.encodeKeys(k, this.kBits, signed);
|
|
1609
|
-
const kIdxPacked = this.kBits < 8 ? tq.packBits(kEnc.indices, this.kBits) : kEnc.indices;
|
|
1610
|
-
if (kIdxPacked !== kEnc.indices) kEnc.indices.dispose();
|
|
1611
|
-
const vEnc = tq.encodeValues(v, this.vBits);
|
|
1612
|
-
const vIdxPacked = this.vBits < 8 ? tq.packBits(vEnc.indices, this.vBits) : vEnc.indices;
|
|
1613
|
-
if (vIdxPacked !== vEnc.indices) vEnc.indices.dispose();
|
|
1614
|
-
return {
|
|
1615
|
-
kIdx: kIdxPacked, kScales: kEnc.scales, kZeros: kEnc.zeros,
|
|
1616
|
-
vPacked: vIdxPacked, vScales: vEnc.scales,
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
/** Decode a TurboQuantTensor's active [B,H,upTo,*] window back to
|
|
1621
|
-
* bf16 [B,H,upTo,headDim] (k, v). Caller owns the returned arrays.
|
|
1622
|
-
* deferV leaves V in the rotated domain (decodeValuesRotated) — the
|
|
1623
|
-
* caller un-rotates its attention output instead (tq.unrotateValues). */
|
|
1739
|
+
#encode(k: MlxArray, v: MlxArray): TurboQuantTensor { return this.#codec.encode(k, v); }
|
|
1624
1740
|
#decode(t: TurboQuantTensor, upTo: number, headDim: number, deferV = false): [MlxArray, MlxArray] {
|
|
1625
|
-
|
|
1626
|
-
const [B, H, , D] = a.shape as [number, number, number, number];
|
|
1627
|
-
return a.slice([0, 0, 0, 0], [B, H, upTo, D]);
|
|
1628
|
-
};
|
|
1629
|
-
const kIdxCut = cut(t.kIdx);
|
|
1630
|
-
const kIdxUnpacked = this.kBits < 8 ? tq.unpackBits(kIdxCut, this.kBits, headDim) : kIdxCut;
|
|
1631
|
-
if (kIdxUnpacked !== kIdxCut) kIdxCut.dispose();
|
|
1632
|
-
const kScalesCut = cut(t.kScales);
|
|
1633
|
-
const kZerosCut = cut(t.kZeros);
|
|
1634
|
-
const k = tq.decodeKeys(kIdxUnpacked, kScalesCut, kZerosCut);
|
|
1635
|
-
for (const a of [kIdxUnpacked, kScalesCut, kZerosCut]) a.dispose();
|
|
1636
|
-
|
|
1637
|
-
const vPackedCut = cut(t.vPacked);
|
|
1638
|
-
const vIdxUnpacked = this.vBits < 8 ? tq.unpackBits(vPackedCut, this.vBits, headDim) : vPackedCut;
|
|
1639
|
-
if (vIdxUnpacked !== vPackedCut) vPackedCut.dispose();
|
|
1640
|
-
const vScalesCut = cut(t.vScales);
|
|
1641
|
-
const v = deferV
|
|
1642
|
-
? tq.decodeValuesRotated(vIdxUnpacked, vScalesCut, this.vBits)
|
|
1643
|
-
: tq.decodeValues(vIdxUnpacked, vScalesCut, this.vBits);
|
|
1644
|
-
for (const a of [vIdxUnpacked, vScalesCut]) a.dispose();
|
|
1645
|
-
|
|
1646
|
-
return [k, v];
|
|
1741
|
+
return this.#codec.decode(t, upTo, headDim, deferV);
|
|
1647
1742
|
}
|
|
1648
1743
|
|
|
1649
1744
|
/** Quantize the newly-appended k/v, append into growable packed
|
|
@@ -1788,6 +1883,7 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1788
1883
|
q.#headDim = D;
|
|
1789
1884
|
}
|
|
1790
1885
|
q.offset = cache.offset;
|
|
1886
|
+
q.minimumReusableOffset = (cache as Cache).minimumReusableOffset ?? 0;
|
|
1791
1887
|
cache.dispose();
|
|
1792
1888
|
return q;
|
|
1793
1889
|
}
|
|
@@ -1796,6 +1892,7 @@ export class TurboQuantKVCache implements Cache {
|
|
|
1796
1892
|
if (this.#kv) disposeTurboQuant(this.#kv);
|
|
1797
1893
|
this.#kv = null;
|
|
1798
1894
|
this.offset = 0;
|
|
1895
|
+
this.minimumReusableOffset = 0;
|
|
1799
1896
|
}
|
|
1800
1897
|
}
|
|
1801
1898
|
|
|
@@ -1868,9 +1965,11 @@ export function quantizedSdpaUnfused(
|
|
|
1868
1965
|
}
|
|
1869
1966
|
}
|
|
1870
1967
|
if (maskArr) {
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1968
|
+
let masked: MlxArray;
|
|
1969
|
+
if (maskArr.dtype === Dtype.bool) {
|
|
1970
|
+
using ninf = ops.scalarLike(FINFO_MIN[scores.dtype] ?? -3.4e38, scores);
|
|
1971
|
+
masked = ops.where(maskArr, scores, ninf);
|
|
1972
|
+
} else masked = ops.add(scores, maskArr);
|
|
1874
1973
|
if (ownsMask) maskArr.dispose();
|
|
1875
1974
|
owned.push(masked);
|
|
1876
1975
|
scores = masked;
|
|
@@ -2080,7 +2179,7 @@ export function quantizedSdpaTiled(
|
|
|
2080
2179
|
function fusedSdpaSupported(q: MlxArray, mask: Mask, groupSize: number, bits: number): boolean {
|
|
2081
2180
|
// Escape hatch mirroring optiq serve's --no-fused-kv: forces the
|
|
2082
2181
|
// stock unfused path everywhere. Also the A/B lever for
|
|
2083
|
-
// scripts/bench-fused-prefill.
|
|
2182
|
+
// scripts/bench-levers.ts fused-prefill. Read per call (cheap next to the
|
|
2084
2183
|
// FFI work) so tests and paired A/B harnesses can flip it in-process.
|
|
2085
2184
|
if (runtimeValue("MLX_BUN_NO_FUSED_SDPA") === "1") return false;
|
|
2086
2185
|
if (bits !== 4 && bits !== 8) return false;
|