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,567 +0,0 @@
|
|
|
1
|
-
# Parallel slots — configurable batched serving
|
|
2
|
-
|
|
3
|
-
Status: **S0 + S1b + S2 SHIPPED** (S2's continuous inject/evict shipped
|
|
4
|
-
with the scheduler; its byte-budget admission half landed 2026-07-03 as
|
|
5
|
-
`--kv-budget`; S3+ deferred)
|
|
6
|
-
Owner: serving layer
|
|
7
|
-
Default: **off** (`--batch 1` = today's serialized path, untouched)
|
|
8
|
-
|
|
9
|
-
> **Flag rename note:** This document was drafted with `--slots N` as the design name.
|
|
10
|
-
> The shipped flag is `--batch N` (`--decode-concurrency` accepted as an mlx_lm.server alias).
|
|
11
|
-
> Occurrences of `--slots` in pre-ship text below refer to today's `--batch N`.
|
|
12
|
-
|
|
13
|
-
## Goal
|
|
14
|
-
|
|
15
|
-
Let the server process more than one generation request concurrently,
|
|
16
|
-
gated entirely behind a `--batch N` flag (originally designed as `--slots N`). `N=1` runs exactly what we
|
|
17
|
-
have today (the serialized promise chain). `N>1` loads a batch
|
|
18
|
-
scheduler that runs multiple sequences through one forward pass.
|
|
19
|
-
|
|
20
|
-
This is a server-path feature — serving speed is the user metric.
|
|
21
|
-
Direct/embedding mode stays batch=1.
|
|
22
|
-
|
|
23
|
-
> **v2 execution plan:** this document remains the PRIMITIVE-design record
|
|
24
|
-
> (masks, caches, lanes). The living gap/debt plan and sequencing for the
|
|
25
|
-
> batching engine is [batching-v2-plan.md](batching-v2-plan.md). Landed from
|
|
26
|
-
> it (2026-07-01, steps 1–3): cache-capability gate (hybrid/SSM models route
|
|
27
|
-
> serial), slice-before-head admit, ONE lock domain (curve `/generate`,
|
|
28
|
-
> `/signal`, adapter mount/unmount via `gateway.runExclusive`), per-row
|
|
29
|
-
> onToken failure containment, drain-on-serial-waiter (no starvation), and
|
|
30
|
-
> the engine-hygiene transplant (pipelined decode via asyncEval, clearCache
|
|
31
|
-
> 1/256 steps, chunked admit prefill interleaved with decode steps).
|
|
32
|
-
> Landed since: **SSM batched path** (Qwen3.5 batches, token-exact vs
|
|
33
|
-
> mlx-lm B=2 — 2026-07-02, batching-perf-path.md P5); **per-row logits
|
|
34
|
-
> processors** (repetition/presence/frequency penalties + logit_bias +
|
|
35
|
-
> min_p/XTC batch — 2026-07-02, same doc); **grammar under batching** (B1
|
|
36
|
-
> per-row matchers — structured-output.md); **KV-budget admission**
|
|
37
|
-
> (`--kv-budget`, queue-don't-OOM, `/stats.batch` fields — 2026-07-03,
|
|
38
|
-
> grammar-spec-batching-integration.md Phase D); **Tier-0 universal
|
|
39
|
-
> coverage** (plain full-attention archs, e.g. Llama, batch via per-row
|
|
40
|
-
> RoPE, token-exact vs mlx-lm B=2 — 2026-07-03, same doc's execution log;
|
|
41
|
-
> maskArray + sliding universal archs stay serial).
|
|
42
|
-
|
|
43
|
-
## STATUS (2026-06-14) — LIVE for full-attention models
|
|
44
|
-
|
|
45
|
-
**`--batch N` now serves B>1** for full-attention models (CPM). The numerics
|
|
46
|
-
were proven first (the batched **forward primitive** `BatchedDecodeMaskCache` +
|
|
47
|
-
per-layer fixes = bit-parity with mlx-lm B=N across all 4 models; static-B AND
|
|
48
|
-
dynamic-B `mergeKVRows`/`filterKVRows` vs mlx-lm `BatchKVCache.merge`/`.extract`/
|
|
49
|
-
`.filter`, CPM L1), then the **scheduler** was built and wired in:
|
|
50
|
-
- `BatchScheduler` (`src/serve/batch-scheduler.ts`) — the detached async driver:
|
|
51
|
-
admission queue → running batch → continuous inject (`mergeKVRows`) / evict
|
|
52
|
-
(`filterKVRows`) → per-row sample + accounting. Gated teacher-forced (KL).
|
|
53
|
-
- `GenerationGateway` (`src/serve/generation-gateway.ts`) — lane picker +
|
|
54
|
-
`AsyncMutex` (serial↔batched GPU/`loraState` exclusivity) + the `_is_batchable`
|
|
55
|
-
predicate. Wired into both `handleChat` call sites → per-row SSE fan-out.
|
|
56
|
-
- End-to-end: `tests/batch-serving.test.ts` (live CPM `--batch 2` server).
|
|
57
|
-
|
|
58
|
-
**Sliding-window (Gemma) dynamic-B is now served too** (2026-06-14):
|
|
59
|
-
`BatchedRotatingCache` (`src/model/batched-rotating.ts`, port of mlx-lm
|
|
60
|
-
`BatchRotatingKVCache` incl. the ring-wrap rolled mask) is bit-exact vs mlx-lm
|
|
61
|
-
model-free (`tests/batched-rotating.test.ts`); the scheduler assembles each
|
|
62
|
-
layer's cache by type and Gemma 12B scheduled greedy == the mlx-lm B=2 golden
|
|
63
|
-
with eviction. So `--batch N` batches BOTH CPM and Gemma.
|
|
64
|
-
|
|
65
|
-
**This meets the mlx-lm-parity bar.** mlx-lm's batched path is bf16 (its
|
|
66
|
-
quantized batching is NYI — `BatchRotatingKVCache.to_quantized` raises), so bf16
|
|
67
|
-
continuous batching IS the drop-in. `--batch N` is therefore a bf16 MODE: KV
|
|
68
|
-
quant unset → bf16 (so the batch path engages out of the box, Option B); serial
|
|
69
|
-
default stays mixed-precision (optiq parity); an explicit `--kv-quant` under
|
|
70
|
-
`--batch N` routes those requests to serial (warned).
|
|
71
|
-
|
|
72
|
-
**Batched + mixed-precision KV quant is NOT a parity gap** — no ancestor does it
|
|
73
|
-
(mlx-lm NYI, optiq no batching), so there's no bit-exact oracle. It's an OPTIONAL
|
|
74
|
-
novel extension: a memory-density win (batching × 4-bit KV compound), gated by
|
|
75
|
-
KL + 6-task quality (per the tree framing), deferred. Other follow-ups (all
|
|
76
|
-
optional polish): the `extend` join op (today joins re-merge), KV-budget
|
|
77
|
-
admission (**LANDED 2026-07-03** — `--kv-budget`), prompt-cache reuse under
|
|
78
|
-
batching, and **paged KV** (zero-padding-waste allocation).
|
|
79
|
-
|
|
80
|
-
## Why it helps (and when it doesn't)
|
|
81
|
-
|
|
82
|
-
The win is **throughput via memory bandwidth**. Decode on Apple Silicon
|
|
83
|
-
is bandwidth-bound (~273 GB/s on the M4 Pro): every token streams the
|
|
84
|
-
entire weight set through the GPU to produce one token. Batch B
|
|
85
|
-
sequences and you stream the weights **once** to produce **B** tokens.
|
|
86
|
-
Aggregate tok/s scales ~linearly until the compute roofline.
|
|
87
|
-
|
|
88
|
-
The crucial design point — **continuous batching, not static
|
|
89
|
-
batching**:
|
|
90
|
-
|
|
91
|
-
- *Static batching*: form a batch, run to completion, then accept the
|
|
92
|
-
next. A request arriving mid-flight waits for the whole batch to
|
|
93
|
-
finish. This is the wrong design; do not build it.
|
|
94
|
-
- *Continuous batching* (iteration-level scheduling): the scheduler
|
|
95
|
-
works per **decode step**. After each token it can inject a
|
|
96
|
-
newly-arrived request (after a short prefill) and retire finished
|
|
97
|
-
ones. A late request does **not** wait for the in-flight one to
|
|
98
|
-
finish — it joins at the next step.
|
|
99
|
-
|
|
100
|
-
So the benefit window is "any time two requests are alive in the same
|
|
101
|
-
wall-clock window" — not "a backlog has formed." Because generations
|
|
102
|
-
last seconds, requests overlap even at modest rates (Little's law:
|
|
103
|
-
concurrent ≈ arrival_rate × mean_generation_time).
|
|
104
|
-
|
|
105
|
-
When it does **not** help: traffic so light that requests never overlap
|
|
106
|
-
in time. Then there's only ever one live sequence and batching is idle
|
|
107
|
-
— which is exactly why `--batch 1` is and stays the default.
|
|
108
|
-
|
|
109
|
-
## Current code: what's batch=1 and what isn't
|
|
110
|
-
|
|
111
|
-
Good news: the hot path is already written over a batch dim `B`.
|
|
112
|
-
|
|
113
|
-
- `Attention.forward` reads `const [B, L] = x.shape` and threads `B`
|
|
114
|
-
through every reshape/transpose/SDPA — `src/model/gemma4.ts:152`.
|
|
115
|
-
- `KVCache` stores `[B, H, S, D]` and grows/slices generically —
|
|
116
|
-
`src/model/gemma4-base.ts:204` (STEP=256 growth).
|
|
117
|
-
- The byte-capped LRU prompt cache already accounts bytes, not count —
|
|
118
|
-
`src/prompt-cache.ts` (the precedent for a byte-budgeted KV pool).
|
|
119
|
-
|
|
120
|
-
`B=1` is baked into a finite, findable set of places:
|
|
121
|
-
|
|
122
|
-
| Location | Hardcoded |
|
|
123
|
-
| --- | --- |
|
|
124
|
-
| `src/generate.ts:295,315,378` | prompt/decode tensors built `[1, L]` / `[1, 1]` |
|
|
125
|
-
| `src/generate.ts:320,235` | `hLast` slice `[1, …]`; `sampleStep` reshape `[1, V]` |
|
|
126
|
-
| `src/generate.ts:247` | sampler history `[1]`, one sampled token/step |
|
|
127
|
-
| `src/model/gemma4.ts:647-653` | per-layer-input reshapes `[1, L, …]` *(pre-S1b inventory — this entry was fixed in S1b; e4b per-layer-input is now B-generic)* |
|
|
128
|
-
| `src/model/gemma4-base.ts:210` | **one scalar `offset` per cache, shared across rows** |
|
|
129
|
-
| `src/model/gemma4.ts:174,206` | **scalar RoPE offset** for the whole batch |
|
|
130
|
-
| `src/model/gemma4-base.ts:256` | `makeMask` from that single offset (returns empty mask at N=1) |
|
|
131
|
-
|
|
132
|
-
## The one genuinely hard problem: per-sequence position
|
|
133
|
-
|
|
134
|
-
Batched sequences are different lengths and at different decode
|
|
135
|
-
positions, but the cache has a single `offset`, RoPE takes a scalar
|
|
136
|
-
offset, and the mask is built from one offset.
|
|
137
|
-
|
|
138
|
-
Solution (mlx-lm's, and ours): **left-padding + per-row mask + per-row
|
|
139
|
-
RoPE offsets.**
|
|
140
|
-
|
|
141
|
-
- Right-align every sequence so "the current token" is the same column
|
|
142
|
-
for all rows → one global advancing offset still works.
|
|
143
|
-
- Pad on the left; mask the padding with a real `[B, 1, 1, S]` boolean
|
|
144
|
-
mask — **even at the N=1 decode step**, which today returns an empty
|
|
145
|
-
mask (`gemma4-base.ts:257`). That shortcut is the first thing to change.
|
|
146
|
-
- RoPE positions become a per-row vector. **We already have the
|
|
147
|
-
primitive**: `ops.ropeDynamic` + the `cache.ropeOffsetArr` array-offset
|
|
148
|
-
path (`gemma4.ts:179,206`), currently used only by compiled decode.
|
|
149
|
-
|
|
150
|
-
Highest-risk sub-item: Gemma3 alternates sliding-window
|
|
151
|
-
(`RotatingKVCache`) and full-attention layers. Per-row offsets through
|
|
152
|
-
the ring cache's wrap semantics is the trickiest mask math in the
|
|
153
|
-
project. Validate the full-attention mask path first; bring sliding
|
|
154
|
-
layers online separately under teacher-forced parity.
|
|
155
|
-
|
|
156
|
-
### Reuse: batched PREFILL already exists (big de-risk, found 2026-06-14)
|
|
157
|
-
|
|
158
|
-
The training path already solves batched padded forward and proves it:
|
|
159
|
-
- `src/train/forward.ts` — `buildBatchedPadMask(B, L, validLengths, window)`
|
|
160
|
-
builds the `[B,1,L,L]` mask (causal AND per-row key-validity, per distinct
|
|
161
|
-
window) and `BatchedMaskCache` routes it through the stock attention by
|
|
162
|
-
wrapping a real cache and overriding only `makeMask`. Comment confirms
|
|
163
|
-
"KVCache/RotatingKVCache are already shape-generic over B."
|
|
164
|
-
- `tests/train-batch-e2e.test.ts` proves a B=2 padded forward's per-row loss
|
|
165
|
-
equals two independent B=1 forwards within bf16 tolerance — i.e. the mask
|
|
166
|
-
makes a real row's logits independent of the other (padded) row. That is
|
|
167
|
-
exactly the teacher-forced gate S1 needs, already passing.
|
|
168
|
-
- `tests/train-batch.test.ts` unit-tests the mask (fast, no model).
|
|
169
|
-
|
|
170
|
-
So batched **prefill** is ~done and reusable (offset-0, right-padded). The
|
|
171
|
-
genuinely new work is batched **decode**: a growing per-row KV with per-row
|
|
172
|
-
offsets, per-row RoPE (array-offset path), and a per-row N=1 key-validity
|
|
173
|
-
mask. First decode brick landed 2026-06-14: `src/model/batched-mask.ts`
|
|
174
|
-
`buildBatchedDecodeMask` (left-padded, nonzero-offset counterpart) +
|
|
175
|
-
`tests/batched-decode-mask.test.ts` (fast). The two mask builders consolidate
|
|
176
|
-
when prefill is wired into serving (S1a).
|
|
177
|
-
|
|
178
|
-
### S1b decode: design traps & sequencing (found 2026-06-14)
|
|
179
|
-
|
|
180
|
-
Reading the attention path surfaced two real subtleties — the kind that
|
|
181
|
-
produce plausible-but-wrong logits, so flagged before writing the cache:
|
|
182
|
-
|
|
183
|
-
1. **Prefill/decode padding conventions differ.** Training's batched prefill
|
|
184
|
-
RIGHT-pads (real tokens left-aligned, positions 0..len-1 → scalar RoPE
|
|
185
|
-
offset 0 is correct). Batched decode wants LEFT-padding (rows right-aligned
|
|
186
|
-
so every row's next write lands in the same column → one advancing offset).
|
|
187
|
-
Left-padding then forces **per-row RoPE** (real position = column −
|
|
188
|
-
leftPad[b]) via the array-offset path, and means the prefill KV must be
|
|
189
|
-
re-laid-out (right-aligned) before decode. First cut: prefill each request
|
|
190
|
-
solo, copy its KV into the right-aligned row of a shared `[B,H,Smax,D]`
|
|
191
|
-
decode buffer, then batch the decode.
|
|
192
|
-
2. **RoPE offset-timing trap (FIXED 2026-06-14).** `Attention.forward` read
|
|
193
|
-
`cache.ropeOffsetArr` twice — for K *before* `updateAndFetch` and for Q
|
|
194
|
-
*after* it (gemma4.ts ~179 vs ~195/201). Since `updateAndFetch` advances
|
|
195
|
-
`offset`, a per-row position array derived from the live offset would give
|
|
196
|
-
K and Q different positions. Fixed by capturing `offsetArr` once alongside
|
|
197
|
-
`offset` (line ~174). Behavior-preserving (real caches leave it unset;
|
|
198
|
-
compiled-decode passes a constant) — verified bit-exact against
|
|
199
|
-
`tests/compiled-decode.test.ts` (12B, 7/7).
|
|
200
|
-
|
|
201
|
-
**Sequencing (parity gate first, on the riskiest piece):**
|
|
202
|
-
- **S1b.1** — gated teacher-forced *decode* parity harness: manually assemble a
|
|
203
|
-
left-padded 2-row KV from two solo prefills, run K batched decode steps with
|
|
204
|
-
a `BatchedDecodeMaskCache` (delegates KV, returns `buildBatchedDecodeMask`,
|
|
205
|
-
exposes per-row `ropeOffsetArr` pinned to the pre-write offset), and assert
|
|
206
|
-
each row's logits match its solo greedy decode within bf16 tolerance. Gated
|
|
207
|
-
behind an opt-in env + local weights (like `train-batch-e2e`). Isolates
|
|
208
|
-
decode-batching numerics from the prefill-assembly plumbing.
|
|
209
|
-
**DONE 2026-06-14** — `BatchedDecodeMaskCache` (src/model/batched-mask.ts) +
|
|
210
|
-
`tests/batched-decode-parity.test.ts` (`MLX_BUN_TEST_BATCH_DECODE=1`,
|
|
211
|
-
MiniCPM5-1B). Passes: the unpadded row is **bit-exact** vs its solo decode
|
|
212
|
-
(batching isolates rows); the left-padded row matches within bounded bf16
|
|
213
|
-
reduction-order noise (≤0.23, stable over 8 steps — the shared buffer shifts
|
|
214
|
-
its keys by leftPad columns, so the attention sum accumulates in a different
|
|
215
|
-
order). **The gate earned its keep**: it caught `LlamaAttention`
|
|
216
|
-
(src/model/minicpm5.ts) roping Q/K with the scalar `cache.offset` and ignoring
|
|
217
|
-
`ropeOffsetArr` → left-padded rows mis-positioned (logit diff 8.7). Fixed to
|
|
218
|
-
use the dynamic array-offset path when present; MiniCPM5 solo parity stays
|
|
219
|
-
bit-exact vs the oracle. (Gemma4's `Attention` already had the array path; its
|
|
220
|
-
capture-once fix is verified bit-exact vs compiled-decode.) Lesson: each model
|
|
221
|
-
family's attention needs the per-row offset path wired separately.
|
|
222
|
-
- **S1b.2** — production KV assembly (solo prefill → left-aligned decode buffer).
|
|
223
|
-
- **S1b.3** — scheduler + B-token/step generate loop + per-row stream fan-out.
|
|
224
|
-
|
|
225
|
-
### The validation matrix — 3 parity layers × every model path
|
|
226
|
-
|
|
227
|
-
**Current state (2026-06-14).** Roster: **CPM (MiniCPM5-1B) + 3 Gemmas
|
|
228
|
-
(e4b / 12B / 26B)**. Every NEW model family (e.g. **Qwen**, planned) adds its
|
|
229
|
-
OWN handler path and its own copy of the three layers below — nothing here is
|
|
230
|
-
generic. This matrix is a snapshot; re-derive it when the roster changes.
|
|
231
|
-
|
|
232
|
-
**Framing (refined 2026-06-14 — it's a TREE, not strict layers).** mlx-lm is the
|
|
233
|
-
base library; **mlx-optiq and mlx-bun are both optimizations on top of it**
|
|
234
|
-
(optiq = mixed-precision per-layer KV quant, "drop-in for mlx_lm.server", no
|
|
235
|
-
batching; mlx-bun = per-model fused kernels + batched serving). Bit-parity with
|
|
236
|
-
an ancestor is just an **easy correctness test where we overlap one** — not a
|
|
237
|
-
porting goal. The "L1/L2" labels below are **parity anchors / correctness
|
|
238
|
-
checkpoints**, not a strict stack: L1 = "matches mlx-lm (bf16)", L2 = "matches
|
|
239
|
-
optiq (mixed-precision quant)"; beyond them is the no-oracle **Lab** class ("our
|
|
240
|
-
own optimizations, no ancestor does them → no oracle" — formerly labeled "L3";
|
|
241
|
-
the `--l3` product tier was deleted 2026-07-05,
|
|
242
|
-
docs/design/unified-engine-frontier-plan.md §6). A combo no ancestor does (e.g. **batched mixed-precision
|
|
243
|
-
serving** — mlx-lm can't do mixed-precision, optiq can't batch) is genuinely new
|
|
244
|
-
territory: gate it by KL + the 6-task quality + benchmarks, never bit-exactness.
|
|
245
|
-
The anchors are still useful — we can always dial back to a bit-exact-vs-ancestor
|
|
246
|
-
config as a correctness checkpoint and build outward.
|
|
247
|
-
|
|
248
|
-
- **L1 — mlx-lm parity (standard KV).** bf16 KV, **bit-exact vs mlx-lm**. The
|
|
249
|
-
foundation oracle (and, since 2026-07-05, the naked default). Path: monolith
|
|
250
|
-
`Gemma4Model` / `MiniCPM5Model`, `ops.sdpa`.
|
|
251
|
-
- **L2 — mlx-optiq parity (mixed-precision KV quant).** quant/mixed KV,
|
|
252
|
-
**bit-exact vs optiq**. Path: `QuantizedKVCache` + `quantizedSdpa` (fused-sdpa
|
|
253
|
-
follows `--kv-quant`: on for `config` — the optiq golden composition; off →
|
|
254
|
-
*unfused* for uniform/bf16).
|
|
255
|
-
- **L3 — DELETED as a product mode 2026-07-05**
|
|
256
|
-
(docs/design/unified-engine-frontier-plan.md §6): no output-changing kernel
|
|
257
|
-
(perf-kernel, fused-decode, fused-gelu) beat the L1 baseline in a paired A/B,
|
|
258
|
-
so the kernels were deleted and `--l3` hard-errors. Its no-oracle territory is
|
|
259
|
-
now the **Lab**: output-changing experiments are env-flag items with a bench +
|
|
260
|
-
expiry, gated by **KL vs the compatible upstream + the 6 eval-task mean
|
|
261
|
-
(`src/eval/tasks/`) + perf numbers** — never bit-exactness — until one earns
|
|
262
|
-
promotion.
|
|
263
|
-
|
|
264
|
-
Degradation lever: bf16 KV drops L2→L1. The surviving kill switches
|
|
265
|
-
(`--compiled-decode`, `--compiled-activations`, `--fused-sdpa`) are per-kernel
|
|
266
|
-
escapes. Each layer must stand alone and stay reachable (don't-delete-
|
|
267
|
-
optionality as a fallback ladder).
|
|
268
|
-
|
|
269
|
-
So a feature like batched decode (`--batch`) is "done" only when green at
|
|
270
|
-
**every (model × layer)** cell — and each cell may need its own fix:
|
|
271
|
-
|
|
272
|
-
| Model · path | L1 (bf16 / mlx-lm) | L2 (quant / optiq) | ~~L3~~ (tier deleted 2026-07-05) |
|
|
273
|
-
| --- | --- | --- | --- |
|
|
274
|
-
| CPM (MiniCPM5) | ✅ **oracle-verified** (= mlx-lm B=2) | ☐ quant + unfused-mask | — |
|
|
275
|
-
| Gemma 12B | ✅ **oracle-verified** (= mlx-lm B=2, short-ctx) | ☐ generated + sliding-window | — |
|
|
276
|
-
| Gemma e4b | ✅ **oracle-verified** (= mlx-lm B=2, short-ctx) | ☐ generated + same | — |
|
|
277
|
-
| Gemma 26B | ✅ **oracle-verified** (= mlx-lm B=2, short-ctx) | ☐ generated + MoE | — |
|
|
278
|
-
| *(Qwen, future)* | *new path* | *new path* | — |
|
|
279
|
-
|
|
280
|
-
**🎯 L1 batched decode COMPLETE (2026-06-14d): the entire L1 row is green** —
|
|
281
|
-
all 4 model paths bit-parity with mlx-lm B=2 (CPM full-attn, Gemma 12B
|
|
282
|
-
sliding+full, e4b per-layer-input+KV-share, 26B MoE). Only one per-path fix was
|
|
283
|
-
needed (e4b's `[1,L,…]` per-layer-input hardcode); sliding (RotatingKVCache),
|
|
284
|
-
KV-sharing, and MoE were all already B-generic. Caveat: short-context (pre-wrap)
|
|
285
|
-
— ring-wrap (>window) is the one remaining L1 follow-up. Next row: **L2**
|
|
286
|
-
(quantized KV → `quantizedSdpaUnfused` + 4-D mask, vs optiq). (The former
|
|
287
|
-
"L3: perf kernels under batching" row is void — those kernels were deleted
|
|
288
|
-
2026-07-05, docs/design/unified-engine-frontier-plan.md §6.)
|
|
289
|
-
|
|
290
|
-
Harness: `tests/batched-decode-parity.test.ts` has two paths — (a)
|
|
291
|
-
`runBatchedDecodeParity` = the internal-consistency KL check (B=N vs our B=1,
|
|
292
|
-
NOT the gate); (b) **`realBatchedGreedy` = the REAL gate**: left-pad → one
|
|
293
|
-
`BatchedDecodeMaskCache` (handles prefill at offset 0 AND per-step decode) →
|
|
294
|
-
batch-prefill → greedy batch-decode, trajectory compared bit-for-bit to the
|
|
295
|
-
mlx-lm B=N oracle golden (`goldens/batched-golden-*.json`, machine-layered via
|
|
296
|
-
`tests/goldens.ts` — per-GPU overrides in `goldens/<machine>/` — generated by
|
|
297
|
-
`scripts/gen-batched-golden.py` in the oracle venv).
|
|
298
|
-
|
|
299
|
-
**CPM L1 oracle-verified 2026-06-14d:** mlx-bun's real batched greedy trajectory
|
|
300
|
-
== mlx-lm B=2 exactly (both rows incl. the left-padded one, 8 steps). Confirms
|
|
301
|
-
`BatchedDecodeMaskCache` == mlx-lm `BatchKVCache` (left-pad + per-row offset RoPE
|
|
302
|
-
+ `j>=leftPad` mask + fused bool sdpa), and that the wrapper handles batched
|
|
303
|
-
PREFILL too (offset 0 → left-pad prefill mask + `ropeOffsetArr = -leftPad`). The
|
|
304
|
-
earlier "KL 0.26" was measured vs the wrong oracle (our own B=1).
|
|
305
|
-
|
|
306
|
-
**Gate philosophy (revised twice, 2026-06-14):** batched decode is NOT bit-exact
|
|
307
|
-
vs single-stream (batching changes the attention kernel's reduction order). The
|
|
308
|
-
RIGHT oracle is **mlx-lm's OWN batch mode at the same B** — mlx-lm B=N ≡ mlx-bun
|
|
309
|
-
B=N, **bit-exact**. Comparing our B=N to our own B=1 (the KL harness) measures
|
|
310
|
-
the wrong thing and can't distinguish a real bug from inherent batched-kernel
|
|
311
|
-
behavior. mlx-lm's batch path (oracle venv, confirmed 2026-06-14):
|
|
312
|
-
`BatchKVCache`/`BatchRotatingKVCache(left_padding)` — left-padded, a **per-row
|
|
313
|
-
`offset` array** (`-left_padding`, `+=1`/step → real per-row position, feeding
|
|
314
|
-
RoPE), mask `j >= left_padding[b]` (`create_causal_mask(left_padding=…)`), and
|
|
315
|
-
for bf16 the **same fused bool-mask `mx.fast.scaled_dot_product_attention`** we
|
|
316
|
-
use. So our approach matches in principle and the **additive-mask idea is wrong**
|
|
317
|
-
(would deviate). Oracle generator: `scripts/gen-batched-golden.py` (drives
|
|
318
|
-
mlx-lm's real batch-prefill + greedy batch-decode, dumps per-row trajectories +
|
|
319
|
-
last-position logits). CPM B=2 oracle captured 2026-06-14.
|
|
320
|
-
|
|
321
|
-
**Remaining gap:** the KL harness solo-prefills each row then *assembles* a
|
|
322
|
-
batched cache, and compares to *solo* decode. mlx-lm batch-PREFILLS (B=N) — so
|
|
323
|
-
even prefill KV differs (B=1 vs B=N kernel). To gate faithfully we need a real
|
|
324
|
-
mlx-bun **batched prefill + batched decode** path, compared bit-exact to the
|
|
325
|
-
mlx-lm B=N golden. (The KL B2-vs-B1 harness stays as a cheap internal-consistency
|
|
326
|
-
check, not the gate.)
|
|
327
|
-
|
|
328
|
-
**Gemma 12B L1 — RESOLVED 2026-06-14d: it was the wrong oracle, not a bug.**
|
|
329
|
-
The real `realBatchedGreedy` path (batch-prefill + decode) matches the mlx-lm
|
|
330
|
-
B=2 oracle **exactly** for Gemma 12B (both rows incl. left-padded, sliding
|
|
331
|
-
layers via RotatingKVCache→BatchRotatingKVCache, short-context). So the "padded
|
|
332
|
-
row KL ~0.26" was entirely an artifact of the KL harness comparing our B=2 to
|
|
333
|
-
our own B=1 (different prefill + reduction order) — mlx-lm B=2 diverges from its
|
|
334
|
-
own B=1 the same way. No additive-mask fix was needed; the bool+fused path is
|
|
335
|
-
correct (it's what mlx-lm uses). The KL B2-vs-B1 harness stays only as an
|
|
336
|
-
internal-consistency check, never the gate.
|
|
337
|
-
|
|
338
|
-
**Caveat:** verified at short context (Lmax ≪ 1024 window → pre-wrap, sliding
|
|
339
|
-
window inactive). **Ring-wrap (context > window) is a separate validation** —
|
|
340
|
-
needs a long-context golden (mlx-lm BatchRotatingKVCache wrapped) + mlx-bun
|
|
341
|
-
RotatingKVCache wrapped past the window.
|
|
342
|
-
|
|
343
|
-
Batched-decode landmines found by the modes analysis (2026-06-14):
|
|
344
|
-
- **e4b**: `computePerLayerInputs` hardcodes `[1,L,…]` (gemma4.ts:647) → B>1
|
|
345
|
-
breaks; plus KV-sharing (sharer `SharedKv`).
|
|
346
|
-
- **Gemmas**: `RotatingKVCache` sliding-window ring — per-row decode mask + ring
|
|
347
|
-
write (the hard one). 26B adds MoE (`GatherQMM`/`SwitchGLU`) batched routing.
|
|
348
|
-
- **Generated handlers (all 3 Gemmas)** repeat the K/Q rope timing trap —
|
|
349
|
-
they read `cache.ropeOffsetArr` twice around `updateAndFetch`
|
|
350
|
-
(generated/gemma4-12b.ts:63 & :74). The fix goes in the GENERATOR
|
|
351
|
-
`scripts/gen-model.ts`, then regenerate (the files are DO-NOT-EDIT).
|
|
352
|
-
- **Quant + batched**: quant batched decode goes through `quantizedSdpaUnfused`
|
|
353
|
-
— correct. (The old "perf kernel bypassed under batching" debt is resolved by
|
|
354
|
-
deletion: the perf/fused-decode kernels were deleted 2026-07-05,
|
|
355
|
-
docs/design/unified-engine-frontier-plan.md §6 — no batched-aware fused decode
|
|
356
|
-
kernel is planned.) `quantizedSdpaUnfused` must be validated with a 4-D bool mask.
|
|
357
|
-
- **All cells**: compiled-decode forced off under `--batch>1`; LoRA batches only
|
|
358
|
-
within one adapter group (else drain).
|
|
359
|
-
|
|
360
|
-
## KV memory: dynamic allocation, not static partition
|
|
361
|
-
|
|
362
|
-
The naive model — reserve `budget / slots` per row, fixed — is rejected:
|
|
363
|
-
it wastes memory and imposes an arbitrary per-slot context cap. We
|
|
364
|
-
allocate **by need**. Three rungs, in increasing order of "who needs it
|
|
365
|
-
most" fidelity and cost:
|
|
366
|
-
|
|
367
|
-
1. **Static partition** — *not built.* Fixed per-slot ceiling. Wasteful.
|
|
368
|
-
2. **Dynamic shared-budget, contiguous caches (padded batch)** — the
|
|
369
|
-
shippable path. Admission is governed by *total projected bytes*
|
|
370
|
-
against one KV budget (mirrors the byte-capped `PromptCache`); each
|
|
371
|
-
sequence grows its own contiguous cache on demand (KVCache already
|
|
372
|
-
grows in 256-token steps). Short prompts use little; long ones take
|
|
373
|
-
more. **Caveat**: a padded batch shares one `[B, H, S, D]` buffer, so
|
|
374
|
-
the buffer width tracks the *longest live sequence* — short rows still
|
|
375
|
-
carry padding to that width. Partial "who needs it most."
|
|
376
|
-
3. **Paged KV (the true goal)** — fixed-size KV blocks in a shared pool,
|
|
377
|
-
gathered by a custom paged-attention Metal kernel. No padding waste;
|
|
378
|
-
the neediest sequence grabs more blocks, others fewer; maximizes
|
|
379
|
-
concurrent rows per GB. This is what "dynamically allocate based on
|
|
380
|
-
who needs it most" actually means. Cost: a custom attention kernel +
|
|
381
|
-
a block manager. Feasible — the project already ships custom Metal
|
|
382
|
-
kernel infrastructure (`src/mlx/metal-kernel.ts`).
|
|
383
|
-
|
|
384
|
-
Plan: ship rung 2 (S1–S2) for the throughput win + dynamic admission
|
|
385
|
-
with no new kernel; build rung 3 (S3+) as the memory-density upgrade.
|
|
386
|
-
Budget accounting is total-bytes from day one.
|
|
387
|
-
|
|
388
|
-
**Current state (2026-06-14):** the batched *primitive* is **rung 2** —
|
|
389
|
-
one shared `[B, H, S, D]` buffer per layer (`KVCache`/`RotatingKVCache`),
|
|
390
|
-
growing in 256-token steps, width = longest live sequence, left-pad
|
|
391
|
-
"waste" on short rows. Rung-2 allocation IS wired into serving via
|
|
392
|
-
`BatchScheduler` + `GenerationGateway` (S1b SHIPPED). But the
|
|
393
|
-
**budget/admission half of rung 2 is NOT built** (no `B × S_max`
|
|
394
|
-
projection, no KV budget enforcement) — that's scheduler work (S2, NOT
|
|
395
|
-
STARTED). *(Since LANDED 2026-07-03: `--kv-budget` aggregate projection
|
|
396
|
-
gate in the scheduler — grammar-spec-batching-integration.md Phase D.)*
|
|
397
|
-
And **rung 3 (paged): the v1 CACHE CLASS shipped** —
|
|
398
|
-
`PagedKVCache`/`BlockPool` behind default-off `--paged-kv`
|
|
399
|
-
(docs/design/paged-kv-cache.md): serial batch=1, Gemma4, bf16,
|
|
400
|
-
gather-then-stock-sdpa (no custom kernel yet), gated bit-exact vs the
|
|
401
|
-
plain path. The rung-3 PAYOFF (batched block allocation replacing the
|
|
402
|
-
pad+concat merge/extend, block-level CoW prefix sharing, the fused paged
|
|
403
|
-
kernel) is still the deferred follow-up — the shipped slice is the block
|
|
404
|
-
manager + correctness proof, not the density win.
|
|
405
|
-
|
|
406
|
-
KV quantization is a force multiplier: 4-bit KV (`generate.ts:67`,
|
|
407
|
-
already supported) is ~4× smaller, so the same budget buys ~4× the slots
|
|
408
|
-
or ~4× context per slot. Batching + KV-quant compound.
|
|
409
|
-
|
|
410
|
-
Note: the existing `2e9` (`server.ts:494`) is the **prompt (prefix-reuse)
|
|
411
|
-
cache**, not live decode KV. Live caches are made fresh per generation
|
|
412
|
-
(`server.ts:517`). Slots introduce a *new* budget: the pool of live
|
|
413
|
-
decode caches.
|
|
414
|
-
|
|
415
|
-
## The scheduler (port of mlx-lm `BatchGenerator` / `ResponseGenerator`)
|
|
416
|
-
|
|
417
|
-
When `slots > 1`, the single promise chain (`server.ts:487`) is replaced
|
|
418
|
-
by a scheduler owning:
|
|
419
|
-
|
|
420
|
-
- a **pending queue** of admitted requests;
|
|
421
|
-
- a **running batch** of ≤ `decodeConcurrency` rows;
|
|
422
|
-
- **admission control** against the KV budget;
|
|
423
|
-
- a **batchable gate** (mlx-lm `_is_batchable`): same model, compatible
|
|
424
|
-
sampler params, **no fixed seed**, no vision, compatible adapter set;
|
|
425
|
-
- **prefill phase** (≤ `promptConcurrency` rows) vs **decode phase**;
|
|
426
|
-
- **draining**: an incompatible request waits for the batch to clear,
|
|
427
|
-
then runs in its own batch.
|
|
428
|
-
|
|
429
|
-
Streaming fan-out is the main plumbing rework: today `generate()` yields
|
|
430
|
-
one token to one SSE stream (`server.ts:964,1037`). Batched, each step
|
|
431
|
-
produces B tokens that must route to B independent response streams,
|
|
432
|
-
each with its own `StopSequencer`/tool-call router. Touches
|
|
433
|
-
`server.ts`, `responses.ts`, `anthropic.ts`.
|
|
434
|
-
|
|
435
|
-
### Concrete plan (the `--batch N` engine, build order — 2026-06-14)
|
|
436
|
-
|
|
437
|
-
Started: `--batch N` flag landed (renamed from `--slots`; `--decode-concurrency`
|
|
438
|
-
accepted as an mlx_lm.server alias). N=1 → today's serial path; N>1 → the engine
|
|
439
|
-
below. The engine is a **mode** (whole server), not a load-fallback (keeps
|
|
440
|
-
determinism + the drop-in promise).
|
|
441
|
-
|
|
442
|
-
**Dynamic-B cache ops** (port of mlx-lm's cache methods — the new piece our
|
|
443
|
-
fixed-B verified forward doesn't have):
|
|
444
|
-
- **`merge(perRowCaches)`** — stack N single-row caches into one `[B,H,S,D]`
|
|
445
|
-
batch, left-pad-aligned (generalizes `realBatchedGreedy`'s assemble).
|
|
446
|
-
- **`filter(keep)`** — slice the B axis to drop finished rows (eviction).
|
|
447
|
-
- **`extend(other)`** — append a freshly-prefilled batch's rows (insertion).
|
|
448
|
-
- mlx-lm's flow: a joining request is **right-padded + prefilled** in a prompt
|
|
449
|
-
batch, then `finalize()` (dynamic-roll) → left-pad, then `merge`/`extend` into
|
|
450
|
-
the running generation batch. Our verified attention/mask/RoPE drop straight in.
|
|
451
|
-
|
|
452
|
-
**Engine loop (Bun-async, NOT Python threads):** an async loop owns the running
|
|
453
|
-
batch. Each iteration: (1) admit waiting+batchable requests up to N (prefill +
|
|
454
|
-
`extend`); (2) one batched decode step (the verified forward); (3) sample
|
|
455
|
-
per-row, push each row's token to its own SSE `ReadableStream` (per-row
|
|
456
|
-
`StopSequencer` + tool parser + sampler); (4) `filter` out rows that hit
|
|
457
|
-
EOS/stop/max-tokens, resolve their streams. HTTP handlers `await` their row's
|
|
458
|
-
stream; no thread, just the event loop + `mx.async_eval` pipelining.
|
|
459
|
-
|
|
460
|
-
**Build sequence:** (1) **DONE 2026-06-14** — dynamic-B cache `merge`/`filter` +
|
|
461
|
-
a pure unit test, then a gated parity test vs an mlx-lm *dynamic* golden (rows
|
|
462
|
-
join/leave mid-stream). `scripts/gen-batched-dynamic-golden.py` drives mlx-lm's
|
|
463
|
-
real `BatchKVCache.merge`/`.extract`/`.filter` through {A,B}→join C→evict A→{B,C};
|
|
464
|
-
`realDynamicBatchedGreedy` (`tests/batched-decode-parity.test.ts`) runs the same
|
|
465
|
-
scenario through `mergeKVRows`/`filterKVRows` and matches all 3 per-row greedy
|
|
466
|
-
trajectories token-for-token (CPM L1). Join = re-merge of extracted advanced-offset
|
|
467
|
-
rows + a fresh solo prefill (proves `mergeKVRows` on non-fresh rows); `extend`
|
|
468
|
-
(keep-the-running-batch optimization, mlx-lm's actual join op) is deferred into
|
|
469
|
-
the scheduler. Added `BatchedDecodeMaskCache.releaseRopeArr()`. (Originally
|
|
470
|
-
full-attention only; sliding-window `BatchedRotatingCache` landed 2026-06-14 —
|
|
471
|
-
see the STATUS block above.)
|
|
472
|
-
(2) **DONE 2026-06-14** — the async loop + per-row SSE wired into `createServer`
|
|
473
|
-
behind `--batch N`. `BatchScheduler` (`src/serve/batch-scheduler.ts`) is the
|
|
474
|
-
detached driver; `GenerationGateway` (`src/serve/generation-gateway.ts`) picks
|
|
475
|
-
the lane and an `AsyncMutex` keeps serial and batched off the GPU/`loraState`
|
|
476
|
-
together. Both `handleChat` call sites route through `gateway.run`; per-row
|
|
477
|
-
onToken closures give per-row SSE fan-out for free. Gated: `tests/batch-scheduler.test.ts`
|
|
478
|
-
(teacher-forced KL, evict + join) + `tests/batch-serving.test.ts` (live CPM
|
|
479
|
-
server, concurrent + streaming + drain). No serial regression (server 17/17,
|
|
480
|
-
tools 13/13).
|
|
481
|
-
(3) **DONE 2026-06-14** — `_is_batchable` gate is the `GenerationGateway.willBatch`
|
|
482
|
-
predicate (full-attention model + no vision/adapter/repetition-penalty/user-seed
|
|
483
|
-
→ batch; else serial). *(The predicate has since widened: repetition
|
|
484
|
-
penalty + logits extras now batch via per-row processors, 2026-07-02.)*
|
|
485
|
-
`B×S_max` memory admission is still TODO — **DONE 2026-07-03**
|
|
486
|
-
(`--kv-budget`, see the v2-plan pointer above).
|
|
487
|
-
|
|
488
|
-
## Fallbacks (must degrade, never break)
|
|
489
|
-
|
|
490
|
-
- **Vision** → always solo. Image embeddings + bidirectional mask need
|
|
491
|
-
offset-0 single-sequence prefill (`gemma4.ts:690` throws otherwise).
|
|
492
|
-
- **LoRA** → batch only requests sharing the same adapter set, else
|
|
493
|
-
drain to solo. `loraState.active` is a single per-generation field
|
|
494
|
-
(`generate.ts:187`); cache namespaces are adapter-keyed
|
|
495
|
-
(`prompt-cache.ts:18`). Per-row adapters is a much harder problem;
|
|
496
|
-
group-by-adapter + drain is accepted (confirmed fine for our use).
|
|
497
|
-
- **Fixed seed / unusual sampler** → drain to solo (matches `_is_batchable`).
|
|
498
|
-
- **Compiled decode** (`generate.ts:348`) → start **disabled** under
|
|
499
|
-
batching (single-sequence shapeless replay); extend to `[B,1]` later.
|
|
500
|
-
- **Prompt cache** → per-row take/put still works, but the
|
|
501
|
-
"single queue ⇒ take/reinsert is race-free" invariant
|
|
502
|
-
(`prompt-cache.ts:7`, `lora.ts:7`) now depends on the **scheduler**
|
|
503
|
-
being the sole owner of cache mutations. Keep all take/put inside the
|
|
504
|
-
scheduler loop.
|
|
505
|
-
|
|
506
|
-
## Config surface
|
|
507
|
-
|
|
508
|
-
- `ServerOptions.batch` (default 1); `kvBudgetBytes` (shipped 2026-07-03);
|
|
509
|
-
later `promptConcurrency`.
|
|
510
|
-
- CLI: `--batch N` (shipped; `--slots N` was the original design name, `--decode-concurrency` accepted as mlx_lm.server alias); `--kv-budget <GB>` (shipped 2026-07-03).
|
|
511
|
-
- `batch === 1` → today's serialized promise chain, untouched.
|
|
512
|
-
`batch > 1` → scheduler.
|
|
513
|
-
|
|
514
|
-
## Phasing (incremental, parity-gated)
|
|
515
|
-
|
|
516
|
-
- **S0 — config seam (DONE).** `--slots N` (now shipped as `--batch N`) / `ServerOptions.slots`
|
|
517
|
-
plumbed, validated, surfaced (ready card + `/stats`). `N>1` warns that
|
|
518
|
-
batched execution lands in S1 and runs serially. No behavior change.
|
|
519
|
-
- **S1 — static 2-wide, base model only. (DONE — S1b LIVE as `--batch N`)** Split by the reuse finding:
|
|
520
|
-
- **S1a (prefill)** — reuse the training machinery (`buildBatchedPadMask`
|
|
521
|
-
/ `BatchedMaskCache`); wire the serving path to prefill B prompts in one
|
|
522
|
-
forward. Mostly generalization + consolidating the two mask builders.
|
|
523
|
-
- **S1b (decode) — SHIPPED 2026-06-14.** `BatchScheduler`
|
|
524
|
-
(`src/serve/batch-scheduler.ts`) + `GenerationGateway`
|
|
525
|
-
(`src/serve/generation-gateway.ts`) wired into `createServer` behind
|
|
526
|
-
`--batch N`. L1 oracle-verified for CPM + all 3 Gemmas (full-attn +
|
|
527
|
-
sliding-window via `BatchedRotatingCache`). Per-row SSE fan-out live.
|
|
528
|
-
See STATUS block above for details.
|
|
529
|
-
**Teacher-forced gate**: a 2-row batch must produce per-row logits matching
|
|
530
|
-
two solo runs within bf16 tolerance (the train-batch-e2e methodology, now
|
|
531
|
-
applied to decode).
|
|
532
|
-
- **S2 — N-wide + continuous injection/eviction. (DONE)** Rows retire on EOS;
|
|
533
|
-
queued requests prefill into freed rows (shipped with the scheduler).
|
|
534
|
-
Dynamic byte-budget admission landed 2026-07-03 as `--kv-budget`
|
|
535
|
-
(aggregate projection gate, queue-don't-OOM —
|
|
536
|
-
grammar-spec-batching-integration.md Phase D).
|
|
537
|
-
- **S3+ — paged KV (v1 cache class SHIPPED; density payoff DEFERRED)** —
|
|
538
|
-
the block manager (`src/model/paged-kv.ts`) shipped behind default-off
|
|
539
|
-
`--paged-kv` at serial scope, bit-exact-gated (paged-kv-cache.md); the
|
|
540
|
-
batched block allocation, block-level CoW, and the custom
|
|
541
|
-
paged-attention kernel remain the follow-ups. KV-quant under batch
|
|
542
|
-
landed separately (Phase 3.1 + milestone 2); LoRA-group batching open.
|
|
543
|
-
|
|
544
|
-
Every phase ships default-off behind `batch=1`; the serialized path is
|
|
545
|
-
never removed (only bypassed).
|
|
546
|
-
|
|
547
|
-
## Open questions / to measure
|
|
548
|
-
|
|
549
|
-
- Sliding-window per-row masking (above) — retire this correctness risk
|
|
550
|
-
early.
|
|
551
|
-
- `async_eval` decode pipelining (`generate.ts:386`) builds step n+1
|
|
552
|
-
before reading n; one row stopping mid-pipeline while others continue
|
|
553
|
-
needs care.
|
|
554
|
-
- Throughput ceiling: the B where we cross bandwidth→compute bound, and
|
|
555
|
-
aggregate tok/s vs solo — measured via `benchmark.sh` (preflight-gated;
|
|
556
|
-
loaded-machine numbers don't count).
|
|
557
|
-
|
|
558
|
-
## References
|
|
559
|
-
|
|
560
|
-
- mlx-lm continuous batching: `BatchGenerator` / `ResponseGenerator`,
|
|
561
|
-
flags `--decode-concurrency` (default 32), `--prompt-concurrency`
|
|
562
|
-
(default 8); `_is_batchable` gate; batch draining.
|
|
563
|
-
https://deepwiki.com/ml-explore/mlx-lm/3.3-http-server ·
|
|
564
|
-
https://github.com/ml-explore/mlx-lm/issues/499
|
|
565
|
-
- optiq: **no** batching — single-process serialized, advises external
|
|
566
|
-
max-concurrency; each in-flight request keeps a resident KV cache.
|
|
567
|
-
https://mlx-optiq.com/docs/serve
|