mlx-bun 0.5.0 → 1.0.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 +1449 -109
- package/THIRD_PARTY_NOTICES.md +882 -0
- package/bin/mlx-bun.convert.mjs +4 -0
- package/bin/mlx-bun.fuse.mjs +4 -0
- package/bin/mlx-bun.generate.mjs +4 -0
- package/bin/mlx-bun.lora.mjs +4 -0
- package/bin/mlx-bun.mjs +13 -21
- package/bin/mlx-bun.server.mjs +4 -0
- package/bin/mlx-bun.upload.mjs +4 -0
- package/dist/web/app.js +9584 -0
- package/package.json +50 -53
- package/src/cli/ane-install.ts +76 -0
- package/src/cli/args.ts +179 -0
- package/src/cli/engine-entry.ts +68 -0
- package/src/cli/inference.ts +214 -0
- package/src/cli/job-entry.ts +97 -0
- package/src/cli/library-host.ts +117 -0
- package/src/cli/main.ts +57 -0
- package/src/cli/memory-engine.ts +163 -0
- package/src/cli/memory.ts +36 -0
- package/src/cli/mlx-lm-aliases.ts +309 -0
- package/src/cli/model-choice.ts +33 -0
- package/src/cli/model-selection.ts +84 -0
- package/src/cli/module-host.ts +11 -0
- package/src/cli/module-verbs.ts +50 -0
- package/src/cli/numerical-policy.ts +12 -0
- package/src/cli/perplexity.ts +89 -0
- package/src/cli/serve-host.ts +423 -0
- package/src/cli/serve-isolated.ts +217 -0
- package/src/cli/serve-options.ts +106 -0
- package/src/cli/serve-state.ts +205 -0
- package/src/cli/serve.ts +500 -0
- package/src/cli/served-model-host.ts +102 -0
- package/src/cli/server-entry.ts +148 -0
- package/src/cli/serving-unit.ts +177 -0
- package/src/{tui.ts → cli/terminal.ts} +13 -8
- package/src/cli/worker-adapters.ts +34 -0
- package/src/cli/worker-entry.ts +272 -0
- package/src/cli/worker-unit.ts +179 -0
- package/src/engine/adapter-operation.ts +26 -0
- package/src/engine/cache-services.ts +114 -0
- package/src/engine/completion.ts +39 -0
- package/src/engine/generation-gateway.ts +358 -0
- package/src/engine/index.ts +59 -0
- package/src/engine/kv-budget.ts +120 -0
- package/src/engine/media-preparation.ts +232 -0
- package/src/engine/model-binding.ts +24 -0
- package/src/engine/model-host.ts +513 -0
- package/src/engine/model-serving.ts +89 -0
- package/src/{serve → engine}/preparation.ts +7 -5
- package/src/{serve → engine}/session-completion-engine.ts +6 -6
- package/src/engine/telemetry.ts +109 -0
- package/src/hub/downloads.ts +116 -0
- package/src/jobs/db.ts +16 -7
- package/src/jobs/events.ts +29 -0
- package/src/jobs/executable.ts +5 -0
- package/src/jobs/host.ts +51 -0
- package/src/jobs/in-process.ts +55 -0
- package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
- package/src/jobs/runner.ts +150 -231
- package/src/jobs/service.ts +75 -0
- package/src/jobs/sse.ts +59 -23
- package/src/jobs/worker-memory.ts +46 -0
- package/src/jobs/worker-process.ts +163 -0
- package/src/jobs/worker-supervisor.ts +258 -0
- package/src/modules.ts +58 -0
- package/src/publishing/catalog-hub.ts +18 -0
- package/src/publishing/credentials.ts +31 -0
- package/src/publishing/upload.ts +37 -0
- package/src/residency/leased-adapters.ts +21 -0
- package/src/residency/local-records.ts +16 -0
- package/src/residency/model-residency.ts +405 -0
- package/src/residency/record-index.ts +28 -0
- package/src/residency/resident-estimate.ts +50 -0
- package/src/{anthropic.ts → server/anthropic.ts} +3 -81
- package/src/server/cache-routes.ts +36 -0
- package/src/{serve → server}/chat-request.ts +10 -10
- package/src/{serve → server}/chat-stage.ts +14 -14
- package/src/{client.ts → server/client.ts} +28 -2
- package/src/{serve → server}/completion-executor.ts +33 -41
- package/src/server/decision-body.ts +55 -0
- package/src/server/decision-routes.ts +134 -0
- package/src/server/decision-wire.ts +228 -0
- package/src/{serve → server}/discovery-routes.ts +25 -82
- package/src/{serve → server}/http.ts +25 -13
- package/src/{serve → server}/inference-request.ts +5 -5
- package/src/server/job-routes.ts +20 -0
- package/src/server/media-prompt.ts +63 -0
- package/src/server/memory-completion-client.ts +46 -0
- package/src/server/model-routes.ts +192 -0
- package/src/{serve → server}/openai-wire.ts +3 -2
- package/src/{serve → server}/pipeline.ts +1 -1
- package/src/server/prompt-builder.ts +13 -0
- package/src/server/prompt-contracts.ts +17 -0
- package/src/server/proxy-routes.ts +187 -0
- package/src/server/publishing-routes.ts +41 -0
- package/src/{serve → server}/request-plan.ts +6 -8
- package/src/{serve → server}/request-prep.ts +47 -50
- package/src/{serve → server}/responses-client.ts +30 -13
- package/src/{responses.ts → server/responses.ts} +12 -93
- package/src/server/routes.ts +205 -0
- package/src/server/start.ts +72 -0
- package/src/server/status-routes.ts +108 -0
- package/src/{serve → server}/text-completion-stage.ts +25 -4
- package/src/server/text-prompt.ts +12 -0
- package/src/{serve → server}/token-streams.ts +36 -38
- package/src/server/worker-routes.ts +311 -0
- package/src/storage/paths.ts +61 -0
- package/src/web/assets.ts +37 -0
- package/src/web/{src → browser}/api.ts +5 -23
- package/src/web/browser/chat-host.ts +17 -0
- package/src/web/browser/installed-panels.ts +6 -0
- package/src/web/browser/main.ts +55 -0
- package/src/web/browser/markdown.ts +279 -0
- package/src/web/browser/memory-panel.ts +9 -0
- package/src/web/browser/palette.ts +64 -0
- package/src/web/browser/protocol.ts +13 -0
- package/src/web/browser/shell.ts +467 -0
- package/src/web/{src → browser}/status.ts +32 -36
- package/src/web/build.ts +51 -0
- package/src/web/public/app.html +1054 -0
- package/src/web/{sw.js → public/sw.js} +4 -3
- package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
- package/src/web/{vendor → public/vendor}/hljs.js +2 -2
- package/THIRD_PARTY_LICENSES.md +0 -51
- package/docs/README.md +0 -107
- package/docs/archive/hlg-sampling.md +0 -350
- package/docs/archive/investigations/audio-input-plan.md +0 -356
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +0 -3617
- package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
- package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
- package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
- package/docs/archive/investigations/curve-bisector-routing.md +0 -123
- package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
- package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
- package/docs/archive/investigations/dspark-handoff.md +0 -224
- package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
- package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
- package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
- package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
- package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
- package/docs/archive/investigations/lab-build-journal.md +0 -157
- package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
- package/docs/archive/investigations/openjarvis-fit.md +0 -198
- package/docs/archive/investigations/openwiki-evaluation.md +0 -249
- package/docs/archive/investigations/optimization_plan.md +0 -346
- package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
- package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
- package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
- package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
- package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
- package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
- package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
- package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
- package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
- package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
- package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
- package/docs/archive/planning/chat-ui-vision.md +0 -50
- package/docs/archive/planning/chunk-task-roadmap.md +0 -77
- package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
- package/docs/archive/planning/journal.md +0 -316
- package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
- package/docs/archive/planning/pi-integration-review.md +0 -178
- package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
- package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
- package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
- package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
- package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
- package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
- package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
- package/docs/archive/planning/release-notes-v0.4.0.md +0 -45
- package/docs/archive/planning/release-notes-v0.5.0.md +0 -37
- package/docs/archive/planning/web-ui-pass-plan.md +0 -213
- package/docs/archive/planning/website-readme-pass-plan.md +0 -224
- package/docs/design/batching.md +0 -2054
- package/docs/design/decode-speed-program.md +0 -2404
- package/docs/design/dreaming-nightly-pipeline.md +0 -684
- package/docs/design/generic-model-support.md +0 -821
- package/docs/design/kv-cache.md +0 -993
- package/docs/design/orpo-training.md +0 -680
- package/docs/design/speculative-decoding.md +0 -2129
- package/docs/design/turboquant.md +0 -2225
- package/docs/design/unified-engine-frontier-plan.md +0 -1987
- package/docs/design/web-chat-redesign.md +0 -695
- package/docs/planning/PRODUCT_ROADMAP.md +0 -593
- package/docs/planning/ResearchTopics.md +0 -119
- package/docs/planning/public-readiness-handoff.md +0 -244
- package/docs/planning/public-readiness.md +0 -149
- package/docs/planning/video-series.md +0 -421
- package/docs/reference/benchmarks.md +0 -3587
- package/docs/reference/cli.md +0 -646
- package/docs/reference/distribution.md +0 -380
- package/docs/reference/environment.md +0 -494
- package/docs/reference/glossary.md +0 -39
- package/docs/reference/library-api.md +0 -688
- package/docs/reference/memory.md +0 -260
- package/docs/reference/models.md +0 -346
- package/docs/reference/server-api.md +0 -1945
- package/docs/reference/server-config.md +0 -973
- package/docs/reference/training.md +0 -462
- package/docs/reference/troubleshooting.md +0 -158
- package/src/admission-plan.ts +0 -18
- package/src/audio/audiotoolbox.ts +0 -112
- package/src/audio/conformer.ts +0 -655
- package/src/audio/decode.ts +0 -133
- package/src/audio/features.ts +0 -202
- package/src/audio/mic-capture.ts +0 -98
- package/src/audio/silero-vad.ts +0 -379
- package/src/audio/transcode.ts +0 -67
- package/src/audio/whisper-decode.ts +0 -774
- package/src/audio/whisper-fast.ts +0 -732
- package/src/audio/whisper-format.ts +0 -73
- package/src/audio/whisper-mel.ts +0 -185
- package/src/audio/whisper-timing.ts +0 -274
- package/src/audio/whisper-tokenizer.ts +0 -239
- package/src/audio/whisper-transcribe.ts +0 -410
- package/src/backends/mlx/assistant-target.ts +0 -46
- package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
- package/src/backends/mlx/autoregressive.ts +0 -129
- package/src/backends/mlx/batch-group.ts +0 -1874
- package/src/backends/mlx/cache-attachment.ts +0 -67
- package/src/backends/mlx/cache-layout.ts +0 -31
- package/src/backends/mlx/checkpoint-state.ts +0 -53
- package/src/backends/mlx/continuation-persistence.ts +0 -107
- package/src/backends/mlx/continuation-request.ts +0 -43
- package/src/backends/mlx/continuation.ts +0 -77
- package/src/backends/mlx/diffusion.ts +0 -39
- package/src/backends/mlx/draft-target.ts +0 -32
- package/src/backends/mlx/encoder-cache.ts +0 -25
- package/src/backends/mlx/evaluation.ts +0 -105
- package/src/backends/mlx/fill-append.ts +0 -58
- package/src/backends/mlx/fill-group.ts +0 -497
- package/src/backends/mlx/full-kv-row-append.ts +0 -32
- package/src/backends/mlx/full-kv-row-donor.ts +0 -54
- package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
- package/src/backends/mlx/gateway-binding.ts +0 -174
- package/src/backends/mlx/graph.ts +0 -38
- package/src/backends/mlx/kv-codec.ts +0 -9
- package/src/backends/mlx/kv-maintenance.ts +0 -161
- package/src/backends/mlx/kv-tensor-rows.ts +0 -212
- package/src/backends/mlx/memory-client.ts +0 -189
- package/src/backends/mlx/methods.ts +0 -90
- package/src/backends/mlx/mixed-iteration.ts +0 -62
- package/src/backends/mlx/model-prompt.ts +0 -178
- package/src/backends/mlx/model-serving.ts +0 -80
- package/src/backends/mlx/paged-cache-codec.ts +0 -27
- package/src/backends/mlx/prefill-cohort.ts +0 -100
- package/src/backends/mlx/prefill-policy.ts +0 -26
- package/src/backends/mlx/prefill-rows.ts +0 -244
- package/src/backends/mlx/prefill.ts +0 -33
- package/src/backends/mlx/projected-context-rows.ts +0 -72
- package/src/backends/mlx/prompt-input.ts +0 -29
- package/src/backends/mlx/qwen-prompt-input.ts +0 -40
- package/src/backends/mlx/request-policy.ts +0 -20
- package/src/backends/mlx/request-state-policy.ts +0 -54
- package/src/backends/mlx/rollback.ts +0 -92
- package/src/backends/mlx/row-sampling.ts +0 -49
- package/src/backends/mlx/serial-executor.ts +0 -251
- package/src/backends/mlx/speculative-group.ts +0 -438
- package/src/backends/mlx/speculative-round.ts +0 -161
- package/src/backends/mlx/speculative.ts +0 -131
- package/src/backends/mlx/state-rows.ts +0 -104
- package/src/backends/mlx/state-views.ts +0 -46
- package/src/backends/mlx/target-layout-capability.ts +0 -35
- package/src/backends/mlx/text-engine.ts +0 -73
- package/src/backends/mlx/transitioning-kv-rows.ts +0 -173
- package/src/chat-template.ts +0 -382
- package/src/cli.ts +0 -3490
- package/src/config.ts +0 -399
- package/src/contracts/completion.ts +0 -16
- package/src/contracts/execution.ts +0 -70
- package/src/contracts/generation.ts +0 -103
- package/src/contracts/host.ts +0 -7
- package/src/contracts/object-cache.ts +0 -8
- package/src/contracts/pi-provider.ts +0 -30
- package/src/contracts/pi.ts +0 -261
- package/src/contracts/prefix-cache.ts +0 -33
- package/src/contracts/resources.ts +0 -25
- package/src/contracts/sampling.ts +0 -23
- package/src/contracts/scheduling.ts +0 -54
- package/src/contracts/tsconfig.json +0 -14
- package/src/dataset/generators.ts +0 -1087
- package/src/dataset/index.ts +0 -12
- package/src/dataset/job.ts +0 -81
- package/src/dataset/llm.ts +0 -71
- package/src/dataset/registry.ts +0 -528
- package/src/diffusion/diffusion-generate.ts +0 -507
- package/src/download.ts +0 -505
- package/src/embed.ts +0 -77
- package/src/engine/admission.ts +0 -59
- package/src/engine/cancellation.ts +0 -38
- package/src/engine/engine.ts +0 -56
- package/src/engine/execution-coordinator.ts +0 -70
- package/src/engine/execution-plan.ts +0 -64
- package/src/engine/execution-tasks.ts +0 -44
- package/src/engine/namespaced-cache.ts +0 -13
- package/src/engine/resources.ts +0 -53
- package/src/engine/scheduler.ts +0 -92
- package/src/engine/session.ts +0 -247
- package/src/eval/README.md +0 -130
- package/src/eval/ifeval.ts +0 -81
- package/src/eval/kl-prompts.ts +0 -29
- package/src/eval/kl.ts +0 -590
- package/src/eval/perplexity.ts +0 -164
- package/src/eval/quality-db.ts +0 -92
- package/src/eval/reference-logits.ts +0 -207
- package/src/eval/runner.ts +0 -124
- package/src/eval/score.ts +0 -49
- package/src/eval/tasks/bfcl.ts +0 -535
- package/src/eval/tasks/gsm8k.ts +0 -98
- package/src/eval/tasks/hashhop.ts +0 -139
- package/src/eval/tasks/humaneval.ts +0 -275
- package/src/eval/tasks/ifeval.ts +0 -525
- package/src/eval/tasks/mmlu.ts +0 -157
- package/src/eval/ultrafeedback.ts +0 -60
- package/src/evaldb.ts +0 -199
- package/src/expert-io.ts +0 -393
- package/src/expert-offload-build.ts +0 -77
- package/src/expert-offload.ts +0 -76
- package/src/expert-residency.ts +0 -1134
- package/src/expert-usage.ts +0 -415
- package/src/fill/echo-index.ts +0 -188
- package/src/fill/fill-session.ts +0 -463
- package/src/fill/proposal.ts +0 -68
- package/src/fill/schema-rows.ts +0 -506
- package/src/fill/tool-boundary.ts +0 -103
- package/src/fit.ts +0 -249
- package/src/generate.ts +0 -1352
- package/src/grammar.ts +0 -604
- package/src/harness-pi.ts +0 -184
- package/src/hf-push.ts +0 -628
- package/src/hub-rest.ts +0 -242
- package/src/index.ts +0 -63
- package/src/inference/denoising.ts +0 -17
- package/src/inference/draft-acceptance.ts +0 -41
- package/src/inference/draft-output.ts +0 -49
- package/src/inference/graph.ts +0 -33
- package/src/inference/prefill.ts +0 -45
- package/src/inference/rollback.ts +0 -7
- package/src/jobs/index.ts +0 -31
- package/src/jobs/job-entry.ts +0 -107
- package/src/jobs/task-client.ts +0 -19
- package/src/jobs/types.ts +0 -2
- package/src/kv-scheme.ts +0 -279
- package/src/kv-store.ts +0 -1135
- package/src/lab/README.md +0 -22
- package/src/lab/curve/curve-designer.html +0 -303
- package/src/lab/curve/curve-sampler.ts +0 -122
- package/src/lab/expert-trace/expert-trace.ts +0 -69
- package/src/lab/paged-kv/paged-attention.ts +0 -117
- package/src/lab/paged-kv/paged-kv-rows.ts +0 -111
- package/src/lab/paged-kv/paged-kv.ts +0 -336
- package/src/library.ts +0 -40
- package/src/lora.ts +0 -408
- package/src/md-modules.d.ts +0 -54
- package/src/media-fetch.ts +0 -442
- package/src/memory/article.ts +0 -558
- package/src/memory/chunk-validation.ts +0 -150
- package/src/memory/chunk.ts +0 -318
- package/src/memory/cluster.ts +0 -351
- package/src/memory/coverage.ts +0 -134
- package/src/memory/crosslink.ts +0 -0
- package/src/memory/db.ts +0 -253
- package/src/memory/entity.ts +0 -203
- package/src/memory/gate.ts +0 -187
- package/src/memory/history.ts +0 -152
- package/src/memory/ingest.ts +0 -335
- package/src/memory/model.ts +0 -183
- package/src/memory/normalize.ts +0 -671
- package/src/memory/parse.ts +0 -22
- package/src/memory/pipeline.ts +0 -275
- package/src/memory/prompts.ts +0 -65
- package/src/memory/query.ts +0 -334
- package/src/memory/reconcile.ts +0 -734
- package/src/memory/redirect.ts +0 -108
- package/src/memory/reindex.ts +0 -325
- package/src/memory/resolve.ts +0 -307
- package/src/memory/rest.ts +0 -245
- package/src/memory/route.ts +0 -272
- package/src/memory/schedule.ts +0 -165
- package/src/memory/stages.ts +0 -777
- package/src/memory/synthesize.ts +0 -1638
- package/src/memory/tools.ts +0 -749
- package/src/memory/vault.ts +0 -1194
- package/src/memory/wikify.ts +0 -1064
- package/src/mlx/array.ts +0 -425
- package/src/mlx/autograd.ts +0 -304
- package/src/mlx/checkpoint.ts +0 -149
- package/src/mlx/compile.ts +0 -168
- package/src/mlx/custom-vjp.ts +0 -188
- package/src/mlx/ffi.ts +0 -531
- package/src/mlx/materialize.ts +0 -23
- package/src/mlx/metal-kernel.ts +0 -159
- package/src/mlx/normalized-argmax.ts +0 -116
- package/src/mlx/ops.ts +0 -1108
- package/src/mlx/quantized-rows.ts +0 -36
- package/src/mlx/token-bitmask.ts +0 -52
- package/src/mlx/turboquant-kv-decode.ts +0 -82
- package/src/mlx/turboquant-kv-inverse.ts +0 -122
- package/src/mlx/turboquant-ops.ts +0 -861
- package/src/mlx/turboquant-tables.ts +0 -237
- package/src/mmap.ts +0 -90
- package/src/model/artifact-identity.ts +0 -73
- package/src/model/batched-kv.ts +0 -235
- package/src/model/batched-mask.ts +0 -279
- package/src/model/batched-quant.ts +0 -212
- package/src/model/batched-quantized-kv.ts +0 -144
- package/src/model/batched-rotating-quant.ts +0 -353
- package/src/model/batched-rotating-state.ts +0 -149
- package/src/model/batched-rotating.ts +0 -338
- package/src/model/batched-row-storage.ts +0 -179
- package/src/model/batched-ssm.ts +0 -52
- package/src/model/batched-turboquant-kv.ts +0 -94
- package/src/model/compiled-decode.ts +0 -712
- package/src/model/decoded-kv-donor.ts +0 -12
- package/src/model/delayed-quantized-kv.ts +0 -72
- package/src/model/delayed-rotating-quantized-kv.ts +0 -93
- package/src/model/delayed-turboquant-kv.ts +0 -57
- package/src/model/diffusion-gemma.ts +0 -1026
- package/src/model/factory.ts +0 -215
- package/src/model/fingerprint.ts +0 -45
- package/src/model/flash-attention.ts +0 -694
- package/src/model/full-prefill-padding.ts +0 -56
- package/src/model/full-prefill-row.ts +0 -105
- package/src/model/gemma4-base.ts +0 -2370
- package/src/model/gemma4.ts +0 -1395
- package/src/model/generated/gemma4-12b.ts +0 -429
- package/src/model/generated/gemma4-26b.ts +0 -448
- package/src/model/generated/gemma4-e4b.ts +0 -717
- package/src/model/generated/index.ts +0 -18
- package/src/model/glm52-atlas.ts +0 -511
- package/src/model/glm52-cache.ts +0 -792
- package/src/model/glm52-config.ts +0 -262
- package/src/model/glm52-container.ts +0 -305
- package/src/model/glm52-coupling.ts +0 -388
- package/src/model/glm52-dsa.ts +0 -606
- package/src/model/glm52-expert-layout.ts +0 -253
- package/src/model/glm52-layout.ts +0 -152
- package/src/model/glm52-memory.ts +0 -500
- package/src/model/glm52-mla.ts +0 -792
- package/src/model/glm52-moe.ts +0 -223
- package/src/model/glm52-pilot.ts +0 -537
- package/src/model/glm52-reference.ts +0 -445
- package/src/model/glm52-residency.ts +0 -557
- package/src/model/glm52-resident-weights.ts +0 -135
- package/src/model/glm52-streamed-experts.ts +0 -453
- package/src/model/glm52-streamed-metal.ts +0 -610
- package/src/model/glm52-weights.ts +0 -503
- package/src/model/glm52.ts +0 -1132
- package/src/model/implementation.ts +0 -69
- package/src/model/kv-attention-view.ts +0 -70
- package/src/model/minicpm5.ts +0 -376
- package/src/model/profile.ts +0 -468
- package/src/model/quantized-append-attention.ts +0 -31
- package/src/model/qwen-append.ts +0 -12
- package/src/model/qwen-conv.ts +0 -52
- package/src/model/qwen3-delta-state.ts +0 -74
- package/src/model/qwen3-delta.ts +0 -451
- package/src/model/qwen3-moe.ts +0 -404
- package/src/model/qwen3-mrope.ts +0 -250
- package/src/model/qwen3.ts +0 -277
- package/src/model/qwen3_5-checkpoint.ts +0 -164
- package/src/model/qwen3_5.ts +0 -881
- package/src/model/rotating-kv-donor.ts +0 -35
- package/src/model/rotating-kv-layout.ts +0 -262
- package/src/model/rotating-row-transaction.ts +0 -35
- package/src/model/speculative-rotating-kv.ts +0 -101
- package/src/model/ssm-prefill-padding.ts +0 -39
- package/src/model/support.ts +0 -110
- package/src/model/token-groups.ts +0 -66
- package/src/model/trellis-balanced-scatter.ts +0 -51
- package/src/model/trellis-linear.ts +0 -672
- package/src/model/trellis-shared-m.ts +0 -151
- package/src/model/trellis-shared-scatter.ts +0 -144
- package/src/model/trellis-splitk-prefill.ts +0 -158
- package/src/model/trellis-tiled-prefill.ts +0 -136
- package/src/model/trellis-vector-expand.ts +0 -62
- package/src/model/trellis-wide-prefill.ts +0 -94
- package/src/model/turboquant-codec.ts +0 -89
- package/src/model/universal/archs.ts +0 -332
- package/src/model/universal/dense.ts +0 -612
- package/src/model/universal/modules.ts +0 -170
- package/src/model/universal/rope.ts +0 -381
- package/src/model/whisper.ts +0 -435
- package/src/native/expert_io.c +0 -523
- package/src/native/frame_extract.swift +0 -140
- package/src/native/mic_capture.swift +0 -92
- package/src/native-pack.ts +0 -163
- package/src/pi-provider.ts +0 -53
- package/src/pi-session.ts +0 -81
- package/src/pi-terminal.ts +0 -274
- package/src/pi-web.ts +0 -1940
- package/src/preflight.ts +0 -158
- package/src/prompt-cache.ts +0 -682
- package/src/quantize/allocator.ts +0 -627
- package/src/quantize/atomic-output.ts +0 -38
- package/src/quantize/calibration.ts +0 -181
- package/src/quantize/config-writer.ts +0 -145
- package/src/quantize/index.ts +0 -84
- package/src/quantize/job.ts +0 -162
- package/src/quantize/quantizer.ts +0 -666
- package/src/quantize/rotate.ts +0 -603
- package/src/quantize/safetensors-writer.ts +0 -249
- package/src/quantize/sensitivity.ts +0 -216
- package/src/quantize/trellis.ts +0 -586
- package/src/quantize/weight-transform.ts +0 -292
- package/src/registry.ts +0 -602
- package/src/runtime-config.ts +0 -77
- package/src/safetensors.ts +0 -165
- package/src/sampler.ts +0 -1012
- package/src/serve/admin-routes.ts +0 -184
- package/src/serve/assets/curve-terrain.html +0 -105
- package/src/serve/assets/training-inference-map.html +0 -575
- package/src/serve/audio-routes.ts +0 -323
- package/src/serve/aux-routes.ts +0 -132
- package/src/serve/batch-scheduler.ts +0 -8
- package/src/serve/checkpoint-identity.ts +0 -64
- package/src/serve/generation-gateway.ts +0 -537
- package/src/serve/isolate.ts +0 -609
- package/src/serve/kv-budget.ts +0 -14
- package/src/serve/lab-routes.ts +0 -341
- package/src/serve/lane-registry.ts +0 -45
- package/src/serve/model-admin-routes.ts +0 -155
- package/src/serve/model-binding.ts +0 -49
- package/src/serve/model-host.ts +0 -511
- package/src/serve/parent-application.ts +0 -56
- package/src/serve/prompt-response-trace.ts +0 -161
- package/src/serve/response-lease.ts +0 -24
- package/src/serve/session-search.ts +0 -326
- package/src/serve/static-routes.ts +0 -101
- package/src/serve/transcription-server.ts +0 -64
- package/src/serve/transcription-service.ts +0 -502
- package/src/server.ts +0 -1282
- package/src/spec/assistant-rows.ts +0 -93
- package/src/spec/assistant-source.ts +0 -126
- package/src/spec/cache-identity.ts +0 -22
- package/src/spec/deepspec-rows.ts +0 -29
- package/src/spec/deepspec-source.ts +0 -145
- package/src/spec/dflash-source.ts +0 -149
- package/src/spec/drafter.ts +0 -304
- package/src/spec/dspark/ab-stats.ts +0 -136
- package/src/spec/dspark/calibration.ts +0 -112
- package/src/spec/dspark/data-dflash.ts +0 -156
- package/src/spec/dspark/data.ts +0 -201
- package/src/spec/dspark/deepspec-module.ts +0 -776
- package/src/spec/dspark/generate-dflash.ts +0 -245
- package/src/spec/dspark/generate.ts +0 -290
- package/src/spec/dspark/loader.ts +0 -23
- package/src/spec/dspark/loss.ts +0 -213
- package/src/spec/dspark/module-dflash.ts +0 -560
- package/src/spec/dspark/module.ts +0 -540
- package/src/spec/dspark/quantize-drafter.ts +0 -91
- package/src/spec/dspark/sample.ts +0 -112
- package/src/spec/generate.ts +0 -276
- package/src/spec/glm52-mtp-graph.ts +0 -29
- package/src/spec/glm52-mtp-rows.ts +0 -132
- package/src/spec/glm52-mtp-source.ts +0 -318
- package/src/spec/glm52-mtp-state.ts +0 -33
- package/src/spec/ngram-source.ts +0 -226
- package/src/spec/projected-draft-rows.ts +0 -73
- package/src/spec/qwen-mtp-module.ts +0 -171
- package/src/spec/qwen-mtp-rows.ts +0 -230
- package/src/spec/qwen-mtp-source.ts +0 -386
- package/src/spec/qwen-mtp-state.ts +0 -30
- package/src/spec/serve-loop.ts +0 -587
- package/src/spec/source.ts +0 -228
- package/src/spec/two-model-rows.ts +0 -62
- package/src/spec/two-model.ts +0 -113
- package/src/ssd-cache.ts +0 -431
- package/src/ssd-durability.ts +0 -209
- package/src/storage/host-buffer.ts +0 -24
- package/src/storage/kv-writer.ts +0 -83
- package/src/storage/kv-writer.worker.js +0 -264
- package/src/storage/retention-policy.ts +0 -37
- package/src/tiered-prompt-cache.ts +0 -45
- package/src/tokenizer.ts +0 -335
- package/src/tool-approvals.ts +0 -131
- package/src/tool-call.ts +0 -695
- package/src/train/dataset.ts +0 -484
- package/src/train/diffusion-lora.ts +0 -216
- package/src/train/export.ts +0 -32
- package/src/train/flash-cce.ts +0 -1234
- package/src/train/forward.ts +0 -266
- package/src/train/fuse.ts +0 -243
- package/src/train/index.ts +0 -46
- package/src/train/job.ts +0 -205
- package/src/train/lora-params.ts +0 -254
- package/src/train/loss.ts +0 -1591
- package/src/train/merge.ts +0 -195
- package/src/train/optimizer.ts +0 -183
- package/src/train/prefix-shared.ts +0 -490
- package/src/train/rank.ts +0 -137
- package/src/train/segmented.ts +0 -1828
- package/src/train/steel-qmm-header.ts +0 -1461
- package/src/train/trainer.ts +0 -1221
- package/src/train/watch.ts +0 -542
- package/src/vision/diffusion-vision.ts +0 -620
- package/src/vision/embedder.ts +0 -176
- package/src/vision/preprocess.ts +0 -247
- package/src/vision/prompt.ts +0 -409
- package/src/vision/qwen3vl-preprocess.ts +0 -409
- package/src/vision/qwen3vl-prompt.ts +0 -233
- package/src/vision/qwen3vl-tower.ts +0 -566
- package/src/vision/siglip.ts +0 -607
- package/src/vision/video-frames.ts +0 -148
- package/src/web/app.html +0 -2155
- package/src/web/app.js +0 -5863
- package/src/web/skills/memory/SKILL.md +0 -120
- package/src/web/skills/web-research/SKILL.md +0 -46
- package/src/web/skills.ts +0 -97
- package/src/web/src/adapters-panel.ts +0 -288
- package/src/web/src/assistant.ts +0 -451
- package/src/web/src/chat.ts +0 -1060
- package/src/web/src/composer.ts +0 -962
- package/src/web/src/dataset.ts +0 -132
- package/src/web/src/finetune.ts +0 -267
- package/src/web/src/hub.ts +0 -428
- package/src/web/src/main.ts +0 -98
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +0 -572
- package/src/web/src/model-picker.ts +0 -159
- package/src/web/src/palette.ts +0 -346
- package/src/web/src/protocol.ts +0 -26
- package/src/web/src/quantize.ts +0 -167
- package/src/web/src/rag.ts +0 -274
- package/src/web/src/sessions.ts +0 -469
- package/src/web/src/shell.ts +0 -804
- package/src/web/src/ui-catalog.ts +0 -78
- package/src/web/src/voice.ts +0 -141
- package/src/web/vendor/README.md +0 -61
- package/src/web-assets.ts +0 -47
- package/src/web-tools.ts +0 -478
- package/src/weights.ts +0 -255
- /package/src/{serve → server}/completion-sink.ts +0 -0
- /package/src/{serve → server}/generated-token-history.ts +0 -0
- /package/src/web/{icon.svg → public/icon.svg} +0 -0
- /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
- /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# Build journal — mlx-bun Lab (web UI + native quantize/train/dataset + pi web chat)
|
|
2
|
-
|
|
3
|
-
*Branch `web-ui-and-native-lab`, 2026-06-13/14. Visual summary:
|
|
4
|
-
[archive/mlx-bun-lab-report.html](../../docs/archive/mlx-bun-lab-report.html). Evidence
|
|
5
|
-
artifact: `benchmarks/lab-verification.json` (regenerable, gitignored — produced by
|
|
6
|
-
`scripts/verify-lab.ts`).*
|
|
7
|
-
|
|
8
|
-
## Goal
|
|
9
|
-
|
|
10
|
-
Re-implement the whole mlx-bun web surface as one beautiful, keynote-grade SPA
|
|
11
|
-
with five sections — a **full pi-agent chat**, plus **Quantize**, **Fine-tune**,
|
|
12
|
-
and **Build Dataset** ported from the OptIQ "lab" — while keeping the status
|
|
13
|
-
page. Compute is **native Bun/MLX (no Python)** per Josh's decision.
|
|
14
|
-
|
|
15
|
-
## The decisions that shaped it
|
|
16
|
-
|
|
17
|
-
1. **The big fork (asked up front).** Quantize + fine-tune compute could be (a)
|
|
18
|
-
native via mlx-c, (b) orchestrate the installed OptIQ Python, or (c) proxy
|
|
19
|
-
the running `optiq lab`. Josh chose **(a) native** — truest to mlx-bun's "the
|
|
20
|
-
buggy layer doesn't need Python" thesis. Chat = **full pi agent** (all tools).
|
|
21
|
-
Verification = **real tiny runs, pausing for go-ahead** before heavy jobs.
|
|
22
|
-
|
|
23
|
-
2. **Gate the riskiest unknown first.** Native LoRA needs autograd across the
|
|
24
|
-
FFI boundary. Rather than discover mid-build that it's infeasible, the first
|
|
25
|
-
thing built was `lab/spikes/phase-train-vag.ts`: a tiny LoRA-shaped loss
|
|
26
|
-
(`mean((x + (x@A)@B − y)²)`) differentiated by `mlx_value_and_grad` through a
|
|
27
|
-
`bun:ffi` JSCallback closure, checked against central finite differences.
|
|
28
|
-
**PASS (<1e-2), exactly 2 grads returned (base frozen).** Only then was the
|
|
29
|
-
trainer worth writing. This single check de-risked the entire subsystem.
|
|
30
|
-
|
|
31
|
-
3. **One SPA, hash-routed, text-imported.** Kept the exact single-binary
|
|
32
|
-
embedding (`with { type: "text" }`) the status page already used — no
|
|
33
|
-
bundler, no asset-resolution to patch in `build-binary.sh`. Design tokens
|
|
34
|
-
lifted from `status-page.html` so the five sections feel like one product.
|
|
35
|
-
|
|
36
|
-
4. **Mixed job-execution model by safety class.** Async Metal command-buffer
|
|
37
|
-
OOM is **uncatchable** (it `std::terminate`s). So GPU jobs (quantize,
|
|
38
|
-
finetune) run as **crash-isolated child `mlx-bun` subprocesses** behind a GPU
|
|
39
|
-
lease; dataset jobs (pure JS + loopback HTTP) run **in-process**. Both write
|
|
40
|
-
the same NDJSON event log + SQLite row, so the SSE/tail path is uniform.
|
|
41
|
-
|
|
42
|
-
5. **Open-book ports, not inventions.** Each engine follows a readable source:
|
|
43
|
-
dataset → `optiq/lab/dataset_templates.py` (13 templates, field-for-field);
|
|
44
|
-
trainer → `optiq/lora/{trainer,dpo,config}.py` + mlx-lm `default_loss`;
|
|
45
|
-
quantize → mlx-lm `convert.py` rules + the on-disk OptIQ model format; jobs →
|
|
46
|
-
`optiq/lab/{jobs,db}.py`; chat → pi SDK `examples/sdk/12-full-control.ts`.
|
|
47
|
-
|
|
48
|
-
## How it was built — parallel orchestration
|
|
49
|
-
|
|
50
|
-
After laying the shared job contract (`src/jobs/types.ts`), six subsystems were
|
|
51
|
-
fanned out to concurrent sub-agents on **non-overlapping files**, then
|
|
52
|
-
integrated centrally:
|
|
53
|
-
|
|
54
|
-
| subsystem | files | how verified |
|
|
55
|
-
|---|---|---|
|
|
56
|
-
| shared jobs | `src/jobs/*` | 12 tests (lifecycle, GPU lease, crash isolation, zombie, SSE) |
|
|
57
|
-
| dataset | `src/dataset/*` | 32 tests (generators, CSV, 90/10 split, 13 templates) |
|
|
58
|
-
| quantize | `src/quantize/*` | 6 tests + real-model e2e |
|
|
59
|
-
| trainer | `src/train/*`, `src/mlx/autograd.ts` | 19 fast + real-model e2e |
|
|
60
|
-
| web SPA | `src/web/app.html` | 7 in-process route tests |
|
|
61
|
-
| pi embed | `src/pi-web.ts` | 9 unit + live chat turn |
|
|
62
|
-
|
|
63
|
-
Shared/owned-by-me files were edited centrally to avoid conflicts:
|
|
64
|
-
`src/server.ts` (websocket + Lab routes + serve the SPA), `src/mlx/{ffi,ops}.ts`
|
|
65
|
-
(autograd/random/save bindings), `src/cli.ts`, `package.json` (pinned
|
|
66
|
-
`@earendil-works/pi-coding-agent@0.79.3`).
|
|
67
|
-
|
|
68
|
-
## Key technical findings
|
|
69
|
-
|
|
70
|
-
- **`mlx_save_safetensors` exists** (mlx-c `io.h`) — the quantizer writes real
|
|
71
|
-
HF-layout shards natively; no hand-rolled byte writer, no dtype/alignment bugs.
|
|
72
|
-
- **`mlx_value_and_grad` + `mlx_closure_new_func_payload`** are drivable from a
|
|
73
|
-
`bun:ffi` JSCallback (the repo already uses one for the mlx error handler).
|
|
74
|
-
The closure swaps the differentiated primals into `LoraWeights.{a,b}`, builds
|
|
75
|
-
the loss graph, and restores; mlx's graph keeps its own refs, so only A/B
|
|
76
|
-
differentiate and the quantized base stays frozen.
|
|
77
|
-
- **No optimizer in mlx-c** → AdamW hand-rolled as array ops (m/v EMA, bias
|
|
78
|
-
correction, decoupled weight decay), each step swapping the leaf handle back
|
|
79
|
-
into the `LoraWeights` so the next forward sees the update.
|
|
80
|
-
- **pi tool-approval gate** is a real pre-execution hook: an inline extension
|
|
81
|
-
via `DefaultResourceLoader.extensionFactories` registers `pi.on("tool_call")`
|
|
82
|
-
returning `{block:true}` to deny — read-only tools auto-allow, bash/edit/write
|
|
83
|
-
await a browser decision (120s auto-deny).
|
|
84
|
-
|
|
85
|
-
## Bugs found and fixed (during the real-model runs)
|
|
86
|
-
|
|
87
|
-
- **LoRA init key shape** — `mlx_random_normal` got a `[1,2]` key (a row slice of
|
|
88
|
-
the split-keys array) but mlx wants `[2]`. Reshaped the per-module subkey.
|
|
89
|
-
- **Quantize e2e path** — the gated test passed a model *id* to
|
|
90
|
-
`quantizeModelDir` instead of a resolved snapshot *path* (the job runner
|
|
91
|
-
resolves via the registry; the direct call didn't). Pointed it at
|
|
92
|
-
`SNAPSHOT_MINICPM5`.
|
|
93
|
-
- **Flaky chat test** — the agent sometimes takes a tool-call turn before
|
|
94
|
-
emitting text; the test resolved on the first `turn_end` and saw no tokens.
|
|
95
|
-
Fixed to settle on the first turn that actually streams text. The embed was
|
|
96
|
-
always correct (a debug run showed 25+ text deltas + a tool execution).
|
|
97
|
-
|
|
98
|
-
## Measured evidence (M4 Pro, MiniCPM5-1B, on-disk)
|
|
99
|
-
|
|
100
|
-
- **Quantize:** 170 modules → **4.50 bits/weight**, **0.62 GB** dir, **7.0 s**,
|
|
101
|
-
reopened correctly through the loader (quantized embed_tokens as `uint32`).
|
|
102
|
-
- **Train:** 20 SFT iters, **loss 4.48 → 0.68** in **4.6 s**, 168 adapters
|
|
103
|
-
mounted. Behavior changed — on "Tell me about your day": base replies normally,
|
|
104
|
-
the UPPERCASE-fixture adapter replies `"MY DAY AHEAD. APPROVE AND PROPERLY…"`.
|
|
105
|
-
- **Chat:** a real WebSocket turn streamed text + executed a tool to completion.
|
|
106
|
-
- **Tests:** 85+ new, all green; `tsc --noEmit` clean; existing server suite
|
|
107
|
-
17/17 (no regressions).
|
|
108
|
-
|
|
109
|
-
## Scope: shipped vs designed-seam
|
|
110
|
-
|
|
111
|
-
- **Shipped (v1):** full SPA (5 sections), pi web chat with tools + approval
|
|
112
|
-
gate, native uniform-affine quantize, native SFT + DPO LoRA, 12/13 dataset
|
|
113
|
-
templates, shared job system, real tiny-run verification.
|
|
114
|
-
- **Designed seams (follow-ups):** ~~OptIQ sensitivity+knapsack mixed-precision
|
|
115
|
-
quantization; batched (B>1) training; `hf_dataset_import`; HF push-to-hub;
|
|
116
|
-
folding pi's assets into the compiled single binary.~~ **ALL DONE in Round 2 —
|
|
117
|
-
see below.**
|
|
118
|
-
|
|
119
|
-
## Round 2 — the leftover seams, ported 1:1 (2026-06-14)
|
|
120
|
-
|
|
121
|
-
Josh: "all of those, port, one to one functionality." Built the five remaining
|
|
122
|
-
items as faithful ports of the OptIQ / mlx-lm source (open book), in parallel on
|
|
123
|
-
non-overlapping files, integrated centrally (server routes + one `app.html` UI
|
|
124
|
-
pass), and verified.
|
|
125
|
-
|
|
126
|
-
- **Mixed-precision quantization** (`src/quantize/{allocator,sensitivity,
|
|
127
|
-
calibration}.ts`) — `optimize_mixed_precision` ported **1:1** from
|
|
128
|
-
`optiq/core/optimizer.py` (greedy knapsack + block-aware floor + protected
|
|
129
|
-
layers + block-run guard + the uniform-vs-bf16 `_kl_reduction` sign detection).
|
|
130
|
-
Verified **bit-identical to OptIQ** on shared synthetic inputs by spawning the
|
|
131
|
-
venv Python in the test (4/4 cases). Per-layer KL sensitivity
|
|
132
|
-
(`analyze_sensitivity_exact`) runs on our graph: dequantize each layer's weight
|
|
133
|
-
to bf16, re-quantize at each candidate bit, swap into the running
|
|
134
|
-
`QuantizedLinear`, forward on a WikiText-2 calibration set, KL vs reference.
|
|
135
|
-
Real e2e: MiniCPM5-1B at target 5.0 bpw → 80×4-bit + 90×8-bit, **5.19 bpw, 32 s**.
|
|
136
|
-
- **Batched (B>1) training** (`src/train/{dataset,forward,loss}.ts`) — ragged
|
|
137
|
-
padding + a `[B,1,L,L]` causal-AND-key-validity mask via a cache wrapper (no
|
|
138
|
-
model/op edits); B=1 stays a separate bit-identical path. Bug found: comparing a
|
|
139
|
-
length-4 reference to a length-6 batched row conflated mask correctness with
|
|
140
|
-
bf16 sequence-length rounding — the corrected shape-held parity test passes,
|
|
141
|
-
confirming the mask isolates rows.
|
|
142
|
-
- **`hf_dataset_import`** (`src/dataset/generators.ts`) — the 13th template via
|
|
143
|
-
the HF datasets-server REST API (pagination, config resolve, label/min-chars/
|
|
144
|
-
cap filters, three output formats).
|
|
145
|
-
- **HF push-to-hub** (`src/hf-push.ts`) — native Hub upload (create repo → LFS
|
|
146
|
-
preupload → S3 PUT → NDJSON commit) for model/adapter/dataset repos, token at
|
|
147
|
-
`~/.mlx-bun/hf.json`. Mock-server tested end-to-end (11 tests); real uploads
|
|
148
|
-
untested by design (would publish under the user's account).
|
|
149
|
-
- **Single-binary web chat** (`scripts/build-binary.sh`) — pi's JS already
|
|
150
|
-
bundles under `bun build --compile`; only `photon_rs_bg.wasm` is sidecarred.
|
|
151
|
-
The compiled binary's headless pi session was verified to construct.
|
|
152
|
-
|
|
153
|
-
UI: one `app.html` pass enabled the Uniform/Mixed quantize toggle + target-bpw
|
|
154
|
-
slider, live merge/export/push on the fine-tune done step, push on quantize &
|
|
155
|
-
dataset, and an HF-token settings gear. New server routes:
|
|
156
|
-
`/api/finetune/{merge,export}`, `/api/{quantize,finetune,dataset}/push`,
|
|
157
|
-
`/api/settings/hf-token`, + mixed-precision params on quantize submit.
|
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
# MiniCPM5 Decode Megakernel — end-to-end build plan (handoff)
|
|
2
|
-
|
|
3
|
-
**Read this top to bottom, then execute the phases in order. Each phase has an
|
|
4
|
-
exact validation gate; do not advance until it passes. At the end you have one
|
|
5
|
-
resident Metal kernel that computes the entire decode forward (all 24 layers →
|
|
6
|
-
logits) in a single dispatch, never handing MLX's planner a graph to mis-plan.**
|
|
7
|
-
|
|
8
|
-
This document is self-contained. You should not need to ask anything to execute it.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 0. Why this exists (the problem, settled)
|
|
13
|
-
|
|
14
|
-
The inference forward composes per-op MLX nodes (`quantized_matmul`, `sdpa`, …).
|
|
15
|
-
We tried to insert our own fused `fast.metal_kernel`s (steel `BlockMMA` GEMM) into
|
|
16
|
-
that forward to fuse+speed it up. **Confirmed dead end:** MLX's lazy buffer planner
|
|
17
|
-
corrupts *chained* `BlockMMA` custom kernels (nondeterministic NaN/garbage/crash) —
|
|
18
|
-
the pointwise `fused-gelu` kernel chained fine (that kernel has since been deleted,
|
|
19
|
-
2026-07-05, in favor of the compiled activations — unified-engine-frontier-plan.md
|
|
20
|
-
§6), ours did not, and ≥2 of ours in one lazy graph corrupt. The only robust fixes are (a) a per-kernel eval boundary (24 syncs/
|
|
21
|
-
forward → net **slower**, 0.5–0.8×), or (b) **don't give the planner anything to
|
|
22
|
-
chain: one resident kernel for the whole forward.** This plan builds (b).
|
|
23
|
-
|
|
24
|
-
The training path already proved the shape: its forward is MLX ops (which chain
|
|
25
|
-
fine) + **one** terminal fused kernel (flash-CCE loss), `detachLeaf` at coarse
|
|
26
|
-
segment boundaries (`src/train/segmented.ts`). Our megakernel is the inference
|
|
27
|
-
analogue — but because inference has no terminal heavy-reduction, the "one kernel"
|
|
28
|
-
must be the *whole forward*, resident, coordinating its own stages.
|
|
29
|
-
|
|
30
|
-
**Scope = DECODE (single new token, M=1).** This is the only case physically
|
|
31
|
-
expressible as one dispatch: a single Metal dispatch cannot barrier across
|
|
32
|
-
threadgroups, so cross-token prefill attention can't be one kernel — but one token
|
|
33
|
-
attending to the *cached* KV has no cross-token dependency. Decode is also the
|
|
34
|
-
latency path that *is* serving speed (`[[serving-speed-is-the-user-metric]]`).
|
|
35
|
-
Prefill stays on the existing (correct, fast, parity) MLX-ops path.
|
|
36
|
-
|
|
37
|
-
**Honest perf expectation:** decode is weight-bandwidth-bound (reading ~1 GB of
|
|
38
|
-
4/8-bit weights per token is the floor for any method). The megakernel's win is
|
|
39
|
-
(1) eliminating ~240 per-op dispatch launches/token, (2) no intermediate device
|
|
40
|
-
round-trips (hidden stays resident), (3) zero MLX-planner involvement. Expect a
|
|
41
|
-
modest but real decode-latency win (overhead elimination), not a bandwidth
|
|
42
|
-
miracle. The *architectural* win (no corruption, fully ours) is the point.
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## 1. Success criteria (definition of done)
|
|
47
|
-
|
|
48
|
-
1. A function `decodeMegakernel(model, tokenId, position) → logits[V]` that runs the
|
|
49
|
-
entire MiniCPM5 decode forward in **one** `MetalKernel.apply` dispatch.
|
|
50
|
-
2. **Correctness gate (L1, bf16 KV):** teacher-forced greedy trajectory matches
|
|
51
|
-
`goldens/minicpm5-parity.json` — argmax agreement **≥ 98/100**, mean per-step
|
|
52
|
-
logit maxDiff small (this is a no-oracle Lab-gated path: ~1–2 bf16 ULP/op
|
|
53
|
-
accumulating; it will NOT be bit-exact, gate by teacher-forced agreement + KL —
|
|
54
|
-
the frozen-trajectory envelope method of the old `tests/perf-kernel-oracle.test.ts`,
|
|
55
|
-
threshold ≥ 56/64-style; that test was deleted 2026-07-05 with its kernel, but
|
|
56
|
-
the methodology stands on its own). DETERMINISTIC
|
|
57
|
-
across runs (no nondeterminism — that was the corruption signature).
|
|
58
|
-
3. **Perf gate:** decode tok/s ≥ the serial baseline on a paired same-process A/B
|
|
59
|
-
(`scripts/experiments/` style). Not slower. (Stretch: measurably faster.)
|
|
60
|
-
4. Default **OFF** behind `MLX_BUN_MEGAKERNEL=1`; the existing forward untouched;
|
|
61
|
-
`bun test tests/minicpm5-*.test.ts` stays green with it off.
|
|
62
|
-
5. Wired into the real decode loop (`src/generate.ts`) for `--megakernel` / env.
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## 2. Hard facts (MiniCPM5-1B-OptiQ-4bit) — do not re-derive
|
|
67
|
-
|
|
68
|
-
**Snapshot:** `/Users/joshrossi/.cache/huggingface/hub/models--mlx-community--MiniCPM5-1B-OptiQ-4bit/snapshots/664aabaed233c653f82716d8dc822234d0091f78`
|
|
69
|
-
|
|
70
|
-
**Config:** `hidden_size H=1536`, `num_hidden_layers=24`, `num_attention_heads=16`,
|
|
71
|
-
`num_key_value_heads=2` (GQA, `nRep=8`), `head_dim=128`, `intermediate_size I=4608`,
|
|
72
|
-
`vocab_size V=130560`, `rms_norm_eps=1e-6`, **`rope_theta=5_000_000`** (NOT 10000 —
|
|
73
|
-
the code reads `t.ropeParameters.full_attention?.ropeTheta`), `tie_word_embeddings=false`.
|
|
74
|
-
`attention scale = head_dim^-0.5 = 128^-0.5`. Plain Llama: **no MiniCPM residual
|
|
75
|
-
scaling** (verified — `LlamaLayer.forward` is `inputNorm→attn→+x→postAttnNorm→mlp→+h`).
|
|
76
|
-
|
|
77
|
-
**Per-layer attention output dims:** q_proj out = `16*128=2048`; k_proj/v_proj out =
|
|
78
|
-
`2*128=256`; o_proj in = `2048`, out = `1536`. MLP: gate/up out = `4608`, down in =
|
|
79
|
-
`4608`, out = `1536`. lm_head out = `130560`, in = `1536`. embed = quantized 8-bit.
|
|
80
|
-
|
|
81
|
-
**Per-TENSOR mixed precision (weight quant):** every linear has its OWN `{bits,
|
|
82
|
-
group_size}` baked in `config.quantization` (group_size always 64; bits ∈ {4,8}).
|
|
83
|
-
This is why the megakernel must be **generated/de-branched per model** — bits is a
|
|
84
|
-
compile-time literal per (layer, matrix), not a runtime value. Full table is in
|
|
85
|
-
`config.json` under `quantization`; read it with `quantFor(config.quantization,
|
|
86
|
-
path)` (`src/model/gemma4-base.ts`). Examples: L0 all 8-bit; L1 q4/k4/v8/o8/g4/u4/d8;
|
|
87
|
-
L7 q8/k4/v4/o8/g4/u8/d4; L23 all 8-bit. **Read the table programmatically per build —
|
|
88
|
-
do not hardcode.**
|
|
89
|
-
|
|
90
|
-
**Quantized weight layout** (`QuantizedLinear`, `src/model/gemma4-base.ts:76`):
|
|
91
|
-
- `.w`: packed `uint32` `[N_out, K_in*bits/32]`. `.scales`,`.biases`: `bf16`
|
|
92
|
-
`[N_out, K_in/group_size]` (affine: `deq = scale*q + bias`).
|
|
93
|
-
- Dequant math is in `STEEL_QMM_HEADER` (`dequantize<T,N,bits>`, `get_pack_factor`,
|
|
94
|
-
`get_bytes_per_pack`) — `src/train/steel-qmm-header.ts`. REUSE IT.
|
|
95
|
-
|
|
96
|
-
**KV cache:** L1 path = `KVCache` (bf16, `src/model/gemma4-base.ts:243`); its
|
|
97
|
-
`updateAndFetch(k,v)` appends `[1,heads,1,D]` at `cache.offset` and returns the full
|
|
98
|
-
`[1,heads,offset+1,D]`. `cache.offset` is the position. L2 path = per-layer
|
|
99
|
-
`QuantizedKVCache` (mixed bits from `config.kvQuant`) — DEFER to Phase 4.
|
|
100
|
-
|
|
101
|
-
**RoPE** (`ops.rope`, `src/mlx/ops.ts:100`): `rope(x[B,heads,L,D], dims=head_dim,
|
|
102
|
-
base=rope_theta, offset, scale=null)`. For decode L=1, offset=position. The
|
|
103
|
-
in-kernel formula (standard NeoX/Llama interleaved-half): for dim pair `i<D/2`,
|
|
104
|
-
`theta_i = position / base^(2i/D)`; rotate `(x[i], x[i+D/2])` by `theta_i`. **Match
|
|
105
|
-
`ops.rope`'s exact convention** — confirm against `src/mlx/ops.ts` rope binding and
|
|
106
|
-
a 1-token parity probe before trusting it (Phase 2 gate catches mismatch).
|
|
107
|
-
|
|
108
|
-
**Validation goldens (ALREADY EXIST, no regen needed):**
|
|
109
|
-
- L1: `goldens/minicpm5-parity.json` (prompt_ids `[0,608,4894,304,6918,357]`,
|
|
110
|
-
greedy_ids[100], vocab 130560) + `goldens/minicpm5-logits-step{0..99}.bin`
|
|
111
|
-
(f32 last-position logits). Produced by `scripts/regen.ts minicpm5`.
|
|
112
|
-
- L2: `goldens/minicpm5-kv-parity.json` + `…-kv-logits-step*.bin`.
|
|
113
|
-
|
|
114
|
-
**Reference forward to copy (the source of truth for correctness):**
|
|
115
|
-
`src/model/minicpm5.ts` — `LlamaAttention.forward` (95–193), `LlamaMLP.forward`,
|
|
116
|
-
`LlamaLayer.forward`, `MiniCPM5Model.forwardHidden/forwardLayers/logitsFromHidden`.
|
|
117
|
-
Transcribe THIS op-for-op into the kernel.
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 3. Already-built, validated building blocks (reuse, don't rebuild)
|
|
122
|
-
|
|
123
|
-
All default-OFF, `bun test tests/minicpm5-*.test.ts` green. From this session:
|
|
124
|
-
- `src/train/steel-qmm-header.ts` — `STEEL_QMM_HEADER`: MLX's verbatim quantized
|
|
125
|
-
`BlockMMA` GEMM + `dequantize`/`QuantizedBlockLoader`/`BlockLoader`. The GEMM
|
|
126
|
-
building blocks for every projection. `qmm_t_impl` (line 1334) is the full GEMM
|
|
127
|
-
reference (takes dims as `constant int&` — for our metal_kernel we inline the
|
|
128
|
-
`BlockMMA` loop with templateInt dims instead, as `FWD_STEEL_SOURCE` does).
|
|
129
|
-
- `src/model/fused-mlp-kernel.ts` — `fusedMlp`: whole MLP (gate+up+silu+down +
|
|
130
|
-
absorbed residual) in ONE dispatch, `hidden` never materialized, per-loader
|
|
131
|
-
mixed-precision bits baked, atomic-accumulated f32 output. **1.9–3.4× standalone
|
|
132
|
-
@ M=8–256, ~1 ULP correct.** This is a working prototype of one megakernel STAGE
|
|
133
|
-
and the dequant/GEMV patterns to copy. (Note: it currently corrupts when CHAINED
|
|
134
|
-
in the lazy graph — that's the whole reason for the megakernel; the kernel math
|
|
135
|
-
itself is correct, proven by `fused-mlp-check.ts (deleted; git history)`.)
|
|
136
|
-
- `src/model/steel-linear-kernel.ts` — `steelLinear`: plain quantized linear as our
|
|
137
|
-
dispatch (q/k/v/o/down), ~1 ULP vs `ops.quantizedMatmul`. The projection GEMV
|
|
138
|
-
pattern.
|
|
139
|
-
- `src/mlx/metal-kernel.ts` — `MetalKernel`: `apply(inputs, {outputShapeFn|outputs,
|
|
140
|
-
grid, threadGroup, templateInts, templateDtypes, atomicOutputs, initValue})`.
|
|
141
|
-
`*_shape` helpers available in-kernel. `metalCapture` for Xcode GPU traces.
|
|
142
|
-
- `src/train/segmented.ts:81` `detachLeaf` (contiguous→eval→`fromBytesCopy`) — the
|
|
143
|
-
graph-break primitive (replicated in `minicpm5.ts`).
|
|
144
|
-
|
|
145
|
-
**Probe scripts (templates for new validation):** `scripts/experiments/swiglu-*.ts`,
|
|
146
|
-
`fused-mlp-*.ts`, `steel-linear-check.ts`, `swiglu-teacherforced.ts` (the
|
|
147
|
-
teacher-forced gate harness — copy it).
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## 4. Architecture of the megakernel
|
|
152
|
-
|
|
153
|
-
### 4.1 The shape
|
|
154
|
-
|
|
155
|
-
One resident dispatch. State lives in **device** buffers (shared across
|
|
156
|
-
threadgroups); the kernel walks layer 0→23 as a sequence of **stages**, with a
|
|
157
|
-
**grid-wide barrier** between stages so stage N+1 sees all of stage N.
|
|
158
|
-
|
|
159
|
-
Per layer, the stages (each a grid-wide-parallel step, barrier after):
|
|
160
|
-
1. `xn = RMSNorm(hidden, inputNorm_w)` → device `xn[H]`
|
|
161
|
-
2. `q = Wq·xn` (`[2048]`), `k = Wk·xn` (`[256]`), `v = Wv·xn` (`[256]`) — quantized
|
|
162
|
-
GEMVs, outputs split across threadgroups → device
|
|
163
|
-
3. `rope(q, pos)`, `rope(k, pos)` (per head, D=128)
|
|
164
|
-
4. write `k,v` into KV cache at `pos` (device)
|
|
165
|
-
5. **attention:** for each of 16 q-heads (GQA: head h uses kv-head `h/8`), online
|
|
166
|
-
softmax of `q_h · K[0..pos]` over the `pos+1` cached keys (scale `128^-0.5`),
|
|
167
|
-
weighted sum of `V[0..pos]` → `attn[2048]`
|
|
168
|
-
6. `o = Wo·attn` (`[1536]`); `hidden += o` (residual)
|
|
169
|
-
7. `hn = RMSNorm(hidden, postAttnNorm_w)` → device `hn[H]`
|
|
170
|
-
8. MLP: `gate = Wg·hn`,`up = Wu·hn` (`[4608]`), `silu(gate)·up`, `down = Wd·(…)`
|
|
171
|
-
(`[1536]`); `hidden += down` (residual) — the `fusedMlp` body, inlined
|
|
172
|
-
After 24 layers: `RMSNorm(hidden, finalNorm_w)` → `lmHead·hidden` → `logits[V]`.
|
|
173
|
-
|
|
174
|
-
### 4.2 The grid-wide barrier (the crux)
|
|
175
|
-
|
|
176
|
-
A single dispatch has no built-in cross-threadgroup barrier. Implement a
|
|
177
|
-
**persistent-kernel software barrier** via two device atomics (`atomic_uint`):
|
|
178
|
-
`arrive` (count) + `sense` (phase). Each barrier: `tg_leader` does
|
|
179
|
-
`atomic_fetch_add(arrive,1)`; if it was the last (`==G`), reset `arrive=0` and flip
|
|
180
|
-
`sense`; all threadgroups spin-read `sense` until it flips; `threadgroup_barrier`
|
|
181
|
-
to fan back into the threadgroup. **HARD REQUIREMENT:** all `G` threadgroups must be
|
|
182
|
-
**co-resident** or this deadlocks (a threadgroup waiting on one never scheduled).
|
|
183
|
-
So `G` must be ≤ the GPU's max concurrent threadgroups.
|
|
184
|
-
|
|
185
|
-
Pick `G` conservatively for the M4 Pro (~16–20 GPU cores). Phase 3 determines the
|
|
186
|
-
safe `G` empirically (start `G=1`, raise until just before the largest occupancy;
|
|
187
|
-
if any deadlock/timeout, lower). Use `dispatchThreads`/grid = `G * TGN`. Provide an
|
|
188
|
-
env `MLX_BUN_MEGAKERNEL_G` to sweep. Document the chosen `G`.
|
|
189
|
-
|
|
190
|
-
> **De-risking:** Phase 2 builds `G=1` (single threadgroup, `threadgroup_barrier`
|
|
191
|
-
> only — NO software barrier, hidden in threadgroup memory) to get CORRECTNESS
|
|
192
|
-
> first. Phase 3 generalizes to `G>1` with the software barrier for SPEED. Keep the
|
|
193
|
-
> per-stage math identical between them.
|
|
194
|
-
|
|
195
|
-
### 4.3 Weight access — the repack (Phase 1)
|
|
196
|
-
|
|
197
|
-
A `metal_kernel` can't take ~500 tensors. Repack at load time into a SMALL fixed set
|
|
198
|
-
of device buffers + an offset/spec table:
|
|
199
|
-
- `WBYTES`: one `uint8` buffer = every quantized weight's packed bytes concatenated
|
|
200
|
-
in a fixed order (L0.q, L0.k, … L23.lm_head). Plus `SCALES` (bf16 concat),
|
|
201
|
-
`BIASES` (bf16 concat). Plus `NORMS` (bf16 concat of all RMSNorm weights).
|
|
202
|
-
- `META`: a `uint32`/`int32` table the kernel indexes by `(layer, matrix)` giving:
|
|
203
|
-
byte-offset into WBYTES, scales offset, biases offset, N, K. **bits/group_size are
|
|
204
|
-
baked as template literals per (layer,matrix) in the GENERATED kernel**, not in
|
|
205
|
-
META (they drive the dequant template instantiation).
|
|
206
|
-
- Because bits varies per matrix, the kernel is **generated** (like
|
|
207
|
-
`scripts/gen-model.ts`): emit a `forwardLayer_L<i>` inline body per layer with that
|
|
208
|
-
layer's 7 matrices' bits baked, OR a single body that `switch`es on a baked
|
|
209
|
-
per-layer-constant array. Prefer generation: produces
|
|
210
|
-
`src/model/generated/minicpm5-megakernel.ts` with all bits as literals.
|
|
211
|
-
|
|
212
|
-
KV cache buffers: `KBUF`,`VBUF` device `[24, max_seq, 2, 128]` bf16 (L1). `pos` is a
|
|
213
|
-
scalar input. Logits out: `[V]` f32.
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## 5. Phased plan (execute in order; gate each)
|
|
218
|
-
|
|
219
|
-
### Phase 1 — Weight repack (foundation)
|
|
220
|
-
**File:** `src/model/megakernel-pack.ts`.
|
|
221
|
-
**Do:** `packMiniCpm5(model) → { wbytes, scales, biases, norms, meta, layout }`.
|
|
222
|
-
Walk `model.layers[i]` (`.attn.{qProj,kProj,vProj,oProj}`, `.mlp.{gate,up,down}`,
|
|
223
|
-
`.inputNorm`,`.postAttnNorm`) + `model.finalNorm`,`model.lmHead`,`model.embed`. For
|
|
224
|
-
each quantized linear concat `.w` bytes → WBYTES, `.scales`→SCALES, `.biases`→BIASES
|
|
225
|
-
(record offsets, N, K, bits, group in `layout`). Concat norm weights → NORMS. Use
|
|
226
|
-
`MlxArray.rawBytes()` (eval first) to get bytes; build with `Bun`'s buffers; upload
|
|
227
|
-
as `MlxArray.fromBytesCopy`.
|
|
228
|
-
**Gate:** unit test — for 3 sample (layer,matrix), slice WBYTES/SCALES/BIASES at the
|
|
229
|
-
recorded offset, rebuild a `QuantizedLinear`, run `ops.quantizedMatmul` on a fixed
|
|
230
|
-
input, assert **byte-identical** to the original `.forward`. (`scripts/experiments/
|
|
231
|
-
megapack-check.ts`.) This catches layout bugs before any kernel.
|
|
232
|
-
|
|
233
|
-
### Phase 2 — Single-threadgroup correct megakernel (G=1)
|
|
234
|
-
**Files:** `src/model/megakernel-kernel.ts` (the `MetalKernel` + dispatch),
|
|
235
|
-
`gen-minicpm5-megakernel.ts (deleted; git history)` (emits the per-layer-bits-baked SOURCE into
|
|
236
|
-
`src/model/generated/minicpm5-megakernel.ts`).
|
|
237
|
-
**Design:** ONE threadgroup (`G=1`, `TGN=256` threads). `hidden[H]` in threadgroup
|
|
238
|
-
memory (6 KB). Loop layers; each stage uses `threadgroup_barrier`. GEMVs: the 256
|
|
239
|
-
threads cover the N outputs in chunks (each thread does `ceil(N/256)` dot-products
|
|
240
|
-
over K, dequantizing weight via `STEEL_QMM_HEADER`'s `dequantize`). RMSNorm: parallel
|
|
241
|
-
sum-of-squares reduction in threadgroup. Attention: loop heads; per head, online
|
|
242
|
-
softmax over `pos+1` cached keys (threads split the key positions, threadgroup
|
|
243
|
-
reduce). Inline the `fusedMlp` math for stage 8 (gate/up into threadgroup, silu·up,
|
|
244
|
-
down). KV read/write from `KBUF`/`VBUF`. Final: finalNorm + lmHead GEMV → logits[V]
|
|
245
|
-
(threads split V).
|
|
246
|
-
**Transcribe** `src/model/minicpm5.ts` op-for-op; match dtypes: accumulate in f32,
|
|
247
|
-
round to bf16 at each op boundary to track the reference (`(T)` casts, as `fusedMlp`
|
|
248
|
-
does). RoPE: match `ops.rope` exactly (verify with a single-layer probe).
|
|
249
|
-
**Gate (THE correctness gate):** `megakernel-teacherforced.ts (deleted 2026-08-23; git history)`
|
|
250
|
-
(copy `swiglu-teacherforced.ts`): drive 100 decode steps via the megakernel,
|
|
251
|
-
teacher-forced on `goldens/minicpm5-parity.json`, KV cache in `KBUF`/`VBUF`. Require
|
|
252
|
-
**≥98/100 argmax agreement, deterministic across 3 runs, no NaN.** If an early step
|
|
253
|
-
mismatches, bisect by stage: add a debug output for `hidden` after layer L (env
|
|
254
|
-
`MLX_BUN_MEGA_DUMP_LAYER=L`) and compare to the reference model's per-layer hidden
|
|
255
|
-
(capture via the wrapper trick in `swiglu-layerN.ts (deleted; git history)`).
|
|
256
|
-
|
|
257
|
-
### Phase 3 — Persistent multi-threadgroup (G>1) for SPEED
|
|
258
|
-
**Do:** generalize Phase 2 to `G` threadgroups. Move `hidden`,`xn`,`hn`,`q/k/v/attn/
|
|
259
|
-
gate/up` to DEVICE scratch (shared). Implement the §4.2 software barrier (`arrive`/
|
|
260
|
-
`sense` atomics, leader-flips-phase, spin). Split each stage's outputs across
|
|
261
|
-
threadgroups by `tid.x` block (GEMV: threadgroup `g` does N-rows `[g*N/G,
|
|
262
|
-
(g+1)*N/G)`). Determine safe `G`: sweep `MLX_BUN_MEGAKERNEL_G` from 1 up; the largest
|
|
263
|
-
that (a) never deadlocks/timeouts and (b) maximizes tok/s. Add a 1-second host-side
|
|
264
|
-
watchdog around the dispatch during the sweep to catch deadlock (kill + report).
|
|
265
|
-
**Gate:** same teacher-forced ≥98/100 + deterministic, AND paired tok/s ≥ baseline.
|
|
266
|
-
If `G>1` corrupts (barrier race), the barrier impl is wrong — verify the leader
|
|
267
|
-
reset ordering and that EVERY thread (not just leaders) waits on `sense` before
|
|
268
|
-
reading shared state, with a `threadgroup_barrier` + `mem_flags::mem_device` after.
|
|
269
|
-
|
|
270
|
-
### Phase 3 — RESULTS (2026-06-24)
|
|
271
|
-
|
|
272
|
-
Multi-threadgroup persistent kernel built + correct. Findings, in order discovered:
|
|
273
|
-
|
|
274
|
-
- **Occupancy sweep:** TGN must be a **power of 2** (the RMSNorm tree reduction
|
|
275
|
-
`for(s=TGN/2;s>0;s>>=1)` silently drops lanes otherwise — TGN=192 gave KL 0.54).
|
|
276
|
-
Smaller TGN packs more co-resident threadgroups (TGN=128 scales past where TGN=256
|
|
277
|
-
collapses). Peak total threads ≈ 12–16k; G beyond co-residency **deadlocks** the
|
|
278
|
-
software barrier (G=144 @ TGN=128 hung). Sweep needs a host watchdog.
|
|
279
|
-
- **Barrier cost** (measured, `megakernel-barrier-time.ts`): ~3.8 µs/barrier @ G=48,
|
|
280
|
-
~145 barriers/token after fusion → ~0.5 ms = ~10% of decode. Not the dominant cost.
|
|
281
|
-
- **bits-literal specialization REJECTED for the *naive* GEMV.** Templating on BITS
|
|
282
|
-
and/or K bloated code/registers → lowered max co-resident G → regressed peak
|
|
283
|
-
(164→102–156; full-K-unroll collapsed to 16). The kernel was occupancy/bandwidth-
|
|
284
|
-
bound, not branch-bound. **Lesson: feed the bottleneck (DRAM-latency-hiding =
|
|
285
|
-
occupancy×ILP), not instruction count.**
|
|
286
|
-
- **THE win — port mlx's `qmv_fast` decode GEMV verbatim** (`quantized.h`
|
|
287
|
-
qmv_fast_impl/load_vector/qdot → `qmv4`/`load_vec_tg`/`qdot_t`). Three things ours
|
|
288
|
-
lacked: (1) `load_vector` pre-scales x so `qdot` is mask-only (no per-nibble
|
|
289
|
-
shifts); (2) affine folds once per group as `scale*accum + sum*bias`; (3) **4 rows
|
|
290
|
-
per simdgroup reusing a register-resident x_thread** → 4× arithmetic per x-load +
|
|
291
|
-
4 independent accumulators hiding FMA/load latency. Our weight layout already
|
|
292
|
-
matches mlx's, all N,K divide the fast-path blocks → no safe-tail path. This is the
|
|
293
|
-
M=1 decode primitive that was never ported (steel-qmm-header is the M>1 GEMM/BlockMMA
|
|
294
|
-
path). **Jumped 0.70× → ~0.93–0.95× baseline; KL improved to 9.5e-4; removed the
|
|
295
|
-
G≥24 down-tiling constraint (any G now correct).**
|
|
296
|
-
- **Barrier fusion:** RMSNorm computed in-place per-threadgroup (`rms_norm_local`,
|
|
297
|
-
every TG normalizes the full hidden — tiny, removes the rms→GEMV grid barrier +
|
|
298
|
-
d_nrm device traffic) and SwiGLU `silu(gate)·up` folded into the down-proj's input
|
|
299
|
-
staging. ~145 barriers/token (from ~194). Perf-neutral at G=32 (barriers already
|
|
300
|
-
hidden) but removes device round-trips.
|
|
301
|
-
- **Generated (layer-unrolled, constants-baked) kernel built** (`genSourceMt`, env
|
|
302
|
-
`MLX_BUN_MEGAKERNEL_GEN=1`): per-(layer,matrix) bits/offsets/N/K + layer index L as
|
|
303
|
-
compile-time literals, no META lookups, no runtime bits dispatch. Layer loop
|
|
304
|
-
unrolled (sequential → registers reused, NO occupancy hit, unlike the inner-K
|
|
305
|
-
unroll). **Correct (KL identical) but perf-NEUTRAL** (202.4 vs 202.9 tok/s) —
|
|
306
|
-
confirms the kernel is bandwidth/barrier-bound, not lookup-bound. Kept behind the
|
|
307
|
-
flag as the architecturally-correct path.
|
|
308
|
-
- **Peak config:** TGN=256, G=32. Paired same-process A/B: megakernel **~202 tok/s**
|
|
309
|
-
vs baseline **~216 tok/s = ~0.94×**. Correctness 97/100 teacher-forced (3 argmax
|
|
310
|
-
near-ties; KL 9.7e-4), deterministic, no NaN.
|
|
311
|
-
- **Remaining gap (~5%) is the resident-kernel structural tax** (~6 grid barriers/
|
|
312
|
-
layer for the row-split GEMV producer→consumer chain + per-stage staging), which is
|
|
313
|
-
largely fundamental to multi-threadgroup decode. Candidate lever to cross baseline:
|
|
314
|
-
fold rope-q into attention to drop one barrier/layer. Needs a CLEAN-machine paired
|
|
315
|
-
measurement (Josh-gated) — the session machine is loaded, only the ratio is trustworthy.
|
|
316
|
-
|
|
317
|
-
### Phase 4 — L2 quantized KV (IN PROGRESS 2026-06-24, spec ready)
|
|
318
|
-
Swap `KBUF/VBUF` for the per-layer quantized-KV format. **Facts gathered:**
|
|
319
|
-
- CPM5 `kv_config.json`: per-layer `bits ∈ {4,8}`, `group_size=64`, quantized along
|
|
320
|
-
**head_dim=128** (so 2 groups per head per k/v). Loaded as `config.kvQuant`.
|
|
321
|
-
- Layout (`QuantizedKVCache`, gemma4-base.ts:407): per (layer,pos) store quantized
|
|
322
|
-
triple along the KVDIM=256 axis: `packed` uint32 `[256*bits/32]`, `scales`/`biases`
|
|
323
|
-
bf16 `[256/64=4]`. Affine `deq = scale*q + bias` (== our weight dequant).
|
|
324
|
-
- **mlx quantize formula to replicate EXACTLY** (`affine_quantize`, quantized.h:2432;
|
|
325
|
-
this is what the golden used via optiq `updateAndFetchQuantized`):
|
|
326
|
-
```
|
|
327
|
-
n_bins=(1<<bits)-1; eps=1e-7
|
|
328
|
-
wmin=min(group), wmax=max(group)
|
|
329
|
-
scale=max((wmax-wmin)/n_bins, eps); side=|wmin|>|wmax|; scale = side? scale : -scale
|
|
330
|
-
edge = side? wmin : wmax; q0=round(edge/scale)
|
|
331
|
-
scale = q0==0? scale : edge/q0; bias = q0==0? 0 : edge
|
|
332
|
-
q = min(round((w-bias)/scale), n_bins) // dequant: scale*q+bias
|
|
333
|
-
```
|
|
334
|
-
**Implementation steps:**
|
|
335
|
-
1. ✅ **DONE — quantize formula validated** (`kv-quant-formula-check.ts (deleted 2026-08-23; git history)`):
|
|
336
|
-
the affine_quantize transcription matches `ops.quantize` to bf16 ULP (scale/bias diff
|
|
337
|
-
1.8e-3 @4-bit, 6e-5 @8-bit; dequant residual = expected bf16-storage rounding). The
|
|
338
|
-
riskiest piece is de-risked. Compute scale/bias in f32, store bf16 (as mlx does).
|
|
339
|
-
2. **Host:** `MegakernelRunner` holds per-layer quantized KBUF/VBUF (packed/scales/
|
|
340
|
-
biases) sized to kvSeq; bits per-layer baked (literal in the generated kernel).
|
|
341
|
-
3. **Kernel write:** after rope, each head's roped k/v (128 dims) → 2 groups of 64 →
|
|
342
|
-
compute wmin/wmax (threadgroup or per-head reduce) → scale/bias → pack q → store at
|
|
343
|
-
current pos. Replaces the bf16 `knewOut/vnewOut` emit.
|
|
344
|
-
4. **Kernel read (attend_simd):** dequant EVERY position incl. current (optiq quantizes
|
|
345
|
-
then reads back, so the current entry must also round-trip) from packed+scales+biases.
|
|
346
|
-
|
|
347
|
-
**Increment 1 (DONE, bf16-storage round-trip): 93/100, KL 1.49e-2, deterministic, no NaN**
|
|
348
|
-
(`kv_rt_group<KVBITS>` after rope, `megakernel-kv-teacherforced.ts`,
|
|
349
|
-
`MLX_BUN_MEGAKERNEL_KVQUANT=1`). Quantize formula correct. **Gap diagnosed:** optiq's
|
|
350
|
-
`quantizedSdpaUnfused` uses `quantizedMatmulQT`, which dequants K/V **to f32 on-the-fly
|
|
351
|
-
inside the matmul** — never storing a bf16 dequantized value. Increment 1 stores
|
|
352
|
-
`bf16(scale·q+bias)` in the bf16 cache → ONE extra bf16 rounding optiq lacks → KL
|
|
353
|
-
15× the L1 path. NOT a formula bug; a storage-precision artifact of the shortcut.
|
|
354
|
-
**Increment 2a — PARITY fix (store-int-q, the cleaner path):** the quantized int `q`
|
|
355
|
-
(0..15 / 0..255) is EXACTLY representable in bf16 (ints ≤256 are exact). So store `q`
|
|
356
|
-
itself in the existing bf16 kcache/vcache (lossless) + per-group scale/bias in small
|
|
357
|
-
side buffers `[NLAYERS,KVSEQ,nKvHeads·headDim/64=4]` bf16; `attend_simd<KVBITS>` computes
|
|
358
|
-
`scale·q+bias` in f32 on read = optiq's exact f32 dequant, ZERO extra rounding → should
|
|
359
|
-
reach the attend_simd ceiling (~97, == L1). Much smaller than bit-packing: kv_rt_group
|
|
360
|
-
stores `q` (not dequant) to d_k/d_v + emits scale/bias new-rows; host adds 4 scale/bias
|
|
361
|
-
buffers + merges; attend dequants (current-pos scale/bias from device scratch, cached
|
|
362
|
-
from the side-buffer inputs). NO memory win (q stored loosely as bf16), but full optiq
|
|
363
|
-
parity — the Phase 4 correctness goal.
|
|
364
|
-
**Increment 2a — DONE, store-int-q: 93/100, KL 1.38e-2, deterministic, no NaN.**
|
|
365
|
-
Stores int q in kcache/vcache (exact in bf16) + bf16 scale/bias; `attend_simd_q<KVBITS>`
|
|
366
|
-
dequants to f32 on read. **Found + fixed a real bug:** the current-pos scale/bias were
|
|
367
|
-
first written to a NON-ATOMIC output (`sbNew`) read cross-threadgroup by attention →
|
|
368
|
-
incoherent (stale) per [[metal-persistent-kernel-coherence]] → 78/100. Fix: route
|
|
369
|
-
current-pos scale/bias through ATOMIC scratch `d_sb` (A_LD/A_ST), copy to `sbNew` only
|
|
370
|
-
for the host merge; cached scale/bias read from the `sb` INPUT (coherent). → 93/100.
|
|
371
|
-
**Surprise / honest correction:** 2a removed increment-1's extra bf16-rounding but
|
|
372
|
-
landed at the SAME 93/100 — so **dequant precision was NOT the gap.** The residual
|
|
373
|
-
(~14× L1's KL) is a genuine divergence between our quantized KV and optiq's, cause
|
|
374
|
-
still OPEN: formula validated vs `ops.quantize` (bf16 ULP), all 24 layers' bits match
|
|
375
|
-
kv_config, coherence fixed. Candidate causes to investigate: optiq may keep recent
|
|
376
|
-
token(s) unquantized (a bf16 staging window) rather than quantize-on-write; or its
|
|
377
|
-
fused quantized-SDPA accumulates differently than our dequant-then-online-softmax.
|
|
378
|
-
**ROOT CAUSE FOUND (decisive, in-process vs our bit-exact reference):** the reference
|
|
379
|
-
`QuantizedKVCache`+`quantizedSdpaUnfused` is bit-exact with the golden (maxDiff 0,
|
|
380
|
-
`tests/minicpm5-kv-parity.test.ts`). Comparing the megakernel's dequantized layer-0 K
|
|
381
|
-
to the reference's: **max diff 0.166 (≈ one quant step).** Comparing the *pre-quant*
|
|
382
|
-
bf16 roped K (L1): **max diff 1 bf16 ULP** (9.0 vs 9.0625; 425/1536 elems > 1 ULP) —
|
|
383
|
-
the `qmv4`-vs-`ops.quantizedMatmul` GEMV rounding difference, i.e. exactly what gives
|
|
384
|
-
L1 its 9.7e-4. **Quantization is DISCONTINUOUS**, so that 1-ULP K difference lands on a
|
|
385
|
-
q-level / group-min-max boundary and flips a level → a full quant-step dequant error →
|
|
386
|
-
amplified 9.7e-4 → 1.38e-2. **NOT a quant bug — the megakernel's intrinsic ~1-ULP GEMV
|
|
387
|
-
difference amplified by quant discontinuity. 93/100 is the L2 ceiling (as 97/100 is L1's);
|
|
388
|
-
bit-exact L2 would need a bit-exact GEMV, defeating qmv4.** Gate L2 by KL + agreement
|
|
389
|
-
(no-oracle, Lab class), not the bit-exact golden. Scripts: `megakernel-kv-{cmpkv,cmpl1,perstep}.ts`.
|
|
390
|
-
**Increment 2b — MEMORY win (later):** bit-pack `q` into uint32 (flat KPACK/VPACK,
|
|
391
|
-
per-layer literal offsets) to actually shrink 4-bit KV. Same logits as 2a. Deferred.
|
|
392
|
-
**Build on the GENERATED kernel** (`genSourceMt`, `MLX_BUN_MEGAKERNEL_GEN`): the
|
|
393
|
-
per-layer KV `bits` is fixed on disk (`kv_config.json`), so bake it as a compile-time
|
|
394
|
-
literal per layer (same "pre-determined" principle as the weight bits) — the quantize/
|
|
395
|
-
dequant helpers are `template<int KVBITS>`, and each unrolled layer emits its literal
|
|
396
|
-
`KVBITS` (and `group_size=64`). No runtime kv-bits table lookup. This is why Phase 4
|
|
397
|
-
makes the generated path the primary one (the looped kernel would need a runtime kv
|
|
398
|
-
table). Phase 4 therefore promotes `genSourceMt` from flag-gated to the L2 default.
|
|
399
|
-
**Gate:** `goldens/minicpm5-kv-parity.json` + `…-kv-logits-step*.bin` (already exist),
|
|
400
|
-
teacher-forced agreement + KL. Defer until L1 solid (L1 IS solid — parity, Phase 3).
|
|
401
|
-
|
|
402
|
-
### Phase 5 — Integration + ship
|
|
403
|
-
**Do:** `MiniCPM5Model.decodeStep(tokenId, pos) → logits` using the megakernel when
|
|
404
|
-
`MLX_BUN_MEGAKERNEL=1`; allocate the packed buffers + KV scratch once at model load
|
|
405
|
-
(or first decode), reuse across steps. Wire into `src/generate.ts` decode loop
|
|
406
|
-
(n23–n44 in `docs/dag/training-inference-map.html`) — the megakernel REPLACES the
|
|
407
|
-
per-step `forwardHidden→logitsFromHidden` at decode (M=1) only; prefill unchanged.
|
|
408
|
-
Guard: bf16 KV (L1) only, no adapters, no vision. Add `--megakernel` CLI flag.
|
|
409
|
-
**Gate:** `bun test tests/minicpm5-*.test.ts` green (off); a new
|
|
410
|
-
`tests/minicpm5-megakernel.test.ts` (teacher-forced gate, env-gated in the pattern
|
|
411
|
-
of the old `tests/perf-kernel-oracle.test.ts` — deleted 2026-07-05 with its kernel,
|
|
412
|
-
unified-engine-frontier-plan.md §6; the envelope-gate methodology stands on its
|
|
413
|
-
own); paired decode tok/s A/B logged. Update
|
|
414
|
-
`STATUS.md` + `benchmarks/RESULTS.md` (preflight-gated `scripts/bench-serve.ts all` for the
|
|
415
|
-
quotable number — `[[dirty-machine-numbers-are-garbage]]`).
|
|
416
|
-
|
|
417
|
-
---
|
|
418
|
-
|
|
419
|
-
## 5.5 Methodology for the next megakernel (learned 2026-06-24)
|
|
420
|
-
|
|
421
|
-
**Copy mlx's exact code verbatim into one file → prove bit-identical (per-op harness,
|
|
422
|
-
maxDiff 0 vs `ops.*`) → static-analyze that one file → THEN optimize incrementally,
|
|
423
|
-
re-running the bit-exact harness after each step.** Why: a hand-port of `qmv_fast` was
|
|
424
|
-
1-ULP-correct but NOT bit-identical to what `ops.quantizedMatmul` actually dispatches
|
|
425
|
-
for **4-bit M=1** (it uses a different kernel/accumulation order — `qmv4-bitexact-check.ts`
|
|
426
|
-
shows 8-bit bit-exact, 4-bit off by exactly 1 bf16 ULP on 12/256 rows, e.g. 1552 vs 1544
|
|
427
|
-
= adjacent bf16 values). Invisible at L1 (9.7e-4) but quantization's DISCONTINUITY
|
|
428
|
-
amplifies it into the L2 gap. A provably-identical starting copy + per-step gating would
|
|
429
|
-
catch any divergence the moment it's introduced, not 24 layers + a quant step downstream.
|
|
430
|
-
Steps: (1) copy mlx `quantized.h` GEMV kernels INCLUDING the per-(bits,M,N,K) dispatch
|
|
431
|
-
(`qmv_fast`/`qmv`/`qmv_quad`), `rms_norm`, `rope`, `sdpa`; (2) per-op maxDiff-0 harness
|
|
432
|
-
before fusing; (3) analyze the single fused file; (4) optimize with the harness as a
|
|
433
|
-
regression gate. This megakernel was built no-oracle-first (Lab-style, KL-gated, NOT bit-exact by design),
|
|
434
|
-
so its 1-ULP/op drift is within contract — bit-exact is a focused REBUILD, not a patch
|
|
435
|
-
(and may cost qmv4's speed if mlx's real 4-bit kernel is the slower qmv_quad).
|
|
436
|
-
|
|
437
|
-
## 6. Pitfalls (learned this session — do not relearn)
|
|
438
|
-
|
|
439
|
-
- **Chained custom kernels corrupt under MLX's planner.** The ENTIRE reason for one
|
|
440
|
-
kernel. Don't reintroduce intermediate custom-kernel boundaries.
|
|
441
|
-
- **`templateDtypes:{T:x.dtype}` is required** or `T` is undeclared (Metal compile
|
|
442
|
-
error). **`.template store<…>`** needed for dependent names when `T` is a template
|
|
443
|
-
param. Bake dims as `templateInts` / read `x_shape[0]`, don't pass a runtime
|
|
444
|
-
`shape` input array (extra lazy nodes).
|
|
445
|
-
- **Match the reference's bf16 rounding op-for-op** (round to `T` at each boundary) —
|
|
446
|
-
`metal::precise::exp`/`tanh` are the only residual vs the reference; that's
|
|
447
|
-
no-oracle (Lab-gated) territory and fine. Do NOT compute everything in f32 then round once (drifts).
|
|
448
|
-
- **`atomicOutputs:true` + `initValue:0`** for any atomic-accumulated output.
|
|
449
|
-
- **GQA:** 16 q-heads, 2 kv-heads, `nRep=8` → q-head `h` reads kv-head `h/8`.
|
|
450
|
-
- **`rope_theta=5e6`, not 10000.** scale `=128^-0.5`. No MiniCPM residual scaling.
|
|
451
|
-
- **Software barrier deadlocks if `G` > co-resident threadgroups.** Start G=1.
|
|
452
|
-
- **`detachLeaf` ≠ `evalAll`:** eval alone doesn't break the graph; only a fresh
|
|
453
|
-
`fromBytesCopy` leaf does. (Relevant only if you keep any boundary.)
|
|
454
|
-
- **`MlxArray` has `.astype`, not `ops.astype`; `ops.reshape`, not `.reshape`.**
|
|
455
|
-
- Oracle venv for any golden regen: `/Users/joshrossi/Code/mlx-lm/.venv/bin/python`,
|
|
456
|
-
`MLX_BUN_ORACLE_VENV=…`, `HF_HUB_DISABLE_XET=1`. (Goldens already exist; only
|
|
457
|
-
regen if you change the prompt.)
|
|
458
|
-
|
|
459
|
-
---
|
|
460
|
-
|
|
461
|
-
## 7. File manifest (what you create)
|
|
462
|
-
|
|
463
|
-
```
|
|
464
|
-
src/model/megakernel-pack.ts # Phase 1: weight repack + layout table
|
|
465
|
-
gen-minicpm5-megakernel.ts (deleted; git history) # Phase 2: emit per-layer-baked kernel SOURCE
|
|
466
|
-
src/model/generated/minicpm5-megakernel.ts # (generated) the kernel SOURCE + bits
|
|
467
|
-
src/model/megakernel-kernel.ts # Phase 2/3: MetalKernel + decodeMegakernel()
|
|
468
|
-
megapack-check.ts (deleted 2026-08-23; git history) # Phase 1 gate
|
|
469
|
-
megakernel-teacherforced.ts (deleted 2026-08-23; git history) # Phase 2/3 correctness gate
|
|
470
|
-
megakernel-perf.ts (deleted 2026-08-23; git history) # Phase 3/5 paired tok/s A/B
|
|
471
|
-
tests/minicpm5-megakernel.test.ts # Phase 5 CI gate (env-gated)
|
|
472
|
-
```
|
|
473
|
-
Modify: `src/model/minicpm5.ts` (add `decodeStep` megakernel branch),
|
|
474
|
-
`src/generate.ts` (decode-loop branch), `src/cli.ts` (`--megakernel`).
|
|
475
|
-
|
|
476
|
-
## 8. Order of operations (the literal checklist)
|
|
477
|
-
|
|
478
|
-
1. [ ] Phase 1: `megakernel-pack.ts` + `megapack-check.ts` → byte-identical gate green.
|
|
479
|
-
2. [ ] Phase 2: generator + `G=1` kernel; `megakernel-teacherforced.ts` ≥98/100,
|
|
480
|
-
deterministic ×3, no NaN. Bisect by layer if it fails.
|
|
481
|
-
3. [ ] Phase 3: `G>1` + software barrier; sweep `MLX_BUN_MEGAKERNEL_G`; same
|
|
482
|
-
correctness gate + tok/s ≥ baseline. Record chosen `G`.
|
|
483
|
-
4. [ ] Phase 4 (optional): L2 quantized KV vs kv-parity golden.
|
|
484
|
-
5. [ ] Phase 5: wire into `decodeStep`/`generate.ts`, env+CLI gate, CI test,
|
|
485
|
-
benchmark, update `STATUS.md`/`RESULTS.md`/memory
|
|
486
|
-
`[[cpm5-generate-and-fuse-inference]]`.
|
|
487
|
-
|
|
488
|
-
**Done = one resident dispatch computes the MiniCPM5 decode forward, ≥98/100
|
|
489
|
-
teacher-forced, deterministic, ≥ baseline tok/s, default-off, CI-gated.**
|