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
package/src/model/gemma4.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { Weights } from "../weights";
|
|
|
23
23
|
import { MlxArray } from "../mlx/array";
|
|
24
24
|
import { Dtype } from "../mlx/ffi";
|
|
25
25
|
import * as ops from "../mlx/ops";
|
|
26
|
-
import { isExpertTracing, recordRouting } from "../expert-trace";
|
|
26
|
+
import { isExpertTracing, recordRouting } from "../lab/expert-trace/expert-trace";
|
|
27
27
|
|
|
28
28
|
// The shared, config-independent machinery lives in gemma4-base.ts
|
|
29
29
|
// (Phase B extraction); this file keeps the architecture-specific
|
|
@@ -45,18 +45,18 @@ import {
|
|
|
45
45
|
RMSNorm,
|
|
46
46
|
RotatingKVCache,
|
|
47
47
|
RotatingQuantizedKVCache,
|
|
48
|
-
TurboQuantKVCache,
|
|
49
48
|
quantizedSdpa,
|
|
50
49
|
type Cache,
|
|
51
50
|
type Mask,
|
|
52
51
|
type SharedKv,
|
|
52
|
+
captureRopeOffsets,
|
|
53
53
|
type LoraWeights,
|
|
54
54
|
} from "./gemma4-base";
|
|
55
55
|
import { Checkpoint } from "../mlx/checkpoint";
|
|
56
56
|
import { flashAttention, getTrainingAttn, flashSupported } from "./flash-attention";
|
|
57
57
|
import { unrotateValues as tqUnrotateValues } from "../mlx/turboquant-ops";
|
|
58
58
|
import { CompiledFunction } from "../mlx/compile";
|
|
59
|
-
import { flagOn } from "../
|
|
59
|
+
import { flagOn } from "../runtime-config";
|
|
60
60
|
|
|
61
61
|
/** Verbatim port of mlx_lm/models/gemma4_text.py:
|
|
62
62
|
* `@partial(mx.compile, shapeless=True) def geglu(gate, x): return nn.gelu_approx(gate) * x`
|
|
@@ -242,7 +242,7 @@ class Attention {
|
|
|
242
242
|
/** A number offset takes the static fast::rope; an array offset (set by
|
|
243
243
|
* compiled-decode trace adapters) takes the dynamic variant — same
|
|
244
244
|
* kernel, offset read from the array (bit-exactness asserted in
|
|
245
|
-
* tests/compile.test.ts). */
|
|
245
|
+
* tests/unit/compile.test.ts). */
|
|
246
246
|
rope(x: MlxArray, offset: number | MlxArray): MlxArray {
|
|
247
247
|
return typeof offset === "number"
|
|
248
248
|
? ops.rope(x, this.headDim, this.ropeBase, offset, this.ropeFreqs)
|
|
@@ -304,15 +304,11 @@ class Attention {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
const offset = cache.offset;
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
// prerequisite for batched decode, where ropeOffsetArr carries per-row
|
|
313
|
-
// positions derived from the pre-write offset and MUST be identical for
|
|
314
|
-
// this step's K and Q.
|
|
315
|
-
const offsetArr = cache.ropeOffsetArr;
|
|
307
|
+
// Own the pre-write positions through every query and shared-KV
|
|
308
|
+
// consumer. Advancing a row cache may replace and release its borrowed
|
|
309
|
+
// offset array before Q's RoPE is built. The fetched shared state owns
|
|
310
|
+
// this retained handle and releases it with K/V at the end of the pass.
|
|
311
|
+
const offsetArr = captureRopeOffsets(cache);
|
|
316
312
|
|
|
317
313
|
const kNormed = this.kNorm!.forward(k);
|
|
318
314
|
const kT = ops.transposeAxes(kNormed, [0, 2, 1, 3]);
|
|
@@ -328,23 +324,29 @@ class Attention {
|
|
|
328
324
|
if (v !== k) v.dispose();
|
|
329
325
|
k.dispose();
|
|
330
326
|
|
|
331
|
-
if (cache
|
|
332
|
-
const
|
|
327
|
+
if (cache.attentionState) {
|
|
328
|
+
const attention = cache.attentionState.appendAndFetch(kRoped, vT);
|
|
329
|
+
kRoped.dispose(); vT.dispose();
|
|
330
|
+
shared = { kind: "view", attention, offset, offsetArr };
|
|
331
|
+
} else if (cache.quantizedAttention) {
|
|
332
|
+
const quantized = cache.quantizedAttention;
|
|
333
|
+
const [kq, vq] = quantized.updateAndFetchQuantized(kRoped, vT);
|
|
333
334
|
kRoped.dispose();
|
|
334
335
|
vT.dispose();
|
|
335
336
|
shared = {
|
|
336
337
|
kind: "quant", keys: kq, values: vq, offset,
|
|
337
|
-
groupSize:
|
|
338
|
+
groupSize: quantized.groupSize, bits: quantized.bits,
|
|
338
339
|
offsetArr,
|
|
339
340
|
};
|
|
340
|
-
} else if (cache
|
|
341
|
+
} else if (cache.rotatedValueAttention) {
|
|
341
342
|
// Deferred-V read: values stay rotated; the attention output is
|
|
342
343
|
// un-rotated below (shared.vRotated) — including by KV-shared
|
|
343
344
|
// consumer layers, which see the flag through sharedIn.
|
|
344
|
-
const [keys, values] = cache.updateAndFetchDeferredV(kRoped, vT);
|
|
345
|
+
const [keys, values] = cache.rotatedValueAttention.updateAndFetchDeferredV(kRoped, vT);
|
|
345
346
|
kRoped.dispose();
|
|
346
347
|
vT.dispose();
|
|
347
|
-
shared = { kind: "plain", keys, values, offset, offsetArr, vRotated: true
|
|
348
|
+
shared = { kind: "plain", keys, values, offset, offsetArr, vRotated: true,
|
|
349
|
+
restoreValues: cache.rotatedValueAttention.captureValueTransform?.() };
|
|
348
350
|
} else {
|
|
349
351
|
const [keys, values] = cache.updateAndFetch(kRoped, vT);
|
|
350
352
|
kRoped.dispose();
|
|
@@ -360,7 +362,9 @@ class Attention {
|
|
|
360
362
|
|
|
361
363
|
let attn: MlxArray;
|
|
362
364
|
const ta = getTrainingAttn();
|
|
363
|
-
if (shared.kind === "
|
|
365
|
+
if (shared.kind === "view") {
|
|
366
|
+
attn = shared.attention.attend(q, 1.0, mask);
|
|
367
|
+
} else if (shared.kind === "quant") {
|
|
364
368
|
attn = quantizedSdpa(q, shared.keys, shared.values, 1.0, mask, shared.groupSize, shared.bits);
|
|
365
369
|
} else if (
|
|
366
370
|
ta === "flash" && flashSupported(q) && (mask.mode === "causal" || mask.mode === "array")
|
|
@@ -370,7 +374,7 @@ class Attention {
|
|
|
370
374
|
// verified route: its dQ/dK/dV vjps are exact (finite-difference checked;
|
|
371
375
|
// it's what mlx-lm's tuner differentiates). The two historical bugs were
|
|
372
376
|
// in THIS flash kernel (spurious dK transpose, divergent dQ barrier),
|
|
373
|
-
// both fixed and gated by tests/flash-attention.test.ts vs the ops.sdpa
|
|
377
|
+
// both fixed and gated by tests/unit/flash-attention.test.ts vs the ops.sdpa
|
|
374
378
|
// oracle — see flash-attention.ts's routing comment. Flash also remains
|
|
375
379
|
// Gemma-guarded in the trainer (e4b >=2K SIGTRAP not yet re-validated).
|
|
376
380
|
// Full layers → window 0 (pure causal); sliding layers → their window.
|
|
@@ -383,7 +387,7 @@ class Attention {
|
|
|
383
387
|
if (shared.kind === "plain" && shared.vRotated) {
|
|
384
388
|
// Attention is linear in V: one InvFWHT on the [B,H,L,D] output
|
|
385
389
|
// replaces one per cached token (TurboQuant deferred-V).
|
|
386
|
-
attn = disposing(attn, tqUnrotateValues(attn));
|
|
390
|
+
attn = disposing(attn, shared.restoreValues ? shared.restoreValues(attn) : tqUnrotateValues(attn));
|
|
387
391
|
}
|
|
388
392
|
const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
|
|
389
393
|
attn.dispose();
|
|
@@ -851,6 +855,13 @@ export class Gemma4Model {
|
|
|
851
855
|
);
|
|
852
856
|
}
|
|
853
857
|
|
|
858
|
+
/** Computed model constants must retain their constructor arithmetic when
|
|
859
|
+
* a restored request enters compiled decode without a prefill first. */
|
|
860
|
+
materializeGraphConstants(): void {
|
|
861
|
+
const constants = this.layers.flatMap(layer => layer.attn.ropeFreqs ? [layer.attn.ropeFreqs] : []);
|
|
862
|
+
if (constants.length) ops.evalAll(constants);
|
|
863
|
+
}
|
|
864
|
+
|
|
854
865
|
/** ids [1, L] → final-norm hidden states [1, L, hidden]. */
|
|
855
866
|
forwardHidden(ids: MlxArray, cache: Cache[]): MlxArray {
|
|
856
867
|
let h = this.embed.encode(ids);
|
|
@@ -885,7 +896,7 @@ export class Gemma4Model {
|
|
|
885
896
|
* which attend bidirectionally among themselves (use_bidirectional_attention:
|
|
886
897
|
* "vision" — text stays causal; audio prompts pass NO bidir mask, and a
|
|
887
898
|
* mixed image+audio prompt drops the image overlay too, §3.3 Q1 of
|
|
888
|
-
* docs/design/
|
|
899
|
+
* docs/design/generic-model-support.md). `ids` ([1, L], the spliced token ids)
|
|
889
900
|
* is required for per-layer-input models (e2b/e4b): multimodal soft-token
|
|
890
901
|
* positions get token 0's per-layer embedding, and their tower content
|
|
891
902
|
* enters only through the projection term (the merged hidden). Matches
|
|
@@ -1000,13 +1011,7 @@ export class Gemma4Model {
|
|
|
1000
1011
|
for (let i = 0; i < this.numDonors; i++) {
|
|
1001
1012
|
const s = intermediates[i];
|
|
1002
1013
|
if (!s) continue;
|
|
1003
|
-
|
|
1004
|
-
s.keys.dispose();
|
|
1005
|
-
s.values.dispose();
|
|
1006
|
-
} else {
|
|
1007
|
-
for (const t of [s.keys, s.values])
|
|
1008
|
-
for (const a of [t.packed, t.scales, t.biases]) a.dispose();
|
|
1009
|
-
}
|
|
1014
|
+
Gemma4Model.disposeSharedKv(s);
|
|
1010
1015
|
}
|
|
1011
1016
|
for (const m of masks.values()) m.arr?.dispose();
|
|
1012
1017
|
|
|
@@ -1015,7 +1020,7 @@ export class Gemma4Model {
|
|
|
1015
1020
|
return h;
|
|
1016
1021
|
}
|
|
1017
1022
|
|
|
1018
|
-
// --- Segmented-backward support (docs/design/
|
|
1023
|
+
// --- Segmented-backward support (docs/design/orpo-training.md
|
|
1019
1024
|
// §4 Phase B). These are ADDITIVE — forwardLayers is untouched. The segmented
|
|
1020
1025
|
// driver builds masks + per-layer inputs ONCE, then drives runLayerRange per
|
|
1021
1026
|
// segment, threading the KV-shared donor K/V across segment boundaries.
|
|
@@ -1056,7 +1061,10 @@ export class Gemma4Model {
|
|
|
1056
1061
|
/** Dispose a fetched donor K/V (plain or quantized) — mirrors forwardLayers'
|
|
1057
1062
|
* donor cleanup. */
|
|
1058
1063
|
private static disposeSharedKv(s: SharedKv): void {
|
|
1059
|
-
|
|
1064
|
+
s.offsetArr?.dispose();
|
|
1065
|
+
if (s.kind === "view") {
|
|
1066
|
+
s.attention.dispose();
|
|
1067
|
+
} else if (s.kind === "plain") {
|
|
1060
1068
|
s.keys.dispose();
|
|
1061
1069
|
s.values.dispose();
|
|
1062
1070
|
} else {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: 9f812d2eb461fcbe
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
|
@@ -34,7 +34,10 @@ export const FINGERPRINT = "9f812d2eb461fcbe";
|
|
|
34
34
|
export let generatedForwardUses = 0;
|
|
35
35
|
|
|
36
36
|
function disposeSharedKv(s: SharedKv): void {
|
|
37
|
-
if (s.kind === "
|
|
37
|
+
if (s.kind === "view") {
|
|
38
|
+
s.attention.dispose();
|
|
39
|
+
s.offsetArr?.dispose();
|
|
40
|
+
} else if (s.kind === "plain") {
|
|
38
41
|
s.keys.dispose();
|
|
39
42
|
s.values.dispose();
|
|
40
43
|
} else {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: c9dd67ed5a525231
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
|
@@ -34,7 +34,10 @@ export const FINGERPRINT = "c9dd67ed5a525231";
|
|
|
34
34
|
export let generatedForwardUses = 0;
|
|
35
35
|
|
|
36
36
|
function disposeSharedKv(s: SharedKv): void {
|
|
37
|
-
if (s.kind === "
|
|
37
|
+
if (s.kind === "view") {
|
|
38
|
+
s.attention.dispose();
|
|
39
|
+
s.offsetArr?.dispose();
|
|
40
|
+
} else if (s.kind === "plain") {
|
|
38
41
|
s.keys.dispose();
|
|
39
42
|
s.values.dispose();
|
|
40
43
|
} else {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// fingerprint: 418e9adc386ea67c
|
|
5
5
|
//
|
|
6
6
|
// Branch-resolved, unrolled forward pass for this architecture
|
|
7
|
-
// (docs/
|
|
8
|
-
// generator's gate: tests/generated-parity.test.ts. The cache-signature
|
|
7
|
+
// (docs/archive/investigations/optimization_plan.md Phase C). Bit-exactness vs the monolith is the
|
|
8
|
+
// generator's gate: tests/parity/generated-parity.test.ts. The cache-signature
|
|
9
9
|
// guard falls back to the monolith for anything this file wasn't
|
|
10
10
|
// generated for (bf16 compat runs, vision bidir prefill).
|
|
11
11
|
|
|
@@ -35,7 +35,10 @@ export const FINGERPRINT = "418e9adc386ea67c";
|
|
|
35
35
|
export let generatedForwardUses = 0;
|
|
36
36
|
|
|
37
37
|
function disposeSharedKv(s: SharedKv): void {
|
|
38
|
-
if (s.kind === "
|
|
38
|
+
if (s.kind === "view") {
|
|
39
|
+
s.attention.dispose();
|
|
40
|
+
s.offsetArr?.dispose();
|
|
41
|
+
} else if (s.kind === "plain") {
|
|
39
42
|
s.keys.dispose();
|
|
40
43
|
s.values.dispose();
|
|
41
44
|
} else {
|
package/src/model/glm52-cache.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// row's logical position without reconstructing per-head K/V.
|
|
12
12
|
|
|
13
13
|
import { MlxArray } from "../mlx/array";
|
|
14
|
+
import { FullPrefillPadding } from "./full-prefill-padding";
|
|
15
|
+
import type { PrefillPadding } from "./gemma4-base";
|
|
14
16
|
import { Dtype } from "../mlx/ffi";
|
|
15
17
|
import * as ops from "../mlx/ops";
|
|
16
18
|
import {
|
|
@@ -210,6 +212,8 @@ export class Glm52Cache implements BatchableCache {
|
|
|
210
212
|
batchSize: number | null = null;
|
|
211
213
|
rowOffsets: number[] = [];
|
|
212
214
|
leftPad: number[] = [];
|
|
215
|
+
#beforeRound: number[] | undefined;
|
|
216
|
+
readonly #padding = new FullPrefillPadding(1);
|
|
213
217
|
|
|
214
218
|
constructor(geometry: MLACacheGeometry) {
|
|
215
219
|
positiveInteger("MLA kvLoraRank", geometry.kvLoraRank);
|
|
@@ -482,6 +486,56 @@ export class Glm52Cache implements BatchableCache {
|
|
|
482
486
|
this.rowOffsets = this.rowOffsets.map((value) => value - n);
|
|
483
487
|
}
|
|
484
488
|
|
|
489
|
+
preparePrefill(padding: PrefillPadding): void { this.#padding.prepare(this, padding); }
|
|
490
|
+
finalizePrefill(): void {
|
|
491
|
+
const next = this.#padding.finalize(this.state(), this);
|
|
492
|
+
if (!next) return;
|
|
493
|
+
this.latent?.dispose(); this.rope?.dispose();
|
|
494
|
+
this.latent = next[0] ?? null; this.rope = next[1] ?? null;
|
|
495
|
+
if (this.dsa && next[2]) this.dsa.restoreState(next[2], this.offset);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
specRoundBegin(): void { this.#beforeRound = [...this.rowOffsets]; }
|
|
499
|
+
specRoundCommit(): void { this.#beforeRound = undefined; }
|
|
500
|
+
|
|
501
|
+
/** Keep each accepted prefix in compressed form. Right alignment lets the
|
|
502
|
+
* next shared append reuse the existing MLA/DSA attention geometry. */
|
|
503
|
+
specRoundRollback(keep: number | readonly number[]): void {
|
|
504
|
+
const nextOffsets = this.#beforeRound!.map((before, row) => before +
|
|
505
|
+
(typeof keep === "number" ? keep : keep[row]!));
|
|
506
|
+
const rejected = this.rowOffsets.map((offset, row) => offset - nextOffsets[row]!);
|
|
507
|
+
if (rejected.every(count => count === rejected[0])) {
|
|
508
|
+
const count = this.batchSize!;
|
|
509
|
+
this.trim(rejected[0]!);
|
|
510
|
+
if (this.batchSize === null) {
|
|
511
|
+
const empty = this.makeEmptyBatch();
|
|
512
|
+
try { this.mergeRows(Array.from({ length: count }, () => empty)); }
|
|
513
|
+
finally { empty.dispose(); }
|
|
514
|
+
}
|
|
515
|
+
this.#beforeRound = undefined; return;
|
|
516
|
+
}
|
|
517
|
+
const width = this.offset;
|
|
518
|
+
const pads = this.leftPad.map((pad, row) => pad + rejected[row]!);
|
|
519
|
+
const removable = Math.min(...pads);
|
|
520
|
+
using indices = ops.fromInt32(rejected.flatMap(count =>
|
|
521
|
+
Array.from({ length: width }, (_, column) => (column - count + width) % width)),
|
|
522
|
+
[this.batchSize!, width, 1]);
|
|
523
|
+
const next: MlxArray[] = [];
|
|
524
|
+
try {
|
|
525
|
+
for (const array of this.state()) {
|
|
526
|
+
using shifted = ops.takeAlongAxis(array, indices, 1);
|
|
527
|
+
next.push(shifted.slice([0, removable, 0], [this.batchSize!, width, array.shape[2]!]));
|
|
528
|
+
}
|
|
529
|
+
} catch (error) { for (const array of next) array.dispose(); throw error; }
|
|
530
|
+
this.latent!.dispose(); this.rope!.dispose();
|
|
531
|
+
this.latent = next[0]!; this.rope = next[1]!;
|
|
532
|
+
if (this.dsa) this.dsa.restoreState(next[2]!, width - removable);
|
|
533
|
+
this.offset = width - removable;
|
|
534
|
+
this.rowOffsets = nextOffsets;
|
|
535
|
+
this.leftPad = pads.map(pad => pad - removable);
|
|
536
|
+
this.#beforeRound = undefined;
|
|
537
|
+
}
|
|
538
|
+
|
|
485
539
|
/** Exact logical bytes of valid f32 compressed state. */
|
|
486
540
|
get byteLength(): number {
|
|
487
541
|
if (this.batchSize === null) return 0;
|
|
@@ -529,8 +583,16 @@ export class Glm52Cache implements BatchableCache {
|
|
|
529
583
|
) {
|
|
530
584
|
throw new Error("MLA mergeRows cache geometry does not match");
|
|
531
585
|
}
|
|
532
|
-
if (generic.batchSize === null || generic.offset === 0)
|
|
533
|
-
|
|
586
|
+
if (generic.batchSize === null || generic.offset === 0) {
|
|
587
|
+
const count = generic.batchSize ?? 1;
|
|
588
|
+
for (let row = 0; row < count; row++) {
|
|
589
|
+
states.push({ latent: ops.zeros([1, 0, this.kvLoraRank], Dtype.float32),
|
|
590
|
+
rope: ops.zeros([1, 0, this.ropeHeadDim], Dtype.float32),
|
|
591
|
+
dsa: this.dsa ? ops.zeros([1, 0, this.dsa.headDim], Dtype.float32) : null });
|
|
592
|
+
offsets.push(0);
|
|
593
|
+
}
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
534
596
|
for (let row = 0; row < generic.batchSize; row++) {
|
|
535
597
|
states.push(generic.fetchRow(row));
|
|
536
598
|
offsets.push(generic.rowOffset(row));
|
|
@@ -577,7 +639,10 @@ export class Glm52Cache implements BatchableCache {
|
|
|
577
639
|
this.offset = width;
|
|
578
640
|
this.rowOffsets = [...offsets];
|
|
579
641
|
this.leftPad = offsets.map((value) => width - value);
|
|
580
|
-
if (this.dsa)
|
|
642
|
+
if (this.dsa) {
|
|
643
|
+
if (width) this.dsa.restoreState(dsa!, width);
|
|
644
|
+
else { this.dsa.data = dsa!; this.dsa.batchSize = offsets.length; }
|
|
645
|
+
}
|
|
581
646
|
} finally {
|
|
582
647
|
for (const state of states) {
|
|
583
648
|
state.latent.dispose();
|
|
@@ -591,6 +656,7 @@ export class Glm52Cache implements BatchableCache {
|
|
|
591
656
|
extractRow(row: number): Glm52Cache {
|
|
592
657
|
if (this.batchSize === null) throw new Error("MLA cache is empty");
|
|
593
658
|
validateRowIndex(row, this.batchSize, "MLA extract");
|
|
659
|
+
if (this.rowOffsets[row] === 0) return this.makeEmptyBatch();
|
|
594
660
|
const state = this.fetchRow(row);
|
|
595
661
|
const copy = (array: MlxArray): MlxArray => ops.mulScalar(array, 1);
|
|
596
662
|
const latent = copy(state.latent);
|
|
@@ -666,7 +732,15 @@ export class Glm52Cache implements BatchableCache {
|
|
|
666
732
|
this.offset = nextWidth;
|
|
667
733
|
this.rowOffsets = nextOffsets;
|
|
668
734
|
this.leftPad = selectedPad.map((value) => value - removablePad);
|
|
669
|
-
|
|
735
|
+
this.#padding.filter(keep);
|
|
736
|
+
if (this.dsa) {
|
|
737
|
+
if (nextWidth) this.dsa.restoreState(dsa!, nextWidth);
|
|
738
|
+
else {
|
|
739
|
+
// A retained cold row still owns batch membership, even though the
|
|
740
|
+
// strict persisted-state restore contract requires nonempty tokens.
|
|
741
|
+
this.dsa.dispose(); this.dsa.data = dsa!; this.dsa.batchSize = keep.length;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
670
744
|
}
|
|
671
745
|
|
|
672
746
|
projectedBytes(tokens: number): number {
|
|
@@ -709,6 +783,8 @@ export class Glm52Cache implements BatchableCache {
|
|
|
709
783
|
this.batchSize = null;
|
|
710
784
|
this.rowOffsets = [];
|
|
711
785
|
this.leftPad = [];
|
|
786
|
+
this.#beforeRound = undefined;
|
|
787
|
+
this.#padding.clear();
|
|
712
788
|
}
|
|
713
789
|
}
|
|
714
790
|
|
package/src/model/glm52-dsa.ts
CHANGED
|
@@ -291,7 +291,7 @@ function getDsaRankKeyKernel(): MetalKernel {
|
|
|
291
291
|
? ~bits
|
|
292
292
|
: (bits ^ 0x80000000u);
|
|
293
293
|
const uint descending = ~ascending;
|
|
294
|
-
keys[i] =
|
|
294
|
+
keys[i] = descending;
|
|
295
295
|
`,
|
|
296
296
|
});
|
|
297
297
|
}
|
|
@@ -308,7 +308,8 @@ function getDsaContractOrderKeyKernel(): MetalKernel {
|
|
|
308
308
|
const uint i = thread_position_in_grid.x;
|
|
309
309
|
const uint position = selected_positions[i];
|
|
310
310
|
const uint threshold_class = selected_scores[i] > threshold ? 0u : 1u;
|
|
311
|
-
|
|
311
|
+
// MLX axis lengths are signed int32; positions leave the high bit free.
|
|
312
|
+
keys[i] = (threshold_class << 31) | position;
|
|
312
313
|
`,
|
|
313
314
|
});
|
|
314
315
|
}
|
|
@@ -318,8 +319,8 @@ function getDsaContractOrderKeyKernel(): MetalKernel {
|
|
|
318
319
|
/**
|
|
319
320
|
* Deterministic on-device equivalent of `selectDsaThresholdTiesF32`.
|
|
320
321
|
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
322
|
+
* Stable sorting of the first uint32 key orders scores descending and keeps
|
|
323
|
+
* equal-score positions ascending, including threshold ties. The second key
|
|
323
324
|
* restores Colibri's observable two-scan order: all scores strictly above the
|
|
324
325
|
* threshold in position order, followed by threshold ties in position order.
|
|
325
326
|
* The caller owns the returned arrays and must call `dispose()` once the last
|
|
@@ -340,18 +341,21 @@ export function selectGlm52DsaDevice(
|
|
|
340
341
|
? scores
|
|
341
342
|
: scores.astype(Dtype.float32);
|
|
342
343
|
const [rankKeys] = getDsaRankKeyKernel().apply([scoresF32], {
|
|
343
|
-
outputs: [{ shape: [contextLength], dtype: Dtype.
|
|
344
|
+
outputs: [{ shape: [contextLength], dtype: Dtype.uint32 }],
|
|
344
345
|
grid: [contextLength, 1, 1],
|
|
345
346
|
threadGroup: [Math.min(256, contextLength), 1, 1],
|
|
346
347
|
});
|
|
347
|
-
|
|
348
|
-
|
|
348
|
+
// Stable argsort retains ascending positions for equal score bits, so the
|
|
349
|
+
// ranking key needs no position field. MLX 0.31.2's GPU argpartition also
|
|
350
|
+
// sorts fully, but does not promise this tie order in its public contract.
|
|
351
|
+
const rankedPositions = ops.argsortAxis(rankKeys!, 0);
|
|
352
|
+
const selectedUnordered = rankedPositions.slice([0], [topK]);
|
|
349
353
|
const selectedScores = ops.takeAlongAxis(scoresF32, selectedUnordered, 0);
|
|
350
354
|
const threshold = ops.minAxis(selectedScores, 0, false);
|
|
351
355
|
const [orderKeys] = getDsaContractOrderKeyKernel().apply(
|
|
352
356
|
[selectedScores, selectedUnordered, threshold],
|
|
353
357
|
{
|
|
354
|
-
outputs: [{ shape: [topK], dtype: Dtype.
|
|
358
|
+
outputs: [{ shape: [topK], dtype: Dtype.uint32 }],
|
|
355
359
|
grid: [topK, 1, 1],
|
|
356
360
|
threadGroup: [Math.min(256, topK), 1, 1],
|
|
357
361
|
},
|
|
@@ -360,7 +364,7 @@ export function selectGlm52DsaDevice(
|
|
|
360
364
|
const positions = ops.takeAlongAxis(selectedUnordered, order, 0);
|
|
361
365
|
|
|
362
366
|
rankKeys!.dispose();
|
|
363
|
-
|
|
367
|
+
rankedPositions.dispose();
|
|
364
368
|
selectedUnordered.dispose();
|
|
365
369
|
selectedScores.dispose();
|
|
366
370
|
orderKeys!.dispose();
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type Glm52Config,
|
|
5
5
|
} from "./glm52-config";
|
|
6
6
|
import { ColibriGlm52Container } from "./glm52-container";
|
|
7
|
-
import type { MemoryPlan } from "../
|
|
7
|
+
import type { MemoryPlan } from "../admission-plan";
|
|
8
8
|
|
|
9
9
|
export const GLM52_G5_GIB = 1024 ** 3;
|
|
10
10
|
export const GLM52_G5_DEFAULT_MACHINE_BYTES = 32 * GLM52_G5_GIB;
|
package/src/model/glm52-mla.ts
CHANGED
|
@@ -484,7 +484,7 @@ export class Glm52Mla {
|
|
|
484
484
|
}
|
|
485
485
|
return this.attendAbsorbed(projection, state, selectedPositions);
|
|
486
486
|
}
|
|
487
|
-
return this.attendReconstructed(projection, state);
|
|
487
|
+
return this.attendReconstructed(projection, state, cache.leftPad);
|
|
488
488
|
} finally {
|
|
489
489
|
disposeState(state);
|
|
490
490
|
disposeProjection(projection);
|
|
@@ -495,6 +495,7 @@ export class Glm52Mla {
|
|
|
495
495
|
attendReconstructed(
|
|
496
496
|
projection: Pick<Glm52MlaProjection, "qNope" | "qRope">,
|
|
497
497
|
state: Pick<MLACompressedState, "latent" | "rope">,
|
|
498
|
+
leftPadding: readonly number[] = [],
|
|
498
499
|
): MlxArray {
|
|
499
500
|
const [batch, tokens] = projection.qNope.shape;
|
|
500
501
|
const keys = state.latent.shape[1]!;
|
|
@@ -570,14 +571,19 @@ export class Glm52Mla {
|
|
|
570
571
|
|
|
571
572
|
const prefixTokens = keys - tokens!;
|
|
572
573
|
let maskedScores = scores;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
574
|
+
const padded = leftPadding.some(value => value > 0);
|
|
575
|
+
if (tokens! > 1 || padded) {
|
|
576
|
+
const maskRows = padded ? batch! : 1;
|
|
577
|
+
const maskValues = new Float32Array(maskRows * tokens! * keys);
|
|
578
|
+
for (let row = 0; row < maskRows; row++) for (let queryToken = 0; queryToken < tokens!; queryToken++) {
|
|
576
579
|
const lastVisible = prefixTokens + queryToken;
|
|
580
|
+
const start = (row * tokens! + queryToken) * keys;
|
|
581
|
+
for (let keyToken = 0; keyToken < (leftPadding[row] ?? 0); keyToken++)
|
|
582
|
+
maskValues[start + keyToken] = -Infinity;
|
|
577
583
|
for (let keyToken = lastVisible + 1; keyToken < keys; keyToken++)
|
|
578
|
-
maskValues[
|
|
584
|
+
maskValues[start + keyToken] = -Infinity;
|
|
579
585
|
}
|
|
580
|
-
const mask = MlxArray.fromFloat32(maskValues, [
|
|
586
|
+
const mask = MlxArray.fromFloat32(maskValues, [maskRows, 1, tokens!, keys]);
|
|
581
587
|
maskedScores = ops.add(scores, mask);
|
|
582
588
|
mask.dispose();
|
|
583
589
|
scores.dispose();
|
package/src/model/glm52.ts
CHANGED
|
@@ -144,8 +144,6 @@ class Glm52DsaIndexer {
|
|
|
144
144
|
forceDense = false,
|
|
145
145
|
): DsaStep {
|
|
146
146
|
const [batch, tokens, hidden] = input.shape;
|
|
147
|
-
if (batch! > 1 && tokens !== 1)
|
|
148
|
-
throw new Error("batched DSA supports ordinary single-token decode only");
|
|
149
147
|
if (hidden !== this.config.hiddenSize)
|
|
150
148
|
throw new Error(`DSA hidden width ${hidden} != ${this.config.hiddenSize}`);
|
|
151
149
|
|
|
@@ -228,7 +226,10 @@ class Glm52DsaIndexer {
|
|
|
228
226
|
}
|
|
229
227
|
const contextLength = allKeys.shape[1]!;
|
|
230
228
|
let selectedPositions: MlxArray | Glm52MlaBatchedSelection | null;
|
|
231
|
-
if (
|
|
229
|
+
if (forceDense || contextLength <= this.config.indexTopk) {
|
|
230
|
+
state.selectFullDense(this.layer, contextLength);
|
|
231
|
+
selectedPositions = null;
|
|
232
|
+
} else if (batch! > 1) {
|
|
232
233
|
const rowSelections: Array<MlxArray | null> = [];
|
|
233
234
|
for (let row = 0; row < batch!; row++) {
|
|
234
235
|
const rowContext = (cache.rowOffsets[row] ?? 0) + tokens!;
|
|
@@ -269,15 +270,6 @@ class Glm52DsaIndexer {
|
|
|
269
270
|
scores.dispose();
|
|
270
271
|
}
|
|
271
272
|
selectedPositions = { rows: rowSelections };
|
|
272
|
-
} else if (forceDense) {
|
|
273
|
-
state.selectFullDense(this.layer, contextLength);
|
|
274
|
-
selectedPositions = null;
|
|
275
|
-
} else if (contextLength <= this.config.indexTopk) {
|
|
276
|
-
state.selectFull(
|
|
277
|
-
this.layer,
|
|
278
|
-
new Float32Array(contextLength),
|
|
279
|
-
);
|
|
280
|
-
selectedPositions = null;
|
|
281
273
|
} else {
|
|
282
274
|
if (tokens !== 1) {
|
|
283
275
|
if (allKeys !== key) allKeys.dispose();
|
|
@@ -533,6 +525,7 @@ export class Glm52DecoderLayer {
|
|
|
533
525
|
this.inputNorm,
|
|
534
526
|
this.config.rmsNormEps,
|
|
535
527
|
);
|
|
528
|
+
const batch = input.shape[0]!;
|
|
536
529
|
const tokens = input.shape[1]!;
|
|
537
530
|
const denseBenchmarkPrefill =
|
|
538
531
|
this.denseBenchmarkPrefillTokens > 0 &&
|
|
@@ -552,10 +545,10 @@ export class Glm52DecoderLayer {
|
|
|
552
545
|
for (let token = 0; token < tokens; token++) {
|
|
553
546
|
const row = normalized.slice(
|
|
554
547
|
[0, token, 0],
|
|
555
|
-
[
|
|
548
|
+
[batch, token + 1, this.config.hiddenSize],
|
|
556
549
|
);
|
|
557
550
|
let dsaKey: MlxArray | null = null;
|
|
558
|
-
let positions: MlxArray | null = null;
|
|
551
|
+
let positions: MlxArray | Glm52MlaBatchedSelection | null = null;
|
|
559
552
|
try {
|
|
560
553
|
if (this.dsa) {
|
|
561
554
|
const step = this.dsa.projectAndSelect(
|
|
@@ -564,18 +557,12 @@ export class Glm52DecoderLayer {
|
|
|
564
557
|
dsaState,
|
|
565
558
|
);
|
|
566
559
|
dsaKey = step.key;
|
|
567
|
-
if (
|
|
568
|
-
step.selectedPositions !== null &&
|
|
569
|
-
!(step.selectedPositions instanceof MlxArray)
|
|
570
|
-
) {
|
|
571
|
-
throw new Error("serial DSA verification received batched selection");
|
|
572
|
-
}
|
|
573
560
|
positions = step.selectedPositions;
|
|
574
561
|
} else {
|
|
575
|
-
positions =
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
562
|
+
positions = batch > 1
|
|
563
|
+
? { rows: Array.from({ length: batch }, (_, row) =>
|
|
564
|
+
dsaState.selectSharedPositions(this.layer, (cache.rowOffsets[row] ?? 0) + 1, row)) }
|
|
565
|
+
: dsaState.selectSharedPositions(this.layer, cache.offset + 1);
|
|
579
566
|
}
|
|
580
567
|
rows.push(this.mla.forward(
|
|
581
568
|
row,
|
|
@@ -609,7 +596,9 @@ export class Glm52DecoderLayer {
|
|
|
609
596
|
dsaKey = step.key;
|
|
610
597
|
positions = step.selectedPositions;
|
|
611
598
|
} else if (dsaState) {
|
|
612
|
-
positions = cache.
|
|
599
|
+
positions = denseBenchmarkPrefill || cache.offset + tokens <= this.config.indexTopk
|
|
600
|
+
? null
|
|
601
|
+
: cache.batchSize !== null && cache.batchSize > 1
|
|
613
602
|
? {
|
|
614
603
|
rows: cache.rowOffsets.map((offset, row) =>
|
|
615
604
|
dsaState.selectSharedPositions(
|
|
@@ -871,18 +860,13 @@ export class Glm52Model {
|
|
|
871
860
|
tokenCount > GLM52_DSA_SPARSE_DECODE_BATCH_MAX &&
|
|
872
861
|
firstOffset + tokenCount > this.glmConfig.indexTopk
|
|
873
862
|
) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
// Colibri selects independently for every query row. G2 preserves that
|
|
880
|
-
// behavior with a deliberately serial correctness fallback once a
|
|
881
|
-
// multi-token prefill crosses index_topk; G6 may vectorize it.
|
|
863
|
+
// Bound sparse-selection state for long prefills while retaining all
|
|
864
|
+
// request rows in each forward. Short verify windows keep the residual
|
|
865
|
+
// and expert graph batched across both requests and query positions.
|
|
882
866
|
const rows: MlxArray[] = [];
|
|
883
867
|
try {
|
|
884
868
|
for (let token = 0; token < tokenCount; token++) {
|
|
885
|
-
const row = ids.slice([0, token], [
|
|
869
|
+
const row = ids.slice([0, token], [ids.shape[0]!, token + 1]);
|
|
886
870
|
try {
|
|
887
871
|
rows.push(this.forwardHidden(row, cache));
|
|
888
872
|
} finally {
|
|
@@ -922,15 +906,10 @@ export class Glm52Model {
|
|
|
922
906
|
tokenCount > GLM52_DSA_SPARSE_DECODE_BATCH_MAX &&
|
|
923
907
|
firstOffset + tokenCount > this.glmConfig.indexTopk
|
|
924
908
|
) {
|
|
925
|
-
if (ids.shape.length !== 2 || ids.shape[0] !== 1) {
|
|
926
|
-
throw new Error(
|
|
927
|
-
"G3 DSA sparse prefill requires serial [1,T] token ids",
|
|
928
|
-
);
|
|
929
|
-
}
|
|
930
909
|
const rows: MlxArray[] = [];
|
|
931
910
|
try {
|
|
932
911
|
for (let token = 0; token < tokenCount; token++) {
|
|
933
|
-
const row = ids.slice([0, token], [
|
|
912
|
+
const row = ids.slice([0, token], [ids.shape[0]!, token + 1]);
|
|
934
913
|
try {
|
|
935
914
|
rows.push(await this.forwardHiddenAsync(row, cache));
|
|
936
915
|
} finally {
|