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
|
@@ -13,8 +13,11 @@ import * as ops from "../mlx/ops";
|
|
|
13
13
|
import { toLogprobs } from "../sampler";
|
|
14
14
|
import { Glm52DecoderLayer, Glm52Model } from "../model/glm52";
|
|
15
15
|
import { MLACache } from "../model/glm52-cache";
|
|
16
|
-
import {
|
|
17
|
-
import type { DraftProvider, DraftSource } from "./source";
|
|
16
|
+
import { Glm52MtpGraph } from "./glm52-mtp-graph";
|
|
17
|
+
import type { DraftProvider, DraftSource, GroupedDraftProvider, TargetView, DraftRowSampling, DraftRowCheckpoint, DraftRowGroup, DraftPrefillGroup } from "./source";
|
|
18
|
+
import { Glm52MtpRows, type Glm52MtpRowState } from "./glm52-mtp-rows";
|
|
19
|
+
import { captureGlm52MtpState, restoreGlm52MtpState } from "./glm52-mtp-state";
|
|
20
|
+
import { applyStateChanges, disposeResources } from "../engine/resources";
|
|
18
21
|
|
|
19
22
|
type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
|
|
20
23
|
|
|
@@ -26,6 +29,10 @@ function lastToken(feed: readonly number[]): number {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export class Glm52NativeMtpProvider implements DraftProvider {
|
|
32
|
+
readonly grouped: GroupedDraftProvider = {
|
|
33
|
+
open: options => this.#openRows(options.target, options.sampling, options.checkpoints),
|
|
34
|
+
openPrefill: options => this.#openRows(options.target, null, options.checkpoints),
|
|
35
|
+
};
|
|
29
36
|
readonly id = "glm52-native-mtp";
|
|
30
37
|
readonly weightsBytes = 0;
|
|
31
38
|
readonly #layer: Glm52DecoderLayer;
|
|
@@ -57,11 +64,39 @@ export class Glm52NativeMtpProvider implements DraftProvider {
|
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
open(opts: Parameters<DraftProvider["open"]>[0]): DraftSource {
|
|
60
|
-
if (opts.target.
|
|
67
|
+
if (opts.target.identity !== this.model)
|
|
61
68
|
throw new Error("native MTP provider was opened for a different target");
|
|
62
69
|
return new Glm52NativeMtpSource(this.model, this.#layer, opts.sampler);
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
#openRows(target: TargetView, sampling: DraftRowSampling | null,
|
|
73
|
+
checkpoints: readonly (DraftRowCheckpoint | null)[]): DraftRowGroup & DraftPrefillGroup {
|
|
74
|
+
if (target.identity !== this.model) throw new Error("native MTP provider was opened for a different target");
|
|
75
|
+
const rows = new Glm52MtpRows(new Glm52MtpGraph(this.model, this.#layer), sampling);
|
|
76
|
+
const prepareAppend = (checkpoints: readonly (DraftRowCheckpoint | null)[]) => {
|
|
77
|
+
const states: (Glm52MtpRowState | null)[] = [];
|
|
78
|
+
try {
|
|
79
|
+
for (const checkpoint of checkpoints) states.push(checkpoint ? restoreGlm52MtpState(checkpoint, () => rows.makeCache()) : null);
|
|
80
|
+
return rows.prepareAppend(states);
|
|
81
|
+
} finally { disposeResources(states.flatMap(state => state ? [state.cache, state.hidden] : [])); }
|
|
82
|
+
};
|
|
83
|
+
const append = (states: readonly (DraftRowCheckpoint | null)[]) => applyStateChanges([() => prepareAppend(states)]);
|
|
84
|
+
try { append(checkpoints); } catch (error) { rows.dispose(); throw error; }
|
|
85
|
+
return {
|
|
86
|
+
namespace: "glm52-native-mtp-v1", prefillMode: "full", tapLayers: [],
|
|
87
|
+
get rowCount() { return rows.rowCount; },
|
|
88
|
+
append, prepareAppend, prefill: (tokens, context) => rows.prefill(tokens, context!),
|
|
89
|
+
materialize: rows.materialize.bind(rows), filterRows: rows.filterRows.bind(rows),
|
|
90
|
+
draft: rows.draft.bind(rows), commit: rows.commit.bind(rows),
|
|
91
|
+
capture(row) {
|
|
92
|
+
const state = rows.extractRow(row);
|
|
93
|
+
try { return captureGlm52MtpState(state); }
|
|
94
|
+
finally { state.cache.dispose(); state.hidden.dispose(); }
|
|
95
|
+
},
|
|
96
|
+
dispose: rows.dispose.bind(rows),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
65
100
|
dispose(): void {
|
|
66
101
|
// The target model owns every shared/MTP weight and the expert tier.
|
|
67
102
|
}
|
|
@@ -72,6 +107,7 @@ export class Glm52NativeMtpSource implements DraftSource {
|
|
|
72
107
|
readonly pinTargetKernelFamily = true;
|
|
73
108
|
readonly weightsBytes = 0;
|
|
74
109
|
#cache: MLACache;
|
|
110
|
+
readonly #graph: Glm52MtpGraph;
|
|
75
111
|
#roundStart = 0;
|
|
76
112
|
#lastDraftCount = 0;
|
|
77
113
|
#closed = false;
|
|
@@ -81,6 +117,7 @@ export class Glm52NativeMtpSource implements DraftSource {
|
|
|
81
117
|
readonly layer: Glm52DecoderLayer,
|
|
82
118
|
readonly sampler: Sampler,
|
|
83
119
|
) {
|
|
120
|
+
this.#graph = new Glm52MtpGraph(model, layer);
|
|
84
121
|
this.#cache = new MLACache({
|
|
85
122
|
kvLoraRank: model.glmConfig.kvLoraRank,
|
|
86
123
|
ropeHeadDim: model.glmConfig.qkRopeHeadDim,
|
|
@@ -256,142 +293,22 @@ export class Glm52NativeMtpSource implements DraftSource {
|
|
|
256
293
|
hidden: MlxArray,
|
|
257
294
|
sampleStep: number,
|
|
258
295
|
): Promise<{ hidden: MlxArray; token: number }> {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
let headInput: MlxArray | null = null;
|
|
268
|
-
let logits: MlxArray | null = null;
|
|
269
|
-
let logprobs: MlxArray | null = null;
|
|
270
|
-
let sampled: MlxArray | null = null;
|
|
271
|
-
try {
|
|
272
|
-
embedded = this.model.weights.embedding(
|
|
273
|
-
ids,
|
|
274
|
-
"model.embed_tokens.weight",
|
|
275
|
-
config.vocabSize,
|
|
276
|
-
config.hiddenSize,
|
|
277
|
-
);
|
|
278
|
-
embeddedNorm = rmsNormF32Mlx(
|
|
279
|
-
embedded,
|
|
280
|
-
this.model.weights.tensor(
|
|
281
|
-
`model.layers.${config.numHiddenLayers}.enorm.weight`,
|
|
282
|
-
),
|
|
283
|
-
config.rmsNormEps,
|
|
284
|
-
);
|
|
285
|
-
hiddenNorm = rmsNormF32Mlx(
|
|
286
|
-
hidden,
|
|
287
|
-
this.model.weights.tensor(
|
|
288
|
-
`model.layers.${config.numHiddenLayers}.hnorm.weight`,
|
|
289
|
-
),
|
|
290
|
-
config.rmsNormEps,
|
|
291
|
-
);
|
|
292
|
-
joined = ops.concatAxis([embeddedNorm, hiddenNorm], 2);
|
|
293
|
-
projected = this.model.weights.linear(
|
|
294
|
-
joined,
|
|
295
|
-
`model.layers.${config.numHiddenLayers}.eh_proj.weight`,
|
|
296
|
-
config.hiddenSize,
|
|
297
|
-
2 * config.hiddenSize,
|
|
298
|
-
);
|
|
299
|
-
output = await this.layer.forwardAsync(projected, this.#cache, null);
|
|
300
|
-
headInput = rmsNormF32Mlx(
|
|
301
|
-
output,
|
|
302
|
-
this.model.weights.tensor(
|
|
303
|
-
`model.layers.${config.numHiddenLayers}.shared_head.norm.weight`,
|
|
304
|
-
),
|
|
305
|
-
config.rmsNormEps,
|
|
306
|
-
);
|
|
307
|
-
logits = this.model.logitsFromHidden(headInput);
|
|
308
|
-
// Sampler contract is [1, V] — same 3-D top-k hazard as
|
|
309
|
-
// qwen-mtp-source #sample (2026-08-20); see the comment there.
|
|
310
|
-
{
|
|
311
|
-
const V = logits.shape[logits.shape.length - 1]!;
|
|
312
|
-
const flat = ops.reshape(logits, [1, V]);
|
|
313
|
-
logits.dispose();
|
|
314
|
-
logits = flat;
|
|
315
|
-
}
|
|
316
|
-
logprobs = toLogprobs(logits);
|
|
317
|
-
sampled = this.sampler(logprobs, sampleStep);
|
|
318
|
-
return {
|
|
319
|
-
hidden: ops.contiguous(output),
|
|
320
|
-
token: ops.itemUint32(sampled),
|
|
321
|
-
};
|
|
322
|
-
} finally {
|
|
323
|
-
ids.dispose();
|
|
324
|
-
embedded?.dispose();
|
|
325
|
-
embeddedNorm?.dispose();
|
|
326
|
-
hiddenNorm?.dispose();
|
|
327
|
-
joined?.dispose();
|
|
328
|
-
projected?.dispose();
|
|
329
|
-
output?.dispose();
|
|
330
|
-
headInput?.dispose();
|
|
331
|
-
logits?.dispose();
|
|
332
|
-
logprobs?.dispose();
|
|
333
|
-
sampled?.dispose();
|
|
334
|
-
}
|
|
296
|
+
using ids = ops.fromInt32([token], [1, 1]);
|
|
297
|
+
using output = await this.#graph.forward(ids, hidden, this.#cache);
|
|
298
|
+
using logits = this.#graph.project(output);
|
|
299
|
+
// Sampling consumes [B,V], independently of the graph's token dimension.
|
|
300
|
+
using flat = ops.reshape(logits, [1, logits.shape.at(-1)!]);
|
|
301
|
+
using logprobs = toLogprobs(flat);
|
|
302
|
+
using sampled = this.sampler(logprobs, sampleStep);
|
|
303
|
+
return { hidden: ops.contiguous(output), token: ops.itemUint32(sampled) };
|
|
335
304
|
}
|
|
336
305
|
|
|
337
|
-
async #absorb(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const ids = ops.fromInt32([...acceptedTokens], [1, k]);
|
|
344
|
-
let embedded: MlxArray | null = null;
|
|
345
|
-
let embeddedNorm: MlxArray | null = null;
|
|
346
|
-
let trueRows: MlxArray | null = null;
|
|
347
|
-
let hiddenNorm: MlxArray | null = null;
|
|
348
|
-
let joined: MlxArray | null = null;
|
|
349
|
-
let projected: MlxArray | null = null;
|
|
350
|
-
let output: MlxArray | null = null;
|
|
351
|
-
try {
|
|
352
|
-
embedded = this.model.weights.embedding(
|
|
353
|
-
ids,
|
|
354
|
-
"model.embed_tokens.weight",
|
|
355
|
-
config.vocabSize,
|
|
356
|
-
config.hiddenSize,
|
|
357
|
-
);
|
|
358
|
-
embeddedNorm = rmsNormF32Mlx(
|
|
359
|
-
embedded,
|
|
360
|
-
this.model.weights.tensor(
|
|
361
|
-
`model.layers.${config.numHiddenLayers}.enorm.weight`,
|
|
362
|
-
),
|
|
363
|
-
config.rmsNormEps,
|
|
364
|
-
);
|
|
365
|
-
trueRows = verifiedHidden.slice(
|
|
366
|
-
[0, 0, 0],
|
|
367
|
-
[1, k, config.hiddenSize],
|
|
368
|
-
);
|
|
369
|
-
hiddenNorm = rmsNormF32Mlx(
|
|
370
|
-
trueRows,
|
|
371
|
-
this.model.weights.tensor(
|
|
372
|
-
`model.layers.${config.numHiddenLayers}.hnorm.weight`,
|
|
373
|
-
),
|
|
374
|
-
config.rmsNormEps,
|
|
375
|
-
);
|
|
376
|
-
joined = ops.concatAxis([embeddedNorm, hiddenNorm], 2);
|
|
377
|
-
projected = this.model.weights.linear(
|
|
378
|
-
joined,
|
|
379
|
-
`model.layers.${config.numHiddenLayers}.eh_proj.weight`,
|
|
380
|
-
config.hiddenSize,
|
|
381
|
-
2 * config.hiddenSize,
|
|
382
|
-
);
|
|
383
|
-
output = await this.layer.forwardAsync(projected, this.#cache, null);
|
|
384
|
-
output.eval();
|
|
385
|
-
} finally {
|
|
386
|
-
ids.dispose();
|
|
387
|
-
embedded?.dispose();
|
|
388
|
-
embeddedNorm?.dispose();
|
|
389
|
-
trueRows?.dispose();
|
|
390
|
-
hiddenNorm?.dispose();
|
|
391
|
-
joined?.dispose();
|
|
392
|
-
projected?.dispose();
|
|
393
|
-
output?.dispose();
|
|
394
|
-
}
|
|
306
|
+
async #absorb(acceptedTokens: readonly number[], verifiedHidden: MlxArray): Promise<void> {
|
|
307
|
+
const count = acceptedTokens.length;
|
|
308
|
+
using ids = ops.fromInt32([...acceptedTokens], [1, count]);
|
|
309
|
+
using trueRows = verifiedHidden.slice([0, 0, 0], [1, count, this.model.glmConfig.hiddenSize]);
|
|
310
|
+
using output = await this.#graph.forward(ids, trueRows, this.#cache);
|
|
311
|
+
output.eval();
|
|
395
312
|
}
|
|
396
313
|
|
|
397
314
|
#checkOpen(): void {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import type { MLACache } from "../model/glm52-cache";
|
|
3
|
+
import { cloneAttachments } from "../backends/mlx/checkpoint-state";
|
|
4
|
+
import { disposeResources } from "../engine/resources";
|
|
5
|
+
import type { DraftRowCheckpoint } from "./source";
|
|
6
|
+
import type { Glm52MtpRowState } from "./glm52-mtp-rows";
|
|
7
|
+
|
|
8
|
+
export function captureGlm52MtpState(state: Glm52MtpRowState): DraftRowCheckpoint {
|
|
9
|
+
const held: MlxArray[] = [];
|
|
10
|
+
try {
|
|
11
|
+
if (state.cache.offset) {
|
|
12
|
+
const cache = state.cache.fetch(); held.push(cache.latent, cache.rope);
|
|
13
|
+
}
|
|
14
|
+
held.push(state.hidden.slice([0, 0, 0], [...state.hidden.shape]));
|
|
15
|
+
return { processedTokens: state.processedTokens, attachment: {
|
|
16
|
+
schema: "glm52-native-mtp-v1", metadata: { processedTokens: state.processedTokens, draftOffset: state.cache.offset },
|
|
17
|
+
tensors: held.splice(0),
|
|
18
|
+
} };
|
|
19
|
+
} finally { disposeResources(held); }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function restoreGlm52MtpState(checkpoint: DraftRowCheckpoint, makeCache: () => MLACache): Glm52MtpRowState {
|
|
23
|
+
const { attachment, processedTokens } = checkpoint;
|
|
24
|
+
if (attachment.schema !== "glm52-native-mtp-v1" || attachment.metadata.processedTokens !== processedTokens)
|
|
25
|
+
throw new Error("invalid native GLM MTP checkpoint alignment");
|
|
26
|
+
const held = cloneAttachments([attachment])[0]!.tensors;
|
|
27
|
+
let cache: MLACache | null = makeCache();
|
|
28
|
+
try {
|
|
29
|
+
const offset = attachment.metadata.draftOffset as number;
|
|
30
|
+
if (offset) { cache.restoreCompressedState(held[0]!, held[1]!, null, offset); held.splice(0, 2); }
|
|
31
|
+
const state = { cache, hidden: held.pop()!, processedTokens }; cache = null; return state;
|
|
32
|
+
} finally { disposeResources([...held, ...(cache ? [cache] : [])]); }
|
|
33
|
+
}
|
package/src/spec/ngram-source.ts
CHANGED
|
@@ -38,8 +38,80 @@
|
|
|
38
38
|
// speculation is structurally never worse than plain decode by more than the
|
|
39
39
|
// JS scan (~µs against 30k-token histories).
|
|
40
40
|
|
|
41
|
-
import { flagOn } from "../
|
|
42
|
-
import type {
|
|
41
|
+
import { flagOn } from "../runtime-config";
|
|
42
|
+
import type { MlxArray } from "../mlx/array";
|
|
43
|
+
import * as ops from "../mlx/ops";
|
|
44
|
+
import type { CheckpointAttachment } from "../backends/mlx/checkpoint-state";
|
|
45
|
+
import type { DraftProvider, DraftSource, DraftRowCheckpoint, DraftRowGroup, DraftPrefillGroup, GroupedDraftProvider } from "./source";
|
|
46
|
+
import { applyStateChanges } from "../engine/resources";
|
|
47
|
+
|
|
48
|
+
/** The matching policy is independent of the executor's feed convention. */
|
|
49
|
+
function proposeNgram(h: readonly number[], max: number, min: number, n: number): number[] {
|
|
50
|
+
const L = h.length;
|
|
51
|
+
for (let k = Math.min(max, L - 1); k >= min; k--) {
|
|
52
|
+
const tailAt = L - k;
|
|
53
|
+
search: for (let i = 0; i + k < L; i++) {
|
|
54
|
+
for (let j = 0; j < k; j++) if (h[i + j] !== h[tailAt + j]) continue search;
|
|
55
|
+
return h.slice(i + k, i + k + n);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const namespace = (max: number, min: number) => `ngram-history-v1:${max}:${min}`;
|
|
62
|
+
const captureHistory = (history: readonly number[], max: number, min: number): CheckpointAttachment => ({
|
|
63
|
+
schema: "ngram-history-v1", metadata: { max, min }, tensors: [ops.fromInt32([...history], [history.length])],
|
|
64
|
+
});
|
|
65
|
+
function restoreHistory(state: DraftRowCheckpoint, max: number, min: number): number[] {
|
|
66
|
+
const { attachment, processedTokens } = state;
|
|
67
|
+
if (attachment.schema !== "ngram-history-v1" || attachment.metadata.max !== max || attachment.metadata.min !== min ||
|
|
68
|
+
attachment.tensors.length !== 1 || attachment.tensors[0]!.shape.length !== 1 || attachment.tensors[0]!.shape[0] !== processedTokens)
|
|
69
|
+
throw new Error("ngram checkpoint does not match its token coverage or lookup policy");
|
|
70
|
+
return attachment.tensors[0]!.toIntTokens();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Each row owns only its committed token history. Verification and sampling
|
|
74
|
+
* remain in the shared method; target storage and scheduling are not needed. */
|
|
75
|
+
class NgramRows implements DraftRowGroup, DraftPrefillGroup {
|
|
76
|
+
readonly prefillMode = "tail-split" as const;
|
|
77
|
+
readonly tapLayers: readonly number[] = [];
|
|
78
|
+
readonly namespace: string;
|
|
79
|
+
#histories: number[][] = [];
|
|
80
|
+
#drafts: number[][] = [];
|
|
81
|
+
constructor(readonly max: number, readonly min: number, checkpoints: readonly (DraftRowCheckpoint | null)[]) {
|
|
82
|
+
this.namespace = namespace(max, min);
|
|
83
|
+
this.append(checkpoints);
|
|
84
|
+
}
|
|
85
|
+
get rowCount() { return this.#histories.length; }
|
|
86
|
+
prepareAppend(checkpoints: readonly (DraftRowCheckpoint | null)[]) {
|
|
87
|
+
let next = [...this.#histories, ...checkpoints.map(state => state ? restoreHistory(state, this.max, this.min) : [])];
|
|
88
|
+
return { commit: () => { const old = this.#histories; this.#histories = next; next = old; }, dispose: () => { next = []; } };
|
|
89
|
+
}
|
|
90
|
+
append(checkpoints: readonly (DraftRowCheckpoint | null)[]) { applyStateChanges([() => this.prepareAppend(checkpoints)]); }
|
|
91
|
+
filterRows(keep: readonly number[]) { this.#histories = keep.map(row => this.#histories[row]!); this.#drafts = []; }
|
|
92
|
+
prefill(tokens: MlxArray): void {
|
|
93
|
+
using packed = ops.contiguous(tokens);
|
|
94
|
+
const ids = packed.toIntTokens(), length = tokens.shape[1]!;
|
|
95
|
+
for (let row = 0; row < this.rowCount; row++) this.#histories[row]!.push(...ids.slice(row * length, (row + 1) * length));
|
|
96
|
+
}
|
|
97
|
+
materialize(): void {}
|
|
98
|
+
draft(pending: readonly number[], depth: number): number[][] {
|
|
99
|
+
this.#drafts = this.#histories.map((history, row) => {
|
|
100
|
+
history.push(pending[row]!);
|
|
101
|
+
return proposeNgram(history, this.max, this.min, depth);
|
|
102
|
+
});
|
|
103
|
+
return this.#drafts;
|
|
104
|
+
}
|
|
105
|
+
commit(accepted: readonly number[]) {
|
|
106
|
+
for (let row = 0; row < this.rowCount; row++) this.#histories[row]!.push(...this.#drafts[row]!.slice(0, accepted[row]!));
|
|
107
|
+
this.#drafts = [];
|
|
108
|
+
}
|
|
109
|
+
capture(row: number): DraftRowCheckpoint {
|
|
110
|
+
const history = this.#histories[row]!;
|
|
111
|
+
return { processedTokens: history.length, attachment: captureHistory(history, this.max, this.min) };
|
|
112
|
+
}
|
|
113
|
+
dispose() { this.#histories = []; this.#drafts = []; }
|
|
114
|
+
}
|
|
43
115
|
|
|
44
116
|
export interface NgramOptions {
|
|
45
117
|
/** Longest suffix k-gram tried first (Saxena max_ngram_size). */
|
|
@@ -53,6 +125,11 @@ export class NgramProvider implements DraftProvider {
|
|
|
53
125
|
readonly weightsBytes = 0;
|
|
54
126
|
readonly max: number;
|
|
55
127
|
readonly min: number;
|
|
128
|
+
readonly grouped: GroupedDraftProvider = {
|
|
129
|
+
supportsTargetAdapters: true,
|
|
130
|
+
open: options => new NgramRows(this.max, this.min, options.checkpoints),
|
|
131
|
+
openPrefill: options => new NgramRows(this.max, this.min, options.checkpoints),
|
|
132
|
+
};
|
|
56
133
|
|
|
57
134
|
constructor(opts: NgramOptions = {}) {
|
|
58
135
|
this.max = Math.max(1, opts.max ?? 3);
|
|
@@ -73,24 +150,33 @@ class NgramSource implements DraftSource {
|
|
|
73
150
|
readonly weightsBytes = 0;
|
|
74
151
|
#hist: number[] = [];
|
|
75
152
|
#lastDrafts: number[] = [];
|
|
153
|
+
readonly #tailSplit = flagOn("MLX_BUN_PREFILL_TAIL_SPLIT", true);
|
|
154
|
+
readonly checkpoint: NonNullable<DraftSource["checkpoint"]>;
|
|
76
155
|
|
|
77
156
|
constructor(
|
|
78
157
|
private readonly max: number,
|
|
79
158
|
private readonly min: number,
|
|
80
|
-
) {
|
|
159
|
+
) {
|
|
160
|
+
this.checkpoint = {
|
|
161
|
+
namespace: namespace(max, min),
|
|
162
|
+
capture: () => captureHistory(this.#hist, this.max, this.min),
|
|
163
|
+
restore: (processedTokens, attachment) => {
|
|
164
|
+
this.#hist = restoreHistory({ processedTokens, attachment }, this.max, this.min);
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
81
168
|
|
|
82
169
|
/** Test hook — the reconstructed prompt+emitted stream (see header). */
|
|
83
170
|
get history(): readonly number[] {
|
|
84
171
|
return this.#hist;
|
|
85
172
|
}
|
|
86
173
|
|
|
87
|
-
prefill(promptIds: number[]): void {
|
|
174
|
+
prefill(promptIds: number[], _context?: Parameters<DraftSource["prefill"]>[1], processedTokens?: number): void {
|
|
88
175
|
// Mirror the serve loop's prefill shape (two-model.ts does the same): under
|
|
89
176
|
// the oracle tail split the last prompt token is never prefilled — it IS
|
|
90
177
|
// the first feed. Legacy shape (kill switch / 1-token prompt): full prompt,
|
|
91
178
|
// and the sampled token0 arrives as the first feed.
|
|
92
|
-
const
|
|
93
|
-
const upTo = tailSplit && promptIds.length > 1 ? promptIds.length - 1 : promptIds.length;
|
|
179
|
+
const upTo = processedTokens ?? (this.#tailSplit && promptIds.length > 1 ? promptIds.length - 1 : promptIds.length);
|
|
94
180
|
this.#hist = promptIds.slice(0, upTo);
|
|
95
181
|
}
|
|
96
182
|
|
|
@@ -111,21 +197,7 @@ class NgramSource implements DraftSource {
|
|
|
111
197
|
/** Longest-k-first, first-occurrence prompt lookup (Saxena/vLLM order):
|
|
112
198
|
* find the trailing k-gram earlier in history, propose what followed it. */
|
|
113
199
|
#propose(n: number): number[] {
|
|
114
|
-
|
|
115
|
-
const L = h.length;
|
|
116
|
-
const kTop = Math.min(this.max, L - 1);
|
|
117
|
-
for (let k = kTop; k >= this.min; k--) {
|
|
118
|
-
const tailAt = L - k;
|
|
119
|
-
// First occurrence, left to right; i + k < L both excludes the trailing
|
|
120
|
-
// self-match and guarantees at least one continuation token.
|
|
121
|
-
search: for (let i = 0; i + k < L; i++) {
|
|
122
|
-
for (let j = 0; j < k; j++) {
|
|
123
|
-
if (h[i + j] !== h[tailAt + j]) continue search;
|
|
124
|
-
}
|
|
125
|
-
return h.slice(i + k, i + k + n);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return [];
|
|
200
|
+
return proposeNgram(this.#hist, this.max, this.min, n);
|
|
129
201
|
}
|
|
130
202
|
|
|
131
203
|
dispose(): void {}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { MlxArray } from "../mlx/array";
|
|
2
|
+
import * as ops from "../mlx/ops";
|
|
3
|
+
import { KVCache, type Cache } from "../model/gemma4-base";
|
|
4
|
+
import { MlxProjectedContextRows } from "../backends/mlx/projected-context-rows";
|
|
5
|
+
import { captureCacheAttachment, restoreCacheAttachment } from "../backends/mlx/cache-attachment";
|
|
6
|
+
import { applyStateChanges, disposeResources } from "../engine/resources";
|
|
7
|
+
import type { PreparedStateChange } from "../contracts/resources";
|
|
8
|
+
import type { DraftPrefillGroup, DraftRowGroup, DraftRowCheckpoint, GroupedDraftProvider, TargetView } from "./source";
|
|
9
|
+
|
|
10
|
+
export interface ProjectedDraftMethod {
|
|
11
|
+
namespace: string;
|
|
12
|
+
schema: string;
|
|
13
|
+
layers: number;
|
|
14
|
+
tapLayers: readonly number[];
|
|
15
|
+
project(hidden: MlxArray, positions: number | MlxArray): { k: MlxArray; v: MlxArray }[];
|
|
16
|
+
draft(context: ReturnType<MlxProjectedContextRows["readAttention"]>, pending: readonly number[], positions: number | MlxArray, depth: number): number[][];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Provider numerics compose with shared context membership and persistence. */
|
|
20
|
+
class ProjectedDraftRows implements DraftPrefillGroup, DraftRowGroup {
|
|
21
|
+
readonly prefillMode = "tail-split" as const;
|
|
22
|
+
readonly tapLayers: readonly number[];
|
|
23
|
+
readonly context: MlxProjectedContextRows;
|
|
24
|
+
constructor(readonly method: ProjectedDraftMethod,
|
|
25
|
+
checkpoints: readonly (DraftRowCheckpoint | null)[]) {
|
|
26
|
+
this.tapLayers = method.tapLayers;
|
|
27
|
+
this.context = new MlxProjectedContextRows(method.layers, { project: method.project });
|
|
28
|
+
try { this.append(checkpoints); } catch (error) { this.dispose(); throw error; }
|
|
29
|
+
}
|
|
30
|
+
get namespace(): string { return this.method.namespace; }
|
|
31
|
+
get rowCount(): number { return this.context.state.rowCount; }
|
|
32
|
+
prepareAppend(checkpoints: readonly (DraftRowCheckpoint | null)[]): PreparedStateChange {
|
|
33
|
+
const restored: Cache[][] = [];
|
|
34
|
+
const empty = () => Array.from({ length: this.method.layers }, () => new KVCache());
|
|
35
|
+
try {
|
|
36
|
+
for (const checkpoint of checkpoints) restored.push(checkpoint ? restoreCacheAttachment(checkpoint.attachment, empty) : empty());
|
|
37
|
+
return this.context.prepareAppend(restored);
|
|
38
|
+
} finally { disposeResources(restored.flat()); }
|
|
39
|
+
}
|
|
40
|
+
append(checkpoints: readonly (DraftRowCheckpoint | null)[]): void {
|
|
41
|
+
if (checkpoints.length) applyStateChanges([() => this.prepareAppend(checkpoints)]);
|
|
42
|
+
}
|
|
43
|
+
prefill(tokens: MlxArray, hidden?: MlxArray): void {
|
|
44
|
+
this.context.append(hidden!, Array(this.rowCount).fill(tokens.shape[1]!)); this.materialize();
|
|
45
|
+
}
|
|
46
|
+
materialize(): void { this.context.materialize(); }
|
|
47
|
+
filterRows(keep: readonly number[]): void { this.context.state.filterRows(keep); }
|
|
48
|
+
capture(row: number): DraftRowCheckpoint {
|
|
49
|
+
const caches = this.context.state.extractRow(row);
|
|
50
|
+
try {
|
|
51
|
+
const processedTokens = caches[0]!.offset;
|
|
52
|
+
return { processedTokens, attachment: captureCacheAttachment(this.method.schema, caches, { processedTokens }) };
|
|
53
|
+
} finally { disposeResources(caches); }
|
|
54
|
+
}
|
|
55
|
+
draft(pending: readonly number[], depth: number): number[][] {
|
|
56
|
+
if (!depth) return pending.map(() => []);
|
|
57
|
+
const positions = this.context.positions, donors = this.context.readAttention();
|
|
58
|
+
using offsets = positions.every(position => position === positions[0]) ? null : ops.fromInt32([...positions], [pending.length]);
|
|
59
|
+
try { return this.method.draft(donors, pending, offsets ?? positions[0]!, depth); }
|
|
60
|
+
finally { donors.dispose(); }
|
|
61
|
+
}
|
|
62
|
+
commit(accepted: readonly number[], hidden: MlxArray): void {
|
|
63
|
+
this.context.append(hidden, accepted.map(count => count + 1));
|
|
64
|
+
}
|
|
65
|
+
dispose(): void { this.context.dispose(); }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function projectedDraftGroups(bind: (target: TargetView) => ProjectedDraftMethod): GroupedDraftProvider {
|
|
69
|
+
const open = ({ target, checkpoints }: {
|
|
70
|
+
target: TargetView; checkpoints: readonly (DraftRowCheckpoint | null)[];
|
|
71
|
+
}) => new ProjectedDraftRows(bind(target), checkpoints);
|
|
72
|
+
return { open, openPrefill: open };
|
|
73
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Qwen MTP numerical graph. Request state, sampling and checkpoint retention
|
|
2
|
+
// belong to its callers; the cache supplies positions and attention storage.
|
|
3
|
+
import type { MlxArray } from "../mlx/array";
|
|
4
|
+
import * as ops from "../mlx/ops";
|
|
5
|
+
import type { ModelConfig } from "../config";
|
|
6
|
+
import type { Weights } from "../weights";
|
|
7
|
+
import { disposing, QuantizedLinear, RMSNorm, type Cache, type Mask } from "../model/gemma4-base";
|
|
8
|
+
import { DenseLinear } from "../model/universal/modules";
|
|
9
|
+
import { compiledSwiglu } from "../model/qwen3_5";
|
|
10
|
+
|
|
11
|
+
type MtpLinear = DenseLinear | QuantizedLinear;
|
|
12
|
+
|
|
13
|
+
/** Projection tensors belong to Weights; dense transpose views belong to
|
|
14
|
+
* the provider's resource stack. Quantized heads keep no dense copy. */
|
|
15
|
+
function loadMtpLinear(
|
|
16
|
+
weights: Weights, path: string, config: ModelConfig, resources: DisposableStack,
|
|
17
|
+
): MtpLinear {
|
|
18
|
+
if (weights.has(`${path}.scales`)) return QuantizedLinear.load(weights, path, config);
|
|
19
|
+
const layer = new DenseLinear(weights.tensor(`${path}.weight`), null);
|
|
20
|
+
resources.use(layer.wT);
|
|
21
|
+
return layer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Qwen3Attention.forward with the companion's dense or quantized projections.
|
|
25
|
+
* Attention operations follow src/model/qwen3_5.ts verbatim. */
|
|
26
|
+
class MtpAttention {
|
|
27
|
+
readonly qProj: MtpLinear;
|
|
28
|
+
readonly kProj: MtpLinear;
|
|
29
|
+
readonly vProj: MtpLinear;
|
|
30
|
+
readonly oProj: MtpLinear;
|
|
31
|
+
readonly qNorm: RMSNorm;
|
|
32
|
+
readonly kNorm: RMSNorm;
|
|
33
|
+
readonly nHeads: number;
|
|
34
|
+
readonly nKvHeads: number;
|
|
35
|
+
readonly headDim: number;
|
|
36
|
+
readonly scale: number;
|
|
37
|
+
readonly ropeDims: number;
|
|
38
|
+
readonly ropeBase: number;
|
|
39
|
+
|
|
40
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, resources: DisposableStack) {
|
|
41
|
+
const t = config.text;
|
|
42
|
+
this.nHeads = t.numAttentionHeads;
|
|
43
|
+
this.nKvHeads = t.numKeyValueHeads;
|
|
44
|
+
this.headDim = t.headDim;
|
|
45
|
+
this.scale = Math.pow(this.headDim, -0.5);
|
|
46
|
+
this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
|
|
47
|
+
this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
|
|
48
|
+
this.qProj = loadMtpLinear(weights, `${prefix}.q_proj`, config, resources);
|
|
49
|
+
this.kProj = loadMtpLinear(weights, `${prefix}.k_proj`, config, resources);
|
|
50
|
+
this.vProj = loadMtpLinear(weights, `${prefix}.v_proj`, config, resources);
|
|
51
|
+
this.oProj = loadMtpLinear(weights, `${prefix}.o_proj`, config, resources);
|
|
52
|
+
this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
|
|
53
|
+
this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
forward(x: MlxArray, mask: Mask, cache: Cache): MlxArray {
|
|
57
|
+
const [B, L] = x.shape as [number, number, number];
|
|
58
|
+
const qp = this.qProj.forward(x);
|
|
59
|
+
const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
|
|
60
|
+
const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
|
|
61
|
+
qpr.dispose();
|
|
62
|
+
const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
|
|
63
|
+
|
|
64
|
+
let k = this.kProj.forward(x);
|
|
65
|
+
let v = this.vProj.forward(x);
|
|
66
|
+
|
|
67
|
+
let q = this.qNorm.forward(qHeads);
|
|
68
|
+
qHeads.dispose();
|
|
69
|
+
q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3]));
|
|
70
|
+
k = disposing(k, ops.reshape(k, [B, L, this.nKvHeads, this.headDim]));
|
|
71
|
+
k = disposing(k, this.kNorm.forward(k));
|
|
72
|
+
k = disposing(k, ops.transposeAxes(k, [0, 2, 1, 3]));
|
|
73
|
+
v = disposing(v, ops.reshape(v, [B, L, this.nKvHeads, this.headDim]));
|
|
74
|
+
v = disposing(v, ops.transposeAxes(v, [0, 2, 1, 3]));
|
|
75
|
+
|
|
76
|
+
const offsets = cache.ropeOffsetArr;
|
|
77
|
+
q = disposing(q, offsets
|
|
78
|
+
? ops.ropeDynamic(q, this.ropeDims, this.ropeBase, offsets, null)
|
|
79
|
+
: ops.rope(q, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
80
|
+
k = disposing(k, offsets
|
|
81
|
+
? ops.ropeDynamic(k, this.ropeDims, this.ropeBase, offsets, null)
|
|
82
|
+
: ops.rope(k, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
83
|
+
|
|
84
|
+
const [keys, values] = cache.updateAndFetch(k, v);
|
|
85
|
+
k.dispose();
|
|
86
|
+
v.dispose();
|
|
87
|
+
const attn = ops.sdpa(q, keys, values, this.scale, mask.mode, mask.arr);
|
|
88
|
+
keys.dispose();
|
|
89
|
+
values.dispose();
|
|
90
|
+
q.dispose();
|
|
91
|
+
|
|
92
|
+
const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
|
|
93
|
+
attn.dispose();
|
|
94
|
+
const merged = ops.reshape(attnT, [B, L, -1]);
|
|
95
|
+
attnT.dispose();
|
|
96
|
+
const sig = ops.sigmoid(gate);
|
|
97
|
+
gate.dispose();
|
|
98
|
+
const gated = ops.mul(merged, sig);
|
|
99
|
+
merged.dispose();
|
|
100
|
+
sig.dispose();
|
|
101
|
+
const out = this.oProj.forward(gated);
|
|
102
|
+
gated.dispose();
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The one MTP decoder block: fc-merge → attention → swiglu MLP → norm. */
|
|
108
|
+
export class MtpModule {
|
|
109
|
+
readonly fc: MtpLinear;
|
|
110
|
+
readonly preFcNormEmbedding: RMSNorm;
|
|
111
|
+
readonly preFcNormHidden: RMSNorm;
|
|
112
|
+
readonly attn: MtpAttention;
|
|
113
|
+
readonly mlpGate: MtpLinear;
|
|
114
|
+
readonly mlpUp: MtpLinear;
|
|
115
|
+
readonly mlpDown: MtpLinear;
|
|
116
|
+
readonly inputNorm: RMSNorm;
|
|
117
|
+
readonly postAttnNorm: RMSNorm;
|
|
118
|
+
readonly finalNorm: RMSNorm;
|
|
119
|
+
|
|
120
|
+
constructor(weights: Weights, config: ModelConfig, resources: DisposableStack) {
|
|
121
|
+
const eps = config.text.rmsNormEps;
|
|
122
|
+
this.fc = loadMtpLinear(weights, "fc", config, resources);
|
|
123
|
+
this.preFcNormEmbedding = new RMSNorm(weights.tensor("pre_fc_norm_embedding.weight"), eps);
|
|
124
|
+
this.preFcNormHidden = new RMSNorm(weights.tensor("pre_fc_norm_hidden.weight"), eps);
|
|
125
|
+
this.attn = new MtpAttention(weights, config, "layers.0.self_attn", resources);
|
|
126
|
+
this.mlpGate = loadMtpLinear(weights, "layers.0.mlp.gate_proj", config, resources);
|
|
127
|
+
this.mlpUp = loadMtpLinear(weights, "layers.0.mlp.up_proj", config, resources);
|
|
128
|
+
this.mlpDown = loadMtpLinear(weights, "layers.0.mlp.down_proj", config, resources);
|
|
129
|
+
this.inputNorm = new RMSNorm(weights.tensor("layers.0.input_layernorm.weight"), eps);
|
|
130
|
+
this.postAttnNorm = new RMSNorm(weights.tensor("layers.0.post_attention_layernorm.weight"), eps);
|
|
131
|
+
this.finalNorm = new RMSNorm(weights.tensor("norm.weight"), eps);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** One block forward over [B,S,·]: token embeddings ([B,S,H], target
|
|
135
|
+
* embed_tokens output) paired with hiddens ([B,S,H], target pre-final-norm
|
|
136
|
+
* or the module's own chained output). Appends S rows to `cache`; returns
|
|
137
|
+
* the module output [B,S,H] (post final norm — what the target lm_head
|
|
138
|
+
* consumes AND what chains into the next step's `hidden`). */
|
|
139
|
+
forward(tokenEmbeds: MlxArray, hiddens: MlxArray, cache: Cache): MlxArray {
|
|
140
|
+
const embNorm = this.preFcNormEmbedding.forward(tokenEmbeds);
|
|
141
|
+
const hidNorm = this.preFcNormHidden.forward(hiddens);
|
|
142
|
+
const joined = ops.concatAxis([embNorm, hidNorm], 2);
|
|
143
|
+
embNorm.dispose();
|
|
144
|
+
hidNorm.dispose();
|
|
145
|
+
const x = this.fc.forward(joined);
|
|
146
|
+
joined.dispose();
|
|
147
|
+
|
|
148
|
+
// Decoder layer (Qwen3Layer.forward shape).
|
|
149
|
+
const L = x.shape[1]!;
|
|
150
|
+
const mask = cache.makeMask(L, null);
|
|
151
|
+
const xn = this.inputNorm.forward(x);
|
|
152
|
+
const r = this.attn.forward(xn, mask, cache);
|
|
153
|
+
xn.dispose();
|
|
154
|
+
mask.arr?.dispose();
|
|
155
|
+
const h = ops.add(x, r);
|
|
156
|
+
x.dispose();
|
|
157
|
+
r.dispose();
|
|
158
|
+
const hn = this.postAttnNorm.forward(h);
|
|
159
|
+
const g = this.mlpGate.forward(hn);
|
|
160
|
+
const u = this.mlpUp.forward(hn);
|
|
161
|
+
hn.dispose();
|
|
162
|
+
const act = compiledSwiglu(g, u);
|
|
163
|
+
g.dispose(); u.dispose();
|
|
164
|
+
const m = this.mlpDown.forward(act);
|
|
165
|
+
act.dispose();
|
|
166
|
+
const out = ops.add(h, m);
|
|
167
|
+
h.dispose();
|
|
168
|
+
m.dispose();
|
|
169
|
+
return disposing(out, this.finalNorm.forward(out));
|
|
170
|
+
}
|
|
171
|
+
}
|