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,346 +0,0 @@
|
|
|
1
|
-
# mlx-bun: gemma4.ts decompose & optimize — action plan
|
|
2
|
-
|
|
3
|
-
> This is the canonical **design/rationale** for the optimization work.
|
|
4
|
-
> Live **status/tracking** (checkboxes, findings) for Phases A–D lives in
|
|
5
|
-
> PLAN-archive.md; Phase E remains in PLAN.md.
|
|
6
|
-
|
|
7
|
-
**Status (verified 2026-06-17):** Phases A–D are **DONE** (`[x]` in PLAN-archive.md, all landed 2026-06-10/11). Phase E — per-config fused decode-SDPA kernel — is **in progress** (`[~]` in PLAN.md, started 2026-06-11; fused kernel exists and is quality-gated per PLAN.md Step 4). The body of this doc remains the durable design rationale; completed phase checkboxes and findings live in PLAN-archive.md; Phase E tracking lives in PLAN.md.
|
|
8
|
-
|
|
9
|
-
**Premise (confirmed from the source read):** the current engine sits at
|
|
10
|
-
parity-to-ahead vs mlx-lm on the 12B *while still interpreting the decode
|
|
11
|
-
graph every token through hundreds of `bun:ffi` crossings, uncompiled.* The
|
|
12
|
-
largest remaining cost is host-side graph rebuilding that is identical
|
|
13
|
-
token-to-token — exactly what `mx.compile` exists to erase. Specialization and
|
|
14
|
-
custom kernels are real but smaller, and both are only *measurable* once the
|
|
15
|
-
host overhead is gone.
|
|
16
|
-
|
|
17
|
-
**Therefore the ordering is non-negotiable:** compile first (the big lever),
|
|
18
|
-
then decompose + specialize (enables the kernel and cleans the graph), then the
|
|
19
|
-
per-config fused kernel (the 2–3× on top). Doing it in any other order means
|
|
20
|
-
optimizing against a baseline that compile would have changed anyway.
|
|
21
|
-
|
|
22
|
-
**Invariant for the entire plan:** the compat-mode bit-exact parity gate
|
|
23
|
-
(`toBe(0)` logits, identical greedy trajectories vs the oracle venv) stays green
|
|
24
|
-
at *every* checkpoint. It is the instrument that makes refactoring safe to do
|
|
25
|
-
fast — any step that changes behavior turns it red the same hour. No step
|
|
26
|
-
proceeds with a red gate. No tolerance is ever added to make a refactor pass;
|
|
27
|
-
divergence is root-caused, never papered over.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Phase A — Compile the decode step (on the current monolith, before any split)
|
|
32
|
-
|
|
33
|
-
**Why first:** this is the 14×-class lever for *this* runtime specifically,
|
|
34
|
-
because mlx-bun's per-step cost is FFI-crossing-heavy in a way Python's
|
|
35
|
-
interpreter loop is not. It is independent of decomposition. Landing it first
|
|
36
|
-
means every later phase is measured against a compiled baseline, so you learn
|
|
37
|
-
exactly how much headroom specialization actually has left to chase instead of
|
|
38
|
-
chasing host overhead that compile already removed.
|
|
39
|
-
|
|
40
|
-
**Steps:**
|
|
41
|
-
|
|
42
|
-
1. **Characterize before touching anything.** Run `decode-split.ts (deleted; git history)`
|
|
43
|
-
(the per-step wall-time splitter that already exists) on all three models at
|
|
44
|
-
@600 and @8k. Record GPU-blocked time vs JS-graph-build time per step. This
|
|
45
|
-
is the control measurement; everything in Phase A is judged against it. The
|
|
46
|
-
prediction: e4b shows the largest JS-graph-build fraction (it has the
|
|
47
|
-
per-layer-input slicing loop no other model runs).
|
|
48
|
-
|
|
49
|
-
2. **Wire `mx.compile` around the single-token decode graph.** The unit to
|
|
50
|
-
compile is one `forward(tokens=[next], cache)` step: embed → forwardLayers →
|
|
51
|
-
finalNorm → logitsFromHidden → argmax. At fixed L=1 every op is shape-stable
|
|
52
|
-
token-to-token; only input values and cache offsets change. Use
|
|
53
|
-
`shapeless=True` semantics so the same compiled graph serves any decode step.
|
|
54
|
-
The cache update is the one stateful part — confirm the compiled region
|
|
55
|
-
either includes the sliceUpdate writes or is structured so the cache mutation
|
|
56
|
-
happens at stable shapes (see step 4).
|
|
57
|
-
|
|
58
|
-
3. **Keep prefill uncompiled (or separately compiled).** Prefill is
|
|
59
|
-
variable-length (L = prompt size), so it does not share a compiled graph with
|
|
60
|
-
decode. Leave prefill on the current path initially; a separate
|
|
61
|
-
fixed-tile-size compiled prefill is a later option, not part of this phase.
|
|
62
|
-
|
|
63
|
-
4. **Resolve the cache-mutation/compile boundary.** The quantized caches do
|
|
64
|
-
`quantize → sliceUpdate(ring) → activeSlices` per step (≈670 FFI crossings
|
|
65
|
-
per token on the 12B across 48 layers). Decide whether these live inside the
|
|
66
|
-
compiled graph (preferred — collapses the crossings into one replay) or
|
|
67
|
-
immediately outside it. The ring-index bookkeeping is host-side integer math
|
|
68
|
-
and stays in JS; only the array ops compile.
|
|
69
|
-
|
|
70
|
-
5. **Parity gate.** Compiled decode must produce bit-exact identical token
|
|
71
|
-
trajectories to the uncompiled path on the fixed prompt suite. Compile must
|
|
72
|
-
not change numerics — if it does, a fusion inside the compiled graph reordered
|
|
73
|
-
an op; root-cause it.
|
|
74
|
-
|
|
75
|
-
6. **Measure.** Re-run `decode-split.ts`. Expected: JS-graph-build fraction
|
|
76
|
-
collapses, e4b ~5% residual shrinks or closes, 12B short-context moves
|
|
77
|
-
further ahead. Record the delta — this number tells you how much the rest of
|
|
78
|
-
the plan is even worth.
|
|
79
|
-
|
|
80
|
-
7. **Re-run the fused-decode A/B *after* compile, not before.**
|
|
81
|
-
`MLX_BUN_FUSED_DECODE` measured ~3% *worse* uncompiled, almost certainly
|
|
82
|
-
because the tile loop's per-iteration host overhead dominates at L=1. Compiled,
|
|
83
|
-
that host cost vanishes and the flag may flip to a win with no kernel written.
|
|
84
|
-
This is a free re-measurement that may close part of the gap on its own.
|
|
85
|
-
|
|
86
|
-
**Phase A exit:** compiled decode green on parity, measured speedup recorded per
|
|
87
|
-
model, fused-decode A/B re-evaluated. Decision point: how much headroom remains?
|
|
88
|
-
That answer scopes Phases D–E.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Phase B — Extract a shared, config-independent base
|
|
93
|
-
|
|
94
|
-
**Why:** before generating per-model files, separate the machinery that is
|
|
95
|
-
genuinely identical across all architectures from the parts that vary. This is
|
|
96
|
-
mechanical, parity-preserving code movement — no behavior change, gate stays
|
|
97
|
-
green throughout.
|
|
98
|
-
|
|
99
|
-
**Moves into `gemma4-base.ts` (or `src/model/gemma4/base.ts`):**
|
|
100
|
-
|
|
101
|
-
- Cache classes: `KVCache`, `QuantizedKVCache`, `RotatingKVCache`,
|
|
102
|
-
`RotatingQuantizedKVCache` — these are architecture-independent storage; which
|
|
103
|
-
*combination* a model uses varies, but the classes themselves don't.
|
|
104
|
-
- SDPA functions: `quantizedSdpaUnfused`, `quantizedSdpaTiled`,
|
|
105
|
-
`fusedSdpaSupported`, `quantizedSdpa`, plus `ops.sdpa` plain dispatch.
|
|
106
|
-
- Mask builders: `createCausalMask`, `bidirMask`, the `Mask`/`MaskMode` types.
|
|
107
|
-
- Primitives: `RMSNorm`, `QuantizedLinear`, `QuantizedEmbedding`,
|
|
108
|
-
`QuantizedSwitchLinear` and the LoRA machinery (`LoraWeights`, `LoraState`).
|
|
109
|
-
- Helpers: `disposing`, `mapTriple`, `disposeTriple`, `logitSoftcap`,
|
|
110
|
-
`argmaxLastPosition`, `lastPositionLogits`, `FINFO_MIN`.
|
|
111
|
-
|
|
112
|
-
**Stays model-specific (does NOT move to base):** `Attention`, `MLP`, MoE
|
|
113
|
-
(`Router`/`Experts`/`SwitchGLU`), `DecoderLayer`, `Gemma4Model.forwardLayers`,
|
|
114
|
-
`computePerLayerInputs` — these branch on architecture and become the
|
|
115
|
-
per-model generated code.
|
|
116
|
-
|
|
117
|
-
**Steps:**
|
|
118
|
-
|
|
119
|
-
1. Create the base module, move the listed classes verbatim, fix imports.
|
|
120
|
-
2. Run the full parity suite + all behavioral tests. Green = the move was
|
|
121
|
-
behavior-preserving. This is the only acceptance criterion for Phase B.
|
|
122
|
-
3. No optimization in this phase. Pure extraction. Resist the urge to "improve
|
|
123
|
-
while moving" — that conflates a refactor bug with an optimization bug.
|
|
124
|
-
|
|
125
|
-
**Phase B exit:** base module extracted, every test green, monolith now contains
|
|
126
|
-
only the model-specific assembly that Phase C will generate.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## Phase C — Generate three per-architecture files (not author — generate)
|
|
131
|
-
|
|
132
|
-
**Reframe from the original request:** the per-model files are *outputs of a
|
|
133
|
-
generator*, not hand-written variants. Loop unrolling and branch elimination are
|
|
134
|
-
mechanical transforms; a codegen script performs them from each model's config.
|
|
135
|
-
Three files = three generator outputs. When the base algorithm changes, you
|
|
136
|
-
regenerate, not hand-edit three copies.
|
|
137
|
-
|
|
138
|
-
**The three targets (your actual runnable set):**
|
|
139
|
-
|
|
140
|
-
- `gemma4-12b.ts` — dense. Full-attention `KVCache` (or quantized variant),
|
|
141
|
-
GQA, no per-layer-input, no MoE, no KV-sharing. The simplest graph.
|
|
142
|
-
- `gemma4-e4b.ts` — the complex one. Per-layer-input gating, KV-sharing
|
|
143
|
-
(donor/sharer layers), sliding/rotating quantized cache. This is where the
|
|
144
|
-
per-step host overhead concentrated, so it's where specialization pays most.
|
|
145
|
-
- `gemma4-26b.ts` — MoE. Router + experts (`SwitchGLU`/`gather_qmm`), parallel
|
|
146
|
-
dense+routed branches.
|
|
147
|
-
|
|
148
|
-
**The generator (`scripts/gen-model.ts`):** reads `config.json` +
|
|
149
|
-
`kv_config.json` for a model and emits the specialized file. What it eliminates,
|
|
150
|
-
all resolvable at generation time from known constants:
|
|
151
|
-
|
|
152
|
-
- The per-layer `instanceof` cache-type check (gemma4.ts:1327) — each layer's
|
|
153
|
-
cache type is known from `layer_types`, so the generated layer calls the right
|
|
154
|
-
fetch directly, no runtime `instanceof`.
|
|
155
|
-
- The `if (this.router)` / `if (perLayerGate)` / KV-sharing forks in
|
|
156
|
-
`DecoderLayer.forward` and `forwardLayers` — each model takes exactly one
|
|
157
|
-
branch; the others are deleted from its file.
|
|
158
|
-
- The `forwardLayers` layer loop — unrolled into straight-line per-layer calls
|
|
159
|
-
with each layer's constants (type, cache index, donor index, sliding/full)
|
|
160
|
-
baked in. (Note: post-compile this is largely cosmetic for *speed* — compile
|
|
161
|
-
already erased the per-step cost — but it produces a cleaner compiled graph
|
|
162
|
-
and removes the `intermediates`/`previousKvs`/`cacheIndex` indirection arrays
|
|
163
|
-
and their per-step lookups.)
|
|
164
|
-
- The per-layer-input `slice`+`reshape` loop (gemma4.ts:1865) on non-e4b models:
|
|
165
|
-
deleted entirely (they don't have it). On e4b: the slice bounds are constant
|
|
166
|
-
per layer, so they're baked.
|
|
167
|
-
|
|
168
|
-
**Steps:**
|
|
169
|
-
|
|
170
|
-
1. Write `gen-model.ts`: input = model dir (config + kv_config), output = one
|
|
171
|
-
`.ts` file importing from `gemma4-base.ts`. Start with the 12B (simplest) to
|
|
172
|
-
prove the generator end-to-end.
|
|
173
|
-
2. Generated 12B must pass parity bit-exact against the oracle. This proves the
|
|
174
|
-
generator preserves behavior — the gate is the generator's test.
|
|
175
|
-
3. Generate e4b and 26B. Each passes parity. The KV-sharing donor/sharer wiring
|
|
176
|
-
in e4b is the trickiest to generate correctly; its parity test is the proof.
|
|
177
|
-
4. Dispatch: model load picks the generated module by config fingerprint
|
|
178
|
-
(arch + kv_config hash), with the **monolithic `gemma4.ts` retained as the
|
|
179
|
-
general fallback** for any model without a generated specialization. Nothing
|
|
180
|
-
is deleted; specialized files are opportunistic overrides.
|
|
181
|
-
|
|
182
|
-
**Phase C exit:** three generated, branchless, unrolled per-model files, each
|
|
183
|
-
bit-exact, dispatched by fingerprint, with the monolith as fallback. Measure
|
|
184
|
-
each vs its Phase-A compiled baseline — expect small wins (cleaner graph, fewer
|
|
185
|
-
indirection lookups), not large ones. The large one was compile.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Phase D — Per-config kernel specialization (kv_config-driven)
|
|
190
|
-
|
|
191
|
-
**Why:** now that each model has its own file and the cache/SDPA dispatch is
|
|
192
|
-
already branch-free per model, bake the *quantization* constants. The
|
|
193
|
-
kv_config.json's per-layer (bits, group_size) becomes compile-time information
|
|
194
|
-
in the generated SDPA and cache calls.
|
|
195
|
-
|
|
196
|
-
**What gets specialized, from the 12B's kv_config as the worked example**
|
|
197
|
-
(42 layers @ 4-bit/gs64, 6 layers @ 8-bit/gs64, group_size uniformly 64):
|
|
198
|
-
|
|
199
|
-
- **Per-layer bit-width resolved at generation, not per-step.** The generated
|
|
200
|
-
layer for index 6 calls the 8-bit SDPA path; index 0 calls the 4-bit path.
|
|
201
|
-
No runtime bit-width parameter, no `fusedSdpaSupported` bit check — the
|
|
202
|
-
generator already knows.
|
|
203
|
-
- **group_size folded as a compile-time constant** (64 everywhere in this
|
|
204
|
-
config) — `dim / group_size`, `32 / bits` (elements-per-int) become literals.
|
|
205
|
-
- **nRep (GQA ratio) baked** — the `nRep > 1` branch and the `expandDims`
|
|
206
|
-
broadcast become a constant-folded reshape with the literal ratio.
|
|
207
|
-
- **head_dim baked** — sliding (256) vs full-attention (512) head dims are known
|
|
208
|
-
per layer.
|
|
209
|
-
|
|
210
|
-
**Important honesty:** Phase D is specialization of the *existing* op-graph
|
|
211
|
-
(constant-folding, branch deletion), still using `ops.quantizedMatmulQT` etc.
|
|
212
|
-
It is NOT yet a custom kernel. Expected gain is modest — it removes parameter
|
|
213
|
-
passing and branches the compiled graph would partly handle anyway. Its real job
|
|
214
|
-
is to make Phase E's custom kernel *possible* by having a single known
|
|
215
|
-
(bits, group_size, nRep, head_dim) per dispatch site.
|
|
216
|
-
|
|
217
|
-
**Steps:**
|
|
218
|
-
|
|
219
|
-
1. Extend `gen-model.ts` to read kv_config and emit per-layer-specialized SDPA
|
|
220
|
-
dispatch (right bit-width path per layer, constants folded).
|
|
221
|
-
2. Parity gate per generated model. Bit-exact required — constant-folding must
|
|
222
|
-
not change numerics.
|
|
223
|
-
3. Measure vs Phase C. Record the (likely small) delta. If negligible, that's a
|
|
224
|
-
*finding*, not a failure — it means compile already captured it, and it tells
|
|
225
|
-
you the kernel in Phase E is the only remaining lever.
|
|
226
|
-
|
|
227
|
-
**Phase D exit:** each generated model has branch-free, constant-folded,
|
|
228
|
-
per-layer-specialized quant dispatch. Every dispatch site now has a single known
|
|
229
|
-
shape — the precondition for Phase E.
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
## Phase E — Per-config fused decode-SDPA kernel (the 2–3×, last)
|
|
234
|
-
|
|
235
|
-
**Why last:** this is the custom Metal kernel, the fun part, and it's the
|
|
236
|
-
*smallest* lever (the search and the turboquant retro both say the fused kernel
|
|
237
|
-
was 2–3× while the buffer/host path was the big win — which you've already
|
|
238
|
-
captured in Phases A and D). Doing it last means you measure its true
|
|
239
|
-
contribution against a compiled, specialized baseline instead of attributing
|
|
240
|
-
host-overhead noise to it.
|
|
241
|
-
|
|
242
|
-
**What the kernel does** (the mlx-qsdpa shape, specialized per bit-width):
|
|
243
|
-
|
|
244
|
-
- Decode-time (L=1) fused attention: stream the quantized KV through QK^T and
|
|
245
|
-
×V in one Metal dispatch, dequantizing inline, **never materializing** the
|
|
246
|
-
`[B,H,1,N]` scores row or the dequantized cache.
|
|
247
|
-
- **nRep baked in** — no `expandDims` GQA broadcast (gemma4.ts:916); the kernel
|
|
248
|
-
loops the query-head group internally with the literal ratio.
|
|
249
|
-
- **bits/group_size as compile-time template parameters** — generate the 4-bit
|
|
250
|
-
variant and the 8-bit variant; dispatch the right one per layer from the
|
|
251
|
-
config (Phase D already resolved which).
|
|
252
|
-
- Online-softmax fused (score → max → exp → ×V accumulate) so the scores never
|
|
253
|
-
hit memory.
|
|
254
|
-
|
|
255
|
-
**Mechanism:** `mx.fast.metal_kernel` (the intended extension point — no MLX
|
|
256
|
-
fork). Lift the *dequant unpacking logic* from a known-correct reference
|
|
257
|
-
(mlx-qsdpa is open, `_BaseCache`-compatible, ~200 lines) — the bit-unpacking and
|
|
258
|
-
scale/zero-point application is the well-trodden, copyable part. The novel part
|
|
259
|
-
is the *fusion boundary* (not writing the intermediate) and the *Bun dispatch*
|
|
260
|
-
(every existing impl dispatches from Python; a Bun-dispatched fused quant-SDPA
|
|
261
|
-
is genuinely unoccupied territory).
|
|
262
|
-
|
|
263
|
-
**Correctness regime — this is where the gate model changes:**
|
|
264
|
-
|
|
265
|
-
- A fused kernel that's faster will **not** be bit-exact with the unfused path
|
|
266
|
-
(online softmax ≠ one-shot softmax in bf16 — this is already documented in the
|
|
267
|
-
tiled path's "tier b" note). So the compat bit-exact gate does NOT apply to
|
|
268
|
-
the kernel output directly.
|
|
269
|
-
- Instead: the kernel runs under the **perf-mode flag** and is gated against the
|
|
270
|
-
**frozen ground-truth snapshot** (capture today's compat-mode logits/
|
|
271
|
-
trajectories on the fixed prompt suite *before* starting Phase E — that's the
|
|
272
|
-
quality oracle once bit-exact-vs-optiq no longer applies).
|
|
273
|
-
- Perf-mode gate = bounded logit tolerance + token-trajectory agreement above a
|
|
274
|
-
set threshold, *labeled with the measured delta* (e.g. "+X% tok/s, −Y% on the
|
|
275
|
-
eval set"), the way a quant ships with its perplexity hit. "No guarantee of
|
|
276
|
-
identical output" means **bounded and measured**, never unchecked.
|
|
277
|
-
- Compat mode (bit-exact, unfused) stays in the codebase permanently as the
|
|
278
|
-
`-O0` reference and differential-testing oracle: when the kernel misbehaves,
|
|
279
|
-
flip to compat, same prompt, and the diff localizes kernel-vs-expectation.
|
|
280
|
-
|
|
281
|
-
**Steps:**
|
|
282
|
-
|
|
283
|
-
1. **Freeze the ground-truth snapshot** (compat-mode logits + trajectories,
|
|
284
|
-
fixed prompt suite, all three models) before writing any kernel. This is the
|
|
285
|
-
perf-mode oracle.
|
|
286
|
-
2. **Metal capture one decode step** (`mlx_metal_start_capture`) on the
|
|
287
|
-
compiled+specialized 12B to confirm the prize size: is the dequantized-cache
|
|
288
|
-
round-trip still fat after compile, or did fusion upstream already thin it?
|
|
289
|
-
If thin, the kernel ceiling is low — record and decide. If fat, proceed.
|
|
290
|
-
3. **Toolchain derisk first:** get a trivial `mx.fast.metal_kernel` compiling,
|
|
291
|
-
dispatched from Bun, running, verified — *before* the GQA numerics. (RMSNorm
|
|
292
|
-
or a no-op fusion proves the `.metal` → mlx-c → `bun:ffi` pipeline end-to-end
|
|
293
|
-
so the real kernel debugs numerics OR plumbing, not both at once.)
|
|
294
|
-
4. **Generate the 4-bit fused decode kernel** for the 42 four-bit layers (where
|
|
295
|
-
42/48 of KV traffic lives and 4-bit's dequant round-trip is most lopsided
|
|
296
|
-
against compute). nRep, group_size baked.
|
|
297
|
-
5. **A/B vs compiled+specialized baseline**, paired, clean machine, at @8k (where
|
|
298
|
-
KV traffic dominates and fusion shows). Isolate the kernel's contribution.
|
|
299
|
-
6. **Decide on the 8-bit six.** 8-bit dequant is cheaper and the data fatter, so
|
|
300
|
-
the fusion win per-byte is smaller — possibly not worth a second kernel; stock
|
|
301
|
-
MLX path may stay for those layers. Measure before committing kernel #2.
|
|
302
|
-
7. **Per-model:** repeat the generation for e4b and 26B configs only if the 12B
|
|
303
|
-
kernel shows a real win. The generator (Phase C/D) already produces the
|
|
304
|
-
dispatch; the kernel is one more generated, per-bit-width artifact keyed by
|
|
305
|
-
fingerprint, each with its own auto-generated parity/quality test.
|
|
306
|
-
|
|
307
|
-
**Phase E exit:** per-config fused decode kernel(s), perf-mode flag, gated
|
|
308
|
-
against the frozen quality oracle with the trade *labeled*, compat mode retained
|
|
309
|
-
as permanent reference. Measured isolated contribution recorded.
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Cross-cutting discipline (applies to every phase)
|
|
314
|
-
|
|
315
|
-
- **Parity gate green at every checkpoint.** Compat bit-exact for Phases A–D;
|
|
316
|
-
frozen-snapshot quality tolerance for Phase E's perf mode. Never add a
|
|
317
|
-
tolerance to make a refactor pass — root-cause divergence.
|
|
318
|
-
- **One flag, one measurement per lever.** Compile, fused-decode, each
|
|
319
|
-
specialization, the kernel — each is independently togglable and independently
|
|
320
|
-
measured ("run it both ways, compare"), so you always know what each bought.
|
|
321
|
-
Never measure two new levers tangled together.
|
|
322
|
-
- **Clean machine or it didn't happen.** Reboot + preflight-gate every quotable
|
|
323
|
-
benchmark; `sudo purge` for cold-start rows.
|
|
324
|
-
- **Losing experiments become documented default-off flags, never deletions.**
|
|
325
|
-
- **Nothing deleted, fallback always present.** The monolith stays as the
|
|
326
|
-
general fallback; specialized/generated files are opportunistic overrides
|
|
327
|
-
dispatched by config fingerprint. An unhandled config runs slow, never broken.
|
|
328
|
-
- **Generator over hand-edits.** Per-model files are regenerated from config,
|
|
329
|
-
not maintained by hand. A base-algorithm fix is one regeneration, not three
|
|
330
|
-
edits. Each generated artifact ships with its generated parity test.
|
|
331
|
-
|
|
332
|
-
## Expected outcome, honestly stated
|
|
333
|
-
|
|
334
|
-
- **Phase A (compile):** the big win. Likely closes the e4b ~5% residual and
|
|
335
|
-
pushes the 12B further ahead. This is the lever that matters most and it's
|
|
336
|
-
pure host-side — no kernel, no Metal.
|
|
337
|
-
- **Phases C–D (decompose + specialize):** small wins (cleaner graph, deleted
|
|
338
|
-
branches/indirection), main value is enabling Phase E and improving
|
|
339
|
-
maintainability via codegen.
|
|
340
|
-
- **Phase E (kernel):** the 2–3× on the attention hot path, biggest at long
|
|
341
|
-
context, smallest lever of the three but the one with no upstream Bun-dispatched
|
|
342
|
-
equivalent — the genuinely novel contribution.
|
|
343
|
-
|
|
344
|
-
The sequence front-loads the largest, cheapest win (compile) and ends with the
|
|
345
|
-
most fun, most novel, smallest one (kernel), with every step gated by the oracle
|
|
346
|
-
that's kept the project honest from the start.
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# ORPO from-base on UltraFeedback (mixed-5bpw) — results + research directions
|
|
2
|
-
|
|
3
|
-
**Status:** experiment complete (2026-06-23). This is the validation run **and** the
|
|
4
|
-
research program it surfaced. Detailed research notes for the session of 2026-06-21…23.
|
|
5
|
-
|
|
6
|
-
**Companion docs:**
|
|
7
|
-
- [orpo-dynamic-lambda.md](../design/orpo-dynamic-lambda.md) — the adaptive-λ / PID controller (extended below).
|
|
8
|
-
- [orpo-future-enhancements.md](../design/orpo-future-enhancements.md) — SimPO + objective variants.
|
|
9
|
-
- [orpo-uf-testing-handoff.md](orpo-uf-testing-handoff.md) — the **earlier** from-*instruct* UF run (4-bit, seq 2048, no-segment). **Distinct** from this run.
|
|
10
|
-
- [segmented-backward-training.md](../design/segmented-backward-training.md), [steel-flash-cce-handoff.md](steel-flash-cce-handoff.md) — the memory/kernel stack this run exercised.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. The run
|
|
15
|
-
|
|
16
|
-
From-**base** (true base, not instruct) ORPO on UltraFeedback, MiniCPM5-1B.
|
|
17
|
-
|
|
18
|
-
- **Base:** `openbmb/MiniCPM5-1B-Base-OptiQ-mixed-5bpw` — the **true base**, mixed-precision quant (unblocked by the quantization fix). NOT the instruct model the earlier run used.
|
|
19
|
-
- **Data:** `uf-binarized-chat` — UltraFeedback as `{prompt:[{role,content}], chosen, rejected}`, **60,630 train pairs**. Prompt is a structured message array; chosen/rejected are raw response strings.
|
|
20
|
-
- **Config:** ORPO, **1 epoch (3,790 optimizer steps)**, B=1, **seq 4096**, rank 16 / scale 2, **λ=0.1**, lr **1e-5**, **segmented backward (2 layers/seg)**, **flash-CCE head**, **grad-accum 16** (eff. batch 16), coeff filter ON (`MLX_BUN_CCE_BWD_FILTER_EPS`, real-data ~0.16%).
|
|
21
|
-
- **Template:** the base quant has **no `tokenizer_config.chat_template`**; `ChatTemplate.load` falls back to **`chat_template.jinja`** (which *we* dropped into the quant when building it). It's **ChatML** (`<|im_start|>{role}\n…<|im_end|>`) **+ MiniCPM's XML tool format** (`<function><param>…</param></function>`, CDATA) **+ the hybrid `<think>` block**. We rendered `enableThinking:false` (injects an empty `<think>\n</think>\n\n` before the response).
|
|
22
|
-
- **Recipe fidelity:** lr 1e-5 + λ 0.1 (NOT the saturating 5e-5); **mean-logp verified length-normalized** over response tokens (`responseOnlyLogpMean`: `sum(logp)/M`, M = response-token count) — this is the single most common ORPO parity break and we are on the right side of it, including in the flash-CCE head (parity-validated to 0.21% vs the full-logits mean path).
|
|
23
|
-
|
|
24
|
-
## 2. Memory validation (the headline systems result)
|
|
25
|
-
|
|
26
|
-
- **Peak 4.82 GB** — the legitimate high-watermark, driven by the longest example (~2,354 tok chosen+rejected, idx 5582, **+ the prompt**), **not** the 4096 cap (all UF < 4096; 0 examples exceed it — that's *why* 4096 was chosen). My earlier ~4 GB estimate was ~0.5 GB low (slope under-call), not a leak.
|
|
27
|
-
- **Active flat at 0.892 GB** across ~3,300+ steps — **bit-identical early-to-recent, zero drift → NO leak.** Active (the baseline), not peak (example-length-driven), is the leak signal. The ~0.4 MB/step leak we caught earlier would've surfaced as **~1.3 GB of drift** over the run; we saw **0**. So any residual leak is bounded *below detectability*.
|
|
28
|
-
- The whole stack — flash-CCE, steel GEMM, segmented backward, AdamW, grad-accum, the coeff filter + block-skip — ran **dispose-balanced end-to-end** for a full epoch. **A 1B ORPO epoch in < 5 GB, holding flat.** This retires the memory risk for the bigger runs below.
|
|
29
|
-
|
|
30
|
-
**Predictive memory model (validated):** peak ≈ resting + (2-layer-segment activation) × (longest sequence). Deterministic, bounded; plateaus once the longest example runs. Watch `active_gb` for drift = the leak detector.
|
|
31
|
-
|
|
32
|
-
## 3. Results — the model
|
|
33
|
-
|
|
34
|
-
- **Val preference accuracy FLAT ~55.5%** across every eval (500→3000: 55.1, 55.1, 56.3, 55.9, 56.3, 55.9). Plateaued by step 500, **never strengthened**. The 55→56 wobble is 1–3 of 256 examples — inside the ±3.1% SE. n=256.
|
|
35
|
-
- **Val loss** crept down (1.356 → 1.305 — the NLL/format half); **train margin** rose slightly (mean 0.19 → 0.25). A **weak, stable preference** reached early and held.
|
|
36
|
-
- This is the **two-ORPO-halves-decouple** result again: the SFT/NLL half learns the format fast; the preference half barely generalizes on a 1B with subtle UF pairs.
|
|
37
|
-
|
|
38
|
-
## 4. Vibe-check — 20 questions through base+UF
|
|
39
|
-
|
|
40
|
-
**Single-turn (fresh context each):**
|
|
41
|
-
- **Good:** simple **code** (prime function correct, sqrt-optimized), **arithmetic** (17×23=391 ✓, 3+5−2=6 ✓), **creative persona** (the pirate plays the role).
|
|
42
|
-
- **Weak/wrong:** **factual recall** ("capital of Australia" → *"there is no specific capital"*; "most recent election" → *"Trump won 2020"*), **translation** ("I love programming" → *"Je love à programmer"*), **identity/privacy/weather** (hedging non-answers), **instruction constraints** ("list exactly three… and nothing else" → preambled anyway).
|
|
43
|
-
|
|
44
|
-
**Two failure MODES (the actually-useful findings):**
|
|
45
|
-
1. **Multi-turn copying loop** — in a running conversation, once a generic disclaimer enters the history, the model **parrots its own previous assistant turn** for nearly every subsequent question (Q4–9, 15–20 were byte-identical). **Root cause: UF is single-turn preference data** — it never learned to hold a conversation, so the path of least resistance is copy-the-last-turn.
|
|
46
|
-
2. **Hedging-disclaimer boilerplate** ("I'm here to help… I am a language model… respectful and safe… I will never fab[ricate]") — over-learned from UF's RLHF-safety filler.
|
|
47
|
-
|
|
48
|
-
## 5. Conclusions
|
|
49
|
-
|
|
50
|
-
1. **VALIDATION SUCCESS.** The chain works end-to-end: quant **base** loads → trains clean a full epoch → **zero leak** → **chats**. ORPO replicated at ~1/60th scale, **on a laptop**, single-stage base→assistant.
|
|
51
|
-
2. **Modest-as-predicted.** base + 1 epoch UF + λ=0.1 = a weak preference (flat 55.5%) and modest quality. We forecast this; it is the expected outcome, **not a miss**.
|
|
52
|
-
3. **The failure modes are DATA artifacts, not method artifacts.** Multi-turn collapse ← single-turn UF; hedging ← UF's disclaimer filler. Both fixable by **changing the data** → this *reinforces* the specialist-curation direction (we control the data). The vibe-check effectively handed us the **curation spec**: add multi-turn examples, filter the disclaimer filler, use our tools in our format.
|
|
53
|
-
4. **CONFOUND CORRECTION (load-bearing).** The gap vs `MiniCPM5-1B-SFT` is **NOT** evidence that single-stage can't replace SFT. We changed **method AND data simultaneously** — 60k UF pairs vs ~400B-token SFT (~1000×). The data difference dominates. The only clean claim is the near-tautological **"60k UF pairs ≠ 400B-token SFT"** (a *data* statement). The **method** question (single-stage vs multi-stage) is **untested** — it requires holding data constant (see §6.5).
|
|
54
|
-
|
|
55
|
-
## 6. Research directions (crystallized this session)
|
|
56
|
-
|
|
57
|
-
These form a build: each removes a confound or fixes a measured weakness of the prior.
|
|
58
|
-
|
|
59
|
-
### 6.1 SimPO, and why it's nearly free for us
|
|
60
|
-
SimPO = reference-free, length-normalized, `−log σ(β·(ℓw−ℓl) − γ)` with a **target margin γ** and **no SFT term** (pure preference; applied *after* SFT). We already compute the length-normalized `ℓw`, `ℓl` (`branchLogpMean`), so SimPO is a **one-term loss swap** → a `--method simpo` + `--beta`/`--gamma`. Natural fit for **SimPO-on-`MiniCPM5-1B-SFT`** (the staged comparison).
|
|
61
|
-
|
|
62
|
-
### 6.2 ORPO + SimPO's margin (the γ-hybrid) — the fix aimed at *our* result
|
|
63
|
-
For our regime (ℓw≈ℓl), ORPO's odds-ratio ≈ the logp difference already; what SimPO *adds* is the **target margin γ**. ORPO's `−log σ(separation)` gradient **vanishes once chosen ≥ rejected** (separation ≥ 0) → the model plateaus at tiny separation = **our flat margin**. The γ margin **moves the saturation point from 0 to γ**, keeping gradient alive. So: `L = NLL(chosen) + λ·[−log σ(β·(ℓw−ℓl) − γ)]` — ORPO's SFT anchor + SimPO's non-saturating push. Aimed directly at the flat-55.5% we measured.
|
|
64
|
-
|
|
65
|
-
### 6.3 SFT + SimPO single-stage (the SimPO analog of ORPO)
|
|
66
|
-
"SFT + SimPO at the same time," the way ORPO is "SFT + DPO at the same time." Composes cleanly (both NLL and SimPO push chosen-logp **up** → reinforce). **Watch-out:** SimPO also pushes `logp(rejected)` **down**, and the NLL anchor grounds chosen but **not** rejected — over-aggressive β/γ over-suppresses coherent rejected text → fluency erosion. Tune so the push doesn't outrun the anchor.
|
|
67
|
-
|
|
68
|
-
### 6.4 Dynamic preference controller (PID/AIMD) — see [orpo-dynamic-lambda.md](../design/orpo-dynamic-lambda.md)
|
|
69
|
-
The flat margin looks like **saturation**, and the *optimal* preference pressure is **non-stationary** (fragile early, robust late) → **no fixed λ is optimal**; a controller tracks the moving target and **beats any fixed value** (a sweep finds the best *constant*; this finds the best *trajectory*). Reframed this session as a **control problem**: NLL = the process variable, the preference weight = the actuator, AIMD/PID = the law. The integral term (the "missing letter") kills chronic steady-state degradation; the derivative (slope) is a **leading** indicator that catches the helping→hurting inflection *before* the level rises. Build **P → PD → PID**, each term earning its place against an observed failure of the prior. Folds the λ/β/γ sweep into one run. Detail + the control-theory framing now in orpo-dynamic-lambda.md §"Control-theory framing (PID/AIMD)".
|
|
70
|
-
|
|
71
|
-
### 6.5 SFT-as-preference / SPIN comparison — the confound-free method test
|
|
72
|
-
**The interesting one.** Take a **chunk of `UltraData-SFT-2605`** (CPM5's *own* SFT data), make preference pairs **SPIN-style** (chosen = the gold answer, **rejected = on-policy: the model's own generation** for that prompt), train single-stage. Removes the data confound (same data CPM5-SFT saw). Two comparisons:
|
|
73
|
-
- **Method-isolating (rigorous):** plain SFT vs single-stage-preference on the **same chunk** → proves "preference-formatted SFT ≥/= SFT" with no confound. *Why it could be ≥:* ORPO on (gold vs self-gen) = NLL does the SFT **+** the preference term adds "commit to gold over your own output."
|
|
74
|
-
- **"Approach CPM5-SFT" (aspirational, data-gated):** needs a large fraction of 400B tokens → the data gap re-enters; don't let it quietly re-import the confound.
|
|
75
|
-
- Smart first cut: a **single-domain chunk** (knowledge or instruction-following — where base+UF was weakest) — small, legible.
|
|
76
|
-
- **NOT an ORPO-paper replication** — a *different*, arguably more novel comparison.
|
|
77
|
-
|
|
78
|
-
### 6.6 Scale-up to a standard 7B, on the laptop
|
|
79
|
-
Memory math (validated): 7B resting ~4.4 GB + 2-layer-segment activations → **~8–12 GB peak at seg 2** on UF-length data; **seg 1** halves the activation term (more recompute = slower) for headroom. **Fits 32 GB with room** — the segmented backward was *built* for exactly this (e4b targeted ~10 GB @ 8192; UF < 4096 is the easy end). **Time, not memory, is the cost** (~7× FLOPs/token → days on a laptop). Prefer a **standard dense 7B (Mistral-7B-base** — the ORPO paper's flagship) over e4b for direct literature comparison; it's standard Llama-arch (≈ MiniCPM5's family) so the handler is ~a day. **Strategic flip:** "7B preference-aligned on a 32 GB consumer laptop" *is* the local-to-you thesis at scale — scale-up strengthens the accessibility pitch instead of diluting it.
|
|
80
|
-
|
|
81
|
-
### 6.7 Diffusion-LM frontier (high-novelty, low *marginal* cost)
|
|
82
|
-
Apply ORPO/SimPO/the controller to a **diffusion language model** (e.g. a Gemma-diffusion). Likely **open territory** (DPO-for-diffusion exists for *images* — Diffusion-DPO — not for diffusion *LMs* + single-stage/dynamic). The diffusion **forward is product-justified anyway** (mlx-optiq has it, mlx-lm doesn't, mlx-vlm does) → once it exists, the preference objective is the same **loss-head swap**. Technical crux: the likelihood is a **stochastic ELBO** (not exact logp), so `ℓw`/`ℓl` are noisy MC estimates → **the dynamic controller's noise-robustness is a *synergy*, not a mismatch.** Length normalization must be rederived for the diffusion loss. Base quality lags AR (24B-diff ≈ 12B-AR), so the contribution is *"does single-stage preference-opt work on diffusion LMs,"* novel regardless.
|
|
83
|
-
|
|
84
|
-
## 7. Template note (for any future run)
|
|
85
|
-
|
|
86
|
-
- For **portability / paper-grade** runs, impose a **clean plain-ChatML** template (no MiniCPM XML-tool / `<think>` extensions) at data-prep instead of falling back to the model's custom one. Trivial (drop a clean `chat_template.jinja` / override `renderDpoPrompt`).
|
|
87
|
-
- The model's **native tool format is the XML `<function>/<param>`** one — any tool-use training data must use **that**, not OpenAI JSON `tool_calls`.
|
|
88
|
-
|
|
89
|
-
## 8. The data — `UltraData-SFT-2605` (the substrate for §6.5)
|
|
90
|
-
|
|
91
|
-
[openbmb/UltraData-SFT-2605](https://huggingface.co/datasets/openbmb/UltraData-SFT-2605) — the actual SFT data behind `MiniCPM5-1B-SFT`:
|
|
92
|
-
- **15,036,178 examples · 319 GB · ~400B tokens** (200B deep-thinking + 200B hybrid-thinking). **Apache 2.0.**
|
|
93
|
-
- **Single-response (gold only) — NOT preference pairs** → the rejected must be manufactured (SPIN-style on-policy).
|
|
94
|
-
- **Single-turn** (no multi-turn) → won't, by itself, fix the multi-turn copying loop.
|
|
95
|
-
- Domains: math / code / knowledge / instruction-following. Thinking split **6.5M deep / 8.5M non-thinking**. **Chunked by domain** → take a slice, never the full 319 GB. (Multi-GB download is the user's to kick off, not a session's.)
|
|
96
|
-
- The three published models: `MiniCPM5-1B-Base` → `MiniCPM5-1B-SFT` → `MiniCPM5-1B` (instruct, RL+OPD on top of SFT).
|
|
97
|
-
|
|
98
|
-
## 9. The framing that ties it together
|
|
99
|
-
|
|
100
|
-
- **What SFT builds vs what preference-opt aligns:** our failure list (multi-turn, instruction discipline, breadth, `<think>`) is ~the SFT stage's job description — but per §5.4 that's mostly a **data** statement (400B curated tokens), not proof the SFT *method* is irreplaceable.
|
|
101
|
-
- **Where single-stage genuinely wins:** specialists (narrow curated data — no foundation breadth needed) and as a **final alignment stage on a good SFT**. NOT a foundation-SFT replacement — *and our own run is the evidence for that boundary, honestly scoped.*
|
|
102
|
-
- **The narrow specialist is the product** (welcome-agent / memory-agent on our closed tool surface); the general UF model was always the **validation**, and it handed us the curation spec for free.
|
|
103
|
-
|
|
104
|
-
## 10. Choosing what's next — the three lenses (NO decision made yet)
|
|
105
|
-
|
|
106
|
-
Apply all three to any §6 direction; they point different ways:
|
|
107
|
-
|
|
108
|
-
1. **Will it work + be useful to US?** (the *product* lens — primary.) Does it improve the specialists we actually ship? Usually gated by a **cheap diagnostic first** — e.g. for the controller: does a *higher fixed λ* or the **γ-margin** even unstick the flat preference? If a fixed push does nothing, *no controller can.* Run the cheap fixed-knob version before the fancy one.
|
|
109
|
-
2. **Interesting paper?** (read / replicated / adds to the ORPO-style discourse.) Strong hooks (control-theory/PID, SFT-as-preference, diffusion-LM preference-opt), but it's a **crowded, competitive space** → the result has to be the non-stationarity / confound-free kind to be more than "auto-tuning," and credibility likely needs **scale** (7B+, standard model, standard benches).
|
|
110
|
-
3. **Fundable / by whom?** The *technique* funds itself via the **product** (cheaper specialist training), independent of any paper. As a paper/grant: compute-cost-sensitive trainers + the local-AI community + project credibility. It's a *method* contribution, not a product.
|
|
111
|
-
|
|
112
|
-
**The trap + the rule:** don't let the *paper* lens drive the work. **Let the product need drive it** (lens 1), run the cheap diagnostics first, and treat the **paper as a harvest** if the result turns out strong — the experiments that make it a good feature are the same ones that'd make it a paper, so the lens-2/3 decision can wait until lens 1 reports.
|
|
113
|
-
|
|
114
|
-
**Rough risk / novelty / cost of the §6 directions (for when we pick):**
|
|
115
|
-
- **§6.2 γ-hybrid / higher-λ diagnostic** — lowest cost, directly targets the measured flat preference. *The cheap first cut.*
|
|
116
|
-
- **§6.1/6.3 SimPO-on-SFT / SFT+SimPO** — low (loss-swap), needs the SFT checkpoint pulled.
|
|
117
|
-
- **§6.4 PID controller** — medium (build + tune), highest method-novelty of the AR options.
|
|
118
|
-
- **§6.5 SPIN-on-UltraData** — medium (chunk + on-policy generation), the **confound-free method test**, distinct from ORPO replication.
|
|
119
|
-
- **§6.6 7B-Mistral-on-laptop** — ~a day handler + days of compute; the credibility + thesis-at-scale move.
|
|
120
|
-
- **§6.7 diffusion** — highest novelty, lift mostly paid by the product (the forward exists anyway); the ambitious swing.
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# flash-CCE host-buffer pin leak — post-mortem + L3 verification approach
|
|
2
|
-
|
|
3
|
-
**Date:** 2026-06-21 · **Status:** fixed (`fix(train): stop host-buffer pin leak…`,
|
|
4
|
-
commit `2592959`) + hardened (`harden(train): anchor ptr()'d FFI buffers…`, `6491696`).
|
|
5
|
-
|
|
6
|
-
## Symptom
|
|
7
|
-
|
|
8
|
-
ORPO LoRA training (MiniCPM5-1B, flash-CCE head + prefix-share + segmented
|
|
9
|
-
backward) crashed **natively** (segfault / "panic: A C++ exception occurred",
|
|
10
|
-
SIGTRAP — *not* an MLX-reported error) after a few hundred steps:
|
|
11
|
-
|
|
12
|
-
| config | crashed at |
|
|
13
|
-
|---|---|
|
|
14
|
-
| accum-4 + seg-2 (most ops/step) | step **363** |
|
|
15
|
-
| accum-4 + no-seg | step **783** |
|
|
16
|
-
| accum-1 + no-seg (fewest ops/step) | step **≥915** |
|
|
17
|
-
|
|
18
|
-
Tells: **deterministic** (same step across runs), **monotonic in ops/step**
|
|
19
|
-
(more kernel ops per step → earlier crash), and MLX `active` memory **flat**
|
|
20
|
-
(~1.3 GB) the whole time. Crucially, **it ran the entire training with zero
|
|
21
|
-
crashes on a 32 GB M1 Max**, and crashed on a 24 GB M4 Pro.
|
|
22
|
-
|
|
23
|
-
## Wrong turns (recorded so we don't repeat them)
|
|
24
|
-
|
|
25
|
-
1. **Agent sandbox** — the first crash was on a session-launched run, so the
|
|
26
|
-
sandbox looked guilty. Ruled out: a user-launched run crashed at the *same*
|
|
27
|
-
step 783.
|
|
28
|
-
2. **GPU-allocator fragmentation / OOM** — `peak` sat at 16.7 GB on a 24 GB
|
|
29
|
-
box. Ruled out: with segmentation `peak` was only 2.9 GB and it crashed
|
|
30
|
-
**earlier** (363), and the same 16.7 GB peak ran fine post-fix. Not memory.
|
|
31
|
-
3. **prefix-share fallback / templated-data seam** — a real new behavior (the
|
|
32
|
-
`</think>\n\n` seam trips the byte-identical prompt check → two-forward
|
|
33
|
-
fallback) but not the cause.
|
|
34
|
-
4. **grad-accum / grad-clip (the new settings this session)** — the right
|
|
35
|
-
*thread* (look at what changed), wrong leaf. Both were exonerated.
|
|
36
|
-
|
|
37
|
-
## Root cause
|
|
38
|
-
|
|
39
|
-
`u32()` in `flash-cce.ts` (and the twin in `model/flash-attention.ts`) built the
|
|
40
|
-
tiny `shape` / `targets` kernel-argument arrays with **`MlxArray.fromView`** —
|
|
41
|
-
the **zero-copy pinning** path. `fromView` inserts the host `Uint8Array` into a
|
|
42
|
-
module-level `pinned` Map and hands MLX a raw pointer with an **async**
|
|
43
|
-
`unpinCallback` ("mlx may release buffers from its eval thread"). These args feed
|
|
44
|
-
a **lazy** kernel and are `dispose()`d in a `finally` *before the kernel
|
|
45
|
-
evaluates* — so the pin is never reclaimed in time. The pins **leaked**.
|
|
46
|
-
|
|
47
|
-
**Proof (the smoking gun).** Instrumenting `pinnedBufferCount()` per step on the
|
|
48
|
-
buggy code:
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
step 1: pinned 32 active 1.087GB
|
|
52
|
-
step 2: pinned 64 active 1.088GB
|
|
53
|
-
...
|
|
54
|
-
step 32: pinned 1024 active 1.099GB → +32 per step, never released
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Perfectly linear `+32/step`, `active` dead flat. The leaked buffers live in the
|
|
58
|
-
JS heap + a threadsafe-JSCallback dtor queue, **outside MLX's `active`
|
|
59
|
-
accounting** — which is exactly why every symptom held: per-op accumulation (more
|
|
60
|
-
kernel calls/step → faster growth → earlier crash), `active` flat, native crash
|
|
61
|
-
(host allocator / callback queue corruption), and **machine-dependence** (a
|
|
62
|
-
24 GB box reuses the freed-but-pinned buffer sooner → a latent use-after-free
|
|
63
|
-
becomes a fatal read; 32 GB had the headroom to never hit it).
|
|
64
|
-
|
|
65
|
-
## Fix
|
|
66
|
-
|
|
67
|
-
`u32()` → **`fromBytesCopy`** (`mlx_array_new_data`, which **copies**). These
|
|
68
|
-
few-int arrays feed a lazy kernel and are disposed before it runs, so they must
|
|
69
|
-
be **MLX-owned**, not host-pinned. A few-int copy is free.
|
|
70
|
-
|
|
71
|
-
**Verification:** `pinned` goes `+32/step → flat 0`; the previously-fatal
|
|
72
|
-
seg-2/accum-4 config ran to **step 548** (past 363) clean; numerics unchanged
|
|
73
|
-
(copy vs pin = same bytes).
|
|
74
|
-
|
|
75
|
-
## Hardening (defense-in-depth)
|
|
76
|
-
|
|
77
|
-
- **`MetalKernel.apply` keepAlive** — anchor every `ptr()`'d JS buffer (output
|
|
78
|
-
shapes, template-arg names, the input-handle array, the result slot) in a
|
|
79
|
-
`keepAlive` list referenced after the FFI calls. `bun:ffi` `ptr()` does not
|
|
80
|
-
retain its argument; this closes the same lifetime class. Pure-additive.
|
|
81
|
-
- Added `pinned: pinnedBufferCount()` to the per-step train metric as a permanent
|
|
82
|
-
**canary** for this bug class (visible in `train-watch`).
|
|
83
|
-
|
|
84
|
-
## Lesson for the codebase
|
|
85
|
-
|
|
86
|
-
`fromView` (zero-copy pin) is only safe for buffers that **outlive the MLX
|
|
87
|
-
array** (mmap'd weights — `fromPointer`/`fromView` with process-lifetime memory).
|
|
88
|
-
For any **transient** host buffer handed to a **lazy** op and disposed before
|
|
89
|
-
eval, use the **copying** constructor (`fromBytesCopy`/`fromInt32`/`fromFloat32`).
|
|
90
|
-
Grep for `fromView` in hot paths when adding kernels.
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
# L3 verification approach (no oracle exists)
|
|
95
|
-
|
|
96
|
-
This bug also clarified how we *should* verify the flash-CCE ORPO head, because
|
|
97
|
-
"bf16-class error" was the wrong framing.
|
|
98
|
-
|
|
99
|
-
## The parity tiers
|
|
100
|
-
|
|
101
|
-
- **L1** — bit-for-bit drop-in for **mlx-lm** (proven: 8192/8192 greedy tokens
|
|
102
|
-
identical).
|
|
103
|
-
- **L2** — bit-for-bit drop-in for **mlx-optiq**.
|
|
104
|
-
- **L3** — mlx-bun originals (the flash-CCE ORPO training head). **Neither
|
|
105
|
-
mlx-lm nor optiq ships ORPO+CCE training — there is no oracle to be bit-exact
|
|
106
|
-
against.**
|
|
107
|
-
|
|
108
|
-
So the "0.28% dh" figure is **not an error rate or a degradation.** It is the
|
|
109
|
-
**floating-point reassociation gap between two *correct* implementations** — the
|
|
110
|
-
flash kernel (tiled, online-softmax, fp32) vs the full-logits MLX head (a
|
|
111
|
-
different reduction order) — and the full-logits head is itself only a *proxy*,
|
|
112
|
-
not an oracle. Two correct reductions in different orders cannot be bit-identical;
|
|
113
|
-
0.28% is the inherent floor, not slop.
|
|
114
|
-
|
|
115
|
-
**Correctness in L3 is proven by what does NOT need a reference implementation:**
|
|
116
|
-
|
|
117
|
-
1. **Finite-difference gradient check** (`flash-fd-check.ts`) — the kernel's `dh`
|
|
118
|
-
vs numerical ground truth to ~4 decimals. This is the real proof: it says the
|
|
119
|
-
kernel computes the *right gradient*, independent of any other implementation.
|
|
120
|
-
2. **By-hand math audit** of the dequant, online-LSE recurrence, `coeff =
|
|
121
|
-
onehot − softmax`, dh accumulation, and the `1/M` cotangent (done; clean).
|
|
122
|
-
3. **Agreement with the full-logits proxy** to fp-reassociation precision
|
|
123
|
-
(logp 0.21%, dh 0.28%) — corroboration, not the proof.
|
|
124
|
-
|
|
125
|
-
## The coeff filter — measure on REAL data, not synthetic
|
|
126
|
-
|
|
127
|
-
`flash-cce-parity.ts` uses a **synthetic random hidden**, which gives a
|
|
128
|
-
**flat-ish** softmax (high entropy) → gradient mass spread across many tokens →
|
|
129
|
-
filtering the near-zero coeffs costs accuracy (the recorded 0.66%→2.7%). **That
|
|
130
|
-
is the filter's worst case and not what it's for.** On real trained-model
|
|
131
|
-
outputs the next-token distribution is **sharply peaked** (low entropy):
|
|
132
|
-
`coeff_v = c·(onehot_v − softmax_v)`, and for almost every non-target token
|
|
133
|
-
`softmax_v ≈ 0`, so its coeff ≈ 0 and dropping it costs ~nothing — the gradient
|
|
134
|
-
mass is in the target + the handful of high-softmax tokens. **The filter is
|
|
135
|
-
designed exactly for the peaky real-data regime; the synthetic benchmark
|
|
136
|
-
maximizes its apparent cost.**
|
|
137
|
-
|
|
138
|
-
**MEASURED (2026-06-22, `scripts/experiments/flash-cce-filter-realdata.ts`) — the
|
|
139
|
-
hypothesis holds.** On 8629 real response tokens from real examples (real model
|
|
140
|
-
forward, **real next-token targets**; mean P(true token) = 53.9% → genuinely
|
|
141
|
-
peaked), the filter's dh error vs exact:
|
|
142
|
-
|
|
143
|
-
| eps | dh error | backward speedup |
|
|
144
|
-
|---|---|---|
|
|
145
|
-
| 1e-6 | 0.024% | 1.15× |
|
|
146
|
-
| **1e-5** | **0.162%** (below the 0.33% bf16 floor → indistinguishable) | **1.35×** |
|
|
147
|
-
| 1e-4 | 0.681% | 1.47× |
|
|
148
|
-
| 1e-3 | 1.943% | 1.52× |
|
|
149
|
-
|
|
150
|
-
The synthetic `REAL=1` test (random targets) showed **21.4% @ 1e-4** — a ~30×
|
|
151
|
-
overstatement, an artifact of the onehot landing on a ~0-softmax random token.
|
|
152
|
-
**Verdict: enable the filter at eps ~1e-5** — free speed (error under the
|
|
153
|
-
reassociation floor) on real data. Re-measure as the model trains (peakedness
|
|
154
|
-
shifts), and always with REAL targets, never synthetic.
|
|
155
|
-
|
|
156
|
-
## Standing L3 gates (what to run, since there's no oracle)
|
|
157
|
-
|
|
158
|
-
Per the project's parity-as-correctness model (bit-exact where an ancestor
|
|
159
|
-
overlaps; KL + quality + benchmarks where it doesn't):
|
|
160
|
-
|
|
161
|
-
1. **Filter-on-real-data** — ✅ DONE (above): near-free at eps ~1e-5 on real
|
|
162
|
-
targets (0.16% dh, 1.35× backward). `flash-cce-filter-realdata.ts`.
|
|
163
|
-
2. **Teacher-forced grad fidelity** — flash head vs full-logits head `dh`
|
|
164
|
-
(gradient cosine + relnorm) on real data, as the standing L3 regression gate
|
|
165
|
-
(replaces the synthetic-data parity number for *quality* claims).
|
|
166
|
-
3. **End-to-end quality eval** — the completed training run's downstream task
|
|
167
|
-
quality (the real proof that L3 trains correctly).
|