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
package/src/model/qwen3_5.ts
CHANGED
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
import type { ModelConfig } from "../config";
|
|
13
13
|
import type { Weights } from "../weights";
|
|
14
14
|
import { MlxArray } from "../mlx/array";
|
|
15
|
-
import { Dtype } from "../mlx/ffi";
|
|
15
|
+
import { Dtype, deviceArchitecture } from "../mlx/ffi";
|
|
16
16
|
import * as ops from "../mlx/ops";
|
|
17
17
|
import { CompiledFunction } from "../mlx/compile";
|
|
18
|
+
import { qwenAppendChunkSize } from "./qwen-append";
|
|
19
|
+
import { TrellisLinear, fusedGateUpEligible, fusedGateUpSwiglu, TRELLIS_MATVEC_MAX_M } from "./trellis-linear";
|
|
18
20
|
import {
|
|
19
21
|
argmaxLastPosition,
|
|
20
22
|
disposeTriple,
|
|
@@ -29,7 +31,14 @@ import {
|
|
|
29
31
|
type Cache,
|
|
30
32
|
type Mask,
|
|
31
33
|
} from "./gemma4-base";
|
|
34
|
+
import { qwen35WeightsView } from "./qwen3_5-checkpoint";
|
|
35
|
+
import { materializeCopy } from "../mlx/materialize";
|
|
32
36
|
import { gatedDeltaUpdate, SSMCache } from "./qwen3-delta";
|
|
37
|
+
import type { QwenConvolution } from "./qwen-conv";
|
|
38
|
+
import {
|
|
39
|
+
activeMrope, applyInterleavedRope, buildMropePositions, mropeInvFreq,
|
|
40
|
+
setActiveMrope, type MropeRequestState,
|
|
41
|
+
} from "./qwen3-mrope";
|
|
33
42
|
|
|
34
43
|
const PREFIX = "language_model";
|
|
35
44
|
|
|
@@ -103,6 +112,10 @@ export function compiledSilu(x: MlxArray): MlxArray {
|
|
|
103
112
|
|
|
104
113
|
/** Gated-DeltaNet linear-attention layer (mlx-lm GatedDeltaNet). */
|
|
105
114
|
export class GatedDeltaNet {
|
|
115
|
+
/** Per-model experiment seam; null retains the oracle graph. The same
|
|
116
|
+
* implementation advances speculative rollback prefixes. Borrowed inputs,
|
|
117
|
+
* owned activation and independent state tail; no global runtime mutation. */
|
|
118
|
+
convolution: QwenConvolution | null = null;
|
|
106
119
|
readonly inProjQkv: QuantizedLinear;
|
|
107
120
|
readonly inProjZ: QuantizedLinear;
|
|
108
121
|
readonly inProjB: QuantizedLinear;
|
|
@@ -142,34 +155,66 @@ export class GatedDeltaNet {
|
|
|
142
155
|
this.normWeight = weights.tensor(`${prefix}.norm.weight`);
|
|
143
156
|
}
|
|
144
157
|
|
|
145
|
-
|
|
158
|
+
#convolve(qkv: MlxArray, state: MlxArray): [MlxArray, MlxArray] {
|
|
159
|
+
if (this.convolution) return this.convolution(qkv, state, this.convWeight);
|
|
160
|
+
const [B, S, D] = qkv.shape as [number, number, number];
|
|
161
|
+
const nKeep = this.convKernel - 1;
|
|
162
|
+
const input = ops.concatAxis([state, qkv], 1);
|
|
163
|
+
// MLX copy and contiguous can both alias the whole prefill buffer.
|
|
164
|
+
// Materialize only the tail so cache residency follows its logical size.
|
|
165
|
+
const view = input.slice([0, S, 0], [B, S + nKeep, D]);
|
|
166
|
+
const tail = materializeCopy(view);
|
|
167
|
+
view.dispose();
|
|
168
|
+
const conv = ops.conv1d(input, this.convWeight, 1, 0, 1, D);
|
|
169
|
+
input.dispose();
|
|
170
|
+
const out = compiledSilu(conv);
|
|
171
|
+
conv.dispose();
|
|
172
|
+
return [out, tail];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
forward(x: MlxArray, cache: SSMCache, independentRows = false): MlxArray {
|
|
146
176
|
const [B, S] = x.shape as [number, number, number];
|
|
147
177
|
const convDim = this.keyDim * 2 + this.valueDim;
|
|
148
178
|
const nKeep = this.convKernel - 1;
|
|
179
|
+
const prof = (globalThis as Record<string, unknown>).__deltaProf as
|
|
180
|
+
Record<string, number>
|
|
181
|
+
| undefined;
|
|
182
|
+
|
|
183
|
+
// Armed speculative verify round (serve loop, serial lane): this forward
|
|
184
|
+
// must be rewindable. Snapshot = hand the REPLACED state slots to the
|
|
185
|
+
// round instead of disposing (free — arrays are immutable); record the
|
|
186
|
+
// position-local kernel inputs (qkv/a/b) and install the prefix replay.
|
|
187
|
+
const spec = cache.specRound;
|
|
188
|
+
if (spec) {
|
|
189
|
+
if (!spec.armed)
|
|
190
|
+
throw new Error("GatedDeltaNet: spec round already recorded this round");
|
|
191
|
+
spec.armed = false;
|
|
192
|
+
spec.S = S;
|
|
193
|
+
spec.replay = (c, keep) => this.#replaySpecPrefix(c, keep);
|
|
194
|
+
}
|
|
149
195
|
|
|
150
|
-
const
|
|
151
|
-
|
|
196
|
+
const t0 = prof ? performance.now() : 0;
|
|
197
|
+
const qkv = this.inProjQkv.forward(x, independentRows); // [B,S,convDim]
|
|
198
|
+
let z = this.inProjZ.forward(x, independentRows);
|
|
152
199
|
z = disposing(z, ops.reshape(z, [B, S, this.numVHeads, this.headVDim]));
|
|
153
|
-
const b = this.inProjB.forward(x); // [B,S,numVHeads]
|
|
154
|
-
const a = this.inProjA.forward(x);
|
|
200
|
+
const b = this.inProjB.forward(x, independentRows); // [B,S,numVHeads]
|
|
201
|
+
const a = this.inProjA.forward(x, independentRows);
|
|
202
|
+
if (prof) { ops.evalAll([qkv, z, b, a]); prof.proj = (prof.proj ?? 0) + performance.now() - t0; }
|
|
203
|
+
const tc = prof ? performance.now() : 0; // [B,S,numVHeads]
|
|
155
204
|
|
|
156
205
|
// Causal depthwise conv with the conv-state prefix (B=1: no ssm mask).
|
|
157
206
|
const convState =
|
|
158
207
|
cache.conv ?? ops.zeros([B, nKeep, convDim], x.dtype);
|
|
159
|
-
const
|
|
208
|
+
const [convOut, newConv] = this.#convolve(qkv, convState);
|
|
160
209
|
if (!cache.conv) convState.dispose();
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
cache.conv?.dispose();
|
|
210
|
+
if (spec) spec.qkv = qkv;
|
|
211
|
+
else qkv.dispose();
|
|
212
|
+
if (spec) spec.prevConv = cache.conv;
|
|
213
|
+
else cache.conv?.dispose();
|
|
167
214
|
cache.conv = newConv;
|
|
168
215
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const convOut = compiledSilu(conv); // nn.silu (mx.compile), matches mlx-lm
|
|
172
|
-
conv.dispose();
|
|
216
|
+
if (prof) { ops.evalAll([convOut]); prof.conv = (prof.conv ?? 0) + performance.now() - tc; }
|
|
217
|
+
const tn = prof ? performance.now() : 0;
|
|
173
218
|
|
|
174
219
|
const [qFlat, kFlat, vFlat] = ops.split(
|
|
175
220
|
convOut, [this.keyDim, 2 * this.keyDim], -1,
|
|
@@ -187,16 +232,26 @@ export class GatedDeltaNet {
|
|
|
187
232
|
q = disposing(q, ops.mulScalar(q, invScale * invScale));
|
|
188
233
|
k = disposing(k, ops.rmsNorm(k, null, 1e-6));
|
|
189
234
|
k = disposing(k, ops.mulScalar(k, invScale));
|
|
235
|
+
if (prof) { ops.evalAll([q, k]); prof.norms = (prof.norms ?? 0) + performance.now() - tn; }
|
|
236
|
+
const tk = prof ? performance.now() : 0;
|
|
190
237
|
|
|
191
238
|
const [out, newState] = gatedDeltaUpdate(
|
|
192
239
|
q, k, v, a, b, this.aLog, this.dtBias, cache.recurrent,
|
|
193
240
|
);
|
|
241
|
+
if (prof) { ops.evalAll([out]); prof.kernel = (prof.kernel ?? 0) + performance.now() - tk; }
|
|
242
|
+
const to = prof ? performance.now() : 0;
|
|
194
243
|
q.dispose();
|
|
195
244
|
k.dispose();
|
|
196
245
|
v.dispose();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
246
|
+
if (spec) {
|
|
247
|
+
spec.a = a;
|
|
248
|
+
spec.b = b;
|
|
249
|
+
spec.prevRecurrent = cache.recurrent;
|
|
250
|
+
} else {
|
|
251
|
+
a.dispose();
|
|
252
|
+
b.dispose();
|
|
253
|
+
cache.recurrent?.dispose();
|
|
254
|
+
}
|
|
200
255
|
cache.recurrent = newState;
|
|
201
256
|
cache.advance(S);
|
|
202
257
|
|
|
@@ -206,11 +261,73 @@ export class GatedDeltaNet {
|
|
|
206
261
|
z.dispose();
|
|
207
262
|
const merged = ops.reshape(gated, [B, S, this.valueDim]);
|
|
208
263
|
gated.dispose();
|
|
209
|
-
const result = this.outProj.forward(merged);
|
|
264
|
+
const result = this.outProj.forward(merged, independentRows);
|
|
210
265
|
merged.dispose();
|
|
266
|
+
if (prof) { ops.evalAll([result]); prof.out = (prof.out ?? 0) + performance.now() - to; }
|
|
211
267
|
return result;
|
|
212
268
|
}
|
|
213
269
|
|
|
270
|
+
/** Speculative rollback replay: with the pre-round snapshot RESTORED onto
|
|
271
|
+
* `cache`, re-advance the first `keep` window tokens from the recorded
|
|
272
|
+
* position-local inputs. Every op mirrors forward() on the same values —
|
|
273
|
+
* conv windows, silu, per-position norms, and the kernel's serial prefix
|
|
274
|
+
* are all independent of the rejected tail — so the resulting conv +
|
|
275
|
+
* recurrent state is BIT-EXACTLY what a forward over only the accepted
|
|
276
|
+
* prefix would have produced. Output projections (z / out_proj) are state-
|
|
277
|
+
* free and skipped: only the states matter here. */
|
|
278
|
+
#replaySpecPrefix(cache: SSMCache, keep: number): void {
|
|
279
|
+
const r = cache.specRound;
|
|
280
|
+
if (!r || !r.qkv || !r.a || !r.b) throw new Error("GatedDeltaNet replay without recorded round inputs");
|
|
281
|
+
const B = r.qkv.shape[0]!;
|
|
282
|
+
const convDim = this.keyDim * 2 + this.valueDim;
|
|
283
|
+
const nKeep = this.convKernel - 1;
|
|
284
|
+
|
|
285
|
+
const qkvPfxView = r.qkv.slice([0, 0, 0], [B, keep, convDim]);
|
|
286
|
+
const qkvPfx = ops.contiguous(qkvPfxView);
|
|
287
|
+
qkvPfxView.dispose();
|
|
288
|
+
const convState = cache.conv ?? ops.zeros([B, nKeep, convDim], qkvPfx.dtype);
|
|
289
|
+
const [convOut, newConv] = this.#convolve(qkvPfx, convState);
|
|
290
|
+
if (!cache.conv) convState.dispose();
|
|
291
|
+
qkvPfx.dispose();
|
|
292
|
+
cache.conv?.dispose();
|
|
293
|
+
cache.conv = newConv;
|
|
294
|
+
|
|
295
|
+
const [qFlat, kFlat, vFlat] = ops.split(
|
|
296
|
+
convOut, [this.keyDim, 2 * this.keyDim], -1,
|
|
297
|
+
) as [MlxArray, MlxArray, MlxArray];
|
|
298
|
+
convOut.dispose();
|
|
299
|
+
let q = ops.reshape(qFlat, [B, keep, this.numKHeads, this.headKDim]);
|
|
300
|
+
qFlat.dispose();
|
|
301
|
+
let k = ops.reshape(kFlat, [B, keep, this.numKHeads, this.headKDim]);
|
|
302
|
+
kFlat.dispose();
|
|
303
|
+
const v = disposing(vFlat, ops.reshape(vFlat, [B, keep, this.numVHeads, this.headVDim]));
|
|
304
|
+
const invScale = Math.pow(this.headKDim, -0.5);
|
|
305
|
+
q = disposing(q, ops.rmsNorm(q, null, 1e-6));
|
|
306
|
+
q = disposing(q, ops.mulScalar(q, invScale * invScale));
|
|
307
|
+
k = disposing(k, ops.rmsNorm(k, null, 1e-6));
|
|
308
|
+
k = disposing(k, ops.mulScalar(k, invScale));
|
|
309
|
+
|
|
310
|
+
const aPfxView = r.a.slice([0, 0, 0], [B, keep, this.numVHeads]);
|
|
311
|
+
const aPfx = ops.contiguous(aPfxView);
|
|
312
|
+
aPfxView.dispose();
|
|
313
|
+
const bPfxView = r.b.slice([0, 0, 0], [B, keep, this.numVHeads]);
|
|
314
|
+
const bPfx = ops.contiguous(bPfxView);
|
|
315
|
+
bPfxView.dispose();
|
|
316
|
+
|
|
317
|
+
const [y, newState] = gatedDeltaUpdate(
|
|
318
|
+
q, k, v, aPfx, bPfx, this.aLog, this.dtBias, cache.recurrent,
|
|
319
|
+
);
|
|
320
|
+
q.dispose();
|
|
321
|
+
k.dispose();
|
|
322
|
+
v.dispose();
|
|
323
|
+
aPfx.dispose();
|
|
324
|
+
bPfx.dispose();
|
|
325
|
+
y.dispose(); // only the state advance matters on the rollback path
|
|
326
|
+
cache.recurrent?.dispose();
|
|
327
|
+
cache.recurrent = newState;
|
|
328
|
+
cache.advance(keep);
|
|
329
|
+
}
|
|
330
|
+
|
|
214
331
|
private rmsNormGated(hidden: MlxArray, gate: MlxArray): MlxArray {
|
|
215
332
|
const xn = ops.rmsNorm(hidden, this.normWeight, this.eps); // bf16
|
|
216
333
|
// Oracle: _precise_swiglu(hidden, gate, xn) — ALWAYS one @mx.compile kernel
|
|
@@ -252,18 +369,18 @@ export class Qwen3Attention {
|
|
|
252
369
|
this.kNorm = new RMSNorm(weights.tensor(`${prefix}.k_norm.weight`), t.rmsNormEps);
|
|
253
370
|
}
|
|
254
371
|
|
|
255
|
-
forward(x: MlxArray, mask: Mask, cache: Cache): MlxArray {
|
|
372
|
+
forward(x: MlxArray, mask: Mask, cache: Cache, independentRows = false): MlxArray {
|
|
256
373
|
const [B, L] = x.shape as [number, number, number];
|
|
257
374
|
|
|
258
375
|
// q_proj emits 2× head_dim per head → split into queries + gate.
|
|
259
|
-
const qp = this.qProj.forward(x);
|
|
376
|
+
const qp = this.qProj.forward(x, independentRows);
|
|
260
377
|
const qpr = disposing(qp, ops.reshape(qp, [B, L, this.nHeads, this.headDim * 2]));
|
|
261
378
|
const [qHeads, gateHeads] = ops.split(qpr, [this.headDim], -1) as [MlxArray, MlxArray];
|
|
262
379
|
qpr.dispose();
|
|
263
380
|
const gate = disposing(gateHeads, ops.reshape(gateHeads, [B, L, this.nHeads * this.headDim]));
|
|
264
381
|
|
|
265
|
-
let k = this.kProj.forward(x);
|
|
266
|
-
let v = this.vProj.forward(x);
|
|
382
|
+
let k = this.kProj.forward(x, independentRows);
|
|
383
|
+
let v = this.vProj.forward(x, independentRows);
|
|
267
384
|
|
|
268
385
|
// q/k norm over head_dim BEFORE transpose (reference order).
|
|
269
386
|
let q = this.qNorm.forward(qHeads);
|
|
@@ -278,14 +395,24 @@ export class Qwen3Attention {
|
|
|
278
395
|
// Batched decode: the scheduler's mask wrapper exposes each row's REAL
|
|
279
396
|
// position as ropeOffsetArr (rows have different prompt lengths); the
|
|
280
397
|
// dynamic-offset kernel is the same fast::rope, bit-exact vs the static
|
|
281
|
-
// form (tests/compile.test.ts). Serial lane: scalar offset, unchanged.
|
|
398
|
+
// form (tests/unit/compile.test.ts). Serial lane: scalar offset, unchanged.
|
|
399
|
+
// Vision requests (serial lane only) install activeMrope for the current
|
|
400
|
+
// forward: 3D interleaved positions via the manual apply — the reference
|
|
401
|
+
// never uses the fused fast-rope kernel when position_ids are supplied,
|
|
402
|
+
// so this IS the oracle's own arithmetic. Text-only stays on ops.rope.
|
|
403
|
+
const mr = activeMrope;
|
|
282
404
|
const offArr = (cache as { ropeOffsetArr?: MlxArray }).ropeOffsetArr;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
405
|
+
if (mr) {
|
|
406
|
+
q = disposing(q, applyInterleavedRope(q, mr));
|
|
407
|
+
k = disposing(k, applyInterleavedRope(k, mr));
|
|
408
|
+
} else {
|
|
409
|
+
q = disposing(q, offArr
|
|
410
|
+
? ops.ropeDynamic(q, this.ropeDims, this.ropeBase, offArr, null)
|
|
411
|
+
: ops.rope(q, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
412
|
+
k = disposing(k, offArr
|
|
413
|
+
? ops.ropeDynamic(k, this.ropeDims, this.ropeBase, offArr, null)
|
|
414
|
+
: ops.rope(k, this.ropeDims, this.ropeBase, cache.offset, null));
|
|
415
|
+
}
|
|
289
416
|
|
|
290
417
|
let attn: MlxArray;
|
|
291
418
|
if (cache instanceof QuantizedKVCache) {
|
|
@@ -316,32 +443,56 @@ export class Qwen3Attention {
|
|
|
316
443
|
const gated = ops.mul(merged, sig);
|
|
317
444
|
merged.dispose();
|
|
318
445
|
sig.dispose();
|
|
319
|
-
const out = this.oProj.forward(gated);
|
|
446
|
+
const out = this.oProj.forward(gated, independentRows);
|
|
320
447
|
gated.dispose();
|
|
321
448
|
return out;
|
|
322
449
|
}
|
|
323
450
|
}
|
|
324
451
|
|
|
452
|
+
/** MLP projections are affine-quantized (QuantizedLinear) or, in a Q2b
|
|
453
|
+
* packed-trellis artifact, TrellisLinear — chosen per module by the config's
|
|
454
|
+
* quantization entry (`mode: "trellis"`). */
|
|
455
|
+
export type MlpLinear = QuantizedLinear | TrellisLinear;
|
|
456
|
+
function loadMlpLinear(weights: Weights, path: string, config: ModelConfig): MlpLinear {
|
|
457
|
+
if (!TrellisLinear.isTrellis(config, path)) return QuantizedLinear.load(weights, path, config);
|
|
458
|
+
// M4 Pro measurements support the 27B down projection at M3/4. The
|
|
459
|
+
// operation also checks variant, dtype and packed-code layout eligibility.
|
|
460
|
+
const useSharedScatterCodebook = config.text.hiddenSize === 5120 &&
|
|
461
|
+
config.text.intermediateSize === 17408 && path.endsWith(".down_proj");
|
|
462
|
+
return TrellisLinear.load(weights, path, config, useSharedScatterCodebook);
|
|
463
|
+
}
|
|
464
|
+
|
|
325
465
|
export class Qwen3MLP {
|
|
326
|
-
readonly gate:
|
|
327
|
-
readonly up:
|
|
328
|
-
readonly down:
|
|
466
|
+
readonly gate: MlpLinear;
|
|
467
|
+
readonly up: MlpLinear;
|
|
468
|
+
readonly down: MlpLinear;
|
|
329
469
|
|
|
330
470
|
constructor(weights: Weights, config: ModelConfig, prefix: string) {
|
|
331
|
-
this.gate =
|
|
332
|
-
this.up =
|
|
333
|
-
this.down =
|
|
471
|
+
this.gate = loadMlpLinear(weights, `${prefix}.gate_proj`, config);
|
|
472
|
+
this.up = loadMlpLinear(weights, `${prefix}.up_proj`, config);
|
|
473
|
+
this.down = loadMlpLinear(weights, `${prefix}.down_proj`, config);
|
|
334
474
|
}
|
|
335
475
|
|
|
336
|
-
forward(x: MlxArray): MlxArray {
|
|
337
|
-
|
|
338
|
-
|
|
476
|
+
forward(x: MlxArray, inputRowContiguous = false, independentRows = false): MlxArray {
|
|
477
|
+
// Packed-trellis decode (M ≤ 4): gate, up and the swiglu in ONE kernel —
|
|
478
|
+
// x read once, no gate/up vectors materialized. This Lab path retains
|
|
479
|
+
// the packed activation arithmetic documented in turboquant.md.
|
|
480
|
+
if (this.gate instanceof TrellisLinear && this.up instanceof TrellisLinear &&
|
|
481
|
+
fusedGateUpEligible(this.gate, this.up) &&
|
|
482
|
+
x.shape.slice(0, -1).reduce((a, b) => a * b, 1) <= TRELLIS_MATVEC_MAX_M) {
|
|
483
|
+
const hidden = fusedGateUpSwiglu(x, this.gate, this.up);
|
|
484
|
+
const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
|
|
485
|
+
hidden.dispose();
|
|
486
|
+
return out;
|
|
487
|
+
}
|
|
488
|
+
const g = this.gate instanceof TrellisLinear ? this.gate.forward(x, inputRowContiguous) : this.gate.forward(x, independentRows);
|
|
489
|
+
const u = this.up instanceof TrellisLinear ? this.up.forward(x, inputRowContiguous) : this.up.forward(x, independentRows);
|
|
339
490
|
// Oracle: down_proj(swiglu(gate_proj(x), up_proj(x))) — swiglu ALWAYS compiled
|
|
340
491
|
// (mlx-lm's @mx.compile swiglu; no unfused silu+mul, matches its kernel set).
|
|
341
492
|
const hidden = compiledSwiglu(g, u);
|
|
342
493
|
g.dispose();
|
|
343
494
|
u.dispose();
|
|
344
|
-
const out = this.down.forward(hidden);
|
|
495
|
+
const out = this.down instanceof QuantizedLinear ? this.down.forward(hidden, independentRows) : this.down.forward(hidden);
|
|
345
496
|
hidden.dispose();
|
|
346
497
|
return out;
|
|
347
498
|
}
|
|
@@ -366,16 +517,18 @@ export class Qwen3Layer {
|
|
|
366
517
|
this.postAttnNorm = new RMSNorm(weights.tensor(`${prefix}.post_attention_layernorm.weight`), config.text.rmsNormEps);
|
|
367
518
|
}
|
|
368
519
|
|
|
369
|
-
forward(x: MlxArray, faMask: Mask, cache: Cache): MlxArray {
|
|
520
|
+
forward(x: MlxArray, faMask: Mask, cache: Cache, independentRows = false): MlxArray {
|
|
370
521
|
const xn = this.inputNorm.forward(x);
|
|
371
522
|
const r = this.isLinear
|
|
372
|
-
? this.linearAttn!.forward(xn, cache as SSMCache)
|
|
373
|
-
: this.selfAttn!.forward(xn, faMask, cache);
|
|
523
|
+
? this.linearAttn!.forward(xn, cache as SSMCache, independentRows)
|
|
524
|
+
: this.selfAttn!.forward(xn, faMask, cache, independentRows);
|
|
374
525
|
xn.dispose();
|
|
375
526
|
const h = ops.add(x, r);
|
|
376
527
|
r.dispose();
|
|
377
528
|
const hn = this.postAttnNorm.forward(h);
|
|
378
|
-
|
|
529
|
+
// RMSNorm allocates an aligned row-contiguous output. Pass that layout
|
|
530
|
+
// proof so Trellis can match native small-prefill matmul without an eval.
|
|
531
|
+
const m = this.mlp.forward(hn, true, independentRows);
|
|
379
532
|
hn.dispose();
|
|
380
533
|
const out = ops.add(h, m);
|
|
381
534
|
h.dispose();
|
|
@@ -399,6 +552,14 @@ export class Qwen35Model {
|
|
|
399
552
|
readonly faIdx: number;
|
|
400
553
|
|
|
401
554
|
constructor(weights: Weights, config: ModelConfig) {
|
|
555
|
+
// Checkpoint-generation normalization BEFORE the first tensor() call: the
|
|
556
|
+
// 5.8-family export names the trunk `model.language_model.*` with a
|
|
557
|
+
// top-level `lm_head`, ships `mtp.*` in-repo, and stores RMSNorm gains as
|
|
558
|
+
// γ−1 with HF-layout conv1d. The view maps all of that onto the names and
|
|
559
|
+
// values this graph reads (mlx-lm's post-sanitize space). No-op — nothing
|
|
560
|
+
// installed at all — for artifacts already in that space.
|
|
561
|
+
const view = qwen35WeightsView(weights);
|
|
562
|
+
if (view) weights.setView(view);
|
|
402
563
|
this.config = config;
|
|
403
564
|
this.tied = config.text.tieWordEmbeddings;
|
|
404
565
|
this.weightsBytes = [...weights.shards.files.values()]
|
|
@@ -432,9 +593,10 @@ export class Qwen35Model {
|
|
|
432
593
|
out.set(`${p}.linear_attn.in_proj_a`, l.linearAttn.inProjA);
|
|
433
594
|
out.set(`${p}.linear_attn.out_proj`, l.linearAttn.outProj);
|
|
434
595
|
}
|
|
435
|
-
|
|
436
|
-
out.set(`${p}.mlp.
|
|
437
|
-
out.set(`${p}.mlp.
|
|
596
|
+
// Trellis-coded MLP tensors are not LoRA targets (no adapter seam yet).
|
|
597
|
+
if (l.mlp.gate instanceof QuantizedLinear) out.set(`${p}.mlp.gate_proj`, l.mlp.gate);
|
|
598
|
+
if (l.mlp.up instanceof QuantizedLinear) out.set(`${p}.mlp.up_proj`, l.mlp.up);
|
|
599
|
+
if (l.mlp.down instanceof QuantizedLinear) out.set(`${p}.mlp.down_proj`, l.mlp.down);
|
|
438
600
|
}
|
|
439
601
|
return out;
|
|
440
602
|
}
|
|
@@ -443,28 +605,142 @@ export class Qwen35Model {
|
|
|
443
605
|
return this.layers.map((l) => (l.isLinear ? new SSMCache() : new KVCache()));
|
|
444
606
|
}
|
|
445
607
|
|
|
608
|
+
/** The 27B append path keeps the native M1 affine arithmetic while Trellis
|
|
609
|
+
* shares up to four rows. Other shapes retain single-token execution. */
|
|
610
|
+
createAppend(policy: { hasAdapters: boolean; pagedKv: boolean }) {
|
|
611
|
+
const t = this.config.text;
|
|
612
|
+
if (policy.hasAdapters || policy.pagedKv || this.loraState.active.length || this.mrope ||
|
|
613
|
+
t.hiddenSize !== 5120 || t.intermediateSize !== 17408 || this.layers.length !== 64 ||
|
|
614
|
+
t.headDim !== 256 || t.numAttentionHeads !== 24 || t.numKeyValueHeads !== 4)
|
|
615
|
+
return null;
|
|
616
|
+
if (deviceArchitecture() !== "applegpu_g16s") return null;
|
|
617
|
+
const qualified = (linear: QuantizedLinear | TrellisLinear) => {
|
|
618
|
+
if (linear instanceof TrellisLinear) return true;
|
|
619
|
+
const { mode, bits, groupSize } = linear.spec;
|
|
620
|
+
return mode === "affine" && [2, 3, 4, 6, 8].includes(bits) && [32, 64, 128].includes(groupSize);
|
|
621
|
+
};
|
|
622
|
+
for (const layer of this.layers) {
|
|
623
|
+
const a = layer.linearAttn, s = layer.selfAttn;
|
|
624
|
+
const attention = a ? [a.inProjQkv, a.inProjZ, a.inProjB, a.inProjA, a.outProj]
|
|
625
|
+
: [s!.qProj, s!.kProj, s!.vProj, s!.oProj];
|
|
626
|
+
if (!attention.every(qualified) || ![layer.mlp.gate, layer.mlp.up, layer.mlp.down].every(qualified))
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
maxChunkSize: (state: readonly Cache[]) => qwenAppendChunkSize(state[0]!.offset),
|
|
631
|
+
forwardHidden: (ids: MlxArray, cache: Cache[]): MlxArray => {
|
|
632
|
+
if (ids.shape.length !== 2 || ids.shape[0] !== 1 || ids.shape[1]! > 4)
|
|
633
|
+
throw new Error("Qwen committed-token append supports one row and at most four positions");
|
|
634
|
+
const hidden = this.embed.encode(ids);
|
|
635
|
+
return this.forwardLayers(hidden, cache, true);
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
446
640
|
forwardHidden(ids: MlxArray, cache: Cache[]): MlxArray {
|
|
447
641
|
const h = this.embed.encode(ids);
|
|
448
642
|
return this.forwardLayers(h, cache);
|
|
449
643
|
}
|
|
450
644
|
|
|
451
|
-
|
|
452
|
-
|
|
645
|
+
/** Active vision mRoPE request state (serial lane; set by the generation
|
|
646
|
+
* gateway around a vision request's run, null for text-only — which keeps
|
|
647
|
+
* the bit-exact fast-rope path). While set, forwardLayers installs the
|
|
648
|
+
* per-forward interleaved cos/sin consumed by every full-attn layer. */
|
|
649
|
+
mrope: MropeRequestState | null = null;
|
|
650
|
+
/** Serving's memory-pressure check, scoped to the serial request. */
|
|
651
|
+
prefillMemoryGuard: (() => void) | null = null;
|
|
652
|
+
#mropeInvFreq: MlxArray | null = null;
|
|
653
|
+
|
|
654
|
+
/** Vision prefill: spliced input embeddings [1, L, H] (image features
|
|
655
|
+
* overwriting image-token rows — the caller builds them). Caller keeps
|
|
656
|
+
* ownership of `embeds`. bidir/ids/multimodal are the Gemma-shaped extras
|
|
657
|
+
* and are unused: Qwen3.5 vision attends fully causally. */
|
|
658
|
+
forwardEmbeddings(
|
|
659
|
+
embeds: MlxArray, cache: Cache[], _bidir: MlxArray | null,
|
|
660
|
+
_ids: MlxArray | null = null, _multimodal: MlxArray | null = null,
|
|
661
|
+
): MlxArray {
|
|
662
|
+
const h = ops.contiguous(embeds); // fresh handle — the layer loop consumes it
|
|
663
|
+
return this.forwardLayers(h, cache);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** Layer-output tap (same contract as Gemma4Model.hiddenTap): the serve
|
|
667
|
+
* loop's forwardMaybeTap sets it around prefill/verify forwards so
|
|
668
|
+
* KV-borrowing draft sources (native Qwen MTP) can read PRE-final-norm
|
|
669
|
+
* hiddens — layer 63's output stream, what mlx-vlm captures with
|
|
670
|
+
* skip_final_norm=True. No-op (and graph-identical) when null. */
|
|
671
|
+
hiddenTap: { layers: Set<number>; pos?: number; captured: Map<number, MlxArray> } | null = null;
|
|
672
|
+
|
|
673
|
+
/** Store layer `i`'s residual stream ([1,L,H]) if the tap requests it.
|
|
674
|
+
* Copies so the loop's h.dispose() can't free the capture. */
|
|
675
|
+
protected captureLayer(i: number, h: MlxArray): void {
|
|
676
|
+
const tap = this.hiddenTap;
|
|
677
|
+
if (!tap || !tap.layers.has(i)) return;
|
|
678
|
+
const H = h.shape[2]!;
|
|
679
|
+
const src = tap.pos !== undefined ? h.slice([0, tap.pos, 0], [1, tap.pos + 1, H]) : h;
|
|
680
|
+
const copy = ops.contiguous(src);
|
|
681
|
+
if (src !== h) src.dispose();
|
|
682
|
+
tap.captured.set(i, copy);
|
|
453
683
|
}
|
|
454
684
|
|
|
455
|
-
protected forwardLayers(h0: MlxArray, cache: Cache[]): MlxArray {
|
|
685
|
+
protected forwardLayers(h0: MlxArray, cache: Cache[], independentRows = false): MlxArray {
|
|
456
686
|
const L = h0.shape[1]!;
|
|
457
687
|
// One full-attention mask shared by all full layers (same offset); linear
|
|
458
688
|
// layers see no ssm mask at B=1.
|
|
459
689
|
const faMask = cache[this.faIdx]!.makeMask(L, null);
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
690
|
+
// Vision requests: one interleaved-mRoPE cos/sin table per forward,
|
|
691
|
+
// shared by all 12 full-attention layers (positions are layer-invariant).
|
|
692
|
+
let mropeFwd: ReturnType<typeof buildMropePositions> | null = null;
|
|
693
|
+
if (this.mrope) {
|
|
694
|
+
const t = this.config.text;
|
|
695
|
+
const ropeDims = Math.trunc(t.headDim * t.partialRotaryFactor);
|
|
696
|
+
const base = t.ropeParameters.full_attention?.ropeTheta ?? 10000;
|
|
697
|
+
this.#mropeInvFreq ??= mropeInvFreq(ropeDims, base);
|
|
698
|
+
mropeFwd = buildMropePositions(
|
|
699
|
+
this.mrope, cache[this.faIdx]!.offset, L, this.#mropeInvFreq, ropeDims,
|
|
700
|
+
);
|
|
701
|
+
setActiveMrope(mropeFwd);
|
|
702
|
+
}
|
|
703
|
+
let h: MlxArray | null = h0;
|
|
704
|
+
const prof = (globalThis as Record<string, unknown>).__deltaProf as
|
|
705
|
+
Record<string, number>
|
|
706
|
+
| undefined;
|
|
707
|
+
try {
|
|
708
|
+
for (let i = 0; i < this.layers.length; i++) {
|
|
709
|
+
if (L > 1) this.prefillMemoryGuard?.();
|
|
710
|
+
const tl = prof ? performance.now() : 0;
|
|
711
|
+
const next = this.layers[i]!.forward(h, faMask, cache[i]!, independentRows);
|
|
712
|
+
h.dispose();
|
|
713
|
+
h = next;
|
|
714
|
+
const mlp = this.layers[i]!.mlp;
|
|
715
|
+
if (L > TRELLIS_MATVEC_MAX_M &&
|
|
716
|
+
[mlp.gate, mlp.up, mlp.down].some((p) => p instanceof TrellisLinear && !p.fallback)) {
|
|
717
|
+
// Materialize the cache outputs too. In particular, the tiny
|
|
718
|
+
// copied conv tail is not a dependency of `h`; leaving it lazy
|
|
719
|
+
// pins the whole prefill conv buffer until the chunk ends.
|
|
720
|
+
const state = cache[i]!.state();
|
|
721
|
+
try { ops.evalAll([h, ...state]); }
|
|
722
|
+
finally { if (cache[i]!.stateNeedsDispose) for (const a of state) a.dispose(); }
|
|
723
|
+
}
|
|
724
|
+
if (prof) {
|
|
725
|
+
ops.evalAll([h]);
|
|
726
|
+
const key = this.layers[i]!.isLinear ? "layerLin" : "layerFull";
|
|
727
|
+
prof[key] = (prof[key] ?? 0) + performance.now() - tl;
|
|
728
|
+
}
|
|
729
|
+
this.captureLayer(i, h); // native-MTP pre-final-norm tap (no-op unless set)
|
|
730
|
+
}
|
|
731
|
+
const out = disposing(h, this.finalNorm.forward(h));
|
|
732
|
+
h = null; // consumed — the finally must not double-free
|
|
733
|
+
return out;
|
|
734
|
+
} finally {
|
|
735
|
+
if (mropeFwd) {
|
|
736
|
+
setActiveMrope(null);
|
|
737
|
+
mropeFwd.posIds.dispose();
|
|
738
|
+
}
|
|
739
|
+
// A mid-loop layer throw must not strand the mask array or the
|
|
740
|
+
// in-flight [1,L,H] residual (2026-08-18 review).
|
|
741
|
+
faMask.arr?.dispose();
|
|
742
|
+
h?.dispose();
|
|
465
743
|
}
|
|
466
|
-
faMask.arr?.dispose();
|
|
467
|
-
return disposing(h, this.finalNorm.forward(h));
|
|
468
744
|
}
|
|
469
745
|
|
|
470
746
|
logitsFromHidden(h: MlxArray): MlxArray {
|
package/src/model/support.ts
CHANGED
|
@@ -56,7 +56,7 @@ export function isDrafterModelType(modelType: string): boolean {
|
|
|
56
56
|
|
|
57
57
|
/** DiffusionGemma (model_type `diffusion_gemma`): block/masked-diffusion canvas
|
|
58
58
|
* model. Non-autoregressive — routed through the diffusion engine, not the AR
|
|
59
|
-
* loop. See docs/design/
|
|
59
|
+
* loop. See docs/design/generic-model-support.md. */
|
|
60
60
|
export function isDiffusionGemmaConfig(config: ModelConfig): boolean {
|
|
61
61
|
return config.modelType === "diffusion_gemma";
|
|
62
62
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Experimental variant 8, k=3 / T=256 / L<=12 only.
|
|
2
|
+
// The word-per-lane kernel uses 24 of 32 SIMD lanes for a 3-bit block.
|
|
3
|
+
// Assign eight outputs to every lane, load adjacent packed words, and align
|
|
4
|
+
// them once so each unrolled decoder has constant shifts. Row accumulation,
|
|
5
|
+
// split boundaries and the separate partial reduction stay identical to v6.
|
|
6
|
+
export const BALANCED_SCATTER_SOURCE = String.raw`
|
|
7
|
+
const uint lane = thread_index_in_simdgroup;
|
|
8
|
+
const uint sg = simdgroup_index_in_threadgroup;
|
|
9
|
+
const uint block = thread_position_in_grid.y * (uint)SG_TG + sg;
|
|
10
|
+
const uint nBlocks = (uint)C / (uint)BT;
|
|
11
|
+
const uint zi = thread_position_in_grid.z;
|
|
12
|
+
const uint sample = zi / (uint)SPLITS;
|
|
13
|
+
const uint split = zi - sample * (uint)SPLITS;
|
|
14
|
+
if (block >= nBlocks || sample >= (uint)M) return;
|
|
15
|
+
const uint wpb = (uint)(BT * K / 32);
|
|
16
|
+
const ulong rowWords = (ulong)(INTERLEAVE ? 2 : nBlocks) * wpb;
|
|
17
|
+
const uint pBase = lane * 8u * (uint)K;
|
|
18
|
+
const uint wi = pBase >> 5u;
|
|
19
|
+
const uint off = pBase & 31u;
|
|
20
|
+
const uint next = (wi + 1u == wpb) ? 0u : wi + 1u;
|
|
21
|
+
const device uint32_t* words = codes + (INTERLEAVE
|
|
22
|
+
? (ulong)(block / 2) * R * 2 * wpb + (block % 2) * wpb : (ulong)block * wpb);
|
|
23
|
+
const uint rowsPer = ((uint)R + (uint)SPLITS - 1u) / (uint)SPLITS;
|
|
24
|
+
const uint r0 = split * rowsPer;
|
|
25
|
+
const uint r1 = metal::min((uint)R, r0 + rowsPer);
|
|
26
|
+
const device T* xs = x + (ulong)sample * (ulong)R;
|
|
27
|
+
float acc[8];
|
|
28
|
+
#pragma clang loop unroll(full)
|
|
29
|
+
for (uint i = 0; i < 8u; ++i) acc[i] = 0.0f;
|
|
30
|
+
for (uint r = r0; r < r1; ++r) {
|
|
31
|
+
const uint word = words[(ulong)r * rowWords + wi];
|
|
32
|
+
const uint nxt = words[(ulong)r * rowWords + next];
|
|
33
|
+
const uint lo = off == 0u ? word : (word >> off) | (nxt << (32u - off));
|
|
34
|
+
const uint hi = off == 0u ? nxt : nxt >> off;
|
|
35
|
+
const float xr = float(xs[r]);
|
|
36
|
+
const float sr = float(scales[r]);
|
|
37
|
+
#pragma clang loop unroll(full)
|
|
38
|
+
for (uint i = 0; i < 8u; ++i) {
|
|
39
|
+
const uint shift = i * (uint)K;
|
|
40
|
+
uint win = lo >> shift;
|
|
41
|
+
if (shift + (uint)L > 32u) win |= hi << (32u - shift);
|
|
42
|
+
win &= (1u << (uint)L) - 1u;
|
|
43
|
+
const float value = (float)trellis_y(win) * (1.0f / 147.800537109375f);
|
|
44
|
+
acc[i] = metal::fma(value * sr, xr, acc[i]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const ulong outBase = ((ulong)sample * (ulong)SPLITS + split) * (ulong)C;
|
|
48
|
+
#pragma clang loop unroll(full)
|
|
49
|
+
for (uint i = 0; i < 8u; ++i)
|
|
50
|
+
partial[outBase + block * (uint)BT + (uint)BT - 1u - lane * 8u - i] = acc[i];
|
|
51
|
+
`;
|