mlx-bun 0.3.0 → 0.5.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 +57 -32
- package/docs/README.md +58 -42
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/archive/planning/release-notes-v0.4.0.md +45 -0
- package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
- package/docs/design/batching.md +1535 -50
- package/docs/design/decode-speed-program.md +394 -1171
- package/docs/design/generic-model-support.md +195 -3
- package/docs/design/kv-cache.md +538 -225
- package/docs/design/speculative-decoding.md +552 -50
- package/docs/design/turboquant.md +140 -17
- package/docs/design/unified-engine-frontier-plan.md +259 -150
- package/docs/planning/PRODUCT_ROADMAP.md +21 -6
- package/docs/planning/public-readiness-handoff.md +244 -0
- package/docs/planning/public-readiness.md +149 -0
- package/docs/reference/benchmarks.md +3537 -1112
- package/docs/reference/cli.md +102 -2
- package/docs/reference/distribution.md +45 -13
- package/docs/reference/environment.md +159 -13
- package/docs/reference/library-api.md +18 -4
- package/docs/reference/memory.md +1 -1
- package/docs/reference/models.md +28 -7
- package/docs/reference/server-api.md +257 -33
- package/docs/reference/server-config.md +372 -151
- package/docs/reference/troubleshooting.md +13 -21
- package/package.json +2 -2
- package/src/audio/audiotoolbox.ts +112 -0
- package/src/audio/conformer.ts +8 -0
- package/src/audio/mic-capture.ts +98 -0
- package/src/audio/silero-vad.ts +379 -0
- package/src/audio/whisper-decode.ts +774 -0
- package/src/audio/whisper-fast.ts +732 -0
- package/src/audio/whisper-format.ts +73 -0
- package/src/audio/whisper-mel.ts +185 -0
- package/src/audio/whisper-timing.ts +274 -0
- package/src/audio/whisper-tokenizer.ts +239 -0
- package/src/audio/whisper-transcribe.ts +410 -0
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +22 -1
- package/src/backends/mlx/batch-group.ts +464 -236
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +107 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/draft-target.ts +7 -22
- package/src/backends/mlx/encoder-cache.ts +25 -0
- package/src/backends/mlx/fill-append.ts +21 -8
- package/src/backends/mlx/fill-group.ts +497 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +122 -19
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +161 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/mixed-iteration.ts +62 -0
- package/src/backends/mlx/model-prompt.ts +46 -32
- package/src/backends/mlx/model-serving.ts +5 -4
- package/src/backends/mlx/paged-cache-codec.ts +27 -0
- package/src/backends/mlx/prefill-cohort.ts +100 -0
- package/src/backends/mlx/prefill-policy.ts +26 -0
- package/src/backends/mlx/prefill-rows.ts +244 -0
- package/src/backends/mlx/prefill.ts +1 -1
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/prompt-input.ts +29 -0
- package/src/backends/mlx/qwen-prompt-input.ts +40 -0
- package/src/backends/mlx/request-state-policy.ts +54 -0
- package/src/backends/mlx/rollback.ts +45 -4
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +51 -73
- package/src/backends/mlx/speculative-group.ts +438 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +24 -7
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +21 -1
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +173 -0
- package/src/cli.ts +333 -36
- package/src/contracts/execution.ts +21 -0
- package/src/contracts/object-cache.ts +8 -0
- package/src/contracts/pi-provider.ts +1 -1
- package/src/contracts/pi.ts +3 -1
- package/src/contracts/prefix-cache.ts +33 -0
- package/src/contracts/resources.ts +7 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +27 -0
- package/src/engine/execution-plan.ts +38 -25
- package/src/engine/execution-tasks.ts +44 -0
- package/src/engine/namespaced-cache.ts +13 -0
- package/src/engine/resources.ts +12 -1
- package/src/engine/scheduler.ts +32 -7
- package/src/fill/fill-session.ts +14 -3
- package/src/fit.ts +29 -15
- package/src/generate.ts +105 -195
- package/src/grammar.ts +13 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/prefill.ts +6 -0
- package/src/inference/rollback.ts +2 -2
- package/src/kv-scheme.ts +92 -12
- package/src/kv-store.ts +183 -93
- package/src/lab/README.md +5 -1
- package/src/lab/paged-kv/paged-attention.ts +117 -0
- package/src/lab/paged-kv/paged-kv-rows.ts +111 -0
- package/src/lab/paged-kv/paged-kv.ts +126 -46
- package/src/md-modules.d.ts +5 -0
- package/src/mlx/array.ts +39 -1
- package/src/mlx/ffi.ts +19 -0
- package/src/mlx/normalized-argmax.ts +116 -0
- package/src/mlx/ops.ts +97 -10
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +12 -4
- package/src/model/batched-quant.ts +8 -2
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +76 -15
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +64 -22
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +94 -0
- package/src/model/compiled-decode.ts +7 -1
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +57 -0
- package/src/model/factory.ts +26 -0
- package/src/model/fingerprint.ts +12 -0
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +183 -82
- package/src/model/gemma4.ts +116 -28
- package/src/model/generated/gemma4-12b.ts +4 -1
- package/src/model/generated/gemma4-26b.ts +4 -1
- package/src/model/generated/gemma4-e4b.ts +4 -1
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/kv-attention-view.ts +70 -0
- package/src/model/minicpm5.ts +11 -7
- package/src/model/profile.ts +29 -4
- package/src/model/quantized-append-attention.ts +31 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3-mrope.ts +7 -9
- package/src/model/qwen3_5.ts +177 -75
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +8 -0
- package/src/model/token-groups.ts +66 -0
- package/src/model/trellis-linear.ts +4 -4
- package/src/model/turboquant-codec.ts +89 -0
- package/src/model/universal/dense.ts +14 -13
- package/src/model/whisper.ts +435 -0
- package/src/native/mic_capture.swift +92 -0
- package/src/native-pack.ts +6 -5
- package/src/pi-web.ts +9 -1
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +306 -86
- package/src/runtime-config.ts +1 -5
- package/src/safetensors.ts +9 -2
- package/src/sampler.ts +37 -25
- package/src/serve/audio-routes.ts +323 -0
- package/src/serve/chat-request.ts +2 -0
- package/src/serve/chat-stage.ts +19 -14
- package/src/serve/completion-executor.ts +9 -1
- package/src/serve/discovery-routes.ts +17 -1
- package/src/serve/generated-token-history.ts +57 -0
- package/src/serve/generation-gateway.ts +88 -38
- package/src/serve/model-binding.ts +7 -1
- package/src/serve/model-host.ts +3 -4
- package/src/serve/preparation.ts +3 -3
- package/src/serve/prompt-response-trace.ts +14 -0
- package/src/serve/request-plan.ts +6 -3
- package/src/serve/request-prep.ts +40 -25
- package/src/serve/text-completion-stage.ts +2 -2
- package/src/serve/transcription-server.ts +64 -0
- package/src/serve/transcription-service.ts +502 -0
- package/src/server.ts +185 -261
- package/src/spec/assistant-rows.ts +93 -0
- package/src/spec/assistant-source.ts +28 -18
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +12 -10
- package/src/spec/dflash-source.ts +28 -4
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/deepspec-module.ts +95 -120
- package/src/spec/dspark/module-dflash.ts +126 -170
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -136
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +115 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +230 -0
- package/src/spec/qwen-mtp-source.ts +84 -226
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +73 -69
- package/src/spec/source.ts +93 -15
- package/src/spec/two-model-rows.ts +62 -0
- package/src/spec/two-model.ts +45 -103
- package/src/ssd-cache.ts +107 -36
- package/src/ssd-durability.ts +29 -27
- package/src/storage/host-buffer.ts +24 -0
- package/src/storage/kv-writer.ts +83 -0
- package/src/storage/kv-writer.worker.js +264 -0
- package/src/storage/retention-policy.ts +37 -0
- package/src/tiered-prompt-cache.ts +45 -0
- package/src/vision/embedder.ts +8 -0
- package/src/vision/prompt.ts +101 -65
- package/src/vision/qwen3vl-prompt.ts +101 -64
- package/src/vision/qwen3vl-tower.ts +11 -0
- package/src/vision/siglip.ts +8 -0
- package/src/web/app.html +12 -0
- package/src/web/app.js +217 -57
- package/src/web/src/chat.ts +4 -1
- package/src/web/src/composer.ts +3 -0
- package/src/web/src/voice.ts +141 -0
- package/src/web/sw.js +1 -1
- package/src/backends/mlx/memory-guard.ts +0 -40
- package/src/spec/prefix-state.ts +0 -45
- /package/docs/{planning → archive/planning}/release-notes-v0.3.0.md +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { MlxArray } from "../../mlx/array";
|
|
2
|
+
import { Dtype } from "../../mlx/ffi";
|
|
3
|
+
import * as ops from "../../mlx/ops";
|
|
4
|
+
import type { Cache, KvDonorAttention } from "../../model/gemma4-base";
|
|
5
|
+
import { captureKvDonorAttention } from "../../model/kv-attention-view";
|
|
6
|
+
import type { AssistantAttention } from "../../spec/drafter";
|
|
7
|
+
import { disposeResources } from "../../engine/resources";
|
|
8
|
+
|
|
9
|
+
/** Bind captured cache validity to Q-only attention. Encoded layouts can
|
|
10
|
+
* supply this same numerical port without changing the draft graph. */
|
|
11
|
+
export function readAssistantDonors(sliding: Cache, full: Cache) {
|
|
12
|
+
const views: KvDonorAttention[] = [], masks: MlxArray[] = [];
|
|
13
|
+
const dispose = () => disposeResources([...masks, ...views]);
|
|
14
|
+
try {
|
|
15
|
+
for (const cache of [sliding, full]) views.push(captureKvDonorAttention(cache));
|
|
16
|
+
const bind = (view: KvDonorAttention): AssistantAttention => {
|
|
17
|
+
// All draft steps borrow the same target snapshot. Build each validity
|
|
18
|
+
// mask once, then reuse it across matching layers and the entire chain.
|
|
19
|
+
const byWindow = new Map<number | null, MlxArray | null>();
|
|
20
|
+
return { attend(query, scale, window) {
|
|
21
|
+
if (!byWindow.has(window)) {
|
|
22
|
+
const width = view.width, B = view.offsets.length;
|
|
23
|
+
const starts = view.starts.map((start, row) => Math.max(start,
|
|
24
|
+
window === null ? start : view.ends[row]! - window));
|
|
25
|
+
let mask: MlxArray | null = null;
|
|
26
|
+
if (starts.some(start => start !== 0) || view.ends.some(end => end !== width)) {
|
|
27
|
+
using columns = ops.arange(0, width, 1, Dtype.int32);
|
|
28
|
+
using lower = ops.fromInt32(starts, [B, 1, 1, 1]);
|
|
29
|
+
using upper = ops.fromInt32([...view.ends], [B, 1, 1, 1]);
|
|
30
|
+
using before = ops.less(columns, lower);
|
|
31
|
+
using after = ops.greaterEqual(columns, upper);
|
|
32
|
+
using invalid = ops.logicalOr(before, after);
|
|
33
|
+
using floats = invalid.astype(view.dtype);
|
|
34
|
+
mask = ops.mulScalar(floats, -1e9);
|
|
35
|
+
masks.push(mask);
|
|
36
|
+
}
|
|
37
|
+
byWindow.set(window, mask);
|
|
38
|
+
}
|
|
39
|
+
const mask = byWindow.get(window)!;
|
|
40
|
+
return view.attend(query, scale, { mode: mask ? "array" : "", arr: mask });
|
|
41
|
+
} };
|
|
42
|
+
};
|
|
43
|
+
return { positions: views[0]!.offsets.map(offset => offset - 1),
|
|
44
|
+
sliding: bind(views[0]!), full: bind(views[1]!), dispose };
|
|
45
|
+
} catch (error) { dispose(); throw error; }
|
|
46
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { MlxArray } from "../../mlx/array";
|
|
2
|
+
import * as ops from "../../mlx/ops";
|
|
3
|
+
import type { AutoregressiveGraph } from "../../inference/graph";
|
|
4
|
+
import type { Cache, BatchableCache, PaddedPrefillCache } from "../../model/gemma4-base";
|
|
5
|
+
import type { DraftRowSampling } from "../../spec/source";
|
|
6
|
+
import { toLogprobs } from "../../sampler";
|
|
7
|
+
import { disposeResources } from "../../engine/resources";
|
|
8
|
+
import { MlxStateRows } from "./state-rows";
|
|
9
|
+
import type { PreparedStateChange } from "../../contracts/resources";
|
|
10
|
+
import { evalCacheState } from "./prefill";
|
|
11
|
+
|
|
12
|
+
export interface MlxDraftLayout extends BatchableCache, PaddedPrefillCache {
|
|
13
|
+
makeEmptyBatch(): MlxDraftLayout;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The draft graph and sampler operate at B. State snapshots own rewind and
|
|
17
|
+
* membership independently of the graph, target verifier and scheduler. */
|
|
18
|
+
export class MlxAutoregressiveDraftRows {
|
|
19
|
+
#history: MlxStateRows[] = [];
|
|
20
|
+
#drafts: number[][] = [];
|
|
21
|
+
#depth = 0;
|
|
22
|
+
#pendingLast: Array<number | null>;
|
|
23
|
+
constructor(readonly graph: AutoregressiveGraph<MlxArray, Cache[], MlxArray>,
|
|
24
|
+
readonly state: MlxStateRows<MlxDraftLayout>, readonly sampling: DraftRowSampling | null) {
|
|
25
|
+
this.#pendingLast = Array.from({ length: state.rowCount }, () => null);
|
|
26
|
+
}
|
|
27
|
+
get pendingLast(): readonly (number | null)[] { return this.#pendingLast; }
|
|
28
|
+
prepareAppend(rows: readonly (readonly Cache[])[], pending: readonly (number | null)[]): PreparedStateChange {
|
|
29
|
+
const state = this.state.prepareAppendMany(rows), next = [...this.#pendingLast, ...pending];
|
|
30
|
+
return { commit: () => { state.commit(); this.#pendingLast = next; }, dispose: () => state.dispose() };
|
|
31
|
+
}
|
|
32
|
+
filterRows(keep: readonly number[]): void {
|
|
33
|
+
this.state.filterRows(keep); this.#pendingLast = keep.map(row => this.#pendingLast[row]!);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async #forward(ids: MlxArray, lengths?: readonly number[]): Promise<MlxArray> {
|
|
37
|
+
const width = ids.shape[1]!, padded = lengths?.some(length => length !== width);
|
|
38
|
+
if (padded) for (const cache of this.state.caches) cache.preparePrefill({
|
|
39
|
+
lengths: [...lengths!], rightPadding: lengths!.map(length => width - length),
|
|
40
|
+
});
|
|
41
|
+
const hidden = await this.graph.forwardHidden(ids, this.state.caches);
|
|
42
|
+
try {
|
|
43
|
+
if (padded) for (const cache of this.state.caches) cache.finalizePrefill();
|
|
44
|
+
return hidden;
|
|
45
|
+
} catch (error) { hidden.dispose(); throw error; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A restored full-accept boundary may have one unprocessed draft token.
|
|
49
|
+
* Combine it with the incoming block on device, retaining per-row lengths. */
|
|
50
|
+
async prefill(tokens: MlxArray): Promise<void> {
|
|
51
|
+
const [B, N] = tokens.shape as [number, number];
|
|
52
|
+
if (this.#pendingLast.some(token => token !== null)) {
|
|
53
|
+
using previous = ops.fromInt32(this.#pendingLast.map(token => token ?? 0), [B, 1]);
|
|
54
|
+
using joined = ops.concatAxis([previous, tokens], 1);
|
|
55
|
+
const lengths = this.#pendingLast.map(token => N + Number(token !== null));
|
|
56
|
+
using indices = ops.fromInt32(lengths.flatMap(length => Array.from({ length: N + 1 },
|
|
57
|
+
(_, position) => length === N + 1 ? position : Math.min(position + 1, N))), [B, N + 1]);
|
|
58
|
+
using inputs = ops.takeAlongAxis(joined, indices, 1);
|
|
59
|
+
(await this.#forward(inputs, lengths)).dispose();
|
|
60
|
+
} else (await this.#forward(tokens)).dispose();
|
|
61
|
+
this.#pendingLast.fill(null); this.materialize();
|
|
62
|
+
}
|
|
63
|
+
materialize(): void { evalCacheState(this.state.caches); }
|
|
64
|
+
|
|
65
|
+
async draft(pending: readonly number[], depth: number, steps: readonly number[]): Promise<number[][]> {
|
|
66
|
+
this.#releaseHistory();
|
|
67
|
+
this.#depth = depth;
|
|
68
|
+
const tokens: MlxArray[] = [];
|
|
69
|
+
const feeds = pending.map((token, row) => this.#pendingLast[row] == null ? [token] : [this.#pendingLast[row]!, token]);
|
|
70
|
+
const lengths = feeds.map(feed => feed.length), width = Math.max(...lengths);
|
|
71
|
+
let ids = ops.fromInt32(feeds.flatMap(feed => [...feed, ...Array(width - feed.length).fill(0)]), [pending.length, width]);
|
|
72
|
+
try {
|
|
73
|
+
// With zero proposals, consume the pending input to keep companion
|
|
74
|
+
// coverage aligned with the target's one-token verification.
|
|
75
|
+
for (let position = 0; position < Math.max(1, depth); position++) {
|
|
76
|
+
const padded = position === 0 && lengths.some(length => length !== width);
|
|
77
|
+
using hidden = await this.#forward(ids, position === 0 ? lengths : undefined);
|
|
78
|
+
this.#history.push(this.state.clone());
|
|
79
|
+
if (depth === 0) break;
|
|
80
|
+
using selection = padded ? ops.fromInt32(lengths.map(length => length - 1), [pending.length, 1, 1]) : null;
|
|
81
|
+
using selected = selection ? ops.takeAlongAxis(hidden, selection, 1) : null;
|
|
82
|
+
using logits = this.graph.projectLogits(selected ?? hidden, { type: "last" });
|
|
83
|
+
using flat = ops.reshape(logits, [pending.length, logits.shape.at(-1)!]);
|
|
84
|
+
using logprobs = toLogprobs(flat);
|
|
85
|
+
const token = this.sampling!.sample(logprobs, steps.map(step => step + position));
|
|
86
|
+
tokens.push(token);
|
|
87
|
+
ids.dispose(); ids = ops.reshape(token, [pending.length, 1]);
|
|
88
|
+
}
|
|
89
|
+
if (depth === 0) this.#drafts = pending.map(() => []);
|
|
90
|
+
else {
|
|
91
|
+
using packed = ops.concatAxis(tokens, 0);
|
|
92
|
+
const values = packed.toIntTokens();
|
|
93
|
+
this.#drafts = pending.map((_, row) => Array.from({ length: depth },
|
|
94
|
+
(_, position) => values[position * pending.length + row]!));
|
|
95
|
+
}
|
|
96
|
+
return this.#drafts;
|
|
97
|
+
} catch (error) { this.#releaseHistory(); throw error; }
|
|
98
|
+
finally { ids.dispose(); disposeResources(tokens); }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Preserve mlx-lm's full-accept re-feed shape: the final draft remains
|
|
102
|
+
* pending and joins the next target token in one forward. Recurrent state
|
|
103
|
+
* uses saved boundaries, never trimming. The lag is companion state. */
|
|
104
|
+
async commit(accepted: readonly number[]): Promise<void> {
|
|
105
|
+
const selected: Cache[][] = [];
|
|
106
|
+
try {
|
|
107
|
+
if (!accepted.every(count => count >= this.#depth - 1)) {
|
|
108
|
+
for (const [row, count] of accepted.entries())
|
|
109
|
+
selected.push(this.#history[Math.min(count, Math.max(0, this.#depth - 1))]!.extractRow(row));
|
|
110
|
+
this.state.mergeRows(selected);
|
|
111
|
+
}
|
|
112
|
+
} finally { disposeResources(selected.flat()); this.#releaseHistory(); }
|
|
113
|
+
|
|
114
|
+
this.#pendingLast = accepted.map((count, row) => this.#depth > 0 && count === this.#depth ? this.#drafts[row]!.at(-1)! : null);
|
|
115
|
+
this.#drafts = []; this.#depth = 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#releaseHistory(): void { disposeResources(this.#history.splice(0)); }
|
|
119
|
+
dispose(): void { this.#releaseHistory(); this.#drafts = []; this.state.dispose(); }
|
|
120
|
+
}
|
|
@@ -5,6 +5,9 @@ import type { RuntimeModel } from "../../model/factory";
|
|
|
5
5
|
import { CompiledDecode } from "../../model/compiled-decode";
|
|
6
6
|
import { runtimeConfig, type RuntimeConfig } from "../../runtime-config";
|
|
7
7
|
import { bindMlxGraph } from "./graph";
|
|
8
|
+
import type { PrefillPolicy } from "../../inference/prefill";
|
|
9
|
+
import { resolveMlxPrefillPolicy } from "./prefill-policy";
|
|
10
|
+
import type { KvSchemeOptions } from "../../kv-scheme";
|
|
8
11
|
|
|
9
12
|
/** One declaration shared by planning and both native execution lanes. */
|
|
10
13
|
export function legacyCompiledDecodeAvailable(model: RuntimeModel): boolean {
|
|
@@ -33,16 +36,33 @@ export interface MlxDecodeStep {
|
|
|
33
36
|
/** Model-owned execution for committed tokens. Recheck the chunk limit after
|
|
34
37
|
* each forward: native arithmetic can change at a cache-length boundary. */
|
|
35
38
|
export interface MlxTokenAppend {
|
|
36
|
-
|
|
39
|
+
/** Affine formats whose committed append retains one-token arithmetic. */
|
|
40
|
+
readonly affineKvBits?: readonly number[];
|
|
41
|
+
readonly turboQuantFormats?: readonly { readonly kBits: number; readonly vBits: number }[];
|
|
42
|
+
/** Maximum positions per row at this cohort size; omitted rows means B=1. */
|
|
43
|
+
maxChunkSize(state: readonly Cache[], rows?: number): number;
|
|
37
44
|
forwardHidden(ids: MlxArray, state: Cache[]): MlxArray | Promise<MlxArray>;
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
/** The model declares which codecs retain its committed-span arithmetic. */
|
|
48
|
+
export function supportsCommittedAppendCache(
|
|
49
|
+
append: Pick<MlxTokenAppend, "affineKvBits" | "turboQuantFormats"> | null | undefined,
|
|
50
|
+
options: Pick<KvSchemeOptions, "kvBits" | "kvConfig" | "turboQuant">,
|
|
51
|
+
): boolean {
|
|
52
|
+
if (options.turboQuant) return !!append?.turboQuantFormats?.some(format =>
|
|
53
|
+
format.kBits === options.turboQuant!.kBits && format.vBits === options.turboQuant!.vBits);
|
|
54
|
+
return options.kvConfig?.length
|
|
55
|
+
? options.kvConfig.every(layer => append?.affineKvBits?.includes(layer.bits))
|
|
56
|
+
: !options.kvBits || !!append?.affineKvBits?.includes(options.kvBits);
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
/** One replaceable binding owns all graph-specific operations used by the AR
|
|
41
60
|
* loop. The current method requires MLX tensors and the legacy Cache[] ABI.
|
|
42
61
|
* Weights are borrowed for the binding's lifetime; generated caches are owned
|
|
43
62
|
* by the run. Caller-provided caches and media remain borrowed. */
|
|
44
63
|
export interface MlxAutoregressiveBinding {
|
|
45
64
|
readonly runtime?: RuntimeConfig;
|
|
65
|
+
readonly prefillPolicy?: PrefillPolicy;
|
|
46
66
|
readonly graph: AutoregressiveGraph<MlxArray, Cache[], MlxArray>;
|
|
47
67
|
readonly eosTokenIds: readonly number[];
|
|
48
68
|
readonly memory: MlxModelMemory;
|
|
@@ -63,6 +83,7 @@ export function bindLegacyAutoregressiveModel(model: RuntimeModel): MlxAutoregre
|
|
|
63
83
|
const runtime = runtimeConfig();
|
|
64
84
|
return {
|
|
65
85
|
runtime,
|
|
86
|
+
prefillPolicy: resolveMlxPrefillPolicy(model.config, runtime),
|
|
66
87
|
graph: bindMlxGraph<Cache[]>(model, {
|
|
67
88
|
id: `legacy:${model.config.modelType}`, artifact: "legacy-resident-model",
|
|
68
89
|
stateAbi: "legacy-cache-array-v1", // not a persistence identity
|