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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { AdmissionPool } from "../engine/admission";
|
|
2
|
+
import { acquireReservation } from "./preparation";
|
|
3
|
+
import { cleanupFailure, disposeResources } from "../engine/resources";
|
|
1
4
|
// GenerationGateway is the scheduling seam between CompletionExecutor and
|
|
2
5
|
// the inference runtime. It declares one mechanism per request: the preserved
|
|
3
6
|
// strict/dedicated serial executor, or the continuous scheduler (`--batch N`).
|
|
@@ -26,31 +29,25 @@
|
|
|
26
29
|
// server.py's all-caches-have-merge check). GLM's capability owns compressed
|
|
27
30
|
// MLA/DSA merge/extract/filter and byte projection; Qwen3.5's SSM cache owns
|
|
28
31
|
// its row state. The remaining request-shape exclusions include vision, LoRA,
|
|
29
|
-
// serial-only KV modes
|
|
32
|
+
// serial-only KV modes
|
|
30
33
|
// (reproducibility ⇒ solo, matching mlx-lm's _is_batchable). Temperature /
|
|
31
34
|
// top-p / top-k DO batch (each row samples with its own seed). Full-attention
|
|
32
35
|
// AND sliding-window (Gemma) models both batch — the scheduler assembles each
|
|
33
36
|
// layer's cache by type. Prompt-cache prefix reuse works on the batch lane
|
|
34
37
|
// too (Phase 3.2): joiners take() at admission; never-merged lone rows put()
|
|
35
|
-
// back on finish
|
|
38
|
+
// back zero-copy on finish; merged rows with ≥256 prompt tokens are extracted
|
|
39
|
+
// and put back by BatchScheduler.#extractAndPut (see docs/design/batching.md).
|
|
36
40
|
|
|
37
41
|
import { MlxArray } from "../mlx/array";
|
|
38
42
|
import type { RuntimeModel } from "../model/factory";
|
|
39
|
-
import { DiffusionGemmaModel } from "../model/diffusion-gemma";
|
|
40
|
-
import {
|
|
41
|
-
KVCache,
|
|
42
|
-
RotatingKVCache,
|
|
43
|
-
isBatchableCache,
|
|
44
|
-
isPlainKvCache,
|
|
45
|
-
isRotatingPlainCache,
|
|
46
|
-
} from "../model/gemma4-base";
|
|
47
|
-
import { SSMCache } from "../model/qwen3-delta";
|
|
48
|
-
import { UniversalDenseModel } from "../model/universal/dense";
|
|
49
43
|
import type { GenerateOptions, GenerateStats, TokenLogprobs } from "../generate";
|
|
50
44
|
import type { KvScheme } from "../kv-scheme";
|
|
45
|
+
import { createRowSampling } from "../backends/mlx/row-sampling";
|
|
51
46
|
import { makeStepSampler } from "../sampler";
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
47
|
+
import type { RowPromptCache } from "../backends/mlx/batch-group";
|
|
48
|
+
import { bindMlxGateway, type MlxGatewayBinding, type MlxBatchGroup } from "../backends/mlx/gateway-binding";
|
|
49
|
+
import type { RuntimeConfig } from "../runtime-config";
|
|
50
|
+
import type { ExecutionRequirements, ResolvedExecution } from "../contracts/execution";
|
|
54
51
|
import type { PromptResponseTrace } from "./prompt-response-trace";
|
|
55
52
|
|
|
56
53
|
/** Async mutex: acquire() resolves to a release fn; releases run FIFO. */
|
|
@@ -61,18 +58,38 @@ class AsyncMutex {
|
|
|
61
58
|
get locked(): boolean {
|
|
62
59
|
return this.#pending > 0;
|
|
63
60
|
}
|
|
64
|
-
acquire(): Promise<() => void> {
|
|
61
|
+
acquire(signal?: AbortSignal): Promise<() => void> {
|
|
62
|
+
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
65
63
|
this.#pending++;
|
|
66
64
|
let release!: () => void;
|
|
67
65
|
const gate = new Promise<void>((r) => { release = r; });
|
|
68
66
|
const wait = this.#tail;
|
|
69
67
|
this.#tail = this.#tail.then(() => gate);
|
|
70
68
|
let released = false;
|
|
71
|
-
|
|
69
|
+
const unlock = () => {
|
|
72
70
|
if (released) return; // idempotent: a double release must not skew #pending
|
|
73
71
|
released = true;
|
|
74
72
|
this.#pending--;
|
|
75
73
|
release();
|
|
74
|
+
};
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
let settled = false;
|
|
77
|
+
const abort = () => {
|
|
78
|
+
if (settled) return;
|
|
79
|
+
settled = true;
|
|
80
|
+
signal?.removeEventListener("abort", abort);
|
|
81
|
+
// Release this waiter's gate, not its predecessor's. Later acquirers
|
|
82
|
+
// still wait for the active holder, preserving mutual exclusion.
|
|
83
|
+
unlock();
|
|
84
|
+
reject(signal!.reason);
|
|
85
|
+
};
|
|
86
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
87
|
+
wait.then(() => {
|
|
88
|
+
if (settled) return;
|
|
89
|
+
settled = true;
|
|
90
|
+
signal?.removeEventListener("abort", abort);
|
|
91
|
+
resolve(unlock);
|
|
92
|
+
});
|
|
76
93
|
});
|
|
77
94
|
}
|
|
78
95
|
}
|
|
@@ -85,7 +102,7 @@ export type Vision = {
|
|
|
85
102
|
embeddings: MlxArray;
|
|
86
103
|
/** bool [L] image-token mask for the bidirectional attention overlay.
|
|
87
104
|
* Absent when the prompt carries ANY audio — audio(-containing) prompts
|
|
88
|
-
* run fully causal (
|
|
105
|
+
* run fully causal (docs/design/generic-model-support.md §3.3 Q1). */
|
|
89
106
|
imageMask?: MlxArray;
|
|
90
107
|
/** bool [L] union multimodal soft-token mask (image | audio) for
|
|
91
108
|
* per-layer-input id zeroing. Absent on the legacy vision-only shape,
|
|
@@ -97,7 +114,7 @@ export type Vision = {
|
|
|
97
114
|
};
|
|
98
115
|
|
|
99
116
|
/** Per-token sink: returning `false` halts this generation (stop sequence).
|
|
100
|
-
* `logprobs` is
|
|
117
|
+
* `logprobs` is populated on either lane when the request asked for
|
|
101
118
|
* logprobs capture (GenerateOptions.logprobs / topLogprobs). */
|
|
102
119
|
export type OnToken = (
|
|
103
120
|
token: number,
|
|
@@ -112,53 +129,11 @@ export type SerialRun = (
|
|
|
112
129
|
onToken: OnToken,
|
|
113
130
|
vision?: Vision,
|
|
114
131
|
trace?: PromptResponseTrace,
|
|
132
|
+
execution?: ResolvedExecution,
|
|
115
133
|
) => Promise<GenerateStats>;
|
|
116
134
|
|
|
117
135
|
/** What the batchable decision needs from a request (cheap to compute). */
|
|
118
|
-
export interface RequestShape {
|
|
119
|
-
readonly hasVision: boolean;
|
|
120
|
-
readonly hasAdapters: boolean;
|
|
121
|
-
readonly hasRepetitionPenalty: boolean;
|
|
122
|
-
/** The user explicitly set `seed` (reproducibility) — not the random default. */
|
|
123
|
-
readonly userSeed: boolean;
|
|
124
|
-
/** KV quantization is active. Per-layer configs may use the continuous
|
|
125
|
-
* scheduler; uniform schemes use the serial mechanism. */
|
|
126
|
-
readonly kvQuant: boolean;
|
|
127
|
-
/** TurboQuant is active (docs/design/turboquant-kv.md). Solo-only in v1:
|
|
128
|
-
* TurboQuantKVCache is a novel Cache implementation (not a KVCache/
|
|
129
|
-
* RotatingKVCache subclass), so #modelCachesBatchable() already excludes
|
|
130
|
-
* it automatically once a request's cache is converted — this flag is the
|
|
131
|
-
* BELT on top of that automatic BRACES, an explicit refusal at the
|
|
132
|
-
* request-shape level so a turbo request never reaches the scheduler even
|
|
133
|
-
* before any cache conversion has happened. Both layers exist on purpose. */
|
|
134
|
-
readonly turboQuant: boolean;
|
|
135
|
-
/** Any of the mlx-lm sampler/processor extensions is active: min_p, XTC,
|
|
136
|
-
* logit_bias, presence/frequency penalty. INFORMATIONAL ONLY: since the
|
|
137
|
-
* continuous scheduler grew per-row samplers/logits processors, these all
|
|
138
|
-
* compose. Placement deliberately does not gate on this field (see the
|
|
139
|
-
* repetition-penalty note in #supportsContinuous). Kept so /stats and
|
|
140
|
-
* scheduling traces can show what
|
|
141
|
-
* a request carries. */
|
|
142
|
-
readonly hasLogitsExtras: boolean;
|
|
143
|
-
/** A grammar controller compiled for this request (response_format /
|
|
144
|
-
* guided_*). Degrade-path requests (compile failed → prompt injection)
|
|
145
|
-
* have NO controller and stay batchable — the injection already happened
|
|
146
|
-
* at the prompt level. B0 routes grammar to serial; B1 makes it batchable
|
|
147
|
-
* (per-row matchers), with MLX_BUN_GRAMMAR_BATCH=0 forcing the B0 serial
|
|
148
|
-
* fallback as the A/B + kill switch. */
|
|
149
|
-
readonly hasGrammar: boolean;
|
|
150
|
-
/** The request asked for logprobs/top_logprobs capture. Batch-lane logprobs
|
|
151
|
-
* are deferred (the scheduler's per-row sampler doesn't capture or read
|
|
152
|
-
* back logprob arrays yet), so these route to the serial lane like the
|
|
153
|
-
* other mlx-lm request extensions above. */
|
|
154
|
-
readonly wantsLogprobs: boolean;
|
|
155
|
-
/** A draft model is configured (serve --draft-model). Server-level and
|
|
156
|
-
* upstream-parity: mlx_lm.server sets is_batchable = (draft is None), so
|
|
157
|
-
* every request routes serial while a draft is mounted — speculation is a
|
|
158
|
-
* B=1 latency optimization, batching a throughput one; they are different
|
|
159
|
-
* mechanisms by design (grammar-spec-batching-integration.md). */
|
|
160
|
-
readonly hasDraft: boolean;
|
|
161
|
-
}
|
|
136
|
+
export interface RequestShape extends ExecutionRequirements {}
|
|
162
137
|
|
|
163
138
|
export type GenerationMechanism = "serial" | "continuous";
|
|
164
139
|
|
|
@@ -167,12 +142,22 @@ export type GenerationMechanism = "serial" | "continuous";
|
|
|
167
142
|
export interface GenerationPlacement {
|
|
168
143
|
readonly shape: RequestShape;
|
|
169
144
|
readonly mechanism: GenerationMechanism;
|
|
145
|
+
readonly execution?: ResolvedExecution;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function disposeUnstartedRequest(options: GenerateOptions, vision?: Vision): void {
|
|
149
|
+
disposeResources([options.grammar, vision?.embeddings, vision?.imageMask,
|
|
150
|
+
vision?.multimodalMask, options.visionPixels].filter((resource) => resource != null));
|
|
170
151
|
}
|
|
171
152
|
|
|
172
153
|
export class GenerationGateway {
|
|
154
|
+
#closed = false;
|
|
155
|
+
readonly #runtime: RuntimeConfig;
|
|
156
|
+
readonly #binding: MlxGatewayBinding;
|
|
157
|
+
readonly #requests: AdmissionPool;
|
|
173
158
|
readonly #mutex = new AsyncMutex();
|
|
174
159
|
readonly #batch: number;
|
|
175
|
-
#scheduler:
|
|
160
|
+
#scheduler: MlxBatchGroup | null = null;
|
|
176
161
|
#rowsSubmitted = 0;
|
|
177
162
|
/** Serial-lane requests waiting for / holding the mutex. While > 0 the
|
|
178
163
|
* scheduler pauses admission (drain) so they can't be starved. */
|
|
@@ -182,11 +167,13 @@ export class GenerationGateway {
|
|
|
182
167
|
/** Lazy, memoized: can the configured KV scheme convert every named cache? */
|
|
183
168
|
#kvSchemeBatchable: boolean | null = null;
|
|
184
169
|
constructor(
|
|
185
|
-
|
|
170
|
+
model: RuntimeModel | MlxGatewayBinding,
|
|
186
171
|
batch: number,
|
|
187
172
|
private readonly serialRun: SerialRun,
|
|
188
173
|
private readonly opts: {
|
|
189
174
|
kvBudgetBytes?: number;
|
|
175
|
+
checkpoints?: boolean;
|
|
176
|
+
stateCodecs?: import("../kv-store").CacheCodecProvider;
|
|
190
177
|
/** The server-wide KV scheme (server.ts kvScheme) — threaded to the
|
|
191
178
|
* scheduler at construction when batchable (Phase 3.1). turboQuant is
|
|
192
179
|
* never threaded to the scheduler (always solo-only — see placement);
|
|
@@ -197,9 +184,14 @@ export class GenerationGateway {
|
|
|
197
184
|
* back on finish. Safe to share with the serial lane — both use it
|
|
198
185
|
* only inside this gateway's mutual-exclusion domain. */
|
|
199
186
|
promptCache?: RowPromptCache;
|
|
187
|
+
adapterNamespace?: (adapters: string[]) => string;
|
|
200
188
|
} = {},
|
|
201
189
|
) {
|
|
190
|
+
this.#binding = "createBatchGroup" in model ? model : bindMlxGateway(model);
|
|
191
|
+
this.#runtime = this.#binding.runtime;
|
|
192
|
+
this.opts = Object.freeze({ ...opts });
|
|
202
193
|
this.#batch = Math.max(1, Math.floor(batch));
|
|
194
|
+
this.#requests = new AdmissionPool(this.#batch);
|
|
203
195
|
}
|
|
204
196
|
|
|
205
197
|
/** True if `--batch N` (N>1) is on (batchability is then per-request). */
|
|
@@ -229,19 +221,31 @@ export class GenerationGateway {
|
|
|
229
221
|
* running/pending. activeRows/pendingRows alone MISS the serial lane —
|
|
230
222
|
* a serial generation holds the mutex but shows zero rows. */
|
|
231
223
|
get busy(): boolean {
|
|
232
|
-
return this.#
|
|
224
|
+
return this.#requests.active > 0 || this.#requests.queued > 0 ||
|
|
225
|
+
this.#mutex.locked || this.activeRows > 0 || this.pendingRows > 0;
|
|
233
226
|
}
|
|
234
227
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
228
|
+
async close(): Promise<void> {
|
|
229
|
+
this.#closed = true;
|
|
230
|
+
this.#requests.close();
|
|
231
|
+
await this.#scheduler?.close();
|
|
232
|
+
await this.onIdle();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Observe idleness. Call runWhenIdle for background work that must also
|
|
236
|
+
* acquire ownership atomically with the idle check. */
|
|
241
237
|
async onIdle(pollMs = 20): Promise<void> {
|
|
242
238
|
while (this.busy) await new Promise<void>((r) => setTimeout(r, pollMs));
|
|
243
239
|
}
|
|
244
240
|
|
|
241
|
+
/** Background work waits without requesting a batch drain. The final busy
|
|
242
|
+
* check and mutex acquisition have no await between them, so a foreground
|
|
243
|
+
* request cannot enter between the check and ownership registration. */
|
|
244
|
+
async runWhenIdle<T>(fn: () => Promise<T>, pollMs = 20): Promise<T> {
|
|
245
|
+
while (this.busy) await new Promise<void>(resolve => setTimeout(resolve, pollMs));
|
|
246
|
+
return this.runExclusive(fn);
|
|
247
|
+
}
|
|
248
|
+
|
|
245
249
|
/** Cumulative rows routed to the batch lane since server start. The serial
|
|
246
250
|
* lane never advances this — /stats' race-free lane-routing observable
|
|
247
251
|
* (active/pending are instantaneous and read 0 once a request finishes). */
|
|
@@ -257,113 +261,22 @@ export class GenerationGateway {
|
|
|
257
261
|
};
|
|
258
262
|
}
|
|
259
263
|
|
|
260
|
-
/** Cache-capability gate (mirrors mlx-lm server.py's all-caches-have-merge
|
|
261
|
-
* check): the scheduler's dynamic-B ops (temporalView/merge/filter) exist
|
|
262
|
-
* on KVCache, RotatingKVCache, and SSMCache (hybrid gated-DeltaNet models
|
|
263
|
-
* — Qwen3.5 — batch via SSMCache.mergeRows/filter; MLX_BUN_BATCH_SSM=0 is
|
|
264
|
-
* the kill switch back to serial routing). */
|
|
265
264
|
#modelCachesBatchable(): boolean {
|
|
266
|
-
|
|
267
|
-
// Tier-0 universal models: batchable for PLAIN full-attention archs
|
|
268
|
-
// only. The 2026-07-03 uneven-row bug (scalar-offset RoPE decoded
|
|
269
|
-
// padded rows at wrong positions) is fixed via
|
|
270
|
-
// UniversalRope.applyDynamic + ropeOffsetArr and GATED token-exact vs
|
|
271
|
-
// mlx-lm B=2 on Llama-3.2-3B (static + dynamic join/leave,
|
|
272
|
-
// tests/batched-decode-parity.test.ts "Llama 3B Tier-0"). The
|
|
273
|
-
// maskArray archs (gemma2-family: forwardLayers builds a pad-blind
|
|
274
|
-
// causal mask) and sliding-window universal archs remain UNVALIDATED
|
|
275
|
-
// cells → serial, per the per-model-cell discipline.
|
|
276
|
-
if (this.model instanceof UniversalDenseModel) {
|
|
277
|
-
const a = this.model.args;
|
|
278
|
-
this.#cacheBatchable =
|
|
279
|
-
!a.maskArray && !a.layerTypes?.includes("sliding_attention");
|
|
280
|
-
return this.#cacheBatchable;
|
|
281
|
-
}
|
|
282
|
-
const ssmOk = runtimeValue("MLX_BUN_BATCH_SSM") !== "0";
|
|
283
|
-
const proto = this.model.makeCache(); // fresh caches hold no buffers
|
|
284
|
-
this.#cacheBatchable = proto.every(
|
|
285
|
-
(c) =>
|
|
286
|
-
c instanceof KVCache ||
|
|
287
|
-
c instanceof RotatingKVCache ||
|
|
288
|
-
isBatchableCache(c) ||
|
|
289
|
-
(ssmOk && c instanceof SSMCache),
|
|
290
|
-
);
|
|
291
|
-
for (const c of proto) c.dispose();
|
|
292
|
-
}
|
|
293
|
-
return this.#cacheBatchable;
|
|
265
|
+
return this.#cacheBatchable ??= this.#binding.cachesBatchable();
|
|
294
266
|
}
|
|
295
267
|
|
|
296
|
-
/** Phase 3.1 + milestone 2: the server's kv scheme batches iff it is a
|
|
297
|
-
* per-layer kvConfig (the L2 mixed scheme) whose every configured layer
|
|
298
|
-
* is a full-attention KVCache (BatchedQuantDecodeMaskCache + quantized
|
|
299
|
-
* merge/extend/filter) or a rotating RotatingKVCache
|
|
300
|
-
* (BatchedRotatingQuantCache — milestone 2, gemma's kv_config). Uniform
|
|
301
|
-
* kvBits (quantizedKvStart threshold semantics via the serial no-byLayer
|
|
302
|
-
* path) and configs naming SSM layers stay serial. */
|
|
303
268
|
#kvBatchable(): boolean {
|
|
304
|
-
|
|
305
|
-
const scheme = this.opts.kvScheme;
|
|
306
|
-
if (scheme?.kind !== "affine-config") {
|
|
307
|
-
this.#kvSchemeBatchable = false;
|
|
308
|
-
} else {
|
|
309
|
-
const proto = this.model.makeCache();
|
|
310
|
-
try {
|
|
311
|
-
this.#kvSchemeBatchable = scheme.batchable(
|
|
312
|
-
this.model.config,
|
|
313
|
-
(layerIdx) =>
|
|
314
|
-
isPlainKvCache(proto[layerIdx]) || isRotatingPlainCache(proto[layerIdx]),
|
|
315
|
-
);
|
|
316
|
-
} finally {
|
|
317
|
-
for (const cache of proto) cache.dispose();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
return this.#kvSchemeBatchable;
|
|
269
|
+
return this.#kvSchemeBatchable ??= !!this.opts.kvScheme && this.#binding.kvBatchable(this.opts.kvScheme);
|
|
322
270
|
}
|
|
323
271
|
|
|
324
|
-
|
|
325
|
-
* continuous scheduler. This is a support check only: it never rewrites
|
|
326
|
-
* MTP/drafting, KV, TurboQuant, grammar, adapters, or sampling. */
|
|
327
|
-
#supportsContinuous(shape: RequestShape): boolean {
|
|
328
|
-
// DiffusionGemma is non-autoregressive — the batch scheduler assumes the AR
|
|
329
|
-
// KV-cache decode path, so it always runs serially through generate().
|
|
330
|
-
if (this.model instanceof DiffusionGemmaModel) return false;
|
|
331
|
-
if (!this.batchingEnabled) return false;
|
|
332
|
-
if (!this.#modelCachesBatchable()) return false;
|
|
333
|
-
// repetitionPenalty / logits extras (min_p, XTC, logit_bias,
|
|
334
|
-
// presence/frequency penalties) batch through the same StepSampler and
|
|
335
|
-
// per-row device history as the serial lane. (Load-bearing beyond opt-in
|
|
336
|
-
// knobs: some models — Qwen3.5 — ship a default repetition penalty in
|
|
337
|
-
// generation_config.json, which used to route EVERY request serial.)
|
|
338
|
-
return (
|
|
339
|
-
!shape.hasVision &&
|
|
340
|
-
!shape.hasAdapters &&
|
|
341
|
-
!shape.wantsLogprobs &&
|
|
342
|
-
!shape.userSeed &&
|
|
343
|
-
// Phase 3.1: a kv-quant request batches when the server's scheme is
|
|
344
|
-
// the batchable kvConfig composition (the scheduler applies it);
|
|
345
|
-
// otherwise it routes serial exactly as before.
|
|
346
|
-
!(shape.kvQuant && !this.#kvBatchable()) &&
|
|
347
|
-
// TurboQuant: solo-only in v1, unconditionally — never partially
|
|
348
|
-
// batchable like kvConfig. The automatic instanceof exclusion in
|
|
349
|
-
// #modelCachesBatchable() already covers this once a cache converts;
|
|
350
|
-
// this is the explicit belt-and-braces refusal at request-shape time
|
|
351
|
-
// (mirrors the kvQuant pattern above — both layers exist on purpose).
|
|
352
|
-
!shape.turboQuant &&
|
|
353
|
-
!shape.hasDraft &&
|
|
354
|
-
// Grammar: B1 makes it batchable (per-row matchers) unless the kill
|
|
355
|
-
// switch forces serial. MLX_BUN_GRAMMAR_BATCH=0 = B0 behavior (serial),
|
|
356
|
-
// the A/B + kill lever for the new code, house style.
|
|
357
|
-
!(shape.hasGrammar && runtimeValue("MLX_BUN_GRAMMAR_BATCH") === "0")
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
place(shape: RequestShape): GenerationPlacement {
|
|
272
|
+
place(shape: RequestShape, options: GenerateOptions = {}): GenerationPlacement {
|
|
362
273
|
const frozenShape = Object.freeze(shape);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
274
|
+
const execution = this.#binding.plan(frozenShape, options, {
|
|
275
|
+
continuous: this.batchingEnabled,
|
|
276
|
+
quantizedBatch: (shape.kvQuant || shape.turboQuant) && this.#kvBatchable(),
|
|
277
|
+
checkpoints: this.opts.checkpoints === true,
|
|
366
278
|
});
|
|
279
|
+
return Object.freeze({ shape: frozenShape, mechanism: execution.mechanism, execution });
|
|
367
280
|
}
|
|
368
281
|
|
|
369
282
|
/** Run `fn` with exclusive ownership of the GPU + shared model state (the
|
|
@@ -372,26 +285,37 @@ export class GenerationGateway {
|
|
|
372
285
|
* mount/unmount all come through here, so nothing runs concurrently with
|
|
373
286
|
* batched decode steps (or with each other). Registers as a serial waiter
|
|
374
287
|
* so the scheduler drains (stops admitting) until `fn` has run. */
|
|
288
|
+
/** Shared by inference, preparation and managed GPU subprocesses. */
|
|
289
|
+
async acquireExecutionLease(
|
|
290
|
+
signal?: AbortSignal,
|
|
291
|
+
trace?: PromptResponseTrace,
|
|
292
|
+
): Promise<import("../contracts/resources").DisposableResource> {
|
|
293
|
+
if (this.#closed) throw new Error("generation gateway is closed");
|
|
294
|
+
this.#serialWaiters++;
|
|
295
|
+
const closeAdmission = trace?.begin("engine.admission_wait", { mechanism: "serial" });
|
|
296
|
+
const resume = () => {
|
|
297
|
+
this.#serialWaiters--;
|
|
298
|
+
if (this.#serialWaiters === 0) this.#scheduler?.kick();
|
|
299
|
+
};
|
|
300
|
+
try {
|
|
301
|
+
const release = await this.#mutex.acquire(signal);
|
|
302
|
+
let released = false;
|
|
303
|
+
return { dispose: () => {
|
|
304
|
+
if (released) return;
|
|
305
|
+
released = true;
|
|
306
|
+
release(); resume();
|
|
307
|
+
} };
|
|
308
|
+
} catch (error) { resume(); throw error; }
|
|
309
|
+
finally { closeAdmission?.(); }
|
|
310
|
+
}
|
|
311
|
+
|
|
375
312
|
async runExclusive<T>(
|
|
376
313
|
fn: () => Promise<T>,
|
|
377
314
|
trace?: PromptResponseTrace,
|
|
315
|
+
signal?: AbortSignal,
|
|
378
316
|
): Promise<T> {
|
|
379
|
-
this
|
|
380
|
-
|
|
381
|
-
mechanism: "serial",
|
|
382
|
-
});
|
|
383
|
-
try {
|
|
384
|
-
const release = await this.#mutex.acquire();
|
|
385
|
-
closeAdmission?.();
|
|
386
|
-
try {
|
|
387
|
-
return await fn();
|
|
388
|
-
} finally {
|
|
389
|
-
release();
|
|
390
|
-
}
|
|
391
|
-
} finally {
|
|
392
|
-
this.#serialWaiters--;
|
|
393
|
-
if (this.#serialWaiters === 0) this.#scheduler?.kick(); // resume admission
|
|
394
|
-
}
|
|
317
|
+
const lease = await this.acquireExecutionLease(signal, trace);
|
|
318
|
+
try { return await fn(); } finally { lease.dispose(); }
|
|
395
319
|
}
|
|
396
320
|
|
|
397
321
|
/** Run one generation on the appropriate lane. onToken is invoked per emitted
|
|
@@ -406,8 +330,26 @@ export class GenerationGateway {
|
|
|
406
330
|
signal?: AbortSignal,
|
|
407
331
|
trace?: PromptResponseTrace,
|
|
408
332
|
): Promise<GenerateStats> {
|
|
333
|
+
let reservation;
|
|
334
|
+
try { reservation = await acquireReservation(this.#requests, signal); }
|
|
335
|
+
catch (error) { cleanupFailure(error, () => disposeUnstartedRequest(options, vision)); }
|
|
336
|
+
try { return await this.#run(promptIds, options, onToken, vision, shape, placement, signal, trace); }
|
|
337
|
+
finally { reservation.dispose(); }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async #run(
|
|
341
|
+
promptIds: number[],
|
|
342
|
+
options: GenerateOptions & { stopSequences?: string[] },
|
|
343
|
+
onToken: OnToken,
|
|
344
|
+
vision: Vision | undefined,
|
|
345
|
+
shape: RequestShape,
|
|
346
|
+
placement: GenerationPlacement,
|
|
347
|
+
signal?: AbortSignal,
|
|
348
|
+
trace?: PromptResponseTrace,
|
|
349
|
+
): Promise<GenerateStats> {
|
|
350
|
+
const disposeUnstarted = () => disposeUnstartedRequest(options, vision);
|
|
409
351
|
if (placement.shape !== shape) {
|
|
410
|
-
|
|
352
|
+
disposeUnstarted();
|
|
411
353
|
throw new Error("generation placement does not belong to this request shape");
|
|
412
354
|
}
|
|
413
355
|
try {
|
|
@@ -415,7 +357,7 @@ export class GenerationGateway {
|
|
|
415
357
|
} catch (e) {
|
|
416
358
|
// The gateway is the first component that accepts ownership of a
|
|
417
359
|
// compiled controller. An already-aborted request reaches neither lane.
|
|
418
|
-
|
|
360
|
+
disposeUnstarted();
|
|
419
361
|
throw e;
|
|
420
362
|
}
|
|
421
363
|
if (placement.mechanism === "serial") {
|
|
@@ -430,40 +372,59 @@ export class GenerationGateway {
|
|
|
430
372
|
let lastHop = performance.now();
|
|
431
373
|
const hop = (): Promise<void> => new Promise<void>((r) => setImmediate(r));
|
|
432
374
|
const hoppingOnToken: OnToken = (token, lp) => {
|
|
433
|
-
|
|
375
|
+
signal?.throwIfAborted();
|
|
434
376
|
const r = onToken(token, lp);
|
|
435
377
|
if (r === false) return false;
|
|
436
378
|
if (performance.now() - lastHop < 25) return r;
|
|
437
379
|
lastHop = performance.now();
|
|
438
380
|
if (r instanceof Promise)
|
|
439
|
-
return r.then((v) =>
|
|
440
|
-
v === false
|
|
441
|
-
|
|
442
|
-
|
|
381
|
+
return r.then(async (v) => {
|
|
382
|
+
if (v === false) return false;
|
|
383
|
+
signal?.throwIfAborted();
|
|
384
|
+
await hop();
|
|
385
|
+
signal?.throwIfAborted();
|
|
386
|
+
return v;
|
|
387
|
+
});
|
|
388
|
+
return hop().then(() => {
|
|
389
|
+
signal?.throwIfAborted();
|
|
390
|
+
return r;
|
|
391
|
+
});
|
|
443
392
|
};
|
|
393
|
+
let started = false;
|
|
444
394
|
return this.runExclusive(async () => {
|
|
445
395
|
signal?.throwIfAborted();
|
|
396
|
+
started = true;
|
|
446
397
|
const stats = await this.serialRun(
|
|
447
|
-
promptIds, options, hoppingOnToken, vision, trace,
|
|
398
|
+
promptIds, signal ? { ...options, signal } : options, hoppingOnToken, vision, trace, placement.execution,
|
|
448
399
|
);
|
|
449
400
|
signal?.throwIfAborted();
|
|
450
401
|
return stats;
|
|
451
|
-
}, trace)
|
|
402
|
+
}, trace, signal)
|
|
452
403
|
// Covers a serial waiter aborted before serialRun takes ownership.
|
|
453
404
|
// generate() also disposes defensively; the operation is idempotent.
|
|
454
|
-
.finally(() =>
|
|
405
|
+
.finally(() => {
|
|
406
|
+
if (started) options.grammar?.dispose();
|
|
407
|
+
else disposeUnstarted();
|
|
408
|
+
});
|
|
455
409
|
}
|
|
456
410
|
|
|
411
|
+
const adapters = options.adapters?.length ? [...options.adapters] : undefined;
|
|
412
|
+
const adapterKey = adapters ? JSON.stringify(adapters) : "";
|
|
413
|
+
const cacheNamespace = adapters
|
|
414
|
+
? () => this.opts.adapterNamespace?.(adapters) ?? adapterKey : "";
|
|
415
|
+
const context = adapters
|
|
416
|
+
? this.#binding.bindAdapterContext?.(adapters, `adapters:${adapterKey}`) : undefined;
|
|
417
|
+
|
|
457
418
|
// The scheduler owns grammar ready/accept sequencing. StepSampler owns the
|
|
458
419
|
// shared processors -> mask -> logprobs -> sample -> history contract.
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
initialHistory: promptIds,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
420
|
+
const method = this.#binding.methodRequest?.(placement.execution, options);
|
|
421
|
+
const continuation = method ? undefined : this.#binding.continuationRequest?.(placement.execution, options, promptIds, onToken);
|
|
422
|
+
const sampling = method ? undefined : continuation ?? createRowSampling(makeStepSampler(options, {
|
|
423
|
+
tokenRepresentation: "device", grammarWait: "external",
|
|
424
|
+
historyUpdate: "after-sample", initialHistory: promptIds,
|
|
425
|
+
captureSelectedLogprob: options.logprobs === true,
|
|
426
|
+
captureTopLogprobs: options.topLogprobs,
|
|
427
|
+
}), onToken);
|
|
467
428
|
|
|
468
429
|
let st;
|
|
469
430
|
this.#rowsSubmitted++;
|
|
@@ -472,12 +433,14 @@ export class GenerationGateway {
|
|
|
472
433
|
});
|
|
473
434
|
try {
|
|
474
435
|
st = await this.#ensureScheduler().submit({
|
|
475
|
-
promptIds,
|
|
436
|
+
promptIds, context, cacheNamespace, prefillChunkSize: options.prefillChunkSize,
|
|
437
|
+
continuation: continuation?.continuation,
|
|
438
|
+
statePolicy: this.#binding.statePolicy?.(placement.execution, options, promptIds.length + (options.maxTokens ?? 512)),
|
|
439
|
+
compiledDecode: placement.execution?.compiledDecode,
|
|
476
440
|
maxTokens: options.maxTokens ?? 512,
|
|
477
|
-
eosTokenIds: options.eosTokenIds ?? this.
|
|
478
|
-
sample,
|
|
479
|
-
|
|
480
|
-
onToken,
|
|
441
|
+
eosTokenIds: options.eosTokenIds ?? this.#binding.config.eosTokenIds,
|
|
442
|
+
...(method ? { method, onToken } : { sample: sampling!.sample,
|
|
443
|
+
plainGreedy: sampling!.plainGreedy, onToken: sampling!.onToken }),
|
|
481
444
|
onAdmitted: closeAdmission,
|
|
482
445
|
trace,
|
|
483
446
|
...(signal ? { signal } : {}),
|
|
@@ -491,7 +454,7 @@ export class GenerationGateway {
|
|
|
491
454
|
});
|
|
492
455
|
} finally {
|
|
493
456
|
closeAdmission?.();
|
|
494
|
-
|
|
457
|
+
sampling?.dispose();
|
|
495
458
|
// The scheduler never owns per-row grammar state.
|
|
496
459
|
options.grammar?.dispose();
|
|
497
460
|
}
|
|
@@ -500,15 +463,22 @@ export class GenerationGateway {
|
|
|
500
463
|
promptTokens: st.promptTokens,
|
|
501
464
|
cachedTokens: st.cachedTokens,
|
|
502
465
|
generatedTokens: st.generatedTokens,
|
|
503
|
-
|
|
466
|
+
finishReason: st.finishReason,
|
|
467
|
+
prefillMs: st.prefillMs,
|
|
468
|
+
decodeMs: st.decodeMs,
|
|
469
|
+
prefillTps: st.prefillMs > 0 ? ((st.promptTokens - st.cachedTokens) / st.prefillMs) * 1000 : 0,
|
|
470
|
+
decodeTps: st.decodeMs > 0 && st.generatedTokens > 1 ? ((st.generatedTokens - 1) / st.decodeMs) * 1000 : 0,
|
|
504
471
|
cacheTokens: [],
|
|
472
|
+
...(st.spec ? { spec: st.spec } : {}),
|
|
505
473
|
};
|
|
506
474
|
}
|
|
507
475
|
|
|
508
|
-
#ensureScheduler():
|
|
476
|
+
#ensureScheduler(): MlxBatchGroup {
|
|
509
477
|
if (!this.#scheduler)
|
|
510
|
-
this.#scheduler =
|
|
478
|
+
this.#scheduler = this.#binding.createBatchGroup({
|
|
479
|
+
runtime: this.#runtime,
|
|
511
480
|
maxBatch: this.#batch,
|
|
481
|
+
stateCodecs: this.opts.stateCodecs,
|
|
512
482
|
kvBudgetBytes: this.opts.kvBudgetBytes,
|
|
513
483
|
// Phase 3.1: the batchable kvConfig composition is applied by the
|
|
514
484
|
// scheduler (solo rows convert at serial chunk boundaries, then
|