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,93 @@
|
|
|
1
|
+
import { decodedKvDonorAttention } from "./decoded-kv-donor";
|
|
2
|
+
import type { MlxArray } from "../mlx/array";
|
|
3
|
+
import { KvTensorRows, type KvTensorRowView } from "../backends/mlx/kv-tensor-rows";
|
|
4
|
+
import { disposeResources } from "../engine/resources";
|
|
5
|
+
import { TurboQuantKVCache, type BatchableCache, type Cache, type Mask, type RotatedValueAttentionState, type PaddedPrefillCache, type PrefillPadding } from "./gemma4-base";
|
|
6
|
+
import { TurboQuantCodec, disposeTurboQuant, type TurboQuantTensor } from "./turboquant-codec";
|
|
7
|
+
|
|
8
|
+
const fields = ["kIdx", "kScales", "kZeros", "vPacked", "vScales"] as const;
|
|
9
|
+
function encoded(planes: readonly MlxArray[]): TurboQuantTensor {
|
|
10
|
+
return { kIdx: planes[0]!, kScales: planes[1]!, kZeros: planes[2]!, vPacked: planes[3]!, vScales: planes[4]! };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The existing TurboQuant codec over shared tensor-row storage. Row changes
|
|
14
|
+
* copy encoded bytes without decoding, rotating or requantizing their values. */
|
|
15
|
+
export class BatchedTurboQuantKVCache implements BatchableCache, PaddedPrefillCache {
|
|
16
|
+
readonly #storage = new KvTensorRows();
|
|
17
|
+
readonly #codec: TurboQuantCodec;
|
|
18
|
+
#reuseOffsets: number[] = [];
|
|
19
|
+
#headDim: number | null = null;
|
|
20
|
+
constructor(readonly kBits: number, readonly vBits: number) {
|
|
21
|
+
this.#codec = new TurboQuantCodec(kBits, vBits, process.env.MLX_BUN_TURBOQUANT_FUSED_DECODE === "1");
|
|
22
|
+
}
|
|
23
|
+
restorePrefillEnds(ends: readonly number[] | undefined): void { this.#storage.restorePrefillEnds(ends); }
|
|
24
|
+
preparePrefill(padding: PrefillPadding): void {
|
|
25
|
+
this.#storage.preparePrefill(padding);
|
|
26
|
+
if (!this.#reuseOffsets.length) this.#reuseOffsets = padding.lengths.map(() => 0);
|
|
27
|
+
}
|
|
28
|
+
finalizePrefill(): void { this.#storage.finalizePrefill(); }
|
|
29
|
+
captureDonorRows(): import("./gemma4-base").KvDonorRows {
|
|
30
|
+
if (!this.#storage.planes.length || this.#headDim === null) throw new Error("cache is empty");
|
|
31
|
+
const [keys, values] = this.#codec.decode(encoded(this.#storage.planes), this.offset, this.#headDim, false);
|
|
32
|
+
return { keys, values, ...this.#storage.captureDonorValidity() };
|
|
33
|
+
}
|
|
34
|
+
captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
|
|
35
|
+
return decodedKvDonorAttention(this.captureDonorRows());
|
|
36
|
+
}
|
|
37
|
+
get rotatedValueAttention(): RotatedValueAttentionState { return this; }
|
|
38
|
+
signature(): string { return `kv:batched-turboquant:${this.kBits}:${this.vBits}`; }
|
|
39
|
+
get minimumReusableOffset(): number { return Math.max(0, ...this.#reuseOffsets); }
|
|
40
|
+
get headDim(): number | null { return this.#headDim; }
|
|
41
|
+
get rowOffsets(): readonly number[] { return this.#storage.rowOffsets; }
|
|
42
|
+
get leftPad(): readonly number[] { return this.#storage.leftPad; }
|
|
43
|
+
get batchSize(): number | null { return this.#storage.batchSize; }
|
|
44
|
+
get offset(): number { return this.#storage.offset; }
|
|
45
|
+
get ropeOffsetArr(): MlxArray | undefined { return this.#storage.ropeOffsetArr; }
|
|
46
|
+
makeEmptyBatch(): BatchedTurboQuantKVCache { return new BatchedTurboQuantKVCache(this.kBits, this.vBits); }
|
|
47
|
+
bytesPerToken(): number { return this.#storage.bytesPerToken(); }
|
|
48
|
+
projectedBytes(tokens: number): number { return this.bytesPerToken() * tokens; }
|
|
49
|
+
makeMask(tokens: number, window: number | null): Mask { return this.#storage.makeMask(tokens, window); }
|
|
50
|
+
isTrimmable(): boolean { return true; }
|
|
51
|
+
trim(count: number): void { this.#storage.trim(count); }
|
|
52
|
+
specRoundBegin(): void { this.#storage.specRoundBegin(); }
|
|
53
|
+
specRoundCommit(): void { this.#storage.specRoundCommit(); }
|
|
54
|
+
specRoundRollback(keep: number | readonly number[]): void { this.#storage.specRoundRollback(keep); }
|
|
55
|
+
#append(k: MlxArray, v: MlxArray, defer: boolean): [MlxArray, MlxArray] {
|
|
56
|
+
const packed = this.#codec.encode(k, v);
|
|
57
|
+
try {
|
|
58
|
+
this.#storage.append(fields.map(field => packed[field]));
|
|
59
|
+
this.#headDim = k.shape[3]!;
|
|
60
|
+
return this.#codec.decode(encoded(this.#storage.planes), this.offset, this.#headDim, defer);
|
|
61
|
+
} finally { disposeTurboQuant(packed); }
|
|
62
|
+
}
|
|
63
|
+
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, false); }
|
|
64
|
+
updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, true); }
|
|
65
|
+
mergeRows(rows: readonly Cache[]): void {
|
|
66
|
+
const views: KvTensorRowView[] = [], held: MlxArray[] = [];
|
|
67
|
+
const reuseOffsets: number[] = [];
|
|
68
|
+
let dim = this.#headDim;
|
|
69
|
+
try {
|
|
70
|
+
for (const row of rows) {
|
|
71
|
+
if (row instanceof BatchedTurboQuantKVCache) {
|
|
72
|
+
views.push(row.#storage); dim ??= row.headDim; reuseOffsets.push(...row.#reuseOffsets);
|
|
73
|
+
} else if (row instanceof TurboQuantKVCache) {
|
|
74
|
+
const planes = row.state(); held.push(...planes); reuseOffsets.push(row.minimumReusableOffset);
|
|
75
|
+
views.push({ planes, rowOffsets: [row.offset], leftPad: [0] }); dim ??= row.headDim;
|
|
76
|
+
} else throw new Error(`TurboQuant row layout cannot merge ${row.signature()}`);
|
|
77
|
+
}
|
|
78
|
+
this.#storage.mergeRows(views); this.#headDim = dim; this.#reuseOffsets = reuseOffsets;
|
|
79
|
+
} finally { disposeResources(held); }
|
|
80
|
+
}
|
|
81
|
+
alignRows(leftPad: readonly number[]): void { this.#storage.alignRows(leftPad); }
|
|
82
|
+
filterRows(keep: readonly number[]): void {
|
|
83
|
+
this.#storage.filterRows(keep); this.#reuseOffsets = keep.map(row => this.#reuseOffsets[row]!);
|
|
84
|
+
}
|
|
85
|
+
state(): MlxArray[] { return this.#storage.planes; }
|
|
86
|
+
extractRow(row: number): TurboQuantKVCache {
|
|
87
|
+
const result = new TurboQuantKVCache(this.kBits, this.vBits), count = this.rowOffsets[row]!;
|
|
88
|
+
if (count) result.restoreState(encoded(this.#storage.extractRow(row)), count, this.#headDim!);
|
|
89
|
+
result.minimumReusableOffset = this.#reuseOffsets[row] ?? 0;
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
dispose(): void { this.#storage.dispose(); this.#headDim = null; this.#reuseOffsets = []; }
|
|
93
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compiled decode step (Phase A of docs/
|
|
1
|
+
// Compiled decode step (Phase A of docs/archive/investigations/optimization_plan.md).
|
|
2
2
|
//
|
|
3
3
|
// The single-token decode graph is identical token-to-token except for
|
|
4
4
|
// integer state (RoPE offset, cache write position, active length). This
|
|
@@ -277,7 +277,10 @@ function decodePhase(c: AnyCache): "concat" | "ring" {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
function disposeShared(s: SharedKv): void {
|
|
280
|
-
if (s.kind === "
|
|
280
|
+
if (s.kind === "view") {
|
|
281
|
+
s.attention.dispose();
|
|
282
|
+
s.offsetArr?.dispose();
|
|
283
|
+
} else if (s.kind === "plain") {
|
|
281
284
|
s.keys.dispose();
|
|
282
285
|
s.values.dispose();
|
|
283
286
|
} else {
|
|
@@ -381,6 +384,9 @@ export class CompiledDecode {
|
|
|
381
384
|
readonly #segmented: boolean;
|
|
382
385
|
|
|
383
386
|
private constructor(readonly model: Gemma4Model) {
|
|
387
|
+
// A fresh resume has no prefill to evaluate lazy FP32 constants. Capture
|
|
388
|
+
// their values, not their construction graph, in every compiled closure.
|
|
389
|
+
model.materializeGraphConstants();
|
|
384
390
|
const t = model.config.text;
|
|
385
391
|
this.#segmented =
|
|
386
392
|
t.numKvSharedLayers === 0 && !t.enableMoeBlock && model.perLayerWidth === 0;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as ops from "../mlx/ops";
|
|
2
|
+
import type { KvDonorAttention, KvDonorRows } from "./gemma4-base";
|
|
3
|
+
|
|
4
|
+
/** Takes ownership of decoded donor planes. Assistant attention uses decoded
|
|
5
|
+
* values, preserving its SDPA arithmetic rather than moving inverse rotation. */
|
|
6
|
+
export function decodedKvDonorAttention(view: KvDonorRows): KvDonorAttention {
|
|
7
|
+
const { keys, values, offsets, starts, ends } = view;
|
|
8
|
+
return { width: keys.shape[2]!, dtype: keys.dtype, offsets, starts, ends,
|
|
9
|
+
attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
|
|
10
|
+
dispose() { keys.dispose(); values.dispose(); },
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { captureFullKvDonorAttention } from "../backends/mlx/full-kv-row-donor";
|
|
2
|
+
import { appendFullKvRows } from "../backends/mlx/full-kv-row-append";
|
|
3
|
+
import { fullRowPadding, fullRowPhysicalLength } from "./full-prefill-row";
|
|
4
|
+
import { captureKvAttention } from "./kv-attention-view";
|
|
5
|
+
import type { MlxArray } from "../mlx/array";
|
|
6
|
+
import * as ops from "../mlx/ops";
|
|
7
|
+
import { FullTransitioningKvRows } from "../backends/mlx/full-transitioning-kv-rows";
|
|
8
|
+
import { BatchedQuantizedKVCache } from "./batched-quantized-kv";
|
|
9
|
+
import { QuantizedKVCache, type Cache, type Mask, type KvAttentionState, type KvAttentionView } from "./gemma4-base";
|
|
10
|
+
|
|
11
|
+
/** Affine storage keeps its native quantized-attention arithmetic while rows
|
|
12
|
+
* cross the conversion boundary independently. Once all rows convert, their
|
|
13
|
+
* packed planes use the existing batched attention implementation. */
|
|
14
|
+
export class DelayedQuantizedKVCache extends FullTransitioningKvRows<BatchedQuantizedKVCache> implements KvAttentionState {
|
|
15
|
+
constructor(readonly groupSize: number, readonly bits: number, readonly start: number,
|
|
16
|
+
readonly maintain: (rows: Cache[]) => void, row?: Cache) {
|
|
17
|
+
super({ signature: `kv:delayed-quant:${bits}:${groupSize}:${start}`, maintain,
|
|
18
|
+
converted: row => row instanceof QuantizedKVCache,
|
|
19
|
+
makeLayout: () => new BatchedQuantizedKVCache(groupSize, bits) }, row);
|
|
20
|
+
}
|
|
21
|
+
get attentionState(): KvAttentionState { return this; }
|
|
22
|
+
captureDonorAttention() {
|
|
23
|
+
return this.packed?.captureDonorAttention() ?? captureFullKvDonorAttention(this.rows, this.leftPad, this.offset);
|
|
24
|
+
}
|
|
25
|
+
makeEmptyBatch(): DelayedQuantizedKVCache { return new DelayedQuantizedKVCache(this.groupSize, this.bits, this.start, this.maintain); }
|
|
26
|
+
updateAndFetch(): [MlxArray, MlxArray] { throw new Error("mixed precision rows use their attention state"); }
|
|
27
|
+
appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView {
|
|
28
|
+
this.advance();
|
|
29
|
+
if (this.packed) return captureKvAttention(this.packed, k, v);
|
|
30
|
+
if (this.rows.every(row => !row.quantizedAttention)) {
|
|
31
|
+
const [keys, values] = appendFullKvRows(this.rows, this.leftPad, this.offset + k.shape[2]!, k, v);
|
|
32
|
+
try { this.syncPositions(true); } catch (error) { keys.dispose(); values.dispose(); throw error; }
|
|
33
|
+
return { attend: (q, scale, mask) => ops.sdpa(q, keys, values, scale, mask.mode, mask.arr),
|
|
34
|
+
dispose() { keys.dispose(); values.dispose(); } };
|
|
35
|
+
}
|
|
36
|
+
const views: KvAttentionView[] = [], pads = this.rows.map((row, index) => this.leftPad[index]! - fullRowPadding(row));
|
|
37
|
+
const lengths = this.rows.map(cache => fullRowPhysicalLength(cache) + k.shape[2]!);
|
|
38
|
+
try {
|
|
39
|
+
for (const [row, cache] of this.rows.entries()) {
|
|
40
|
+
const slice = (a: MlxArray) => a.slice([row, 0, 0, 0], [row + 1, a.shape[1]!, a.shape[2]!, a.shape[3]!]);
|
|
41
|
+
using kr = slice(k), vr = slice(v);
|
|
42
|
+
views.push(captureKvAttention(cache, kr, vr));
|
|
43
|
+
}
|
|
44
|
+
this.syncPositions(true);
|
|
45
|
+
} catch (error) { for (const view of views) view.dispose(); throw error; }
|
|
46
|
+
return {
|
|
47
|
+
attend(q, scale, mask) {
|
|
48
|
+
const outputs: MlxArray[] = [];
|
|
49
|
+
try {
|
|
50
|
+
for (const [row, view] of views.entries()) {
|
|
51
|
+
using qr = q.slice([row, 0, 0, 0], [row + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
|
|
52
|
+
// Capture alignment with the KV tensors, never consult live rows.
|
|
53
|
+
const start = mask.arr?.shape.map(() => 0), end = mask.arr ? [...mask.arr.shape] : undefined;
|
|
54
|
+
if (start && end) {
|
|
55
|
+
if (end.length === 4 && end[0]! > 1) { start[0] = row; end[0] = row + 1; }
|
|
56
|
+
start[start.length - 1] = pads[row]!;
|
|
57
|
+
end[end.length - 1] = pads[row]! + lengths[row]!;
|
|
58
|
+
}
|
|
59
|
+
using arr = mask.arr ? mask.arr.slice(start!, end!) : null;
|
|
60
|
+
outputs.push(view.attend(qr, 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 view of views) view.dispose(); },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
updateAndAttend(q: MlxArray, k: MlxArray, v: MlxArray, scale: number, mask: Mask): MlxArray {
|
|
69
|
+
const view = this.appendAndFetch(k, v);
|
|
70
|
+
try { return view.attend(q, scale, mask); } finally { view.dispose(); }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import * as ops from "../mlx/ops";
|
|
3
|
+
import { RotatingKVCache, type Cache, type KvAttentionState, type KvAttentionView, type PaddedPrefillCache, type PrefillPadding } from "./gemma4-base";
|
|
4
|
+
import { SpeculativeTransitioningKvRows } from "../backends/mlx/transitioning-kv-rows";
|
|
5
|
+
import { AlignedRotatingCache, alignRotatingRows, RotatingAffineLayout, SpeculativeRotatingAffineLayout, RotatingKvPositions } from "./rotating-kv-layout";
|
|
6
|
+
import { BatchedRotatingQuantCache } from "./batched-rotating-quant";
|
|
7
|
+
import { captureKvAttention, combineKvDonorAttention } from "./kv-attention-view";
|
|
8
|
+
|
|
9
|
+
/** Precision changes preserve each row's physical columns. The model owns
|
|
10
|
+
* queries and scale; the shared lifecycle owns membership and conversion. */
|
|
11
|
+
export class DelayedRotatingQuantizedKVCache extends SpeculativeTransitioningKvRows<RotatingAffineLayout> implements KvAttentionState, PaddedPrefillCache {
|
|
12
|
+
constructor(readonly maxSize: number, readonly groupSize: number, readonly bits: number, readonly start: number,
|
|
13
|
+
readonly maintain: (rows: Cache[]) => void, row?: Cache, readonly speculative = false) {
|
|
14
|
+
super({ signature: `kv:delayed-rotating-quant:${maxSize}:${bits}:${groupSize}:${start}`, maintain,
|
|
15
|
+
converted: row => row instanceof AlignedRotatingCache && row.inner instanceof BatchedRotatingQuantCache,
|
|
16
|
+
makeLayout: () => speculative ? new SpeculativeRotatingAffineLayout(maxSize, groupSize, bits) : new RotatingAffineLayout(maxSize, groupSize, bits), prepareRows: alignRotatingRows,
|
|
17
|
+
packRows: (layout, rows) => layout.adoptAlignedRows(rows as readonly AlignedRotatingCache[]),
|
|
18
|
+
extractRow: row => (row as AlignedRotatingCache).extract(speculative ? maxSize : undefined),
|
|
19
|
+
rollbackRow: (row, before, keep, preserve) => (row as AlignedRotatingCache).rollback(before, keep, preserve) }, row, new RotatingKvPositions(maxSize));
|
|
20
|
+
}
|
|
21
|
+
get attentionState(): KvAttentionState { return this; }
|
|
22
|
+
captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
|
|
23
|
+
if (this.packed) return this.packed.captureDonorAttention();
|
|
24
|
+
const views: import("./gemma4-base").KvDonorAttention[] = [];
|
|
25
|
+
try {
|
|
26
|
+
for (const row of this.rows) views.push((row as AlignedRotatingCache).captureDonorAttention());
|
|
27
|
+
return combineKvDonorAttention(views);
|
|
28
|
+
} catch (error) { for (const view of views) view.dispose(); throw error; }
|
|
29
|
+
}
|
|
30
|
+
override makeMask(tokens: number, window: number | null) {
|
|
31
|
+
if (this.speculative && this.leftPad.every(pad => pad === 0) &&
|
|
32
|
+
this.offset + tokens <= Math.min(this.maxSize, window ?? this.maxSize))
|
|
33
|
+
return { mode: tokens === 1 ? "" as const : "causal" as const, arr: null };
|
|
34
|
+
return super.makeMask(tokens, window);
|
|
35
|
+
}
|
|
36
|
+
preparePrefill(padding: PrefillPadding): void {
|
|
37
|
+
if (!this.batchSize) {
|
|
38
|
+
const rows = padding.lengths.map(() => new RotatingKVCache(this.maxSize));
|
|
39
|
+
try { this.mergeRows(rows); } finally { for (const row of rows) row.dispose(); }
|
|
40
|
+
}
|
|
41
|
+
if (this.packed) { this.packed.preparePrefill(padding); return; }
|
|
42
|
+
// Every physical row follows the same block geometry, even when only a
|
|
43
|
+
// sibling has right padding. The token lengths still belong to each row.
|
|
44
|
+
const rightPaddedBatch = padding.rightPadding?.some(pad => pad > 0);
|
|
45
|
+
for (const [row, cache] of this.rows.entries()) (cache as AlignedRotatingCache).inner.preparePrefill({
|
|
46
|
+
lengths: [padding.lengths[row]!],
|
|
47
|
+
...(padding.leftPadding ? { leftPadding: [padding.leftPadding[row]!] } : {}),
|
|
48
|
+
...(padding.rightPadding ? { rightPadding: [padding.rightPadding[row]!] } : {}),
|
|
49
|
+
}, rightPaddedBatch);
|
|
50
|
+
this.syncPositions(true);
|
|
51
|
+
}
|
|
52
|
+
finalizePrefill(): void {
|
|
53
|
+
if (this.packed) { this.packed.finalizePrefill(); return; }
|
|
54
|
+
for (const cache of this.rows) (cache as AlignedRotatingCache).inner.finalizePrefill();
|
|
55
|
+
this.syncPositions(true);
|
|
56
|
+
}
|
|
57
|
+
get maxTokens(): number { return this.maxSize; }
|
|
58
|
+
projectedBytes(tokens: number): number { return this.bytesPerToken() * Math.min(tokens, this.maxSize); }
|
|
59
|
+
makeEmptyBatch(): DelayedRotatingQuantizedKVCache {
|
|
60
|
+
return new DelayedRotatingQuantizedKVCache(this.maxSize, this.groupSize, this.bits, this.start, this.maintain, undefined, this.speculative);
|
|
61
|
+
}
|
|
62
|
+
updateAndFetch(): [MlxArray, MlxArray] { throw new Error("mixed precision rows use their attention state"); }
|
|
63
|
+
releaseRopeArr(): void { this.packed?.releaseRopeArr(); }
|
|
64
|
+
appendAndFetch(k: MlxArray, v: MlxArray): KvAttentionView {
|
|
65
|
+
this.advance();
|
|
66
|
+
if (this.packed) return captureKvAttention(this.packed, k, v);
|
|
67
|
+
const views: KvAttentionView[] = [];
|
|
68
|
+
try {
|
|
69
|
+
for (const [row, cache] of this.rows.entries()) {
|
|
70
|
+
const slice = (a: MlxArray) => a.slice([row, 0, 0, 0], [row + 1, a.shape[1]!, a.shape[2]!, a.shape[3]!]);
|
|
71
|
+
using kr = slice(k), vr = slice(v);
|
|
72
|
+
views.push(captureKvAttention(cache, kr, vr));
|
|
73
|
+
}
|
|
74
|
+
this.syncPositions(true);
|
|
75
|
+
} catch (error) { for (const view of views) view.dispose(); throw error; }
|
|
76
|
+
return {
|
|
77
|
+
attend(q, scale, mask) {
|
|
78
|
+
const outputs: MlxArray[] = [];
|
|
79
|
+
try {
|
|
80
|
+
for (const [row, view] of views.entries()) {
|
|
81
|
+
using qr = q.slice([row, 0, 0, 0], [row + 1, q.shape[1]!, q.shape[2]!, q.shape[3]!]);
|
|
82
|
+
const start = mask.arr?.shape.map(() => 0), end = mask.arr ? [...mask.arr.shape] : undefined;
|
|
83
|
+
if (start && end && end.length === 4 && end[0]! > 1) { start[0] = row; end[0] = row + 1; }
|
|
84
|
+
using arr = mask.arr ? mask.arr.slice(start!, end!) : null;
|
|
85
|
+
outputs.push(view.attend(qr, scale, { mode: mask.mode, arr }));
|
|
86
|
+
}
|
|
87
|
+
return outputs.length === 1 ? ops.contiguous(outputs[0]!) : ops.concatAxis(outputs, 0);
|
|
88
|
+
} finally { for (const output of outputs) output.dispose(); }
|
|
89
|
+
},
|
|
90
|
+
dispose() { for (const view of views) view.dispose(); },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { captureFullKvDonorRows } from "../backends/mlx/full-kv-row-donor";
|
|
2
|
+
import { decodedKvDonorAttention } from "./decoded-kv-donor";
|
|
3
|
+
import { appendFullKvRows } from "../backends/mlx/full-kv-row-append";
|
|
4
|
+
import type { MlxArray } from "../mlx/array";
|
|
5
|
+
import { Dtype } from "../mlx/ffi";
|
|
6
|
+
import * as ops from "../mlx/ops";
|
|
7
|
+
import { unrotateValues } from "../mlx/turboquant-ops";
|
|
8
|
+
import { type Cache, type RotatedValueAttentionState, TurboQuantKVCache } from "./gemma4-base";
|
|
9
|
+
import { BatchedTurboQuantKVCache } from "./batched-turboquant-kv";
|
|
10
|
+
import { FullTransitioningKvRows } from "../backends/mlx/full-transitioning-kv-rows";
|
|
11
|
+
|
|
12
|
+
/** TurboQuant tensor access and value-domain handling over the shared
|
|
13
|
+
* row-transition lifecycle. Model projections and attention remain batched. */
|
|
14
|
+
export class DelayedTurboQuantKVCache extends FullTransitioningKvRows<BatchedTurboQuantKVCache> implements RotatedValueAttentionState {
|
|
15
|
+
#rotated: boolean[] = [];
|
|
16
|
+
constructor(readonly kBits: number, readonly vBits: number, readonly start: number,
|
|
17
|
+
readonly maintain: (rows: Cache[]) => void, row?: Cache) {
|
|
18
|
+
super({ signature: `kv:delayed-turboquant:${kBits}:${vBits}:${start}`, maintain,
|
|
19
|
+
converted: row => row instanceof TurboQuantKVCache,
|
|
20
|
+
makeLayout: () => new BatchedTurboQuantKVCache(kBits, vBits) }, row);
|
|
21
|
+
}
|
|
22
|
+
captureDonorRows(): import("./gemma4-base").KvDonorRows {
|
|
23
|
+
return this.packed?.captureDonorRows() ?? captureFullKvDonorRows(this.rows, this.leftPad, this.offset);
|
|
24
|
+
}
|
|
25
|
+
captureDonorAttention(): import("./gemma4-base").KvDonorAttention {
|
|
26
|
+
return decodedKvDonorAttention(this.captureDonorRows());
|
|
27
|
+
}
|
|
28
|
+
get rotatedValueAttention(): RotatedValueAttentionState { return this; }
|
|
29
|
+
#append(k: MlxArray, v: MlxArray, deferred: boolean): [MlxArray, MlxArray] {
|
|
30
|
+
this.advance();
|
|
31
|
+
if (this.packed) {
|
|
32
|
+
this.#rotated = Array.from({ length: this.batchSize! }, () => true);
|
|
33
|
+
return deferred ? this.packed.updateAndFetchDeferredV(k, v) : this.packed.updateAndFetch(k, v);
|
|
34
|
+
}
|
|
35
|
+
this.#rotated = this.rows.map(row => !!row.rotatedValueAttention);
|
|
36
|
+
const result = appendFullKvRows(this.rows, this.leftPad, this.offset + k.shape[2]!, k, v, deferred);
|
|
37
|
+
try { this.syncPositions(true); } catch (error) { result[0].dispose(); result[1].dispose(); throw error; }
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, false); }
|
|
42
|
+
updateAndFetchDeferredV(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] { return this.#append(k, v, true); }
|
|
43
|
+
captureValueTransform(): (output: MlxArray) => MlxArray {
|
|
44
|
+
const rotated = [...this.#rotated];
|
|
45
|
+
if (rotated.every(value => value)) return unrotateValues;
|
|
46
|
+
if (rotated.every(value => !value)) return output => ops.contiguous(output);
|
|
47
|
+
return output => {
|
|
48
|
+
using restored = unrotateValues(output);
|
|
49
|
+
using flags = ops.fromInt32(rotated.map(value => Number(value)), [rotated.length, 1, 1, 1]);
|
|
50
|
+
using mask = flags.astype(Dtype.bool);
|
|
51
|
+
return ops.where(mask, restored, output);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
makeEmptyBatch(): DelayedTurboQuantKVCache { return new DelayedTurboQuantKVCache(this.kBits, this.vBits, this.start, this.maintain); }
|
|
55
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cleanupFailure, disposeResources } from "../engine/resources";
|
|
1
2
|
// DiffusionGemma-26B-A4B-it (model_type "diffusion_gemma") — the first
|
|
2
3
|
// NON-autoregressive model in the codebase. Instead of a left-to-right AR loop
|
|
3
4
|
// it runs an ENCODER prefill over the prompt (builds a KV cache) and then a
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
// Ported verbatim from optiq/vlm/_mlxvlm/models/diffusion_gemma/language.py.
|
|
11
12
|
// Reuses mlx-bun's quantized primitives (QuantizedLinear/Embedding/SwitchLinear,
|
|
12
13
|
// RMSNorm, KVCache/RotatingKVCache) from gemma4-base. Architecture deltas vs
|
|
13
|
-
// gemma4 (verified against the checkpoint, see docs/design/
|
|
14
|
+
// gemma4 (verified against the checkpoint, see docs/design/generic-model-support.md):
|
|
14
15
|
// - attention scale=1.0, NO attention softcap (only final-logit softcap 30 fp32)
|
|
15
16
|
// - QK/V-norm POST-proj / PRE-RoPE; v_norm is RMSNormNoScale; V gets no RoPE
|
|
16
17
|
// - full-attention layers (5,11,17,23,29) reuse k as v (no v_proj), hd=512 kv=2,
|
|
@@ -626,8 +627,8 @@ export class DiffusionGemmaModel {
|
|
|
626
627
|
/** Prefill from a pre-spliced image+text prompt → populated cache. */
|
|
627
628
|
prefillVision(splicedIds: number[], pixels: MlxArray): Cache[] {
|
|
628
629
|
const cache = this.makeCache();
|
|
629
|
-
this.encodeVisionPrefill(splicedIds, pixels, cache);
|
|
630
|
-
|
|
630
|
+
try { this.encodeVisionPrefill(splicedIds, pixels, cache); return cache; }
|
|
631
|
+
catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
|
|
631
632
|
}
|
|
632
633
|
|
|
633
634
|
/** One KVCache (full) / RotatingKVCache (sliding) per layer — the encoder
|
|
@@ -899,16 +900,15 @@ export class DiffusionGemmaModel {
|
|
|
899
900
|
* loop reuses it across all steps of a canvas block). */
|
|
900
901
|
prefill(promptIds: number[]): Cache[] {
|
|
901
902
|
const cache = this.makeCache();
|
|
902
|
-
this.encodePrompt(promptIds, cache);
|
|
903
|
-
|
|
903
|
+
try { this.encodePrompt(promptIds, cache); return cache; }
|
|
904
|
+
catch (error) { cleanupFailure(error, () => disposeResources(cache)); }
|
|
904
905
|
}
|
|
905
906
|
|
|
906
907
|
/** One denoising-step decoder pass -> softcapped logits [1, canvas, vocab]. */
|
|
907
908
|
decoderLogits(canvasArr: MlxArray, cache: Cache[], scEmbeddings: MlxArray | null): MlxArray {
|
|
908
909
|
const h = this.decodeCanvasArr(canvasArr, cache, null, scEmbeddings);
|
|
909
|
-
|
|
910
|
-
h.dispose();
|
|
911
|
-
return logits;
|
|
910
|
+
try { return this.logitsFromHidden(h); }
|
|
911
|
+
finally { h.dispose(); }
|
|
912
912
|
}
|
|
913
913
|
|
|
914
914
|
/** Dequantized embedding table (once per call), for self-conditioning soft
|
package/src/model/factory.ts
CHANGED
|
@@ -24,16 +24,55 @@ import {
|
|
|
24
24
|
} from "./glm52-memory";
|
|
25
25
|
import { UniversalDenseModel } from "./universal/dense";
|
|
26
26
|
import { genericArgsFor } from "./universal/archs";
|
|
27
|
+
import {
|
|
28
|
+
ModelImplementationRegistry,
|
|
29
|
+
type ModelImplementation,
|
|
30
|
+
type ModelImplementationProvider,
|
|
31
|
+
} from "./implementation";
|
|
27
32
|
import {
|
|
28
33
|
assertResolvedModelProfile,
|
|
29
34
|
resolveModelProfile,
|
|
30
35
|
type ResolvedModelProfile,
|
|
36
|
+
type ResolveModelProfileOptions,
|
|
37
|
+
type ModelGraph,
|
|
31
38
|
} from "./profile";
|
|
32
39
|
|
|
33
40
|
export type RuntimeModel =
|
|
34
41
|
| Gemma4Model | MiniCPM5Model | Qwen35Model | Qwen3Model | Qwen3MoeModel
|
|
35
42
|
| DiffusionGemmaModel | Glm52Model | UniversalDenseModel;
|
|
36
43
|
|
|
44
|
+
function residentImplementation(
|
|
45
|
+
id: string,
|
|
46
|
+
graph: ModelGraph,
|
|
47
|
+
create: ModelImplementation<Weights, RuntimeModel>["create"],
|
|
48
|
+
): ModelImplementation<Weights, RuntimeModel> {
|
|
49
|
+
return { id, graph, loader: "safetensors",
|
|
50
|
+
loop: graph === "diffusion-gemma" ? "diffusion" : "autoregressive", create };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Engine-owned registrations. Exact quant profiles can name additional
|
|
54
|
+
* implementations in a composed registry, without changing sessions or files. */
|
|
55
|
+
export const MLX_MODEL_IMPLEMENTATIONS = new ModelImplementationRegistry<Weights, RuntimeModel>([
|
|
56
|
+
residentImplementation("diffusion-gemma", "diffusion-gemma", (weights, config) => new DiffusionGemmaModel(weights, config)),
|
|
57
|
+
residentImplementation("minicpm5", "minicpm5", (weights, config) => new MiniCPM5Model(weights, config)),
|
|
58
|
+
residentImplementation("qwen3.5", "qwen3.5", (weights, config) => new Qwen35Model(weights, config)),
|
|
59
|
+
residentImplementation("qwen3-moe", "qwen3-moe", (weights, config) => new Qwen3MoeModel(weights, config)),
|
|
60
|
+
residentImplementation("qwen3", "qwen3", (weights, config) => new Qwen3Model(weights, config)),
|
|
61
|
+
residentImplementation("gemma4", "gemma4", (weights, config) => new Gemma4Model(weights, config)),
|
|
62
|
+
residentImplementation("gemma4-generated", "gemma4", (weights, config) => {
|
|
63
|
+
const Graph = GENERATED.get(configFingerprint(config));
|
|
64
|
+
if (!Graph) throw new Error("no generated Gemma graph for this config; refusing to fall back");
|
|
65
|
+
return new Graph(weights, config);
|
|
66
|
+
}),
|
|
67
|
+
residentImplementation("universal-dense", "universal-dense", (weights, config) =>
|
|
68
|
+
new UniversalDenseModel(weights, config, genericArgsFor(config)!)),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
export interface ModelOpenOptions<Model = RuntimeModel> extends Glm52RuntimeOpenOptions {
|
|
72
|
+
readonly profiles?: ResolveModelProfileOptions;
|
|
73
|
+
readonly implementations?: ModelImplementationProvider<Weights, Model>;
|
|
74
|
+
}
|
|
75
|
+
|
|
37
76
|
export interface Glm52RuntimeOpenOptions {
|
|
38
77
|
/** Whole-process ceiling. Defaults to the smaller of the validated 25 GiB
|
|
39
78
|
* preset and physical RAM. */
|
|
@@ -100,47 +139,51 @@ export async function openGlm52RuntimeModel(
|
|
|
100
139
|
* model.safetensors.index.json, so it must bypass Weights.open and use its
|
|
101
140
|
* dedicated header catalog/tensor source.
|
|
102
141
|
*/
|
|
103
|
-
export
|
|
142
|
+
export function openModel<Model>(
|
|
104
143
|
modelDir: string,
|
|
105
|
-
options:
|
|
106
|
-
): Promise<
|
|
144
|
+
options: ModelOpenOptions<Model> & { readonly implementations: ModelImplementationProvider<Weights, Model> },
|
|
145
|
+
): Promise<Model>;
|
|
146
|
+
export function openModel(modelDir: string, options?: ModelOpenOptions): Promise<RuntimeModel>;
|
|
147
|
+
export async function openModel<Model>(
|
|
148
|
+
modelDir: string,
|
|
149
|
+
options: ModelOpenOptions<Model> = {},
|
|
150
|
+
): Promise<Model | RuntimeModel> {
|
|
107
151
|
const config = await loadModelConfig(modelDir);
|
|
108
|
-
const profile = resolveModelProfile(config);
|
|
109
|
-
if (profile.profile.execution.loader === "colibri")
|
|
152
|
+
const profile = resolveModelProfile(config, options.profiles);
|
|
153
|
+
if (profile.profile.execution.loader === "colibri") {
|
|
154
|
+
if (options.implementations || profile.profile.execution.implementation !== undefined)
|
|
155
|
+
throw new Error("custom Colibri implementations require a streamed loader binding; refusing to fall back");
|
|
110
156
|
return (await openGlm52RuntimeModel(modelDir, options)).model;
|
|
111
|
-
|
|
157
|
+
}
|
|
158
|
+
// Resolve before opening weights; missing or incompatible code allocates nothing.
|
|
159
|
+
const implementation = (options.implementations ?? MLX_MODEL_IMPLEMENTATIONS).select(config, profile);
|
|
160
|
+
const weights = await Weights.open(modelDir);
|
|
161
|
+
try { return implementation.create(weights, config, profile); }
|
|
162
|
+
catch (error) { weights.dispose(); throw error; }
|
|
112
163
|
}
|
|
113
164
|
|
|
114
|
-
/** Construct the
|
|
165
|
+
/** Construct the binding named by a validated profile. A supplied registry can
|
|
166
|
+
* return any backend-owned interface; the compatibility default returns the
|
|
167
|
+
* legacy model union. The caller retains ownership of the supplied weights. */
|
|
168
|
+
export function createModel<Model>(
|
|
169
|
+
weights: Weights, config: ModelConfig, resolved: ResolvedModelProfile | undefined,
|
|
170
|
+
implementations: ModelImplementationProvider<Weights, Model>,
|
|
171
|
+
): Model;
|
|
115
172
|
export function createModel(
|
|
173
|
+
weights: Weights, config: ModelConfig, resolved?: ResolvedModelProfile,
|
|
174
|
+
implementations?: ModelImplementationProvider<Weights, RuntimeModel>,
|
|
175
|
+
): RuntimeModel;
|
|
176
|
+
export function createModel<Model>(
|
|
116
177
|
weights: Weights,
|
|
117
178
|
config: ModelConfig,
|
|
118
179
|
resolved: ResolvedModelProfile = resolveModelProfile(config),
|
|
119
|
-
|
|
180
|
+
implementations: ModelImplementationProvider<Weights, Model> | ModelImplementationProvider<Weights, RuntimeModel> = MLX_MODEL_IMPLEMENTATIONS,
|
|
181
|
+
): Model | RuntimeModel {
|
|
120
182
|
assertResolvedModelProfile(config, resolved);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
128
|
-
// DiffusionGemma is non-autoregressive. The profile pairs this graph with
|
|
129
|
-
// the denoising loop; generate() recognizes the constructed model surface.
|
|
130
|
-
case "diffusion-gemma": return new DiffusionGemmaModel(weights, config);
|
|
131
|
-
case "minicpm5": return new MiniCPM5Model(weights, config);
|
|
132
|
-
case "qwen3.5": return new Qwen35Model(weights, config);
|
|
133
|
-
case "qwen3-moe": return new Qwen3MoeModel(weights, config);
|
|
134
|
-
case "qwen3": return new Qwen3Model(weights, config);
|
|
135
|
-
case "gemma4": {
|
|
136
|
-
// Generated specializations remain exact config matches. Output-changing
|
|
137
|
-
// request methods stay independently resolved and cannot be fallbacks.
|
|
138
|
-
const cls = execution.specialization === "generated"
|
|
139
|
-
? GENERATED.get(configFingerprint(config))!
|
|
140
|
-
: Gemma4Model;
|
|
141
|
-
return new cls(weights, config);
|
|
142
|
-
}
|
|
143
|
-
case "universal-dense":
|
|
144
|
-
return new UniversalDenseModel(weights, config, genericArgsFor(config)!);
|
|
145
|
-
}
|
|
183
|
+
if (resolved.profile.execution.loader === "colibri")
|
|
184
|
+
throw new Error(
|
|
185
|
+
"glm_moe_dsa uses the direct Colibri container; construct it with " +
|
|
186
|
+
"openModel(modelDir) or Glm52Model.open(modelDir)",
|
|
187
|
+
);
|
|
188
|
+
return implementations.select(config, resolved).create(weights, config, resolved);
|
|
146
189
|
}
|
package/src/model/fingerprint.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Config fingerprint for generated-model dispatch (docs/
|
|
1
|
+
// Config fingerprint for generated-model dispatch (docs/archive/investigations/optimization_plan.md
|
|
2
2
|
// Phase C): a stable hash over every config field that changes the
|
|
3
3
|
// decode graph structure, plus the kv_config quant layout. A generated
|
|
4
4
|
// specialization is used only when the fingerprint of the loaded config
|
|
@@ -545,7 +545,7 @@ export function flashBackward(
|
|
|
545
545
|
// (dK_j[d] → [kv_row][d]); no post-transpose. (An earlier "fix" transposed
|
|
546
546
|
// it on the false belief the buffer was swapped — that transpose is only the
|
|
547
547
|
// identity at Tkv==D and corrupted dK for Tkv≠D; removed after
|
|
548
|
-
//
|
|
548
|
+
// flash-dkv-debug.ts (deleted 2026-08-23; git history) showed the raw output is correct.)
|
|
549
549
|
return [dq[0]!, dkv[0]!, dkv[1]!];
|
|
550
550
|
} finally {
|
|
551
551
|
Dvec.dispose();
|
|
@@ -621,8 +621,8 @@ export function flashAttention(
|
|
|
621
621
|
// - This flash kernel is the L2 path: a port of mlx-optiq's
|
|
622
622
|
// flash_attention_metal (O(L) backward memory). Its parity oracle is OPTIQ,
|
|
623
623
|
// not finite differences. Confirmed: optiq's flash dK == ops.sdpa to f16
|
|
624
|
-
// (scripts/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
|
|
625
|
-
// (tests/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
|
|
624
|
+
// (scripts/oracle/flash-optiq-check.py, rel 0.0%), and mlx-bun's flash == ops.sdpa
|
|
625
|
+
// (tests/unit/flash-attention.test.ts) ⟹ mlx-bun flash == optiq flash (L2 parity).
|
|
626
626
|
//
|
|
627
627
|
// Two real port bugs were fixed to reach that parity (the original diverged
|
|
628
628
|
// ~100% from optiq on dK): a spurious dK transpose in flashBackward (corrupted
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import * as ops from "../mlx/ops";
|
|
3
|
+
import type { PrefillPadding } from "./gemma4-base";
|
|
4
|
+
import { rowRollIndices } from "./batched-row-storage";
|
|
5
|
+
import { disposeResources } from "../engine/resources";
|
|
6
|
+
|
|
7
|
+
interface Positions { rowOffsets: number[]; leftPad: number[] }
|
|
8
|
+
|
|
9
|
+
/** mlx-lm BatchKVCache.prepare/finalize (MIT), over arbitrary stored planes.
|
|
10
|
+
* Padding changes token positions; the representation's codec is uninvolved. */
|
|
11
|
+
export class FullPrefillPadding {
|
|
12
|
+
constructor(readonly tokenAxis = 2) {}
|
|
13
|
+
#ends?: number[];
|
|
14
|
+
get endOffsets(): readonly number[] | undefined { return this.#ends; }
|
|
15
|
+
restoreEnds(ends: readonly number[] | undefined): void { this.#ends = ends ? [...ends] : undefined; }
|
|
16
|
+
validOffset(offset: number, row: number): number { return Math.min(offset, this.#ends?.[row] ?? offset); }
|
|
17
|
+
|
|
18
|
+
prepare(positions: Positions, padding: PrefillPadding): void {
|
|
19
|
+
if (!positions.rowOffsets.length) {
|
|
20
|
+
positions.rowOffsets = padding.lengths.map(() => 0);
|
|
21
|
+
positions.leftPad = [...positions.rowOffsets];
|
|
22
|
+
}
|
|
23
|
+
if (padding.leftPadding) {
|
|
24
|
+
positions.leftPad = positions.leftPad.map((pad, row) => pad + padding.leftPadding![row]!);
|
|
25
|
+
positions.rowOffsets = positions.rowOffsets.map((offset, row) => offset - padding.leftPadding![row]!);
|
|
26
|
+
}
|
|
27
|
+
// Like rotating storage, retain absolute valid ends. A cohort can finish
|
|
28
|
+
// before its planned width when a longer request leaves during prefill.
|
|
29
|
+
this.#ends = padding.rightPadding?.some(pad => pad > 0)
|
|
30
|
+
? padding.lengths.map((length, row) => positions.rowOffsets[row]! + length) : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Borrow planes; return owned replacements only when padding needs moving.
|
|
34
|
+
* Stage every plane before committing positions, including on FFI failure. */
|
|
35
|
+
finalize(planes: readonly MlxArray[], positions: Positions): MlxArray[] | undefined {
|
|
36
|
+
if (!this.#ends) return;
|
|
37
|
+
const padding = this.#ends.map((end, row) => Math.max(0, positions.rowOffsets[row]! - end));
|
|
38
|
+
if (!padding.some(pad => pad > 0)) { this.#ends = undefined; return; }
|
|
39
|
+
const next: MlxArray[] = [];
|
|
40
|
+
try {
|
|
41
|
+
if (planes.length) {
|
|
42
|
+
const width = planes[0]!.shape[this.tokenAxis]!;
|
|
43
|
+
using indices = rowRollIndices(width, padding);
|
|
44
|
+
using compressed = this.tokenAxis === 1 ? ops.reshape(indices, [padding.length, width, 1]) : null;
|
|
45
|
+
for (const plane of planes) next.push(ops.takeAlongAxis(plane, compressed ?? indices, this.tokenAxis));
|
|
46
|
+
}
|
|
47
|
+
} catch (error) { disposeResources(next); throw error; }
|
|
48
|
+
positions.rowOffsets = positions.rowOffsets.map((offset, row) => offset - padding[row]!);
|
|
49
|
+
positions.leftPad = positions.leftPad.map((pad, row) => pad + padding[row]!);
|
|
50
|
+
this.#ends = undefined;
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
filter(keep: readonly number[]): void { if (this.#ends) this.#ends = keep.map(row => this.#ends![row]!); }
|
|
55
|
+
clear(): void { this.#ends = undefined; }
|
|
56
|
+
}
|