mlx-bun 0.0.13 → 0.3.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 -578
- package/bin/mlx-bun.mjs +1 -1
- package/docs/README.md +91 -0
- package/docs/{design → archive}/hlg-sampling.md +3 -3
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/colibri-glm52-port.md +1 -1
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{investigations → archive/investigations}/hlg-sampling-investigation.md +1 -1
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +92 -0
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/{planning → archive/planning}/chat-ui-vision.md +1 -1
- package/docs/{planning → archive/planning}/chunk-task-roadmap.md +1 -1
- package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +4 -4
- package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +1 -1
- package/docs/{planning → archive/planning}/release-notes-v0.0.9.md +1 -1
- package/docs/archive/planning/release-notes-v0.2.0.md +49 -0
- package/docs/{planning → archive/planning}/website-readme-pass-plan.md +1 -1
- package/docs/design/batching.md +569 -0
- package/docs/design/decode-speed-program.md +3178 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +579 -334
- package/docs/design/kv-cache.md +680 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1627 -0
- package/docs/design/turboquant.md +2102 -0
- package/docs/design/unified-engine-frontier-plan.md +1831 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +34 -1
- package/docs/planning/release-notes-v0.3.0.md +35 -0
- package/docs/planning/video-series.md +421 -0
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +395 -149
- package/docs/reference/distribution.md +321 -181
- package/docs/reference/environment.md +348 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +580 -142
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +137 -6
- package/docs/reference/server-api.md +938 -422
- package/docs/reference/server-config.md +652 -320
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +10 -5
- package/src/admission-plan.ts +18 -0
- package/src/anthropic.ts +92 -3
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1646 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +47 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/gateway-binding.ts +71 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/memory-guard.ts +40 -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.ts +33 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/rollback.ts +51 -0
- package/src/backends/mlx/serial-executor.ts +273 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-views.ts +26 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/cli.ts +189 -61
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +49 -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/resources.ts +18 -0
- package/src/contracts/scheduling.ts +27 -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 +51 -0
- package/src/engine/resources.ts +42 -0
- package/src/engine/scheduler.ts +67 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +263 -23
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +10 -79
- package/src/eval/tasks/bfcl.ts +2 -1
- package/src/eval/tasks/gsm8k.ts +2 -1
- package/src/eval/tasks/hashhop.ts +2 -1
- package/src/eval/tasks/humaneval.ts +5 -4
- package/src/eval/tasks/ifeval.ts +2 -1
- package/src/eval/tasks/mmlu.ts +2 -1
- package/src/expert-io.ts +3 -1
- package/src/expert-offload.ts +3 -1
- 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 +24 -43
- package/src/generate.ts +740 -337
- package/src/grammar.ts +14 -8
- package/src/harness-pi.ts +15 -34
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +27 -0
- package/src/inference/denoising.ts +17 -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 +15 -14
- 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 +199 -0
- package/src/kv-store.ts +459 -234
- 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} +7 -5
- package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +20 -4
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +32 -170
- 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 +3 -4
- package/src/mlx/array.ts +9 -10
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +66 -22
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +64 -19
- 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/batched-mask.ts +9 -3
- package/src/model/batched-quant.ts +2 -2
- package/src/model/batched-rotating-quant.ts +45 -99
- package/src/model/batched-rotating-state.ts +73 -0
- package/src/model/batched-rotating.ts +57 -106
- package/src/model/batched-row-storage.ts +127 -0
- package/src/model/compiled-decode.ts +4 -3
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +87 -49
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/gemma4-base.ts +187 -17
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +52 -148
- package/src/model/generated/gemma4-26b.ts +34 -94
- package/src/model/generated/gemma4-e4b.ts +28 -76
- package/src/model/glm52-cache.ts +19 -9
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +28 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +7 -8
- package/src/model/profile.ts +443 -0
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta.ts +98 -2
- package/src/model/qwen3-mrope.ts +252 -0
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +336 -60
- 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/universal/dense.ts +1 -1
- package/src/model/universal/modules.ts +8 -1
- package/src/native/frame_extract.swift +140 -0
- package/src/native-pack.ts +15 -7
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +14 -320
- package/src/prompt-cache.ts +97 -54
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/atomic-output.ts +38 -0
- package/src/quantize/calibration.ts +2 -1
- package/src/quantize/config-writer.ts +16 -3
- package/src/quantize/index.ts +28 -1
- package/src/quantize/job.ts +11 -0
- package/src/quantize/quantizer.ts +265 -100
- package/src/quantize/rotate.ts +603 -0
- package/src/quantize/safetensors-writer.ts +79 -1
- package/src/quantize/sensitivity.ts +41 -59
- package/src/quantize/trellis.ts +586 -0
- package/src/quantize/weight-transform.ts +292 -0
- package/src/responses.ts +130 -3
- package/src/runtime-config.ts +81 -0
- package/src/sampler.ts +276 -4
- package/src/serve/admin-routes.ts +184 -0
- package/src/serve/aux-routes.ts +132 -0
- package/src/serve/batch-scheduler.ts +8 -1558
- 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 +354 -0
- package/src/serve/completion-sink.ts +157 -0
- package/src/serve/discovery-routes.ts +204 -0
- package/src/serve/generation-gateway.ts +211 -243
- 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/kv-budget.ts +14 -0
- package/src/serve/lab-routes.ts +341 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +155 -0
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +512 -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/prompt-response-trace.ts +147 -0
- package/src/serve/request-plan.ts +162 -0
- package/src/serve/request-prep.ts +346 -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 +101 -0
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +523 -3551
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +9 -8
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +2 -2
- 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 +5 -4
- 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-source.ts +9 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +528 -0
- package/src/spec/serve-loop.ts +179 -213
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +95 -24
- package/src/ssd-durability.ts +207 -0
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +14 -13
- package/src/train/forward.ts +38 -1
- package/src/train/loss.ts +3 -2
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +9 -6
- package/src/train/trainer.ts +9 -8
- package/src/vision/preprocess.ts +1 -1
- package/src/vision/prompt.ts +48 -2
- package/src/vision/qwen3vl-preprocess.ts +409 -0
- package/src/vision/qwen3vl-prompt.ts +196 -0
- package/src/vision/qwen3vl-tower.ts +555 -0
- package/src/vision/video-frames.ts +148 -0
- package/src/web/app.html +1 -1
- package/src/web/app.js +15 -6
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +16 -9
- 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 +134 -33
- 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 -268
- 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-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 -189
- 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 -288
- 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/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/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/investigations/hlg-ab.html +0 -34
- package/docs/investigations/hlg-explorer.html +0 -238
- package/docs/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/investigations/hlg-figs/cross.svg +0 -1
- package/docs/investigations/hlg-figs/edges.svg +0 -1
- package/docs/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/investigations/hlg-report.html +0 -262
- package/docs/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -100
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -13
- /package/docs/{investigations → archive/investigations}/chunk-finetune-experiment.md +0 -0
- /package/docs/{investigations → archive/investigations}/colibri-oracle-pin.md +0 -0
- /package/docs/{investigations → archive/investigations}/curve-bisector-routing.md +0 -0
- /package/docs/{investigations → archive/investigations}/decode-roofline-lookagain.md +0 -0
- /package/docs/{investigations → archive/investigations}/dspark-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/expert-offload-single-user-moe.md +0 -0
- /package/docs/{investigations → archive/investigations}/indexshare-performance-spike.md +0 -0
- /package/docs/{investigations → archive/investigations}/kernel-perf-review-2026-07.md +0 -0
- /package/docs/{investigations → archive/investigations}/lab-build-journal.md +0 -0
- /package/docs/{investigations → archive/investigations}/openwiki-evaluation.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-base-uf-experiment-and-directions.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-flash-cce-pin-leak.md +0 -0
- /package/docs/{investigations → archive/investigations}/orpo-uf-testing-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/pi-builtin-investigation.md +0 -0
- /package/docs/{investigations → archive/investigations}/segmented-backward-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/starter-model-port-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/steel-flash-cce-handoff.md +0 -0
- /package/docs/{investigations → archive/investigations}/trainer-validation-experiment.md +0 -0
- /package/docs/{planning → archive/planning}/chat-ui-vision-memory-first.md +0 -0
- /package/docs/{planning → archive/planning}/journal.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.10.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.11.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.12.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.0.13.md +0 -0
- /package/docs/{planning → archive/planning}/web-ui-pass-plan.md +0 -0
- /package/{docs/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
// Native Qwen3.8 MTP draft source — the Qwen-TRAINED multi-step prediction
|
|
2
|
+
// head, split from the raw release's last shard and published as
|
|
3
|
+
// mlx-community/Qwen3.8-27B-MTP-* (model_type "qwen3_5_mtp", block_size 3).
|
|
4
|
+
//
|
|
5
|
+
// Reference implementation (locate + port + gate, don't design):
|
|
6
|
+
// mlx_vlm/speculative/drafters/qwen3_5_mtp/qwen3_5_mtp.py. Mechanism, per
|
|
7
|
+
// step: h = fc(concat(rms_emb(embed(token)), rms_hidden(hidden))) → ONE
|
|
8
|
+
// full-attention decoder layer (own KVCache, positions continue the target
|
|
9
|
+
// sequence) → norm → the TARGET's lm_head → next draft token; recursive for
|
|
10
|
+
// the block ("trained with multiple steps"). The drafter is NOT standalone:
|
|
11
|
+
// it binds the target's embed_tokens + lm_head (mtp_use_dedicated_embeddings
|
|
12
|
+
// false) and consumes the target's PRE-final-norm last-layer hidden (mlx-vlm
|
|
13
|
+
// captures it with skip_final_norm=True) — carried here by the tapLayers
|
|
14
|
+
// machinery, NOT the seam's post-norm anchorHidden.
|
|
15
|
+
//
|
|
16
|
+
// Row convention (predict-2-ahead): drafter KV row at position p is built
|
|
17
|
+
// from (embed(token at position p+1), hidden at position p). Invariant
|
|
18
|
+
// across rounds: after commit, the drafter offset equals the next pending
|
|
19
|
+
// token's position minus zero — i.e. draft() always opens by building the
|
|
20
|
+
// pending token's row from the TARGET's TRUE hidden at the emitted position
|
|
21
|
+
// (held over from the verify tap). Computationally identical to mlx-vlm's
|
|
22
|
+
// commit-time "seed" append, just performed at the top of the next round.
|
|
23
|
+
//
|
|
24
|
+
// Companion projections may be dense or quantized according to their metadata.
|
|
25
|
+
// Published drafter norms are already sanitized to runtime layout; no +1.0 shift.
|
|
26
|
+
|
|
27
|
+
import { MlxArray } from "../mlx/array";
|
|
28
|
+
import * as ops from "../mlx/ops";
|
|
29
|
+
import { materializeCopy } from "../mlx/materialize";
|
|
30
|
+
import { toLogprobs } from "../sampler";
|
|
31
|
+
import { loadModelConfig, type ModelConfig } from "../config";
|
|
32
|
+
import { Weights } from "../weights";
|
|
33
|
+
import { disposing, KVCache, QuantizedLinear, RMSNorm, type Mask } from "../model/gemma4-base";
|
|
34
|
+
import { DenseLinear } from "../model/universal/modules";
|
|
35
|
+
import type { DraftProvider, DraftSource, QwenMtpTarget } from "./source";
|
|
36
|
+
import type { Cache } from "../model/gemma4";
|
|
37
|
+
import { cloneKvCaches } from "../kv-store";
|
|
38
|
+
import { cacheBytes } from "../prompt-cache";
|
|
39
|
+
import { disposeResources } from "../engine/resources";
|
|
40
|
+
import { SpeculativePrefixStore, type SpeculativePrefixState } from "./prefix-state";
|
|
41
|
+
|
|
42
|
+
type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
|
|
43
|
+
|
|
44
|
+
const DRAFT_PREFILL_CHUNK = 2048;
|
|
45
|
+
|
|
46
|
+
type MtpLinear = DenseLinear | QuantizedLinear;
|
|
47
|
+
|
|
48
|
+
/** Projection tensors belong to Weights; dense transpose views belong to
|
|
49
|
+
* the provider's resource stack. Quantized heads keep no dense copy. */
|
|
50
|
+
function loadMtpLinear(
|
|
51
|
+
weights: Weights, path: string, config: ModelConfig, resources: DisposableStack,
|
|
52
|
+
): MtpLinear {
|
|
53
|
+
if (weights.has(`${path}.scales`)) return QuantizedLinear.load(weights, path, config);
|
|
54
|
+
const layer = new DenseLinear(weights.tensor(`${path}.weight`), null);
|
|
55
|
+
resources.use(layer.wT);
|
|
56
|
+
return layer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Qwen3Attention.forward with the companion's dense or quantized projections.
|
|
60
|
+
* Attention operations follow src/model/qwen3_5.ts verbatim. */
|
|
61
|
+
class MtpAttention {
|
|
62
|
+
readonly qProj: MtpLinear;
|
|
63
|
+
readonly kProj: MtpLinear;
|
|
64
|
+
readonly vProj: MtpLinear;
|
|
65
|
+
readonly oProj: MtpLinear;
|
|
66
|
+
readonly qNorm: RMSNorm;
|
|
67
|
+
readonly kNorm: RMSNorm;
|
|
68
|
+
readonly nHeads: number;
|
|
69
|
+
readonly nKvHeads: number;
|
|
70
|
+
readonly headDim: number;
|
|
71
|
+
readonly scale: number;
|
|
72
|
+
readonly ropeDims: number;
|
|
73
|
+
readonly ropeBase: number;
|
|
74
|
+
|
|
75
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, resources: DisposableStack) {
|
|
76
|
+
const t = config.text;
|
|
77
|
+
this.nHeads = t.numAttentionHeads;
|
|
78
|
+
this.nKvHeads = t.numKeyValueHeads;
|
|
79
|
+
this.headDim = t.headDim;
|
|
80
|
+
this.scale = Math.pow(this.headDim, -0.5);
|
|
81
|
+
this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
|
|
82
|
+
this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
|
|
83
|
+
this.qProj = loadMtpLinear(weights, `${prefix}.q_proj`, config, resources);
|
|
84
|
+
this.kProj = loadMtpLinear(weights, `${prefix}.k_proj`, config, resources);
|
|
85
|
+
this.vProj = loadMtpLinear(weights, `${prefix}.v_proj`, config, resources);
|
|
86
|
+
this.oProj = loadMtpLinear(weights, `${prefix}.o_proj`, config, resources);
|
|
87
|
+
this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
|
|
88
|
+
this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
forward(x: MlxArray, mask: Mask, cache: KVCache): MlxArray {
|
|
92
|
+
const [B, L] = x.shape as [number, number, number];
|
|
93
|
+
const qp = this.qProj.forward(x);
|
|
94
|
+
const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
|
|
95
|
+
const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
|
|
96
|
+
qpr.dispose();
|
|
97
|
+
const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
|
|
98
|
+
|
|
99
|
+
let k = this.kProj.forward(x);
|
|
100
|
+
let v = this.vProj.forward(x);
|
|
101
|
+
|
|
102
|
+
let q = this.qNorm.forward(qHeads);
|
|
103
|
+
qHeads.dispose();
|
|
104
|
+
q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3]));
|
|
105
|
+
k = disposing(k, ops.reshape(k, [B, L, this.nKvHeads, this.headDim]));
|
|
106
|
+
k = disposing(k, this.kNorm.forward(k));
|
|
107
|
+
k = disposing(k, ops.transposeAxes(k, [0, 2, 1, 3]));
|
|
108
|
+
v = disposing(v, ops.reshape(v, [B, L, this.nKvHeads, this.headDim]));
|
|
109
|
+
v = disposing(v, ops.transposeAxes(v, [0, 2, 1, 3]));
|
|
110
|
+
|
|
111
|
+
q = disposing(q, ops.rope(q, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
112
|
+
k = disposing(k, ops.rope(k, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
113
|
+
|
|
114
|
+
const [keys, values] = cache.updateAndFetch(k, v);
|
|
115
|
+
k.dispose();
|
|
116
|
+
v.dispose();
|
|
117
|
+
const attn = ops.sdpa(q, keys, values, this.scale, mask.mode, mask.arr);
|
|
118
|
+
keys.dispose();
|
|
119
|
+
values.dispose();
|
|
120
|
+
q.dispose();
|
|
121
|
+
|
|
122
|
+
const attnT = ops.transposeAxes(attn, [0, 2, 1, 3]);
|
|
123
|
+
attn.dispose();
|
|
124
|
+
const merged = ops.reshape(attnT, [B, L, -1]);
|
|
125
|
+
attnT.dispose();
|
|
126
|
+
const sig = ops.sigmoid(gate);
|
|
127
|
+
gate.dispose();
|
|
128
|
+
const gated = ops.mul(merged, sig);
|
|
129
|
+
merged.dispose();
|
|
130
|
+
sig.dispose();
|
|
131
|
+
const out = this.oProj.forward(gated);
|
|
132
|
+
gated.dispose();
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The one MTP decoder block: fc-merge → attention → swiglu MLP → norm. */
|
|
138
|
+
class MtpModule {
|
|
139
|
+
readonly fc: MtpLinear;
|
|
140
|
+
readonly preFcNormEmbedding: RMSNorm;
|
|
141
|
+
readonly preFcNormHidden: RMSNorm;
|
|
142
|
+
readonly attn: MtpAttention;
|
|
143
|
+
readonly mlpGate: MtpLinear;
|
|
144
|
+
readonly mlpUp: MtpLinear;
|
|
145
|
+
readonly mlpDown: MtpLinear;
|
|
146
|
+
readonly inputNorm: RMSNorm;
|
|
147
|
+
readonly postAttnNorm: RMSNorm;
|
|
148
|
+
readonly finalNorm: RMSNorm;
|
|
149
|
+
|
|
150
|
+
constructor(weights: Weights, config: ModelConfig, resources: DisposableStack) {
|
|
151
|
+
const eps = config.text.rmsNormEps;
|
|
152
|
+
this.fc = loadMtpLinear(weights, "fc", config, resources);
|
|
153
|
+
this.preFcNormEmbedding = new RMSNorm(weights.tensor("pre_fc_norm_embedding.weight"), eps);
|
|
154
|
+
this.preFcNormHidden = new RMSNorm(weights.tensor("pre_fc_norm_hidden.weight"), eps);
|
|
155
|
+
this.attn = new MtpAttention(weights, config, "layers.0.self_attn", resources);
|
|
156
|
+
this.mlpGate = loadMtpLinear(weights, "layers.0.mlp.gate_proj", config, resources);
|
|
157
|
+
this.mlpUp = loadMtpLinear(weights, "layers.0.mlp.up_proj", config, resources);
|
|
158
|
+
this.mlpDown = loadMtpLinear(weights, "layers.0.mlp.down_proj", config, resources);
|
|
159
|
+
this.inputNorm = new RMSNorm(weights.tensor("layers.0.input_layernorm.weight"), eps);
|
|
160
|
+
this.postAttnNorm = new RMSNorm(weights.tensor("layers.0.post_attention_layernorm.weight"), eps);
|
|
161
|
+
this.finalNorm = new RMSNorm(weights.tensor("norm.weight"), eps);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** One block forward over [1,S,·]: token embeddings ([1,S,H], target
|
|
165
|
+
* embed_tokens output) paired with hiddens ([1,S,H], target pre-final-norm
|
|
166
|
+
* or the module's own chained output). Appends S rows to `cache`; returns
|
|
167
|
+
* the module output [1,S,H] (post final norm — what the target lm_head
|
|
168
|
+
* consumes AND what chains into the next step's `hidden`). */
|
|
169
|
+
forward(tokenEmbeds: MlxArray, hiddens: MlxArray, cache: KVCache): MlxArray {
|
|
170
|
+
const embNorm = this.preFcNormEmbedding.forward(tokenEmbeds);
|
|
171
|
+
const hidNorm = this.preFcNormHidden.forward(hiddens);
|
|
172
|
+
const joined = ops.concatAxis([embNorm, hidNorm], 2);
|
|
173
|
+
embNorm.dispose();
|
|
174
|
+
hidNorm.dispose();
|
|
175
|
+
const x = this.fc.forward(joined);
|
|
176
|
+
joined.dispose();
|
|
177
|
+
|
|
178
|
+
// Decoder layer (Qwen3Layer.forward shape).
|
|
179
|
+
const L = x.shape[1]!;
|
|
180
|
+
const mask = cache.makeMask(L, null);
|
|
181
|
+
const xn = this.inputNorm.forward(x);
|
|
182
|
+
const r = this.attn.forward(xn, mask, cache);
|
|
183
|
+
xn.dispose();
|
|
184
|
+
mask.arr?.dispose();
|
|
185
|
+
const h = ops.add(x, r);
|
|
186
|
+
x.dispose();
|
|
187
|
+
r.dispose();
|
|
188
|
+
const hn = this.postAttnNorm.forward(h);
|
|
189
|
+
const g = this.mlpGate.forward(hn);
|
|
190
|
+
const u = this.mlpUp.forward(hn);
|
|
191
|
+
hn.dispose();
|
|
192
|
+
const silu = ops.silu(g);
|
|
193
|
+
g.dispose();
|
|
194
|
+
const act = ops.mul(silu, u);
|
|
195
|
+
silu.dispose();
|
|
196
|
+
u.dispose();
|
|
197
|
+
const m = this.mlpDown.forward(act);
|
|
198
|
+
act.dispose();
|
|
199
|
+
const out = ops.add(h, m);
|
|
200
|
+
h.dispose();
|
|
201
|
+
m.dispose();
|
|
202
|
+
return disposing(out, this.finalNorm.forward(out));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class QwenMtpPrefixState implements SpeculativePrefixState {
|
|
207
|
+
constructor(readonly tokens: number[], readonly targetIdentity: object,
|
|
208
|
+
readonly namespace: string, readonly bytes: number, readonly target: Cache[],
|
|
209
|
+
public draft: KVCache | null, public hidden: MlxArray | null) {}
|
|
210
|
+
|
|
211
|
+
dispose(): void {
|
|
212
|
+
const resources = [...this.target.splice(0), this.draft, this.hidden];
|
|
213
|
+
this.draft = null;
|
|
214
|
+
this.hidden = null;
|
|
215
|
+
disposeResources(resources.filter((r) => r != null));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export class QwenMtpProvider implements DraftProvider {
|
|
220
|
+
readonly id: string;
|
|
221
|
+
readonly weightsBytes: number;
|
|
222
|
+
readonly #module: MtpModule;
|
|
223
|
+
readonly #config: ModelConfig;
|
|
224
|
+
readonly #resources: DisposableStack;
|
|
225
|
+
readonly #prefixStore = new SpeculativePrefixStore<QwenMtpPrefixState>();
|
|
226
|
+
|
|
227
|
+
private constructor(id: string, config: ModelConfig, weightsBytes: number, module: MtpModule, resources: DisposableStack) {
|
|
228
|
+
this.id = id;
|
|
229
|
+
this.#config = config;
|
|
230
|
+
this.#module = module;
|
|
231
|
+
this.weightsBytes = weightsBytes;
|
|
232
|
+
this.#resources = resources;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static async load(dir: string): Promise<QwenMtpProvider> {
|
|
236
|
+
const config = await loadModelConfig(dir);
|
|
237
|
+
if (config.modelType !== "qwen3_5_mtp")
|
|
238
|
+
throw new Error(`${dir}: not a qwen3_5_mtp drafter (model_type ${config.modelType})`);
|
|
239
|
+
const weights = await Weights.open(dir);
|
|
240
|
+
using resources = new DisposableStack();
|
|
241
|
+
resources.defer(() => weights.dispose());
|
|
242
|
+
const weightsBytes = [...weights.shards.files.values()]
|
|
243
|
+
.reduce((a, f) => a + f.mmap.size, 0);
|
|
244
|
+
const module = new MtpModule(weights, config, resources);
|
|
245
|
+
return new QwenMtpProvider(
|
|
246
|
+
dir.split("/").filter(Boolean).at(-1) ?? "qwen-mtp",
|
|
247
|
+
config, weightsBytes, module, resources.move(),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
open(opts: Parameters<DraftProvider["open"]>[0]): DraftSource {
|
|
252
|
+
if (this.#resources.disposed)
|
|
253
|
+
throw new Error("qwen MTP provider is disposed");
|
|
254
|
+
const target = opts.target.qwenMtp;
|
|
255
|
+
if (!target)
|
|
256
|
+
throw new Error("qwen MTP drafting requires a qwen3_5-family target");
|
|
257
|
+
if (target.hiddenSize !== this.#config.text.hiddenSize) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`qwen MTP drafter hidden ${this.#config.text.hiddenSize} != target ` +
|
|
260
|
+
`${target.hiddenSize} — split from a different checkpoint?`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
return new QwenMtpSource(target, this.#module, opts.sampler,
|
|
264
|
+
{ store: this.#prefixStore, identity: opts.target.identity });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
dispose(): void {
|
|
268
|
+
// Release cached transpose views before their native weight maps. MLX
|
|
269
|
+
// retains buffers needed by outstanding GPU commands until completion.
|
|
270
|
+
disposeResources([this.#prefixStore, { dispose: () => this.#resources.dispose() }]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export class QwenMtpSource implements DraftSource {
|
|
275
|
+
// Full-prompt target prefill (the bonus token exists before round 1, and
|
|
276
|
+
// the tap covers every prompt position — mlx-vlm's flow).
|
|
277
|
+
readonly prefillMode = "full" as const;
|
|
278
|
+
readonly weightsBytes = 0; // provider owns the drafter weights
|
|
279
|
+
/** Pre-final-norm tap: the LAST layer's output stream (index nLayers-1).
|
|
280
|
+
* The seam's anchorHidden is post-final-norm and is deliberately unused. */
|
|
281
|
+
readonly tapLayers: number[];
|
|
282
|
+
|
|
283
|
+
readonly #target: QwenMtpTarget;
|
|
284
|
+
readonly #module: MtpModule;
|
|
285
|
+
readonly #sampler: Sampler;
|
|
286
|
+
#cache = new KVCache();
|
|
287
|
+
readonly prefix: DraftSource["prefix"];
|
|
288
|
+
#prefilledTokens = 0;
|
|
289
|
+
#hasDrafted = false;
|
|
290
|
+
/** Target pre-norm hidden at the position preceding the next pending
|
|
291
|
+
* token: prefill's last tapped row, then each commit's vCtx row at the
|
|
292
|
+
* emitted position. draft() consumes it to build the pending row. [1,1,H] */
|
|
293
|
+
#pendingTrueHidden: MlxArray | null = null;
|
|
294
|
+
#roundAppended = 0;
|
|
295
|
+
#closed = false;
|
|
296
|
+
|
|
297
|
+
constructor(target: QwenMtpTarget, module: MtpModule, sampler: Sampler,
|
|
298
|
+
prefix?: { store: SpeculativePrefixStore<QwenMtpPrefixState>; identity: object }) {
|
|
299
|
+
this.#target = target;
|
|
300
|
+
this.#module = module;
|
|
301
|
+
this.#sampler = sampler;
|
|
302
|
+
this.tapLayers = [target.layerCount - 1];
|
|
303
|
+
if (prefix) this.prefix = {
|
|
304
|
+
restore: (prompt, caches, namespace, maxBytes) => {
|
|
305
|
+
this.#checkOpen();
|
|
306
|
+
if (this.#prefilledTokens !== 0 || this.#hasDrafted)
|
|
307
|
+
throw new Error("Qwen MTP prefix restore requires a fresh source");
|
|
308
|
+
const entry = prefix.store.take(prompt, prefix.identity, namespace, maxBytes);
|
|
309
|
+
if (!entry) return 0;
|
|
310
|
+
try {
|
|
311
|
+
if (entry.target.length !== caches.length || !entry.draft || !entry.hidden ||
|
|
312
|
+
entry.draft.offset !== entry.tokens.length - 1)
|
|
313
|
+
throw new Error("invalid paired Qwen MTP prefix state");
|
|
314
|
+
disposeResources(caches);
|
|
315
|
+
caches.splice(0, caches.length, ...entry.target.splice(0));
|
|
316
|
+
this.#cache.dispose();
|
|
317
|
+
this.#cache = entry.draft;
|
|
318
|
+
entry.draft = null;
|
|
319
|
+
this.#pendingTrueHidden?.dispose();
|
|
320
|
+
this.#pendingTrueHidden = entry.hidden;
|
|
321
|
+
entry.hidden = null;
|
|
322
|
+
return this.#prefilledTokens = entry.tokens.length;
|
|
323
|
+
} finally { entry.dispose(); }
|
|
324
|
+
},
|
|
325
|
+
capture: (tokens, caches, namespace, maxBytes) => {
|
|
326
|
+
this.#checkOpen();
|
|
327
|
+
if (tokens.length !== this.#prefilledTokens || !this.#pendingTrueHidden ||
|
|
328
|
+
this.#cache.offset !== tokens.length - 1 || this.#roundAppended !== 0)
|
|
329
|
+
throw new Error("Qwen MTP snapshot requires an aligned prefill boundary");
|
|
330
|
+
const bytes = cacheBytes([...caches, this.#cache]) + this.#pendingTrueHidden.nbytes;
|
|
331
|
+
if (bytes > maxBytes) { prefix.store.dispose(); return; }
|
|
332
|
+
const retained: Cache[] = [];
|
|
333
|
+
let draft: KVCache | null = null, hidden: MlxArray | null = null;
|
|
334
|
+
try {
|
|
335
|
+
retained.push(...cloneKvCaches(caches));
|
|
336
|
+
draft = cloneKvCaches([this.#cache])[0] as KVCache;
|
|
337
|
+
hidden = this.#pendingTrueHidden.slice([0, 0, 0], this.#pendingTrueHidden.shape);
|
|
338
|
+
const entry = new QwenMtpPrefixState([...tokens], prefix.identity, namespace,
|
|
339
|
+
bytes, retained.splice(0), draft, hidden);
|
|
340
|
+
draft = null;
|
|
341
|
+
hidden = null;
|
|
342
|
+
prefix.store.put(entry, maxBytes);
|
|
343
|
+
} finally { disposeResources([...retained, draft, hidden].filter((r) => r != null)); }
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Drafter prefill: rows for positions 0..L-2, keyed (token_{p+1}, h_p) —
|
|
349
|
+
* the (pending, h_{L-1}) row is NOT built here (the pending token is
|
|
350
|
+
* sampled after target prefill); its true hidden is kept for round 1. */
|
|
351
|
+
async prefill(promptIds: number[], ctxML?: MlxArray): Promise<void> {
|
|
352
|
+
this.#checkOpen();
|
|
353
|
+
if (!ctxML)
|
|
354
|
+
throw new Error("qwen MTP prefill requires the tapped pre-final-norm context");
|
|
355
|
+
try {
|
|
356
|
+
if (this.#hasDrafted)
|
|
357
|
+
throw new Error("qwen MTP prefill cannot follow drafting");
|
|
358
|
+
const L = promptIds.length;
|
|
359
|
+
const start = this.#prefilledTokens;
|
|
360
|
+
const H = ctxML.shape[2]!;
|
|
361
|
+
if (L <= start || ctxML.shape[1]! !== L - start)
|
|
362
|
+
throw new Error(`qwen MTP tap covered ${ctxML.shape[1]} of ${L - start} new prompt positions`);
|
|
363
|
+
if (start > 0) {
|
|
364
|
+
if (!this.#pendingTrueHidden || this.#cache.offset !== start - 1)
|
|
365
|
+
throw new Error("qwen MTP prefix state is not aligned");
|
|
366
|
+
this.#stepOne(promptIds[start]!, this.#pendingTrueHidden).dispose();
|
|
367
|
+
}
|
|
368
|
+
this.#pendingTrueHidden?.dispose();
|
|
369
|
+
// A slice would retain the complete prompt buffer in the saved prefix.
|
|
370
|
+
// Materialize this one row before handing off the prefill state.
|
|
371
|
+
using tail = ctxML.slice([0, L - start - 1, 0], [1, L - start, H]);
|
|
372
|
+
this.#pendingTrueHidden = materializeCopy(tail);
|
|
373
|
+
for (let pos = start; pos + 1 < L; pos += DRAFT_PREFILL_CHUNK) {
|
|
374
|
+
const n = Math.min(DRAFT_PREFILL_CHUNK, L - 1 - pos);
|
|
375
|
+
const shifted = promptIds.slice(pos + 1, pos + 1 + n);
|
|
376
|
+
const ids = ops.fromInt32(shifted, [1, n]);
|
|
377
|
+
const embeds = this.#target.embed(ids);
|
|
378
|
+
ids.dispose();
|
|
379
|
+
const hiddens = ctxML.slice([0, pos - start, 0], [1, pos + n - start, H]);
|
|
380
|
+
const out = this.#module.forward(embeds, hiddens, this.#cache);
|
|
381
|
+
embeds.dispose();
|
|
382
|
+
hiddens.dispose();
|
|
383
|
+
out.dispose(); // prefill outputs are not seeds; only KV matters here
|
|
384
|
+
// Bound the lazy KV graph at each chunk. Evaluating the unused output
|
|
385
|
+
// would also execute attention and the MLP, which prefill does not need.
|
|
386
|
+
ops.evalAll(this.#cache.state());
|
|
387
|
+
}
|
|
388
|
+
if (this.#cache.offset !== L - 1)
|
|
389
|
+
throw new Error(`qwen MTP prefill offset ${this.#cache.offset}, expected ${L - 1}`);
|
|
390
|
+
// Also covers a restored-prefix bridge with no subsequent full chunk.
|
|
391
|
+
ops.evalAll([this.#pendingTrueHidden, ...this.#cache.state()]);
|
|
392
|
+
this.#prefilledTokens = L;
|
|
393
|
+
} finally {
|
|
394
|
+
ctxML?.dispose(); // ownership per the seam contract
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async draft(feed: number[], n: number, stepBase: number): Promise<number[]> {
|
|
399
|
+
this.#checkOpen();
|
|
400
|
+
this.#hasDrafted = true;
|
|
401
|
+
if (this.#roundAppended !== 0)
|
|
402
|
+
throw new Error("qwen MTP draft called before the prior round committed");
|
|
403
|
+
if (n <= 0) return [];
|
|
404
|
+
const pending = feed.at(-1);
|
|
405
|
+
if (!Number.isSafeInteger(pending) || pending! < 0)
|
|
406
|
+
throw new Error("qwen MTP requires a non-empty token feed");
|
|
407
|
+
|
|
408
|
+
const drafts: number[] = [];
|
|
409
|
+
let chained: MlxArray | null = null;
|
|
410
|
+
const startOffset = this.#cache.offset;
|
|
411
|
+
try {
|
|
412
|
+
// Build the pending token's row from the TRUE target hidden at the
|
|
413
|
+
// preceding position (prefill tail on round 1, verify tap afterwards).
|
|
414
|
+
if (!this.#pendingTrueHidden)
|
|
415
|
+
throw new Error("qwen MTP draft before prefill/commit");
|
|
416
|
+
const out = this.#stepOne(pending!, this.#pendingTrueHidden);
|
|
417
|
+
this.#pendingTrueHidden.dispose();
|
|
418
|
+
this.#pendingTrueHidden = null;
|
|
419
|
+
this.#roundAppended++;
|
|
420
|
+
chained = out;
|
|
421
|
+
drafts.push(this.#sample(out, stepBase));
|
|
422
|
+
while (drafts.length < n) {
|
|
423
|
+
const out = this.#stepOne(drafts.at(-1)!, chained!);
|
|
424
|
+
chained!.dispose();
|
|
425
|
+
chained = out;
|
|
426
|
+
this.#roundAppended++;
|
|
427
|
+
drafts.push(this.#sample(out, stepBase + drafts.length));
|
|
428
|
+
}
|
|
429
|
+
return drafts;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
const appended = this.#cache.offset - startOffset;
|
|
432
|
+
if (appended > 0) this.#cache.trim(appended);
|
|
433
|
+
this.#roundAppended = 0;
|
|
434
|
+
throw error;
|
|
435
|
+
} finally {
|
|
436
|
+
chained?.dispose();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** mlx-vlm accept semantics: keep drafted rows for accepted positions,
|
|
441
|
+
* trim the rejected tail, then append the missing accepted row (all-accept
|
|
442
|
+
* case) and the correction/bonus row using the TARGET's verified pre-norm
|
|
443
|
+
* hiddens — harvesting the last output as the next round's seed. */
|
|
444
|
+
async commit(
|
|
445
|
+
d: number,
|
|
446
|
+
kAccept: number,
|
|
447
|
+
vCtxML?: MlxArray,
|
|
448
|
+
_verifiedHidden?: MlxArray,
|
|
449
|
+
acceptedTokens: readonly number[] = [],
|
|
450
|
+
): Promise<void> {
|
|
451
|
+
this.#checkOpen();
|
|
452
|
+
try {
|
|
453
|
+
if (!vCtxML)
|
|
454
|
+
throw new Error("qwen MTP commit requires the tapped verify context");
|
|
455
|
+
if (kAccept > 0 && acceptedTokens.length !== kAccept)
|
|
456
|
+
throw new Error(
|
|
457
|
+
`qwen MTP received ${acceptedTokens.length} accepted tokens for k=${kAccept}`,
|
|
458
|
+
);
|
|
459
|
+
// Rows appended this round cover window tokens 0..d-1 (pending +
|
|
460
|
+
// drafts[0..d-2]); rows keyed by ACCEPTED window tokens (0..kAccept)
|
|
461
|
+
// survive — trim the rejected tail.
|
|
462
|
+
const trim = Math.max(this.#roundAppended - 1 - kAccept, 0);
|
|
463
|
+
if (trim > 0) this.#cache.trim(trim);
|
|
464
|
+
|
|
465
|
+
// All-accept case: window token d (= drafts[d-1]) has no drafted row —
|
|
466
|
+
// append it from the TRUE verify hidden (vCtx row d-1, the hidden at
|
|
467
|
+
// its preceding position). The correction/bonus row is NOT built here:
|
|
468
|
+
// the serve loop re-feeds that token in the next draft()'s `feed`, and
|
|
469
|
+
// draft() opens by building its row from #pendingTrueHidden below.
|
|
470
|
+
const H = vCtxML.shape[2]!;
|
|
471
|
+
if (kAccept === d && d > 0) {
|
|
472
|
+
const ids = ops.fromInt32([acceptedTokens[kAccept - 1]!], [1, 1]);
|
|
473
|
+
const embeds = this.#target.embed(ids);
|
|
474
|
+
ids.dispose();
|
|
475
|
+
const hidden = vCtxML.slice([0, d - 1, 0], [1, d, H]);
|
|
476
|
+
const out = this.#module.forward(embeds, hidden, this.#cache);
|
|
477
|
+
embeds.dispose();
|
|
478
|
+
hidden.dispose();
|
|
479
|
+
out.dispose();
|
|
480
|
+
}
|
|
481
|
+
// Next round's pending row input: the target's verified pre-norm
|
|
482
|
+
// hidden at the emitted position.
|
|
483
|
+
this.#pendingTrueHidden?.dispose();
|
|
484
|
+
this.#pendingTrueHidden = vCtxML.slice([0, kAccept, 0], [1, kAccept + 1, H]);
|
|
485
|
+
} finally {
|
|
486
|
+
vCtxML?.dispose(); // seam: commit takes ownership
|
|
487
|
+
this.#roundAppended = 0;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
dispose(): void {
|
|
492
|
+
if (this.#closed) return;
|
|
493
|
+
this.#closed = true;
|
|
494
|
+
this.#cache.dispose();
|
|
495
|
+
this.#pendingTrueHidden?.dispose();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** One module forward for (token, hidden) — appends one KV row. */
|
|
499
|
+
#stepOne(token: number, hidden: MlxArray): MlxArray {
|
|
500
|
+
using ids = ops.fromInt32([token], [1, 1]);
|
|
501
|
+
using embed = this.#target.embed(ids);
|
|
502
|
+
ids.dispose();
|
|
503
|
+
return this.#module.forward(embed, hidden, this.#cache);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** Sample a draft token from the module output via the TARGET's lm head
|
|
507
|
+
* and the request sampler (per-step RNG stream discipline). */
|
|
508
|
+
#sample(moduleOut: MlxArray, step: number): number {
|
|
509
|
+
using logits = this.#target.logitsFromHidden(moduleOut);
|
|
510
|
+
// Sampler contract is [1, V] (the main decode loop's shape). moduleOut
|
|
511
|
+
// is [1, 1, H] → logits [1, 1, V]; without this reshape any sampler
|
|
512
|
+
// that slices 2-D (top-k) throws "[slice] Invalid number of indices…
|
|
513
|
+
// dimension 3" — the serve-lane MTP 500 (chat defaults carry the
|
|
514
|
+
// model's top_k=20; the greedy bench harness never hit it).
|
|
515
|
+
const V = logits.shape[logits.shape.length - 1]!;
|
|
516
|
+
using flat = ops.reshape(logits, [1, V]);
|
|
517
|
+
logits.dispose();
|
|
518
|
+
using logprobs = toLogprobs(flat);
|
|
519
|
+
flat.dispose();
|
|
520
|
+
using tok = this.#sampler(logprobs, step);
|
|
521
|
+
logprobs.dispose();
|
|
522
|
+
return ops.itemUint32(tok);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
#checkOpen(): void {
|
|
526
|
+
if (this.#closed) throw new Error("qwen MTP source is closed");
|
|
527
|
+
}
|
|
528
|
+
}
|