mlx-bun 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -588
- package/docs/README.md +94 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
- package/docs/design/batching.md +1841 -0
- package/docs/design/decode-speed-program.md +2346 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +655 -334
- package/docs/design/kv-cache.md +824 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1990 -0
- package/docs/design/turboquant.md +2212 -0
- package/docs/design/unified-engine-frontier-plan.md +1833 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +50 -1
- package/docs/planning/release-notes-v0.4.0.md +45 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +3613 -0
- package/docs/reference/cli.md +434 -160
- package/docs/reference/distribution.md +328 -182
- package/docs/reference/environment.md +398 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +113 -6
- package/docs/reference/server-api.md +944 -429
- package/docs/reference/server-config.md +740 -338
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +158 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1790 -0
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +106 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +32 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +130 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +133 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill-cohort.ts +89 -0
- package/src/backends/mlx/prefill-rows.ts +200 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/request-state-policy.ts +41 -0
- package/src/backends/mlx/rollback.ts +89 -0
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +242 -0
- package/src/backends/mlx/speculative-group.ts +359 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +46 -0
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
- package/src/cli.ts +66 -58
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +60 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/prefix-cache.ts +23 -0
- package/src/contracts/resources.ts +25 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +45 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +53 -0
- package/src/engine/resources.ts +53 -0
- package/src/engine/scheduler.ts +72 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +30 -16
- package/src/generate.ts +709 -417
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-scheme.ts +70 -12
- package/src/kv-store.ts +177 -63
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
- package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +11 -11
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +20 -6
- package/src/model/batched-quant.ts +9 -3
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +78 -17
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +65 -23
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +93 -0
- package/src/model/compiled-decode.ts +8 -2
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +55 -0
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +179 -80
- package/src/model/gemma4.ts +40 -32
- package/src/model/generated/gemma4-12b.ts +6 -3
- package/src/model/generated/gemma4-26b.ts +6 -3
- package/src/model/generated/gemma4-e4b.ts +6 -3
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/implementation.ts +69 -0
- package/src/model/kv-attention-view.ts +67 -0
- package/src/model/minicpm5.ts +17 -15
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +202 -78
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/turboquant-codec.ts +83 -0
- package/src/model/universal/dense.ts +15 -14
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +110 -69
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +40 -29
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +183 -213
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +511 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +23 -18
- package/src/serve/request-prep.ts +344 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +335 -2711
- package/src/spec/assistant-rows.ts +92 -0
- package/src/spec/assistant-source.ts +31 -55
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +16 -14
- package/src/spec/dflash-source.ts +36 -12
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +97 -122
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +129 -172
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -137
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +93 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +219 -0
- package/src/spec/qwen-mtp-source.ts +147 -195
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +201 -201
- package/src/spec/source.ts +110 -13
- package/src/spec/two-model-rows.ts +61 -0
- package/src/spec/two-model.ts +44 -102
- package/src/ssd-cache.ts +99 -19
- package/src/ssd-durability.ts +36 -20
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +62 -60
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
package/src/ssd-durability.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { cloneAttachments } from "./backends/mlx/checkpoint-state";
|
|
2
|
+
import { cleanupFailure, disposeResources } from "./engine/resources";
|
|
1
3
|
import type { Cache } from "./model/gemma4";
|
|
2
4
|
import type { PromptCacheEntry } from "./prompt-cache";
|
|
3
5
|
import type { SpillItem, SpillQueue } from "./kv-store";
|
|
4
6
|
|
|
5
7
|
export interface DurabilityGateway {
|
|
6
8
|
readonly busy: boolean;
|
|
7
|
-
|
|
9
|
+
runWhenIdle<T>(fn: () => Promise<T>): Promise<T>;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export interface DurabilityPromptCache {
|
|
@@ -115,9 +117,9 @@ export class SsdDurabilityCoordinator {
|
|
|
115
117
|
this.#attempts.set(key, task);
|
|
116
118
|
try {
|
|
117
119
|
const outcome = await task;
|
|
118
|
-
if (outcome
|
|
120
|
+
if (outcome === "stored" && this.#dirty.get(key) === rec)
|
|
119
121
|
this.#dirty.delete(key);
|
|
120
|
-
if (outcome
|
|
122
|
+
if (outcome !== "stored" && !force && this.#dirty.get(key) === rec)
|
|
121
123
|
this.#arm(key, this.busyRetryMs);
|
|
122
124
|
return outcome;
|
|
123
125
|
} finally {
|
|
@@ -128,14 +130,17 @@ export class SsdDurabilityCoordinator {
|
|
|
128
130
|
async #store(rec: DirtySnapshot): Promise<StoreOutcome> {
|
|
129
131
|
let snap: SpillItem | null = null;
|
|
130
132
|
try {
|
|
131
|
-
snap = await this.gateway.
|
|
133
|
+
snap = await this.gateway.runWhenIdle(async () => {
|
|
134
|
+
if (this.isAlreadyDurable(rec.tokens, rec.ns)) return null;
|
|
132
135
|
const entry = this.promptCache.findExact(rec.tokens, rec.ns);
|
|
133
136
|
if (!entry) return null;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
137
|
+
const caches = this.cloneCaches(entry.caches);
|
|
138
|
+
try {
|
|
139
|
+
return { tokens: [...entry.tokens], caches, ns: rec.ns,
|
|
140
|
+
attachments: cloneAttachments(entry.attachments) };
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return cleanupFailure(error, () => disposeResources(caches));
|
|
143
|
+
}
|
|
139
144
|
});
|
|
140
145
|
} catch {
|
|
141
146
|
return "failed";
|
|
@@ -150,7 +155,7 @@ export class SsdDurabilityCoordinator {
|
|
|
150
155
|
this.#timers.clear();
|
|
151
156
|
|
|
152
157
|
let flushedSnapshots = 0;
|
|
153
|
-
|
|
158
|
+
const missing = new Map<string, DirtySnapshot>();
|
|
154
159
|
const droppedBefore = this.spillQueue.droppedCount;
|
|
155
160
|
const failedBefore = this.spillQueue.failedCount;
|
|
156
161
|
|
|
@@ -160,22 +165,33 @@ export class SsdDurabilityCoordinator {
|
|
|
160
165
|
|
|
161
166
|
// Flush one entry at a time. This prevents the queue cap from dropping a
|
|
162
167
|
// boundary snapshot while a large final snapshot is already in flight.
|
|
168
|
+
// Attempt each record version once. Missing state stays dirty, so a
|
|
169
|
+
// second flush cannot report success without a real durable snapshot.
|
|
170
|
+
const attempted = new Set<DirtySnapshot>();
|
|
163
171
|
while (this.#dirty.size > 0) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
for (const key of
|
|
167
|
-
if (
|
|
172
|
+
const records = [...this.#dirty.entries()].filter(([, rec]) => !attempted.has(rec));
|
|
173
|
+
if (records.length === 0) break;
|
|
174
|
+
for (const [key, rec] of records) {
|
|
175
|
+
if (this.#dirty.get(key) !== rec) continue;
|
|
176
|
+
attempted.add(rec);
|
|
168
177
|
const outcome = await this.#attempt(key, true);
|
|
169
178
|
await this.spillQueue.drain();
|
|
170
179
|
if (outcome === "stored") {
|
|
171
180
|
flushedSnapshots++;
|
|
172
|
-
progressed = true;
|
|
173
181
|
} else if (outcome === "missing") {
|
|
174
|
-
|
|
175
|
-
progressed = true;
|
|
182
|
+
missing.set(key, rec);
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
|
-
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// A later trimmable snapshot may now cover a superseded RAM ancestor.
|
|
188
|
+
// Reconcile against committed SSD coverage after all writes settle;
|
|
189
|
+
// never clear a missing prefix merely because another write succeeded.
|
|
190
|
+
for (const [key, rec] of missing) {
|
|
191
|
+
if (!this.isAlreadyDurable(rec.tokens, rec.ns)) continue;
|
|
192
|
+
if (this.#dirty.get(key) === rec) this.#dirty.delete(key);
|
|
193
|
+
missing.delete(key);
|
|
194
|
+
flushedSnapshots++;
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
const stats = this.stats;
|
|
@@ -186,9 +202,9 @@ export class SsdDurabilityCoordinator {
|
|
|
186
202
|
stats.pendingSpills === 0 &&
|
|
187
203
|
stats.droppedSpills === droppedBefore &&
|
|
188
204
|
stats.failedSpills === failedBefore &&
|
|
189
|
-
|
|
205
|
+
missing.size === 0,
|
|
190
206
|
flushedSnapshots,
|
|
191
|
-
missingSnapshots,
|
|
207
|
+
missingSnapshots: missing.size,
|
|
192
208
|
elapsedMs: performance.now() - started,
|
|
193
209
|
};
|
|
194
210
|
}
|
package/src/tool-call.ts
CHANGED
|
@@ -161,7 +161,7 @@ export function parseToolCalls(text: string): ParsedToolCall[] {
|
|
|
161
161
|
|
|
162
162
|
const TOOL_CALL_BLOCK_RE = /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/gi;
|
|
163
163
|
const XML_FUNCTION_EQUALS_RE = /^\s*<function=([^>\s]+)>\s*([\s\S]*?)\s*<\/function>\s*$/i;
|
|
164
|
-
const XML_PARAMETER_EQUALS_RE = /<parameter=([^>\s]+)
|
|
164
|
+
const XML_PARAMETER_EQUALS_RE = /<parameter=([^>\s]+)>([\s\S]*?)<\/parameter>/gi;
|
|
165
165
|
const GLM52_ARG_KEY_RE = /<arg_key>/i;
|
|
166
166
|
const GLM52_ARG_PAIR_RE = /<arg_key>\s*([\s\S]*?)\s*<\/arg_key>\s*<arg_value>\s*([\s\S]*?)\s*<\/arg_value>/gi;
|
|
167
167
|
const XML_FUNCTION_ATTR_RE = /<function\s+name=["']([^"']+)["']\s*>\s*([\s\S]*?)\s*<\/function>/gi;
|
|
@@ -208,11 +208,9 @@ function toolParameterSchema(
|
|
|
208
208
|
return null;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
/**
|
|
212
|
-
*
|
|
213
|
-
|
|
214
|
-
function decodeToolValue(value: string, schema: Record<string, unknown> | null): unknown {
|
|
215
|
-
const text = decodeXml(value);
|
|
211
|
+
/** Decode a value after its caller removes protocol-specific delimiters.
|
|
212
|
+
* String-typed params stay raw; everything else is JSON-decoded when possible. */
|
|
213
|
+
function decodeToolValue(text: string, schema: Record<string, unknown> | null): unknown {
|
|
216
214
|
if (!text) return "";
|
|
217
215
|
const types = schemaTypeNames(schema);
|
|
218
216
|
if (types.size && [...types].every((t) => t === "string" || t === "null")) return text;
|
|
@@ -546,23 +544,59 @@ function parseJsonToolCall(block: string): ParsedToolCall | null {
|
|
|
546
544
|
return repairs.length ? { ...call, repaired: true, repairs } : call;
|
|
547
545
|
}
|
|
548
546
|
|
|
549
|
-
function parseXmlEqualsToolCall(block: string, tools: ToolSpec[]): ParsedToolCall | null {
|
|
547
|
+
function parseXmlEqualsToolCall(block: string, tools: ToolSpec[], strict = false): ParsedToolCall | null {
|
|
550
548
|
const match = XML_FUNCTION_EQUALS_RE.exec(block);
|
|
551
549
|
XML_FUNCTION_EQUALS_RE.lastIndex = 0;
|
|
552
550
|
if (!match) return null;
|
|
551
|
+
if (strict && (match.index !== 0 || match[0].length !== block.length)) return null;
|
|
553
552
|
const name = match[1]!.trim();
|
|
554
553
|
const body = match[2]!;
|
|
555
554
|
const args: Record<string, unknown> = {};
|
|
555
|
+
let cursor = 0;
|
|
556
556
|
for (const param of body.matchAll(XML_PARAMETER_EQUALS_RE)) {
|
|
557
|
+
if (strict && body.slice(cursor, param.index).trim()) return null;
|
|
557
558
|
const key = param[1]!.trim();
|
|
558
559
|
if (!key) throw new Error(`tool '${name}' contains an empty parameter name`);
|
|
559
|
-
|
|
560
|
+
if (strict && Object.hasOwn(args, key)) return null;
|
|
561
|
+
// Qwen's format carries raw text, not XML entity-encoded text. Match
|
|
562
|
+
// mlx_lm.tool_parsers.qwen3_coder: remove one framing newline at each
|
|
563
|
+
// end, preserving indentation, literal entities and JSON string escapes.
|
|
564
|
+
const value = param[2]!.replace(/^\n|\n$/g, "");
|
|
565
|
+
args[key] = decodeToolValue(value, toolParameterSchema(tools, name, key));
|
|
566
|
+
cursor = param.index + param[0].length;
|
|
560
567
|
}
|
|
561
568
|
XML_PARAMETER_EQUALS_RE.lastIndex = 0;
|
|
569
|
+
if (strict && body.slice(cursor).trim()) return null;
|
|
562
570
|
return { name, arguments: args };
|
|
563
571
|
}
|
|
564
572
|
|
|
565
|
-
|
|
573
|
+
/** One complete envelope, with full consumption and no syntax repair. Used
|
|
574
|
+
* to establish structural fill boundaries, never to verify model predictions. */
|
|
575
|
+
export function parseStrictToolCall(text: string, tools: ToolSpec[]): ParsedToolCall | null {
|
|
576
|
+
const open = "<tool_call>", close = "</tool_call>";
|
|
577
|
+
if (!text.startsWith(open) || !text.endsWith(close)) return null;
|
|
578
|
+
const body = text.slice(open.length, -close.length).trim();
|
|
579
|
+
if (body.includes(open) || body.includes(close)) return null;
|
|
580
|
+
try {
|
|
581
|
+
let call: ParsedToolCall | null;
|
|
582
|
+
if (body.startsWith("{")) {
|
|
583
|
+
// JSON.parse must succeed before the existing payload interpretation.
|
|
584
|
+
JSON.parse(body);
|
|
585
|
+
call = parseJsonToolCall(body);
|
|
586
|
+
} else {
|
|
587
|
+
call = parseXmlEqualsToolCall(body, tools, true) ?? parseGlm52ToolCall(body, tools, true);
|
|
588
|
+
}
|
|
589
|
+
if (!call || call.repaired || !tools.some(tool => toolSpecName(tool) === call.name)) return null;
|
|
590
|
+
return call;
|
|
591
|
+
} catch { return null; }
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** A textual argument delimiter is complete only after its closing bracket. */
|
|
595
|
+
export function hasCompleteToolValueDelimiter(text: string): boolean {
|
|
596
|
+
return /^(?:<\/parameter>|<\/arg_value>)/.test(text.trimStart());
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function parseGlm52ToolCall(block: string, tools: ToolSpec[], strict = false): ParsedToolCall | null {
|
|
566
600
|
const firstArg = block.search(GLM52_ARG_KEY_RE);
|
|
567
601
|
const name = (firstArg < 0 ? block : block.slice(0, firstArg)).trim();
|
|
568
602
|
if (!name || !/^[^\s<>]+$/.test(name)) return null;
|
|
@@ -576,7 +610,8 @@ function parseGlm52ToolCall(block: string, tools: ToolSpec[]): ParsedToolCall |
|
|
|
576
610
|
if (body.slice(cursor, pair.index).trim()) return null;
|
|
577
611
|
const key = pair[1]!.trim();
|
|
578
612
|
if (!key) throw new Error(`tool '${name}' contains an empty argument name`);
|
|
579
|
-
|
|
613
|
+
if (strict && Object.hasOwn(args, key)) return null;
|
|
614
|
+
args[key] = decodeToolValue(decodeXml(pair[2]!), toolParameterSchema(tools, name, key));
|
|
580
615
|
cursor = pair.index + pair[0].length;
|
|
581
616
|
matched = true;
|
|
582
617
|
}
|
|
@@ -594,7 +629,7 @@ function parseXmlAttrToolCalls(text: string, tools: ToolSpec[]): ParsedToolCall[
|
|
|
594
629
|
for (const param of body.matchAll(XML_PARAM_ATTR_RE)) {
|
|
595
630
|
const key = param[1]!.trim();
|
|
596
631
|
if (!key) throw new Error(`tool '${name}' contains an empty parameter name`);
|
|
597
|
-
args[key] = decodeToolValue(param[2]
|
|
632
|
+
args[key] = decodeToolValue(decodeXml(param[2]!), toolParameterSchema(tools, name, key));
|
|
598
633
|
}
|
|
599
634
|
XML_PARAM_ATTR_RE.lastIndex = 0;
|
|
600
635
|
calls.push({ name, arguments: args });
|
|
@@ -616,9 +651,11 @@ export function parseGeneratedToolCalls(text: string, tools: ToolSpec[]): Parsed
|
|
|
616
651
|
const calls: ParsedToolCall[] = [];
|
|
617
652
|
for (const block of text.matchAll(TOOL_CALL_BLOCK_RE)) {
|
|
618
653
|
const body = block[1]!.trim();
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
654
|
+
// A valid Qwen parameter may itself contain JSON. Dispatch by the outer
|
|
655
|
+
// format before JSON repair can mistake that value for the tool envelope.
|
|
656
|
+
const parsed = /^<function=/i.test(body)
|
|
657
|
+
? parseXmlEqualsToolCall(body, tools)
|
|
658
|
+
: parseJsonToolCall(body) ?? parseGlm52ToolCall(body, tools);
|
|
622
659
|
if (!parsed) throw new Error("unsupported tool_call payload format");
|
|
623
660
|
calls.push(parsed);
|
|
624
661
|
}
|
package/src/train/flash-cce.ts
CHANGED
|
@@ -728,7 +728,7 @@ const BWD_SG_SOURCE = String.raw`
|
|
|
728
728
|
// (flipped 2026-07-02 — the kernel-perf-review backlog #1 flip). The old
|
|
729
729
|
// 0.66%→2.7% dh-accuracy scare was on RANDOM targets (flat softmax, the filter's
|
|
730
730
|
// worst case — see PLAN.md "filter-on-real-data"); the real-data gate
|
|
731
|
-
// (
|
|
731
|
+
// (flash-cce-filter-realdata.ts (deleted 2026-08-23; git history)) measured, on REAL chunk-ORPO
|
|
732
732
|
// data (M1 Max, 2026-07-02): CPM5 eps 1e-5 = 0.343% dh for 1.41×; e4b = 0.158% dh
|
|
733
733
|
// for 1.70× — and the teacher-forced full-logits fidelity shows the filter's added
|
|
734
734
|
// error is far below the flash-vs-full fp-reassociation scatter that already
|
|
@@ -781,7 +781,7 @@ function bwdSgKernel(dimTiles: number, filterEps: string, blockEps: string): Met
|
|
|
781
781
|
// accumulated into D's frags LANE-LOCALLY (frag += frag — no get_coord math, the
|
|
782
782
|
// per-lane element mapping is identical between temp and D). dh atomic-added
|
|
783
783
|
// across the NBLK vocab-block programs. Validated dh ~1e-4 rel vs autograd.
|
|
784
|
-
// (Design + parity:
|
|
784
|
+
// (Design + parity: steel-bwd-htile-test.ts (deleted 2026-08-23; git history).)
|
|
785
785
|
// PHASE-2 DEQUANT: the Wd[32,HTw] staging uses MLX's vectorized+fused
|
|
786
786
|
// QuantizedBlockLoader (the SAME loader the forward uses), NOT a manual scalar
|
|
787
787
|
// dequant. The wrinkle: phase-2's GEMM contracts over vocab (K=32) and outputs H
|
package/src/train/forward.ts
CHANGED
|
@@ -31,6 +31,8 @@ import type { RuntimeModel } from "../model/factory";
|
|
|
31
31
|
* backward recompute, and a stateful (appending) cache would corrupt on the
|
|
32
32
|
* second run. */
|
|
33
33
|
export class TrainingCache implements Cache {
|
|
34
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
35
|
+
signature(): string { return "train:training"; }
|
|
34
36
|
offset = 0;
|
|
35
37
|
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
|
|
36
38
|
// Return fresh views: the caller disposes its input k/v right after (its
|
|
@@ -67,6 +69,8 @@ export class TrainingCache implements Cache {
|
|
|
67
69
|
* threw on the missing conv/recurrent/advance surface (the recorded
|
|
68
70
|
* "mlx-bun perplexity cannot score qwen3_5" gap). */
|
|
69
71
|
export class TrainingSSMCache implements Cache {
|
|
72
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
73
|
+
signature(): string { return "train:training-ssm"; }
|
|
70
74
|
offset = 0;
|
|
71
75
|
specRound: null = null;
|
|
72
76
|
get conv(): MlxArray | null { return null; }
|
|
@@ -192,6 +196,8 @@ export function buildBatchedPadMask(
|
|
|
192
196
|
* forwardLayers only disposes the mask it pulled from makeMask, so makeMask
|
|
193
197
|
* hands back a fresh view each call and keeps the canonical copy itself. */
|
|
194
198
|
class BatchedMaskCache implements Cache {
|
|
199
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
200
|
+
signature(): string { return "train:batched-mask"; }
|
|
195
201
|
constructor(
|
|
196
202
|
private readonly inner: Cache,
|
|
197
203
|
private readonly window: number | null,
|
package/src/train/loss.ts
CHANGED
|
@@ -1190,7 +1190,7 @@ export function fusedRespLogpMean(
|
|
|
1190
1190
|
// [chunk,vocabBlock]. Otherwise the whole-vocab analytic head below.
|
|
1191
1191
|
const vb = vocabBlock > 0 && vocabBlock < V ? vocabBlock : 0;
|
|
1192
1192
|
// Auto-dispatch by M (kernel backlog #3; measured 2026-07-02 on the M1 Max,
|
|
1193
|
-
//
|
|
1193
|
+
// head-dispatch-sweep.ts (deleted 2026-08-23; git history)): the fused QM head is FASTER
|
|
1194
1194
|
// at short M (e4b M=64 1.6×, M=512 1.13×; CPM5 faster at EVERY M) — the
|
|
1195
1195
|
// flash head's win is residency, plus time at long M on e4b (M≥2048 after
|
|
1196
1196
|
// the 2026-07-02 filter/blockMax flip; 8K: 10.7 vs 13.2 s). So a
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// prompt dominates (our chunk/document fine-tunes), ~0 when the response does.
|
|
12
12
|
//
|
|
13
13
|
// Bit-exact with the two-forward path (validated in
|
|
14
|
-
//
|
|
14
|
+
// prefix-shared-parity.ts (deleted 2026-08-23; git history)): each predicted token uses a hidden
|
|
15
15
|
// conditioned on exactly the same context + RoPE positions —
|
|
16
16
|
// chosen[k] from H[P-1+k] (k=0..Rc-1; H[P-1] = prompt's last)
|
|
17
17
|
// rejected[k] from H[P-1] (k=0) / H[P+Rc+k-1] (k≥1)
|
|
@@ -62,6 +62,8 @@ export function blockSparseMask(P: number, Rc: number, Rr: number): MlxArray {
|
|
|
62
62
|
* TrainingCache) but `makeMask` returns the block-sparse mask. One per layer;
|
|
63
63
|
* MiniCPM5's runLayerRange calls makeMask once and disposes the returned arr. */
|
|
64
64
|
export class PrefixSharedCache implements Cache {
|
|
65
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
66
|
+
signature(): string { return "train:prefix-shared"; }
|
|
65
67
|
offset = 0;
|
|
66
68
|
constructor(
|
|
67
69
|
private readonly P: number, private readonly Rc: number, private readonly Rr: number,
|
|
@@ -371,6 +373,8 @@ export function blockSparsePrefixMaskGemma(P: number, Rc: number, Rr: number, wi
|
|
|
371
373
|
* type serves both (the window arg selects the sliding term). One per DONOR
|
|
372
374
|
* layer (sharers reuse donors' fetched KV). */
|
|
373
375
|
class Gemma4PrefixSharedCache implements Cache {
|
|
376
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
377
|
+
signature(): string { return "train:gemma4prefix-shared"; }
|
|
374
378
|
offset = 0;
|
|
375
379
|
constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
|
|
376
380
|
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
|
package/src/train/segmented.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// segment-by-segment so only ONE segment's activations are ever live, instead
|
|
3
3
|
// of holding every layer's recompute activations at once (the spike that makes
|
|
4
4
|
// naive gradient checkpointing crash at long context). Design + proof:
|
|
5
|
-
// docs/design/
|
|
5
|
+
// docs/design/orpo-training.md (mechanism validated on a toy in
|
|
6
6
|
// scripts/ckpt-mem-test.ts, SEG mode: 0.000% grad error, peak below per-layer
|
|
7
7
|
// checkpointing; bit-exact on MiniCPM5 under flash attention).
|
|
8
8
|
//
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// `value_and_grad(sum(stop_grad(dh) (.) segment_forward(...)))` is numerically
|
|
25
25
|
// identical but LEAKS ~one activation buffer per segment per step at the mlx
|
|
26
26
|
// level (measured; not GC/cache/synchronize/reuse reclaimable — see
|
|
27
|
-
// docs/design/
|
|
27
|
+
// docs/design/orpo-training.md §9.4). mlx_vjp takes the cotangent
|
|
28
28
|
// directly, needs no surrogate, and does not leak.
|
|
29
29
|
//
|
|
30
30
|
// IMPORTANT lifetime fact: mlx `eval` does NOT detach — an eval'd array retains
|
|
@@ -130,7 +130,7 @@ export function planSegmentsBySize(nLayers: number, segSize: number): [number, n
|
|
|
130
130
|
// segment_size 1 measures 14.59 GB @8K (+3% step time) vs 17.5 @seg2. Below
|
|
131
131
|
// that needs the [M,V]-bounded SFT head (backlog #8, ~3 GB head-vjp spike)
|
|
132
132
|
// and/or an O(L)-memory attention backward. Evidence:
|
|
133
|
-
//
|
|
133
|
+
// seg-isolation-smoke.ts (deleted 2026-08-23; git history) + MLX_BUN_SEG_MEM_LOG=1 probes.
|
|
134
134
|
|
|
135
135
|
/** Detach `a` into a graph-free LEAF holding its exact (evaluated) bytes, then
|
|
136
136
|
* dispose `a`. mlx retains the upstream graph after `eval`, so a segment
|
|
@@ -1517,6 +1517,8 @@ export class SegmentedBackwardOrpoGemma4 {
|
|
|
1517
1517
|
* and null for full layers; the window arg selects the sliding term. One per DONOR
|
|
1518
1518
|
* layer (sharers reuse donors' fetched KV inside runLayerRange). */
|
|
1519
1519
|
class Gemma4PrefixSharedSegCache implements Cache {
|
|
1520
|
+
/** Training-only adapter; never admitted, merged, or persisted. */
|
|
1521
|
+
signature(): string { return "train:gemma4-prefix-shared-seg"; }
|
|
1520
1522
|
offset = 0;
|
|
1521
1523
|
constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
|
|
1522
1524
|
updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
|
package/src/train/trainer.ts
CHANGED
|
@@ -82,7 +82,7 @@ export interface TrainConfig {
|
|
|
82
82
|
* beats per-layer checkpointing at long context, where naive checkpointing
|
|
83
83
|
* holds every layer's recompute activations at once and crashes. B=1 SFT
|
|
84
84
|
* only (the responseOnlyCe path); mutually exclusive with gradCheckpoint.
|
|
85
|
-
* Numerically identical to off. See docs/design/
|
|
85
|
+
* Numerically identical to off. See docs/design/orpo-training.md. */
|
|
86
86
|
segmentSize: number;
|
|
87
87
|
/** Keep ALL eval-step checkpoints (full mountable adapters under
|
|
88
88
|
* checkpoints/step-<NNNNN>-val<loss>/) + write metrics.json. Off by default
|
|
@@ -440,12 +440,12 @@ export async function trainLora(
|
|
|
440
440
|
|
|
441
441
|
// ---------------------------------------------------------------------------
|
|
442
442
|
// Segment planning: uniform planSegmentsBySize only. A full-attention
|
|
443
|
-
// ISOLATION planner (kernel-review backlog #2 /
|
|
443
|
+
// ISOLATION planner (kernel-review backlog #2 / docs/design/orpo-training.md
|
|
444
444
|
// §5) was built and A/B-measured 2026-07-02: ZERO peak win — mlx's sdpa
|
|
445
445
|
// backward is O(L²) for EVERY layer (~3.5 GB/layer @8K e4b), sliding included,
|
|
446
446
|
// so the worst segment is set by layer count alone and segment_size is the
|
|
447
447
|
// whole knob (seg1 = 14.59 GB @8K, +3% step time — fits the 24 GB M4 Pro).
|
|
448
|
-
// Evidence:
|
|
448
|
+
// Evidence: seg-isolation-smoke.ts (deleted 2026-08-23; git history); note in segmented.ts.
|
|
449
449
|
// ---------------------------------------------------------------------------
|
|
450
450
|
|
|
451
451
|
// ---------------------------------------------------------------------------
|
|
@@ -473,7 +473,7 @@ async function sftLoop(
|
|
|
473
473
|
// Segmented backward: stream the SFT backward segment-by-segment (only one
|
|
474
474
|
// segment's activations live at a time). Phase A is MiniCPM5 SFT B=1 only;
|
|
475
475
|
// it replaces the single value_and_grad below and is mutually exclusive with
|
|
476
|
-
// gradient checkpointing (see docs/design/
|
|
476
|
+
// gradient checkpointing (see docs/design/orpo-training.md).
|
|
477
477
|
const useSegmented = cfg.segmentSize > 0;
|
|
478
478
|
let segmented: SegmentedBackward | SegmentedBackwardGemma4 | null = null;
|
|
479
479
|
if (useSegmented) {
|
|
@@ -532,13 +532,13 @@ async function sftLoop(
|
|
|
532
532
|
// hand-rolled flash kernel is an opt-in (MLX_BUN_TRAIN_ATTN=flash) O(L)-memory
|
|
533
533
|
// path for memory-bound long context — now correct (its dK-transpose and
|
|
534
534
|
// dQ-causal-barrier bugs were fixed; FD-validated in
|
|
535
|
-
//
|
|
535
|
+
// flash-fd-check.ts (deleted 2026-08-23; git history)), though ~30× slower than ops.sdpa.
|
|
536
536
|
// Cleared in finally.
|
|
537
537
|
//
|
|
538
538
|
// GEMMA GUARD (same convention as the launcher's env sanitization in
|
|
539
539
|
// cli.ts / job.ts, but enforced at the trainer): e4b on this path SIGTRAPed
|
|
540
540
|
// (uncatchable native crash) at multi-K sequence lengths (>=2K, reproduced in
|
|
541
|
-
//
|
|
541
|
+
// segmented-grad-test-e4b.ts (deleted 2026-08-23; git history); docs/reference/training.md)
|
|
542
542
|
// and has NOT been re-validated at that scale since the two kernel fixes —
|
|
543
543
|
// the regression tests stop at T<=256. cli.ts even defaults e4b seq to 8192,
|
|
544
544
|
// so a stale `export MLX_BUN_TRAIN_ATTN=flash` from a MiniCPM5 experiment
|
package/src/vision/prompt.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// n = min(ceil(duration_ms/40), 750) (processing_gemma4.py) — embed the full
|
|
9
9
|
// sequence, then overwrite the soft-token rows with the towers' features.
|
|
10
10
|
//
|
|
11
|
-
// Attention semantics (
|
|
11
|
+
// Attention semantics (docs/design/generic-model-support.md §3.3, resolved vs the oracle):
|
|
12
12
|
// - image runs attend bidirectionally among themselves (bidirMask), BUT
|
|
13
13
|
// any audio token in the prompt disables the vision overlay entirely —
|
|
14
14
|
// mixed image+audio prompts run fully causal (bidirMask = null);
|
|
@@ -115,9 +115,42 @@ export class Qwen3VLVisionTower {
|
|
|
115
115
|
return names;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/** Checkpoint-generation naming for the tower. Pre-5.8 artifacts store
|
|
119
|
+
* `vision_tower.*`; the transformers-5.8 export stores `model.visual.*`
|
|
120
|
+
* (mlx-lm's text-only qwen3_5 drops BOTH prefixes in `Model.sanitize`, so
|
|
121
|
+
* the mapping between them is ours). Verified 2026-09-01 across
|
|
122
|
+
* Qwen3.8-27B-tqalloc-norot vs the staged pre-5.8 artifact: 331/333
|
|
123
|
+
* tensors byte-identical under the rename, patch_embed.proj.weight
|
|
124
|
+
* identical under the layout permutation below, and merger.linear_fc2
|
|
125
|
+
* differs only because the staged artifact is rotation-folded. */
|
|
126
|
+
static #sourceName(canonical: string, prefix: string): string {
|
|
127
|
+
return prefix + canonical.slice("vision_tower.".length);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** HF stores the Conv3d patch embed as (out, in, kT, kH, kW); MLX (and this
|
|
131
|
+
* tower) want (out, kT, kH, kW, in). Proven a pure permutation on the
|
|
132
|
+
* artifacts above — transpose(0,2,3,4,1) of the 5.8 tensor is bit-equal to
|
|
133
|
+
* the pre-5.8 one. */
|
|
134
|
+
static #fixPatchEmbed(w: Map<string, MlxArray>): void {
|
|
135
|
+
const key = "vision_tower.patch_embed.proj.weight";
|
|
136
|
+
const a = w.get(key);
|
|
137
|
+
if (!a || a.shape.length !== 5) return;
|
|
138
|
+
if (a.shape[4] === 3) return; // already MLX layout
|
|
139
|
+
const t = ops.transposeAxes(a, [0, 2, 3, 4, 1]);
|
|
140
|
+
const c = ops.contiguous(t);
|
|
141
|
+
t.dispose();
|
|
142
|
+
a.dispose();
|
|
143
|
+
w.set(key, c);
|
|
144
|
+
}
|
|
145
|
+
|
|
118
146
|
/** Load one safetensors file's map and move `wanted` tensors into `out`
|
|
119
|
-
* (
|
|
120
|
-
|
|
147
|
+
* under their CANONICAL (`vision_tower.*`) names; `source` maps each
|
|
148
|
+
* canonical name to the stored one. Missing names are skipped; the map is
|
|
149
|
+
* always freed. */
|
|
150
|
+
static #takeFromFile(
|
|
151
|
+
path: string, wanted: string[], out: Map<string, MlxArray>,
|
|
152
|
+
source: (canonical: string) => string = (n) => n,
|
|
153
|
+
): void {
|
|
121
154
|
const arrMap = new BigUint64Array([C.mlx_map_string_to_array_new()]);
|
|
122
155
|
const metaMap = new BigUint64Array([C.mlx_map_string_to_string_new()]);
|
|
123
156
|
const arrMapPtr = ptr(arrMap);
|
|
@@ -136,7 +169,7 @@ export class Qwen3VLVisionTower {
|
|
|
136
169
|
if (out.has(name)) continue;
|
|
137
170
|
const slot = new BigUint64Array([C.mlx_array_new()]);
|
|
138
171
|
const slotPtr = ptr(slot);
|
|
139
|
-
if (C.mlx_map_string_to_array_get(slotPtr, handle, ptr(cstr(name))) !== 0) {
|
|
172
|
+
if (C.mlx_map_string_to_array_get(slotPtr, handle, ptr(cstr(source(name)))) !== 0) {
|
|
140
173
|
C.mlx_array_free(read.u64(slotPtr, 0));
|
|
141
174
|
continue;
|
|
142
175
|
}
|
|
@@ -156,28 +189,37 @@ export class Qwen3VLVisionTower {
|
|
|
156
189
|
// OptiQ-convention sidecar (their artifacts + ours pre-2026-08-18).
|
|
157
190
|
Qwen3VLVisionTower.#takeFromFile(sidecar, names, weights);
|
|
158
191
|
} else {
|
|
159
|
-
// In-main fallback: the bf16
|
|
192
|
+
// In-main fallback: the bf16 tower tensors live in the main
|
|
160
193
|
// safetensors shards (the mlx-vlm convention — one copy serves both
|
|
161
|
-
// ecosystems; sidecar duplication retired 2026-08-18).
|
|
194
|
+
// ecosystems; sidecar duplication retired 2026-08-18). The 5.8-family
|
|
195
|
+
// export names them `model.visual.*` instead of `vision_tower.*`.
|
|
162
196
|
const idxPath = `${modelDir}/model.safetensors.index.json`;
|
|
163
197
|
let files: string[];
|
|
198
|
+
let prefixes = ["vision_tower.", "model.visual."];
|
|
164
199
|
if (existsSync(idxPath)) {
|
|
165
200
|
const idx = JSON.parse(readFileSync(idxPath, "utf8")) as
|
|
166
201
|
{ weight_map: Record<string, string> };
|
|
167
|
-
|
|
168
|
-
names
|
|
169
|
-
)
|
|
202
|
+
prefixes = prefixes.filter((p) =>
|
|
203
|
+
Qwen3VLVisionTower.#sourceName(names[0]!, p) in idx.weight_map);
|
|
204
|
+
files = [...new Set(prefixes.flatMap((p) => names
|
|
205
|
+
.map((n) => idx.weight_map[Qwen3VLVisionTower.#sourceName(n, p)])
|
|
206
|
+
.filter((f): f is string => !!f)))];
|
|
170
207
|
if (files.length === 0)
|
|
171
|
-
throw new Error(`no
|
|
208
|
+
throw new Error(`no vision tower tensors in ${modelDir} (index has none)`);
|
|
172
209
|
} else {
|
|
173
210
|
files = ["model.safetensors"];
|
|
174
211
|
}
|
|
175
212
|
for (const f of files)
|
|
176
|
-
|
|
213
|
+
for (const p of prefixes)
|
|
214
|
+
Qwen3VLVisionTower.#takeFromFile(
|
|
215
|
+
`${modelDir}/${f}`, names, weights,
|
|
216
|
+
(n) => Qwen3VLVisionTower.#sourceName(n, p),
|
|
217
|
+
);
|
|
177
218
|
}
|
|
178
219
|
for (const name of names)
|
|
179
220
|
if (!weights.has(name))
|
|
180
221
|
throw new Error(`qwen vision weights missing tensor ${name} (sidecar or in-main)`);
|
|
222
|
+
Qwen3VLVisionTower.#fixPatchEmbed(weights);
|
|
181
223
|
} catch (e) {
|
|
182
224
|
for (const [, a] of weights) a.dispose();
|
|
183
225
|
throw e;
|
package/src/web/app.html
CHANGED
|
@@ -1216,7 +1216,7 @@
|
|
|
1216
1216
|
<!-- PWA installability (plan §9 Phase 3, beat-matrix Axis 10): manifest +
|
|
1217
1217
|
inline SVG icon (src/web/manifest.webmanifest, src/web/icon.svg — no
|
|
1218
1218
|
binary PNGs, per the hygiene gate; some browsers won't render an SVG
|
|
1219
|
-
app icon and that's an accepted tradeoff, see
|
|
1219
|
+
app icon and that's an accepted tradeoff, see docs/reference/server-config.md).
|
|
1220
1220
|
theme-color matches the app's --bg token so the OS chrome doesn't
|
|
1221
1221
|
flash a mismatched color on launch. -->
|
|
1222
1222
|
<link rel="manifest" href="/manifest.webmanifest">
|