mlx-bun 0.4.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 +1453 -88
- 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 +10 -8
- 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 +12 -10
- package/src/{serve → server}/chat-stage.ts +27 -22
- package/src/{client.ts → server/client.ts} +28 -2
- package/src/{serve → server}/completion-executor.ts +42 -42
- 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 +42 -83
- package/src/server/generated-token-history.ts +57 -0
- 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 +8 -8
- package/src/{serve → server}/request-prep.ts +82 -68
- 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 -94
- package/docs/archive/hlg-sampling.md +0 -350
- package/docs/archive/investigations/audio-input-plan.md +0 -356
- 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/web-ui-pass-plan.md +0 -213
- package/docs/archive/planning/website-readme-pass-plan.md +0 -224
- package/docs/design/batching.md +0 -1841
- package/docs/design/decode-speed-program.md +0 -2349
- package/docs/design/dreaming-nightly-pipeline.md +0 -684
- package/docs/design/generic-model-support.md +0 -705
- package/docs/design/kv-cache.md +0 -824
- package/docs/design/orpo-training.md +0 -680
- package/docs/design/speculative-decoding.md +0 -1990
- package/docs/design/turboquant.md +0 -2212
- package/docs/design/unified-engine-frontier-plan.md +0 -1880
- package/docs/design/web-chat-redesign.md +0 -695
- package/docs/planning/PRODUCT_ROADMAP.md +0 -594
- package/docs/planning/ResearchTopics.md +0 -119
- package/docs/planning/release-notes-v0.4.0.md +0 -45
- package/docs/planning/video-series.md +0 -421
- package/docs/reference/benchmarks.md +0 -3613
- package/docs/reference/cli.md +0 -585
- package/docs/reference/distribution.md +0 -355
- package/docs/reference/environment.md +0 -398
- package/docs/reference/glossary.md +0 -39
- package/docs/reference/library-api.md +0 -674
- package/docs/reference/memory.md +0 -260
- package/docs/reference/models.md +0 -334
- package/docs/reference/server-api.md +0 -1775
- package/docs/reference/server-config.md +0 -846
- package/docs/reference/training.md +0 -462
- package/docs/reference/troubleshooting.md +0 -158
- package/src/admission-plan.ts +0 -18
- package/src/audio/conformer.ts +0 -647
- package/src/audio/decode.ts +0 -133
- package/src/audio/features.ts +0 -202
- package/src/audio/transcode.ts +0 -67
- 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 -108
- package/src/backends/mlx/batch-group.ts +0 -1790
- 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 -106
- 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/evaluation.ts +0 -105
- package/src/backends/mlx/fill-append.ts +0 -45
- 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 -130
- 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 -133
- 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/model-prompt.ts +0 -164
- package/src/backends/mlx/model-serving.ts +0 -79
- package/src/backends/mlx/prefill-cohort.ts +0 -89
- package/src/backends/mlx/prefill-rows.ts +0 -200
- package/src/backends/mlx/prefill.ts +0 -33
- package/src/backends/mlx/projected-context-rows.ts +0 -72
- package/src/backends/mlx/request-policy.ts +0 -20
- package/src/backends/mlx/request-state-policy.ts +0 -41
- package/src/backends/mlx/rollback.ts +0 -89
- package/src/backends/mlx/row-sampling.ts +0 -49
- package/src/backends/mlx/serial-executor.ts +0 -242
- package/src/backends/mlx/speculative-group.ts +0 -359
- package/src/backends/mlx/speculative-round.ts +0 -161
- package/src/backends/mlx/speculative.ts +0 -114
- 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 -165
- package/src/chat-template.ts +0 -382
- package/src/cli.ts +0 -3168
- package/src/config.ts +0 -399
- package/src/contracts/completion.ts +0 -16
- package/src/contracts/execution.ts +0 -60
- package/src/contracts/generation.ts +0 -103
- package/src/contracts/host.ts +0 -7
- package/src/contracts/pi-provider.ts +0 -30
- package/src/contracts/pi.ts +0 -259
- package/src/contracts/prefix-cache.ts +0 -23
- package/src/contracts/resources.ts +0 -25
- package/src/contracts/sampling.ts +0 -23
- package/src/contracts/scheduling.ts +0 -45
- 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 -53
- package/src/engine/resources.ts +0 -53
- package/src/engine/scheduler.ts +0 -72
- 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 -452
- 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 -1346
- package/src/grammar.ts +0 -591
- 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 -39
- 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 -257
- package/src/kv-store.ts +0 -1099
- package/src/lab/README.md +0 -18
- 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-kv-rows.ts +0 -83
- package/src/lab/paged-kv/paged-kv.ts +0 -270
- package/src/library.ts +0 -40
- package/src/lora.ts +0 -408
- package/src/md-modules.d.ts +0 -49
- 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 -388
- 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 -512
- package/src/mlx/materialize.ts +0 -23
- package/src/mlx/metal-kernel.ts +0 -159
- package/src/mlx/ops.ts +0 -1021
- 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 -93
- 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 -55
- package/src/model/diffusion-gemma.ts +0 -1026
- package/src/model/factory.ts +0 -189
- package/src/model/fingerprint.ts +0 -33
- 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 -2351
- package/src/model/gemma4.ts +0 -1315
- 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 -67
- package/src/model/minicpm5.ts +0 -376
- package/src/model/profile.ts +0 -443
- 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 -252
- package/src/model/qwen3.ts +0 -277
- package/src/model/qwen3_5-checkpoint.ts +0 -164
- package/src/model/qwen3_5.ts +0 -819
- 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 -102
- 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 -83
- 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/native/expert_io.c +0 -523
- package/src/native/frame_extract.swift +0 -140
- package/src/native-pack.ts +0 -162
- 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 -1932
- package/src/preflight.ts +0 -158
- package/src/prompt-cache.ts +0 -460
- 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 -81
- package/src/safetensors.ts +0 -158
- package/src/sampler.ts +0 -1011
- 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/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 -496
- 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 -43
- package/src/serve/model-host.ts +0 -511
- package/src/serve/parent-application.ts +0 -56
- package/src/serve/prompt-response-trace.ts +0 -147
- 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/server.ts +0 -1314
- package/src/spec/assistant-rows.ts +0 -92
- 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 -317
- package/src/spec/glm52-mtp-state.ts +0 -33
- package/src/spec/ngram-source.ts +0 -204
- 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 -219
- package/src/spec/qwen-mtp-source.ts +0 -383
- package/src/spec/qwen-mtp-state.ts +0 -30
- package/src/spec/serve-loop.ts +0 -585
- package/src/spec/source.ts +0 -215
- package/src/spec/two-model-rows.ts +0 -61
- package/src/spec/two-model.ts +0 -113
- package/src/ssd-cache.ts +0 -372
- package/src/ssd-durability.ts +0 -211
- 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 -168
- package/src/vision/preprocess.ts +0 -247
- package/src/vision/prompt.ts +0 -373
- package/src/vision/qwen3vl-preprocess.ts +0 -409
- package/src/vision/qwen3vl-prompt.ts +0 -196
- package/src/vision/qwen3vl-tower.ts +0 -555
- package/src/vision/siglip.ts +0 -599
- package/src/vision/video-frames.ts +0 -148
- package/src/web/app.html +0 -2143
- package/src/web/app.js +0 -5703
- 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 -1057
- package/src/web/src/composer.ts +0 -959
- 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/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/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
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// Own one loaded model, its draft and lazy media towers. Application entry
|
|
2
|
+
// points supply the artifact and options; no HTTP or scheduling lives here.
|
|
3
|
+
import type { KvQuantSpec, ModelConfig } from "@mlx-bun/inference/artifacts/config";
|
|
4
|
+
import type { Weights } from "@mlx-bun/inference/artifacts";
|
|
5
|
+
import type { RuntimeModel, RuntimeOpenOptions, ResolvedModelProfile, ModelImplementationProvider, GenerationDefaults,
|
|
6
|
+
CompositionRequest } from "@mlx-bun/inference/models";
|
|
7
|
+
import type { ChatTemplate, LoadedTokenizer, SentinelTokens } from "@mlx-bun/inference/input";
|
|
8
|
+
import type { AdapterManager } from "@mlx-bun/inference/adapters";
|
|
9
|
+
import type { AudioTokenIds, VisionTokenIds, VisionEncoder } from "@mlx-bun/inference/input/vision";
|
|
10
|
+
import type { AudioEncoder } from "@mlx-bun/inference/contracts/mlx";
|
|
11
|
+
import type { MediaPreparation } from "./media-preparation";
|
|
12
|
+
|
|
13
|
+
/** An audio encoder the context owns; released with it. */
|
|
14
|
+
export type LoadedAudioEncoder = AudioEncoder & { dispose?(): void };
|
|
15
|
+
import { sidecarShipsAudioTower } from "@mlx-bun/hub/registry";
|
|
16
|
+
import { fit } from "@mlx-bun/inference/execution/fit";
|
|
17
|
+
import { cleanupFailure, disposeResources } from "@mlx-bun/inference/runtime/resources";
|
|
18
|
+
import type { Composition, DisposableResource, MemoryPlan } from "@mlx-bun/inference/contracts/portable";
|
|
19
|
+
import type { DraftKind } from "@mlx-bun/inference/generation/speculative/draft-kind";
|
|
20
|
+
import type { DraftProviderRegistry, LoadedDraft } from "@mlx-bun/inference/generation/speculative/draft-registry";
|
|
21
|
+
|
|
22
|
+
export interface ServedModelInfo { readonly config: ModelConfig; readonly weightsBytes: number; }
|
|
23
|
+
|
|
24
|
+
export interface AdapterService extends Pick<AdapterManager, keyof AdapterManager> {}
|
|
25
|
+
|
|
26
|
+
export interface ModelContext<Model = RuntimeModel> {
|
|
27
|
+
/** Releases owned artifacts, draft and loaded towers after execution stops. */
|
|
28
|
+
dispose(): void;
|
|
29
|
+
/** State serialization supplied by this model/backend implementation. */
|
|
30
|
+
stateCodecs?: import("@mlx-bun/inference/state").CacheCodecProvider;
|
|
31
|
+
model: Model;
|
|
32
|
+
/** Declared external artifact/family profile that selected model
|
|
33
|
+
* construction. Request-level methods are resolved separately. */
|
|
34
|
+
profile: ResolvedModelProfile;
|
|
35
|
+
tokenizer: LoadedTokenizer;
|
|
36
|
+
template: ChatTemplate | null;
|
|
37
|
+
/** The model's declared marker tokens for tool calls and the reasoning
|
|
38
|
+
* channel, resolved against `tokenizer`; absent when generated text carries
|
|
39
|
+
* them as ordinary text. */
|
|
40
|
+
sentinels?: SentinelTokens | null;
|
|
41
|
+
/** The model's declared chat-generation defaults; absent means the generic ones. */
|
|
42
|
+
generationDefaults?: GenerationDefaults;
|
|
43
|
+
modelId: string;
|
|
44
|
+
/** Lazily-loaded vision tower cache — null until the first image request
|
|
45
|
+
* (see `getVisionTower`). The tower (SigLIP ~hundreds of MB, encoder-free
|
|
46
|
+
* smaller) is not loaded for text-only sessions. */
|
|
47
|
+
vision: VisionEncoder | null;
|
|
48
|
+
/** Loads + selects the vision tower on demand; null when the model has no
|
|
49
|
+
* (supported) vision sidecar. Invoked at most once, then cached in
|
|
50
|
+
* `vision`. */
|
|
51
|
+
loadVision: (() => VisionEncoder) | null;
|
|
52
|
+
/** Null when neither the config nor the model's declaration supplies the image soft tokens. */
|
|
53
|
+
visionTokenIds: VisionTokenIds | null;
|
|
54
|
+
/** Lazily-loaded Conformer audio tower — null until the first audio
|
|
55
|
+
* request (see `getAudioTower`). Same sidecar file as vision
|
|
56
|
+
* (optiq_vision.safetensors), separate tower; text-only sessions never
|
|
57
|
+
* pay for it. */
|
|
58
|
+
audio: LoadedAudioEncoder | null;
|
|
59
|
+
/** Loads the audio tower on demand; null when the model can't do audio
|
|
60
|
+
* (no `audio_config` in config.json or no sidecar). No flags — audio
|
|
61
|
+
* auto-enables exactly like vision. */
|
|
62
|
+
loadAudio: (() => LoadedAudioEncoder) | null;
|
|
63
|
+
/** null when the model has no `audio_config` (audio-incapable). */
|
|
64
|
+
audioTokenIds: AudioTokenIds | null;
|
|
65
|
+
/** Family media preparation, bound once at load; borrows this context. */
|
|
66
|
+
media: MediaPreparation;
|
|
67
|
+
adapters: AdapterService;
|
|
68
|
+
/** Per-layer KV quantization from the repo's kv_config.json (null if
|
|
69
|
+
* absent): what `--kv-quant config` composes into the loaded model's KV
|
|
70
|
+
* scheme (`composition.kv`). */
|
|
71
|
+
kvConfig: KvQuantSpec[] | null;
|
|
72
|
+
/** Model-author recommended sampling from generation_config.json —
|
|
73
|
+
* optiq serve injects these as server defaults (gen_config.py);
|
|
74
|
+
* explicit request fields always win. */
|
|
75
|
+
genDefaults: GenSamplingDefaults;
|
|
76
|
+
/** Selected draft provider; null means no draft configured. */
|
|
77
|
+
draft?: {
|
|
78
|
+
provider: import("@mlx-bun/inference/generation/speculative").DraftProvider;
|
|
79
|
+
numDraftTokens: number;
|
|
80
|
+
/** Each round's draft count comes from the graph's verify costs (`--num-draft-tokens adaptive`). */
|
|
81
|
+
adaptiveDepth?: boolean;
|
|
82
|
+
/** The adaptive gate's settings the graph measured for this drafter's kind (`adaptiveDraftGates`). */
|
|
83
|
+
gate?: import("@mlx-bun/inference/contracts/mlx").AdaptiveDraftGateSettings;
|
|
84
|
+
/** The provider is the checkpoint's own draft head, not a configured drafter. */
|
|
85
|
+
native?: true;
|
|
86
|
+
/** The registered kind that loaded it (a graph may declare an adaptive width for it). */
|
|
87
|
+
kind?: string;
|
|
88
|
+
} | null;
|
|
89
|
+
/** The composition this load resolved before building the graph: device,
|
|
90
|
+
* KV scheme, draft depth, prefill chunk and
|
|
91
|
+
* rows per forward. Absent for a context an engine-owned implementation or a
|
|
92
|
+
* caller built. */
|
|
93
|
+
readonly composition?: Composition;
|
|
94
|
+
/** Present only for a runtime that plans its memory before opening weights:
|
|
95
|
+
* the exact header-derived process equation it runs under. */
|
|
96
|
+
memoryPlan?: MemoryPlan | null;
|
|
97
|
+
/** Live telemetry of such a runtime, surfaced by `/stats`. */
|
|
98
|
+
runtimeDiagnostics?(): Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type LoadedModelContext = ModelContext<ServedModelInfo>;
|
|
102
|
+
|
|
103
|
+
export interface ModelHostSource {
|
|
104
|
+
readonly modelDir: string;
|
|
105
|
+
readonly modelId: string | undefined;
|
|
106
|
+
readonly options: Omit<LoadContextOptions, "implementations">;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface LoadContextOptions<Model extends ServedModelInfo = RuntimeModel> {
|
|
110
|
+
/** Raw generation and embeddings do not require a chat template. Default true. */
|
|
111
|
+
requireChatTemplate?: boolean;
|
|
112
|
+
/** Engine-owned complete implementations may own their loader and methods.
|
|
113
|
+
* Selection happens before opening the default resident or streamed weights.
|
|
114
|
+
* They receive these options in their source and resolve no composition here. */
|
|
115
|
+
implementations?: ModelImplementationProvider<ModelHostSource, Promise<ModelContext<Model>>, void>;
|
|
116
|
+
profiles?: import("@mlx-bun/inference/models").ResolveModelProfileOptions;
|
|
117
|
+
memoryBudgetBytes?: number;
|
|
118
|
+
/** The KV flags the composition's scheme resolves from (`--kv-quant`, its
|
|
119
|
+
* TurboQuant spelling, `--quantized-kv-start`); absent means bf16. */
|
|
120
|
+
kv?: CompositionRequest["kv"];
|
|
121
|
+
/** Paged KV (`--paged-kv`, `--paged-kv-block-size`); absent means unpaged. */
|
|
122
|
+
pagedKv?: CompositionRequest["pagedKv"];
|
|
123
|
+
/** Most rows one forward carries (`--batch`). Default 8. */
|
|
124
|
+
maxRows?: number;
|
|
125
|
+
/** Resource overrides for runtimes that plan memory up front; models
|
|
126
|
+
* without one ignore them. */
|
|
127
|
+
runtime?: RuntimeOpenOptions;
|
|
128
|
+
/** Snapshot dir of a draft model for speculative decoding
|
|
129
|
+
* (`--draft-model`). Loaded alongside the target; the pair must share
|
|
130
|
+
* a tokenizer family. */
|
|
131
|
+
draftModelDir?: string;
|
|
132
|
+
/** Drafts per round (`--num-draft-tokens`, mlx_lm.server default 3). */
|
|
133
|
+
numDraftTokens?: number;
|
|
134
|
+
/** `--num-draft-tokens adaptive`: choose each round's count, up to numDraftTokens,
|
|
135
|
+
* from the graph's measured verify costs. Refused without a drafter or costs. */
|
|
136
|
+
adaptiveDraftDepth?: boolean;
|
|
137
|
+
/** Draft-provider kind override (`--draft-kind`); any kind the registry knows. */
|
|
138
|
+
draftKind?: DraftKind | (string & {});
|
|
139
|
+
/** The draft providers this load may select and detect among; defaults to
|
|
140
|
+
* the library's. Extend a registry to add a provider kind. */
|
|
141
|
+
draftProviders?: DraftProviderRegistry;
|
|
142
|
+
/** A ready draft provider and its round width, instead of loading one from
|
|
143
|
+
* `draftModelDir`/`draftKind`. The context owns it from the call on. */
|
|
144
|
+
draftProvider?: LoadedDraft;
|
|
145
|
+
ngramMax?: number;
|
|
146
|
+
ngramMin?: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface GenSamplingDefaults {
|
|
150
|
+
temperature?: number;
|
|
151
|
+
topP?: number;
|
|
152
|
+
topK?: number;
|
|
153
|
+
repetitionPenalty?: number;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { detectDraftKind, type DraftKind } from "@mlx-bun/inference/generation/speculative/draft-kind";
|
|
157
|
+
|
|
158
|
+
/** Serving requires a template; one-shot consumers retain main's raw fallback. */
|
|
159
|
+
export async function loadContextTemplate(modelDir: string, required: boolean,
|
|
160
|
+
load: (directory: string) => Promise<ChatTemplate>): Promise<ChatTemplate | null> {
|
|
161
|
+
try { return await load(modelDir); }
|
|
162
|
+
catch (error) { if (required) throw error; return null; }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function requireChatTemplate<T extends { template: ChatTemplate | null; modelId: string }>(context: T): asserts context is T & { template: ChatTemplate } {
|
|
166
|
+
if (!context.template) throw new Error(`model ${context.modelId} has no chat template`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function loadGenSamplingDefaults(modelDir: string): Promise<GenSamplingDefaults> {
|
|
170
|
+
const file = Bun.file(`${modelDir}/generation_config.json`);
|
|
171
|
+
if (!(await file.exists())) return {};
|
|
172
|
+
try {
|
|
173
|
+
const raw = (await file.json()) as Record<string, unknown>;
|
|
174
|
+
const num = (v: unknown) => (typeof v === "number" ? v : undefined);
|
|
175
|
+
return {
|
|
176
|
+
temperature: num(raw.temperature),
|
|
177
|
+
topP: num(raw.top_p),
|
|
178
|
+
topK: num(raw.top_k),
|
|
179
|
+
repetitionPenalty: num(raw.repetition_penalty),
|
|
180
|
+
};
|
|
181
|
+
} catch {
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function loadContext<Model extends ServedModelInfo>(
|
|
187
|
+
modelDir: string, modelId: string | undefined,
|
|
188
|
+
opts: LoadContextOptions<Model> & {
|
|
189
|
+
implementations: ModelImplementationProvider<ModelHostSource, Promise<ModelContext<Model>>, void>;
|
|
190
|
+
},
|
|
191
|
+
): Promise<ModelContext<Model>>;
|
|
192
|
+
export function loadContext(modelDir: string, modelId?: string, opts?: LoadContextOptions): Promise<ModelContext>;
|
|
193
|
+
export function loadContext<Model extends ServedModelInfo>(
|
|
194
|
+
modelDir: string, modelId: string | undefined, opts: LoadContextOptions<Model>,
|
|
195
|
+
): Promise<LoadedModelContext>;
|
|
196
|
+
export async function loadContext(
|
|
197
|
+
modelDir: string, modelId?: string,
|
|
198
|
+
opts: LoadContextOptions<ServedModelInfo> = {},
|
|
199
|
+
): Promise<LoadedModelContext> {
|
|
200
|
+
const { loadModelConfig } = await import("@mlx-bun/inference/artifacts/config");
|
|
201
|
+
const { resolveModelProfile } = await import("@mlx-bun/inference/models/profile");
|
|
202
|
+
const config = await loadModelConfig(modelDir);
|
|
203
|
+
const profile = resolveModelProfile(config, opts.profiles);
|
|
204
|
+
if (opts.implementations) {
|
|
205
|
+
const implementation = opts.implementations.select(config, profile);
|
|
206
|
+
const { implementations: _provider, ...options } = opts;
|
|
207
|
+
return implementation.create({ modelDir, modelId, options }, config, profile);
|
|
208
|
+
}
|
|
209
|
+
const [{ Weights }, { createModel, declaredGraph, openPlannedRuntime, plansMemory, loadModelChatTemplate,
|
|
210
|
+
sentinelDeclarationFor, mediaTokenDeclarationFor, generationDefaultsFor, resolveComposition, stateCodecsOf },
|
|
211
|
+
{ loadTokenizer, resolveSentinelTokens, resolveVisionTokenIds, resolveAudioTokenIds },
|
|
212
|
+
{ AdapterManager }, { bindLegacyDraftTarget }, { defaultDraftProviders }] = await Promise.all([
|
|
213
|
+
import("@mlx-bun/inference/artifacts"), import("@mlx-bun/inference/models"),
|
|
214
|
+
import("@mlx-bun/inference/input"),
|
|
215
|
+
import("@mlx-bun/inference/adapters"), import("@mlx-bun/inference/generation/speculative/binding"),
|
|
216
|
+
import("@mlx-bun/inference/generation/speculative/draft-kind"),
|
|
217
|
+
]);
|
|
218
|
+
const planned = plansMemory(profile);
|
|
219
|
+
const draftRegistry = opts.draftProviders ?? defaultDraftProviders();
|
|
220
|
+
if (opts.draftProvider && (opts.draftModelDir !== undefined || opts.draftKind !== undefined))
|
|
221
|
+
throw new Error("a ready draft provider cannot combine with --draft-model/--draft-kind");
|
|
222
|
+
// A kind that bundles its companion in the target artifact (`--draft-kind
|
|
223
|
+
// mtp`: the artifact's own mtp/ subfolder — single-repo packaging, the
|
|
224
|
+
// companion is a complete model dir the provider already loads) resolves
|
|
225
|
+
// there when no --draft-model is given. Explicit --draft-model still wins; a
|
|
226
|
+
// missing bundle is a clear refusal.
|
|
227
|
+
const bundledCompanion = opts.draftKind === undefined ? undefined : draftRegistry.get(opts.draftKind)?.bundledCompanion;
|
|
228
|
+
if (bundledCompanion && !opts.draftModelDir) {
|
|
229
|
+
const bundled = `${modelDir}/${bundledCompanion}`;
|
|
230
|
+
if (await Bun.file(`${bundled}/config.json`).exists()) {
|
|
231
|
+
opts = { ...opts, draftModelDir: bundled };
|
|
232
|
+
} else {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`--draft-kind ${opts.draftKind} needs a companion: pass --draft-model <dir> or use ` +
|
|
235
|
+
`an artifact that bundles one at <model>/${bundledCompanion}/ (none at ${bundled})`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const externalDraft = opts.draftModelDir !== undefined || opts.draftKind !== undefined || opts.draftProvider !== undefined;
|
|
240
|
+
const resolvedDraftKind = opts.draftModelDir
|
|
241
|
+
? opts.draftKind ?? await draftRegistry.detect(opts.draftModelDir)
|
|
242
|
+
: opts.draftKind;
|
|
243
|
+
if (planned && externalDraft && opts.runtime?.nativeDraft === true)
|
|
244
|
+
throw new Error("the checkpoint-native draft head and --draft-model/--draft-kind are mutually exclusive");
|
|
245
|
+
const owned = new Set<DisposableResource>();
|
|
246
|
+
const release = (resource: DisposableResource) => { owned.delete(resource); resource.dispose(); };
|
|
247
|
+
try {
|
|
248
|
+
let weights: Weights | null = null;
|
|
249
|
+
let model!: RuntimeModel;
|
|
250
|
+
// A runtime that plans memory opens its own graph and owns its native draft head.
|
|
251
|
+
let runtime: Awaited<ReturnType<typeof openPlannedRuntime>> | null = null;
|
|
252
|
+
if (planned) {
|
|
253
|
+
const nativeDraft = !externalDraft && opts.runtime?.nativeDraft !== false;
|
|
254
|
+
const plannedCaches = resolveComposition(config, { kv: opts.kv, pagedKv: opts.pagedKv });
|
|
255
|
+
runtime = await openPlannedRuntime(modelDir, profile, {
|
|
256
|
+
...opts.runtime,
|
|
257
|
+
memoryBudgetBytes: opts.memoryBudgetBytes ?? opts.runtime?.memoryBudgetBytes,
|
|
258
|
+
// Preserve the existing loader's draft residency equation.
|
|
259
|
+
batchSize: nativeDraft || externalDraft ? 1 : opts.runtime?.batchSize,
|
|
260
|
+
// An explicit alternate drafter replaces the native head.
|
|
261
|
+
nativeDraft,
|
|
262
|
+
// Its caches compose the KV scheme and paging the composition resolves below from the same flags.
|
|
263
|
+
kv: plannedCaches.kv, pagedKv: plannedCaches.pagedKv,
|
|
264
|
+
});
|
|
265
|
+
model = runtime.model;
|
|
266
|
+
owned.add(runtime.model);
|
|
267
|
+
} else {
|
|
268
|
+
weights = await Weights.open(modelDir);
|
|
269
|
+
owned.add(weights);
|
|
270
|
+
}
|
|
271
|
+
// memoryBudget enforcement at load: Weights.open only mmaps
|
|
272
|
+
// (no GPU allocation yet), so a model whose weights can never serve
|
|
273
|
+
// within the budget is refused HERE — before any unified-memory
|
|
274
|
+
// commitment — with an actionable error instead of a Metal OOM later.
|
|
275
|
+
if (!planned && opts.memoryBudgetBytes) {
|
|
276
|
+
const weightsBytes = [...weights!.shards.files.values()]
|
|
277
|
+
.reduce((a, f) => a + f.mmap.size, 0);
|
|
278
|
+
const report = fit(config, weightsBytes, 1, undefined, undefined, 0, opts.memoryBudgetBytes);
|
|
279
|
+
if (report.maxSafeContext < 1)
|
|
280
|
+
throw new Error(
|
|
281
|
+
`model does not fit the memory budget: weights ${(weightsBytes / 1e9).toFixed(2)} GB ` +
|
|
282
|
+
`+ prefill transient leave no room for any context within ` +
|
|
283
|
+
`${(opts.memoryBudgetBytes / 1e9).toFixed(2)} GB`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const tokenizer = await loadTokenizer(modelDir);
|
|
287
|
+
|
|
288
|
+
// Speculative decoding: load the draft (mlx_lm.server --draft-model). The
|
|
289
|
+
// draft artifact's KIND selects the provider, each of which recognizes and
|
|
290
|
+
// loads its own artifact; all share ONE serve loop
|
|
291
|
+
// (packages/inference/src/generation/speculative/run.ts). `--draft-kind`
|
|
292
|
+
// overrides the detect. The loaded drafter settles the composition's draft
|
|
293
|
+
// depth, so it loads before the target graph is built; the pairing probe
|
|
294
|
+
// below needs that graph.
|
|
295
|
+
let draft: ModelContext["draft"] = null;
|
|
296
|
+
let validateDraft = true;
|
|
297
|
+
if (opts.draftProvider) {
|
|
298
|
+
draft = opts.draftProvider;
|
|
299
|
+
owned.add(draft.provider);
|
|
300
|
+
} else if (resolvedDraftKind !== undefined) {
|
|
301
|
+
const kind = draftRegistry.get(resolvedDraftKind);
|
|
302
|
+
if (kind && !kind.artifact && opts.draftModelDir)
|
|
303
|
+
throw new Error(`--draft-kind ${resolvedDraftKind} is model-free — drop --draft-model (it would be ignored)`);
|
|
304
|
+
// Every artifact kind names an artifact to load — refuse instead of
|
|
305
|
+
// silently serving without speculation.
|
|
306
|
+
if (kind?.artifact && !opts.draftModelDir)
|
|
307
|
+
throw new Error(`--draft-kind ${resolvedDraftKind} requires --draft-model`);
|
|
308
|
+
draft = await draftRegistry.load(resolvedDraftKind, {
|
|
309
|
+
dir: opts.draftModelDir, target: { vocabSize: config.text.vocabSize, tokenizer },
|
|
310
|
+
numDraftTokens: opts.numDraftTokens, ngram: { max: opts.ngramMax, min: opts.ngramMin },
|
|
311
|
+
});
|
|
312
|
+
owned.add(draft.provider);
|
|
313
|
+
// A model-free provider has no pairing to probe and no weights to budget.
|
|
314
|
+
validateDraft = kind!.artifact;
|
|
315
|
+
}
|
|
316
|
+
// A checkpoint-native draft head (a planned runtime's is the production
|
|
317
|
+
// default) comes from the graph's own declaration: it uses the
|
|
318
|
+
// already-planned bounded auxiliary tier and the same tokenizer, so there is
|
|
319
|
+
// no second artifact or compatibility probe to load. A planned runtime has
|
|
320
|
+
// opened its graph above, so its head settles the depth here; a resident
|
|
321
|
+
// graph declares one only once it is built, below.
|
|
322
|
+
const declaredDraftHead = async (): Promise<ModelContext["draft"]> => {
|
|
323
|
+
const declared = await draftRegistry.native(model);
|
|
324
|
+
return declared ? { ...declared, native: true } : null;
|
|
325
|
+
};
|
|
326
|
+
if (!draft && planned) draft = await declaredDraftHead();
|
|
327
|
+
// Resolved once, before the resident graph is built, which receives it. A
|
|
328
|
+
// planned runtime built its graph from its own memory plan above.
|
|
329
|
+
const composition = resolveComposition(config, { kv: opts.kv, pagedKv: opts.pagedKv, maxRows: opts.maxRows,
|
|
330
|
+
draft: draft && { numDraftTokens: draft.numDraftTokens, adaptive: opts.adaptiveDraftDepth } });
|
|
331
|
+
if (!planned) {
|
|
332
|
+
model = createModel(weights!, config, composition, profile);
|
|
333
|
+
owned.add(model);
|
|
334
|
+
}
|
|
335
|
+
// Generation must stop on the tokenizer's eos_token — the chat turn
|
|
336
|
+
// terminator (e.g. Qwen <|im_end|> = 248046). Some configs (Qwen3.5-4B)
|
|
337
|
+
// declare a different eos_token_id in config.json than the chat format
|
|
338
|
+
// emits, so without this a turn never ends and generation runs away,
|
|
339
|
+
// hallucinating both sides of the dialogue until max_tokens. mlx-lm stops on
|
|
340
|
+
// the tokenizer eos; union it in. No-op when already present (Gemma, 27B).
|
|
341
|
+
if (tokenizer.eosTokenId != null && !config.eosTokenIds.includes(tokenizer.eosTokenId))
|
|
342
|
+
config.eosTokenIds = [...config.eosTokenIds, tokenizer.eosTokenId];
|
|
343
|
+
|
|
344
|
+
if (draft && validateDraft) {
|
|
345
|
+
const { provider } = draft;
|
|
346
|
+
// Fail-fast pairing validation (2026-07-07 review): a provider's grouped
|
|
347
|
+
// rows validate the (target, drafter) pairing when they open — a target
|
|
348
|
+
// that lacks the port they read, a DeepSpec or DFlash 2 target-layer-count
|
|
349
|
+
// mismatch — so a mismatch refuses at load instead of failing every drafted
|
|
350
|
+
// request. Opening prefill rows with no members allocates no row tensors.
|
|
351
|
+
{
|
|
352
|
+
const probeCaches = model.makeCache();
|
|
353
|
+
try {
|
|
354
|
+
provider.grouped.openPrefill({ target: bindLegacyDraftTarget(model, probeCaches), checkpoints: [] }).dispose();
|
|
355
|
+
} catch (err) {
|
|
356
|
+
release(provider);
|
|
357
|
+
throw new Error(
|
|
358
|
+
`--draft-model is incompatible with this target: ${err instanceof Error ? err.message : String(err)}`,
|
|
359
|
+
);
|
|
360
|
+
} finally {
|
|
361
|
+
for (const c of probeCaches) c.dispose();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (opts.memoryBudgetBytes) {
|
|
365
|
+
const targetBytes = weights
|
|
366
|
+
? [...weights.shards.files.values()].reduce((a, f) => a + f.mmap.size, 0)
|
|
367
|
+
: model.weightsBytes;
|
|
368
|
+
// Draft weights shrink the target's envelope. Draft KV is not modeled
|
|
369
|
+
// (small relative to its weights at serve contexts); admission stays
|
|
370
|
+
// approximately conservative via the combined-weights term.
|
|
371
|
+
const report = fit(config, targetBytes + provider.weightsBytes, 1, undefined, undefined, 0, opts.memoryBudgetBytes);
|
|
372
|
+
if (report.maxSafeContext < 1) {
|
|
373
|
+
release(provider);
|
|
374
|
+
throw new Error(
|
|
375
|
+
`target + draft do not fit the memory budget (draft adds ` +
|
|
376
|
+
`${(provider.weightsBytes / 1e9).toFixed(2)} GB)`,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!draft && !planned) draft = await declaredDraftHead();
|
|
383
|
+
|
|
384
|
+
if (draft) owned.add(draft.provider);
|
|
385
|
+
if (opts.adaptiveDraftDepth) {
|
|
386
|
+
if (!draft) throw new Error("--num-draft-tokens adaptive needs a drafter (--draft-model, --draft-kind or the checkpoint's MTP head)");
|
|
387
|
+
const graph = declaredGraph(model);
|
|
388
|
+
if (!graph.verifyRoundCosts)
|
|
389
|
+
throw new Error("--num-draft-tokens adaptive needs a graph measured on this machine (one that declares its verify costs); this model's graph does not");
|
|
390
|
+
// A drafter kind whose adaptive width the graph measured drafts up to it, unless the request named a width, and
|
|
391
|
+
// gates with the settings the graph measured for it.
|
|
392
|
+
const width = opts.numDraftTokens === undefined && draft.kind !== undefined ? graph.adaptiveDraftWidths?.get(draft.kind) : undefined;
|
|
393
|
+
const gate = draft.kind !== undefined ? graph.adaptiveDraftGates?.get(draft.kind) : undefined;
|
|
394
|
+
draft = { ...draft, adaptiveDepth: true, ...(width === undefined ? {} : { numDraftTokens: width }), ...(gate ? { gate } : {}) };
|
|
395
|
+
}
|
|
396
|
+
const adapters = new AdapterManager(model);
|
|
397
|
+
owned.add({ dispose() { disposeResources(adapters.list().map(({ id }) => ({ dispose() { adapters.unmount(id); } }))); } });
|
|
398
|
+
// A compiled decode step borrows graph constants and weights. Retire it first,
|
|
399
|
+
// after the owning engine has drained, including if later loading fails.
|
|
400
|
+
owned.add({ dispose() { declaredGraph(model).releaseCompiledDecode?.(); } });
|
|
401
|
+
const template = await loadContextTemplate(modelDir, opts.requireChatTemplate ?? true, dir => loadModelChatTemplate(dir, profile));
|
|
402
|
+
const id = modelId ?? modelDir.split("/").filter(Boolean).at(-1)!;
|
|
403
|
+
// Soft-token ids: the checkpoint's config, else the token text the profile declares.
|
|
404
|
+
const mediaTokens = mediaTokenDeclarationFor(profile);
|
|
405
|
+
const visionTokenIds = resolveVisionTokenIds(config.raw, tokenizer, mediaTokens?.vision ?? null);
|
|
406
|
+
const audioTokenIds = resolveAudioTokenIds(config.raw, tokenizer, mediaTokens?.audio ?? null);
|
|
407
|
+
const sentinelTexts = sentinelDeclarationFor(profile);
|
|
408
|
+
const { bindMediaPreparation } = await import("./media-preparation");
|
|
409
|
+
// Encoders load lazily (getVisionTower/getAudioTower): text-only sessions
|
|
410
|
+
// never pay for a tower. The graph knows which its checkpoint ships.
|
|
411
|
+
const encoders = await declaredGraph(model).mediaEncoders?.(modelDir, { shipsAudioTower: sidecarShipsAudioTower })
|
|
412
|
+
?? { vision: null, audio: null };
|
|
413
|
+
const stateCodecs = stateCodecsOf(model);
|
|
414
|
+
const context: Omit<ModelContext, "dispose"> = {
|
|
415
|
+
...(stateCodecs ? { stateCodecs } : {}),
|
|
416
|
+
draft,
|
|
417
|
+
model,
|
|
418
|
+
profile,
|
|
419
|
+
sentinels: sentinelTexts ? resolveSentinelTokens(tokenizer, sentinelTexts) : null,
|
|
420
|
+
generationDefaults: generationDefaultsFor(profile),
|
|
421
|
+
composition,
|
|
422
|
+
memoryPlan: runtime?.memoryPlan ?? null,
|
|
423
|
+
...(runtime ? { runtimeDiagnostics: runtime.diagnostics } : {}),
|
|
424
|
+
adapters,
|
|
425
|
+
kvConfig: config.kvQuant,
|
|
426
|
+
genDefaults: await loadGenSamplingDefaults(modelDir),
|
|
427
|
+
tokenizer,
|
|
428
|
+
template,
|
|
429
|
+
modelId: id,
|
|
430
|
+
vision: null,
|
|
431
|
+
loadVision: encoders.vision,
|
|
432
|
+
visionTokenIds,
|
|
433
|
+
// Audio mirrors vision: loaded on the first audio request only
|
|
434
|
+
// (`02d723a:docs/design/generic-model-support.md` §6.6).
|
|
435
|
+
audio: null,
|
|
436
|
+
loadAudio: encoders.audio,
|
|
437
|
+
audioTokenIds,
|
|
438
|
+
// The family route is chosen once; it borrows the towers through this
|
|
439
|
+
// context's lazy slots, so the context stays their only owner.
|
|
440
|
+
media: await bindMediaPreparation({
|
|
441
|
+
modelId: id, tokenizer, template, visionTokenIds, audioTokenIds,
|
|
442
|
+
visionTower: () => getVisionTower(context), audioTower: () => getAudioTower(context),
|
|
443
|
+
}, model),
|
|
444
|
+
};
|
|
445
|
+
return ownModelContext(context, [...owned].reverse());
|
|
446
|
+
} catch (error) { return cleanupFailure(error, () => disposeResources([...owned].reverse())); }
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** Lazily load + cache the vision tower on first use. A sidecar that fails
|
|
450
|
+
* to load is a capability gap, not a fatal error: returns null and the
|
|
451
|
+
* request is answered with a 400 (the loader is cleared so we don't retry
|
|
452
|
+
* a known-bad load every request). */
|
|
453
|
+
export function getVisionTower(ctx: Pick<ModelContext<unknown>, "vision" | "loadVision">): VisionEncoder | null {
|
|
454
|
+
if (ctx.vision) return ctx.vision;
|
|
455
|
+
if (!ctx.loadVision) return null;
|
|
456
|
+
try {
|
|
457
|
+
ctx.vision = ctx.loadVision();
|
|
458
|
+
return ctx.vision;
|
|
459
|
+
} catch (e) {
|
|
460
|
+
console.warn(`vision sidecar not loadable (${(e as Error).message}) — serving text-only`);
|
|
461
|
+
ctx.loadVision = null;
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Lazily load + cache the audio tower on first use. Unlike vision's
|
|
467
|
+
* warn-and-continue (text-only degrade is correct for requests WITHOUT
|
|
468
|
+
* images), this is only ever consulted for requests WITH audio — the
|
|
469
|
+
* caller turns null into an explicit 400, never a silent text-only
|
|
470
|
+
* degrade. A failed load is not retried every request. (The stub-sidecar
|
|
471
|
+
* case — the local 12B state — never gets here: the graph checks the
|
|
472
|
+
* sidecar header and returns a null loader.) */
|
|
473
|
+
export function getAudioTower(ctx: Pick<ModelContext<unknown>, "audio" | "loadAudio">): LoadedAudioEncoder | null {
|
|
474
|
+
if (ctx.audio) return ctx.audio;
|
|
475
|
+
if (!ctx.loadAudio) return null;
|
|
476
|
+
try {
|
|
477
|
+
ctx.audio = ctx.loadAudio();
|
|
478
|
+
return ctx.audio;
|
|
479
|
+
} catch (e) {
|
|
480
|
+
console.warn(`audio sidecar not loadable (${(e as Error).message})`);
|
|
481
|
+
ctx.loadAudio = null;
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Who releases a served context. "owned": its server disposes it once, after
|
|
487
|
+
* execution drains, including when startup fails. "borrowed": its server never
|
|
488
|
+
* disposes it; the caller keeps it usable and disposes it after the server closed. */
|
|
489
|
+
export type ContextOwnership = "owned" | "borrowed";
|
|
490
|
+
|
|
491
|
+
/** The one release rule every serving composition applies to its context. */
|
|
492
|
+
export function releaseContext(context: { dispose(): void }, ownership: ContextOwnership): void {
|
|
493
|
+
if (ownership === "owned") context.dispose();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Attach an idempotent owner to a loaded context. Supplied resources are owned,
|
|
497
|
+
* never borrowed; all are attempted even when an individual cleanup fails. */
|
|
498
|
+
export function ownModelContext<Model extends ServedModelInfo>(
|
|
499
|
+
context: Omit<ModelContext<Model>, "dispose">, resources: readonly DisposableResource[],
|
|
500
|
+
): ModelContext<Model> {
|
|
501
|
+
const owned = [...resources];
|
|
502
|
+
let disposed = false;
|
|
503
|
+
return Object.assign(context, {
|
|
504
|
+
dispose() {
|
|
505
|
+
if (disposed) return;
|
|
506
|
+
disposed = true;
|
|
507
|
+
const media = [context.vision, context.audio].filter((value): value is NonNullable<typeof value> => value != null);
|
|
508
|
+
context.vision = null; context.audio = null;
|
|
509
|
+
context.loadVision = null; context.loadAudio = null;
|
|
510
|
+
disposeResources([...media.map(value => ({ dispose() { value.dispose?.(); } })), ...owned]);
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { MlxHeadForward, MlxHeadRange, MlxSequenceRange, MlxSequenceForward } from "@mlx-bun/inference/contracts/mlx";
|
|
2
|
+
import type { Composition } from "@mlx-bun/inference/contracts/portable";
|
|
3
|
+
import type { ModelContext, LoadedModelContext } from "./model-host";
|
|
4
|
+
import type { ModelBinding } from "./model-binding";
|
|
5
|
+
|
|
6
|
+
/** The loaded composition as `/stats` reports it: the record's fields, with
|
|
7
|
+
* snake_case keys like the rest of that view. */
|
|
8
|
+
function compositionWire(composition: Composition): unknown {
|
|
9
|
+
const wire = (value: unknown): unknown => Array.isArray(value) ? value.map(wire)
|
|
10
|
+
: value !== null && typeof value === "object"
|
|
11
|
+
? Object.fromEntries(Object.entries(value).map(([key, field]) => [key.replace(/[A-Z]/g, c => `_${c.toLowerCase()}`), wire(field)]))
|
|
12
|
+
: value;
|
|
13
|
+
return wire(composition);
|
|
14
|
+
}
|
|
15
|
+
/** Existing model classes enter the interface once here. New implementations
|
|
16
|
+
* supply a binding explicitly and need no forward/makeCache methods. */
|
|
17
|
+
export async function modelServingBinding(context: LoadedModelContext, supplied?: ModelBinding): Promise<ModelBinding> {
|
|
18
|
+
if (supplied) return supplied;
|
|
19
|
+
const [{ declaredGraph, embeddingDeclarationFor }, { embedMany, embeddingTerminatorId, isEmbeddingModel }, { bindMlxGateway }, { MLX_VERSION, deviceArchitecture }, ops, { sequenceHiddenOperation, sequenceLogitsOperation, scoreDecisionAnswers }] = await Promise.all([
|
|
20
|
+
import("@mlx-bun/inference/models"), import("@mlx-bun/inference/embeddings"),
|
|
21
|
+
import("@mlx-bun/inference/execution"), import("@mlx-bun/mlx/ffi"), import("@mlx-bun/mlx/ops"), import("@mlx-bun/inference/scoring"),
|
|
22
|
+
]);
|
|
23
|
+
const ctx = context as ModelContext;
|
|
24
|
+
const model = ctx.model;
|
|
25
|
+
const scoring = model as { readonly sequence?: readonly MlxSequenceRange[]; readonly sequenceHidden?: MlxSequenceForward;
|
|
26
|
+
readonly sequenceLogits?: readonly MlxHeadRange[]; readonly logitsFromHidden?: MlxHeadForward };
|
|
27
|
+
if (typeof model.makeCache !== "function")
|
|
28
|
+
throw new Error("model implementation must supply its serving binding");
|
|
29
|
+
const graph = declaredGraph(model);
|
|
30
|
+
const hasSequence = !!scoring.sequence?.some(range => typeof range.operation === "function") || typeof scoring.sequenceHidden === "function";
|
|
31
|
+
const hasHead = !!scoring.sequenceLogits?.some(range => typeof range.operation === "function") || typeof scoring.logitsFromHidden === "function";
|
|
32
|
+
const adapters = typeof model.loraTargets === "function" && model.loraTargets().size > 0;
|
|
33
|
+
const gateway = bindMlxGateway(model, ctx.draft ?? undefined);
|
|
34
|
+
const capabilities = gateway.capabilities;
|
|
35
|
+
const embedding = isEmbeddingModel(model);
|
|
36
|
+
const declaration = embedding ? embeddingDeclarationFor(ctx.profile) : null;
|
|
37
|
+
if (embedding && !declaration) throw new Error(`profile ${ctx.profile.profile.id} declares no embedding recipe for its embedding graph`);
|
|
38
|
+
const terminator = declaration ? embeddingTerminatorId(ctx.tokenizer, declaration.terminator) : 0;
|
|
39
|
+
return {
|
|
40
|
+
stateCompatibility: `mlx-${MLX_VERSION}-${deviceArchitecture()}`,
|
|
41
|
+
gateway,
|
|
42
|
+
async signal(ids, count, minimum) {
|
|
43
|
+
if (!hasSequence || !hasHead) throw new TypeError("model declares no full-sequence scoring operation for signal");
|
|
44
|
+
const caches = model.makeCache();
|
|
45
|
+
try {
|
|
46
|
+
// The whole prompt as one window (the scoring forward), the head over every position.
|
|
47
|
+
const input = ops.fromInt32(ids, [1, ids.length]);
|
|
48
|
+
let hidden;
|
|
49
|
+
try { hidden = sequenceHiddenOperation(scoring, input, caches, ids.length); } finally { input.dispose(); }
|
|
50
|
+
let logits;
|
|
51
|
+
try { logits = sequenceLogitsOperation(model, hidden); } finally { hidden.dispose(); }
|
|
52
|
+
let f: Float32Array;
|
|
53
|
+
let vocab: number;
|
|
54
|
+
try {
|
|
55
|
+
const [, length, width] = logits.shape as [number, number, number];
|
|
56
|
+
vocab = width;
|
|
57
|
+
const last = logits.slice([0, length - 1, 0], [1, length, width]);
|
|
58
|
+
try { f = last.toFloat32(); } finally { last.dispose(); }
|
|
59
|
+
} finally { logits.dispose(); }
|
|
60
|
+
let mx = -Infinity;
|
|
61
|
+
for (const value of f) if (value > mx) mx = value;
|
|
62
|
+
let sum = 0;
|
|
63
|
+
for (const value of f) sum += Math.exp(value - mx);
|
|
64
|
+
const lse = mx + Math.log(sum);
|
|
65
|
+
const bins = new Array<number>(count).fill(0);
|
|
66
|
+
for (const value of f) {
|
|
67
|
+
const t = Math.max(0, Math.min(1, (value - lse - minimum) / -minimum));
|
|
68
|
+
const bin = Math.min(count - 1, Math.floor(t * count));
|
|
69
|
+
bins[bin] = (bins[bin] ?? 0) + 1;
|
|
70
|
+
}
|
|
71
|
+
return { bins, vocab };
|
|
72
|
+
} finally { for (const cache of caches) cache.dispose(); }
|
|
73
|
+
},
|
|
74
|
+
discovery: { adapters, training: adapters && ((hasSequence && hasHead) || typeof graph.trainable?.denoising?.canvasLogits === "function"),
|
|
75
|
+
dsa: capabilities.sparseAttention, embeddings: embedding, decisions: hasSequence && hasHead },
|
|
76
|
+
...(embedding ? { embed: (inputs: string[], instruction?: string) => embedMany(model, ctx.tokenizer, terminator, inputs, instruction) } : {}),
|
|
77
|
+
...(hasSequence && hasHead ? { decide: async ({ prompt, candidates, vision, signal, execute }: Parameters<NonNullable<ModelBinding["decide"]>>[0]) => {
|
|
78
|
+
if (vision && !graph.bindMediaInput) throw new TypeError("graph declares no media input for decisions");
|
|
79
|
+
return (await scoreDecisionAnswers({ makeCache: () => model.makeCache(),
|
|
80
|
+
forward: (ids, cache, rows) => sequenceHiddenOperation(scoring, ids, cache, rows),
|
|
81
|
+
logits: hidden => sequenceLogitsOperation(model, hidden) }, prompt, candidates,
|
|
82
|
+
{ signal, execute, ...(vision ? { input: graph.bindMediaInput!(vision) } : {}) })).probabilities;
|
|
83
|
+
} } : {}),
|
|
84
|
+
diagnostics() {
|
|
85
|
+
const runtime = ctx.runtimeDiagnostics?.();
|
|
86
|
+
return { ...(ctx.composition ? { composition: compositionWire(ctx.composition) } : {}), ...(runtime ? { runtime } : {}) };
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** Host-side admission before grammar/media preparation can allocate native
|
|
2
2
|
* resources. The implementation shares the generation execution domain. */
|
|
3
3
|
export interface PreparationExecutor {
|
|
4
|
-
reserve?(kind: "media" | "constraint", signal?: AbortSignal): Promise<import("
|
|
4
|
+
reserve?(kind: "media" | "constraint" | "decision", signal?: AbortSignal): Promise<import("@mlx-bun/inference/contracts/portable").DisposableResource>;
|
|
5
5
|
run<T>(prepare: () => Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
import { AdmissionPool } from "
|
|
9
|
-
import { CancellationSource } from "
|
|
8
|
+
import { AdmissionPool } from "@mlx-bun/inference/execution/admission";
|
|
9
|
+
import { CancellationSource } from "@mlx-bun/inference/execution/cancellation";
|
|
10
10
|
|
|
11
11
|
export async function acquireReservation(pool: Pick<AdmissionPool, "acquire">, signal?: AbortSignal) {
|
|
12
12
|
signal?.throwIfAborted();
|
|
@@ -14,20 +14,22 @@ export async function acquireReservation(pool: Pick<AdmissionPool, "acquire">, s
|
|
|
14
14
|
const abort = () => cancellation.cancel("requested");
|
|
15
15
|
signal?.addEventListener("abort", abort, { once: true });
|
|
16
16
|
try { return await pool.acquire(cancellation); }
|
|
17
|
+
catch (error) { throw signal?.aborted ? signal.reason : error; }
|
|
17
18
|
finally { signal?.removeEventListener("abort", abort); }
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
+
/** Bound retained preparations independently from the execution queue. */
|
|
21
22
|
export function createPreparationExecutor(
|
|
22
|
-
run: PreparationExecutor["run"], grammarCapacity: number,
|
|
23
|
+
run: PreparationExecutor["run"], grammarCapacity: number, mediaCapacity = 1,
|
|
23
24
|
): PreparationExecutor & { close(): void } {
|
|
24
|
-
const media = new AdmissionPool(1);
|
|
25
|
+
const media = new AdmissionPool(Math.max(1, mediaCapacity));
|
|
25
26
|
const constraints = new AdmissionPool(Math.max(1, grammarCapacity));
|
|
27
|
+
const decisions = new AdmissionPool(Math.max(1, grammarCapacity));
|
|
26
28
|
return {
|
|
27
29
|
run,
|
|
28
30
|
async reserve(kind, signal) {
|
|
29
|
-
return acquireReservation(kind === "media" ? media : constraints, signal);
|
|
31
|
+
return acquireReservation(kind === "media" ? media : kind === "decision" ? decisions : constraints, signal);
|
|
30
32
|
},
|
|
31
|
-
close() { media.close(); constraints.close(); },
|
|
33
|
+
close() { media.close(); constraints.close(); decisions.close(); },
|
|
32
34
|
};
|
|
33
35
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { cleanupFailure } from "
|
|
2
|
-
import type { ExecutionPlan, InferenceMethod, Timer } from "
|
|
3
|
-
import { CancellationSource, throwIfCancelled } from "
|
|
4
|
-
import { createInferenceEngine } from "
|
|
5
|
-
import type { GenerateStats } from "
|
|
6
|
-
import type { CompletionEngine } from "./completion
|
|
1
|
+
import { cleanupFailure } from "@mlx-bun/inference/runtime/resources";
|
|
2
|
+
import type { ExecutionPlan, InferenceMethod, Timer } from "@mlx-bun/inference/contracts/portable";
|
|
3
|
+
import { CancellationSource, throwIfCancelled } from "@mlx-bun/inference/execution/cancellation";
|
|
4
|
+
import { createInferenceEngine } from "@mlx-bun/inference/execution/engine";
|
|
5
|
+
import type { GenerateStats } from "@mlx-bun/inference/generation";
|
|
6
|
+
import type { CompletionEngine } from "./completion";
|
|
7
7
|
|
|
8
8
|
const timer: Timer = {
|
|
9
9
|
after(ms, callback) {
|