mlx-bun 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -588
- package/docs/README.md +91 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/design/batching.md +569 -0
- package/docs/design/decode-speed-program.md +3178 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +579 -334
- package/docs/design/kv-cache.md +680 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1627 -0
- package/docs/design/turboquant.md +2102 -0
- package/docs/design/unified-engine-frontier-plan.md +1831 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +34 -1
- package/docs/planning/release-notes-v0.3.0.md +35 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +1162 -0
- package/docs/reference/cli.md +394 -159
- package/docs/reference/distribution.md +321 -182
- package/docs/reference/environment.md +348 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +104 -6
- package/docs/reference/server-api.md +886 -425
- package/docs/reference/server-config.md +638 -330
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +166 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1646 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +47 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/gateway-binding.ts +71 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/memory-guard.ts +40 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/rollback.ts +51 -0
- package/src/backends/mlx/serial-executor.ts +273 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-views.ts +26 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/cli.ts +58 -25
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +49 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/resources.ts +18 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +51 -0
- package/src/engine/resources.ts +42 -0
- package/src/engine/scheduler.ts +67 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +1 -1
- package/src/generate.ts +656 -268
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-store.ts +109 -49
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +9 -10
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/batched-mask.ts +8 -2
- package/src/model/batched-quant.ts +1 -1
- package/src/model/batched-rotating-quant.ts +2 -2
- package/src/model/batched-rotating.ts +1 -1
- package/src/model/compiled-decode.ts +1 -1
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/gemma4-base.ts +26 -9
- package/src/model/gemma4.ts +6 -6
- package/src/model/generated/gemma4-12b.ts +2 -2
- package/src/model/generated/gemma4-26b.ts +2 -2
- package/src/model/generated/gemma4-e4b.ts +2 -2
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/implementation.ts +69 -0
- package/src/model/minicpm5.ts +6 -8
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +140 -56
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/universal/dense.ts +1 -1
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/prompt-cache.ts +96 -53
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +4 -4
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +154 -193
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +512 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +19 -15
- package/src/serve/request-prep.ts +346 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +317 -2649
- package/src/spec/assistant-source.ts +10 -44
- package/src/spec/deepspec-source.ts +6 -6
- package/src/spec/dflash-source.ts +8 -8
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +2 -2
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +4 -3
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +1 -1
- package/src/spec/ngram-source.ts +1 -1
- package/src/spec/prefix-state.ts +45 -0
- package/src/spec/qwen-mtp-source.ts +166 -69
- package/src/spec/serve-loop.ts +154 -156
- package/src/spec/source.ts +42 -10
- package/src/spec/two-model.ts +1 -1
- package/src/ssd-cache.ts +80 -12
- package/src/ssd-durability.ts +25 -13
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +5 -3
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
package/src/prompt-cache.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { leaseCacheState } from "./backends/mlx/state-views";
|
|
2
|
+
import { withResource, cleanupFailure, disposeResources, ownResource } from "./engine/resources";
|
|
1
3
|
// Byte-capped LRU prompt cache — the RAM tier of the Layer-0 KV store.
|
|
2
4
|
//
|
|
3
5
|
// The mlx-lm lesson (PLAN.md): a count-capped cache of multi-GB KV
|
|
@@ -5,13 +7,14 @@
|
|
|
5
7
|
// per entry) and evicts least-recently-used until under the cap.
|
|
6
8
|
//
|
|
7
9
|
// Usage pattern (single generation queue, so take/put is race-free):
|
|
8
|
-
// const hit = cache.take(promptIds); // longest-prefix match
|
|
10
|
+
// const hit = cache.take(promptIds); // longest-prefix match
|
|
9
11
|
// generate(model, promptIds, { cache: hit?.caches ?? fresh })
|
|
10
12
|
// cache.put([...promptIds, ...generated], caches, ns, hit?.retain);
|
|
11
|
-
// take() is NON-CONSUMING
|
|
12
|
-
// leaves the donor
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// take() is NON-CONSUMING for trimmable caches: it serves zero-copy clones
|
|
14
|
+
// and leaves the donor in place, so N agents can share one prefill.
|
|
15
|
+
// Qwen hybrid entries containing recurrent SSM state transfer ownership on
|
|
16
|
+
// an exact hit, avoiding an extra resident recurrent snapshot. put() supersedes
|
|
17
|
+
// same-ns prefix-ancestors (when the new entry is trimmable) so a conversation
|
|
15
18
|
// stays one entry, not one per turn.
|
|
16
19
|
//
|
|
17
20
|
// TIERING (Layer 0, unified-engine plan): when a ColdTier is attached,
|
|
@@ -55,8 +58,9 @@ function makeSharedRetain(retain: (() => void) | undefined): { acquire(): () =>
|
|
|
55
58
|
|
|
56
59
|
/** The cold (SSD) tier the RAM cache tiers over. Structural — this module
|
|
57
60
|
* never imports ssd-cache (which imports us); the server binds
|
|
58
|
-
* SsdCacheStore + the model into this shape (src/server.ts).
|
|
59
|
-
*
|
|
61
|
+
* SsdCacheStore + the model into this shape (src/server.ts). Lookup and
|
|
62
|
+
* restore failures return null; store returns false on failure. Operations
|
|
63
|
+
* that require persistence retain their donor if storing fails. */
|
|
60
64
|
export interface ColdTier {
|
|
61
65
|
/** Longest stored usable prefix for prompt/ns — index-only, no I/O.
|
|
62
66
|
* `handle` is the tier's opaque entry token, passed back to restore. */
|
|
@@ -65,7 +69,9 @@ export interface ColdTier {
|
|
|
65
69
|
* pages fault in lazily). `retain` must run after the caches are
|
|
66
70
|
* disposed (it unmaps the backing file). Null on any failure. */
|
|
67
71
|
restore(handle: unknown): { tokens: number[]; caches: Cache[]; retain: () => void } | null;
|
|
68
|
-
|
|
72
|
+
/** Borrow live caches and finish persistence before returning. A false
|
|
73
|
+
* result prevents a required demotion or recurrent ownership transfer. */
|
|
74
|
+
store(tokens: number[], caches: Cache[], ns: string): boolean | void;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
/** Spill sink for eviction/demotion (write-behind, 2026-07-06).
|
|
@@ -105,14 +111,7 @@ export interface PromptCacheEntry {
|
|
|
105
111
|
export function cacheBytes(caches: Cache[]): number {
|
|
106
112
|
let total = 0;
|
|
107
113
|
for (const c of caches) {
|
|
108
|
-
|
|
109
|
-
for (const a of state) total += a.nbytes;
|
|
110
|
-
// Most Cache kinds' state() returns their own live-owned arrays (safe
|
|
111
|
-
// to just read), but TurboQuantKVCache allocates fresh trimmed slice
|
|
112
|
-
// views per call (kv-store.ts snapshotCache/cloneKvCaches contract) —
|
|
113
|
-
// those must be disposed here or they leak (see generate.ts
|
|
114
|
-
// evalCacheState for the same hazard on the prefill path).
|
|
115
|
-
if (c.stateNeedsDispose) for (const a of state) a.dispose();
|
|
114
|
+
total += withResource(leaseCacheState(c), (state) => state.reduce((bytes, a) => bytes + a.nbytes, 0));
|
|
116
115
|
}
|
|
117
116
|
return total;
|
|
118
117
|
}
|
|
@@ -202,8 +201,8 @@ export class PromptCache {
|
|
|
202
201
|
if (spill) try { spill(entry); } catch {}
|
|
203
202
|
}
|
|
204
203
|
}
|
|
205
|
-
|
|
206
|
-
entry.retain?.();
|
|
204
|
+
disposeResources(entry.caches);
|
|
205
|
+
entry.retain?.(); // backing release requires successful cache disposal
|
|
207
206
|
}
|
|
208
207
|
|
|
209
208
|
get totalBytes(): number {
|
|
@@ -214,14 +213,36 @@ export class PromptCache {
|
|
|
214
213
|
return this.#entries.length;
|
|
215
214
|
}
|
|
216
215
|
|
|
216
|
+
/** Release LRU entries until the caller's live-memory check is satisfied.
|
|
217
|
+
* Unlike background eviction, pressure relief must finish the SSD write
|
|
218
|
+
* and release the buffers now, not enqueue clones that keep them alive.
|
|
219
|
+
* Call under the engine lock, after taking the request's reusable prefix. */
|
|
220
|
+
relievePressure(overBudget: () => boolean): number {
|
|
221
|
+
let count = 0;
|
|
222
|
+
while (this.#entries.length && overBudget()) {
|
|
223
|
+
let oldest = 0;
|
|
224
|
+
for (let i = 1; i < this.#entries.length; i++)
|
|
225
|
+
if (this.#entries[i]!.lastUsed < this.#entries[oldest]!.lastUsed) oldest = i;
|
|
226
|
+
const { entry } = this.#entries[oldest]!;
|
|
227
|
+
// Keep the entry resident if persistence throws. A failed SSD write
|
|
228
|
+
// must not be presented as a successful demotion.
|
|
229
|
+
if (this.#cold) {
|
|
230
|
+
if (this.#cold.store(entry.tokens, entry.caches, entry.ns) === false) break;
|
|
231
|
+
}
|
|
232
|
+
else this.#spillSync?.(entry);
|
|
233
|
+
this.#entries.splice(oldest, 1);
|
|
234
|
+
this.#disposeEntry(entry, false);
|
|
235
|
+
this.demotions++;
|
|
236
|
+
count++;
|
|
237
|
+
}
|
|
238
|
+
return count;
|
|
239
|
+
}
|
|
240
|
+
|
|
217
241
|
/** Find the entry with the longest usable common prefix of `prompt`
|
|
218
|
-
* across BOTH tiers
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* prefix. The caller owns the returned caches — dispose them or put()
|
|
223
|
-
* an extended entry (which supersedes prefix-ancestors), honoring
|
|
224
|
-
* `retain` either way.
|
|
242
|
+
* across BOTH tiers. Trimmable RAM entries are served non-consumingly as
|
|
243
|
+
* zero-copy clones; exact hits on Qwen hybrid entries containing recurrent
|
|
244
|
+
* SSM state transfer the donor itself. The caller owns the returned caches —
|
|
245
|
+
* dispose them or put() an extended entry, honoring `retain` either way.
|
|
225
246
|
*
|
|
226
247
|
* Usable prefix = common prefix capped at prompt.length - 1 (at least
|
|
227
248
|
* one token must be forwarded to produce logits). Entries longer than
|
|
@@ -258,27 +279,22 @@ export class PromptCache {
|
|
|
258
279
|
if (hit && hit.prefixLen > bestLen) {
|
|
259
280
|
const loaded = this.#cold.restore(hit.handle);
|
|
260
281
|
if (loaded) {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
caches: loaded.caches,
|
|
278
|
-
ns,
|
|
279
|
-
retain: loaded.retain,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
+
const owner = ownResource(loaded, (entry) =>
|
|
283
|
+
this.#disposeEntry({ ...entry, ns }, false));
|
|
284
|
+
const result = withResource(owner, (entry) => {
|
|
285
|
+
const trimNeeded = entry.tokens.length - hit.prefixLen;
|
|
286
|
+
if (trimNeeded > 0 && !entry.caches.every((cache) => cache.isTrimmable())) {
|
|
287
|
+
console.warn(
|
|
288
|
+
`[prompt-cache] cold entry unusable: ${entry.tokens.length} tokens, ` +
|
|
289
|
+
`needs trim ${trimNeeded} but untrimmable (wrapped ring/SSM) — re-prefilling`,
|
|
290
|
+
);
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (trimNeeded > 0) for (const cache of entry.caches) cache.trim(trimNeeded);
|
|
294
|
+
const tokens = entry.tokens.slice(0, hit.prefixLen);
|
|
295
|
+
return { ...owner.transfer(), tokens, ns };
|
|
296
|
+
});
|
|
297
|
+
if (result) return result;
|
|
282
298
|
}
|
|
283
299
|
}
|
|
284
300
|
}
|
|
@@ -296,9 +312,25 @@ export class PromptCache {
|
|
|
296
312
|
const rec = this.#entries[bestIdx]!;
|
|
297
313
|
rec.lastUsed = ++this.#clock;
|
|
298
314
|
rec.lastUsedMs = Date.now();
|
|
315
|
+
// Qwen recurrent caches are untrimmable. Transfer the exact-boundary
|
|
316
|
+
// entry instead of retaining a second recurrent snapshot in RAM.
|
|
317
|
+
// This reduced memory pressure in the recorded multi-turn reproduction.
|
|
318
|
+
// Save the boundary before consumption: the extended recurrent state
|
|
319
|
+
// cannot be trimmed back, and a delayed writer can no longer find it
|
|
320
|
+
// in RAM after ownership moves. An already durable boundary is cheap.
|
|
321
|
+
if (rec.entry.caches.some((c) => c.signature() === "ssm")) {
|
|
322
|
+
if (this.#cold?.store(rec.entry.tokens, rec.entry.caches, ns) === false)
|
|
323
|
+
throw new Error("prompt-cache: failed to persist recurrent boundary before transfer");
|
|
324
|
+
this.#entries.splice(bestIdx, 1);
|
|
325
|
+
return rec.entry;
|
|
326
|
+
}
|
|
299
327
|
const clones = this.#clone(rec.entry.caches);
|
|
300
328
|
const trimNeeded = rec.entry.tokens.length - bestLen;
|
|
301
|
-
|
|
329
|
+
try {
|
|
330
|
+
if (trimNeeded > 0) for (const c of clones) c.trim(trimNeeded);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
cleanupFailure(error, () => disposeResources(clones));
|
|
333
|
+
}
|
|
302
334
|
return {
|
|
303
335
|
tokens: rec.entry.tokens.slice(0, bestLen),
|
|
304
336
|
caches: clones,
|
|
@@ -337,10 +369,20 @@ export class PromptCache {
|
|
|
337
369
|
* (spilling each to the cold tier first, when one is attached). If the
|
|
338
370
|
* entry itself exceeds the cap it is spilled + disposed, not stored. */
|
|
339
371
|
put(tokens: number[], caches: Cache[], ns = "", retain?: () => void): void {
|
|
372
|
+
// All cache-provided validation runs before ownership is adopted.
|
|
340
373
|
const bytes = cacheBytes(caches);
|
|
374
|
+
const trimmable = caches.every((cache) => cache.isTrimmable());
|
|
375
|
+
const discard = (entry: PromptCacheEntry, spill: boolean) => {
|
|
376
|
+
try { this.#disposeEntry(entry, spill); }
|
|
377
|
+
catch (error) {
|
|
378
|
+
// The new entry is already owned here. Throwing back to its previous
|
|
379
|
+
// owner would invite disposal of state still held by this store.
|
|
380
|
+
console.warn(`[prompt-cache] entry cleanup failed: ${error}`);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
341
383
|
const entry: PromptCacheEntry = { tokens, caches, ns, retain };
|
|
342
384
|
if (bytes > this.maxBytes) {
|
|
343
|
-
|
|
385
|
+
discard(entry, true);
|
|
344
386
|
return;
|
|
345
387
|
}
|
|
346
388
|
// The donor's retain becomes ref-counted so take()'s clones can hold
|
|
@@ -359,7 +401,7 @@ export class PromptCache {
|
|
|
359
401
|
if (old.entry.tokens.length !== tokens.length) continue;
|
|
360
402
|
if (commonPrefixLength(old.entry.tokens, tokens) !== tokens.length) continue;
|
|
361
403
|
this.#entries = this.#entries.filter((r) => r !== old);
|
|
362
|
-
|
|
404
|
+
discard(old.entry, false);
|
|
363
405
|
}
|
|
364
406
|
// SUPERSEDE strict prefix-ancestors (the SSD store's rule, brought to
|
|
365
407
|
// RAM — prefix sharing would otherwise grow an entry per turn): same-ns
|
|
@@ -370,13 +412,13 @@ export class PromptCache {
|
|
|
370
412
|
// can only serve exact-length matches, so shorter ancestors — e.g.
|
|
371
413
|
// the prompt-boundary snapshot that makes drifted multi-turn agents
|
|
372
414
|
// hit — must survive.
|
|
373
|
-
if (
|
|
415
|
+
if (trimmable) {
|
|
374
416
|
for (const old of [...this.#entries]) {
|
|
375
417
|
if (old === rec || old.entry.ns !== ns) continue;
|
|
376
418
|
if (old.entry.tokens.length >= tokens.length) continue;
|
|
377
419
|
if (commonPrefixLength(old.entry.tokens, tokens) !== old.entry.tokens.length) continue;
|
|
378
420
|
this.#entries = this.#entries.filter((r) => r !== old);
|
|
379
|
-
|
|
421
|
+
discard(old.entry, false);
|
|
380
422
|
}
|
|
381
423
|
}
|
|
382
424
|
while (this.totalBytes > this.maxBytes && this.#entries.length > 1) {
|
|
@@ -384,7 +426,7 @@ export class PromptCache {
|
|
|
384
426
|
for (let i = 1; i < this.#entries.length; i++)
|
|
385
427
|
if (this.#entries[i]!.lastUsed < this.#entries[lruIdx]!.lastUsed) lruIdx = i;
|
|
386
428
|
const [evicted] = this.#entries.splice(lruIdx, 1);
|
|
387
|
-
|
|
429
|
+
discard(evicted!.entry, true);
|
|
388
430
|
}
|
|
389
431
|
try { this.onPut?.(tokens, ns); } catch {}
|
|
390
432
|
}
|
|
@@ -413,7 +455,8 @@ export class PromptCache {
|
|
|
413
455
|
}
|
|
414
456
|
|
|
415
457
|
clear(): void {
|
|
416
|
-
|
|
458
|
+
const entries = this.#entries;
|
|
417
459
|
this.#entries = [];
|
|
460
|
+
disposeResources(entries.map(({ entry }) => ({ dispose: () => this.#disposeEntry(entry, false) })));
|
|
418
461
|
}
|
|
419
462
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// - NaN-sensitivity handling (treat as maximally sensitive)
|
|
12
12
|
// - post-greedy block-run guard
|
|
13
13
|
//
|
|
14
|
-
// Verified bit-for-bit against the Python via tests/quantize-allocator.test.ts.
|
|
14
|
+
// Verified bit-for-bit against the Python via tests/parity/quantize-allocator.test.ts.
|
|
15
15
|
//
|
|
16
16
|
// The latency-aware variants (`optimize_latency_aware`,
|
|
17
17
|
// `optimize_for_latency_budget`) are intentionally NOT ported — they pull in
|
|
@@ -13,7 +13,9 @@ import { existsSync } from "node:fs";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
15
15
|
/** A per-module quantization override. `false` means "left unquantized". */
|
|
16
|
-
export type PerLayerEntry =
|
|
16
|
+
export type PerLayerEntry =
|
|
17
|
+
| { bits: number; groupSize: number; mode?: string; trellis?: { L: number; code: string; axis: 0 | 1 } }
|
|
18
|
+
| false;
|
|
17
19
|
|
|
18
20
|
/** The default quantization definition for the model. */
|
|
19
21
|
export interface QuantDef {
|
|
@@ -28,7 +30,7 @@ export interface QuantizationBlock {
|
|
|
28
30
|
bits: number;
|
|
29
31
|
mode: string;
|
|
30
32
|
// plus per-module entries: { [modulePath]: {bits, group_size} | false }
|
|
31
|
-
[modulePath: string]: number | string | { bits: number; group_size: number } | false;
|
|
33
|
+
[modulePath: string]: number | string | { bits: number; group_size: number; mode?: string; trellis?: { L: number; code: string; axis: 0 | 1 } } | false;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -47,7 +49,12 @@ export function buildQuantizationBlock(
|
|
|
47
49
|
mode: def.mode ?? "affine",
|
|
48
50
|
};
|
|
49
51
|
for (const [path, entry] of perLayer) {
|
|
50
|
-
|
|
52
|
+
if (entry === false) { block[path] = false; continue; }
|
|
53
|
+
const v: { bits: number; group_size: number; mode?: string; trellis?: { L: number; code: string; axis: 0 | 1 } } =
|
|
54
|
+
{ bits: entry.bits, group_size: entry.groupSize };
|
|
55
|
+
if (entry.mode) v.mode = entry.mode;
|
|
56
|
+
if (entry.trellis) v.trellis = entry.trellis;
|
|
57
|
+
block[path] = v;
|
|
51
58
|
}
|
|
52
59
|
return block;
|
|
53
60
|
}
|
package/src/quantize/rotate.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Folds an orthogonal rotation offline into producer/consumer weight pairs so
|
|
4
4
|
// weights are quantized in a basis where outlier channels are smeared into
|
|
5
5
|
// near-Gaussian marginals. Recipe, fold table, and the decided deviations from
|
|
6
|
-
// the QuaRot/SpinQuant references live in docs/design/turboquant
|
|
6
|
+
// the QuaRot/SpinQuant references live in docs/design/turboquant.md —
|
|
7
7
|
// notably: γ-fold FIRST (R1 only commutes with gain-free RMSNorm), SpinQuant's
|
|
8
8
|
// fully-offline per-head R2 (NOT QuaRot's, which needs a runtime op), NO R4
|
|
9
9
|
// half-fold on down_proj's input, NO embedding mean-centering (not an exact
|
|
@@ -47,7 +47,7 @@ function assertPow2(n: number, what: string): void {
|
|
|
47
47
|
if (n < 2 || (n & (n - 1)) !== 0)
|
|
48
48
|
throw new Error(
|
|
49
49
|
`rotation fold: ${what}=${n} is not a power of two — the Kronecker ` +
|
|
50
|
-
`Hadamard path is not implemented (docs/design/turboquant
|
|
50
|
+
`Hadamard path is not implemented (docs/design/turboquant.md)`,
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -323,7 +323,7 @@ export function foldLlamaWeights(
|
|
|
323
323
|
// elementwise in head space) does not commute with a per-head rotation, so R2
|
|
324
324
|
// is architecturally off for this family — recorded in the design doc.
|
|
325
325
|
//
|
|
326
|
-
// Residual corridors (docs/design/turboquant
|
|
326
|
+
// Residual corridors (docs/design/turboquant.md, W1 map):
|
|
327
327
|
// readers (@R1 input dim, γ premultiplied): q/k/v_proj,
|
|
328
328
|
// linear_attn.in_proj_{qkv,z,b,a}, mlp.gate/up_proj, lm_head
|
|
329
329
|
// writers (R1ᵀ output dim): self_attn.o_proj, linear_attn.out_proj,
|
|
@@ -370,7 +370,7 @@ export interface QwenFoldPlan {
|
|
|
370
370
|
* FoldOp — pure name analysis, no arrays. R1-ONLY (+γ): the attention output
|
|
371
371
|
* gate (`o_proj(out · σ(gate))`, elementwise in head space) does not commute
|
|
372
372
|
* with a per-head rotation, so R2 is architecturally off for this family.
|
|
373
|
-
* Corridor map: docs/design/turboquant
|
|
373
|
+
* Corridor map: docs/design/turboquant.md §W1.
|
|
374
374
|
*/
|
|
375
375
|
export function planQwen35Fold(names: readonly string[], prefix = "language_model."): QwenFoldPlan {
|
|
376
376
|
const P = prefix;
|