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/mlx/array.ts
CHANGED
|
@@ -46,17 +46,16 @@ let nextPinId = 1;
|
|
|
46
46
|
// do nothing — a dtor mlx can safely call from ANY thread. The pointed-to
|
|
47
47
|
// memory's lifetime is the CALLER's contract (weight mmaps live for the
|
|
48
48
|
// process; restored KV is COPIED at load since 2026-07-07 — see kv-store.ts
|
|
49
|
-
// loadKvCache — so no restore mapping needs pinning anymore).
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
dlsym: { args: ["u64", "ptr"], returns: "u64" },
|
|
49
|
+
// loadKvCache — so no restore mapping needs pinning anymore). Bun >= 1.4
|
|
50
|
+
// returns a numeric function address from .ptr; verified against dlsym on
|
|
51
|
+
// 1.4.0 and 1.4.2. Keep the library open for every native destructor's lifetime.
|
|
52
|
+
const libcFree = dlopen("/usr/lib/libSystem.B.dylib", {
|
|
53
|
+
free: { args: ["ptr"], returns: "void" },
|
|
55
54
|
});
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
// The pinned bun-types predates the runtime's numeric .ptr property.
|
|
56
|
+
const freeFnAddr = Number(Reflect.get(libcFree.symbols.free, "ptr"));
|
|
57
|
+
if (!Number.isSafeInteger(freeFnAddr) || freeFnAddr <= 0)
|
|
58
|
+
throw new Error("free.ptr is invalid — zero-copy arrays need a native no-op dtor");
|
|
60
59
|
|
|
61
60
|
export function pinnedBufferCount(): number {
|
|
62
61
|
return pinned.size;
|
package/src/mlx/compile.ts
CHANGED
|
@@ -23,8 +23,14 @@ import { MlxArray } from "./array";
|
|
|
23
23
|
|
|
24
24
|
export type TraceFn = (inputs: MlxArray[]) => MlxArray[];
|
|
25
25
|
|
|
26
|
-
const traceRegistry = new Map<number, { fn: TraceFn; count: number }>();
|
|
26
|
+
const traceRegistry = new Map<number, { fn: TraceFn; count: number; shapeless: boolean }>();
|
|
27
27
|
let nextTraceId = 1;
|
|
28
|
+
let shapelessTracing = false;
|
|
29
|
+
|
|
30
|
+
/** Fixed-shape custom kernels must fall back while a shapeless graph traces. */
|
|
31
|
+
export function isShapelessTracing(): boolean {
|
|
32
|
+
return shapelessTracing;
|
|
33
|
+
}
|
|
28
34
|
/** Error thrown by a trace fn, carried across the C boundary (the
|
|
29
35
|
* trampoline must not throw into mlx; it returns non-zero instead). */
|
|
30
36
|
let traceError: unknown = null;
|
|
@@ -40,9 +46,11 @@ export let traceCalls = 0;
|
|
|
40
46
|
// error handler in ffi.ts).
|
|
41
47
|
const trampoline = new JSCallback(
|
|
42
48
|
(resPtr: number, argsVec: bigint, payload: number): number => {
|
|
49
|
+
const previousShapeless = shapelessTracing;
|
|
43
50
|
try {
|
|
44
51
|
const entry = traceRegistry.get(payload);
|
|
45
52
|
if (!entry) throw new Error(`compile trampoline: unknown trace id ${payload}`);
|
|
53
|
+
shapelessTracing ||= entry.shapeless;
|
|
46
54
|
const fn = entry.fn;
|
|
47
55
|
entry.count++;
|
|
48
56
|
traceCalls++;
|
|
@@ -66,6 +74,8 @@ const trampoline = new JSCallback(
|
|
|
66
74
|
} catch (e) {
|
|
67
75
|
traceError = e;
|
|
68
76
|
return 1;
|
|
77
|
+
} finally {
|
|
78
|
+
shapelessTracing = previousShapeless;
|
|
69
79
|
}
|
|
70
80
|
},
|
|
71
81
|
{ args: ["ptr", "u64", "ptr"], returns: "i32" },
|
|
@@ -95,7 +105,7 @@ export class CompiledFunction {
|
|
|
95
105
|
|
|
96
106
|
constructor(fn: TraceFn, shapeless = true) {
|
|
97
107
|
this.#traceId = nextTraceId++;
|
|
98
|
-
traceRegistry.set(this.#traceId, { fn, count: 0 });
|
|
108
|
+
traceRegistry.set(this.#traceId, { fn, count: 0, shapeless });
|
|
99
109
|
// payload is the trace id smuggled through the void* (never deref'd);
|
|
100
110
|
// dtor is null — nothing to free on the C side.
|
|
101
111
|
const raw = C.mlx_closure_new_func_payload(trampoline.ptr, this.#traceId as never, null);
|
package/src/mlx/ffi.ts
CHANGED
|
@@ -10,21 +10,22 @@
|
|
|
10
10
|
// - mlx_optional_int is `{ int32 value; bool has_value }` = 8 bytes, by
|
|
11
11
|
// value in one register: value in bits 0..31, has_value at bit 32.
|
|
12
12
|
|
|
13
|
-
import { dlopen, FFIType, ptr, read } from "bun:ffi";
|
|
13
|
+
import { dlopen, FFIType, ptr, read, type Pointer } from "bun:ffi";
|
|
14
14
|
import { existsSync } from "node:fs";
|
|
15
15
|
import { dirname, join } from "node:path";
|
|
16
|
-
import { nativePackDir } from "../native-pack";
|
|
16
|
+
import { MLX_CORE_VERSION, nativePackDir } from "../native-pack";
|
|
17
|
+
import { runtimeValue } from "../runtime-config";
|
|
17
18
|
|
|
18
19
|
const { ptr: P, i32, u64, f32, f64, cstring } = FFIType;
|
|
19
20
|
|
|
20
21
|
/** libmlxc resolution, in order: explicit env override → next to the
|
|
21
22
|
* executable (the `bun build --compile` sidecar layout, where
|
|
22
23
|
* libmlxc.dylib + libmlx.dylib + mlx.metallib ship beside the binary —
|
|
23
|
-
* see docs/reference/
|
|
24
|
+
* see docs/reference/distribution.md) → the downloaded native-pack cache
|
|
24
25
|
* (src/native-pack.ts; populated on first run by the CLI) → homebrew
|
|
25
26
|
* (arm64, then Intel prefix). Keep in sync with nativeRuntimeDir(). */
|
|
26
27
|
function resolveLibmlxc(): string {
|
|
27
|
-
const env =
|
|
28
|
+
const env = runtimeValue("MLX_BUN_LIBMLXC");
|
|
28
29
|
if (env) return env;
|
|
29
30
|
const beside = join(dirname(process.execPath), "libmlxc.dylib");
|
|
30
31
|
if (existsSync(beside)) return beside;
|
|
@@ -37,8 +38,12 @@ function resolveLibmlxc(): string {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export const LIBMLXC_PATH = resolveLibmlxc();
|
|
40
|
-
|
|
41
41
|
export const C = dlopen(LIBMLXC_PATH, {
|
|
42
|
+
// linked MLX core version, independent of the C API family
|
|
43
|
+
mlx_version: { args: [P], returns: i32 },
|
|
44
|
+
mlx_string_new: { args: [], returns: u64 },
|
|
45
|
+
mlx_string_data: { args: [u64], returns: cstring },
|
|
46
|
+
mlx_string_free: { args: [u64], returns: i32 },
|
|
42
47
|
// array lifecycle
|
|
43
48
|
mlx_array_new: { args: [], returns: u64 },
|
|
44
49
|
mlx_array_new_data: { args: [P, P, i32, i32], returns: u64 },
|
|
@@ -77,6 +82,7 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
77
82
|
mlx_device_info_get: { args: [P, u64], returns: i32 },
|
|
78
83
|
mlx_device_info_free: { args: [u64], returns: i32 },
|
|
79
84
|
mlx_device_info_get_size: { args: [P, u64, cstring], returns: i32 },
|
|
85
|
+
mlx_device_info_get_string: { args: [P, u64, cstring], returns: i32 },
|
|
80
86
|
mlx_get_default_device: { args: [P], returns: i32 },
|
|
81
87
|
mlx_device_free: { args: [u64], returns: i32 },
|
|
82
88
|
mlx_synchronize: { args: [u64], returns: i32 },
|
|
@@ -97,8 +103,13 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
97
103
|
mlx_fast_rms_norm: { args: [P, u64, u64, f32, u64], returns: i32 },
|
|
98
104
|
// (res, x, weight may-be-null, bias may-be-null, eps, stream)
|
|
99
105
|
mlx_fast_layer_norm: { args: [P, u64, u64, u64, f32, u64], returns: i32 },
|
|
106
|
+
// MLX 0.32.2: mask, sinks, force_fused, stream.
|
|
107
|
+
mlx_fast_scaled_dot_product_attention: {
|
|
108
|
+
args: [P, u64, u64, u64, f32, cstring, u64, u64, FFIType.bool, u64], returns: i32,
|
|
109
|
+
},
|
|
100
110
|
mlx_matmul: { args: [P, u64, u64, u64], returns: i32 },
|
|
101
111
|
mlx_contiguous: { args: [P, u64, FFIType.bool, u64], returns: i32 },
|
|
112
|
+
mlx_copy: { args: [P, u64, u64], returns: i32 },
|
|
102
113
|
// (res: mlx_vector_array*, w, opt group_size, opt bits, mode, global_scale, stream)
|
|
103
114
|
mlx_quantize: { args: [P, u64, u64, u64, cstring, u64, u64], returns: i32 },
|
|
104
115
|
mlx_vector_array_get: { args: [P, u64, u64], returns: i32 },
|
|
@@ -106,15 +117,14 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
106
117
|
mlx_softmax_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
|
|
107
118
|
mlx_max_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
|
|
108
119
|
mlx_sum_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
|
|
120
|
+
// Optional dtype uses the eight-byte value/has_value layout of optional int.
|
|
121
|
+
mlx_cumsum_axis: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64, u64], returns: i32 },
|
|
122
|
+
mlx_cummax_axis: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
|
|
109
123
|
mlx_maximum: { args: [P, u64, u64, u64], returns: i32 },
|
|
110
124
|
mlx_expand_dims: { args: [P, u64, i32, u64], returns: i32 },
|
|
111
125
|
mlx_logical_or: { args: [P, u64, u64, u64], returns: i32 },
|
|
112
126
|
// (res, x, dims, traditional, opt base, scale, offset, freqs may-be-null, stream)
|
|
113
127
|
mlx_fast_rope: { args: [P, u64, i32, FFIType.bool, u64, f32, i32, u64, u64], returns: i32 },
|
|
114
|
-
// (res, q, k, v, scale, mask_mode, mask may-be-null, sinks may-be-null, stream)
|
|
115
|
-
mlx_fast_scaled_dot_product_attention: {
|
|
116
|
-
args: [P, u64, u64, u64, f32, cstring, u64, u64, u64], returns: i32,
|
|
117
|
-
},
|
|
118
128
|
// ops (grown as phases need them)
|
|
119
129
|
mlx_add: { args: [P, u64, u64, u64], returns: i32 },
|
|
120
130
|
mlx_subtract: { args: [P, u64, u64, u64], returns: i32 },
|
|
@@ -148,8 +158,6 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
148
158
|
mlx_arange: { args: [P, f64, f64, f64, i32, u64], returns: i32 },
|
|
149
159
|
mlx_take_along_axis: { args: [P, u64, u64, i32, u64], returns: i32 },
|
|
150
160
|
mlx_put_along_axis: { args: [P, u64, u64, u64, i32, u64], returns: i32 },
|
|
151
|
-
mlx_cumsum: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
|
|
152
|
-
mlx_cummax: { args: [P, u64, i32, FFIType.bool, FFIType.bool, u64], returns: i32 },
|
|
153
161
|
mlx_where: { args: [P, u64, u64, u64, u64], returns: i32 },
|
|
154
162
|
mlx_logsumexp_axis: { args: [P, u64, i32, FFIType.bool, u64], returns: i32 },
|
|
155
163
|
mlx_greater_equal: { args: [P, u64, u64, u64], returns: i32 },
|
|
@@ -173,16 +181,11 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
173
181
|
// (res, input, weight, stride, padding, dilation, groups, stream) — depthwise
|
|
174
182
|
// causal conv1d for Qwen3.5 gated-DeltaNet (groups == channels, padding 0).
|
|
175
183
|
mlx_conv1d: { args: [P, u64, u64, i32, i32, i32, i32, u64], returns: i32 },
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// bun:ffi 1.3.14 writes one 8-byte slot per arg — the callee reads shifted
|
|
182
|
-
// garbage and mlx segfaults on a bogus stream. Workaround: declare the two
|
|
183
|
-
// stack ints as ONE u64 (dilation_1 | groups<<32) so Bun's slot layout
|
|
184
|
-
// byte-matches the Apple ABI. ops.conv2d does the packing.
|
|
185
|
-
mlx_conv2d: { args: [P, u64, u64, i32, i32, i32, i32, i32, u64, u64], returns: i32 },
|
|
184
|
+
// Natural mlx-c signatures. Bun 1.4 fixed the macOS arm64 stack packing
|
|
185
|
+
// bug recorded in lab/repro/bun-ffi-stack-args, so these must match the
|
|
186
|
+
// header exactly. mlx-bun now requires Bun 1.4 or newer.
|
|
187
|
+
mlx_conv2d: { args: [P, u64, u64, i32, i32, i32, i32, i32, i32, i32, u64], returns: i32 },
|
|
188
|
+
mlx_conv3d: { args: [P, u64, u64, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, u64], returns: i32 },
|
|
186
189
|
// --- training: autograd (value_and_grad) — proven in lab/spikes/phase-train-vag.ts.
|
|
187
190
|
// mlx_value_and_grad(res: mlx_closure_value_and_grad*, fun: mlx_closure,
|
|
188
191
|
// argnums: int*, n) → builds a value+grad closure differentiating the
|
|
@@ -223,7 +226,7 @@ export const C = dlopen(LIBMLXC_PATH, {
|
|
|
223
226
|
mlx_save_safetensors: { args: [cstring, u64, u64], returns: i32 },
|
|
224
227
|
mlx_map_string_to_array_insert: { args: [u64, cstring, u64], returns: i32 },
|
|
225
228
|
mlx_map_string_to_string_insert: { args: [u64, cstring, cstring], returns: i32 },
|
|
226
|
-
// compile via closures (Phase A compiled decode — docs/
|
|
229
|
+
// compile via closures (Phase A compiled decode — docs/archive/investigations/optimization_plan.md).
|
|
227
230
|
// mlx_closure is the usual one-pointer struct; the func-payload variant
|
|
228
231
|
// carries an id we use to route the trace callback back to JS.
|
|
229
232
|
mlx_closure_new: { args: [], returns: u64 },
|
|
@@ -317,6 +320,22 @@ export function takeMlxError(): string | null {
|
|
|
317
320
|
return e;
|
|
318
321
|
}
|
|
319
322
|
|
|
323
|
+
function readRuntimeVersion(): string {
|
|
324
|
+
const slot = new BigUint64Array([C.mlx_string_new()]);
|
|
325
|
+
const p = ptr(slot);
|
|
326
|
+
try {
|
|
327
|
+
if (C.mlx_version(p) !== 0)
|
|
328
|
+
throw new Error(`mlx_version failed: ${takeMlxError() ?? "(no mlx message)"}`);
|
|
329
|
+
return C.mlx_string_data(read.u64(p, 0)).toString();
|
|
330
|
+
} finally { C.mlx_string_free(read.u64(p, 0)); }
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Read once at startup; kernel eligibility must follow the linked core. */
|
|
334
|
+
export const MLX_VERSION = readRuntimeVersion();
|
|
335
|
+
if (MLX_VERSION !== MLX_CORE_VERSION) {
|
|
336
|
+
throw new Error(`mlx-bun requires its bundled MLX ${MLX_CORE_VERSION}; ${LIBMLXC_PATH} loaded MLX ${MLX_VERSION}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
320
339
|
/** mlx_dtype enum values (array.h). */
|
|
321
340
|
export const enum Dtype {
|
|
322
341
|
bool = 0, uint8 = 1, uint16 = 2, uint32 = 3, uint64 = 4,
|
|
@@ -408,6 +427,31 @@ export function clearCache(): void {
|
|
|
408
427
|
C.mlx_clear_cache();
|
|
409
428
|
}
|
|
410
429
|
|
|
430
|
+
let cachedDeviceArchitecture: string | undefined;
|
|
431
|
+
|
|
432
|
+
/** Native architecture identifier, used to match MLX's hardware dispatch. */
|
|
433
|
+
export function deviceArchitecture(): string {
|
|
434
|
+
if (cachedDeviceArchitecture !== undefined) return cachedDeviceArchitecture;
|
|
435
|
+
const devSlot = new BigUint64Array(1), devPtr = ptr(devSlot);
|
|
436
|
+
if (C.mlx_get_default_device(devPtr) !== 0)
|
|
437
|
+
throw new Error(`mlx_get_default_device failed: ${takeMlxError() ?? ""}`);
|
|
438
|
+
const dev = read.u64(devPtr, 0);
|
|
439
|
+
const infoSlot = new BigUint64Array([C.mlx_device_info_new()]), infoPtr = ptr(infoSlot);
|
|
440
|
+
try {
|
|
441
|
+
if (C.mlx_device_info_get(infoPtr, dev) !== 0)
|
|
442
|
+
throw new Error(`mlx_device_info_get failed: ${takeMlxError() ?? ""}`);
|
|
443
|
+
const out = new BigUint64Array(1), outPtr = ptr(out);
|
|
444
|
+
const key = Buffer.from("architecture\0", "utf8");
|
|
445
|
+
if (C.mlx_device_info_get_string(outPtr, read.u64(infoPtr, 0), ptr(key)) !== 0)
|
|
446
|
+
throw new Error("architecture not in device info");
|
|
447
|
+
// Copy before the device-info object releases the native string.
|
|
448
|
+
return cachedDeviceArchitecture = new CString(Number(read.u64(outPtr, 0)) as Pointer).toString();
|
|
449
|
+
} finally {
|
|
450
|
+
C.mlx_device_info_free(read.u64(infoPtr, 0));
|
|
451
|
+
C.mlx_device_free(dev);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
411
455
|
/** Metal's recommended max working-set size for the default device. */
|
|
412
456
|
export function maxRecommendedWorkingSetSize(): number {
|
|
413
457
|
const devSlot = new BigUint64Array(1);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Explicit compact storage for a value that must release a larger backing
|
|
2
|
+
// buffer. MLX 0.31.2 Copy::eval shares storage; contiguous() may also alias.
|
|
3
|
+
// MLX's dynamic slice allocates the logical output size and has a native VJP.
|
|
4
|
+
import { MlxArray } from "./array";
|
|
5
|
+
import * as ops from "./ops";
|
|
6
|
+
|
|
7
|
+
let zero: MlxArray | undefined;
|
|
8
|
+
|
|
9
|
+
/** Borrow an array; return an owned, row-contiguous copy on the GPU.
|
|
10
|
+
* Values and dtype are unchanged. Evaluation stays lazy and never reads data
|
|
11
|
+
* back to the host. Use only when independent compact storage is required;
|
|
12
|
+
* immutable array aliases normally avoid this allocation and dispatch.
|
|
13
|
+
* Slice sizes are concrete, so use shape-specialized compiled graphs. */
|
|
14
|
+
export function materializeCopy(input: MlxArray): MlxArray {
|
|
15
|
+
zero ??= MlxArray.fromInt32(new Int32Array([0]), []);
|
|
16
|
+
if (input.ndim > 0) return ops.sliceDynamic(input, zero, [0], input.shape);
|
|
17
|
+
const vector = ops.reshape(input, [1]);
|
|
18
|
+
try {
|
|
19
|
+
const copy = ops.sliceDynamic(vector, zero, [0], [1]);
|
|
20
|
+
try { return ops.reshape(copy, []); }
|
|
21
|
+
finally { copy.dispose(); }
|
|
22
|
+
} finally { vector.dispose(); }
|
|
23
|
+
}
|
package/src/mlx/metal-kernel.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// mx.fast.metal_kernel from Bun (docs/
|
|
1
|
+
// mx.fast.metal_kernel from Bun (docs/archive/investigations/optimization_plan.md Phase E): the
|
|
2
2
|
// intended MLX extension point for custom Metal kernels — no fork. The
|
|
3
3
|
// kernel body is Metal Shading Language; mlx generates the signature
|
|
4
4
|
// from input/output names (inputs by name, outputs by name, plus
|
|
@@ -29,15 +29,15 @@ export interface MetalKernelSpec {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface MetalKernelCall {
|
|
32
|
-
/** Concrete output shapes/dtypes. Provide this OR `outputShapeFn
|
|
33
|
-
*
|
|
34
|
-
* primitive — required to compose inside an mx.compile'd / shapeless closure,
|
|
35
|
-
* where the wrapper must answer "given these input shapes, what comes out?"). */
|
|
32
|
+
/** Concrete output shapes/dtypes. Provide this OR `outputShapeFn`.
|
|
33
|
+
* Both are captured when apply() builds the custom primitive. */
|
|
36
34
|
outputs?: { shape: number[]; dtype: Dtype }[];
|
|
37
|
-
/** Derive
|
|
35
|
+
/** Derive outputs during eager construction or a shape-specific compile trace.
|
|
36
|
+
* This JS callback is not native shape inference: MLX's CustomKernel lacks
|
|
37
|
+
* output_shapes(), so it cannot participate in shapeless replay. */
|
|
38
38
|
outputShapeFn?: (inputs: MlxArray[]) => { shape: number[]; dtype: Dtype }[];
|
|
39
|
-
/** Concrete launch grid, OR derive it
|
|
40
|
-
*
|
|
39
|
+
/** Concrete launch grid, OR derive it during eager construction or tracing.
|
|
40
|
+
* Native replay reuses the captured grid without invoking this callback. */
|
|
41
41
|
grid: [number, number, number] | ((inputs: MlxArray[]) => [number, number, number]);
|
|
42
42
|
threadGroup: [number, number, number];
|
|
43
43
|
templateInts?: Record<string, number>;
|
|
@@ -73,9 +73,8 @@ export class MetalKernel {
|
|
|
73
73
|
// flash-CCE pin bug). Pure-additive: holds references, changes no logic.
|
|
74
74
|
const keepAlive: unknown[] = [];
|
|
75
75
|
try {
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// during a compile/replay trace, instead of baking in one call's shapes.
|
|
76
|
+
// Resolve callbacks once for this eager call or shape-specific trace.
|
|
77
|
+
// A compiled graph captures these values; replay does not call JS again.
|
|
79
78
|
const outputs = call.outputShapeFn ? call.outputShapeFn(inputs) : call.outputs;
|
|
80
79
|
if (!outputs) throw new Error("metal_kernel: provide outputs or outputShapeFn");
|
|
81
80
|
const grid = typeof call.grid === "function" ? call.grid(inputs) : call.grid;
|
package/src/mlx/ops.ts
CHANGED
|
@@ -113,6 +113,15 @@ export function matmul(a: MlxArray, b: MlxArray, s: S = gpuStream): MlxArray {
|
|
|
113
113
|
return new MlxArray(outArray("matmul", (o) => C.mlx_matmul(o, a.handle, b.handle, s)));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/** Bit-identical copy through a FRESH primitive node (mx.copy). Unlike
|
|
117
|
+
* `contiguous`, this always creates a new graph node even for an already-
|
|
118
|
+
* contiguous input — the tool for DETACHING an array from a multi-output
|
|
119
|
+
* primitive's sibling set (a retained sibling output pins every co-output's
|
|
120
|
+
* buffer natively; see gatedDeltaUpdate's state detach, 2026-08-20). */
|
|
121
|
+
export function copyOf(a: MlxArray, s: S = gpuStream): MlxArray {
|
|
122
|
+
return new MlxArray(outArray("copy", (o) => C.mlx_copy(o, a.handle, s)));
|
|
123
|
+
}
|
|
124
|
+
|
|
116
125
|
/** Materialize a row-major contiguous copy (needed before rawBytes on views). */
|
|
117
126
|
export function contiguous(a: MlxArray, s: S = gpuStream): MlxArray {
|
|
118
127
|
return new MlxArray(
|
|
@@ -161,7 +170,7 @@ export function ropeScaled(
|
|
|
161
170
|
* of a baked int — required inside compiled decode graphs, where the
|
|
162
171
|
* offset changes every step but the graph must not. Same kernel as
|
|
163
172
|
* `rope`; bit-exactness vs the static form is asserted in
|
|
164
|
-
* tests/compile.test.ts. */
|
|
173
|
+
* tests/unit/compile.test.ts. */
|
|
165
174
|
export function ropeDynamic(
|
|
166
175
|
x: MlxArray, dims: number, base: number | null, offset: MlxArray,
|
|
167
176
|
freqs: MlxArray | null, s: S = gpuStream,
|
|
@@ -180,7 +189,7 @@ export function ropeDynamic(
|
|
|
180
189
|
* the dynamic twin of `ropeScaled`, for the universal rope factory's
|
|
181
190
|
* batched-decode path (per-row [B] offsets under left-padding). Same
|
|
182
191
|
* kernel as `ropeDynamic`; static/dynamic bit-exactness is asserted in
|
|
183
|
-
* tests/compile.test.ts. */
|
|
192
|
+
* tests/unit/compile.test.ts. */
|
|
184
193
|
export function ropeScaledDynamic(
|
|
185
194
|
x: MlxArray, dims: number, traditional: boolean, base: number | null,
|
|
186
195
|
scale: number, offset: MlxArray, freqs: MlxArray | null, s: S = gpuStream,
|
|
@@ -204,7 +213,7 @@ export function sdpa(
|
|
|
204
213
|
outArray("fast_sdpa", (o) =>
|
|
205
214
|
C.mlx_fast_scaled_dot_product_attention(
|
|
206
215
|
o, q.handle, k.handle, v.handle, scale, ptr(cstr(maskMode)),
|
|
207
|
-
maskArr?.handle ?? 0n, 0n, s,
|
|
216
|
+
maskArr?.handle ?? 0n, 0n, false, s,
|
|
208
217
|
),
|
|
209
218
|
),
|
|
210
219
|
);
|
|
@@ -305,7 +314,8 @@ export function takeAxis(a: MlxArray, indices: MlxArray, axis: number, s: S = gp
|
|
|
305
314
|
}
|
|
306
315
|
|
|
307
316
|
export function reshape(a: MlxArray, shape: number[], s: S = gpuStream): MlxArray {
|
|
308
|
-
|
|
317
|
+
// bun:ffi cannot take a pointer to an empty view; ndim=0 ignores the dummy.
|
|
318
|
+
const buf = new Int32Array(shape.length ? shape : [0]);
|
|
309
319
|
return new MlxArray(
|
|
310
320
|
outArray("reshape", (o) => C.mlx_reshape(o, a.handle, ptr(buf), BigInt(shape.length), s)),
|
|
311
321
|
);
|
|
@@ -445,14 +455,14 @@ export function putAlongAxis(
|
|
|
445
455
|
|
|
446
456
|
export function cumsum(a: MlxArray, axis: number, s: S = gpuStream): MlxArray {
|
|
447
457
|
return new MlxArray(
|
|
448
|
-
outArray("cumsum", (o) => C.
|
|
458
|
+
outArray("cumsum", (o) => C.mlx_cumsum_axis(o, a.handle, axis, false, true, 0n, s)),
|
|
449
459
|
);
|
|
450
460
|
}
|
|
451
461
|
|
|
452
462
|
/** Cumulative maximum along `axis` (inclusive, forward) — mlx.core.cummax. */
|
|
453
463
|
export function cummax(a: MlxArray, axis: number, s: S = gpuStream): MlxArray {
|
|
454
464
|
return new MlxArray(
|
|
455
|
-
outArray("cummax", (o) => C.
|
|
465
|
+
outArray("cummax", (o) => C.mlx_cummax_axis(o, a.handle, axis, false, true, s)),
|
|
456
466
|
);
|
|
457
467
|
}
|
|
458
468
|
|
|
@@ -467,10 +477,13 @@ export function where(cond: MlxArray, x: MlxArray, y: MlxArray, s: S = gpuStream
|
|
|
467
477
|
// (lab/repro/bun-ffi-f64/) traced the root cause to a Bun bug: after DFG
|
|
468
478
|
// tier-up, typed-array reads following a bun:ffi call return stale values
|
|
469
479
|
// (the JIT eliminates the load across the native call). Not f64 marshaling
|
|
470
|
-
// — args reach C intact. See PLAN.md Phase 4 findings.
|
|
471
|
-
//
|
|
472
|
-
|
|
480
|
+
// — args reach C intact. See PLAN.md Phase 4 findings. Reuse large ranges
|
|
481
|
+
// across attention layers and sampling, but bound their retained storage:
|
|
482
|
+
// causal masks request a new range on every round once context exceeds 64K.
|
|
483
|
+
const arangeCache = new Map<string, { array: MlxArray; bytes: number }>();
|
|
473
484
|
const ARANGE_CACHE_MIN = 65536;
|
|
485
|
+
const ARANGE_CACHE_BYTES = 8 * 1024 * 1024;
|
|
486
|
+
let arangeCacheBytes = 0;
|
|
474
487
|
|
|
475
488
|
export function arange(start: number, stop: number, step: number, dtype: Dtype, s: S = gpuStream): MlxArray {
|
|
476
489
|
if (!Number.isInteger(start) || !Number.isInteger(step) || step === 0)
|
|
@@ -478,7 +491,11 @@ export function arange(start: number, stop: number, step: number, dtype: Dtype,
|
|
|
478
491
|
const n = Math.max(0, Math.ceil((stop - start) / step));
|
|
479
492
|
const key = `${start}|${step}|${n}|${dtype}`;
|
|
480
493
|
const cached = arangeCache.get(key);
|
|
481
|
-
if (cached)
|
|
494
|
+
if (cached) {
|
|
495
|
+
arangeCache.delete(key);
|
|
496
|
+
arangeCache.set(key, cached);
|
|
497
|
+
return cached.array.slice([0], [n], s);
|
|
498
|
+
}
|
|
482
499
|
|
|
483
500
|
let arr: MlxArray;
|
|
484
501
|
if (dtype === Dtype.int32) {
|
|
@@ -494,8 +511,20 @@ export function arange(start: number, stop: number, step: number, dtype: Dtype,
|
|
|
494
511
|
arr = new MlxArray(outArray("arange", (o) => C.mlx_arange(o, start, stop, step, dtype, s)));
|
|
495
512
|
}
|
|
496
513
|
if (n >= ARANGE_CACHE_MIN) {
|
|
497
|
-
|
|
498
|
-
|
|
514
|
+
const bytes = arr.nbytes;
|
|
515
|
+
if (bytes <= ARANGE_CACHE_BYTES) {
|
|
516
|
+
while (arangeCacheBytes + bytes > ARANGE_CACHE_BYTES) {
|
|
517
|
+
const [oldKey, old] = arangeCache.entries().next().value!;
|
|
518
|
+
arangeCache.delete(oldKey);
|
|
519
|
+
arangeCacheBytes -= old.bytes;
|
|
520
|
+
// Returned slices retain their own native reference, including lazy
|
|
521
|
+
// views that have not been evaluated when this owner is evicted.
|
|
522
|
+
old.array.dispose();
|
|
523
|
+
}
|
|
524
|
+
arangeCache.set(key, { array: arr, bytes });
|
|
525
|
+
arangeCacheBytes += bytes;
|
|
526
|
+
return arr.slice([0], [n], s);
|
|
527
|
+
}
|
|
499
528
|
}
|
|
500
529
|
return arr;
|
|
501
530
|
}
|
|
@@ -602,7 +631,7 @@ export function sliceDynamic(
|
|
|
602
631
|
/** slice_update with the start index as an ARRAY (one int32 per entry in
|
|
603
632
|
* `axes`) — the compiled-decode form of the per-step cache write. Same
|
|
604
633
|
* write as `sliceUpdate`; bit-exactness vs the static form is asserted
|
|
605
|
-
* in tests/compile.test.ts. */
|
|
634
|
+
* in tests/unit/compile.test.ts. */
|
|
606
635
|
export function sliceUpdateDynamic(
|
|
607
636
|
src: MlxArray, update: MlxArray, start: MlxArray, axes: number[], s: S = gpuStream,
|
|
608
637
|
): MlxArray {
|
|
@@ -880,20 +909,36 @@ export function conv1d(
|
|
|
880
909
|
|
|
881
910
|
/** nn.Conv2d: input [B, H, W, C_in] (NHWC), weight [C_out, kH, kW, C_in/groups]
|
|
882
911
|
* → [B, H', W', C_out]. mlx weight layout — the oracle transposes PyTorch's
|
|
883
|
-
* [C_out, C_in, kH, kW] at load. For the gemma-4 audio SSCP subsampler.
|
|
884
|
-
* dilation_1 and groups ride in one packed u64 — see the mlx_conv2d hazard
|
|
885
|
-
* note in ffi.ts (bun:ffi stack-arg ABI bug). */
|
|
912
|
+
* [C_out, C_in, kH, kW] at load. For the gemma-4 audio SSCP subsampler. */
|
|
886
913
|
export function conv2d(
|
|
887
914
|
input: MlxArray, weight: MlxArray, stride: [number, number] = [1, 1],
|
|
888
915
|
padding: [number, number] = [0, 0], dilation: [number, number] = [1, 1],
|
|
889
916
|
groups = 1, s: S = gpuStream,
|
|
890
917
|
): MlxArray {
|
|
891
|
-
const dilation1AndGroups =
|
|
892
|
-
(BigInt(dilation[1] >>> 0) & 0xffffffffn) | (BigInt(groups >>> 0) << 32n);
|
|
893
918
|
return new MlxArray(
|
|
894
919
|
outArray("conv2d", (o) =>
|
|
895
920
|
C.mlx_conv2d(o, input.handle, weight.handle, stride[0], stride[1],
|
|
896
|
-
padding[0], padding[1], dilation[0],
|
|
921
|
+
padding[0], padding[1], dilation[0], dilation[1], groups, s),
|
|
922
|
+
),
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** nn.Conv3d: input [B, D, H, W, C_in] (NDHWC), weight [C_out, kD, kH, kW,
|
|
927
|
+
* C_in/groups] → [B, D', H', W', C_out]. The Qwen3-VL vision patch embed
|
|
928
|
+
* (kernel == stride) — the conv KERNEL's accumulation order is the parity-
|
|
929
|
+
* relevant arithmetic; a value-equivalent GEMM differs by bf16 ulp. */
|
|
930
|
+
export function conv3d(
|
|
931
|
+
input: MlxArray, weight: MlxArray,
|
|
932
|
+
stride: [number, number, number] = [1, 1, 1],
|
|
933
|
+
padding: [number, number, number] = [0, 0, 0],
|
|
934
|
+
dilation: [number, number, number] = [1, 1, 1],
|
|
935
|
+
groups = 1, s: S = gpuStream,
|
|
936
|
+
): MlxArray {
|
|
937
|
+
return new MlxArray(
|
|
938
|
+
outArray("conv3d", (o) =>
|
|
939
|
+
C.mlx_conv3d(o, input.handle, weight.handle, stride[0], stride[1],
|
|
940
|
+
stride[2], padding[0], padding[1], padding[2], dilation[0],
|
|
941
|
+
dilation[1], dilation[2], groups, s),
|
|
897
942
|
),
|
|
898
943
|
);
|
|
899
944
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { MlxArray } from "./array";
|
|
2
|
+
import * as ops from "./ops";
|
|
3
|
+
|
|
4
|
+
/** Run independent affine M=1 matrix-vector products in one native batched call.
|
|
5
|
+
* Weight/scales/bias views share their original buffers. MLX 0.32.2's
|
|
6
|
+
* flattened M>=2 path uses different arithmetic, so appending known tokens
|
|
7
|
+
* must explicitly select this layout when preserving decode state.
|
|
8
|
+
*/
|
|
9
|
+
export function quantizedMatmulRows(
|
|
10
|
+
x: MlxArray, w: MlxArray, scales: MlxArray, biases: MlxArray | null,
|
|
11
|
+
spec: ops.QuantSpec,
|
|
12
|
+
): MlxArray {
|
|
13
|
+
const shape = x.shape, width = shape[shape.length - 1]!;
|
|
14
|
+
const rows = shape.reduce((a, b) => a * b, 1) / width;
|
|
15
|
+
if (rows === 1) return ops.quantizedMatmul(x, w, scales, biases, spec);
|
|
16
|
+
if (spec.mode !== "affine")
|
|
17
|
+
throw new Error("Independent-row batched matmul requires affine quantization");
|
|
18
|
+
if (w.shape.length !== 2 || scales.shape.length !== 2 || (biases && biases.shape.length !== 2))
|
|
19
|
+
throw new Error("Independent-row matmul requires shared two-dimensional weights");
|
|
20
|
+
const views: MlxArray[] = [];
|
|
21
|
+
const view = (value: MlxArray, newShape: number[]) => {
|
|
22
|
+
const result = ops.reshape(value, newShape); views.push(result); return result;
|
|
23
|
+
};
|
|
24
|
+
let output: MlxArray | undefined;
|
|
25
|
+
try {
|
|
26
|
+
const input = view(x, [rows, 1, width]);
|
|
27
|
+
const weight = view(w, [1, ...w.shape]);
|
|
28
|
+
const scale = view(scales, [1, ...scales.shape]);
|
|
29
|
+
const bias = biases ? view(biases, [1, ...biases.shape]) : null;
|
|
30
|
+
output = ops.quantizedMatmul(input, weight, scale, bias, spec);
|
|
31
|
+
return ops.reshape(output, [...shape.slice(0, -1), output.shape.at(-1)!]);
|
|
32
|
+
} finally {
|
|
33
|
+
output?.dispose();
|
|
34
|
+
for (const value of views) value.dispose();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// XGrammar's additive 0/-Infinity mask contract, decoded from packed bits.
|
|
2
|
+
// Reference: mlc-ai/xgrammar b5048784, apply_token_bitmask_mlx.py (Apache-2.0).
|
|
3
|
+
import { MlxArray } from "./array";
|
|
4
|
+
import { CompiledFunction } from "./compile";
|
|
5
|
+
import { Dtype } from "./ffi";
|
|
6
|
+
import { MetalKernel } from "./metal-kernel";
|
|
7
|
+
|
|
8
|
+
let compiled: CompiledFunction | undefined;
|
|
9
|
+
let kernel: MetalKernel | undefined;
|
|
10
|
+
|
|
11
|
+
function maskGraph(logits: MlxArray, bits: MlxArray): MlxArray {
|
|
12
|
+
kernel ??= new MetalKernel({
|
|
13
|
+
name: "mlx_bun_token_bitmask",
|
|
14
|
+
inputNames: ["logits", "bits"],
|
|
15
|
+
outputNames: ["out"],
|
|
16
|
+
source: String.raw`
|
|
17
|
+
const uint i = thread_position_in_grid.x;
|
|
18
|
+
if (i >= SIZE) return;
|
|
19
|
+
const uint id = i % V;
|
|
20
|
+
const uint word = uint(bits[id >> 5]);
|
|
21
|
+
const bool valid = ((word >> (id & 31)) & 1u) != 0;
|
|
22
|
+
out[i] = logits[i] + T(valid ? 0.0f : -INFINITY);
|
|
23
|
+
`,
|
|
24
|
+
});
|
|
25
|
+
return kernel.apply([logits, bits], {
|
|
26
|
+
outputs: [{ shape: logits.shape, dtype: logits.dtype }],
|
|
27
|
+
grid: [logits.size, 1, 1],
|
|
28
|
+
threadGroup: [128, 1, 1],
|
|
29
|
+
templateDtypes: { T: logits.dtype },
|
|
30
|
+
templateInts: { V: logits.shape.at(-1)!, SIZE: logits.size },
|
|
31
|
+
})[0]!;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Apply one little-endian token bitmask to every row along the final axis.
|
|
35
|
+
* Borrow logits; return an owned lazy result. Copy the mutable host bits now,
|
|
36
|
+
* so a matcher can advance before GPU evaluation. Missing words reject tokens.
|
|
37
|
+
* Preserve the additive-mask expression, including NaNs and signed zero.
|
|
38
|
+
* Shape-specialized compilation handles bf16/f16/f32 and strided inputs. */
|
|
39
|
+
export function applyTokenBitmask(logits: MlxArray, bits: Int32Array): MlxArray {
|
|
40
|
+
const vocab = logits.shape.at(-1);
|
|
41
|
+
if (!vocab || logits.size === 0 || logits.size > 0x7fffffff)
|
|
42
|
+
throw new Error("token bitmask requires nonempty logits with at most 2^31-1 elements");
|
|
43
|
+
if (![Dtype.bfloat16, Dtype.float16, Dtype.float32].includes(logits.dtype))
|
|
44
|
+
throw new Error("token bitmask requires bf16, f16 or f32 logits");
|
|
45
|
+
const copy = new Int32Array(Math.ceil(vocab / 32));
|
|
46
|
+
copy.set(bits.subarray(0, copy.length));
|
|
47
|
+
const deviceBits = MlxArray.fromInt32(copy, [copy.length]);
|
|
48
|
+
try {
|
|
49
|
+
compiled ??= new CompiledFunction(([x, mask]) => [maskGraph(x!, mask!)], false);
|
|
50
|
+
return compiled.apply([logits, deviceBits])[0]!;
|
|
51
|
+
} finally { deviceBits.dispose(); }
|
|
52
|
+
}
|