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,69 @@
|
|
|
1
|
+
import type { ModelConfig } from "../config";
|
|
2
|
+
import {
|
|
3
|
+
assertResolvedModelProfile,
|
|
4
|
+
type GenerationLoop,
|
|
5
|
+
type ModelGraph,
|
|
6
|
+
type ModelLoader,
|
|
7
|
+
type ResolvedModelProfile,
|
|
8
|
+
} from "./profile";
|
|
9
|
+
|
|
10
|
+
/** Engine code implements this port. Source and Model remain backend-specific;
|
|
11
|
+
* selection introduces no tensor conversion or dispatch in the token loop.
|
|
12
|
+
* create borrows source; a failed construction must release its own resources. */
|
|
13
|
+
export interface ModelImplementation<Source, Model> {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly graph: ModelGraph;
|
|
16
|
+
readonly loader: ModelLoader;
|
|
17
|
+
readonly loop: GenerationLoop;
|
|
18
|
+
create(source: Source, config: ModelConfig, profile: ResolvedModelProfile): Model;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ModelImplementationProvider<Source, Model> {
|
|
22
|
+
/** Validate identity and composition, then return one compatible binding.
|
|
23
|
+
* A declared implementation must never silently fall back to another. */
|
|
24
|
+
select(config: ModelConfig, profile: ResolvedModelProfile): ModelImplementation<Source, Model>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Immutable composition, with identity matching delegated to model profiles.
|
|
28
|
+
* Exact declarations are requirements: absent/incompatible code is an error,
|
|
29
|
+
* never an invitation to retry with a different numerical implementation. */
|
|
30
|
+
export class ModelImplementationRegistry<Source, Model> implements ModelImplementationProvider<Source, Model> {
|
|
31
|
+
private readonly entries: ReadonlyMap<string, ModelImplementation<Source, Model>>;
|
|
32
|
+
|
|
33
|
+
constructor(implementations: readonly ModelImplementation<Source, Model>[]) {
|
|
34
|
+
const entries = new Map<string, ModelImplementation<Source, Model>>();
|
|
35
|
+
for (const implementation of implementations) {
|
|
36
|
+
if (!implementation.id.trim()) throw new Error("model implementation id must not be empty");
|
|
37
|
+
if (entries.has(implementation.id))
|
|
38
|
+
throw new Error(`duplicate model implementation ${implementation.id}`);
|
|
39
|
+
entries.set(implementation.id, Object.freeze({
|
|
40
|
+
id: implementation.id,
|
|
41
|
+
graph: implementation.graph,
|
|
42
|
+
loader: implementation.loader,
|
|
43
|
+
loop: implementation.loop,
|
|
44
|
+
create: implementation.create.bind(implementation),
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
this.entries = entries;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Return a new composition; active models retain their existing binding. */
|
|
51
|
+
with(...implementations: readonly ModelImplementation<Source, Model>[]): ModelImplementationRegistry<Source, Model> {
|
|
52
|
+
return new ModelImplementationRegistry([...this.entries.values(), ...implementations]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
select(config: ModelConfig, profile: ResolvedModelProfile): ModelImplementation<Source, Model> {
|
|
56
|
+
assertResolvedModelProfile(config, profile);
|
|
57
|
+
const execution = profile.profile.execution;
|
|
58
|
+
// These default IDs preserve the existing dedicated/generated/family choice.
|
|
59
|
+
const id = execution.implementation ?? (execution.specialization === "generated"
|
|
60
|
+
? `${execution.graph}-generated` : execution.graph);
|
|
61
|
+
const implementation = this.entries.get(id);
|
|
62
|
+
if (!implementation)
|
|
63
|
+
throw new Error(`model profile ${profile.profile.id} requires unavailable implementation ${id}; refusing to fall back`);
|
|
64
|
+
if (implementation.graph !== execution.graph || implementation.loader !== execution.loader ||
|
|
65
|
+
implementation.loop !== execution.loop)
|
|
66
|
+
throw new Error(`model implementation ${id} is incompatible with profile ${profile.profile.id}; refusing to fall back`);
|
|
67
|
+
return implementation;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import * as ops from "../mlx/ops";
|
|
3
|
+
import type { Cache, KvAttentionView, KvDonorAttention } from "./gemma4-base";
|
|
4
|
+
import { quantizedSdpa, disposeTriple } from "./gemma4-base";
|
|
5
|
+
|
|
6
|
+
export function captureKvDonorAttention(cache: Cache): KvDonorAttention {
|
|
7
|
+
if (cache.captureDonorAttention) return cache.captureDonorAttention();
|
|
8
|
+
const view = cache.captureDonorRows!();
|
|
9
|
+
return {
|
|
10
|
+
width: view.keys.shape[2]!, dtype: view.keys.dtype,
|
|
11
|
+
offsets: view.offsets, starts: view.starts, ends: view.ends,
|
|
12
|
+
attend: (q, scale, mask) => ops.sdpa(q, view.keys, view.values, scale, mask.mode, mask.arr),
|
|
13
|
+
dispose() { view.keys.dispose(); view.values.dispose(); },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function quantizedDonorAttention(keys: ops.QuantizedTensor, values: ops.QuantizedTensor,
|
|
18
|
+
validity: Pick<KvDonorAttention, "offsets" | "starts" | "ends">,
|
|
19
|
+
groupSize: number, bits: number): KvDonorAttention {
|
|
20
|
+
return {
|
|
21
|
+
width: keys.packed.shape[2]!, dtype: keys.scales.dtype, ...validity,
|
|
22
|
+
attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, groupSize, bits),
|
|
23
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function captureKvAttention(cache: Cache, k: MlxArray, v: MlxArray): KvAttentionView {
|
|
28
|
+
if (cache.attentionState) return cache.attentionState.appendAndFetch(k, v);
|
|
29
|
+
const quantized = cache.quantizedAttention;
|
|
30
|
+
if (quantized) {
|
|
31
|
+
const { groupSize, bits } = quantized;
|
|
32
|
+
const [keys, values] = quantized.updateAndFetchQuantized(k, v);
|
|
33
|
+
return {
|
|
34
|
+
attend: (q, scale, mask) => quantizedSdpa(q, keys, values, scale, mask, groupSize, bits),
|
|
35
|
+
dispose() { disposeTriple(keys); disposeTriple(values); },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const [keys, values] = cache.updateAndFetch(k, v);
|
|
39
|
+
return {
|
|
40
|
+
attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
|
|
41
|
+
dispose() { keys.dispose(); values.dispose(); },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/** Takes ownership of already aligned per-row donor views. Each storage codec
|
|
47
|
+
* keeps its own attention arithmetic while the caller supplies one query batch. */
|
|
48
|
+
export function combineKvDonorAttention(rows: readonly KvDonorAttention[]): KvDonorAttention {
|
|
49
|
+
return { width: rows[0]!.width, dtype: rows[0]!.dtype,
|
|
50
|
+
offsets: rows.flatMap(row => [...row.offsets]), starts: rows.flatMap(row => [...row.starts]),
|
|
51
|
+
ends: rows.flatMap(row => [...row.ends]),
|
|
52
|
+
attend(q, scale, mask) {
|
|
53
|
+
const outputs: MlxArray[] = [];
|
|
54
|
+
try {
|
|
55
|
+
for (const [index, row] of rows.entries()) {
|
|
56
|
+
using query = q.slice([index, 0, 0, 0], [index + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
|
|
57
|
+
const from = mask.arr?.shape.map(() => 0), to = mask.arr ? [...mask.arr.shape] : undefined;
|
|
58
|
+
if (from && to && to.length === 4 && to[0]! > 1) { from[0] = index; to[0] = index + 1; }
|
|
59
|
+
using arr = mask.arr ? mask.arr.slice(from!, to!) : null;
|
|
60
|
+
outputs.push(row.attend(query, scale, { mode: mask.mode, arr }));
|
|
61
|
+
}
|
|
62
|
+
return outputs.length === 1 ? ops.contiguous(outputs[0]!) : ops.concatAxis(outputs, 0);
|
|
63
|
+
} finally { for (const output of outputs) output.dispose(); }
|
|
64
|
+
},
|
|
65
|
+
dispose() { for (const row of rows) row.dispose(); },
|
|
66
|
+
};
|
|
67
|
+
}
|
package/src/model/minicpm5.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
KVCache,
|
|
15
15
|
LoraState,
|
|
16
16
|
QuantizedKVCache,
|
|
17
|
-
TurboQuantKVCache,
|
|
18
17
|
QuantizedEmbedding,
|
|
19
18
|
QuantizedLinear,
|
|
20
19
|
quantizedSdpa,
|
|
@@ -30,7 +29,7 @@ import { runtimeValue } from "../runtime-config";
|
|
|
30
29
|
* `@partial(mx.compile, shapeless=True) def swiglu(gate, x): return nn.silu(gate) * x`
|
|
31
30
|
* mx.compile fuses sigmoid + mul + mul into ONE kernel (mlx-lm's decode
|
|
32
31
|
* `CV2ISigmoid…Multiply`) instead of our three separate dispatches. Traced
|
|
33
|
-
* once, replayed thereafter
|
|
32
|
+
* once, replayed thereafter for decode and prefill. */
|
|
34
33
|
let _swigluClosure: CompiledFunction | null = null;
|
|
35
34
|
export function compiledSwiglu(gate: MlxArray, up: MlxArray): MlxArray {
|
|
36
35
|
if (!_swigluClosure) {
|
|
@@ -143,23 +142,28 @@ export class LlamaAttention {
|
|
|
143
142
|
q = disposing(q, ropeStep(q));
|
|
144
143
|
k = disposing(k, ropeStep(k));
|
|
145
144
|
let attn: MlxArray;
|
|
146
|
-
if (cache
|
|
147
|
-
const
|
|
145
|
+
if (cache.attentionState) {
|
|
146
|
+
const view = cache.attentionState.appendAndFetch(k, v);
|
|
147
|
+
k.dispose(); v.dispose();
|
|
148
|
+
try { attn = view.attend(q, this.scale, mask); } finally { view.dispose(); }
|
|
149
|
+
} else if (cache.quantizedAttention) {
|
|
150
|
+
const quantized = cache.quantizedAttention;
|
|
151
|
+
const [keys, values] = quantized.updateAndFetchQuantized(k, v);
|
|
148
152
|
k.dispose();
|
|
149
153
|
v.dispose();
|
|
150
|
-
attn = quantizedSdpa(q, keys, values, this.scale, mask,
|
|
154
|
+
attn = quantizedSdpa(q, keys, values, this.scale, mask, quantized.groupSize, quantized.bits);
|
|
151
155
|
disposeTriple(keys);
|
|
152
156
|
disposeTriple(values);
|
|
153
|
-
} else if (cache
|
|
157
|
+
} else if (cache.rotatedValueAttention) {
|
|
154
158
|
// Deferred-V: sdpa runs on rotated values, then one InvFWHT on the
|
|
155
159
|
// output (linear in V) — see TurboQuantKVCache.updateAndFetchDeferredV.
|
|
156
|
-
const [keys, values] = cache.updateAndFetchDeferredV(k, v);
|
|
160
|
+
const [keys, values] = cache.rotatedValueAttention.updateAndFetchDeferredV(k, v);
|
|
157
161
|
k.dispose();
|
|
158
162
|
v.dispose();
|
|
159
163
|
const rotated = ops.sdpa(q, keys, values, this.scale, mask.mode, mask.arr);
|
|
160
164
|
keys.dispose();
|
|
161
165
|
values.dispose();
|
|
162
|
-
attn = tqUnrotateValues(rotated);
|
|
166
|
+
attn = cache.rotatedValueAttention.captureValueTransform?.()(rotated) ?? tqUnrotateValues(rotated);
|
|
163
167
|
rotated.dispose();
|
|
164
168
|
} else {
|
|
165
169
|
const [keys, values] = cache.updateAndFetch(k, v);
|
|
@@ -200,15 +204,13 @@ export class LlamaMLP {
|
|
|
200
204
|
|
|
201
205
|
/** MLP: returns mlp(x); the caller adds the residual when given. */
|
|
202
206
|
forward(x: MlxArray, residual?: MlxArray): MlxArray {
|
|
203
|
-
const H = x.shape[x.shape.length - 1]!;
|
|
204
|
-
const M = x.shape.reduce((a, b) => a * b, 1) / H;
|
|
205
207
|
const gate = this.gate.forward(x);
|
|
206
208
|
const up = this.up.forward(x);
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
// trace
|
|
209
|
+
// Match mlx_lm activations.py at every sequence length. Separate kernels
|
|
210
|
+
// differ numerically on MLX 0.32.2 at larger prefill shapes. An enclosing
|
|
211
|
+
// compiled trace owns fusion itself; the explicit opt-out stays available.
|
|
210
212
|
let hidden: MlxArray;
|
|
211
|
-
if (
|
|
213
|
+
if (!isCompiledTrace() && runtimeValue("MLX_BUN_COMPILED_SWIGLU") !== "0") {
|
|
212
214
|
hidden = compiledSwiglu(gate, up);
|
|
213
215
|
gate.dispose();
|
|
214
216
|
up.dispose();
|
|
@@ -321,7 +323,7 @@ export class MiniCPM5Model {
|
|
|
321
323
|
* NEVER disposed — the caller owns it (for segmented backward `h` is a leaf
|
|
322
324
|
* whose lifetime is managed by the autograd closure); intra-range
|
|
323
325
|
* intermediates ARE disposed. Building block for `forwardLayers` and the
|
|
324
|
-
* segmented-backward training path (docs/design/
|
|
326
|
+
* segmented-backward training path (docs/design/orpo-training.md).
|
|
325
327
|
* MiniCPM5 is plain full-attention with no KV-sharing, so each layer reads
|
|
326
328
|
* its own stateless `cache[i]` and a single causal mask. */
|
|
327
329
|
runLayerRange(h: MlxArray, aIdx: number, bIdx: number, cache: Cache[]): MlxArray {
|
package/src/model/profile.ts
CHANGED
|
@@ -58,6 +58,9 @@ export interface ModelExecutionComposition {
|
|
|
58
58
|
readonly graph: ModelGraph;
|
|
59
59
|
readonly loop: GenerationLoop;
|
|
60
60
|
readonly specialization: ModelSpecialization;
|
|
61
|
+
/** Engine-owned code registration. Model files never supply executable code.
|
|
62
|
+
* Omit to retain the existing graph implementation. */
|
|
63
|
+
readonly implementation?: string;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
/** A profile declares construction only. Request methods such as MTP, KV
|
|
@@ -323,6 +326,10 @@ function executionCapabilities(profile: ModelProfile): EngineCapability[] {
|
|
|
323
326
|
|
|
324
327
|
function validateProfile(profile: ModelProfile): void {
|
|
325
328
|
if (!profile.id) throw new Error("model profile id must not be empty");
|
|
329
|
+
if (profile.execution.implementation !== undefined &&
|
|
330
|
+
(!profile.execution.implementation.trim() || !profile.artifactFingerprint ||
|
|
331
|
+
profile.execution.specialization !== "artifact"))
|
|
332
|
+
throw new Error(`model profile ${profile.id} must bind a named implementation to an exact artifact`);
|
|
326
333
|
if ((profile.artifactFingerprint === undefined) !== (profile.configFingerprint === undefined))
|
|
327
334
|
throw new Error(
|
|
328
335
|
`model profile ${profile.id} must declare artifactFingerprint and configFingerprint together`,
|
|
@@ -413,6 +420,7 @@ export function assertResolvedModelProfile(
|
|
|
413
420
|
config: ModelConfig,
|
|
414
421
|
resolved: ResolvedModelProfile,
|
|
415
422
|
): void {
|
|
423
|
+
validateProfile(resolved.profile);
|
|
416
424
|
const fingerprint = configFingerprint(config);
|
|
417
425
|
if (resolved.artifact.configFingerprint !== fingerprint)
|
|
418
426
|
throw new Error(
|
|
@@ -424,8 +432,10 @@ export function assertResolvedModelProfile(
|
|
|
424
432
|
`model profile ${resolved.profile.id} selects ${resolved.profile.execution.graph}, ` +
|
|
425
433
|
`which is incompatible with ${config.modelType}`,
|
|
426
434
|
);
|
|
427
|
-
if (resolved.exactArtifact
|
|
428
|
-
resolved.
|
|
435
|
+
if (resolved.exactArtifact !== (resolved.profile.artifactFingerprint !== undefined) ||
|
|
436
|
+
(resolved.exactArtifact &&
|
|
437
|
+
(resolved.profile.artifactFingerprint !== resolved.artifact.fingerprint ||
|
|
438
|
+
resolved.profile.configFingerprint !== fingerprint)))
|
|
429
439
|
throw new Error(
|
|
430
440
|
`model profile ${resolved.profile.id} does not match artifact ` +
|
|
431
441
|
`${resolved.artifact.fingerprint ?? "<unidentified>"}`,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// MLX 0.32.2, mlx/backend/metal/scaled_dot_product_attention.cpp (MIT):
|
|
2
|
+
// https://github.com/ml-explore/mlx/blob/v0.32.2/mlx/backend/metal/scaled_dot_product_attention.cpp
|
|
3
|
+
// For the qualified Qwen GQA-6/D256 graph on applegpu_g16s, one-pass/vector
|
|
4
|
+
// selection and two-pass block counts change after these inclusive KV lengths.
|
|
5
|
+
// A committed span must stay within one arithmetic regime to match M=1.
|
|
6
|
+
const ATTENTION_BOUNDARIES = [1023, 1024, 8192, 32768, 65536];
|
|
7
|
+
|
|
8
|
+
export function qwenAppendChunkSize(cacheTokens: number): number {
|
|
9
|
+
for (const end of ATTENTION_BOUNDARIES)
|
|
10
|
+
if (cacheTokens < end) return Math.min(4, end - cacheTokens);
|
|
11
|
+
return 4;
|
|
12
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Experimental Qwen GDN convolution. Its two outputs are evaluated together:
|
|
2
|
+
// consuming the activation also materializes the small, independently owned
|
|
3
|
+
// state tail, without keeping a concatenated prefill buffer alive for it.
|
|
4
|
+
// Arithmetic follows MLX 0.31.2 depthwise_conv_1d and unary sigmoid (MIT,
|
|
5
|
+
// Copyright Apple Inc.), preserving the bf16 intermediate rounding.
|
|
6
|
+
import type { MlxArray } from "../mlx/array";
|
|
7
|
+
import { Dtype } from "../mlx/ffi";
|
|
8
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
9
|
+
|
|
10
|
+
/** Borrow all inputs; return owned activation and an independent state tail. */
|
|
11
|
+
export type QwenConvolution = (x: MlxArray, state: MlxArray, weight: MlxArray) => [MlxArray, MlxArray];
|
|
12
|
+
|
|
13
|
+
let kernel: MetalKernel | null = null;
|
|
14
|
+
export const fusedQwenConvolution: QwenConvolution = (x, state, weight) => {
|
|
15
|
+
const [B, M, D] = x.shape as [number, number, number];
|
|
16
|
+
const K = weight.shape[1]!;
|
|
17
|
+
if (x.shape.length !== 3 || weight.shape.length !== 3 || weight.shape[0] !== D ||
|
|
18
|
+
weight.shape[2] !== 1 || K !== 4 || state.shape.join() !== [B, K - 1, D].join() ||
|
|
19
|
+
![Dtype.bfloat16, Dtype.float32].includes(x.dtype) || state.dtype !== x.dtype || weight.dtype !== x.dtype)
|
|
20
|
+
throw new Error("fused Qwen convolution requires matching bf16/f32 inputs, [B,M,D], [B,3,D], [D,4,1]");
|
|
21
|
+
kernel ??= new MetalKernel({ name: "qwen_causal_conv_silu_tail",
|
|
22
|
+
inputNames: ["state", "x", "w"], outputNames: ["out", "tail"],
|
|
23
|
+
source: String.raw`
|
|
24
|
+
const uint c = thread_position_in_grid.x;
|
|
25
|
+
const uint t = thread_position_in_grid.y;
|
|
26
|
+
const uint b = thread_position_in_grid.z;
|
|
27
|
+
if (c >= D) return;
|
|
28
|
+
if (t < M) {
|
|
29
|
+
float acc = 0.0f;
|
|
30
|
+
for (uint i = 0; i < K; ++i) {
|
|
31
|
+
const uint row = t + i;
|
|
32
|
+
const T value = row < K-1 ? state[(b*(K-1)+row)*D+c] : x[(b*M+row-(K-1))*D+c];
|
|
33
|
+
acc += float(value) * float(w[c*K+i]);
|
|
34
|
+
}
|
|
35
|
+
const T conv = T(acc);
|
|
36
|
+
// Preserve the pinned MLX sigmoid's typed operations and bf16
|
|
37
|
+
// intermediate rounding. Promoting this expression to f32 differs.
|
|
38
|
+
const auto y = 1 / (1 + metal::exp(metal::abs(conv)));
|
|
39
|
+
const T sig = conv < 0 ? y : 1-y;
|
|
40
|
+
out[(b*M+t)*D+c] = conv * sig;
|
|
41
|
+
}
|
|
42
|
+
if (t < K-1) {
|
|
43
|
+
const uint row = M + t;
|
|
44
|
+
tail[(b*(K-1)+t)*D+c] = row < K-1 ? state[(b*(K-1)+row)*D+c] : x[(b*M+row-(K-1))*D+c];
|
|
45
|
+
}
|
|
46
|
+
` });
|
|
47
|
+
return kernel.apply([state, x, weight], {
|
|
48
|
+
outputs: [{ shape: [B, M, D], dtype: x.dtype }, { shape: [B, K - 1, D], dtype: x.dtype }],
|
|
49
|
+
grid: [D, Math.max(M, K - 1), B], threadGroup: [256, 1, 1],
|
|
50
|
+
templateDtypes: { T: x.dtype }, templateInts: { D, M, K },
|
|
51
|
+
}) as [MlxArray, MlxArray];
|
|
52
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { MlxArray } from "../mlx/array";
|
|
2
|
+
import { Dtype } from "../mlx/ffi";
|
|
3
|
+
import { MetalKernel } from "../mlx/metal-kernel";
|
|
4
|
+
import * as ops from "../mlx/ops";
|
|
5
|
+
import { computeG } from "./qwen3-delta";
|
|
6
|
+
|
|
7
|
+
// The same recurrent arithmetic as gated_delta_step, with no query/output
|
|
8
|
+
// calculation. Each row replays only its retained prefix of the verify window.
|
|
9
|
+
const SOURCE = String.raw`
|
|
10
|
+
const int T = Tin[0];
|
|
11
|
+
auto n = thread_position_in_grid.z;
|
|
12
|
+
auto b_idx = n / Hv;
|
|
13
|
+
auto hv_idx = n % Hv;
|
|
14
|
+
auto hk_idx = hv_idx / (Hv / Hk);
|
|
15
|
+
constexpr int n_per_t = Dk / 32;
|
|
16
|
+
auto dk_idx = thread_position_in_threadgroup.x;
|
|
17
|
+
auto dv_idx = thread_position_in_grid.y;
|
|
18
|
+
auto k_ = k + b_idx * T * Hk * Dk + hk_idx * Dk;
|
|
19
|
+
auto v_ = v + b_idx * T * Hv * Dv + hv_idx * Dv;
|
|
20
|
+
auto g_ = g + b_idx * T * Hv;
|
|
21
|
+
auto beta_ = beta + b_idx * T * Hv;
|
|
22
|
+
auto i_state = state_in + (n * Dv + dv_idx) * Dk;
|
|
23
|
+
auto o_state = state_out + (n * Dv + dv_idx) * Dk;
|
|
24
|
+
float state[n_per_t];
|
|
25
|
+
for (int i = 0; i < n_per_t; ++i)
|
|
26
|
+
state[i] = static_cast<float>(i_state[n_per_t * dk_idx + i]);
|
|
27
|
+
const int keep = lengths[b_idx];
|
|
28
|
+
for (int t = 0; t < keep; ++t) {
|
|
29
|
+
float kv_mem = 0.0f;
|
|
30
|
+
for (int i = 0; i < n_per_t; ++i) {
|
|
31
|
+
auto s_idx = n_per_t * dk_idx + i;
|
|
32
|
+
state[i] = state[i] * g_[hv_idx];
|
|
33
|
+
kv_mem += state[i] * k_[s_idx];
|
|
34
|
+
}
|
|
35
|
+
kv_mem = simd_sum(kv_mem);
|
|
36
|
+
auto delta = (v_[dv_idx] - kv_mem) * beta_[hv_idx];
|
|
37
|
+
for (int i = 0; i < n_per_t; ++i) {
|
|
38
|
+
auto s_idx = n_per_t * dk_idx + i;
|
|
39
|
+
state[i] = state[i] + k_[s_idx] * delta;
|
|
40
|
+
}
|
|
41
|
+
k_ += Hk * Dk;
|
|
42
|
+
v_ += Hv * Dv;
|
|
43
|
+
g_ += Hv;
|
|
44
|
+
beta_ += Hv;
|
|
45
|
+
}
|
|
46
|
+
for (int i = 0; i < n_per_t; ++i)
|
|
47
|
+
o_state[n_per_t * dk_idx + i] = state[i];
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
let kernel: MetalKernel | undefined;
|
|
51
|
+
|
|
52
|
+
/** State-only prefix replay. Inputs keep their original [B,T,...] strides;
|
|
53
|
+
* lengths are request-owned accepted counts, never scheduling decisions. */
|
|
54
|
+
export function gatedDeltaState(
|
|
55
|
+
k: MlxArray, v: MlxArray, a: MlxArray, b: MlxArray,
|
|
56
|
+
aLog: MlxArray, dtBias: MlxArray, state: MlxArray | null,
|
|
57
|
+
lengths: readonly number[],
|
|
58
|
+
): MlxArray {
|
|
59
|
+
const [B, T, Hk, Dk] = k.shape as [number, number, number, number];
|
|
60
|
+
const [, , Hv, Dv] = v.shape as [number, number, number, number];
|
|
61
|
+
using beta = ops.sigmoid(b);
|
|
62
|
+
using g = computeG(aLog, a, dtBias);
|
|
63
|
+
using zero = state ? null : ops.zeros([B, Hv, Dv, Dk], Dtype.float32);
|
|
64
|
+
using t = ops.fromInt32([T], [1]);
|
|
65
|
+
using kept = ops.fromInt32([...lengths], [B]);
|
|
66
|
+
kernel ??= new MetalKernel({ name: "gated_delta_prefix_state",
|
|
67
|
+
inputNames: ["k", "v", "g", "beta", "state_in", "Tin", "lengths"],
|
|
68
|
+
outputNames: ["state_out"], source: SOURCE, ensureRowContiguous: true });
|
|
69
|
+
return kernel.apply([k, v, g, beta, state ?? zero!, t, kept], {
|
|
70
|
+
outputs: [{ shape: [B, Hv, Dv, Dk], dtype: Dtype.float32 }],
|
|
71
|
+
grid: [32, Dv, B * Hv], threadGroup: [32, 4, 1],
|
|
72
|
+
templateInts: { Dk, Dv, Hk, Hv },
|
|
73
|
+
})[0]!;
|
|
74
|
+
}
|
package/src/model/qwen3-delta.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// default (use_kernel = not training), and its float accumulation / simd_sum
|
|
6
6
|
// reduction order differ from the pure-ops fallback — so BIT-EXACT parity with
|
|
7
7
|
// mlx-lm requires the SAME kernel, dispatched with the SAME grid/threadgroup.
|
|
8
|
-
// We port the non-vectorized
|
|
9
|
-
//
|
|
8
|
+
// We port the non-vectorized variant (g.ndim == 3). Padded prefill selects
|
|
9
|
+
// the oracle masked specialization; unpadded execution retains its kernel.
|
|
10
10
|
//
|
|
11
11
|
// Numerics (must match the reference dtypes exactly — mlx infers the kernel's
|
|
12
12
|
// pointer element types from the input arrays):
|
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
// GQA is handled inside the kernel (hk_idx = hv_idx / (Hv/Hk)); q/k stay at Hk.
|
|
21
21
|
|
|
22
22
|
import { MlxArray } from "../mlx/array";
|
|
23
|
+
import { materializeCopy } from "../mlx/materialize";
|
|
23
24
|
import { Dtype } from "../mlx/ffi";
|
|
24
25
|
import { MetalKernel } from "../mlx/metal-kernel";
|
|
25
26
|
import { CompiledFunction } from "../mlx/compile";
|
|
26
27
|
import * as ops from "../mlx/ops";
|
|
28
|
+
import type { SsmPrefillPadding } from "./ssm-prefill-padding";
|
|
27
29
|
import type { Cache, Mask } from "./gemma4-base";
|
|
28
30
|
|
|
29
31
|
// Verbatim body of mlx-lm's gated_delta_step (has_mask=False, vectorized=False),
|
|
@@ -104,7 +106,15 @@ const SOURCE = String.raw`
|
|
|
104
106
|
`;
|
|
105
107
|
|
|
106
108
|
let kernel: MetalKernel | null = null;
|
|
107
|
-
|
|
109
|
+
let maskedKernel: MetalKernel | null = null;
|
|
110
|
+
function getKernel(masked = false): MetalKernel {
|
|
111
|
+
if (masked) return maskedKernel ??= new MetalKernel({
|
|
112
|
+
name: "gated_delta_step_mask",
|
|
113
|
+
inputNames: ["q", "k", "v", "g", "beta", "state_in", "Tin", "mask"],
|
|
114
|
+
outputNames: ["y", "state_out"],
|
|
115
|
+
source: SOURCE.replace("if (true)", "if (mask[b_idx * T + t])"),
|
|
116
|
+
ensureRowContiguous: true,
|
|
117
|
+
});
|
|
108
118
|
if (!kernel)
|
|
109
119
|
kernel = new MetalKernel({
|
|
110
120
|
name: "gated_delta_step",
|
|
@@ -154,7 +164,7 @@ export function computeG(aLog: MlxArray, a: MlxArray, dtBias: MlxArray): MlxArra
|
|
|
154
164
|
* state: [B, Hv, Dv, Dk] f32 (or null → zeros) */
|
|
155
165
|
export function gatedDeltaUpdate(
|
|
156
166
|
q: MlxArray, k: MlxArray, v: MlxArray, a: MlxArray, b: MlxArray,
|
|
157
|
-
aLog: MlxArray, dtBias: MlxArray, state: MlxArray | null,
|
|
167
|
+
aLog: MlxArray, dtBias: MlxArray, state: MlxArray | null, mask: MlxArray | null = null,
|
|
158
168
|
): [MlxArray, MlxArray] {
|
|
159
169
|
const [B, , Hk, Dk] = q.shape as [number, number, number, number];
|
|
160
170
|
const [, , Hv, Dv] = v.shape as [number, number, number, number];
|
|
@@ -171,7 +181,9 @@ export function gatedDeltaUpdate(
|
|
|
171
181
|
|
|
172
182
|
const T = q.shape[1]!;
|
|
173
183
|
const tArr = MlxArray.fromInt32(new Int32Array([T]), [1]);
|
|
174
|
-
const
|
|
184
|
+
const inputs = [q, k, v, g, beta, stateIn, tArr];
|
|
185
|
+
if (mask) inputs.push(mask);
|
|
186
|
+
const [y, stateOut] = getKernel(mask !== null).apply(inputs, {
|
|
175
187
|
outputs: [
|
|
176
188
|
{ shape: [B, T, Hv, Dv], dtype: q.dtype },
|
|
177
189
|
{ shape: [B, Hv, Dv, Dk], dtype: Dtype.float32 },
|
|
@@ -207,6 +219,7 @@ export interface SsmSpecRound {
|
|
|
207
219
|
prevConv: MlxArray | null;
|
|
208
220
|
prevRecurrent: MlxArray | null;
|
|
209
221
|
prevOffset: number;
|
|
222
|
+
prevOffsets: number[] | null;
|
|
210
223
|
/** Pre-conv in_proj output [B,S,convDim] — position-local. */
|
|
211
224
|
qkv: MlxArray | null;
|
|
212
225
|
/** Gate/beta projections [B,S,Hv] — position-local. */
|
|
@@ -216,14 +229,15 @@ export interface SsmSpecRound {
|
|
|
216
229
|
S: number;
|
|
217
230
|
/** Layer-bound prefix replay: called AFTER the snapshot is restored onto
|
|
218
231
|
* the cache; advances conv/recurrent/offset by `keep` tokens. */
|
|
219
|
-
replay: ((cache: SSMCache, keep: number) => void) | null;
|
|
232
|
+
replay: ((cache: SSMCache, keep: number | readonly number[]) => void) | null;
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
export class SSMCache implements Cache {
|
|
223
236
|
conv: MlxArray | null = null;
|
|
224
237
|
recurrent: MlxArray | null = null;
|
|
225
238
|
offset = 0;
|
|
226
|
-
|
|
239
|
+
prefillPadding: SsmPrefillPadding | null = null;
|
|
240
|
+
/** Live speculative verify round; null outside a verify transaction. */
|
|
227
241
|
specRound: SsmSpecRound | null = null;
|
|
228
242
|
/** Per-row token coverage (batch lane only; null on serial B=1 caches,
|
|
229
243
|
* where `offset` IS the row's count). mlx-lm's ArraysCache tracks no
|
|
@@ -248,15 +262,13 @@ export class SSMCache implements Cache {
|
|
|
248
262
|
throw new Error("SSMCache has no KV updateAndFetch (gated-DeltaNet layer)");
|
|
249
263
|
}
|
|
250
264
|
|
|
251
|
-
makeMask(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
// batched decode feeds one real token per row — so unlike mlx-lm's
|
|
255
|
-
// left-padded batch-prefill there is never a pad position to mask.
|
|
256
|
-
return { mode: "", arr: null };
|
|
265
|
+
makeMask(N: number, _windowSize: number | null): Mask {
|
|
266
|
+
const arr = this.prefillPadding?.makeMask(N) ?? null;
|
|
267
|
+
return { mode: arr ? "array" : "", arr };
|
|
257
268
|
}
|
|
258
269
|
|
|
259
270
|
advance(n: number): void {
|
|
271
|
+
if (this.prefillPadding) { this.advanceRows(this.prefillPadding.advance(n)); return; }
|
|
260
272
|
this.offset += n;
|
|
261
273
|
// Batched rows step together (one forward advances every row), so the
|
|
262
274
|
// per-row counts move in lockstep; they DIFFER only in their merge-time
|
|
@@ -264,6 +276,13 @@ export class SSMCache implements Cache {
|
|
|
264
276
|
if (this.offsets) for (let i = 0; i < this.offsets.length; i++) this.offsets[i]! += n;
|
|
265
277
|
}
|
|
266
278
|
|
|
279
|
+
/** Methods may retain different verified prefixes for each active request. */
|
|
280
|
+
advanceRows(counts: readonly number[]): void {
|
|
281
|
+
if (!this.offsets) { this.offset += counts[0]!; return; }
|
|
282
|
+
for (let row = 0; row < this.offsets.length; row++) this.offsets[row]! += counts[row]!;
|
|
283
|
+
this.offset = Math.max(...this.offsets);
|
|
284
|
+
}
|
|
285
|
+
|
|
267
286
|
state(): MlxArray[] {
|
|
268
287
|
const out: MlxArray[] = [];
|
|
269
288
|
if (this.conv) out.push(this.conv);
|
|
@@ -279,7 +298,7 @@ export class SSMCache implements Cache {
|
|
|
279
298
|
throw new Error("SSMCache is not trimmable");
|
|
280
299
|
}
|
|
281
300
|
|
|
282
|
-
// --- speculative verify-round support
|
|
301
|
+
// --- speculative verify-round support -----------------------------------
|
|
283
302
|
// The serve loop arms a round before the verify forward and resolves it
|
|
284
303
|
// after the accept walk. The layer's forward, seeing an armed round, hands
|
|
285
304
|
// its replaced state slots to the round instead of disposing them, records
|
|
@@ -294,6 +313,7 @@ export class SSMCache implements Cache {
|
|
|
294
313
|
prevConv: null,
|
|
295
314
|
prevRecurrent: null,
|
|
296
315
|
prevOffset: this.offset,
|
|
316
|
+
prevOffsets: this.offsets ? [...this.offsets] : null,
|
|
297
317
|
qkv: null,
|
|
298
318
|
a: null,
|
|
299
319
|
b: null,
|
|
@@ -306,12 +326,12 @@ export class SSMCache implements Cache {
|
|
|
306
326
|
this.#dropSpecRound();
|
|
307
327
|
}
|
|
308
328
|
|
|
309
|
-
specRoundRollback(keep: number): void {
|
|
329
|
+
specRoundRollback(keep: number | readonly number[]): void {
|
|
310
330
|
const r = this.specRound;
|
|
311
331
|
if (!r) throw new Error("SSMCache.specRoundRollback without an armed round");
|
|
312
332
|
if (r.armed)
|
|
313
333
|
throw new Error("SSMCache.specRoundRollback before the verify forward recorded");
|
|
314
|
-
if (keep < 0 || keep > r.S)
|
|
334
|
+
if (typeof keep === "number" ? keep < 0 || keep > r.S : keep.some(count => count < 0 || count > r.S))
|
|
315
335
|
throw new Error(`SSMCache.specRoundRollback keep=${keep} outside window S=${r.S}`);
|
|
316
336
|
// Restore the pre-round snapshot (ownership moves back to the cache).
|
|
317
337
|
this.conv?.dispose();
|
|
@@ -321,9 +341,8 @@ export class SSMCache implements Cache {
|
|
|
321
341
|
r.prevConv = null;
|
|
322
342
|
r.prevRecurrent = null;
|
|
323
343
|
this.offset = r.prevOffset;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (keep > 0) {
|
|
344
|
+
this.offsets = r.prevOffsets;
|
|
345
|
+
if (typeof keep === "number" ? keep > 0 : keep.some(count => count > 0)) {
|
|
327
346
|
if (!r.replay) throw new Error("SSMCache.specRoundRollback with no recorded replay");
|
|
328
347
|
r.replay(this, keep); // advances conv/recurrent/offset by `keep`
|
|
329
348
|
}
|
|
@@ -347,6 +366,7 @@ export class SSMCache implements Cache {
|
|
|
347
366
|
this.recurrent?.dispose();
|
|
348
367
|
this.conv = null;
|
|
349
368
|
this.recurrent = null;
|
|
369
|
+
this.prefillPadding = null;
|
|
350
370
|
}
|
|
351
371
|
|
|
352
372
|
// --- batch-lane dynamic-B ops (BatchScheduler only) ----------------------
|
|
@@ -386,25 +406,30 @@ export class SSMCache implements Cache {
|
|
|
386
406
|
|
|
387
407
|
/** Evict rows not in `keep` (ascending row indices), in place. */
|
|
388
408
|
filter(keep: number[]): void {
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
409
|
+
if (this.conv && this.recurrent) {
|
|
410
|
+
const idx = ops.fromInt32(keep, [keep.length]);
|
|
411
|
+
const conv = ops.takeAxis(this.conv, idx, 0);
|
|
412
|
+
const recurrent = ops.takeAxis(this.recurrent, idx, 0);
|
|
413
|
+
idx.dispose();
|
|
414
|
+
this.conv.dispose();
|
|
415
|
+
this.recurrent.dispose();
|
|
416
|
+
this.conv = conv;
|
|
417
|
+
this.recurrent = recurrent;
|
|
418
|
+
}
|
|
419
|
+
this.prefillPadding?.filter(keep);
|
|
420
|
+
if (this.offsets) {
|
|
421
|
+
this.offsets = keep.map((i) => this.offsets![i]!);
|
|
422
|
+
this.offset = Math.max(0, ...this.offsets);
|
|
423
|
+
}
|
|
399
424
|
}
|
|
400
425
|
|
|
401
426
|
filterRows(keep: readonly number[]): void { this.filter([...keep]); }
|
|
402
427
|
|
|
403
428
|
/** Row `i` as a fresh SERIAL cache — port of mlx-lm ArraysCache.extract
|
|
404
429
|
* (cache.py:673-676: `cache.cache = [c[idx : idx + 1] for c in self.cache]`,
|
|
405
|
-
* a B-axis slice of every state slot).
|
|
406
|
-
*
|
|
407
|
-
*
|
|
430
|
+
* a B-axis slice of every state slot). A proper subset gets compact storage
|
|
431
|
+
* so retaining one row does not pin the other rows. A singleton retains an
|
|
432
|
+
* independent handle over its complete allocation. Offset =
|
|
408
433
|
* the row's OWN coverage, not the shared max. */
|
|
409
434
|
extractRow(i: number): SSMCache {
|
|
410
435
|
if (!this.conv || !this.recurrent)
|
|
@@ -414,10 +439,8 @@ export class SSMCache implements Cache {
|
|
|
414
439
|
lo[0] = i;
|
|
415
440
|
const hi = [...a.shape];
|
|
416
441
|
hi[0] = i + 1;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
view.dispose();
|
|
420
|
-
return own;
|
|
442
|
+
using view = a.slice(lo, hi);
|
|
443
|
+
return a.shape[0] === 1 ? ops.contiguous(view) : materializeCopy(view);
|
|
421
444
|
};
|
|
422
445
|
const out = new SSMCache();
|
|
423
446
|
out.conv = cut(this.conv);
|