mlx-bun 0.2.0 → 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 -588
- package/docs/README.md +91 -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/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- 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 +2 -2
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +394 -159
- package/docs/reference/distribution.md +321 -182
- package/docs/reference/environment.md +348 -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 +104 -6
- package/docs/reference/server-api.md +886 -425
- package/docs/reference/server-config.md +638 -330
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +9 -4
- 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 +58 -25
- 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 +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 +1 -1
- package/src/generate.ts +656 -268
- 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/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-store.ts +109 -49
- 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/{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 +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 +9 -10
- 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/batched-mask.ts +8 -2
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +2 -2
- package/src/model/batched-rotating.ts +1 -1
- package/src/model/compiled-decode.ts +1 -1
- 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/gemma4-base.ts +26 -9
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +2 -2
- package/src/model/generated/gemma4-26b.ts +2 -2
- package/src/model/generated/gemma4-e4b.ts +2 -2
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +6 -8
- 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_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +140 -56
- 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-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/prompt-cache.ts +96 -53
- 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 +4 -4
- 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 +154 -193
- 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 +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/request-plan.ts +19 -15
- 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 +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +317 -2649
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +8 -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 +4 -3
- 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 +1 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +166 -69
- package/src/spec/serve-loop.ts +154 -156
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +80 -12
- package/src/ssd-durability.ts +25 -13
- 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 +5 -3
- 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
|
@@ -21,31 +21,48 @@
|
|
|
21
21
|
// (held over from the verify tap). Computationally identical to mlx-vlm's
|
|
22
22
|
// commit-time "seed" append, just performed at the top of the next round.
|
|
23
23
|
//
|
|
24
|
-
//
|
|
25
|
-
// already sanitized
|
|
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
26
|
|
|
27
27
|
import { MlxArray } from "../mlx/array";
|
|
28
28
|
import * as ops from "../mlx/ops";
|
|
29
|
+
import { materializeCopy } from "../mlx/materialize";
|
|
29
30
|
import { toLogprobs } from "../sampler";
|
|
30
31
|
import { loadModelConfig, type ModelConfig } from "../config";
|
|
31
32
|
import { Weights } from "../weights";
|
|
32
|
-
import { disposing, KVCache, RMSNorm, type Mask } from "../model/gemma4-base";
|
|
33
|
+
import { disposing, KVCache, QuantizedLinear, RMSNorm, type Mask } from "../model/gemma4-base";
|
|
33
34
|
import { DenseLinear } from "../model/universal/modules";
|
|
34
|
-
import {
|
|
35
|
-
import type {
|
|
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";
|
|
36
41
|
|
|
37
42
|
type Sampler = (logprobs: MlxArray, step: number) => MlxArray;
|
|
38
43
|
|
|
39
44
|
const DRAFT_PREFILL_CHUNK = 2048;
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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. */
|
|
44
61
|
class MtpAttention {
|
|
45
|
-
readonly qProj:
|
|
46
|
-
readonly kProj:
|
|
47
|
-
readonly vProj:
|
|
48
|
-
readonly oProj:
|
|
62
|
+
readonly qProj: MtpLinear;
|
|
63
|
+
readonly kProj: MtpLinear;
|
|
64
|
+
readonly vProj: MtpLinear;
|
|
65
|
+
readonly oProj: MtpLinear;
|
|
49
66
|
readonly qNorm: RMSNorm;
|
|
50
67
|
readonly kNorm: RMSNorm;
|
|
51
68
|
readonly nHeads: number;
|
|
@@ -55,7 +72,7 @@ class MtpAttention {
|
|
|
55
72
|
readonly ropeDims: number;
|
|
56
73
|
readonly ropeBase: number;
|
|
57
74
|
|
|
58
|
-
constructor(weights: Weights, config: ModelConfig, prefix: string) {
|
|
75
|
+
constructor(weights: Weights, config: ModelConfig, prefix: string, resources: DisposableStack) {
|
|
59
76
|
const t = config.text;
|
|
60
77
|
this.nHeads = t.numAttentionHeads;
|
|
61
78
|
this.nKvHeads = t.numKeyValueHeads;
|
|
@@ -63,12 +80,10 @@ class MtpAttention {
|
|
|
63
80
|
this.scale = Math.pow(this.headDim, -0.5);
|
|
64
81
|
this.ropeDims = Math.trunc(this.headDim * t.partialRotaryFactor);
|
|
65
82
|
this.ropeBase = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
70
|
-
this.vProj = dense(`${prefix}.v_proj`);
|
|
71
|
-
this.oProj = dense(`${prefix}.o_proj`);
|
|
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);
|
|
72
87
|
this.qNorm = new RMSNorm(weights.tensor(`${prefix}.q_norm.weight`), t.rmsNormEps);
|
|
73
88
|
this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
|
|
74
89
|
}
|
|
@@ -121,28 +136,26 @@ class MtpAttention {
|
|
|
121
136
|
|
|
122
137
|
/** The one MTP decoder block: fc-merge → attention → swiglu MLP → norm. */
|
|
123
138
|
class MtpModule {
|
|
124
|
-
readonly fc:
|
|
139
|
+
readonly fc: MtpLinear;
|
|
125
140
|
readonly preFcNormEmbedding: RMSNorm;
|
|
126
141
|
readonly preFcNormHidden: RMSNorm;
|
|
127
142
|
readonly attn: MtpAttention;
|
|
128
|
-
readonly mlpGate:
|
|
129
|
-
readonly mlpUp:
|
|
130
|
-
readonly mlpDown:
|
|
143
|
+
readonly mlpGate: MtpLinear;
|
|
144
|
+
readonly mlpUp: MtpLinear;
|
|
145
|
+
readonly mlpDown: MtpLinear;
|
|
131
146
|
readonly inputNorm: RMSNorm;
|
|
132
147
|
readonly postAttnNorm: RMSNorm;
|
|
133
148
|
readonly finalNorm: RMSNorm;
|
|
134
149
|
|
|
135
|
-
constructor(weights: Weights, config: ModelConfig) {
|
|
150
|
+
constructor(weights: Weights, config: ModelConfig, resources: DisposableStack) {
|
|
136
151
|
const eps = config.text.rmsNormEps;
|
|
137
|
-
|
|
138
|
-
new DenseLinear(weights.tensor(`${path}.weight`), null);
|
|
139
|
-
this.fc = dense("fc");
|
|
152
|
+
this.fc = loadMtpLinear(weights, "fc", config, resources);
|
|
140
153
|
this.preFcNormEmbedding = new RMSNorm(weights.tensor("pre_fc_norm_embedding.weight"), eps);
|
|
141
154
|
this.preFcNormHidden = new RMSNorm(weights.tensor("pre_fc_norm_hidden.weight"), eps);
|
|
142
|
-
this.attn = new MtpAttention(weights, config, "layers.0.self_attn");
|
|
143
|
-
this.mlpGate =
|
|
144
|
-
this.mlpUp =
|
|
145
|
-
this.mlpDown =
|
|
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);
|
|
146
159
|
this.inputNorm = new RMSNorm(weights.tensor("layers.0.input_layernorm.weight"), eps);
|
|
147
160
|
this.postAttnNorm = new RMSNorm(weights.tensor("layers.0.post_attention_layernorm.weight"), eps);
|
|
148
161
|
this.finalNorm = new RMSNorm(weights.tensor("norm.weight"), eps);
|
|
@@ -162,7 +175,7 @@ class MtpModule {
|
|
|
162
175
|
const x = this.fc.forward(joined);
|
|
163
176
|
joined.dispose();
|
|
164
177
|
|
|
165
|
-
// Decoder layer (Qwen3Layer.forward shape
|
|
178
|
+
// Decoder layer (Qwen3Layer.forward shape).
|
|
166
179
|
const L = x.shape[1]!;
|
|
167
180
|
const mask = cache.makeMask(L, null);
|
|
168
181
|
const xn = this.inputNorm.forward(x);
|
|
@@ -190,18 +203,33 @@ class MtpModule {
|
|
|
190
203
|
}
|
|
191
204
|
}
|
|
192
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
|
+
|
|
193
219
|
export class QwenMtpProvider implements DraftProvider {
|
|
194
220
|
readonly id: string;
|
|
195
221
|
readonly weightsBytes: number;
|
|
196
222
|
readonly #module: MtpModule;
|
|
197
223
|
readonly #config: ModelConfig;
|
|
224
|
+
readonly #resources: DisposableStack;
|
|
225
|
+
readonly #prefixStore = new SpeculativePrefixStore<QwenMtpPrefixState>();
|
|
198
226
|
|
|
199
|
-
private constructor(id: string, config: ModelConfig,
|
|
227
|
+
private constructor(id: string, config: ModelConfig, weightsBytes: number, module: MtpModule, resources: DisposableStack) {
|
|
200
228
|
this.id = id;
|
|
201
229
|
this.#config = config;
|
|
202
230
|
this.#module = module;
|
|
203
|
-
this.weightsBytes =
|
|
204
|
-
|
|
231
|
+
this.weightsBytes = weightsBytes;
|
|
232
|
+
this.#resources = resources;
|
|
205
233
|
}
|
|
206
234
|
|
|
207
235
|
static async load(dir: string): Promise<QwenMtpProvider> {
|
|
@@ -209,28 +237,37 @@ export class QwenMtpProvider implements DraftProvider {
|
|
|
209
237
|
if (config.modelType !== "qwen3_5_mtp")
|
|
210
238
|
throw new Error(`${dir}: not a qwen3_5_mtp drafter (model_type ${config.modelType})`);
|
|
211
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);
|
|
212
245
|
return new QwenMtpProvider(
|
|
213
246
|
dir.split("/").filter(Boolean).at(-1) ?? "qwen-mtp",
|
|
214
|
-
config,
|
|
247
|
+
config, weightsBytes, module, resources.move(),
|
|
215
248
|
);
|
|
216
249
|
}
|
|
217
250
|
|
|
218
251
|
open(opts: Parameters<DraftProvider["open"]>[0]): DraftSource {
|
|
219
|
-
|
|
220
|
-
|
|
252
|
+
if (this.#resources.disposed)
|
|
253
|
+
throw new Error("qwen MTP provider is disposed");
|
|
254
|
+
const target = opts.target.qwenMtp;
|
|
255
|
+
if (!target)
|
|
221
256
|
throw new Error("qwen MTP drafting requires a qwen3_5-family target");
|
|
222
|
-
if (target.
|
|
257
|
+
if (target.hiddenSize !== this.#config.text.hiddenSize) {
|
|
223
258
|
throw new Error(
|
|
224
259
|
`qwen MTP drafter hidden ${this.#config.text.hiddenSize} != target ` +
|
|
225
|
-
`${target.
|
|
260
|
+
`${target.hiddenSize} — split from a different checkpoint?`,
|
|
226
261
|
);
|
|
227
262
|
}
|
|
228
|
-
return new QwenMtpSource(target, this.#module, opts.sampler
|
|
263
|
+
return new QwenMtpSource(target, this.#module, opts.sampler,
|
|
264
|
+
{ store: this.#prefixStore, identity: opts.target.identity });
|
|
229
265
|
}
|
|
230
266
|
|
|
231
267
|
dispose(): void {
|
|
232
|
-
//
|
|
233
|
-
//
|
|
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() }]);
|
|
234
271
|
}
|
|
235
272
|
}
|
|
236
273
|
|
|
@@ -243,10 +280,13 @@ export class QwenMtpSource implements DraftSource {
|
|
|
243
280
|
* The seam's anchorHidden is post-final-norm and is deliberately unused. */
|
|
244
281
|
readonly tapLayers: number[];
|
|
245
282
|
|
|
246
|
-
readonly #target:
|
|
283
|
+
readonly #target: QwenMtpTarget;
|
|
247
284
|
readonly #module: MtpModule;
|
|
248
285
|
readonly #sampler: Sampler;
|
|
249
|
-
|
|
286
|
+
#cache = new KVCache();
|
|
287
|
+
readonly prefix: DraftSource["prefix"];
|
|
288
|
+
#prefilledTokens = 0;
|
|
289
|
+
#hasDrafted = false;
|
|
250
290
|
/** Target pre-norm hidden at the position preceding the next pending
|
|
251
291
|
* token: prefill's last tapped row, then each commit's vCtx row at the
|
|
252
292
|
* emitted position. draft() consumes it to build the pending row. [1,1,H] */
|
|
@@ -254,11 +294,55 @@ export class QwenMtpSource implements DraftSource {
|
|
|
254
294
|
#roundAppended = 0;
|
|
255
295
|
#closed = false;
|
|
256
296
|
|
|
257
|
-
constructor(target:
|
|
297
|
+
constructor(target: QwenMtpTarget, module: MtpModule, sampler: Sampler,
|
|
298
|
+
prefix?: { store: SpeculativePrefixStore<QwenMtpPrefixState>; identity: object }) {
|
|
258
299
|
this.#target = target;
|
|
259
300
|
this.#module = module;
|
|
260
301
|
this.#sampler = sampler;
|
|
261
|
-
this.tapLayers = [target.
|
|
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
|
+
};
|
|
262
346
|
}
|
|
263
347
|
|
|
264
348
|
/** Drafter prefill: rows for positions 0..L-2, keyed (token_{p+1}, h_p) —
|
|
@@ -269,27 +353,43 @@ export class QwenMtpSource implements DraftSource {
|
|
|
269
353
|
if (!ctxML)
|
|
270
354
|
throw new Error("qwen MTP prefill requires the tapped pre-final-norm context");
|
|
271
355
|
try {
|
|
272
|
-
if (this.#
|
|
273
|
-
throw new Error("qwen MTP
|
|
356
|
+
if (this.#hasDrafted)
|
|
357
|
+
throw new Error("qwen MTP prefill cannot follow drafting");
|
|
274
358
|
const L = promptIds.length;
|
|
359
|
+
const start = this.#prefilledTokens;
|
|
275
360
|
const H = ctxML.shape[2]!;
|
|
276
|
-
if (ctxML.shape[1]! !== L)
|
|
277
|
-
throw new Error(`qwen MTP tap covered ${ctxML.shape[1]} of ${L} prompt positions`);
|
|
278
|
-
|
|
279
|
-
|
|
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) {
|
|
280
374
|
const n = Math.min(DRAFT_PREFILL_CHUNK, L - 1 - pos);
|
|
281
375
|
const shifted = promptIds.slice(pos + 1, pos + 1 + n);
|
|
282
376
|
const ids = ops.fromInt32(shifted, [1, n]);
|
|
283
|
-
const embeds = this.#target.embed
|
|
377
|
+
const embeds = this.#target.embed(ids);
|
|
284
378
|
ids.dispose();
|
|
285
|
-
const hiddens = ctxML.slice([0, pos, 0], [1, pos + n, H]);
|
|
379
|
+
const hiddens = ctxML.slice([0, pos - start, 0], [1, pos + n - start, H]);
|
|
286
380
|
const out = this.#module.forward(embeds, hiddens, this.#cache);
|
|
287
381
|
embeds.dispose();
|
|
288
382
|
hiddens.dispose();
|
|
289
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());
|
|
290
387
|
}
|
|
291
388
|
if (this.#cache.offset !== L - 1)
|
|
292
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;
|
|
293
393
|
} finally {
|
|
294
394
|
ctxML?.dispose(); // ownership per the seam contract
|
|
295
395
|
}
|
|
@@ -297,6 +397,7 @@ export class QwenMtpSource implements DraftSource {
|
|
|
297
397
|
|
|
298
398
|
async draft(feed: number[], n: number, stepBase: number): Promise<number[]> {
|
|
299
399
|
this.#checkOpen();
|
|
400
|
+
this.#hasDrafted = true;
|
|
300
401
|
if (this.#roundAppended !== 0)
|
|
301
402
|
throw new Error("qwen MTP draft called before the prior round committed");
|
|
302
403
|
if (n <= 0) return [];
|
|
@@ -316,8 +417,8 @@ export class QwenMtpSource implements DraftSource {
|
|
|
316
417
|
this.#pendingTrueHidden.dispose();
|
|
317
418
|
this.#pendingTrueHidden = null;
|
|
318
419
|
this.#roundAppended++;
|
|
319
|
-
drafts.push(this.#sample(out, stepBase));
|
|
320
420
|
chained = out;
|
|
421
|
+
drafts.push(this.#sample(out, stepBase));
|
|
321
422
|
while (drafts.length < n) {
|
|
322
423
|
const out = this.#stepOne(drafts.at(-1)!, chained!);
|
|
323
424
|
chained!.dispose();
|
|
@@ -369,7 +470,7 @@ export class QwenMtpSource implements DraftSource {
|
|
|
369
470
|
const H = vCtxML.shape[2]!;
|
|
370
471
|
if (kAccept === d && d > 0) {
|
|
371
472
|
const ids = ops.fromInt32([acceptedTokens[kAccept - 1]!], [1, 1]);
|
|
372
|
-
const embeds = this.#target.embed
|
|
473
|
+
const embeds = this.#target.embed(ids);
|
|
373
474
|
ids.dispose();
|
|
374
475
|
const hidden = vCtxML.slice([0, d - 1, 0], [1, d, H]);
|
|
375
476
|
const out = this.#module.forward(embeds, hidden, this.#cache);
|
|
@@ -396,33 +497,29 @@ export class QwenMtpSource implements DraftSource {
|
|
|
396
497
|
|
|
397
498
|
/** One module forward for (token, hidden) — appends one KV row. */
|
|
398
499
|
#stepOne(token: number, hidden: MlxArray): MlxArray {
|
|
399
|
-
|
|
400
|
-
|
|
500
|
+
using ids = ops.fromInt32([token], [1, 1]);
|
|
501
|
+
using embed = this.#target.embed(ids);
|
|
401
502
|
ids.dispose();
|
|
402
|
-
|
|
403
|
-
embed.dispose();
|
|
404
|
-
return out;
|
|
503
|
+
return this.#module.forward(embed, hidden, this.#cache);
|
|
405
504
|
}
|
|
406
505
|
|
|
407
506
|
/** Sample a draft token from the module output via the TARGET's lm head
|
|
408
507
|
* and the request sampler (per-step RNG stream discipline). */
|
|
409
508
|
#sample(moduleOut: MlxArray, step: number): number {
|
|
410
|
-
|
|
509
|
+
using logits = this.#target.logitsFromHidden(moduleOut);
|
|
411
510
|
// Sampler contract is [1, V] (the main decode loop's shape). moduleOut
|
|
412
511
|
// is [1, 1, H] → logits [1, 1, V]; without this reshape any sampler
|
|
413
512
|
// that slices 2-D (top-k) throws "[slice] Invalid number of indices…
|
|
414
513
|
// dimension 3" — the serve-lane MTP 500 (chat defaults carry the
|
|
415
514
|
// model's top_k=20; the greedy bench harness never hit it).
|
|
416
515
|
const V = logits.shape[logits.shape.length - 1]!;
|
|
417
|
-
|
|
516
|
+
using flat = ops.reshape(logits, [1, V]);
|
|
418
517
|
logits.dispose();
|
|
419
|
-
|
|
518
|
+
using logprobs = toLogprobs(flat);
|
|
420
519
|
flat.dispose();
|
|
421
|
-
|
|
520
|
+
using tok = this.#sampler(logprobs, step);
|
|
422
521
|
logprobs.dispose();
|
|
423
|
-
|
|
424
|
-
tok.dispose();
|
|
425
|
-
return id;
|
|
522
|
+
return ops.itemUint32(tok);
|
|
426
523
|
}
|
|
427
524
|
|
|
428
525
|
#checkOpen(): void {
|