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/jobs/runner.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CancellationSource } from "../engine/cancellation";
|
|
1
2
|
// Job runner: turns a registered `JobRunner` into a tracked, logged job.
|
|
2
3
|
// Two execution modes — both create a row, both stream NDJSON events to the
|
|
3
4
|
// log, both own terminal status:
|
|
@@ -14,6 +15,8 @@
|
|
|
14
15
|
// Port of optiq lab jobs.py `submit` + `_job_main`, split across this file
|
|
15
16
|
// and job-entry.ts.
|
|
16
17
|
|
|
18
|
+
import type { DisposableResource } from "../contracts/resources";
|
|
19
|
+
import { createJobTaskClient } from "./task-client";
|
|
17
20
|
import { appendFileSync } from "node:fs";
|
|
18
21
|
import { fileURLToPath } from "node:url";
|
|
19
22
|
import type { Emit, JobEvent, JobRunner } from "./types";
|
|
@@ -104,18 +107,28 @@ export interface SubmitResult {
|
|
|
104
107
|
* on success sets 'done'/progress 1/ended_at + emits 'done', on throw sets
|
|
105
108
|
* 'failed'/error/ended_at + emits 'failed'. Returns once the task is
|
|
106
109
|
* scheduled (does not await completion — poll the row / tail the log). */
|
|
110
|
+
interface InProcessWork { cancellation: CancellationSource; done: Promise<void>; }
|
|
111
|
+
const inProcess = new WeakMap<JobStore, Set<InProcessWork>>();
|
|
112
|
+
|
|
107
113
|
export function submitInProcess(
|
|
108
114
|
store: JobStore,
|
|
109
115
|
kind: string,
|
|
110
116
|
config: Record<string, unknown>,
|
|
111
117
|
outputPath?: string,
|
|
112
118
|
): SubmitResult {
|
|
119
|
+
if (closedStores.has(store)) throw new Error("job host is closed");
|
|
113
120
|
const row = store.create(kind, config, outputPath);
|
|
114
121
|
const emit = makeEmit(store, row.id, row.log_path);
|
|
115
122
|
store.setStatus(row.id, "running");
|
|
116
123
|
emit({ type: "started", ts: Date.now() });
|
|
117
124
|
|
|
118
125
|
const runner = getRunner(kind);
|
|
126
|
+
const cancellation = new CancellationSource();
|
|
127
|
+
const running = inProcess.get(store) ?? new Set<InProcessWork>();
|
|
128
|
+
inProcess.set(store, running);
|
|
129
|
+
let finish!: () => void;
|
|
130
|
+
const work = { cancellation, done: new Promise<void>((resolve) => { finish = resolve; }) };
|
|
131
|
+
running.add(work);
|
|
119
132
|
void (async () => {
|
|
120
133
|
if (!runner) {
|
|
121
134
|
const error = `no runner registered for kind "${kind}"`;
|
|
@@ -124,7 +137,7 @@ export function submitInProcess(
|
|
|
124
137
|
return;
|
|
125
138
|
}
|
|
126
139
|
try {
|
|
127
|
-
const result = await runner(
|
|
140
|
+
const result = await createJobTaskClient(runner).run(JSON.parse(row.config_json), emit, cancellation);
|
|
128
141
|
const out = result?.outputPath;
|
|
129
142
|
if (out) store.setOutputPath(row.id, out);
|
|
130
143
|
store.setProgress(row.id, 1);
|
|
@@ -135,7 +148,7 @@ export function submitInProcess(
|
|
|
135
148
|
store.setStatus(row.id, "failed", { error, endedAt: nowIso() });
|
|
136
149
|
emit({ type: "failed", error, ts: Date.now() });
|
|
137
150
|
}
|
|
138
|
-
})();
|
|
151
|
+
})().finally(() => { running.delete(work); finish(); });
|
|
139
152
|
|
|
140
153
|
return { jobId: row.id, outputPath };
|
|
141
154
|
}
|
|
@@ -145,16 +158,25 @@ export function submitInProcess(
|
|
|
145
158
|
// ---------------------------------------------------------------------------
|
|
146
159
|
|
|
147
160
|
interface QueuedSpawn {
|
|
161
|
+
acquire?: (signal: AbortSignal) => Promise<DisposableResource>;
|
|
162
|
+
lease?: DisposableResource;
|
|
163
|
+
abort: AbortController;
|
|
164
|
+
proc?: Bun.Subprocess;
|
|
165
|
+
finished: Promise<void>;
|
|
166
|
+
finish(): void;
|
|
148
167
|
store: JobStore;
|
|
149
168
|
jobId: string;
|
|
150
169
|
entry: string;
|
|
151
170
|
bin: string;
|
|
171
|
+
compiled: boolean;
|
|
152
172
|
spawn: typeof Bun.spawn;
|
|
153
173
|
onComplete?: (jobId: string, code: number) => void;
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
let gpuLeaseHolder: string | null = null;
|
|
157
177
|
const spawnQueue: QueuedSpawn[] = [];
|
|
178
|
+
let activeSpawn: QueuedSpawn | undefined;
|
|
179
|
+
const closedStores = new WeakSet<JobStore>();
|
|
158
180
|
|
|
159
181
|
/** True while a GPU (subprocess) job is running — the server gates token
|
|
160
182
|
* generation on this so quantize/finetune don't fight live inference. */
|
|
@@ -168,6 +190,8 @@ export function currentGpuJob(): string | null {
|
|
|
168
190
|
}
|
|
169
191
|
|
|
170
192
|
export interface SubprocessOpts {
|
|
193
|
+
/** Host execution lease, held until child exit (including crashes). */
|
|
194
|
+
acquire?: (signal: AbortSignal) => Promise<DisposableResource>;
|
|
171
195
|
/** Override the entry script (tests). Defaults to JOB_ENTRY_PATH. */
|
|
172
196
|
entry?: string;
|
|
173
197
|
/** Override the runtime binary (tests). Defaults to "bun". */
|
|
@@ -189,14 +213,20 @@ export function submitSubprocess(
|
|
|
189
213
|
outputPath?: string,
|
|
190
214
|
opts: SubprocessOpts = {},
|
|
191
215
|
): SubmitResult {
|
|
216
|
+
if (closedStores.has(store)) throw new Error("job host is closed");
|
|
192
217
|
const row = store.create(kind, config, outputPath);
|
|
218
|
+
let finish!: () => void;
|
|
219
|
+
const finished = new Promise<void>((resolve) => { finish = resolve; });
|
|
193
220
|
const item: QueuedSpawn = {
|
|
221
|
+
abort: new AbortController(), finished, finish,
|
|
194
222
|
store,
|
|
195
223
|
jobId: row.id,
|
|
196
224
|
entry: opts.entry ?? JOB_ENTRY_PATH,
|
|
225
|
+
compiled: opts.entry === undefined && opts.bin === undefined && JOB_ENTRY_PATH.includes("$bunfs"),
|
|
197
226
|
bin: opts.bin ?? "bun",
|
|
198
227
|
spawn: opts.spawn ?? Bun.spawn,
|
|
199
228
|
onComplete: opts.onComplete,
|
|
229
|
+
acquire: opts.acquire,
|
|
200
230
|
};
|
|
201
231
|
spawnQueue.push(item);
|
|
202
232
|
drainQueue();
|
|
@@ -215,7 +245,7 @@ function spawnNow(item: QueuedSpawn): void {
|
|
|
215
245
|
// a file-backed DB.
|
|
216
246
|
let proc: Bun.Subprocess<"ignore", "pipe", "pipe">;
|
|
217
247
|
try {
|
|
218
|
-
proc = spawn([bin, entry, jobId], {
|
|
248
|
+
proc = spawn(item.compiled ? [process.execPath, "__job", jobId] : [bin, entry, jobId], {
|
|
219
249
|
stdout: "pipe",
|
|
220
250
|
stderr: "pipe",
|
|
221
251
|
env: {
|
|
@@ -230,14 +260,16 @@ function spawnNow(item: QueuedSpawn): void {
|
|
|
230
260
|
error: errString(e),
|
|
231
261
|
endedAt: nowIso(),
|
|
232
262
|
});
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`[jobs] failed to record spawn failure: ${errString(error)}`);
|
|
233
265
|
} finally {
|
|
234
|
-
releaseLease(
|
|
266
|
+
releaseLease(item);
|
|
235
267
|
}
|
|
236
268
|
return;
|
|
237
269
|
}
|
|
238
270
|
|
|
239
|
-
|
|
240
|
-
const logPath =
|
|
271
|
+
item.proc = proc;
|
|
272
|
+
const logPath = store.get(jobId)?.log_path;
|
|
241
273
|
const logLine = (line: string) => {
|
|
242
274
|
if (!line || !logPath) return;
|
|
243
275
|
const ev: JobEvent = { type: "log", line };
|
|
@@ -252,7 +284,7 @@ function spawnNow(item: QueuedSpawn): void {
|
|
|
252
284
|
// code 0 ⇒ trust the child's terminal status (it set done/failed itself).
|
|
253
285
|
// non-zero ⇒ if the row never reached terminal (crash before the wrapper
|
|
254
286
|
// could write), force it failed.
|
|
255
|
-
if (code !== 0) {
|
|
287
|
+
try { if (code !== 0) {
|
|
256
288
|
const cur = store.get(jobId);
|
|
257
289
|
if (cur && (cur.status === "queued" || cur.status === "running")) {
|
|
258
290
|
store.setStatus(jobId, "failed", {
|
|
@@ -260,15 +292,25 @@ function spawnNow(item: QueuedSpawn): void {
|
|
|
260
292
|
endedAt: nowIso(),
|
|
261
293
|
});
|
|
262
294
|
}
|
|
295
|
+
} } catch (error) {
|
|
296
|
+
console.error(`[jobs] failed to record child exit: ${errString(error)}`);
|
|
297
|
+
} finally {
|
|
298
|
+
releaseLease(item);
|
|
299
|
+
try { item.onComplete?.(jobId, code); } catch {}
|
|
300
|
+
drainQueue();
|
|
263
301
|
}
|
|
264
|
-
releaseLease(jobId);
|
|
265
|
-
try { item.onComplete?.(jobId, code); } catch {}
|
|
266
|
-
drainQueue();
|
|
267
302
|
})();
|
|
268
303
|
}
|
|
269
304
|
|
|
270
|
-
function releaseLease(
|
|
271
|
-
|
|
305
|
+
function releaseLease(item: QueuedSpawn): void {
|
|
306
|
+
try { item.lease?.dispose(); }
|
|
307
|
+
catch (error) { console.error(`[jobs] execution lease cleanup failed: ${errString(error)}`); }
|
|
308
|
+
finally {
|
|
309
|
+
item.lease = undefined;
|
|
310
|
+
if (gpuLeaseHolder === item.jobId) gpuLeaseHolder = null;
|
|
311
|
+
if (activeSpawn === item) activeSpawn = undefined;
|
|
312
|
+
item.finish();
|
|
313
|
+
}
|
|
272
314
|
}
|
|
273
315
|
|
|
274
316
|
/** Run the next queued subprocess job if the lease is free. */
|
|
@@ -279,8 +321,51 @@ export function drainQueue(): void {
|
|
|
279
321
|
// A synchronous spawn failure releases the lease. Keep draining
|
|
280
322
|
// iteratively so a run of bad queue entries cannot recurse or wedge the
|
|
281
323
|
// first startable job behind them.
|
|
282
|
-
|
|
324
|
+
activeSpawn = next;
|
|
325
|
+
if (next.acquire) {
|
|
326
|
+
gpuLeaseHolder = next.jobId; // reserve FIFO order while inference drains
|
|
327
|
+
void Promise.resolve().then(() => next.acquire!(next.abort.signal)).then((lease) => {
|
|
328
|
+
next.lease = lease;
|
|
329
|
+
if (next.abort.signal.aborted) throw next.abort.signal.reason;
|
|
330
|
+
spawnNow(next);
|
|
331
|
+
}).catch((error) => {
|
|
332
|
+
try {
|
|
333
|
+
next.store.setStatus(next.jobId, "failed", { error: errString(error), endedAt: nowIso() });
|
|
334
|
+
} catch (recordError) {
|
|
335
|
+
console.error(`[jobs] failed to record admission failure: ${errString(recordError)}`);
|
|
336
|
+
} finally { releaseLease(next); }
|
|
337
|
+
}).finally(drainQueue);
|
|
338
|
+
} else spawnNow(next);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Dataset work stays in-process, but belongs to the application host too. */
|
|
343
|
+
export async function closeInProcessJobs(store: JobStore): Promise<void> {
|
|
344
|
+
closedStores.add(store);
|
|
345
|
+
const active = [...inProcess.get(store) ?? []];
|
|
346
|
+
for (const work of active) work.cancellation.cancel("engine_closed");
|
|
347
|
+
await Promise.all(active.map((work) => work.done));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Stop only this host's managed GPU jobs. Await process death before releasing
|
|
351
|
+
* the execution lease; queued and admission-waiting jobs never spawn. */
|
|
352
|
+
export async function closeSubprocessJobs(store: JobStore): Promise<void> {
|
|
353
|
+
closedStores.add(store);
|
|
354
|
+
for (let i = spawnQueue.length - 1; i >= 0; i--) {
|
|
355
|
+
const item = spawnQueue[i]!;
|
|
356
|
+
if (item.store !== store) continue;
|
|
357
|
+
spawnQueue.splice(i, 1);
|
|
358
|
+
store.setStatus(item.jobId, "failed", { error: "job host closed", endedAt: nowIso() });
|
|
359
|
+
item.finish();
|
|
283
360
|
}
|
|
361
|
+
const active = activeSpawn;
|
|
362
|
+
if (!active || active.store !== store) return;
|
|
363
|
+
active.abort.abort(new Error("job host closed"));
|
|
364
|
+
const proc = active.proc;
|
|
365
|
+
if (proc) proc.kill("SIGTERM");
|
|
366
|
+
const force = proc ? setTimeout(() => { if (proc.exitCode === null) proc.kill("SIGKILL"); }, 3000) : undefined;
|
|
367
|
+
try { await active.finished; }
|
|
368
|
+
finally { if (force) clearTimeout(force); }
|
|
284
369
|
}
|
|
285
370
|
|
|
286
371
|
/** Read a child stream line-by-line, buffering partial trailing lines, and
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TaskClient } from "../contracts/completion";
|
|
2
|
+
import { throwIfCancelled } from "../engine/cancellation";
|
|
3
|
+
import type { JobEvent, JobRunner } from "./types";
|
|
4
|
+
|
|
5
|
+
/** Numerical runners retain their artifact policy; the client owns delivery
|
|
6
|
+
* and cooperative cancellation at progress boundaries. No event buffering. */
|
|
7
|
+
export function createJobTaskClient(runner: JobRunner): TaskClient<
|
|
8
|
+
Record<string, unknown>, JobEvent, { outputPath?: string } | void
|
|
9
|
+
> {
|
|
10
|
+
return {
|
|
11
|
+
async run(request, report, cancellation) {
|
|
12
|
+
const check = () => { if (cancellation) throwIfCancelled(cancellation); };
|
|
13
|
+
check();
|
|
14
|
+
const result = await runner((event) => { check(); report(event); check(); }, request);
|
|
15
|
+
check();
|
|
16
|
+
return result;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
package/src/jobs/types.ts
CHANGED
|
@@ -1,58 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// vocabulary. Mirrors the optiq lab SSE event grammar so the web wizards
|
|
4
|
-
// port over mechanically (started → log/stage/metric → done|failed).
|
|
5
|
-
|
|
6
|
-
/** A single streamed job event, serialized one-per-line into the job's
|
|
7
|
-
* NDJSON log and re-emitted to the browser as SSE `data:` frames. */
|
|
8
|
-
export type JobEvent =
|
|
9
|
-
| { type: "started"; ts: number }
|
|
10
|
-
| { type: "log"; line: string }
|
|
11
|
-
| {
|
|
12
|
-
type: "stage";
|
|
13
|
-
stage: string;
|
|
14
|
-
progress?: number; // 0..1
|
|
15
|
-
message?: string;
|
|
16
|
-
[k: string]: unknown;
|
|
17
|
-
}
|
|
18
|
-
| {
|
|
19
|
-
type: "metric";
|
|
20
|
-
kind: "train" | "val";
|
|
21
|
-
step: number;
|
|
22
|
-
loss: number;
|
|
23
|
-
[k: string]: unknown; // grad_norm, learning_rate, tokens_per_sec, accuracy, margin, progress, message
|
|
24
|
-
}
|
|
25
|
-
| { type: "done"; ts: number; output_dir?: string; summary?: unknown; [k: string]: unknown }
|
|
26
|
-
| { type: "failed"; error: string; ts: number };
|
|
27
|
-
|
|
28
|
-
/** Sink a job calls to report progress. Implementations append to the log
|
|
29
|
-
* file and (for stage/metric events carrying `progress`/`message`) update
|
|
30
|
-
* the SQLite row. Never throws — a logging failure must not kill a job. */
|
|
31
|
-
export type Emit = (e: JobEvent) => void;
|
|
32
|
-
|
|
33
|
-
/** The unit of work. Returns an optional output path recorded on the row.
|
|
34
|
-
* Receives the parsed `config` from the submit request. */
|
|
35
|
-
export type JobRunner = (
|
|
36
|
-
emit: Emit,
|
|
37
|
-
config: Record<string, unknown>,
|
|
38
|
-
) => Promise<{ outputPath?: string } | void>;
|
|
39
|
-
|
|
40
|
-
/** Job kinds in the system. */
|
|
41
|
-
export type JobKind = "quantize" | "finetune" | "dataset" | "noop" | "crash";
|
|
42
|
-
|
|
43
|
-
export type JobStatus = "queued" | "running" | "done" | "failed" | "zombie";
|
|
44
|
-
|
|
45
|
-
/** A persisted job row (shape returned by the HTTP `GET /api/jobs/:id`). */
|
|
46
|
-
export interface JobRow {
|
|
47
|
-
id: string;
|
|
48
|
-
kind: string;
|
|
49
|
-
status: JobStatus;
|
|
50
|
-
config_json: string;
|
|
51
|
-
progress: number;
|
|
52
|
-
message: string | null;
|
|
53
|
-
log_path: string;
|
|
54
|
-
output_path: string | null;
|
|
55
|
-
error: string | null;
|
|
56
|
-
started_at: string;
|
|
57
|
-
ended_at: string | null;
|
|
58
|
-
}
|
|
1
|
+
/** Compatibility exports; producer and browser share the portable wire contract. */
|
|
2
|
+
export type * from "../contracts/jobs";
|
package/src/kv-scheme.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { KvQuantSpec, ModelConfig, TurboQuantScheme } from "./config";
|
|
2
|
+
|
|
3
|
+
const BF16_BYTES = 2;
|
|
4
|
+
|
|
5
|
+
export type KvQuantOverride = "off" | "config" | number | undefined;
|
|
6
|
+
|
|
7
|
+
export interface KvSchemeOptions {
|
|
8
|
+
kvBits?: number;
|
|
9
|
+
kvGroupSize?: number;
|
|
10
|
+
kvConfig?: KvQuantSpec[];
|
|
11
|
+
quantizedKvStart?: number;
|
|
12
|
+
turboQuant?: TurboQuantScheme;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ResolvedKvSchemeOptions extends Omit<KvSchemeOptions, "kvConfig" | "turboQuant"> {
|
|
16
|
+
readonly kvConfig?: readonly Readonly<KvQuantSpec>[];
|
|
17
|
+
readonly turboQuant?: Readonly<TurboQuantScheme>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type KvSchemeKind = "bf16" | "affine-uniform" | "affine-config" | "turbo";
|
|
21
|
+
|
|
22
|
+
export interface KvGeometry {
|
|
23
|
+
fullBytesPerToken: number;
|
|
24
|
+
slidingBytesPerToken: number;
|
|
25
|
+
linearStateBytes: number;
|
|
26
|
+
window: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Bytes per stored affine-quantized KV element, including scale and bias. */
|
|
30
|
+
export function kvQuantBytesPerElement(bits: number, groupSize: number): number {
|
|
31
|
+
return bits / 8 + 4 / groupSize;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function kvGeometry(config: ModelConfig, options: KvSchemeOptions = {}): KvGeometry {
|
|
35
|
+
const text = config.text;
|
|
36
|
+
const byLayer = options.kvConfig?.length
|
|
37
|
+
? new Map(options.kvConfig.map((entry) => [entry.layerIdx, entry]))
|
|
38
|
+
: null;
|
|
39
|
+
const uniformBytes = options.kvBits
|
|
40
|
+
? kvQuantBytesPerElement(options.kvBits, options.kvGroupSize ?? 64)
|
|
41
|
+
: BF16_BYTES;
|
|
42
|
+
const elementBytes = (layer: number): number => {
|
|
43
|
+
const entry = byLayer?.get(layer);
|
|
44
|
+
return entry
|
|
45
|
+
? kvQuantBytesPerElement(entry.bits, entry.groupSize)
|
|
46
|
+
: byLayer ? BF16_BYTES : uniformBytes;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let linearLayers = 0;
|
|
50
|
+
let fullBytesPerToken = 0;
|
|
51
|
+
let slidingBytesPerToken = 0;
|
|
52
|
+
for (let layer = 0; layer < text.numHiddenLayers; layer++) {
|
|
53
|
+
const type = text.layerTypes[layer] ?? "full_attention";
|
|
54
|
+
if (type === "linear_attention") {
|
|
55
|
+
linearLayers++;
|
|
56
|
+
} else if (type === "sliding_attention") {
|
|
57
|
+
slidingBytesPerToken +=
|
|
58
|
+
2 * text.numKeyValueHeads * text.headDim * elementBytes(layer);
|
|
59
|
+
} else {
|
|
60
|
+
fullBytesPerToken +=
|
|
61
|
+
2 * text.numGlobalKeyValueHeads * text.globalHeadDim * elementBytes(layer);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const convDim = 2 * text.linearNumKeyHeads * text.linearKeyHeadDim +
|
|
65
|
+
text.linearNumValueHeads * text.linearValueHeadDim;
|
|
66
|
+
const linearStateBytes = linearLayers === 0 ? 0 : linearLayers * (
|
|
67
|
+
text.linearNumValueHeads * text.linearValueHeadDim * text.linearKeyHeadDim * 4 +
|
|
68
|
+
Math.max(0, text.linearConvKernelDim - 1) * convDim * BF16_BYTES
|
|
69
|
+
);
|
|
70
|
+
return {
|
|
71
|
+
fullBytesPerToken,
|
|
72
|
+
slidingBytesPerToken,
|
|
73
|
+
linearStateBytes,
|
|
74
|
+
window: text.slidingWindow,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function kvBytesAt(
|
|
79
|
+
config: ModelConfig,
|
|
80
|
+
tokens: number,
|
|
81
|
+
options: KvSchemeOptions = {},
|
|
82
|
+
): number {
|
|
83
|
+
const value = kvGeometry(config, options);
|
|
84
|
+
return value.fullBytesPerToken * tokens +
|
|
85
|
+
value.slidingBytesPerToken * Math.min(tokens, value.window) +
|
|
86
|
+
value.linearStateBytes;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class KvScheme {
|
|
90
|
+
readonly kind: KvSchemeKind;
|
|
91
|
+
readonly options: Readonly<ResolvedKvSchemeOptions>;
|
|
92
|
+
|
|
93
|
+
constructor(kind: KvSchemeKind, options: KvSchemeOptions) {
|
|
94
|
+
this.kind = kind;
|
|
95
|
+
const kvConfig = options.kvConfig?.map((entry) =>
|
|
96
|
+
Object.freeze({ ...entry }),
|
|
97
|
+
);
|
|
98
|
+
const turboQuant = options.turboQuant
|
|
99
|
+
? Object.freeze({ ...options.turboQuant })
|
|
100
|
+
: undefined;
|
|
101
|
+
this.options = Object.freeze({
|
|
102
|
+
...options,
|
|
103
|
+
...(kvConfig ? { kvConfig: Object.freeze(kvConfig) } : {}),
|
|
104
|
+
...(turboQuant ? { turboQuant } : {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get quantized(): boolean {
|
|
109
|
+
return this.kind !== "bf16";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get cacheKey(): string {
|
|
113
|
+
if (this.kind === "turbo") {
|
|
114
|
+
const turbo = this.options.turboQuant!;
|
|
115
|
+
return `turbo-k${turbo.kBits}v${turbo.vBits}`;
|
|
116
|
+
}
|
|
117
|
+
if (this.kind === "affine-uniform") return `kv${this.options.kvBits}`;
|
|
118
|
+
if (this.kind === "affine-config") return "config";
|
|
119
|
+
return "bf16";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get label(): string {
|
|
123
|
+
if (this.kind === "turbo") {
|
|
124
|
+
const turbo = this.options.turboQuant!;
|
|
125
|
+
return `turbo k${turbo.kBits}v${turbo.vBits}`;
|
|
126
|
+
}
|
|
127
|
+
if (this.kind === "affine-uniform") return `uniform-kv${this.options.kvBits}`;
|
|
128
|
+
if (this.kind === "affine-config") return "mixed (kv_config.json)";
|
|
129
|
+
return "bf16";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
bytesAt(config: ModelConfig, tokens: number): number {
|
|
133
|
+
// TurboQuant stays conservatively billed as bf16 until its packed layout
|
|
134
|
+
// exposes a stable projector.
|
|
135
|
+
return kvBytesAt(config, tokens, this.fitOptions);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Mutable compatibility value for the numerical generator. The scheme
|
|
139
|
+
* keeps its declaration immutable and hands legacy consumers owned copies. */
|
|
140
|
+
get generationOptions(): KvSchemeOptions {
|
|
141
|
+
const { kvConfig, turboQuant, ...scalar } = this.options;
|
|
142
|
+
return {
|
|
143
|
+
...scalar,
|
|
144
|
+
...(kvConfig
|
|
145
|
+
? { kvConfig: kvConfig.map((entry) => ({ ...entry })) }
|
|
146
|
+
: {}),
|
|
147
|
+
...(turboQuant
|
|
148
|
+
? { turboQuant: { ...turboQuant } }
|
|
149
|
+
: {}),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
get fitOptions(): KvSchemeOptions {
|
|
154
|
+
return this.kind === "turbo" ? {} : this.generationOptions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The current batch engine accepts only per-layer affine schemes whose
|
|
158
|
+
* named cache instances can perform the required conversion. The capability
|
|
159
|
+
* probe is mandatory for quantized schemes so config shape alone can never
|
|
160
|
+
* authorize placement. */
|
|
161
|
+
batchable(config: ModelConfig, canConvert?: (layerIdx: number) => boolean): boolean {
|
|
162
|
+
if (this.kind === "bf16") return true;
|
|
163
|
+
if (this.kind !== "affine-config") return false;
|
|
164
|
+
if (!canConvert) return false;
|
|
165
|
+
return this.options.kvConfig!.every((entry) => {
|
|
166
|
+
if (entry.layerIdx < 0 || entry.layerIdx >= config.text.numHiddenLayers) return false;
|
|
167
|
+
return (config.text.layerTypes[entry.layerIdx] ?? "full_attention") !== "linear_attention" &&
|
|
168
|
+
canConvert(entry.layerIdx);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function resolveKvScheme(input: {
|
|
174
|
+
override?: KvQuantOverride;
|
|
175
|
+
turboQuant?: TurboQuantScheme;
|
|
176
|
+
config?: readonly KvQuantSpec[] | null;
|
|
177
|
+
missingConfig?: "bf16" | "error";
|
|
178
|
+
}): KvScheme {
|
|
179
|
+
if (input.turboQuant) {
|
|
180
|
+
return new KvScheme("turbo", {
|
|
181
|
+
turboQuant: input.turboQuant,
|
|
182
|
+
quantizedKvStart: 0,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (input.override === "off" || input.override === undefined)
|
|
186
|
+
return new KvScheme("bf16", {});
|
|
187
|
+
if (input.override === "config") {
|
|
188
|
+
if (!input.config?.length) {
|
|
189
|
+
if (input.missingConfig === "error")
|
|
190
|
+
throw new Error("model has no kv_config.json (--kv-quant config)");
|
|
191
|
+
return new KvScheme("bf16", {});
|
|
192
|
+
}
|
|
193
|
+
return new KvScheme("affine-config", { kvConfig: [...input.config] });
|
|
194
|
+
}
|
|
195
|
+
return new KvScheme("affine-uniform", {
|
|
196
|
+
kvBits: input.override,
|
|
197
|
+
quantizedKvStart: 0,
|
|
198
|
+
});
|
|
199
|
+
}
|