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
|
@@ -0,0 +1,1990 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: landed
|
|
3
|
+
axis: ON
|
|
4
|
+
canonical-for: speculative-decoding
|
|
5
|
+
plan-anchor: "Phase 14 — Qwen 3.x family bring-up `[~]`"
|
|
6
|
+
last-verified: 2026-09-07
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Speculative decoding — one verifier, every draft source
|
|
10
|
+
|
|
11
|
+
Speculative decoding in mlx-bun is ONE verify/accept executor
|
|
12
|
+
(`src/spec/serve-loop.ts`, `specServeRun`) behind ONE seam
|
|
13
|
+
(`src/spec/source.ts`, `DraftProvider` / `DraftSource` / `TargetView`), with
|
|
14
|
+
every drafter — a second model, a KV-borrowing head, a trained block
|
|
15
|
+
drafter, a model-free lookup, a target-native MTP row — differing only in
|
|
16
|
+
what fills the draft. Output is the target's own samples at every position;
|
|
17
|
+
a drafter can only change decode SPEED (acceptance, tokens per target
|
|
18
|
+
forward), never which tokens are emitted. This doc is canonical for the
|
|
19
|
+
seam contract, the verifier/rollback contract, the source roster and each
|
|
20
|
+
source's oracle, and the DSpark program (our trained drafter + DeepSeek's
|
|
21
|
+
released one). Status and changelog prose live in PLAN.md; this doc keeps the
|
|
22
|
+
design plus a short dated History.
|
|
23
|
+
|
|
24
|
+
Consolidated 2026-08-23 from `docs/design/speculative-decoding.md` (base),
|
|
25
|
+
`docs/design/speculative-decoding.md` (phases 0–6 + the wall-clock-negative problem
|
|
26
|
+
statement) and `docs/design/speculative-decoding.md` (12B assistant γ=1 result).
|
|
27
|
+
The DSpark handoff (first live run, recipe) is archived at
|
|
28
|
+
[docs/archive/investigations/dspark-handoff.md](../archive/investigations/dspark-handoff.md).
|
|
29
|
+
|
|
30
|
+
## 1. The seam (`src/spec/source.ts`)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
DraftProvider server-lifetime: owns the loaded draft weights
|
|
34
|
+
.open({ sampler, target: { model, caches } }) → DraftSource (per request)
|
|
35
|
+
DraftSource per request: owns draft-side state
|
|
36
|
+
prefillMode? "tail-split" (default, mlx-lm shape) | "full" (native MTP)
|
|
37
|
+
pinTargetKernelFamily? GLM native MTP: one M=1 quantized family across the verify batch
|
|
38
|
+
tapLayers? layer indices the loop must capture on prefill + every verify forward
|
|
39
|
+
prefill(promptIds, ctxML?) seed draft state (ctxML iff tapLayers)
|
|
40
|
+
draft(feed, n, stepBase, anchorHidden?) → 0..n RETURN LENGTH IS AUTHORITATIVE
|
|
41
|
+
commit(d, kAccept, vCtxML?, verifiedHidden?, acceptedTokens?)
|
|
42
|
+
weightsBytes admission accounting (0 when the provider owns the weights)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- `target` is a read-only view of the target model + its LIVE caches (the
|
|
46
|
+
same arrays the loop drives). Two-model ignores it; every KV-borrowing
|
|
47
|
+
source reads donor K/V, embeddings, lm-head or hidden taps through it.
|
|
48
|
+
- `sampler` is the REQUEST sampler (mlx-lm parity: drafts are sampled with
|
|
49
|
+
the same sampler as the target; greedy drafting under a temperature>0
|
|
50
|
+
request is not parity — two-model and both MTP sources honor this; the
|
|
51
|
+
assistant and DSpark sources draft greedily, which is lossless but not
|
|
52
|
+
parity at temp>0).
|
|
53
|
+
- `feed` is mlx-lm's re-feed rule (generate.py:645-648): `[correction]` after
|
|
54
|
+
a rejected round, `[lastDraft, bonus]` after an all-accept round.
|
|
55
|
+
- `draft()` may return fewer than `n` (DSpark confidence pruning) or ZERO
|
|
56
|
+
(DeepSpec ℓ=0 / ngram no-match): the loop degenerates to one plain target
|
|
57
|
+
step, still tapped and committed for context-growing sources.
|
|
58
|
+
- `commit()` ownership: `vCtxML` transfers to the source; `verifiedHidden`
|
|
59
|
+
stays caller-owned.
|
|
60
|
+
- Per-slot readiness: each source binds its own target state (donor views,
|
|
61
|
+
anchor hidden, tapped context), which is what a batched executor would
|
|
62
|
+
need to open one source per row. The executor is the hard part — variable
|
|
63
|
+
accept length per slot breaks a uniform-B step — so per-slot spec is a
|
|
64
|
+
batched-scheduler project tracked in
|
|
65
|
+
[unified-engine-frontier-plan.md](unified-engine-frontier-plan.md), not
|
|
66
|
+
free composition. Today a mounted draft forces the serial lane for every
|
|
67
|
+
request (upstream `is_batchable = draft is None`).
|
|
68
|
+
|
|
69
|
+
## 2. The verifier / rollback contract (`src/spec/serve-loop.ts`)
|
|
70
|
+
|
|
71
|
+
Faithful to mlx-lm `speculative_generate_step` (generate.py:473-654, read
|
|
72
|
+
from the oracle venv). Per round:
|
|
73
|
+
|
|
74
|
+
1. **Draft** `d ≤ n` tokens (`n = min(γ, tokens left)`), with the anchor
|
|
75
|
+
hidden offered to sources that borrow it.
|
|
76
|
+
2. **One target forward** over `[pending, ...drafts]` (d+1 positions),
|
|
77
|
+
tapped when `tapLayers` is set. The lm-head runs ONCE over the whole
|
|
78
|
+
window (batched — matches mlx-lm and optiq; NOT bit-exact to stock
|
|
79
|
+
token-at-a-time decode at bf16 knife-edges, see §2.1).
|
|
80
|
+
3. **Accept walk**: sample the target at each position in order through the
|
|
81
|
+
shared `StepSampler` (processors, grammar mask, history — identical
|
|
82
|
+
ordering to the serial and batched lanes); accept while the target's
|
|
83
|
+
token equals the draft's (exact token-match acceptance, NOT
|
|
84
|
+
distribution-level rejection sampling); the first mismatch emits the
|
|
85
|
+
target's correction, an all-accept round emits the bonus. EOS is never
|
|
86
|
+
emitted as content, even when it arrives as an accepted draft.
|
|
87
|
+
4. **Emit** the round's tokens through `onToken` one at a time, in order
|
|
88
|
+
(bursts of ≤ d+1), so stop-sequence matching and detokenization see the
|
|
89
|
+
stream `generate()` would produce.
|
|
90
|
+
5. **Roll back + commit**: the verify window kept positions `0..kAccept`.
|
|
91
|
+
Trimmable caches `trim(d − kAccept)`; recurrent caches
|
|
92
|
+
`specRoundRollback(kAccept + 1)`; on full accept `specRoundCommit()`.
|
|
93
|
+
Then `source.commit(d, kAccept, vCtxML, vHidden, drafts[0..kAccept))`;
|
|
94
|
+
the next anchor hidden is the verify hidden at the emitted position.
|
|
95
|
+
6. **Chain** via the re-feed rule; `pending = emit`.
|
|
96
|
+
|
|
97
|
+
**Cache rollback contract** (`src/model/gemma4-base.ts` `Cache`):
|
|
98
|
+
`trim(n, bypass?)` for trimmable caches; the optional spec-round trio
|
|
99
|
+
`specRoundBegin()` / `specRoundCommit()` / `specRoundRollback(keep)` for
|
|
100
|
+
non-trimmable recurrent state. `SSMCache` (`src/model/qwen3-delta.ts`,
|
|
101
|
+
gated-DeltaNet) implements the trio: the snapshot is free (MLX arrays are
|
|
102
|
+
immutable — the layer hands its replaced conv/recurrent refs to the round),
|
|
103
|
+
and rollback restores the snapshot then REPLAYS the kept `keep` window tokens
|
|
104
|
+
through conv1d + the delta kernel from recorded position-local inputs
|
|
105
|
+
(`qkv`/`a`/`b`, retained not copied). Replay is bit-exact by construction:
|
|
106
|
+
the kernel's per-thread loop is serial, so the prefix arithmetic is identical
|
|
107
|
+
whether or not the rejected tail was processed (gated in
|
|
108
|
+
tests/qwen-ssm-specround.test.ts). The loop arms every cache before the
|
|
109
|
+
verify forward; a cache that is neither trimmable nor round-capable ends
|
|
110
|
+
speculation for the request.
|
|
111
|
+
|
|
112
|
+
**Deliberate deviations from upstream**
|
|
113
|
+
|
|
114
|
+
- Rotating-cache ring wrap: upstream RAISES when a `RotatingKVCache` stops
|
|
115
|
+
being trimmable mid-generation (generate.py:529-533); a serve endpoint
|
|
116
|
+
must not 500 mid-stream, so the loop checks `offset + n + 1 < maxSize`
|
|
117
|
+
before each round and, if the window is about to wrap, STOPS SPECULATING
|
|
118
|
+
and finishes with plain single-token decode (bit-equivalent — the
|
|
119
|
+
target's own samples). The standalone `src/spec/generate.ts` loop keeps
|
|
120
|
+
the reference behavior and throws.
|
|
121
|
+
- Prompt-cache reuse is BYPASSED on the spec path (fresh caches per spec
|
|
122
|
+
request, `cachedTokens = 0`). mlx-lm composes spec with its LRU prompt
|
|
123
|
+
cache; wiring ours through `PromptCache` + the SSD tier is the open item
|
|
124
|
+
in §8.
|
|
125
|
+
- Grammar × spec (novel — no runtime serves both): the drafter runs FREE;
|
|
126
|
+
the grammar mask rides the accept walk in `samplePos` (mask before
|
|
127
|
+
sample; the matcher advances on emitted tokens only, so rejected drafts
|
|
128
|
+
never touch grammar state and no matcher rollback exists). Grammar
|
|
129
|
+
termination mid-burst truncates the round. Gates: greedy grammar+spec ≡
|
|
130
|
+
greedy grammar-only long-prefix + 100% schema validity
|
|
131
|
+
(tests/spec-serve.test.ts).
|
|
132
|
+
|
|
133
|
+
Telemetry (`usage.speculation`): drafted / accepted / rejected / targetCalls /
|
|
134
|
+
rounds / acceptanceLengths / tokensPerForward / forwardsSaved, plus
|
|
135
|
+
per-position `draftedByPos` / `acceptedByPos` (the acceptance-A/B report).
|
|
136
|
+
`MLX_BUN_SPEC_TRACE=1` prints per-round feed + drafts.
|
|
137
|
+
|
|
138
|
+
### 2.1 Prefill convention and what "lossless" means here
|
|
139
|
+
|
|
140
|
+
- **Oracle prefill shape** (`MLX_BUN_PREFILL_TAIL_SPLIT`, default on): mlx-lm's
|
|
141
|
+
speculative path drains BOTH models to len−1 and has NO separate step-0 —
|
|
142
|
+
the un-drained last prompt token HEADS the first verify window, and the
|
|
143
|
+
first emitted token is that window's position-0 sample. A separate L=1
|
|
144
|
+
step-0 is ulp-different from the (1+γ)-window GEMM and flips near-ties.
|
|
145
|
+
Gated live 4/4 token-for-token vs the oracle venv (γ∈{2,3} × 2 prompts,
|
|
146
|
+
including a knife-edge cell). Sources with `prefillMode: "full"` (Qwen
|
|
147
|
+
MTP, GLM MTP) take the full-prompt shape their references use.
|
|
148
|
+
- **Batched verify vs stock decode**: both oracles (mlx-lm, optiq) batch the
|
|
149
|
+
verify lm-head, so greedy spec is bit-exact to THE SPEC ORACLE and agrees
|
|
150
|
+
with stock decode only on tie-free prompts. An earlier port verified
|
|
151
|
+
per-position to match stock — a target no real implementation has, at a
|
|
152
|
+
γ× lm-head read tax — and was replaced by `picksBatched`
|
|
153
|
+
(`src/spec/generate.ts`, 2026-06-14). Losslessness gates therefore use
|
|
154
|
+
TIE-FREE prompts (tests/spec-decode.test.ts, spec-serve-assistant,
|
|
155
|
+
qwen38-mtp) or long-prefix agreement (spec-decode-12b): a flip on a
|
|
156
|
+
tie-free prompt is an accept/reject/rollback bug, never rounding.
|
|
157
|
+
- **"Lossless" is a claim about the verifier, not the artifact.** The
|
|
158
|
+
Qwen3.8-27B TQ artifact's serve gauntlet recorded MTP at 76% acceptance /
|
|
159
|
+
2.53 tokens per forward WITH an output-divergence flag vs plain greedy
|
|
160
|
+
(consistent with verify-width reduction-order near-ties, the same class as
|
|
161
|
+
the 12B step-0 convention; margin analysis queued) — it is NOT claimed
|
|
162
|
+
lossless on the model card. The 27B bf16-target pairing gate, by
|
|
163
|
+
contrast, was token-identical on its tie-free prompt (PLAN 14g).
|
|
164
|
+
|
|
165
|
+
## 3. Draft-source roster
|
|
166
|
+
|
|
167
|
+
`--draft-model <dir>` mounts a drafter; the artifact's KIND selects the
|
|
168
|
+
provider (`src/server.ts` `detectDraftKind`; `--draft-kind` overrides).
|
|
169
|
+
Detection order: `dspark.json` → `dspark`; `config.json` `architectures[0]
|
|
170
|
+
=== "Gemma4DSparkModel"` → `deepspec`; `model_type` containing `assistant` →
|
|
171
|
+
`assistant`; `model_type` ending `_mtp` → `mtp`; else `two-model`. `ngram`
|
|
172
|
+
is never detected (no artifact) and refuses a `--draft-model`; any other
|
|
173
|
+
kind without one is refused. `--draft-kind mtp` with no `--draft-model`
|
|
174
|
+
resolves the companion bundled at `<model>/mtp/` (single-repo artifacts) and
|
|
175
|
+
refuses otherwise. GLM-5.2's checkpoint-native MTP row mounts by default
|
|
176
|
+
(`--mtp on|off`) and is mutually exclusive with an explicit drafter.
|
|
177
|
+
|
|
178
|
+
| kind | source | what fills the draft | target state used | draft sampler | default γ | oracle / gate |
|
|
179
|
+
|---|---|---|---|---|---|---|
|
|
180
|
+
| `two-model` | `src/spec/two-model.ts` | a full second model, autoregressive, own KV | none | request sampler | 3 (mlx_lm.server) | **L1**: mlx_lm.server `--draft-model`, same pair, greedy, token-for-token (spec-vs-spec). Shared B1/B>1 autoregressive draft state retains independent attention/recurrent boundaries and the pending final-token lag; paired RAM/SSD checkpoints use backend cache codecs. Tokenizer-family probe at startup refuses mismatches (upstream silently accepts ~0%). |
|
|
181
|
+
| `assistant` | `src/spec/assistant-source.ts` (+ `drafter.ts`) | Gemma "-assistant" Q-only 4-layer head | donor K/V (last sliding + last full layer) + anchor hidden, borrowed each step | greedy (own head) | 3 | **L2**: optiq `spec_generate` — e4b γ=2 identical 48-token output AND identical accept/reject trace (drafted 60 / accepted 17 / target calls 31), `scripts/oracle/oracle-spec.py`. Head picked by TENSOR PRESENCE (centroid vs tied-embedding) — optiq's config-based detection loads the 12B/26B unified drafters with zero-init centroids → 0% acceptance; ours 29% on the same artifact. |
|
|
182
|
+
| `dspark` | `src/spec/dflash-source.ts` (+ `dspark/module-dflash.ts`) | our trained DSpark block drafter (KV injection + Markov + confidence) | multi-layer hidden tap (`tapLayers`, e4b {20,31,41,42}) grown into H_ctx | greedy | pinned to `cfg.gamma` | **Lab** (no external oracle for our checkpoints): CPU smoke, infer-loop bit-identity pins, real-weights serve gate. See §4. |
|
|
183
|
+
| `deepspec` | `src/spec/deepspec-source.ts` (+ `dspark/deepspec-module.ts`) | DeepSeek's released `Gemma4DSparkModel` drafters (e.g. `dspark_gemma4_12b_block7`, γ=7) | tap `[5,17,29,41,46]` on the 12B, projected into an incremental context-KV cache; accepted rows only | argmax (temp-0 reference) | pinned to `block_size` | **DeepSpec reference** at temperature 0: its leaky rejection sampling degenerates to exact argmax token-match, RNG-free — round-for-round trace fixtures (`scripts/oracle/oracle-dspark-deepspec.py`; the compare script was deleted 2026-08-23, git history). |
|
|
184
|
+
| `ngram` | `src/spec/ngram-source.ts` | model-free prompt lookup: longest trailing k-gram (max..min) matched at its FIRST earlier occurrence; the continuation is the draft | token history rebuilt from prompt + feed/commit | none | 10 (Saxena `num_pred_tokens`); `--ngram-max 3` / `--ngram-min 1` | Ported from Prompt Lookup Decoding (Saxena) + vLLM `ngram`. Lossless by verify — gated token-identical to non-spec greedy (tests/spec-ngram.test.ts on e4b; tests/qwen35-spec-ngram.test.ts on the 0.8B hybrid, proving real accepts AND real DeltaNet rollbacks). |
|
|
185
|
+
| `mtp` | `src/spec/qwen-mtp-source.ts` | Qwen-trained MTP head (`qwen3_5_mtp`, one full-attention layer + `fc` merge; `mlx-community/Qwen3.8-27B-MTP-*`) | target `embed_tokens` + `lm_head` (not standalone) + PRE-final-norm last-layer hidden via `tapLayers = [nLayers−1]`; `prefillMode: "full"` | request sampler | `block_size − 1` (=2) | Reference: mlx-vlm `qwen3_5_mtp` drafter. Draft-logit parity vs mlx-vlm 0.6.14: draft tokens exact through the chained block, worst \|Δlogprob\| 1.9e-1 = bf16-vs-f32 floor. Losslessness: token-identical on the tie-free 27B pairing gate (tests/qwen38-mtp.test.ts, opt-in `MLX_BUN_TEST_QWEN38_MTP=1`). Target rollback = the SSMCache spec-round contract (§2). |
|
|
186
|
+
| GLM native | `src/spec/glm52-mtp-source.ts` | GLM-5.2's checkpoint-native MTP row (`enorm`/`hnorm`/`eh_proj` + one decoder layer, int8 routed experts) | target embeddings + output head + anchor hidden; own `MLACache` role `mtp`; `prefillMode: "full"`, `pinTargetKernelFamily` | request sampler | `glmMemoryPlan.mtpDraftTokens` | Direct Colibri (SPEC_PIN contract; "niente prefill" — the first draft opens a decode-only window from the anchor). Commit rebuilds accepted rows from the target's VERIFIED hidden window (only the first speculative row was conditioned on a true hidden). Measured in docs/reference/benchmarks.md "Colibri G4 serial native MTP". |
|
|
187
|
+
|
|
188
|
+
The `source.ts` header roster lists five of these (two-model, assistant,
|
|
189
|
+
dflash, ngram, GLM MTP); `deepspec` and `mtp` are wired in `server.ts` and
|
|
190
|
+
the CLI and belong in the same list — this table is the complete roster.
|
|
191
|
+
|
|
192
|
+
**Row conventions worth knowing.** Qwen MTP is predict-2-ahead: drafter KV
|
|
193
|
+
row at position p is built from `(embed(token_{p+1}), hidden_p)`; `draft()`
|
|
194
|
+
opens by building the pending token's row from the target's TRUE hidden at
|
|
195
|
+
the emitted position (held over from the verify tap), then chains its own
|
|
196
|
+
output for the rest of the block; `commit()` trims rejected rows and, on
|
|
197
|
+
all-accept, appends the missing accepted row from the verify hidden. The
|
|
198
|
+
sampler contract is `[1, V]` — the module output `[1,1,H]` must be
|
|
199
|
+
reshaped before sampling or 2-D-slicing samplers (top-k) throw (the
|
|
200
|
+
2026-08-20 serve-lane MTP 500; both MTP sources carry the fix).
|
|
201
|
+
|
|
202
|
+
## 4. DSpark — the trained block drafter
|
|
203
|
+
|
|
204
|
+
DSpark (DeepSeek, arXiv:2607.05147, "Confidence-Scheduled Speculative
|
|
205
|
+
Decoding with Semi-Autoregressive Generation") = a **DFlash** parallel
|
|
206
|
+
backbone (KV-injected, all γ draft tokens in one pass) + a lightweight
|
|
207
|
+
**sequential head** (Markov, Eq 5; RNN, Eq 6) adding intra-block dependency
|
|
208
|
+
+ a **confidence head → calibration → draft-length scheduler**. DFlash alone
|
|
209
|
+
suffers suffix decay; the sequential head is what makes it DSpark.
|
|
210
|
+
|
|
211
|
+
### 4.1 Mechanism (our module, `src/spec/dspark/module-dflash.ts`, paper Eq 2–3)
|
|
212
|
+
|
|
213
|
+
- **Eq 2 (context):** tap the target's hidden states at layers `{l1…lm}`
|
|
214
|
+
(e4b default {20,31,41,42}, the last = post-finalNorm sentinel at index
|
|
215
|
+
nLayers), concat on the feature axis, project to draft width:
|
|
216
|
+
`H_ctx ∈ [L_ctx, d]`. Extraction is the parity-safe `model.hiddenTap`
|
|
217
|
+
(no-op when off) in `gemma4.ts` `forwardLayers`; `qwen3_5.ts` carries the
|
|
218
|
+
same contract.
|
|
219
|
+
- **Eq 3 (injection):** every draft layer forms
|
|
220
|
+
`K_i = [W_i^K H_ctx; W_i^K H_d]`, `V_i` likewise, `Q_i = W_i^Q H_d`; the
|
|
221
|
+
γ-block queries attend bidirectionally over `[context ++ block]`. The
|
|
222
|
+
draft projects the target's HIDDEN STATES with its OWN Wk/Wv (not the
|
|
223
|
+
target's K/V — that is the assistant drafter).
|
|
224
|
+
- **Markov head (Eq 5):** `B_k = W1[x_{k−1}]·W2`, r=256, W2 zero-init →
|
|
225
|
+
starts as pure DFlash, τ climbs as intra-block dependency is learned.
|
|
226
|
+
- **Confidence head (Eq 7):** `c_k = σ(wᵀ[h_k; W1[x_{k−1}]])` (we add a
|
|
227
|
+
zero-init bias — superset).
|
|
228
|
+
- **Loss (Eq 8–12):** `0.1·CE + 0.9·TV + 1.0·BCE`, position weights
|
|
229
|
+
`w_k = exp(−(k−1)/γ)`, analytic c*. Audit verdict: matches the paper to
|
|
230
|
+
the letter; our defaults (γ=5, 5 layers, Markov) = their production
|
|
231
|
+
config.
|
|
232
|
+
- **RNN head (`cfg.seqHead: "rnn"`)**: ungated Elman
|
|
233
|
+
`s_k = tanh(s_{k−1}·wH + E[x_{k−1}] + bH)`, `B_k = s_k·wO` (wO zero-init;
|
|
234
|
+
init-equivalent to Markov). The paper's Eq 6 is a gated cell over
|
|
235
|
+
`[s_{k−1}; W₁[x_{k−1}]; h_k]` — ours differs, kept as a variant (paper
|
|
236
|
+
calls the RNN gains marginal; DeepSpec ships the gated form if ever
|
|
237
|
+
wanted).
|
|
238
|
+
- **Confidence-scheduled pruning (Alg 1, single-user form)**: `forwardInfer`
|
|
239
|
+
drops position k (and truncates the block) when
|
|
240
|
+
`c_k < thresholds[k] ?? minConf`; position 0 always survives. Activation is
|
|
241
|
+
checkpoint-driven (`cfg.sts` thresholds) or `MLX_BUN_DSPARK_MINCONF`;
|
|
242
|
+
uncalibrated checkpoints draft fixed-γ. Invariant: pruning changes how
|
|
243
|
+
many positions the target verifies, never what is emitted (gated:
|
|
244
|
+
truncation-never-redraw, prefix-identical to the unpruned block).
|
|
245
|
+
- **Calibration (`src/spec/dspark/calibration.ts`)**: per position, the
|
|
246
|
+
smallest threshold whose Laplace-smoothed `P(accepted | conf ≥ τ)` meets
|
|
247
|
+
the target; pos 0 → 0; under-sampled → 0; unreachable → 1.0. This fits the
|
|
248
|
+
RELEASED reference's scheduler shape (per-position threshold truncation on
|
|
249
|
+
sigmoid confidence, default 0 = off, ℓ=0 allowed). The PAPER's §3.2.1 STS
|
|
250
|
+
(per-position temperatures by ECE grid search on survival products) and
|
|
251
|
+
Alg 1's throughput maximization over a profiled SPS(B) table live in
|
|
252
|
+
DeepSeek's UNRELEASED serving layer — paper-faithful STS/Alg-1 is a Lab
|
|
253
|
+
item only if the survival-product scheduler ever earns a default.
|
|
254
|
+
- **Verify**: the serve loop's token-match acceptance (§2). The standalone
|
|
255
|
+
`generate-dflash.ts` keeps the paper's distribution-level verify
|
|
256
|
+
(`min(1, p/q)` accept + residual `norm(relu(p−q))` resample) for the
|
|
257
|
+
measure path; greedy is identical, temp>0 is lossless either way but
|
|
258
|
+
token-match accepts less.
|
|
259
|
+
- **Rollback past the sliding window**: `trim(n, bypass=true)` physically
|
|
260
|
+
slices rejected tips on rotating caches (`gemma4-base.ts`).
|
|
261
|
+
- **Checkpoint format**: `dspark.json` stamps `variant: "dspark"` (legacy
|
|
262
|
+
`"dflash"` accepted); `src/spec/dspark/loader.ts` `loadDsparkDrafter()`
|
|
263
|
+
dispatches by variant and REFUSES v1 single-vector checkpoints
|
|
264
|
+
(`module.ts`/`data.ts`/`generate.ts` — the superseded baseline that
|
|
265
|
+
dropped KV injection) with a pointer at the trainer.
|
|
266
|
+
|
|
267
|
+
### 4.2 The v1 → v2 finding (why "faithful" matters)
|
|
268
|
+
|
|
269
|
+
v1 fused a single final-hidden vector as one token — it removed the paper's
|
|
270
|
+
load-bearing mechanism and never tested it. v2 is faithful. Getting v2 to
|
|
271
|
+
paper-range required an off-by-one fix in the TV-loss target alignment
|
|
272
|
+
(block position k predicts `x_{t+1+k}` whose target is
|
|
273
|
+
`softmax(LM_head(h_{t+k}))`; the data gathered `t+k+1`) — present in both
|
|
274
|
+
v1 and v2, fixed in `data-dflash.ts` (`blockIdx = g+k`). Overfit on 3
|
|
275
|
+
articles: per-position acceptance ~0.75, τ 3.24 (paper range 0.6–0.9 / τ
|
|
276
|
+
3–4) — the architecture is correct. 160 articles generalize to ~0.17 and
|
|
277
|
+
plateau; width is not the ceiling (`dDraft` 1024 and 2560 both plateau);
|
|
278
|
+
the paper trains on 1.3M samples × 10 epochs. (M1 Max 32 GB, e4b
|
|
279
|
+
OptiQ-4bit, 2026-06-30.)
|
|
280
|
+
|
|
281
|
+
### 4.3 DeepSpec — the released reference, and why DSpark has an oracle
|
|
282
|
+
|
|
283
|
+
DeepSeek open-sourced DeepSpec (MIT) + trained drafters incl.
|
|
284
|
+
`deepseek-ai/dspark_gemma4_12b_block7` (6.86 GB bf16, γ=7, for
|
|
285
|
+
gemma-4-12B-it). Their drafter is a DIFFERENT module from ours: a full
|
|
286
|
+
Gemma4-shaped 5-layer transformer — hidden 3840, 16 Q heads × head_dim 512,
|
|
287
|
+
1 KV head with K≡V (`attention_k_eq_v`, no v_proj), scale 1.0 (QK-norm),
|
|
288
|
+
partial RoPE 0.25 θ=1e6, per-layer `layer_scalar`, scaled embed (×√3840),
|
|
289
|
+
UNTIED lm_head, final softcap 30, incremental context-KV cache. 74 bf16
|
|
290
|
+
tensors, no key prefix, `architectures: ["Gemma4DSparkModel"]`. 12B tap
|
|
291
|
+
layers `[5,17,29,41,46]` (their layer_id+1 output convention == our
|
|
292
|
+
`tapLayers` index; no final-norm sentinel). Ported verbatim in
|
|
293
|
+
`deepspec-module.ts` (transcribe their forward, then op-for-op), with
|
|
294
|
+
context rows cached post-norm-post-rope (bit-equivalent to their in-round
|
|
295
|
+
concat; argued in `projectContextKV`). Argmax is over the bf16 sum — an f32
|
|
296
|
+
sum reorders near-ties vs torch.
|
|
297
|
+
|
|
298
|
+
Consequence: DSpark is no longer oracle-less. The DeepSpec port + their
|
|
299
|
+
checkpoint is the no-training path to a 12B speedup, gateable
|
|
300
|
+
round-for-round at temp 0. Our trainable module remains the research path
|
|
301
|
+
for custom targets. Their training recipe (for scale reference):
|
|
302
|
+
open-perfectblend regenerated by the target, 10 epochs, batch 512, lr 6e-4,
|
|
303
|
+
~38 TB cached hiddens.
|
|
304
|
+
|
|
305
|
+
The drafter also loads QUANTIZED: `scripts/dspark.ts quantize` writes an
|
|
306
|
+
mlx-native affine sibling (`src/spec/dspark/quantize-drafter.ts`: every 2-D
|
|
307
|
+
matmul weight + `embed_tokens`/`markov_w1` as quantized-gather tables;
|
|
308
|
+
`confidence_head`, norms, `layer_scalar`, biases stay bf16 — the mlx
|
|
309
|
+
`false` convention), and `deepspec-module.ts` detects `.scales` siblings and
|
|
310
|
+
routes through `quantized_matmul` / QuantizedEmbedding gather-dequant
|
|
311
|
+
(mlx-lm numerics). 8-bit tracks bf16 token-for-token on the synthetic
|
|
312
|
+
checkpoint. Drafter numerics only move ACCEPTANCE, never correctness, so the
|
|
313
|
+
entire quality gate for any drafter quantization is one acceptance A/B.
|
|
314
|
+
|
|
315
|
+
### 4.4 The wall-clock problem (why spec is opt-in)
|
|
316
|
+
|
|
317
|
+
Spec decode amortizes fixed draft overhead only when the TARGET forward is
|
|
318
|
+
expensive. The recurring finding across every drafter and target:
|
|
319
|
+
|
|
320
|
+
- **e4b is near the worst case** (fast target): assistant γ=2 ran 0.78× of
|
|
321
|
+
a 54 tok/s baseline at ~23% acceptance (Phase 6, 2026-06-10); even a τ≈3
|
|
322
|
+
DSpark drafter nets ~0.4–0.5×.
|
|
323
|
+
- **12B assistant γ=1 is a modest win** — see History 2026-06-14.
|
|
324
|
+
- **12B + DeepSpec block7 (first live run, 2026-07-07, loaded M4 Pro
|
|
325
|
+
24 GB, directional only):** τ ≈ 2.8 committed tokens per target forward
|
|
326
|
+
at 26–33% per-token acceptance (the predicted OptiQ-4bit-target
|
|
327
|
+
degradation vs their bf16 training) — the algorithm WORKS — but
|
|
328
|
+
wall-clock LOST ~3.4× (spec 14.6 vs serial 49.8 aggregate tok/s, conc-4).
|
|
329
|
+
The tax is the drafter: 6.9 GB bf16 → ~3 GB of weight reads per round
|
|
330
|
+
(2 GB untied lm_head + 7 × 134 MB markov_w2 + layers), ~14 host syncs
|
|
331
|
+
per round in the transcription-faithful `draftBlock`, tapped verifies
|
|
332
|
+
falling back to the unoptimized monolith, and ~14 GB of paired weights
|
|
333
|
+
on a 24 GB box.
|
|
334
|
+
- **27B + Qwen native MTP (2026-08-18, quiet M1 Max 32 GB, interleaved
|
|
335
|
+
off/on ×3, spreads 2.2%/4.4%, arms token-identical):** median OFF 15.75
|
|
336
|
+
vs ON 12.93 tok/s — 0.821× at γ=2 despite 61% acceptance and 2.25
|
|
337
|
+
tokens/forward. The bf16 head's serial per-draft cost (full-vocab lm-head
|
|
338
|
+
matmul + sample per draft) exceeds the saved 27B forwards. Levers if
|
|
339
|
+
reopened: quantize the head; batch/defer its per-draft lm-head sampling.
|
|
340
|
+
- **GLM-5.2 native MTP (M1 Max 32 GB, Colibri G4):** 1.235× wall
|
|
341
|
+
throughput, 32/92 accepted, 2.065 tokens per verify forward — the one
|
|
342
|
+
measured wall-clock win, on a target whose forward is expensive enough
|
|
343
|
+
(docs/reference/benchmarks.md).
|
|
344
|
+
|
|
345
|
+
Doctrine: a lone request must never get slower because a flag exists; spec
|
|
346
|
+
stays opt-in per (target, drafter) pair until it wins a clean-machine
|
|
347
|
+
paired A/B; losing configs stay documented default-off. Denominator
|
|
348
|
+
discipline: speedups only ever vs the SAME model's non-spec baseline.
|
|
349
|
+
|
|
350
|
+
### 4.5 The DSpark serving program (12B) — phases and gates
|
|
351
|
+
|
|
352
|
+
Goal: spec ≥ 1.3× serial decode on 12B at the best config, clean-machine
|
|
353
|
+
paired A/B — then decide when it is on by default. Objective stated right
|
|
354
|
+
(Josh, 2026-07-07): 26–33% acceptance with the bf16 drafter is the
|
|
355
|
+
WORKABLE baseline (τ≈2.8 already pays in forwards); the question
|
|
356
|
+
quantization must answer is PRESERVATION — do we keep that number at
|
|
357
|
+
4-bit? Sensitivity is MEASURED, never guessed: optiq's method (simulate-quant
|
|
358
|
+
one group → KL vs reference on a calibration mix → greedy knapsack to target
|
|
359
|
+
bpw), cheaper on the drafter because cached tapped hiddens remove the 12B
|
|
360
|
+
from the sweep loop. TurboQuant's role is better-quant-at-EQUAL-bits (finer
|
|
361
|
+
levels around the Gaussian peak) — a preservation instrument, measured at
|
|
362
|
+
equal bpw before any lower rung.
|
|
363
|
+
|
|
364
|
+
| phase | what | state |
|
|
365
|
+
|---|---|---|
|
|
366
|
+
| 0a | oracle-gate the bf16 DeepSpec port ONCE (round-for-round vs their temp-0 trace on the bf16 HF 12B; OptiQ arm acceptance-only) | open (Josh: torch venv + GPU) |
|
|
367
|
+
| 0b | γ sweep {2,3,5,7} at conc 1 → per-γ tok/s / acceptance / TTFT table | open |
|
|
368
|
+
| 0c | time attribution (draft GPU / draft host syncs / verify / tap+commit; paging check) — orders phases 1–3 by measured value | open |
|
|
369
|
+
| 1a | `scripts/dspark.ts quantize` | done 2026-07-07 |
|
|
370
|
+
| 1b | quantized forward in `deepspec-module.ts` | done 2026-07-07 |
|
|
371
|
+
| 1c | `scripts/dspark.ts ab` — same target, same ≥32 prompts, temp 0, drafter A vs B; per-position acceptance, τ, tok/s, paired verdict (drop ≤ 3 pts AND wall-clock strictly improves); stats in `ab-stats.ts`, unit-tested | done 2026-07-07 |
|
|
372
|
+
| 1d | RUN 4-bit vs bf16 at best-γ | open (GPU) |
|
|
373
|
+
| 1e | measured sensitivity map (optiq's method on cached hiddens) → knapsack allocation → one confirming A/B | open |
|
|
374
|
+
| 1.5 | OPTIONAL acceptance upside: adaptation fine-tune on OUR target's tapped hiddens (their checkpoint as init; gradient in their torch trainer vs a local path — undecided); threshold sweep at 26% (objective = tok/s, not τ); target-fidelity axis measurement | open |
|
|
375
|
+
| 2 | tighten `Gemma4DSparkModel.draftBlock`: on-device token chaining (one host read per round), compute no confidence at threshold ≤ 0, skip unread `baseLogits`; gate = bit-identity on the synthetic checkpoint + real-checkpoint determinism | open |
|
|
376
|
+
| 3 | `captureLayer` in the generated Gemma forwards (`scripts/gen-model.ts`) so tapped prefill/verify keep the fast path; drop the `hiddenTap !== null` fallback guard; regen 12b/26b/e4b; gate = generated-parity + tap-parity + spec bench | open (guard still present 2026-08-23) |
|
|
377
|
+
| 4 | serving UX on 0–3 data: per-kind γ default (trained γ is the cap, not necessarily the default), quantized-drafter UX (`mlx-bun get` → quantize), spec × prompt-cache composition, surface audit (`/stats` drafter+kind+γ; server-config "speculative serving" section), the default decision per pair written down | open |
|
|
378
|
+
| 5 | TurboQuant weight scheme for the drafter: TQ-4 vs affine-4 AT EQUAL BPW through the 1c harness (headline); lower rungs only after that is won; method (TQ vs affine) and allocation (knapsack) are orthogonal axes, never mixed within an artifact; per-tensor mixed rungs from the measured table (lm_head ~2 GB read fully per round; embed ~2 GB gather-only; markov_w2 134 MB ×7) | open |
|
|
379
|
+
| 6 | clean-machine protocol → promote to benchmarks + default decision; close or re-defer loose ends (gated RNN head, paper Alg-1/STS, per-slot spec) | open |
|
|
380
|
+
|
|
381
|
+
Exit criteria: (1) ≥ 1.3× on 12B clean-machine paired, or a written verdict
|
|
382
|
+
that it cannot be reached and why; (2) acceptance PRESERVED and understood
|
|
383
|
+
(the quantized drafter holds the bf16 baseline, or the mixed rung that does
|
|
384
|
+
is adopted, or the loss is measured and accepted); (3) the
|
|
385
|
+
acceptance-per-byte curve exists (TQ vs 4-bit vs mixed vs bf16); (4) the
|
|
386
|
+
surface is documented, defaults decided per pair, and `mlx-bun get` →
|
|
387
|
+
serve → speedup works without reading a design doc.
|
|
388
|
+
|
|
389
|
+
### 4.6 The 27B program (Qwen3.8, 2026-08-19)
|
|
390
|
+
|
|
391
|
+
The TurboQuant-weights campaign's 17 GB Qwen3.8-27B (~11.8 tok/s decode,
|
|
392
|
+
2026-08-19) is the slow-target regime where τ≈3 nets 2–3×, and it retires
|
|
393
|
+
the "27B infeasible to train on 24 GB" premise: with the quantized trunk
|
|
394
|
+
FROZEN (inference-only taps + teacher logits) and only the ~5-layer drafter
|
|
395
|
+
holding grads/optimizer state, drafter training fits a 32 GB machine.
|
|
396
|
+
Verify/rollback across the 48 DeltaNet layers is proven engine behavior
|
|
397
|
+
(§2). Two tracks, cheap-first, queued behind the certification suite's GPU
|
|
398
|
+
ownership (PLAN.md TurboQuant-weights phase, "Queued follow-ups"):
|
|
399
|
+
|
|
400
|
+
- **Track A — confidence-scheduled verification on the stock MTP head (no
|
|
401
|
+
training).** Dynamic per-step block length: v0 confidence = the draft's
|
|
402
|
+
own token probability, mapped through the `scripts/dspark.ts calibrate`
|
|
403
|
+
pattern to expected acceptance; stop drafting when marginal
|
|
404
|
+
expected-accept drops below the verify amortization point. Calibration
|
|
405
|
+
data = thousands of (confidence, accepted?) pairs from served traffic.
|
|
406
|
+
Gate: paired A/B tok/s vs fixed-γ MTP; lossless by construction.
|
|
407
|
+
- **Track B — true DSpark drafter for the 27B.** The faithful v2 retargeted:
|
|
408
|
+
tap-layer selection for the 64-layer trunk (bias toward the 16
|
|
409
|
+
full-attention layers + post-finalNorm), data by TEACHER-FORCED passes
|
|
410
|
+
over corpus text with the frozen trunk (every position yields a sample).
|
|
411
|
+
Open idea: initialize/distill against the shipped MTP head. Gate:
|
|
412
|
+
generalizing per-position acceptance must beat the MTP head's 0.68 or
|
|
413
|
+
Track B is dropped for Track A + MTP.
|
|
414
|
+
|
|
415
|
+
Drafts trained on the TQ artifact are married to its rotation basis (card
|
|
416
|
+
note if published). `RadixArk/Qwen3.8-27B-DSpark` exists as a cross-check
|
|
417
|
+
candidate (PLAN 14h, optional).
|
|
418
|
+
|
|
419
|
+
### 4.7 Packed 27B MTP recheck after kernel changes (2026-09-05)
|
|
420
|
+
|
|
421
|
+
The M4 Pro screen uses the actual packed k300 artifact and the existing
|
|
422
|
+
folded MTP companion from the affine flagship. Their recorded R1 seed,
|
|
423
|
+
hidden size and R2 setting agree; the head's documented final-gamma
|
|
424
|
+
approximation remains a draft-quality limitation. No head was downloaded,
|
|
425
|
+
trained or changed. The target verifies every MTP proposal; this is separate
|
|
426
|
+
from strict fill's no-verification assertions.
|
|
427
|
+
|
|
428
|
+
`tests/parity/qwen38-mtp.test.ts` now supports a warm native wall-time
|
|
429
|
+
diagnostic via `MLX_BUN_TEST_MTP_REPORT`. Target/draft path overrides select
|
|
430
|
+
the exact pair; `MLX_BUN_TEST_MTP_GAMMA` selects 1..4. The report records
|
|
431
|
+
both token sequences, actual completion counts, first-token and full-request
|
|
432
|
+
wall time, speculation counters, source and artifact identity, and machine
|
|
433
|
+
state. It does not compare differently defined decode-rate counters.
|
|
434
|
+
|
|
435
|
+
Six AB/BA blocks on the short enumeration fixture compare gamma 2 on variants
|
|
436
|
+
6 and 8, then gamma 1/2/3/4 on variant 8. The optimized verification kernels
|
|
437
|
+
improve MTP's economics on that fixture; gamma 4 loses when the target append
|
|
438
|
+
crosses the packed/expanded boundary. Exact emitted-token identity holds in
|
|
439
|
+
the observed cells. These are loaded-machine diagnostics under
|
|
440
|
+
`reports/qwen38-rd/mtp-v*-gamma*.json`, not default-promotion evidence.
|
|
441
|
+
|
|
442
|
+
A predeclared code, explanation, arithmetic, extraction, JSON and prose
|
|
443
|
+
screen has one warm pair per prompt with alternating arm order. Four valid
|
|
444
|
+
cases improve, while prose loses with lower draft acceptance. The extraction
|
|
445
|
+
case ran past chat EOS and is excluded from serving comparisons. All six
|
|
446
|
+
preserve the observed token IDs, including that invalid overrun. Reports and the frozen prompts are under
|
|
447
|
+
`reports/qwen38-rd/mtp-corpus-screen/` and `mtp-screen-prompts.json`. The
|
|
448
|
+
fixed output budget truncates some answers; this is not a task-quality eval.
|
|
449
|
+
MTP remains opt-in. Repeat diverse longer sessions, include rejection/rollback
|
|
450
|
+
cost and memory, and test a bounded adaptive policy on held-out prompts before
|
|
451
|
+
changing that decision.
|
|
452
|
+
|
|
453
|
+
The next adaptive screen stays within the existing `DraftSource` contract:
|
|
454
|
+
start at two drafts, decrease after a partial acceptance and increase toward
|
|
455
|
+
three only after two fully accepted rounds. Per-request state stays with the
|
|
456
|
+
source. Keep one draft as the floor for this experiment. Qwen MTP's current
|
|
457
|
+
`draft(n<=0)` returns without appending its pending cache row, and the
|
|
458
|
+
zero-draft commit does not add that row. A skip/recovery policy therefore
|
|
459
|
+
needs an explicit cache-advancement design before it can preserve the source's
|
|
460
|
+
alignment. This is a source-audit constraint on a future policy, not a failure
|
|
461
|
+
in the current fixed-positive-length path. Compare complete native/HTTP
|
|
462
|
+
requests and memory against both fixed gamma and non-spec generation on new
|
|
463
|
+
frozen prompts before retaining an adaptive policy.
|
|
464
|
+
|
|
465
|
+
The subsequent three-arm serving gate freezes three new prompts and the
|
|
466
|
+
adaptive rule before measurement. It uses the lossless interleaved packed
|
|
467
|
+
artifact, variant 13 and shared affine dispatch, with one serial request at a
|
|
468
|
+
time, temperature zero, seed 42, thinking disabled, no prompt/SSD cache and a
|
|
469
|
+
128-token budget. Six balanced-order blocks start 18 fresh servers. All exit
|
|
470
|
+
cleanly; measured sources remain fixed. Across 126 warm/measured responses,
|
|
471
|
+
all 84 native-versus-MTP text, prompt-count and finish-reason comparisons
|
|
472
|
+
match. The 12 JSON completion-count differences remain in the report. Native
|
|
473
|
+
`generateInner` counts the stopping EOS before excluding it from content;
|
|
474
|
+
`specRunInner` excludes it from both emitted content and `generatedTokens`.
|
|
475
|
+
Thus JSON reports 112 versus 111 despite identical response text. Compare
|
|
476
|
+
complete wall time for that cell, not the differently counted decode rates.
|
|
477
|
+
|
|
478
|
+
On the M4 Pro 24 GB, these are diagnostic medians in seconds:
|
|
479
|
+
|
|
480
|
+
| Frozen prompt | Native | Fixed two drafts | Adaptive one to three drafts |
|
|
481
|
+
|---|---:|---:|---:|
|
|
482
|
+
| TypeScript run-length encoder | 11.912 | 6.690 | 6.483 |
|
|
483
|
+
| Composite database index explanation | 11.914 | 7.217 | 7.062 |
|
|
484
|
+
| Normalize three records to JSON | 10.969 | 5.968 | 5.751 |
|
|
485
|
+
|
|
486
|
+
Every fixed/native, adaptive/native and adaptive/fixed complete-time pair
|
|
487
|
+
improves. Median paired adaptive/fixed time ratios are 0.9698, 0.9782 and
|
|
488
|
+
0.9624 respectively. Fixed MTP also reduces observed TTFT by about 160 ms;
|
|
489
|
+
adaptive/fixed TTFT is essentially unchanged. Median per-server peak RSS is
|
|
490
|
+
12,370,534,400 bytes native, 13,045,129,216 fixed and 13,029,392,384 adaptive.
|
|
491
|
+
The adaptive source exercises widths one, two and three; its policy state is
|
|
492
|
+
per request through the existing provider interface. The earlier broad corpus
|
|
493
|
+
and these held-out cells still do not establish a universal MTP gain. Code
|
|
494
|
+
and explanation stop at the token budget, so this is not a task-quality eval.
|
|
495
|
+
Machine preflight fails on existing swap use. Fixed MTP remains opt-in and
|
|
496
|
+
the adaptive wrapper is research-only. Raw evidence and source identities are
|
|
497
|
+
`reports/qwen38-rd/mtp-heldout-{plan,http,http-review}.json`. The native follow-up runs both arm orders and preserves every emitted token
|
|
498
|
+
ID in all 18 runs. Its 500 draft rounds include 78 partial-acceptance commits
|
|
499
|
+
and all three adaptive widths. Assertions check source offsets before and
|
|
500
|
+
after each draft and all 488 commits; all 12 sources dispose cleanly. Both
|
|
501
|
+
JSON runs emit exactly 111 IDs on every arm, confirming the separate native
|
|
502
|
+
EOS accounting. This is an alignment and trajectory gate, not a claim of
|
|
503
|
+
identical cache bytes between speculative and serial computation. The first
|
|
504
|
+
native run includes loading, so its times do not form a warm performance
|
|
505
|
+
comparison. Evidence: `reports/qwen38-rd/mtp-heldout-native{,-review}.json`.
|
|
506
|
+
Longer contexts, other quants and both quiet-machine gates remain open.
|
|
507
|
+
|
|
508
|
+
The EOS usage discrepancy is now fixed in the speculative serving loop.
|
|
509
|
+
It counts a stopping EOS from the initial sample, accepted draft, correction,
|
|
510
|
+
bonus or plain-decode fallback without emitting it. A callback or token-budget
|
|
511
|
+
stop before that position excludes the unused suffix. This follows the pinned
|
|
512
|
+
mlx-lm `stream_generate` final `generation_tokens=n+1` accounting. Nine
|
|
513
|
+
model-free cases reproduce seven old failures and cover both early-stop
|
|
514
|
+
controls. All typechecks, hygiene and 1,812 model-free tests pass. The fresh
|
|
515
|
+
three-arm HTTP gate completes 21 requests with fixed sources, clean exits and
|
|
516
|
+
no text, prompt-count, completion-count or finish-reason differences. JSON
|
|
517
|
+
now reports 112 tokens on every arm. This single block checks the fix; the
|
|
518
|
+
historical timing reports retain their original counts. Evidence:
|
|
519
|
+
`spec-token-counts-{before,after,typecheck,hygiene,model-free}.txt` and
|
|
520
|
+
`mtp-post-eos-http{,-review}.json` under the campaign reports.
|
|
521
|
+
|
|
522
|
+
An observed EOS now survives the method and serving adapters as
|
|
523
|
+
`finishReason: "stop"`. Native and speculative loops record it explicitly;
|
|
524
|
+
the adapters use the observed cause before their legacy count-based fallback.
|
|
525
|
+
Tool-call and explicit text-stop handling retain precedence. The first HTTP
|
|
526
|
+
replay exposed two additional serving overrides: at the known 112-token JSON
|
|
527
|
+
EOS budget, it still returned `"length"`. Three regression tests reproduced
|
|
528
|
+
those failures before the adapter fix. The final fresh replay completes all
|
|
529
|
+
21 requests with fixed sources, clean exits and unchanged text/counts.
|
|
530
|
+
Native, fixed and adaptive MTP now report `"stop"` for that JSON response
|
|
531
|
+
and `"length"` for the two genuinely budget-limited responses. All typechecks,
|
|
532
|
+
hygiene and 1,820 model-free tests pass. The speculative tests also cover
|
|
533
|
+
bonus EOS at the limit and early callback stops. Evidence:
|
|
534
|
+
`spec-eos-serving-{before,after,typecheck,hygiene,model-free}.txt`, the failed
|
|
535
|
+
`mtp-eos-budget-http{,-review}.json` gate and the passing
|
|
536
|
+
`mtp-eos-budget-integrated-http{,-review}.json` replay.
|
|
537
|
+
|
|
538
|
+
The continuous scheduler already records the terminal cause in `BatchStats`;
|
|
539
|
+
the gateway now preserves it when returning `GenerateStats`. Two regression
|
|
540
|
+
tests reproduce the dropped stop/length field before this correction. The
|
|
541
|
+
expanded model-free tier passes 1,822 tests, with all typechecks and hygiene
|
|
542
|
+
passing. Two fresh default-scheduler servers then preserve all six HTTP
|
|
543
|
+
responses under synchronous/bounded projection evaluation. Both measured JSON
|
|
544
|
+
responses count 112 tokens and finish with `"stop"`; every request reports
|
|
545
|
+
the wire lane `"batched"` and increments the scheduler submission count.
|
|
546
|
+
Sources stay fixed and both servers exit cleanly. The test seeds MLX to 42
|
|
547
|
+
at startup and uses greedy sampling; an explicit request seed would force
|
|
548
|
+
serial placement. The initial harness used the wrong wire enum and stopped
|
|
549
|
+
at its routing assertion. Evidence:
|
|
550
|
+
`spec-eos-continuous-{before,after,typecheck,hygiene,model-free}.txt`,
|
|
551
|
+
`eos-budget-continuous-http.json` and
|
|
552
|
+
`eos-budget-continuous-v2-http{,-review}.json`.
|
|
553
|
+
|
|
554
|
+
The first draft-only quantization screen replaces the MTP head's eight dense
|
|
555
|
+
projections with affine g64 8-bit or 4-bit matmuls. Target computation and
|
|
556
|
+
fixed two-token verification are unchanged. Three fresh servers complete 21
|
|
557
|
+
requests with identical response text, counts and finish reasons, fixed
|
|
558
|
+
sources and clean exits. Relative to the dense draft, 8-bit complete time is
|
|
559
|
+
flat on code and falls about 2% on explanation/JSON; 4-bit falls 0.92%, 3.15%
|
|
560
|
+
and 3.06%. These are single-block diagnostics, requiring repeated pairs.
|
|
561
|
+
Acceptance changes slightly, including an extra code verification round.
|
|
562
|
+
The prototype retains dense buffers in every arm and adds 451/239 MB of
|
|
563
|
+
quantized arrays, so it establishes no memory saving. Its preload explicitly
|
|
564
|
+
disposes providers after request shutdown because normal CLI exit retains
|
|
565
|
+
the resident provider; the initial experiment stopped on that cleanup check.
|
|
566
|
+
Evidence: `mtp-quantized-draft-screen-http.json` and the completed
|
|
567
|
+
`mtp-quantized-draft-screen-v2-http{,-review}.json` under campaign reports.
|
|
568
|
+
|
|
569
|
+
The six-block repeat completes 126 requests across 18 fresh servers with
|
|
570
|
+
all 84 paired response comparisons identical, fixed sources, clean exits
|
|
571
|
+
and explicit provider disposal. Median paired request wall time with the
|
|
572
|
+
4-bit draft falls 1.45% on code, 3.09% on explanation and 2.03% on JSON,
|
|
573
|
+
with six/five/four improving pairs. The 8-bit reductions are 0.34%, 2.16%
|
|
574
|
+
and 2.07%, with six/six/five improving pairs. TTFT is approximately flat.
|
|
575
|
+
The retained dense buffers mean sampled RSS remains unsuitable evidence
|
|
576
|
+
for the benefit of removing those weights. Quantized on-disk artifacts and
|
|
577
|
+
their actual loader/serving/memory gates are next; broader prompts, quants
|
|
578
|
+
and quiet acceptance remain. Evidence:
|
|
579
|
+
`mtp-quantized-draft-repeat-http{,-review}.json`.
|
|
580
|
+
|
|
581
|
+
The existing atomic CPU converter produces separate folded 4-bit and 8-bit
|
|
582
|
+
MTP artifacts with 238,930,944 and 451,267,584 tensor bytes, respectively.
|
|
583
|
+
Both retain all seven dense norm tensors and quantize exactly eight matrices.
|
|
584
|
+
The 4-bit artifact fails the initial comparison against GPU quantization,
|
|
585
|
+
but all 31 saved tensors match fresh CPU quantization or source passthrough.
|
|
586
|
+
CPU/GPU results differ in 16 quantized tensors, totaling 810,927 differing
|
|
587
|
+
bytes. The pinned Python oracle reproduces Bun's CPU and GPU hashes for all
|
|
588
|
+
three fc tensors, and the original companion files are unchanged. MLX 0.31.2
|
|
589
|
+
uses `std::rint` in the CPU affine quantizer and `round` in Metal; these
|
|
590
|
+
artifacts therefore require fresh serving/acceptance gates rather than
|
|
591
|
+
inheriting the GPU prototype's results. The 8-bit artifact passes all 31 CPU
|
|
592
|
+
comparisons, with fixed sources and zero active allocation after cleanup.
|
|
593
|
+
The provider now loads companion projections through the existing
|
|
594
|
+
`QuantizedLinear` when scales are present and retains dense projection
|
|
595
|
+
behavior otherwise. Its resource stack continues to own dense transpose
|
|
596
|
+
views; quantized companions retain no dense matrices. No draft policy or
|
|
597
|
+
target verification changes. The loader and initial serving/memory gates
|
|
598
|
+
pass below. Evidence: `mtp-draft-artifact-q4-{build,audit}.json`,
|
|
599
|
+
`mtp-draft-quant-oracle.json` and `mtp-draft-artifact-q8-build.json`.
|
|
600
|
+
|
|
601
|
+
The integrated loader passes all three typechecks, hygiene and the existing
|
|
602
|
+
1,822-test model-free suite. Its ownership file also passes a new malformed
|
|
603
|
+
quantization-metadata case after earlier dense projections have loaded.
|
|
604
|
+
Nine materialized provider cycles cover dense/8-bit/4-bit heads three times
|
|
605
|
+
each. Loaded allocation is exactly 849,398,784/451,267,584/238,930,944 bytes,
|
|
606
|
+
and every cycle returns to its initial zero active allocation after repeated
|
|
607
|
+
disposal, without GC. The initial ownership helper read the counter before
|
|
608
|
+
lazy weight materialization and failed its coverage assertion at zero/zero;
|
|
609
|
+
it did not reproduce a leak. These are companion allocation measurements,
|
|
610
|
+
not complete-server RSS. The additional typecheck after the new ownership
|
|
611
|
+
case also passes. Evidence: `mtp-quantized-loader-{typecheck,hygiene,model-free}.txt`,
|
|
612
|
+
`mtp-quantized-loader-typecheck-v2.txt`,
|
|
613
|
+
`mtp-quantized-loader-ownership-tests-v2.txt` and
|
|
614
|
+
`mtp-artifact-ownership{,-v2}.json`.
|
|
615
|
+
|
|
616
|
+
The saved-artifact HTTP screen completes 21 responses across three fresh
|
|
617
|
+
servers. All 14 dense-versus-quantized comparisons preserve text, prompt and
|
|
618
|
+
completion counts, and finish reasons. Sources remain fixed; all servers exit
|
|
619
|
+
cleanly. Observation confirms dense projections only in the dense arm and
|
|
620
|
+
quantized projections only in the smaller arms. Explicit provider disposal
|
|
621
|
+
releases exactly each companion's tensor allocation and reaches the same
|
|
622
|
+
11,936,569,478-byte resident-target baseline. This proves the smaller heads
|
|
623
|
+
load without retained dense matrices. Both quantized heads need one extra
|
|
624
|
+
target call on the code prompt, so cheaper projections do not guarantee a
|
|
625
|
+
faster request. Six balanced repeat blocks and instrumented native replay
|
|
626
|
+
are the next gates. Evidence: `mtp-artifact-screen-http{,-review}.json`.
|
|
627
|
+
|
|
628
|
+
Six balanced saved-artifact blocks then complete 126 responses from 18 fresh
|
|
629
|
+
servers, with all 84 paired text/count/finish comparisons exact, fixed sources
|
|
630
|
+
and clean exits. On the M4 Pro 24 GB, median paired complete-time changes
|
|
631
|
+
for code/explanation/JSON are -1.48/-3.67/-2.95% for the 4-bit companion and
|
|
632
|
+
-0.65/-2.29/-1.86% for 8-bit. The 4-bit arm wins 5/6, 6/6 and 5/6 pairs;
|
|
633
|
+
8-bit wins all six pairs per fixture. TTFT is approximately flat. Median
|
|
634
|
+
per-server peak RSS is 13,036,756,992 bytes dense, 12,740,034,560 bytes 8-bit
|
|
635
|
+
and 12,531,777,536 bytes 4-bit. Unlike the earlier GPU projection prototype,
|
|
636
|
+
these arms actually replace the dense companion tensors. Every explicit
|
|
637
|
+
provider disposal releases exactly its companion's allocation to the same
|
|
638
|
+
resident-target baseline. Existing system swap prevents canonical benchmark
|
|
639
|
+
claims. Instrumented native token/logit/state checks, broader contexts/quants,
|
|
640
|
+
pressure and quiet-machine acceptance remain. Evidence:
|
|
641
|
+
`mtp-artifact-repeat-http{,-review}.json`.
|
|
642
|
+
|
|
643
|
+
The subsequent native audit runs all three saved companions against the same
|
|
644
|
+
full-prefill native generation on each short fixture. All nine emitted-ID,
|
|
645
|
+
usage and terminal-cause comparisons pass. For every verification window,
|
|
646
|
+
it clones the incoming target state and replays the same tokens individually
|
|
647
|
+
with the same hidden taps. All 1,184 complete vocabulary-logit rows across
|
|
648
|
+
395 windows match byte-for-byte. At the first three and every sixteenth
|
|
649
|
+
window per request, every live attention and recurrent cache array also
|
|
650
|
+
matches, covering 48 complete state samples. All 386 commits, including 58
|
|
651
|
+
partial accepts, preserve the source position; all nine sources dispose.
|
|
652
|
+
Three fresh children exit cleanly with fixed sources and the same 16,390-byte
|
|
653
|
+
post-model-disposal active counter. This is instrumented correctness evidence,
|
|
654
|
+
not a timing result. Longer fixtures and other quants remain separate gates.
|
|
655
|
+
Evidence: `mtp-artifact-native{,-review}.json`.
|
|
656
|
+
|
|
657
|
+
Two new warehouse-ledger fixtures extend that audit to 688 and 1,968 prompt
|
|
658
|
+
tokens, using matching 2,048-token full-prefill chunks. Dense, 8-bit and 4-bit
|
|
659
|
+
companions preserve all six native/MTP ID, count and finish comparisons.
|
|
660
|
+
All 180 vocabulary-logit rows and 18 sampled complete cache states match;
|
|
661
|
+
54 fully accepted commits preserve position. All twelve native/MTP outputs
|
|
662
|
+
are valid JSON and contain the independently computed requested records.
|
|
663
|
+
Each response counts 30 generation tokens. Sources stay fixed, all three
|
|
664
|
+
children exit cleanly and each leaves the same 16,390-byte active counter.
|
|
665
|
+
This extends correctness coverage; long serving pressure and other quants
|
|
666
|
+
remain open. Evidence: `mtp-artifact-native-context{,-review}.json`.
|
|
667
|
+
|
|
668
|
+
The matching saved-companion HTTP screen completes 15 warm/measured responses
|
|
669
|
+
from three fresh servers at those two context lengths. All ten paired
|
|
670
|
+
text/count/finish comparisons match, with fixed sources and clean exits.
|
|
671
|
+
Both measured fixtures stop at 30 generation tokens. Dense and quantized
|
|
672
|
+
projection counts and explicit provider release pass on every arm. This is
|
|
673
|
+
one serving screen, not a repeated performance or long-agent pressure claim.
|
|
674
|
+
Evidence: `mtp-artifact-context-screen-http.json`.
|
|
675
|
+
|
|
676
|
+
Matched-prefill timing screens now separate native tail-split, native full
|
|
677
|
+
prefill and dense fixed-two MTP in fresh HTTP servers. All 21 packed responses
|
|
678
|
+
preserve text/count/finish across the three arms. The RTN4 screen also completes
|
|
679
|
+
21 responses; MTP matches native full-prefill throughout, while the two measured
|
|
680
|
+
code/explanation tail-split responses retain their known text differences.
|
|
681
|
+
Both screens have fixed sources and clean exits. On the M4 Pro 24 GB, complete
|
|
682
|
+
MTP request times improve 38.8–44.0% on packed and 27.6–38.1% on RTN4 relative
|
|
683
|
+
to native full-prefill. Each is one block, pending balanced repeats. MTP's
|
|
684
|
+
remaining TTFT advantage motivates checking native work before the first yield;
|
|
685
|
+
its decode loop constructs the next step before emitting the current token.
|
|
686
|
+
No default prefill policy changes. Evidence:
|
|
687
|
+
`mtp-matched-prefill-{r6,rtn4}-screen-http{,-review}.json`.
|
|
688
|
+
|
|
689
|
+
The packed matched-prefill repeat completes six balanced three-arm blocks,
|
|
690
|
+
18 servers and 126 warm/measured responses. All text, token counts and finish
|
|
691
|
+
reasons match across native tail-split, native full-prefill and fixed-two
|
|
692
|
+
MTP. Sources stay fixed and all servers exit cleanly. Against native full
|
|
693
|
+
prefill on the M4 Pro 24 GB, MTP changes median paired complete request time
|
|
694
|
+
by -43.22% for code, -38.81% for explanation and -44.92% for JSON. Every pair
|
|
695
|
+
improves. Corresponding TTFT changes are -7.36/-8.42/-5.47%. Median paired
|
|
696
|
+
peak server RSS rises by 684,335,104 bytes, about 653 MiB; this samples startup
|
|
697
|
+
and the whole request sequence, not each fixture's physical-memory demand.
|
|
698
|
+
Native tail-split adds roughly 0.77–0.86% complete time and 7.37–10.46% TTFT
|
|
699
|
+
relative to native full-prefill. Existing swap and background CPU activity
|
|
700
|
+
in the final block keep these results diagnostic. All samples are retained.
|
|
701
|
+
Broader workloads and quiet acceptance remain.
|
|
702
|
+
Evidence: `mtp-matched-prefill-r6-repeat{,-review}.json`.
|
|
703
|
+
|
|
704
|
+
The RTN4 repeat also completes six balanced blocks, 18 servers and 126
|
|
705
|
+
responses with fixed sources and clean exits. MTP and native full-prefill
|
|
706
|
+
match all text/count/finish comparisons. Native tail-split retains the code
|
|
707
|
+
and explanation differences in every block. Relative to native full-prefill
|
|
708
|
+
on the M4 Pro 24 GB, median paired MTP complete-time changes are -34.95% for
|
|
709
|
+
code, -26.95% for explanation and -38.48% for JSON. Code and explanation
|
|
710
|
+
improve in five of six pairs; JSON improves in all six. Median paired sampled
|
|
711
|
+
peak server RSS rises by 850,173,952 bytes, about 811 MiB. The losing block
|
|
712
|
+
overlaps another application's TypeScript and Playwright work, followed by
|
|
713
|
+
system indexing and increased swap. Retain that block and the other samples;
|
|
714
|
+
these diagnostic medians do not substitute for quiet acceptance. Evidence:
|
|
715
|
+
`mtp-matched-prefill-rtn4-repeat{,-review}.json`.
|
|
716
|
+
|
|
717
|
+
The RTN4 cross-quant screen uses its unrotated target and the original
|
|
718
|
+
unrotated mlx-community MTP head, with the same three held-out prompts and
|
|
719
|
+
fixed/adaptive policies. All three servers exit cleanly with fixed sources.
|
|
720
|
+
Usage counts and finish reasons match, but both MTP arms differ from native
|
|
721
|
+
on the measured code and explanation responses. Those timings do not establish
|
|
722
|
+
identical-work speedups. The JSON response matches at 117 counted tokens;
|
|
723
|
+
its single-block complete times are 8.292/5.042/4.776 seconds for native,
|
|
724
|
+
fixed and adaptive MTP. Repeated timing and a first-divergence audit remain.
|
|
725
|
+
Keep the packed result specific to its tested artifact and prompts. Evidence:
|
|
726
|
+
`mtp-rtn4-screen-{plan,http,http-review}.json` under the campaign reports.
|
|
727
|
+
|
|
728
|
+
The RTN4 native follow-up reproduces code/explanation divergence at emitted
|
|
729
|
+
token indices 48/26 with seed 42 and greedy sampling. For each of 140 target
|
|
730
|
+
verification windows, it clones the pre-forward cache and replays the same
|
|
731
|
+
IDs through serial M1 forwards with the same hidden taps. All 420 projected
|
|
732
|
+
logit rows have identical values, and all cache offsets agree. Instrumented
|
|
733
|
+
MTP preserves the uninstrumented emitted IDs on all three fixtures. Thus a
|
|
734
|
+
local verification-window arithmetic difference does not explain these
|
|
735
|
+
responses. Native tail-split versus MTP full-prompt prefill and state
|
|
736
|
+
accumulation require separate controls.
|
|
737
|
+
Sources remain fixed. Evidence: `mtp-rtn4-divergence-v2-audit{,-review}.json`.
|
|
738
|
+
|
|
739
|
+
Two fresh processes isolate the prefill choice using the existing
|
|
740
|
+
`MLX_BUN_PREFILL_TAIL_SPLIT` setting. Native traces show 57+1, 53+1 and 91+1
|
|
741
|
+
prompt forwards with splitting, versus 58, 54 and 92 without it. MTP uses
|
|
742
|
+
full-prompt prefill in both processes. With splitting enabled, the earlier
|
|
743
|
+
code/explanation token differences recur. With splitting disabled, every
|
|
744
|
+
native emitted ID matches MTP on all three fixtures; MTP itself is unchanged
|
|
745
|
+
across processes. Counts are 128/128/117 including JSON EOS. Both children
|
|
746
|
+
exit cleanly and all source hashes stay fixed. This isolates the observed
|
|
747
|
+
trajectory differences to the native prefill policy for these fixtures.
|
|
748
|
+
Retain the original failed comparisons and add a native full-prefill arm
|
|
749
|
+
when measuring MTP, so prefill savings and drafting savings are distinct.
|
|
750
|
+
Changing that policy still needs its own oracle/quality gates. Evidence:
|
|
751
|
+
`mtp-rtn4-prefill-control{,-review}.json` under the campaign reports.
|
|
752
|
+
|
|
753
|
+
Variant 10 further shares small-M scatter work. Six warm AB/BA blocks at
|
|
754
|
+
gamma 2 and 3 on both enumeration and the earlier losing prose prompt now
|
|
755
|
+
retain an observed wall-time gain with identical token IDs. Enumeration
|
|
756
|
+
prefers gamma 3; prose prefers gamma 2. This changes the measured cost of
|
|
757
|
+
verification, not the recorded acceptance sequences. The reports are
|
|
758
|
+
`reports/qwen38-rd/mtp-v10-{planets,prose}-gamma{2,3}.json`. These repeated
|
|
759
|
+
fixtures are still diagnostic, with no general workload or quiet-machine
|
|
760
|
+
verdict. Native request rows are recorded in the eval DB under
|
|
761
|
+
`mlx-bun-native-diagnostic`, which does not replace the status page's
|
|
762
|
+
ordinary inference measurements.
|
|
763
|
+
|
|
764
|
+
The native MTP test now also uses the server's config-plus-tokenizer EOS set
|
|
765
|
+
and records it. The repeated variant-10 enumeration/prose cells contain no
|
|
766
|
+
additional EOS ID within their fixed budget, so that correction does not
|
|
767
|
+
truncate those sequences. The broader corpus audit found chat-EOS overrun
|
|
768
|
+
in extraction only. Its two DB rows and the old strict-fill rows now carry
|
|
769
|
+
explicit exclusion notes; corrected strict-fill reports are recorded separately.
|
|
770
|
+
|
|
771
|
+
The provider-lifetime audit found a separate cleanup bug. The Qwen provider's
|
|
772
|
+
old no-op `dispose()` assumed process-lifetime mapped weights, but `Weights`
|
|
773
|
+
now owns native MLX maps and each dense projection also retains a transpose
|
|
774
|
+
view. Three bounded load/use/source-dispose/provider-dispose cycles retained
|
|
775
|
+
849,398,784 additional MLX bytes each, including after garbage collection.
|
|
776
|
+
The provider now owns a `DisposableStack`: weight-map cleanup is registered
|
|
777
|
+
first, then each derived transpose view, so disposal releases views before
|
|
778
|
+
maps. The same scope unwinds a partially constructed module. Disposal is
|
|
779
|
+
idempotent and opening a source after provider disposal is rejected. The
|
|
780
|
+
caller closes active request sources before unloading their provider, as for
|
|
781
|
+
the other providers. Inference operations and their ordering are unchanged.
|
|
782
|
+
|
|
783
|
+
The real 27B MTP-head probe returns active MLX memory to zero after each of
|
|
784
|
+
three cycles, before GC. Model-free tests also cover map/view release, repeat
|
|
785
|
+
disposal and failure at the final norm after earlier views have been created.
|
|
786
|
+
Evidence: `reports/qwen38-rd/mtp-provider-dispose-{before,after}.json` and
|
|
787
|
+
`mtp-provider-ownership-tests.txt`. All three typechecks, hygiene and the full
|
|
788
|
+
model-free tier pass: 1,803 tests, 10 skips and no failures. The post-change serving
|
|
789
|
+
gate completes all three native/fixed/adaptive servers with clean exits and
|
|
790
|
+
matching response text on the frozen requests. Only the previously documented
|
|
791
|
+
JSON EOS-count difference remains; sources stay fixed. Evidence:
|
|
792
|
+
`reports/qwen38-rd/mtp-post-dispose-http.json`. This addresses drafter unload/replacement;
|
|
793
|
+
it is separate from the already-fixed target-cache disposal and admission work.
|
|
794
|
+
|
|
795
|
+
### 4.8 Paired Qwen MTP conversation state
|
|
796
|
+
|
|
797
|
+
`DraftSource.checkpoint` supplies the method's companion state. Qwen captures
|
|
798
|
+
draft KV and the preceding target hidden row; the executor pairs these with
|
|
799
|
+
target KV/recurrent state and exact processed token IDs in the shared cache.
|
|
800
|
+
Restore requires an exact prefix under the same compatibility namespace.
|
|
801
|
+
RAM hits lend immutable views and preserve the donor. Reuse never trims
|
|
802
|
+
recurrent state to an arbitrary common prefix.
|
|
803
|
+
|
|
804
|
+
The MTP cache is one position behind the target at a prefill boundary. Extending
|
|
805
|
+
it first pairs the next incoming token with the saved preceding hidden row,
|
|
806
|
+
then processes the remaining suffix. Future generation cannot alter the retained
|
|
807
|
+
prefix. The working-tree migration removes the provider's private store and
|
|
808
|
+
separate budget. All tensors use the existing RAM budget, eviction and SSD
|
|
809
|
+
queue. Native bf16/KV4 tests pass on M1 Max and M4 Pro through repeated RAM
|
|
810
|
+
reuse and SSD cache/provider restart. Completed generated-state publication
|
|
811
|
+
and matched performance remain open. Storage and identity details live in
|
|
812
|
+
[kv-cache method companion state](kv-cache.md#method-companion-state).
|
|
813
|
+
|
|
814
|
+
`DraftProvider.grouped` now opens method-owned row state using the same
|
|
815
|
+
companion checkpoint format. Its sampler is injected; the provider retains
|
|
816
|
+
weights while the group owns draft KV and hidden rows. Prepared requests can
|
|
817
|
+
join, retire, reorder, drain and rejoin without exposing tensor layouts to
|
|
818
|
+
scheduling or introducing another cache store. Capture and membership changes
|
|
819
|
+
occur between committed rounds. The request source and group share Qwen's
|
|
820
|
+
attachment encoding and restoration.
|
|
821
|
+
|
|
822
|
+
The provider interface is adopted in the working tree after bf16/KV4/KV8
|
|
823
|
+
full-round controls at B=1/2/4 on both Macs, including rebuilt checkpoints,
|
|
824
|
+
SSD restart continuations and both complete model-free suites. Extraction
|
|
825
|
+
removes padding, so the continuation control also rebuilds its raw state to
|
|
826
|
+
match attention geometry. The initial M4 comparison against retained padding
|
|
827
|
+
changed subsequent draft KV despite matching tokens and target state; that
|
|
828
|
+
result is retained. These are internal refactor controls. Coordinated
|
|
829
|
+
target/draft admission now uses staged state publication, described in
|
|
830
|
+
batching §7. Source
|
|
831
|
+
identity and raw checks are in the composition-baseline reports under
|
|
832
|
+
`mtp-provider-group-manifest.json`.
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
The isolated serving candidate adds method preparation and advancement to the
|
|
836
|
+
existing execution group. The scheduler retains its queue, execution lease and
|
|
837
|
+
admission policy; the method owns target/draft state, acceptance and sampling.
|
|
838
|
+
The gateway asks the binding for its supported grouped methods and forwards
|
|
839
|
+
output metadata and speculative usage. No second MTP scheduler is introduced.
|
|
840
|
+
Its real KV4 HTTP test passes on M1 Max and M4 Pro: four submitted requests
|
|
841
|
+
reach a target verification graph with shape B=4, each returns seeded samples
|
|
842
|
+
and logprobs, and the group drains. B=1 token and acceptance checks compare
|
|
843
|
+
against the existing request producer. Callback-stop, failure, cancellation,
|
|
844
|
+
EOS and shared RAM-prefix checks are included in the extended native gate.
|
|
845
|
+
Mixed HTTP checks on both Macs compose structured JSON, seeded sampling and
|
|
846
|
+
logprobs with ordinary requests in the same MTP group. Shared-method SSD
|
|
847
|
+
restart preserves target/draft state, tokens and acceptance traces on both.
|
|
848
|
+
An extended M1 native check also admits a shorter-lived request with a different
|
|
849
|
+
prompt length while another request is decoding, then repeats the same schedule
|
|
850
|
+
with matching outputs. Admission now publishes target state, draft state and
|
|
851
|
+
executor membership together before releasing superseded state. A reproduced
|
|
852
|
+
cleanup failure formerly left a rejected row active; the admission failure
|
|
853
|
+
handler now retires that row before another method round. The candidate remains
|
|
854
|
+
isolated pending complete composition and paired performance acceptance.
|
|
855
|
+
Timing results and their limits live in benchmarks.md. Evidence:
|
|
856
|
+
`reports/qwen38-closeout/composition-baseline/mtp-serving-method-manifest.json`.
|
|
857
|
+
|
|
858
|
+
For the original private-cache candidate, the M4 Pro unit gate passed 18 tests. Native prompts at 128, 513 and
|
|
859
|
+
2,051 tokens pass six restore checks with unchanged state hashes, token IDs
|
|
860
|
+
and acceptance traces; repeated allocation is stable at each length. The HTTP
|
|
861
|
+
gate completes eight responses with identical text, token counts and finish
|
|
862
|
+
reasons across uncached and cached MTP policies, including repeated cache hits.
|
|
863
|
+
The native restore check also passes with Luke's coding sampling recipe,
|
|
864
|
+
temperature 0.6, top-p 0.95, top-k 20 and seed 42: six unchanged state restores
|
|
865
|
+
and exact repeated continuations. Evidence: `mtp-prefix-luke-sampling.json`.
|
|
866
|
+
These gates establish the initial composition, not complete long-task speed or
|
|
867
|
+
pressure acceptance. The fresh thinking-off Pi task reused saved prefixes and
|
|
868
|
+
finished generation without compaction, but its generated app failed acceptance.
|
|
869
|
+
The task disposition and next frozen configuration are in decode-speed-program
|
|
870
|
+
section 7.7. Raw evidence: `reports/qwen38-rd/mtp-cache-candidate-native.json` and
|
|
871
|
+
`reports/qwen38-rd/mtp-cache-http-gate.json`.
|
|
872
|
+
|
|
873
|
+
## 5. Files
|
|
874
|
+
|
|
875
|
+
- `src/spec/source.ts` — the seam. `src/spec/serve-loop.ts` — the executor.
|
|
876
|
+
- Sources: `two-model.ts`, `assistant-source.ts` (+ `drafter.ts`),
|
|
877
|
+
`dflash-source.ts`, `deepspec-source.ts`, `ngram-source.ts`,
|
|
878
|
+
`qwen-mtp-source.ts`, `glm52-mtp-source.ts`.
|
|
879
|
+
- `src/spec/generate.ts` — the standalone optiq-oracled assistant loop
|
|
880
|
+
(`specGenerate`, full-prompt prefill, throws on ring wrap).
|
|
881
|
+
- `src/spec/dspark/`: `module-dflash.ts` (our module), `deepspec-module.ts`
|
|
882
|
+
(DeepSeek's), `loader.ts`, `data-dflash.ts`, `generate-dflash.ts`,
|
|
883
|
+
`loss.ts`, `sample.ts`, `calibration.ts`, `quantize-drafter.ts`,
|
|
884
|
+
`ab-stats.ts`; `module.ts`/`data.ts`/`generate.ts` = v1 baseline (refused
|
|
885
|
+
for serving).
|
|
886
|
+
- `src/model/gemma4.ts` `hiddenTap`/`captureLayer`; `src/model/qwen3_5.ts`
|
|
887
|
+
`hiddenTap`; `src/model/gemma4-base.ts` `Cache` (`trim(n, bypass)`, the
|
|
888
|
+
spec-round trio); `src/model/qwen3-delta.ts` `SSMCache.specRound*`.
|
|
889
|
+
- `src/server.ts` `detectDraftKind` / `loadContext` (provider selection, γ
|
|
890
|
+
pinning, tokenizer probe, fail-fast pairing probe-open, admission).
|
|
891
|
+
- `scripts/dspark.ts <train|regen|calibrate|quantize|ab>` — the DSpark
|
|
892
|
+
pipeline (one dispatcher, jobs in `scripts/dspark/`):
|
|
893
|
+
```
|
|
894
|
+
bun scripts/dspark.ts regen --topics <topics.txt> --out <data> --max-resp 320
|
|
895
|
+
bun scripts/dspark.ts train --data <data> --out <ckpt> --iters 8000 --batch 8 [--resume] [--ddraft 2560] [--seq-head rnn]
|
|
896
|
+
bun scripts/dspark.ts calibrate --drafter <ckpt> --data <prompts.jsonl>
|
|
897
|
+
bun scripts/dspark.ts quantize <drafter-dir> [--out <dir>] [--bits 4|8] [--group-size 32|64]
|
|
898
|
+
bun scripts/dspark.ts ab --target <id|dir> --drafter-a <dir> --drafter-b <dir> [--num-draft-tokens N] [--n-prompts 32] [--max-drop 3]
|
|
899
|
+
```
|
|
900
|
+
(The standalone measure/compare/bench one-offs — `dspark-measure-dflash`,
|
|
901
|
+
`dspark-deepspec-compare`, `spec-bench`, `spec-dump`, `qwen38-mtp-ab` —
|
|
902
|
+
were deleted 2026-08-23; git history.)
|
|
903
|
+
- Oracles: `scripts/oracle/oracle-spec.py` (optiq spec_generate),
|
|
904
|
+
`scripts/oracle/oracle-dspark-deepspec.py` (DeepSpec temp-0 trace),
|
|
905
|
+
`scripts/oracle/oracle-qwen38-mtp-logits.py` (mlx-vlm drafter logits).
|
|
906
|
+
- Tests: `spec-decode.test.ts` (e4b assistant, exact + knife-edge),
|
|
907
|
+
`spec-decode-12b.test.ts` (long-prefix rollback gate), `spec-serve.test.ts`
|
|
908
|
+
(L1 knife-edge, grammar × spec), `spec-serve-assistant.test.ts`,
|
|
909
|
+
`spec-ngram.test.ts`, `qwen35-spec-ngram.test.ts`,
|
|
910
|
+
`qwen-ssm-specround.test.ts`, `qwen38-mtp.test.ts` (opt-in),
|
|
911
|
+
`dspark-{smoke,infer-loop,rnn,calibration,deepspec,ab-stats}.test.ts`.
|
|
912
|
+
- User-facing mirror: docs/reference/server-config.md (`--draft-model`,
|
|
913
|
+
`--draft-kind`, `--num-draft-tokens`, `--ngram-*`, `--mtp`,
|
|
914
|
+
`MLX_BUN_PREFILL_TAIL_SPLIT`, `MLX_BUN_DSPARK_MINCONF`), cli.md,
|
|
915
|
+
docs/reference/server-config.md.
|
|
916
|
+
|
|
917
|
+
## 6. Composition rules (as served)
|
|
918
|
+
|
|
919
|
+
- Method selection precedes placement. Qualified Qwen MTP uses grouped
|
|
920
|
+
target/draft state, sampling and checkpoint interfaces in the shared
|
|
921
|
+
executor. Other draft providers still use the serial verifier.
|
|
922
|
+
- KV policy stays with the request. Shared Qwen MTP supports bf16 and
|
|
923
|
+
start-zero uniform KV4/KV8/TurboQuant. Unsupported compositions select
|
|
924
|
+
ordinary decoding. Current defaults and qualifications live in server-config.md.
|
|
925
|
+
- Structured output composes (the constrained verify walk).
|
|
926
|
+
- Qwen MTP prefill state uses the shared RAM/SSD cache. The source supplies
|
|
927
|
+
draft KV and the pending hidden row through its checkpoint interface;
|
|
928
|
+
the cache owns retention and persistence. Other speculative sources still
|
|
929
|
+
begin with fresh state until they supply a checkpoint codec. Generated-output
|
|
930
|
+
capture remains R17 work.
|
|
931
|
+
- Native GLM MTP and an explicit `--draft-model`/`--draft-kind` are mutually
|
|
932
|
+
exclusive; one provider per request so two draft histories never advance
|
|
933
|
+
target or grammar state twice.
|
|
934
|
+
|
|
935
|
+
## 7. Token fast-forwarding — lookup, not speculation
|
|
936
|
+
|
|
937
|
+
**No draft/verify contract is involved.** Nothing in this section routes
|
|
938
|
+
through `src/spec/`; there is no drafter, no verify forward, no rollback, and
|
|
939
|
+
no comparison against what the model "would have" produced. It is filed here
|
|
940
|
+
only because the spec lane is the neighbouring mechanism and the two are
|
|
941
|
+
routinely confused. The one fact borrowed from it is that a multi-token
|
|
942
|
+
mid-decode append works — which the grammar jump-forward burst already proved.
|
|
943
|
+
|
|
944
|
+
**The doctrine.** A transformer is a next-token function over a token
|
|
945
|
+
sequence. It has no memory of who wrote which token, so an APPENDED token is
|
|
946
|
+
indistinguishable from a sampled one: injection is pure context extension. When
|
|
947
|
+
the engine already knows the next *m* tokens, consulting the model for them
|
|
948
|
+
buys nothing — it writes them into the KV itself with ONE chunked forward and
|
|
949
|
+
resumes sampling after them. The model is consulted only for tokens it does
|
|
950
|
+
not already know.
|
|
951
|
+
|
|
952
|
+
**What "already knows" means in the strict tier.** A request's
|
|
953
|
+
`tools` array plus the model's own chat template determine large parts of the
|
|
954
|
+
assistant turn: the tool-call opening scaffold, the remainder of a tool name
|
|
955
|
+
after its first disambiguating token, the punctuation from the name to the
|
|
956
|
+
arguments object, and — when the schema has exactly one required property —
|
|
957
|
+
the argument key itself. Those spans are compiled per request by **scaffold
|
|
958
|
+
probing**: render the same conversation several ways, tokenize each, and diff
|
|
959
|
+
the token id sequences. What does not change is the template's fixed scaffold,
|
|
960
|
+
by construction. The compiler knows nothing about tool-call syntax: Qwen3.5's
|
|
961
|
+
`<function=NAME>`/`<parameter=KEY>` XML, a JSON `<tool_call>` template, and
|
|
962
|
+
GLM's `<arg_key>`/`<arg_value>` all compile through the same diff, and a
|
|
963
|
+
template that does not render `tool_calls` (or renders them without the name)
|
|
964
|
+
produces identical probes, an empty diff, and no rows — degrade to no-fill,
|
|
965
|
+
never wrong output. Same technique as `request-prep.ts::stableLenFor` uses on
|
|
966
|
+
the generation primer, applied to the assistant turn.
|
|
967
|
+
|
|
968
|
+
**Serialization and state invariants.**
|
|
969
|
+
- **Every span is sliced from a rendering the model could actually produce** —
|
|
970
|
+
real tool names, real schema keys. The diff only decides WHERE to cut; the
|
|
971
|
+
ids always come from the real-name rendering, so every cut is a token
|
|
972
|
+
boundary of a producible stream. This is not a stylistic preference. Qwen3.5
|
|
973
|
+
renders `<function=get_weather>`, which the tokenizer encodes as
|
|
974
|
+
`< function =get _weather >` — `=get` is ONE token. A probe named
|
|
975
|
+
`zzalphatoolqq` splits the same position as `… = zzalpha…`, inventing a
|
|
976
|
+
boundary after `=`. A scaffold row cut there injects a bare `=`, the model
|
|
977
|
+
then emits `get` where it would have emitted `=get`, and the result is
|
|
978
|
+
byte-identical TEXT over divergent token IDS — with KV that no longer
|
|
979
|
+
matches what a plain decode would have written. Caught on Qwen3.5-0.8B
|
|
980
|
+
(2026-08-31); the regression gate is id containment, not text containment
|
|
981
|
+
(`tests/unit/fill-schema-rows.test.ts`, `tests/parity/fill-strict.test.ts`).
|
|
982
|
+
With one tool the whole header through the sole key is one span; with
|
|
983
|
+
several, the scaffold ends exactly where the real merged name tokens diverge
|
|
984
|
+
(`=get` vs `=search`) and per-tool name rows resume from there.
|
|
985
|
+
- Never `encode(fragment)` in isolation — same reason.
|
|
986
|
+
- Every row is anchored at a token carrying LETTERS (e.g. `<tool_call>`,
|
|
987
|
+
`</parameter`), with leading template-join whitespace dropped from the
|
|
988
|
+
trigger. Markup alone is not enough: `</` opens every closing tag a model
|
|
989
|
+
might write in prose or a code block, so arming on it would inject tool-call
|
|
990
|
+
markup into an HTML snippet.
|
|
991
|
+
- A close row exists only when EVERY tool in the request takes exactly one
|
|
992
|
+
required argument — otherwise the model may still be about to write a second
|
|
993
|
+
`<parameter=…>`, and injecting the close would silently drop it. The
|
|
994
|
+
request's own schema decides whether the row exists.
|
|
995
|
+
- Ending the turn stays the model's decision: every span is cut at the first
|
|
996
|
+
EOS id (the grammar jump burst's missing EOS check is deliberately not
|
|
997
|
+
inherited).
|
|
998
|
+
- A span shorter than 2 ids is rejected — it would save no forward.
|
|
999
|
+
- The cache-alignment invariant: the fill forward carries ONLY the injected
|
|
1000
|
+
ids. The normal step already consumed the trigger token and wrote its KV;
|
|
1001
|
+
forwarding `[trigger, ...ids]` would duplicate a position and silently
|
|
1002
|
+
corrupt both the KV and `PromptCache.put`'s key. `MLX_BUN_FILL_TRACE=1`
|
|
1003
|
+
asserts `cache[0].offset === promptTokens.length + forwarded.length` on both
|
|
1004
|
+
sides of every append.
|
|
1005
|
+
- Injected tokens flow through `CompletionSink.push` one at a time, in the
|
|
1006
|
+
same shape as the jump burst, so `StopMatcher` fires mid-burst exactly where
|
|
1007
|
+
it would have. The append happens BEFORE any of the burst's yields, which is
|
|
1008
|
+
what makes a consumer break mid-burst safe — `forwarded` already describes
|
|
1009
|
+
the cache exactly.
|
|
1010
|
+
|
|
1011
|
+
**The deviation, stated plainly.** Injection bypasses the sampler. At
|
|
1012
|
+
`temperature > 0` a filled reply is not the same draw an unfilled one would
|
|
1013
|
+
have been (the sampler is never asked about those positions). That is a
|
|
1014
|
+
behavior-policy deviation, not a numerics one, and it is why the feature is
|
|
1015
|
+
opt-in (`MLX_BUN_FILL=strict`, default off). At `temperature 0` the strict
|
|
1016
|
+
tier must demonstrate token identity against ordinary generation. A fixed
|
|
1017
|
+
template serialization does not prove that the unconstrained model chooses
|
|
1018
|
+
it. The weights gate is `tests/parity/fill-strict.test.ts`; section 7.4 states
|
|
1019
|
+
the stronger contract needed for a claim of guaranteed deterministic replay.
|
|
1020
|
+
|
|
1021
|
+
**Mechanism.** `src/generate.ts::generateInner` — the same burst shape as the
|
|
1022
|
+
grammar `jumpEmit` branch, with the DEFERRED trigger (fill reads the token the
|
|
1023
|
+
pipelined loop already read back; it does not add grammar's eager readback).
|
|
1024
|
+
On an ASSERT fill the in-flight sample for the next position is dropped
|
|
1025
|
+
unexamined — a discarded pipeline dispatch, counted as `wastedSamples`, not a
|
|
1026
|
+
rejected draft. Grammar and fill are forbidden in the same iteration
|
|
1027
|
+
(asserted).
|
|
1028
|
+
|
|
1029
|
+
### 7.1 One interface, two policies (`src/fill/proposal.ts`)
|
|
1030
|
+
|
|
1031
|
+
Every source that can propose the next tokens implements one interface, and
|
|
1032
|
+
one apply primitive in the decode loop hosts both policies — because the
|
|
1033
|
+
EXPENSIVE half is identical: one chunked forward advancing the KV (and the
|
|
1034
|
+
recurrent SSM state) over the whole span.
|
|
1035
|
+
|
|
1036
|
+
```ts
|
|
1037
|
+
interface ProposalSource { propose(tail: TokenView): Proposal | null }
|
|
1038
|
+
interface Proposal { ids: number[]; policy: "assert" | "verify"; origin: … }
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
- **assert** — the tokens are DETERMINED (a template scaffold). Append and move
|
|
1042
|
+
on: no readback, no checkpoint, no rewind. Strict rows are always assert.
|
|
1043
|
+
- **verify** — the tokens are LIKELY (a session self-copy). Position 0 is
|
|
1044
|
+
checked BEFORE the forward against the in-flight sample — free, and a
|
|
1045
|
+
mismatch costs nothing because nothing has been written yet. Positions
|
|
1046
|
+
1..m−1 are checked against the argmax already sitting in THAT SAME forward's
|
|
1047
|
+
logits, so verification adds no pass over the weights. The rejected tail is
|
|
1048
|
+
rewound and decode resumes at the first disagreement, which reproduces
|
|
1049
|
+
exactly the stream an unfilled run would have produced. A wrong guess costs
|
|
1050
|
+
a rewound forward, never a wrong token.
|
|
1051
|
+
|
|
1052
|
+
**Rewind reuses the spec lane's cache contract, not its executor.** Trimmable
|
|
1053
|
+
caches drop the tail with `trim(n)`; NON-trimmable recurrent caches (SSMCache —
|
|
1054
|
+
gated-DeltaNet conv + recurrent state) go through `specRoundBegin()` before the
|
|
1055
|
+
forward and `specRoundRollback(keep)` after the accept walk, which restores the
|
|
1056
|
+
pre-round snapshot and bit-exactly REPLAYS the accepted prefix (§2). Those are
|
|
1057
|
+
plain `Cache`-interface methods driven by the owning layer, so nothing in
|
|
1058
|
+
`src/spec/serve-loop.ts` is touched. A model whose caches can do neither still
|
|
1059
|
+
gets assert fills; verify proposals are dropped and counted
|
|
1060
|
+
(`usage.fill.verifyUnsupported`). Checkpointing is real work (~48 DeltaNet
|
|
1061
|
+
states on Qwen3.8) and is measured: `usage.fill.checkpointMs`.
|
|
1062
|
+
|
|
1063
|
+
**Remaining fill integration.** Prompt lookup, Qwen MTP and standalone
|
|
1064
|
+
drafting now share the grouped method lifecycle at B1/B>1 for qualified
|
|
1065
|
+
target layouts (see batching.md). DSpark and other provider migrations remain.
|
|
1066
|
+
Unifying fill proposals with draft proposals through one apply primitive is
|
|
1067
|
+
still open; the existing fill implementation retains its cache transaction
|
|
1068
|
+
contract while that method composition is qualified.
|
|
1069
|
+
|
|
1070
|
+
### 7.2 The echo tier (K3c, Lab, `MLX_BUN_FILL=echo`)
|
|
1071
|
+
|
|
1072
|
+
A growing per-request k-gram index over promptIds plus everything emitted
|
|
1073
|
+
(injected tokens included — the model cannot tell them apart, so neither does
|
|
1074
|
+
the index). The structure is the TS port of `GrowingMatcher` from the corpus
|
|
1075
|
+
study: sequence + k-gram → positions, appended incrementally, bucket scan
|
|
1076
|
+
capped at the NEAREST occurrences (`src/fill/echo-index.ts`).
|
|
1077
|
+
|
|
1078
|
+
The lookup is the boring half. The **stopping rule** is the measurement that
|
|
1079
|
+
made the tier worth building:
|
|
1080
|
+
|
|
1081
|
+
- **Branch-point stopping.** A match says where this context occurred before;
|
|
1082
|
+
it says nothing about how far the future agrees with the past. So a span
|
|
1083
|
+
extends only while EVERY nearby occurrence continues the same way, and stops
|
|
1084
|
+
the moment they disagree. That fork is exactly where old-query-vs-new-query
|
|
1085
|
+
divergence lives, and delimiters fall out for free — a closing quote is where
|
|
1086
|
+
histories fork.
|
|
1087
|
+
- **Corroboration decides the policy.** `assert` requires (a) no branch stop,
|
|
1088
|
+
(b) the span ends at a delimiter-class token, AND (c) at least TWO
|
|
1089
|
+
occurrences agreed across the whole span. A single occurrence is a copy, not
|
|
1090
|
+
a pattern: it will happily replay whatever followed it in the transcript —
|
|
1091
|
+
including another role's turn — so it is the model's call. Verified on the
|
|
1092
|
+
0.8B tokenizer against a synthetic agent transcript: an uncorroborated copy
|
|
1093
|
+
ran 30 tokens past `</tool_call>` into a mocked tool RESULT. Under verify
|
|
1094
|
+
that costs one rewound forward; under assert it would have been wrong
|
|
1095
|
+
output. Everything else is `verify`.
|
|
1096
|
+
- **Delimiters** are read off the template by the strict-row compiler (the
|
|
1097
|
+
first non-whitespace token that follows an argument value — `"` for JSON,
|
|
1098
|
+
`</` for Qwen3.5's XML). They clamp ECHO spans only: a strict scaffold
|
|
1099
|
+
legitimately contains the same tokens as structure (`{"name": "` is three
|
|
1100
|
+
quotes deep) and is determined by construction.
|
|
1101
|
+
|
|
1102
|
+
Deterministic value transforms (url-encode, JSON-escape) as
|
|
1103
|
+
`(source-span, transform)` table entries are NOT implemented — the seam is the
|
|
1104
|
+
same `ProposalSource` interface, and the corpus rates that motivate them are in
|
|
1105
|
+
PLAN K3.
|
|
1106
|
+
|
|
1107
|
+
The bar for this tier is NOT token identity (sampling never guaranteed that):
|
|
1108
|
+
it is a paired A/B on task success and wall clock over mocked-replay agent
|
|
1109
|
+
sessions. Default off until that lands.
|
|
1110
|
+
|
|
1111
|
+
**Composition.** Serial lane only, and it never FORCES a request serial — a
|
|
1112
|
+
batch-placed request simply does not fill, because `generate()` is the only
|
|
1113
|
+
site that reads `options.fill`. Refused for: a compiled grammar (forced tokens
|
|
1114
|
+
are its job), `logprobs`/`top_logprobs` (injected tokens have no distribution
|
|
1115
|
+
row — the same rule as `shouldUseGrammarJump`), a user-fixed `seed`
|
|
1116
|
+
(reproducibility: the step index would skip injected positions), media
|
|
1117
|
+
prompts, a mounted draft model, quantized/TurboQuant KV (post-conversion
|
|
1118
|
+
multi-token append is L-generic but unvalidated), and sliding-window models
|
|
1119
|
+
(RotatingKVCache multi-token append is O(window) via `#updateConcat` — one
|
|
1120
|
+
warning, then no fill).
|
|
1121
|
+
|
|
1122
|
+
**Mismatch policy.** If `parseGeneratedToolCalls` rejects the emitted markup
|
|
1123
|
+
on a request whose rows fired, `usage.fill.parseFallback` increments and
|
|
1124
|
+
strict rows disarm. Today the served parse runs at sink flush, i.e. after the
|
|
1125
|
+
generation ends, so within one request this is telemetry; the seam is wired so
|
|
1126
|
+
an incremental parse (or a plan cached across requests) disarms for real.
|
|
1127
|
+
|
|
1128
|
+
### 7.3 Measuring it (K3d, `scripts/fill.ts`)
|
|
1129
|
+
|
|
1130
|
+
Neither tier ships on a vibe. The harness replays RECORDED agent sessions with
|
|
1131
|
+
their tool results mocked verbatim from the JSONL — the transcript IS the
|
|
1132
|
+
environment, a model cannot tell an executed result from a recorded one — so a
|
|
1133
|
+
whole corpus replays deterministically and side-effect-free. The session reader
|
|
1134
|
+
is a straight port of the corpus study's (`reports/k3-replication/analyze.py`
|
|
1135
|
+
`load_session` / `serialize_tool_call`), including its `excludeFromContext` rule
|
|
1136
|
+
and corrupt-line tolerance, so the measured rates in PLAN K3 keep describing
|
|
1137
|
+
what the harness actually sends. The recordings carry no `tools` array, so the
|
|
1138
|
+
harness SYNTHESIZES one per session (union of observed argument keys; keys
|
|
1139
|
+
present in every call marked required) — a reconstruction that is itself part
|
|
1140
|
+
of what the A/B measures.
|
|
1141
|
+
|
|
1142
|
+
```
|
|
1143
|
+
bun scripts/fill.ts replay --sessions <file|dir> --server-url <url>
|
|
1144
|
+
bun scripts/fill.ts ab --sessions <file|dir> --url-a <off> --url-b <echo>
|
|
1145
|
+
bun scripts/fill.ts ab --showcase fixtures/showcase-silicon-exchange.txt --url-a … --url-b …
|
|
1146
|
+
bun scripts/fill.ts report <runs.jsonl> [--by-tool]
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
Fill is a process-wide lever, so the two arms are two SERVERS (A without
|
|
1150
|
+
`MLX_BUN_FILL`, B with it). Turns interleave A, B, A, B … so load drift and
|
|
1151
|
+
thermal state land on both arms, and the verdict pairs on (session, turn, rep).
|
|
1152
|
+
|
|
1153
|
+
**The echo-tier gate**: task-output agreement must not drop within CI AND
|
|
1154
|
+
median wall clock must strictly improve. Agreement is the CALL the model made
|
|
1155
|
+
(tool name + arguments, key-order independent) or a prose turn's normalized
|
|
1156
|
+
text — never token identity, which sampling never promised and which an agent
|
|
1157
|
+
loop does not care about. "Does not drop" is McNemar's paired counts with a
|
|
1158
|
+
one-sided 95% bound, so a tie or a swing inside the noise passes and a real
|
|
1159
|
+
regression does not. The strict tier keeps its own, stricter bar (token
|
|
1160
|
+
identity at temperature 0, `tests/parity/fill-strict.test.ts`).
|
|
1161
|
+
|
|
1162
|
+
**The showcase** runs ONE large tool-dense prompt ×3 interleaved and reports
|
|
1163
|
+
emitted vs decoded tok/s, the fill fraction, time-to-first-tool-call, and the
|
|
1164
|
+
comparison with a one-token-per-forward bandwidth estimate. An emitted rate
|
|
1165
|
+
above that estimate can show amortized weight reads; the injected positions
|
|
1166
|
+
still pass through the state-update graph. `apparent = decoded / (1 − fillFrac)`
|
|
1167
|
+
omits append cost and is not a speedup measurement.
|
|
1168
|
+
|
|
1169
|
+
The verdict math is model-free and unit-tested against a stub server
|
|
1170
|
+
(`tests/research/fill-echo-replay.test.ts`), including the ways it FAILS — a
|
|
1171
|
+
harness that can only produce PASS is not a gate.
|
|
1172
|
+
|
|
1173
|
+
**Live result, 2026-09-02 (M1 Max 32 GB, mjriii/Qwen3.8-27B 4.80 bpw, six
|
|
1174
|
+
recorded pi sessions, 32 paired turns, `--batch 1`).** Two servers do not fit
|
|
1175
|
+
the wired ceiling at 27B, so the arms ran SERIALLY (one server restarted
|
|
1176
|
+
between them; `fill report` now prints the paired verdict for two `replay`
|
|
1177
|
+
files). Two false starts are findings in their own right: (1) the default
|
|
1178
|
+
server lane is the batch scheduler, which never fills — an A/B on it is two
|
|
1179
|
+
identical arms (fill 0.0%, same 4925 tokens); the serial lane needs
|
|
1180
|
+
`--batch 1`. (2) The strict tier compiled ZERO rows on the 27B: Qwen3.x
|
|
1181
|
+
thinking templates end the primer in `<think>\n` and the reply opens with
|
|
1182
|
+
`\n</think>`, the tokenizer merges the two newlines, the primer stops being a
|
|
1183
|
+
token prefix and the compiler bailed silently. Fixed (`schema-rows.ts
|
|
1184
|
+
after()`: text-level boundary, suffix tokenized on its own; 4 rows compile on
|
|
1185
|
+
the 27B). So the run measured the ECHO tier alone:
|
|
1186
|
+
|
|
1187
|
+
| arm | tok/s emitted · decoded | fill | median wall | agreement |
|
|
1188
|
+
|---|---|---|---|---|
|
|
1189
|
+
| fill-off | 8.0 · 8.0 | 0 | 7663 ms | 3.1% |
|
|
1190
|
+
| fill-echo | 8.1 · 7.5 | 7.4% (bash 10%, read 32%) | 7630 ms | 6.3% |
|
|
1191
|
+
|
|
1192
|
+
369 tokens injected in 66 echo events; verify accepted 369 / rejected 1195
|
|
1193
|
+
(76% of proposed span positions), 31 branch stops, 147 ms of checkpoints.
|
|
1194
|
+
Agreement held (B-only agreements 1, A-only 0; the McNemar bound cannot
|
|
1195
|
+
resolve +3 pts at n=32). **Wall clock ×1.00 — the echo gate FAILS**: each
|
|
1196
|
+
event pays a multi-token forward for its whole span and a rewind, and with
|
|
1197
|
+
three of four proposed positions rejected the forward compute per accepted
|
|
1198
|
+
token exceeds a decode step. Echo stays Lab, default off. Levers before a
|
|
1199
|
+
rerun: a longer anchor (`MLX_BUN_FILL_K`), fewer candidates, a span cap near
|
|
1200
|
+
the observed accepted length (~5–6), and asserting corroborated spans instead
|
|
1201
|
+
of verifying. Absolute rates are not quotable: the box ran at 8 tok/s (2.3 GB
|
|
1202
|
+
swapped, the 16 GB server plus the day's leftovers) where 19 is predicted;
|
|
1203
|
+
both arms saw the same conditions.
|
|
1204
|
+
|
|
1205
|
+
**Strict tier, same day, corrected rows** (serial arms back to back, 32 paired
|
|
1206
|
+
turns): fill 5.3% of emitted tokens, all proposed strict spans injected
|
|
1207
|
+
(assert policy, no agreement readback), identical tool calls on every paired turn,
|
|
1208
|
+
median wall ×0.99 (the win is bounded by the fill fraction). The proposal
|
|
1209
|
+
trace (`MLX_BUN_FILL_TRACE=<file.jsonl>`, `bun scripts/fill.ts trace`) is
|
|
1210
|
+
what made the rows right: the first strict run's list showed the scaffold row
|
|
1211
|
+
firing on `</think>` and asserting `<tool_call>…` where the model would have
|
|
1212
|
+
written prose ("Hey", "You", "I") in 10 of 47 template proposals, while the
|
|
1213
|
+
schema-derived name/close rows agreed 9/9. The scaffold trigger now runs
|
|
1214
|
+
through the first token where the call rendering diverges from a plain-
|
|
1215
|
+
content rendering — the model's own `<tool_call>` — and the failure class is
|
|
1216
|
+
gone by construction. Echo-tier list, same tool: 66 events, verify accepted
|
|
1217
|
+
369 / rejected 1195, survival by position falls to ~50% by position 6 — the
|
|
1218
|
+
policy levers named above are what the list points at. **Showcase measured**
|
|
1219
|
+
(same day, `fixtures/showcase-silicon-exchange.txt`, 1024 tokens × 2 reps,
|
|
1220
|
+
serial arms): echo 8.9 tok/s emitted = 8.9 decoded, off 9.0 — fill 0.0%.
|
|
1221
|
+
A fresh build prompt has no earlier session text to copy, so the echo index
|
|
1222
|
+
has nothing to propose; the bandwidth-ceiling demonstration (25.2 tok/s for
|
|
1223
|
+
14.76 GiB at 400 GB/s) needs a turn that REPEATS earlier context. The fixture
|
|
1224
|
+
is the wrong shape for the tier it was written for — a multi-turn edit loop
|
|
1225
|
+
is the honest showcase. Repro to file: turn 8 of session
|
|
1226
|
+
`2026-08-18T04-34-36-341Z_01a01326…` kills the server on either lane (bare
|
|
1227
|
+
MLX C++ exception, three of three runs, `runs/k3/serve-*.log`).
|
|
1228
|
+
|
|
1229
|
+
**Files.** `src/fill/proposal.ts` (the interface + the two policies),
|
|
1230
|
+
`src/fill/fill-session.ts` (sources, clamping, flags, telemetry),
|
|
1231
|
+
`src/fill/schema-rows.ts` (scaffold probing + value delimiters),
|
|
1232
|
+
`src/fill/echo-index.ts` (k-gram index + branch-point rule), the apply
|
|
1233
|
+
primitive in `src/generate.ts`, `fillPlanFor` in `src/serve/request-prep.ts`,
|
|
1234
|
+
the attach + serve-level refusals in `src/serve/chat-stage.ts`, `usage.fill` in
|
|
1235
|
+
`src/serve/{completion-executor,openai-wire}.ts`. Tests:
|
|
1236
|
+
`tests/unit/fill-{session,schema-rows,echo-index,generate-loop}.test.ts`,
|
|
1237
|
+
`tests/serve/fill-{composition,stream}.test.ts`,
|
|
1238
|
+
`tests/parity/fill-strict.test.ts` (weights-gated). Harness: `scripts/fill.ts`
|
|
1239
|
+
+ `scripts/fill/{session-replay,runner,client,metrics,args,replay,ab,report}.ts`,
|
|
1240
|
+
`tests/unit/fill-session-reader.test.ts`,
|
|
1241
|
+
`tests/research/fill-echo-replay.test.ts`,
|
|
1242
|
+
`fixtures/showcase-silicon-exchange.txt`.
|
|
1243
|
+
User-facing mirror: `docs/reference/server-config.md` (`MLX_BUN_FILL`,
|
|
1244
|
+
`MLX_BUN_FILL_MAX_SPAN`, `MLX_BUN_FILL_TRACE`, `MLX_BUN_FILL_K`,
|
|
1245
|
+
`MLX_BUN_FILL_CANDIDATES`, `MLX_BUN_FILL_INDEX_MAX`), `server-api.md`
|
|
1246
|
+
(`usage.fill`).
|
|
1247
|
+
|
|
1248
|
+
### 7.4 No-verification replay contract and next experiments
|
|
1249
|
+
|
|
1250
|
+
The 2026-09-04 Qwen performance program makes this a dedicated track.
|
|
1251
|
+
Production `assert` must remain free of verification. Separate correctness
|
|
1252
|
+
runs may inspect every skipped prediction; those instrumented runs cannot
|
|
1253
|
+
supply the performance number. `applyProposal` in `generate.ts` already
|
|
1254
|
+
implements this separation: `verify` or a trace filename computes logits at
|
|
1255
|
+
all appended positions, while normal `assert` projects only the final hidden
|
|
1256
|
+
state to resume sampling. It still forwards the span through model layers
|
|
1257
|
+
and updates attention KV and GDN state.
|
|
1258
|
+
|
|
1259
|
+
This track appends serialization fixed by the current tool-call state. It
|
|
1260
|
+
does not predict unknown tokens. Once the protocol fixes a field following
|
|
1261
|
+
a selected tool name, that field can become context immediately; sampling
|
|
1262
|
+
resumes where the tool or argument value has a choice. Map fixed protocol
|
|
1263
|
+
fields through the model's own chat template, including the preceding token
|
|
1264
|
+
boundary. Pi's client-side tool representation and a model's wire rendering
|
|
1265
|
+
can differ. Parser state must distinguish structural fields from the same
|
|
1266
|
+
text inside argument strings, quoted examples or code. Offline parity audits
|
|
1267
|
+
validate the implementation; they do not add verification to served asserts.
|
|
1268
|
+
|
|
1269
|
+
There are different possible guarantees:
|
|
1270
|
+
|
|
1271
|
+
| source | what would justify an assertion |
|
|
1272
|
+
|---|---|
|
|
1273
|
+
| Enforced output grammar/protocol | All legal continuations share the emitted token span at the current parser state. Account for tokenizer boundaries and stop conditions. Grammar uniqueness over bytes does not automatically imply unique token IDs. |
|
|
1274
|
+
| Deterministic application transform | An explicit contract fixes the source span and transformation, such as escaping a selected literal. Choosing which source/value to emit remains a model/application decision. |
|
|
1275
|
+
| Cached continuation | Exact model/quant, prefix, recurrent state, positional metadata and sampler/constraint state establish an identical continuation. A matching short suffix is insufficient. |
|
|
1276
|
+
| Template/schema row | Establishes a valid serialization. Without enforced generation constraints, token identity with the model is an empirical gate and can fail even at temperature zero. |
|
|
1277
|
+
| Repeated text | Agreement among observed occurrences is evidence, not certainty; retain the existing Lab distinction for echo assertions. |
|
|
1278
|
+
|
|
1279
|
+
Audit suffix triggers against real parser/role state, tool-name prefixes,
|
|
1280
|
+
optional/extra parameters, `additionalProperties`, multiple calls, quoted
|
|
1281
|
+
markup, code blocks, escaping, EOS and partial-burst cancellation. The original
|
|
1282
|
+
strict row source matched a token suffix without an independent parser-state
|
|
1283
|
+
guard. Do not interpret an all-assert `accepted` count as verified agreement.
|
|
1284
|
+
|
|
1285
|
+
A CPU-only audit with the actual R6 tokenizer/template finds a concrete close
|
|
1286
|
+
boundary error in both thinking modes. The compiled close trigger ends at
|
|
1287
|
+
`</parameter`, before `>`. A valid bash argument containing
|
|
1288
|
+
`printf "%s" "</parameterization>"` therefore triggers the structural close
|
|
1289
|
+
inside argument data. Injecting the current row changes the parsed command to
|
|
1290
|
+
`printf "%s" "`. Both complete calls parse; the suffix alone cannot establish
|
|
1291
|
+
that the value ended. The audit constructs valid token streams from the exact
|
|
1292
|
+
compiled trigger IDs; it does not claim these are observed model trajectories
|
|
1293
|
+
or quantify their frequency. Scaffold/close rows also match reasoning, quoted
|
|
1294
|
+
prose and fenced examples without a role/parser guard. Evidence:
|
|
1295
|
+
fill-parser-boundary-r6.json. Keep fill off by default. Complete the delimiter
|
|
1296
|
+
and establish protocol state before asserting its continuation; runtime model
|
|
1297
|
+
verification is not the remedy for a missing structural proof.
|
|
1298
|
+
|
|
1299
|
+
An isolated parser prototype now waits for the complete `</parameter>` or
|
|
1300
|
+
`</arg_value>` delimiter and requires a request-local tool-only assistant
|
|
1301
|
+
context, optionally following closed reasoning. It reuses the tool parser
|
|
1302
|
+
with full input consumption and no repair; quoted or fenced examples,
|
|
1303
|
+
nested reasoning, unfinished literals, duplicate XML/GLM keys and requests
|
|
1304
|
+
above the probe budget decline strict fills. Cached row plans create context
|
|
1305
|
+
from the current prompt and do not retain the first request's messages.
|
|
1306
|
+
The CPU-only real-R6-tokenizer audit rejects all sixteen negative contexts
|
|
1307
|
+
and preserves four positive rendered token streams across both thinking
|
|
1308
|
+
modes, including the original `</parameterization>` command. Each positive
|
|
1309
|
+
case injects nineteen tokens in two spans with zero verification events.
|
|
1310
|
+
The broader parser/stream suite passes 138 tests. Evidence:
|
|
1311
|
+
`fill-boundary-tokenizer-audit-v4.json` and `fill-boundary-cpu-tests-v8.log`.
|
|
1312
|
+
The parser and model-owned append method are integrated in the main tree.
|
|
1313
|
+
The gates below distinguish the original short-context integration from
|
|
1314
|
+
the subsequent attention-boundary correction.
|
|
1315
|
+
On the consolidated MLX 0.32.2 core with the interleaved R6 artifact and
|
|
1316
|
+
variant 13, the first weather fixture emits identical tokens but four-token
|
|
1317
|
+
appends change recurrent state and continuation logits. One-token appends
|
|
1318
|
+
restore exact state, all four continuation probes and disposal accounting,
|
|
1319
|
+
with the same twelve injected tokens and zero verification. The earlier
|
|
1320
|
+
four-token acceptance used a different runtime/artifact configuration;
|
|
1321
|
+
it does not establish this combination. Isolate the changed arithmetic
|
|
1322
|
+
before selecting an append shape. Evidence: `fill-boundary-native-r6.json`
|
|
1323
|
+
and `fill-boundary-native-m1-r6.json`. A first-layer operation audit localizes
|
|
1324
|
+
the drift to native affine projections: embedding and normalization match,
|
|
1325
|
+
but MLX's new multirow matvec differs from repeated M1. Broadcast weight
|
|
1326
|
+
views with input shape `[M,1,K]` select native batched M1 instead. All six
|
|
1327
|
+
boundary/chunk cases then match every captured intermediate and both state
|
|
1328
|
+
arrays exactly, without copying stored weights. The arithmetic prototype
|
|
1329
|
+
then passes seven full native fixtures per R6/RTN4 quant, with exact emitted
|
|
1330
|
+
IDs, complete state, four continuation probes, usage and disposal accounting.
|
|
1331
|
+
Runtime verification remains zero. RTN4's negative-intent code-block prompt
|
|
1332
|
+
produces a valid weather call in both arms; its original no-fill expectation
|
|
1333
|
+
is retained as a failed fixture assumption, and the rerun classifies that
|
|
1334
|
+
cell as a generated-call identity control. Forced-token negative contexts
|
|
1335
|
+
remain covered by the tokenizer/parser gates. Evidence:
|
|
1336
|
+
`fill-first-layer-shape-probe.json` and `fill-first-layer-batched-m1.json`.
|
|
1337
|
+
The full-model reports are `fill-boundary-native-batch1-r6.json` and
|
|
1338
|
+
`fill-boundary-native-batch1-v2-rtn4.json`.
|
|
1339
|
+
The original guarded-parser/arithmetic prototype also passes six balanced
|
|
1340
|
+
three-arm HTTP blocks per quant on the loaded M4 Pro 24 GB. All 144 responses
|
|
1341
|
+
match across 36 clean server exits, with fixed effective seed 42, empty prompt
|
|
1342
|
+
caches, identical tool calls and zero verification. Four-token append reduces
|
|
1343
|
+
paired-median complete time for weather/bash fixtures by 9.95%/15.24% on R6
|
|
1344
|
+
and 1.92%/3.34% on RTN4; every corresponding pair improves. M1 append is
|
|
1345
|
+
mostly flat, and RSS supplies no memory-reduction claim. Tool output is
|
|
1346
|
+
buffered until completion here, so these are complete-request and first
|
|
1347
|
+
meaningful-tool-output results, not decode-rate measurements. Source hashes
|
|
1348
|
+
remain fixed across the initial screen and five remaining order blocks.
|
|
1349
|
+
Evidence: `fill-boundary-http-{screen,repeat,review}.json`. The model-owned
|
|
1350
|
+
integration passes the same fourteen native cases and six balanced four-arm
|
|
1351
|
+
HTTP blocks per quant. All 192 responses match across 48 clean server exits.
|
|
1352
|
+
Paired-median weather/bash complete time improves by 9.87%/15.37% on R6
|
|
1353
|
+
and 1.97%/2.77% on RTN4, with every pair improving. Ordinary generation's
|
|
1354
|
+
before/after medians differ by at most 0.23%. These remain loaded M4 Pro
|
|
1355
|
+
diagnostics. The independent-row operator matches sequential M1 across
|
|
1356
|
+
720 shape/bit/group/dtype/layout cases, with unchanged stored weight bytes.
|
|
1357
|
+
The main-source state regression passes on both quants, and eight actual
|
|
1358
|
+
HTTP cancellations recover exact responses and steady active allocations.
|
|
1359
|
+
Evidence: `fill-append-integration.json`,
|
|
1360
|
+
`fill-append-http-integration-review.json`, `fill-append-main-native.json`
|
|
1361
|
+
and `fill-append-lifecycle.json`.
|
|
1362
|
+
|
|
1363
|
+
A longer-context audit finds another native dispatch dependency. MLX 0.32.2
|
|
1364
|
+
changes its attention kernel and two-pass reduction block count according
|
|
1365
|
+
to sequence length and GPU architecture. The rules are in
|
|
1366
|
+
[MLX's attention dispatch](https://github.com/ml-explore/mlx/blob/v0.32.2/mlx/backend/metal/scaled_dot_product_attention.cpp).
|
|
1367
|
+
The qualified GQA-6/D256 path on
|
|
1368
|
+
`applegpu_g16s` must end chunks at inclusive KV lengths 1023, 1024, 8192,
|
|
1369
|
+
32768 and 65536. An unsplit append that crosses a transition can change
|
|
1370
|
+
arithmetic for its earlier positions. The first operation screen reproduces
|
|
1371
|
+
18 failures among 108 cases. The corrected model-owned limit is rechecked
|
|
1372
|
+
after every chunk; MLX still selects the kernels. Other architectures retain
|
|
1373
|
+
single-token appends pending their own qualification.
|
|
1374
|
+
|
|
1375
|
+
All 225 corrected attention cases through 65,537 cached tokens are exact
|
|
1376
|
+
across bf16, fp16 and fp32, and active allocation returns to zero. Full R6
|
|
1377
|
+
and RTN4 models also match eight committed positions from identical prefixes
|
|
1378
|
+
of 1021 and 8189 tokens. Hidden rows, per-position M1 vocabulary projections,
|
|
1379
|
+
complete live state and four ordinary continuation probes match exactly;
|
|
1380
|
+
the borrowed prefix state and post-arm active allocation are unchanged.
|
|
1381
|
+
These teacher-forced checks establish arithmetic identity, not that the
|
|
1382
|
+
model would independently choose those committed IDs. Production regression
|
|
1383
|
+
coverage checks the attention boundaries and dynamic model limit. The final
|
|
1384
|
+
model-free suite passes 1,947 tests with ten skips and no failures; all three
|
|
1385
|
+
typechecks pass. The corrected path also passes eight actual HTTP
|
|
1386
|
+
cancellations, fourteen completed responses and exact steady allocation
|
|
1387
|
+
recovery across both quants. Final serving timing acceptance passes the
|
|
1388
|
+
internal-SSD diagnostic below; held-out, combined, pressure and quiet gates
|
|
1389
|
+
remain open.
|
|
1390
|
+
Evidence: `fill-sdpa-context-probe.json`,
|
|
1391
|
+
`fill-sdpa-context-boundaries.json`, `fill-append-context.json`,
|
|
1392
|
+
`fill-append-final-model-free.log` and `fill-append-lifecycle-boundaries.json`.
|
|
1393
|
+
The final-source timing attempts complete 116 responses with exact content,
|
|
1394
|
+
tool calls and usage comparisons. Two RTN4 first-request warmups hit Metal
|
|
1395
|
+
GPU timeouts, one with fill disabled and one with fill enabled. Neither
|
|
1396
|
+
executes an append forward. Keep both failed requests and the incomplete
|
|
1397
|
+
block in the evidence. Complete three-arm blocks cover six R6 pairs and
|
|
1398
|
+
three RTN4 pairs; every append-versus-native pair improves complete time on
|
|
1399
|
+
both fixtures. The dynamic boundary checks retain the earlier append benefit.
|
|
1400
|
+
Separate recovery, storage and loading controls do not fill missing timing
|
|
1401
|
+
cells. The failure investigation and its limits live in
|
|
1402
|
+
[environment.md](../reference/environment.md#external-storage-loading).
|
|
1403
|
+
Evidence: `fill-append-http-final.json`, `fill-append-http-resume.json` and
|
|
1404
|
+
`fill-append-http-final-partial-review.json`.
|
|
1405
|
+
|
|
1406
|
+
After Josh specifies internal storage for active models, a separate final
|
|
1407
|
+
comparison uses byte-verified R6 and RTN4 copies with ordinary lazy loading.
|
|
1408
|
+
All 96 responses across 24 fresh servers match, with twelve balanced pairs,
|
|
1409
|
+
zero failures, zero verification calls and identical final active allocation.
|
|
1410
|
+
On the M4 Pro 24 GB, the median paired complete-time reductions are 9.83% and
|
|
1411
|
+
15.40% for R6's weather/bash fixtures, and 1.63% and 2.94% for RTN4. Every pair
|
|
1412
|
+
improves on each fixture. Tool output is buffered, so first visible output
|
|
1413
|
+
arrives near completion; these figures do not establish faster ordinary
|
|
1414
|
+
decode. The machine still fails the quiet preflight, so these are diagnostic
|
|
1415
|
+
results. Both external-drive failures remain recorded separately. Evidence:
|
|
1416
|
+
`fill-append-http-internal-repeat.json` and `fill-append-http-internal-review.json`.
|
|
1417
|
+
|
|
1418
|
+
Quantized-KV composition remains disabled. A direct R6 append screen with
|
|
1419
|
+
TurboQuant k8v3 preserves hidden rows, logits, live cache state and four
|
|
1420
|
+
continuations at prefixes 128 and 1021, but exposes retained temporary state
|
|
1421
|
+
views between chunks. `appendFillHidden` now uses the existing
|
|
1422
|
+
`leaseCacheStates` ownership interface, as prefill does. Explicit release
|
|
1423
|
+
removes the extra 21,528,576 and 131,153,920 active bytes in those two checks;
|
|
1424
|
+
both ordinary and fused TurboQuant decoding then have identical post-arm
|
|
1425
|
+
allocation and numerical results. This fixes the research composition's
|
|
1426
|
+
view lifetime; the ordinary served path already excludes this combination.
|
|
1427
|
+
Seven targeted ownership tests and all three typechecks pass. The affine
|
|
1428
|
+
KV4 comparison fails both contexts, first changing recurrent state at layer
|
|
1429
|
+
4 after the first attention layer. Keep its failure and the composition
|
|
1430
|
+
guard. Other quant/cache modes, longer contexts and actual serving
|
|
1431
|
+
performance remain unqualified. Evidence:
|
|
1432
|
+
`fill-append-quant-cache-review.json` and
|
|
1433
|
+
`fill-append-state-ownership-native-test.log`.
|
|
1434
|
+
|
|
1435
|
+
The literal/reasoning checks deliberately decline ambiguous text, including some
|
|
1436
|
+
otherwise valid prose with unmatched quote characters. This is bounded
|
|
1437
|
+
coverage, not proof for every unconstrained model trajectory.
|
|
1438
|
+
|
|
1439
|
+
The schema-choice audit reproduced assertions that selected a first key or
|
|
1440
|
+
closed the call while other valid arguments remained. The compiler now uses
|
|
1441
|
+
a sole required key only when `additionalProperties: false` and no nonempty
|
|
1442
|
+
`patternProperties` map establish that no other key is legal. Optional keys
|
|
1443
|
+
may come before a required key. When no property is required, the common
|
|
1444
|
+
span also includes the empty-argument rendering. This follows the
|
|
1445
|
+
[JSON Schema object applicators](https://json-schema.org/draft/2020-12/json-schema-core#section-10.3.2),
|
|
1446
|
+
where omitting `additionalProperties` leaves those properties unconstrained.
|
|
1447
|
+
Open objects also compare an undeclared-key rendering, even when declared
|
|
1448
|
+
keys share a tokenizer prefix. Twenty-seven regressions across XML, JSON and
|
|
1449
|
+
GLM templates preserve these
|
|
1450
|
+
choices, alongside the existing positive fill cases. This is a compiler
|
|
1451
|
+
correctness fix, with no runtime verification or new inference operation.
|
|
1452
|
+
The real packed-Qwen and RTN4 tokenizers each pass 20 cases across thinking
|
|
1453
|
+
on/off with nonzero row matches. The packed weighted regression also retains
|
|
1454
|
+
token-identical output and nonzero injection with four-token appends. All
|
|
1455
|
+
1,867 model-free tests and three typechecks pass. Parser/role-state proof
|
|
1456
|
+
and the broader session corpus remain. Evidence is
|
|
1457
|
+
`fill-schema-open-objects-before.json`,
|
|
1458
|
+
`fill-schema-choices-original-control-corrected.txt`,
|
|
1459
|
+
`fill-schema-shared-prefix-merged-before.txt`,
|
|
1460
|
+
`fill-schema-choices-model-free-final.txt`,
|
|
1461
|
+
`fill-schema-tokenizer-{r6,rtn4}-final.json` and
|
|
1462
|
+
`fill-schema-choices-weighted-r6.json` in the campaign report directory.
|
|
1463
|
+
|
|
1464
|
+
Run the actual artifact regression with
|
|
1465
|
+
`MLX_BUN_TEST_FILL_MODEL=<local-model-dir> MLX_BUN_FILL=strict bun test tests/parity/fill-strict.test.ts`.
|
|
1466
|
+
It selects one artifact per process and defaults to the small Qwen fixture
|
|
1467
|
+
when no override is set. This is an initial positive-fill identity check;
|
|
1468
|
+
extend it to the held-out 27B session/adversarial corpus before a default.
|
|
1469
|
+
Track token IDs and subsequent logits/state as well as parsed tool arguments.
|
|
1470
|
+
|
|
1471
|
+
On 2026-09-05 the actual packed Qwen3.8-27B passes this regression on both
|
|
1472
|
+
trellis variants 6 and 7. Both runs perform nonzero assert injection with
|
|
1473
|
+
zero verification events and emit the same token IDs as their unfilled
|
|
1474
|
+
control. Logs: `reports/qwen38-rd/strict-fill-variant{6,7}.txt`. This is one
|
|
1475
|
+
weather/tool-call fixture; it does not establish universal determinism or a
|
|
1476
|
+
wall-time win. Test duration includes loading and warmup and is not the
|
|
1477
|
+
performance comparison. The older logs' `baselineForwardSteps` field contains
|
|
1478
|
+
the baseline generated-token count; the test now labels it accordingly.
|
|
1479
|
+
|
|
1480
|
+
The native tests now union config EOS with tokenizer EOS, matching
|
|
1481
|
+
`loadRuntimeModel`. The packed config alone omits the chat terminator. Earlier
|
|
1482
|
+
`strict-fill-v10-{wall,max4-wall}.json` runs continued beyond the first chat
|
|
1483
|
+
turn and are excluded from serving-performance conclusions. This was a test
|
|
1484
|
+
policy error; the server already applies the union. The initial variant-6/7
|
|
1485
|
+
logs also used the config-only stop policy.
|
|
1486
|
+
|
|
1487
|
+
Corrected variant-10 diagnostics warm complete answers and retain six
|
|
1488
|
+
alternating on/off pairs per cap. Both the default cap and the existing
|
|
1489
|
+
`MLX_BUN_FILL_MAX_SPAN=4` cap preserve the first-turn token IDs with nonzero
|
|
1490
|
+
assertions and no verification. Both improve observed request time; the
|
|
1491
|
+
shorter cap injects fewer tokens and avoids an expanded append. These are
|
|
1492
|
+
separate diagnostic sessions with prefill noise, not a default-policy verdict
|
|
1493
|
+
or a direct comparison of truncation against chunking. Corrected reports:
|
|
1494
|
+
`reports/qwen38-rd/strict-fill-v10-cap{32,4}-served-eos.json`.
|
|
1495
|
+
`MLX_BUN_TEST_FILL_REPORT` enables the retained test's paired diagnostic;
|
|
1496
|
+
`MLX_BUN_TEST_FILL_BLOCKS`, `MLX_BUN_TEST_FILL_PROMPT` and
|
|
1497
|
+
`MLX_BUN_TEST_FILL_REVERSE` select its workload and schedule. Reports record
|
|
1498
|
+
the cap, rows, prompt IDs, exact emitted IDs, counters and whole-request time.
|
|
1499
|
+
`MLX_BUN_TEST_FILL_THINKING=0|1` and `MLX_BUN_TEST_FILL_TOKENS` select the
|
|
1500
|
+
thinking policy and completion budget for broader fixtures. The test rejects
|
|
1501
|
+
emitted EOS IDs; its report records the effective stop set.
|
|
1502
|
+
The corrected variant-11 default-cap repeat also preserves first-turn IDs
|
|
1503
|
+
with nonzero injection, zero verification and lower complete-request time.
|
|
1504
|
+
Evidence: `reports/qwen38-rd/strict-fill-v11-cap32-served-eos.json`.
|
|
1505
|
+
The EOS-correct five-case audit (weather control plus four frozen adversarial
|
|
1506
|
+
prompts) preserves all emitted IDs, cache-token records and offsets. Quoted
|
|
1507
|
+
malformed markup and code blocks produce no injections and retain exact
|
|
1508
|
+
state/probe logits. The three injected cases preserve the response but differ
|
|
1509
|
+
in live state after longer appends; subsequent teacher-forced logits differ
|
|
1510
|
+
too. All verification counts remain zero. Explicit seed 42 reproduces those
|
|
1511
|
+
results; temperature-zero sampling uses argmax. Reducing the span cap to four
|
|
1512
|
+
restores exact state and four subsequent probes in the weather/multiple-call
|
|
1513
|
+
fixtures, while injecting fewer tokens. Evidence:
|
|
1514
|
+
`reports/qwen38-rd/fill-adversarial-state-v12-eos-review.json`,
|
|
1515
|
+
`fill-state-v12-cap4-eos.json` and
|
|
1516
|
+
`fill-state-v12-cap{32,4}-seed42.json` in the same directory.
|
|
1517
|
+
|
|
1518
|
+
An isolated follow-up removes generation and sampling entirely. It copies a
|
|
1519
|
+
nine-token span from the unfilled model's recorded output and appends it to
|
|
1520
|
+
byte-identical cloned starting caches. Serial and repeated serial appends,
|
|
1521
|
+
full M3/4 appends, and chunks of at most four tokens agree exactly in state,
|
|
1522
|
+
final logits and the next teacher-forced probe. Full M5/9 appends reproduce
|
|
1523
|
+
the numerical difference. Restoring the saved serial cache preserves its
|
|
1524
|
+
bytes and next logits exactly. Thus copying a cached state works as expected;
|
|
1525
|
+
recomputing known tokens through a different graph shape changes numerics.
|
|
1526
|
+
The packed projection dispatch crosses from small-M matvec to prefill at
|
|
1527
|
+
M=5; that prefill path also rounds reconstructed weights to the activation
|
|
1528
|
+
dtype. This is a compute-path distinction, not a changed token span or seed.
|
|
1529
|
+
Evidence: `reports/qwen38-rd/fill-identical-input-append-review.json`.
|
|
1530
|
+
|
|
1531
|
+
Four-token execution chunks now preserve every asserted token and pass all
|
|
1532
|
+
five complete-generation fixtures at seed 42. Emitted ids, cache-covered ids,
|
|
1533
|
+
live cache bytes and four subsequent teacher-forced logits are exact against
|
|
1534
|
+
ordinary generation. Positive cases still inject 12, 8 and 20 tokens with no
|
|
1535
|
+
verification; both negative cases inject zero. The production implementation
|
|
1536
|
+
passes the same audit, without the prototype model adapter. It is a shared
|
|
1537
|
+
MLX append operation, selected by a captured FillSession setting rather than
|
|
1538
|
+
model inspection in the generation loop. Verify proposals retain a single
|
|
1539
|
+
forward because recurrent rollback records one forward per round. Tests cover
|
|
1540
|
+
budget clamping, one final vocabulary projection, a consumer break inside the
|
|
1541
|
+
burst, cancellation, failed-chunk cleanup and recurrent verification rollback.
|
|
1542
|
+
The setting and default live in server-config.md.
|
|
1543
|
+
|
|
1544
|
+
Six balanced three-arm blocks per positive timing fixture compare ordinary
|
|
1545
|
+
generation, existing full-span fill and four-token execution chunks. The full
|
|
1546
|
+
span is retained in both fill arms; every pair preserves emitted ids and
|
|
1547
|
+
cache-covered ids. The chunked prototype improves complete-request diagnostic
|
|
1548
|
+
timing in every pair, with higher peak allocation than ordinary generation.
|
|
1549
|
+
These are local M4 Pro observations with residual swap, not quiet benchmark
|
|
1550
|
+
rows or a universal determinism guarantee. Six production HTTP server pairs
|
|
1551
|
+
then preserve all 24 paired warm/measured responses, tool calls and injection
|
|
1552
|
+
counts. Every measured fixture pair improves complete-request time. Dispatch
|
|
1553
|
+
counters confirm chunking is used only by the candidate, all twelve servers
|
|
1554
|
+
exit cleanly, and source hashes remain fixed. HTTP uses greedy temperature zero
|
|
1555
|
+
without an explicit seed because explicit HTTP seeds currently exclude fill;
|
|
1556
|
+
the separate native acceptance uses seed 42. Broader held-out/parser, model
|
|
1557
|
+
and quiet/second-Mac gates remain. Evidence:
|
|
1558
|
+
`reports/qwen38-rd/fill-chunked-complete-requests-review.json`,
|
|
1559
|
+
`fill-chunked-state-v12-seed42-review.json`,
|
|
1560
|
+
`fill-integrated-state-v12-seed42-review.json` and
|
|
1561
|
+
`fill-integrated-http-packed-review.json`, plus
|
|
1562
|
+
`fill-chunked-integrated-{unit,model-free,typecheck,hygiene}.txt`.
|
|
1563
|
+
|
|
1564
|
+
Performance experiments: eliminate redundant in-flight sampling/head work,
|
|
1565
|
+
merge adjacent determined spans, reuse compiled schema plans with exact
|
|
1566
|
+
identity keys, and choose append batch size from the packed-trellis M curve.
|
|
1567
|
+
For the discarded head, test scheduling the next model body first, reading
|
|
1568
|
+
the current token while that body runs, then projecting only when the fill
|
|
1569
|
+
decision requires a sampled continuation. The existing graph interface already
|
|
1570
|
+
separates body and head. Cache advancement must be tracked independently of
|
|
1571
|
+
a pending sample. Verify and trace policies still require their sampled
|
|
1572
|
+
position; assert must not acquire runtime verification. Check EOS, cancellation,
|
|
1573
|
+
budget limits and consumer breaks, and measure unfilled tokens for any loss
|
|
1574
|
+
of pipeline overlap before retaining this change.
|
|
1575
|
+
The first staged-body prototype passes 26 lifecycle/verification checks and
|
|
1576
|
+
all five frozen 27B fixtures. Both arms retain strict fill and four-token
|
|
1577
|
+
appends, with compiled decode disabled. Emitted IDs, cache-covered tokens,
|
|
1578
|
+
live cache bytes and all continuation probes match. The positive fixtures
|
|
1579
|
+
retain their injected spans with zero wasted samples and zero verification;
|
|
1580
|
+
the two negative fixtures still inject nothing. This establishes the staged
|
|
1581
|
+
path's tested correctness, not its speed or a universal forced-token proof.
|
|
1582
|
+
Six complete-request pairs per fixture include an enabled-fill negative
|
|
1583
|
+
fixture and an explicit fill-disabled control. All emitted IDs, cache-covered
|
|
1584
|
+
tokens and injection counts match. Timing is approximately flat in every
|
|
1585
|
+
fixture; the disabled control moves by as much as the best positive fixture.
|
|
1586
|
+
Peak allocation is unchanged or slightly higher. Close this staging prototype
|
|
1587
|
+
without integration: removing discarded samples does not establish a useful
|
|
1588
|
+
complete-request gain. Evidence: `reports/qwen38-rd/fill-staged-unit.txt`,
|
|
1589
|
+
`fill-staged-state-v13-seed42-review.json` and
|
|
1590
|
+
`fill-staged-complete-requests-review.json`. Closed staging helpers are removed.
|
|
1591
|
+
Superseded chunking/state/HTTP research helpers are removed; their frozen
|
|
1592
|
+
fixtures, raw reports and the retained production append helper remain.
|
|
1593
|
+
On default variant 6, M>4 triggers dense expansion. Experimental variants
|
|
1594
|
+
11/12 add direct tiles and change that cost curve; span policy needs the
|
|
1595
|
+
selected variant's measured append costs. A longer known span is not
|
|
1596
|
+
necessarily cheaper per token. Inspect cache writes and resume logits in traces, then
|
|
1597
|
+
time with `MLX_BUN_FILL_TRACE` unset. Measure emitted/predicted counts, total
|
|
1598
|
+
wall time, first-tool-call latency and task success. Extend the current
|
|
1599
|
+
grammar/KV/batch/logprobs/seed exclusions individually after their own gates.
|
|
1600
|
+
The full experimental matrix and completion rule are in
|
|
1601
|
+
[decode-speed-program.md](decode-speed-program.md#7-qwen38-27b-research-program).
|
|
1602
|
+
|
|
1603
|
+
### 7.5 Logical jump-ahead prefill
|
|
1604
|
+
|
|
1605
|
+
Proposal by Josh Rossi, recorded 2026-09-08. Research proposal, not an
|
|
1606
|
+
implemented semantic intervention or a measured task-time improvement.
|
|
1607
|
+
This is the canonical write-up and proposed preprint question. The existing
|
|
1608
|
+
fixed-serialization implementation is described in §7.4. The broader
|
|
1609
|
+
proposal deliberately permits a different continuation.
|
|
1610
|
+
|
|
1611
|
+
The governing [product objective](../planning/PRODUCT_ROADMAP.md#product-north-star),
|
|
1612
|
+
in Josh's words, is "The absolute BEST local AI experience on a mac". It
|
|
1613
|
+
covers the entire project, including simple installation, direct application
|
|
1614
|
+
integration and JavaScript/TypeScript development. A preprint is optional.
|
|
1615
|
+
This research serves that objective by finishing tasks sooner, producing
|
|
1616
|
+
better results, or making a more capable model practical on the same hardware.
|
|
1617
|
+
A hypothetical 30% reduction in task completion time would be a meaningful
|
|
1618
|
+
product improvement, not a measured result of this proposal. Evaluate the
|
|
1619
|
+
complete experience, including quality, reliability, latency and memory,
|
|
1620
|
+
rather than treating a token-rate increase or a publication as the goal.
|
|
1621
|
+
|
|
1622
|
+
The model often spends time deciding what to do, writes relevant content
|
|
1623
|
+
in its reasoning, and then generates that content again inside a tool call.
|
|
1624
|
+
Josh proposes letting the runtime recognize the intended action, construct
|
|
1625
|
+
some or all of its tool-call representation, and append it through prefill.
|
|
1626
|
+
The model continues from the supplied history and actual tool result.
|
|
1627
|
+
The runtime does not ask the target model to verify that it would have
|
|
1628
|
+
sampled each supplied token.
|
|
1629
|
+
|
|
1630
|
+
The research question has two parts:
|
|
1631
|
+
|
|
1632
|
+
1. With perfect knowledge of an eligible continuation, how much decode time
|
|
1633
|
+
can be replaced by cheaper prefill and construction work?
|
|
1634
|
+
2. With an imperfect predictor, how often can the model recover from an
|
|
1635
|
+
unsuitable intervention, and what does that recovery cost?
|
|
1636
|
+
|
|
1637
|
+
The deciding quantity is time saved by logical jump-ahead prefill minus time
|
|
1638
|
+
spent on recovery, with intervention overhead included and task quality held
|
|
1639
|
+
to an independently evaluated standard. Prediction errors do not by
|
|
1640
|
+
themselves rule out a useful speedup. Their consequences determine the cost.
|
|
1641
|
+
|
|
1642
|
+
#### Context management is the broader premise
|
|
1643
|
+
|
|
1644
|
+
Josh's framing is that working with LLMs is largely context management.
|
|
1645
|
+
For the local inference system under our control, the runtime owns every
|
|
1646
|
+
token supplied to the model. At each generation step, the abstraction is
|
|
1647
|
+
`token sequence in -> next-token distribution -> sampled token`.
|
|
1648
|
+
The KV and recurrent caches are computed representations of that history,
|
|
1649
|
+
not an independent record of the path the model might have taken.
|
|
1650
|
+
|
|
1651
|
+
The runtime can construct, insert, replace, summarize or remove context.
|
|
1652
|
+
It can choose what the model needs to decide and what software can supply.
|
|
1653
|
+
The full original transcript need not remain the active inference context.
|
|
1654
|
+
It can remain in the audit log while a different sequence drives the next
|
|
1655
|
+
step. The resulting sequence still must be processed consistently by the
|
|
1656
|
+
model and preserve enough information for the task.
|
|
1657
|
+
|
|
1658
|
+
Logical jump-ahead prefill and micro-compaction are two applications of this
|
|
1659
|
+
control. One supplies a continuation before it is redundantly generated.
|
|
1660
|
+
The other retires or rewrites material after its immediate purpose is served.
|
|
1661
|
+
Both should be judged by successful task completion, elapsed time and the
|
|
1662
|
+
cost of recovering from an inadequate context decision.
|
|
1663
|
+
|
|
1664
|
+
#### Mechanism and example
|
|
1665
|
+
|
|
1666
|
+
Suppose the reasoning contains a settled instruction to write `hello.txt`
|
|
1667
|
+
with the contents `hello, world!`. The normal path generates a tool name,
|
|
1668
|
+
argument names, path and escaped contents. The proposed path constructs that
|
|
1669
|
+
representation from the available trace, appends it as assistant tool-call
|
|
1670
|
+
content, and executes the call through the normal tool dispatcher.
|
|
1671
|
+
|
|
1672
|
+
An intervention consists of:
|
|
1673
|
+
|
|
1674
|
+
1. Detect an eligible boundary using only context available at that moment.
|
|
1675
|
+
2. Select an action or source span and construct the continuation, including
|
|
1676
|
+
required escaping and protocol delimiters.
|
|
1677
|
+
3. Append the supplied tokens through the model's state-update path, then
|
|
1678
|
+
resume generation at the next unresolved choice or dispatch a complete call.
|
|
1679
|
+
4. Return the actual tool outcome and allow the model to continue, including
|
|
1680
|
+
correcting an unsuitable action when needed.
|
|
1681
|
+
|
|
1682
|
+
The state update still runs model forward computation. Attention KV,
|
|
1683
|
+
recurrent state, positions and parser state must describe the new sequence.
|
|
1684
|
+
It saves sequential token decisions and can avoid intermediate vocabulary
|
|
1685
|
+
projections and verification work. Copying text from an earlier position
|
|
1686
|
+
does not generally permit copying that position's KV, because its context
|
|
1687
|
+
and position differ. Editing an existing prefix also requires invalidating
|
|
1688
|
+
and recomputing affected state. Appending at the current boundary is the
|
|
1689
|
+
smallest implementation to study.
|
|
1690
|
+
|
|
1691
|
+
There is no hidden record of the alternate tokens the model would have
|
|
1692
|
+
sampled. Given consistent state for the supplied prefix, subsequent decoding
|
|
1693
|
+
conditions on that prefix. This does not imply that every supplied action is
|
|
1694
|
+
appropriate or that the model always recovers. Those are empirical questions.
|
|
1695
|
+
|
|
1696
|
+
#### Perfect prediction and imperfect recovery
|
|
1697
|
+
|
|
1698
|
+
Perfect prediction supplies an upper-bound experiment. Replacing a known
|
|
1699
|
+
span's serial generation with a cheaper append saves time. Span size,
|
|
1700
|
+
context length, kernel behavior and construction overhead determine the
|
|
1701
|
+
amount. Very short spans can cost more to detect and append than to decode.
|
|
1702
|
+
|
|
1703
|
+
For the oracle experiment, use the saved future continuation to choose the
|
|
1704
|
+
span, while requiring the copied source content to exist before the jump.
|
|
1705
|
+
This deliberately uses hindsight to measure opportunity. It is not evidence
|
|
1706
|
+
that an online detector can identify those opportunities. An online policy
|
|
1707
|
+
must receive only the prefix available at the intervention boundary.
|
|
1708
|
+
|
|
1709
|
+
With an imperfect policy, an action may still satisfy the task even when it
|
|
1710
|
+
differs from the saved continuation. Another action may require correction.
|
|
1711
|
+
For example, the runtime supplies `ls` in an unsuitable working directory.
|
|
1712
|
+
The returned listing fails to answer the model's question, so the model
|
|
1713
|
+
changes directory or asks for a different listing. The extra reasoning,
|
|
1714
|
+
request prefill, tool calls and tool execution count as recovery cost.
|
|
1715
|
+
|
|
1716
|
+
Record errors the model fails to notice, persistent loops and unsuccessful
|
|
1717
|
+
tasks too. Recovery is possible, not guaranteed. A retry alone is not proof
|
|
1718
|
+
of recovery; the task must regain useful progress and ultimately meet its
|
|
1719
|
+
acceptance criteria. Identical sampled tokens are not required for success.
|
|
1720
|
+
|
|
1721
|
+
#### Cost model
|
|
1722
|
+
|
|
1723
|
+
For an intervention, define:
|
|
1724
|
+
|
|
1725
|
+
| Symbol | Cost |
|
|
1726
|
+
|---|---|
|
|
1727
|
+
| `D` | Serial decode time avoided for the replaced span |
|
|
1728
|
+
| `P` | Time to prefill the supplied continuation and resume |
|
|
1729
|
+
| `H` | Detection, selection, serialization and other intervention overhead |
|
|
1730
|
+
| `R` | Additional downstream time caused by correction or other consequences |
|
|
1731
|
+
|
|
1732
|
+
The local accounting model is:
|
|
1733
|
+
|
|
1734
|
+
```text
|
|
1735
|
+
net saving = D - P - H - R
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
If a fraction `q` of interventions require recovery, and their mean added
|
|
1739
|
+
cost is `C`, a simplified expectation is:
|
|
1740
|
+
|
|
1741
|
+
```text
|
|
1742
|
+
expected net saving = E[D - P - H] - q * C
|
|
1743
|
+
break-even recovery frequency = E[D - P - H] / C
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
The break-even expression assumes positive `C`, successful eventual recovery
|
|
1747
|
+
and no other downstream differences. Here `q` measures harmful interventions
|
|
1748
|
+
requiring recovery, not disagreement with a hypothetical sampled string.
|
|
1749
|
+
Span length, uncertainty and recovery cost may be correlated; measure their
|
|
1750
|
+
joint behavior rather than multiplying unrelated averages.
|
|
1751
|
+
|
|
1752
|
+
This decomposition explains the mechanism. The authoritative measurement is
|
|
1753
|
+
complete task wall time against a paired baseline. A changed trajectory can
|
|
1754
|
+
alter later reasoning, tool latency, cache reuse and compaction. Those effects
|
|
1755
|
+
belong in the result even when they cannot be assigned to one jump. Do not
|
|
1756
|
+
double-count overlapping spans or add savings from dependent interventions
|
|
1757
|
+
as if each occurred in the original unmodified run.
|
|
1758
|
+
|
|
1759
|
+
#### Evidence and first experiment
|
|
1760
|
+
|
|
1761
|
+
The completed Kanban trace supplies a workload and an offline opportunity
|
|
1762
|
+
analysis. Generation-to-generation duplicate coverage and the narrower
|
|
1763
|
+
reasoning-to-tool coverage are recorded in the
|
|
1764
|
+
[completed Kanban benchmark](../reference/benchmarks.md#completed-fresh-pi-kanban-with-bounded-range-cache-m4-pro-2026-09-08).
|
|
1765
|
+
Those counts retain first occurrences, merge overlapping later positions and
|
|
1766
|
+
exclude replayed request history. They use retokenized payloads rather than
|
|
1767
|
+
the original sampled token stream, so they are opportunity estimates.
|
|
1768
|
+
The longest whole-file repeat was a write followed by a read. It does not
|
|
1769
|
+
establish that the model first wrote that whole file in reasoning.
|
|
1770
|
+
|
|
1771
|
+
Start with these experiment arms:
|
|
1772
|
+
|
|
1773
|
+
| Arm | Purpose |
|
|
1774
|
+
|---|---|
|
|
1775
|
+
| Ordinary decoding | Establish task success, timing and natural retry behavior |
|
|
1776
|
+
| Existing strict fill | Isolate savings from fixed serialization already supported |
|
|
1777
|
+
| Oracle content jumps | Estimate savings with hindsight and no selection mistakes |
|
|
1778
|
+
| Online logical jumps | Measure an implementable policy using only the current prefix |
|
|
1779
|
+
| Controlled mistaken jumps | Measure detection, recovery and failure under known perturbations |
|
|
1780
|
+
|
|
1781
|
+
For the online arm, begin with complete literal content already in the trace
|
|
1782
|
+
and a partially generated tool call that identifies the destination. This
|
|
1783
|
+
reduces the decisions the runtime must infer. Then evaluate earlier action
|
|
1784
|
+
selection as a separate policy. Track changes in opportunity coverage and
|
|
1785
|
+
recovery cost as the policy becomes more permissive.
|
|
1786
|
+
|
|
1787
|
+
Use fresh workspaces and matched prompts, models, quantization, tool access,
|
|
1788
|
+
context limits and hardware conditions. Preserve the same cache and decoding
|
|
1789
|
+
configuration across arms where supported. The Kanban configuration and
|
|
1790
|
+
strict fill currently have composition restrictions; establish a supported
|
|
1791
|
+
common control before attributing a difference to logical jumps. Keep the
|
|
1792
|
+
separate MLX-LM comparison as a reference experiment, not a substitute for
|
|
1793
|
+
this within-engine ablation.
|
|
1794
|
+
|
|
1795
|
+
Record supplied and sampled tokens separately, source-span provenance,
|
|
1796
|
+
intervention boundaries, append cost, selection cost, tool outcomes, extra
|
|
1797
|
+
turns, recovery time, compactions and complete task time. Keep raw generated
|
|
1798
|
+
text and supplied text distinguishable in the audit log, while making the
|
|
1799
|
+
model-visible history consistent with the action actually executed.
|
|
1800
|
+
|
|
1801
|
+
Repeat paired runs over multiple tasks and sampling trials. A matching seed
|
|
1802
|
+
does not preserve the trajectory after an intervention. Evaluate artifacts
|
|
1803
|
+
with independent task checks. Report success rate and the distribution of
|
|
1804
|
+
task times, including slow recoveries, timeouts and failures. Fast failed
|
|
1805
|
+
runs must not improve the reported time-to-success result. Where retries
|
|
1806
|
+
are allowed, include their cost under a predefined retry budget.
|
|
1807
|
+
|
|
1808
|
+
#### Micro-compaction after tool execution
|
|
1809
|
+
|
|
1810
|
+
Josh proposes replacing a long reasoning segment after its associated tool
|
|
1811
|
+
calls have completed. For example, the model may spend roughly 50,000 tokens
|
|
1812
|
+
planning and drafting, then write files and inspect the results. At that
|
|
1813
|
+
boundary, the runtime could replace the reasoning with a concise account of
|
|
1814
|
+
decisions, completed actions, observed results and unresolved work.
|
|
1815
|
+
|
|
1816
|
+
This is selective compaction at a task boundary. It need not wait until the
|
|
1817
|
+
whole context window approaches its limit. A candidate procedure is:
|
|
1818
|
+
|
|
1819
|
+
1. Identify the reasoning segment whose associated actions have completed.
|
|
1820
|
+
2. Preserve current requirements, decisions that still constrain the work,
|
|
1821
|
+
observed failures and pending questions in a compact replacement.
|
|
1822
|
+
3. Retain the relevant tool-call/result pairs and references to written
|
|
1823
|
+
artifacts. Keep the original trace available outside the active context.
|
|
1824
|
+
4. Build the revised token sequence, recompute affected model state, and
|
|
1825
|
+
continue the task from that sequence.
|
|
1826
|
+
|
|
1827
|
+
The replacement may come from a model-generated summary or deterministic
|
|
1828
|
+
extraction of completed actions and results. Compare these methods rather
|
|
1829
|
+
than assuming another long generation is necessary. A successful file write
|
|
1830
|
+
provides an artifact the model can read again; whether that makes the full
|
|
1831
|
+
earlier draft disposable depends on the remaining task. Missing rationale
|
|
1832
|
+
or a discarded constraint can require recovery even when the file survives.
|
|
1833
|
+
|
|
1834
|
+
Removing earlier tokens changes the context for retained later tokens.
|
|
1835
|
+
Ordinary prefix caching can preserve the unchanged prefix, but it cannot
|
|
1836
|
+
generally reuse the old suffix state as if the removed reasoning were still
|
|
1837
|
+
present. Recompute that suffix under the new history. Any method that keeps
|
|
1838
|
+
stale state intentionally is a separate approximation to evaluate.
|
|
1839
|
+
|
|
1840
|
+
Micro-compaction cannot recover the time already spent generating the long
|
|
1841
|
+
reasoning segment. It can reduce later context-processing work and memory
|
|
1842
|
+
use, and may change subsequent generation or avoid a larger compaction.
|
|
1843
|
+
Measure those effects over the remaining task:
|
|
1844
|
+
|
|
1845
|
+
```text
|
|
1846
|
+
net saving = future context-processing and trajectory savings
|
|
1847
|
+
- summary/extraction cost
|
|
1848
|
+
- state-rebuild and management cost
|
|
1849
|
+
- extra recovery cost
|
|
1850
|
+
```
|
|
1851
|
+
|
|
1852
|
+
Because a warm prefix cache already avoids much repeated prefill, removed
|
|
1853
|
+
token counts alone do not establish savings. Context length can still affect
|
|
1854
|
+
subsequent attention work and capacity. Record actual cache reuse, rebuild
|
|
1855
|
+
time, memory, later decode time and rereads caused by missing information.
|
|
1856
|
+
|
|
1857
|
+
Compare the existing compaction policy, post-tool micro-compaction,
|
|
1858
|
+
jump-ahead alone, and their combination. Use the same task checks and failure
|
|
1859
|
+
accounting as the jump experiment. Their gains need not add: removing a
|
|
1860
|
+
reasoning segment also removes a potential source for later content copying.
|
|
1861
|
+
An external source store could retain it, with retrieval cost included.
|
|
1862
|
+
|
|
1863
|
+
#### Related work and the proposed preprint
|
|
1864
|
+
|
|
1865
|
+
Josh identified the connection to structured output and grammars at the
|
|
1866
|
+
outset. [XGrammar](https://github.com/mlc-ai/xgrammar) implements efficient
|
|
1867
|
+
grammar-constrained generation. SGLang's
|
|
1868
|
+
[jump-forward decoding](https://www.lmsys.org/blog/2024-02-05-compressed-fsm/)
|
|
1869
|
+
directly prefills strings fixed by a grammar until the next branch. These
|
|
1870
|
+
are established relatives of fixed-serialization fill.
|
|
1871
|
+
|
|
1872
|
+
[Copy-as-Decode](https://arxiv.org/abs/2604.18170), an April 2026 preprint,
|
|
1873
|
+
is direct prior art for unverified content prefill. The model emits an
|
|
1874
|
+
explicit instruction selecting input line ranges, then the runtime prefills
|
|
1875
|
+
their contents without probabilistic verification. It studies an oracle
|
|
1876
|
+
savings bound and span-selection errors. Editing belongs within this
|
|
1877
|
+
proposal's scope, including when edits are tool calls; the application label
|
|
1878
|
+
does not establish a separate invention. The relevant difference to study
|
|
1879
|
+
is runtime inference from reasoning or a partial call versus an explicit
|
|
1880
|
+
model-selected copy instruction, and the cost of subsequent agent recovery.
|
|
1881
|
+
The paper does not establish agentic multi-file coverage.
|
|
1882
|
+
|
|
1883
|
+
Harness intervention is also established. AI SDK supports
|
|
1884
|
+
[custom tool-call repair](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#tool-call-repair),
|
|
1885
|
+
Pydantic AI exposes
|
|
1886
|
+
[JSON and argument modification hooks](https://github.com/pydantic/pydantic-ai/blob/main/docs/capabilities/custom.md),
|
|
1887
|
+
and OpenClaw can
|
|
1888
|
+
[insert loop warnings and block repeated calls](https://docs.openclaw.ai/tools/loop-detection).
|
|
1889
|
+
These show that an agent system can alter actions or subsequent context
|
|
1890
|
+
outside the sampler. They do not measure this proposal's speedup.
|
|
1891
|
+
|
|
1892
|
+
Exact [speculative sampling](https://proceedings.mlr.press/v202/leviathan23a.html)
|
|
1893
|
+
preserves the target distribution conditional on a given context and sampling
|
|
1894
|
+
policy. Logical jumps choose supplied context without that requirement.
|
|
1895
|
+
An exact speculative decoder could still accelerate free generation between
|
|
1896
|
+
jumps.
|
|
1897
|
+
|
|
1898
|
+
The search so far has not identified a direct demonstration of runtime
|
|
1899
|
+
inference of a tool continuation from current reasoning, unverified prefill
|
|
1900
|
+
of that continuation, and whole-agent evaluation of savings against recovery
|
|
1901
|
+
cost. This is a bounded search finding, not a priority claim or proof of
|
|
1902
|
+
absence. The broader unverified-copy mechanism already has overlapping work.
|
|
1903
|
+
|
|
1904
|
+
A working preprint title is **Logical jump-ahead prefill: trading redundant
|
|
1905
|
+
agent decoding for recovery cost**. The contribution to pursue is a defined
|
|
1906
|
+
intervention mechanism, an oracle opportunity bound, an online policy, and a
|
|
1907
|
+
measured relationship between saved decode time and recovery cost at retained
|
|
1908
|
+
task quality. A negative or workload-dependent result would still answer the
|
|
1909
|
+
question. The current trace analysis motivates that study; it does not yet
|
|
1910
|
+
establish the paper's performance result. A broader literature review and
|
|
1911
|
+
the controlled experiments above remain necessary before claiming novelty
|
|
1912
|
+
or a practical speedup.
|
|
1913
|
+
|
|
1914
|
+
## 8. Open items
|
|
1915
|
+
|
|
1916
|
+
- K3 (fill): the packed 27B positive-fill token-identity fixture passes;
|
|
1917
|
+
held-out/adversarial token and state identity plus a paired wall-time win
|
|
1918
|
+
remain before default-on (§7.4). Echo-tier policy levers before any
|
|
1919
|
+
rerun (§7.3); a multi-turn showcase fixture (the single-prompt one fills
|
|
1920
|
+
0%); the turn-8 server-crash repro (`lab/repro/serve-crash-turn8`).
|
|
1921
|
+
|
|
1922
|
+
- DSpark serving program phases 0, 1d, 1e, 1.5, 2, 3, 4, 5, 6 (§4.5) —
|
|
1923
|
+
every GPU measurement is Josh's shell; Phase 3 (generated-forward tap)
|
|
1924
|
+
and Phase 2 (draftBlock tightening) are agent-runnable.
|
|
1925
|
+
- 27B Track A / Track B (§4.6; PLAN.md boxes) + 14h DSpark cross-check.
|
|
1926
|
+
- Spec × prompt-cache composition (serve-loop v1 bypass).
|
|
1927
|
+
- Per-slot spec under batching (unified-engine frontier row, not this
|
|
1928
|
+
program).
|
|
1929
|
+
- Qwen MTP wall-clock: head quantization / deferred per-draft lm-head
|
|
1930
|
+
sampling (the 0.821× verdict's levers); the TQ-artifact divergence-flag
|
|
1931
|
+
margin analysis (§2.1).
|
|
1932
|
+
- Assistant drafter γ≥2 on 12B: the 262k tied-embedding argmax per draft
|
|
1933
|
+
step is the dominant cost; capping/approximating it could extend the win
|
|
1934
|
+
past γ=1. 26B-MoE unmeasured (expected loss — decode reads top-8/128
|
|
1935
|
+
experts, so per-token target cost is lower than its size).
|
|
1936
|
+
- Optional `strictVerify` (per-position verify, "spec output identical to
|
|
1937
|
+
spec-off") — a product nicety no oracle provides; not implemented; ask.
|
|
1938
|
+
- Verify the Eq 6 / §3.2.1 shapes against the paper PDF (both flagged in
|
|
1939
|
+
code); paper-faithful gated RNN head and survival-product scheduler stay
|
|
1940
|
+
Lab items.
|
|
1941
|
+
|
|
1942
|
+
## History
|
|
1943
|
+
|
|
1944
|
+
- **2026-06-10** — e4b assistant drafter measured a net loss (γ=2 0.78× of
|
|
1945
|
+
54 tok/s, ~23% acceptance); spec ships default-off.
|
|
1946
|
+
- **2026-06-14** — `docs/design/speculative-decoding.md` result: the verify path
|
|
1947
|
+
had targeted the WRONG oracle (per-position lm-head to match stock decode;
|
|
1948
|
+
no real implementation does this) — fixed with `picksBatched`, now
|
|
1949
|
+
bit-exact to optiq `spec_generate` on e4b γ=2 (identical output and
|
|
1950
|
+
accept/reject trace). 12B + `gemma-4-12B-it-assistant-bf16`, batched
|
|
1951
|
+
verify, loaded machine (paired ratios only): γ=1 **1.09×** (42%
|
|
1952
|
+
acceptance), γ=2 0.91× (29%), γ=3 0.72× (23%), γ=4 0.56× (17%) — the
|
|
1953
|
+
earlier "net loss at every γ" (0.96/0.77/0.67/0.52×) was partly the
|
|
1954
|
+
wrong-oracle tax. γ≥2 loses because the 12B drafter is heavy (hidden
|
|
1955
|
+
1024, 16 heads, 262k tied head per draft step). Clean-machine rerun
|
|
1956
|
+
never promoted; 26B not run.
|
|
1957
|
+
- **2026-06-29/30** — DSpark v1 (single-vector, superseded) then faithful
|
|
1958
|
+
v2 (KV injection + Markov + confidence) built; TV-loss off-by-one found;
|
|
1959
|
+
overfit τ 3.24 proves the architecture (M1 Max 32 GB, e4b).
|
|
1960
|
+
- **2026-07-06** — Serve integration: the seam extended to carry target
|
|
1961
|
+
state (TargetView, tapLayers, ctx flow); provider selection by artifact
|
|
1962
|
+
kind; γ pinned to the trained block. Paper components code-complete
|
|
1963
|
+
(pruning, calibration, RNN variant, loop tightening, `dspark` rename +
|
|
1964
|
+
loader). DeepSpec audit: our loss/heads match the paper; the released
|
|
1965
|
+
scheduler is threshold-truncation, not the paper's STS/Alg-1; DeepSpec's
|
|
1966
|
+
temp-0 trace becomes the oracle. Tests 21/21 smoke, 17/17 dspark files,
|
|
1967
|
+
3/3 real-weights serve gate.
|
|
1968
|
+
- **2026-07-07** — DeepSpec port first live run on 12B (τ≈2.8, 26–33%
|
|
1969
|
+
acceptance, wall-clock −3.4× on a loaded M4 Pro 24 GB); the serving
|
|
1970
|
+
program written; 1a/1b/1c landed the same day; oracle prefill convention
|
|
1971
|
+
re-anchored to mlx-lm's spec shape (4/4 token-for-token incl. knife-edge);
|
|
1972
|
+
accepted-draft EOS leak fixed.
|
|
1973
|
+
- **2026-08-17/18** — Qwen native MTP: DeltaNet rollback solved via the
|
|
1974
|
+
spec-round snapshot/replay contract (bit-exact by construction, gated);
|
|
1975
|
+
27B pairing gate token-identical (88% = 30/34, 2.82 tok/forward); quiet
|
|
1976
|
+
M1 Max A/B 0.821× at γ=2 (61% acceptance) — durable negative perf
|
|
1977
|
+
verdict, opt-in only; drafter-logit parity vs mlx-vlm 0.6.14 exact
|
|
1978
|
+
through the chained block. TQ artifact gauntlet: MTP 76% accept / 2.53
|
|
1979
|
+
tok/forward with a divergence flag, not claimed lossless.
|
|
1980
|
+
- **2026-08-19** — 27B program (Track A / Track B) recorded, queued behind
|
|
1981
|
+
the certification suite.
|
|
1982
|
+
- **2026-08-20** — MTP `[1,V]` sampler-shape fix (serve-lane 500 under
|
|
1983
|
+
top-k); GLM native MTP source carries the same note.
|
|
1984
|
+
- **2026-08-23** — DSpark scripts consolidated under `scripts/dspark.ts
|
|
1985
|
+
<job>`; one-off measure/compare/bench scripts deleted (git history); this
|
|
1986
|
+
doc consolidates the three design docs.
|
|
1987
|
+
- **2026-09-02** — K3 live A/B on the 27B (§7.3): echo tier injects 7.4%
|
|
1988
|
+
but wall clock ×1.00 (76% verify rejection) — gate FAILS, stays Lab; batch
|
|
1989
|
+
lane never fills; strict rows were silently empty on thinking templates
|
|
1990
|
+
(primer newline merge) — fixed.
|