mlx-bun 0.0.8 → 0.0.9
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 +112 -20
- package/docs/design/batching-v2-plan.md +515 -0
- package/docs/design/bucketing-stage.md +196 -0
- package/docs/design/compat-cli-surface-design.md +11 -11
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dreaming-nightly-pipeline.md +291 -0
- package/docs/design/dspark-speculative-decoding.md +106 -0
- package/docs/design/generic-model-support.md +381 -0
- package/docs/design/memory-inference-path.md +422 -0
- package/docs/design/memory-synthesis.md +287 -0
- package/docs/design/minicpm5-decode-megakernel.md +484 -0
- package/docs/design/mlx-lm-tool-parity-plan.md +535 -0
- package/docs/design/orpo-dynamic-lambda.md +41 -0
- package/docs/design/orpo-training.md +35 -5
- package/docs/design/parallel-slots.md +12 -1
- package/docs/design/parity-tier-dag.md +13 -5
- package/docs/design/spec-decode-larger-targets.md +229 -0
- package/docs/design/tauri-desktop-app.md +2 -2
- package/docs/design/the-dreaming-handoff.md +141 -0
- package/docs/design/the-dreaming-master-plan.md +446 -0
- package/docs/design/write-pipeline-entity.md +468 -0
- package/docs/investigations/curve-bisector-routing.md +1 -1
- package/docs/investigations/decode-roofline-lookagain.md +262 -0
- package/docs/investigations/dspark-handoff.md +58 -0
- package/docs/investigations/kernel-perf-review-2026-07.md +92 -0
- package/docs/planning/PRODUCT_ROADMAP.md +2 -2
- package/docs/planning/curve-sampler-research-plan.md +331 -0
- package/docs/planning/memory-docs-and-dag-plan.md +185 -0
- package/docs/planning/pi-integration-review.md +178 -0
- package/docs/planning/release-notes-v0.0.9.md +122 -0
- package/docs/planning/web-ui-pass-plan.md +213 -0
- package/docs/planning/website-readme-pass-plan.md +224 -0
- package/docs/reference/cli.md +255 -0
- package/docs/reference/embedding.md +7 -2
- package/docs/reference/library-api.md +22 -3
- package/docs/reference/memory.md +5 -5
- package/docs/reference/models.md +137 -0
- package/docs/reference/orpo-quickstart.md +6 -0
- package/docs/reference/server-api.md +187 -9
- package/docs/reference/server-config.md +40 -9
- package/docs/reference/training.md +25 -11
- package/package.json +1 -1
- package/src/anthropic.ts +22 -0
- package/src/assets/curve-designer.html +1 -1
- package/src/chat-template.ts +25 -7
- package/src/cli.ts +1029 -86
- package/src/config.ts +4 -1
- package/src/download.ts +99 -8
- package/src/embed.ts +77 -0
- package/src/eval/perplexity.ts +164 -0
- package/src/eval/runner.ts +15 -6
- package/src/eval/tasks/bfcl.ts +1 -1
- package/src/generate.ts +141 -18
- package/src/harness-pi.ts +43 -17
- package/src/index.ts +2 -0
- package/src/lora.ts +2 -2
- package/src/memory/article.ts +558 -0
- package/src/memory/chunk-validation.ts +150 -0
- package/src/memory/chunk.ts +305 -12
- package/src/memory/cluster.ts +344 -17
- package/src/memory/coverage.ts +134 -0
- package/src/memory/crosslink.ts +0 -0
- package/src/memory/db.ts +184 -45
- package/src/memory/entity.ts +203 -0
- package/src/memory/gate.ts +187 -0
- package/src/memory/history.ts +152 -0
- package/src/memory/ingest.ts +324 -11
- package/src/memory/model.ts +321 -0
- package/src/memory/normalize.ts +671 -0
- package/src/memory/parse.ts +22 -0
- package/src/memory/pipeline.ts +242 -30
- package/src/memory/prompts.ts +42 -0
- package/src/memory/query.ts +334 -0
- package/src/memory/reconcile.ts +734 -0
- package/src/memory/redirect.ts +108 -0
- package/src/memory/reindex.ts +325 -0
- package/src/memory/resolve.ts +307 -0
- package/src/memory/route.ts +272 -0
- package/src/memory/schedule.ts +6 -4
- package/src/memory/stages.ts +774 -0
- package/src/memory/synthesize.ts +1629 -20
- package/src/memory/tools.ts +446 -96
- package/src/memory/vault.ts +462 -35
- package/src/memory/wikify.ts +1057 -16
- package/src/mlx/ffi.ts +3 -0
- package/src/mlx/ops.ts +46 -0
- package/src/model/compiled-decode.ts +43 -8
- package/src/model/factory.ts +29 -8
- package/src/model/flash-attention.ts +4 -2
- package/src/model/fused-decode-kernel.ts +14 -7
- package/src/model/fused-mlp-kernel.ts +169 -0
- package/src/model/fused-swiglu-kernel.ts +240 -0
- package/src/model/gemma4-base.ts +62 -6
- package/src/model/gemma4.ts +33 -2
- package/src/model/minicpm5.ts +106 -8
- package/src/model/qwen3.ts +276 -0
- package/src/model/steel-linear-kernel.ts +96 -0
- package/src/model/support.ts +38 -8
- package/src/model/universal/archs.ts +332 -0
- package/src/model/universal/dense.ts +576 -0
- package/src/model/universal/modules.ts +163 -0
- package/src/model/universal/rope.ts +366 -0
- package/src/pi-provider.ts +1 -1
- package/src/pi-terminal.ts +29 -8
- package/src/pi-web.ts +19 -2
- package/src/quantize/config-writer.ts +1 -1
- package/src/registry.ts +215 -7
- package/src/responses.ts +23 -0
- package/src/sampler.ts +220 -9
- package/src/serve/batch-scheduler.ts +272 -80
- package/src/serve/generation-gateway.ts +86 -18
- package/src/server.ts +612 -49
- package/src/spec/drafter.ts +81 -49
- package/src/spec/dspark/data-dflash.ts +156 -0
- package/src/spec/dspark/data.ts +201 -0
- package/src/spec/dspark/generate-dflash.ts +193 -0
- package/src/spec/dspark/generate.ts +290 -0
- package/src/spec/dspark/loss.ts +213 -0
- package/src/spec/dspark/module-dflash.ts +361 -0
- package/src/spec/dspark/module.ts +540 -0
- package/src/spec/dspark/sample.ts +112 -0
- package/src/spec/generate.ts +40 -19
- package/src/status-page.html +1 -1
- package/src/tokenizer.ts +7 -0
- package/src/train/flash-cce.ts +70 -15
- package/src/train/fuse.ts +243 -0
- package/src/train/index.ts +3 -0
- package/src/train/job.ts +8 -0
- package/src/train/loss.ts +289 -30
- package/src/train/prefix-shared.ts +33 -5
- package/src/train/segmented.ts +67 -11
- package/src/train/trainer.ts +92 -28
- package/src/web/skills/memory/SKILL.md +40 -17
|
@@ -11,13 +11,22 @@
|
|
|
11
11
|
// queue). One AsyncMutex enforces it: a serial run holds it for its duration;
|
|
12
12
|
// the scheduler holds it for its whole active period (first admit → batch
|
|
13
13
|
// empties). So batched requests run concurrently with EACH OTHER (the point),
|
|
14
|
-
// but never alongside a serial-lane generation. A non-batchable request
|
|
15
|
-
// the batch
|
|
16
|
-
//
|
|
14
|
+
// but never alongside a serial-lane generation. A non-batchable request DRAINS
|
|
15
|
+
// the batch: while any serial-lane request is waiting, the scheduler stops
|
|
16
|
+
// admitting new rows (admissionHeld), finishes the running ones, and releases
|
|
17
|
+
// the mutex; the serial request runs solo, then kick() resumes admission.
|
|
18
|
+
// (mlx-lm's drain_batch semantics — without this, sustained batchable traffic
|
|
19
|
+
// starves the serial lane forever.) Non-generation GPU/model-state work (the
|
|
20
|
+
// curve endpoints, adapter mount/unmount) goes through runExclusive() so there
|
|
21
|
+
// is exactly ONE mutual-exclusion domain.
|
|
17
22
|
//
|
|
18
|
-
// v1 batchable gate (the rest → serial): batch>1 AND
|
|
19
|
-
//
|
|
20
|
-
//
|
|
23
|
+
// v1 batchable gate (the rest → serial): batch>1 AND every model cache is a
|
|
24
|
+
// dynamic-B-capable type (KVCache | RotatingKVCache — mirrors mlx-lm
|
|
25
|
+
// server.py's all-caches-have-merge check; Qwen3.5's SSMCache has no
|
|
26
|
+
// merge/filter/temporalView, so hybrid models route serial until the ArraysCache
|
|
27
|
+
// port — batching-v2-plan item h) AND bf16 KV (no kv-quant — the batched
|
|
28
|
+
// scheduler runs bf16; mixed-precision-KV batching is the novel-combo L2
|
|
29
|
+
// follow-up) AND no vision AND no LoRA adapters AND no repetition penalty
|
|
21
30
|
// (per-row logits processors are a later refinement) AND no user-fixed seed
|
|
22
31
|
// (reproducibility ⇒ solo, matching mlx-lm's _is_batchable). Temperature /
|
|
23
32
|
// top-p / top-k DO batch (each row samples with its own seed). Full-attention
|
|
@@ -28,7 +37,8 @@
|
|
|
28
37
|
import { MlxArray } from "../mlx/array";
|
|
29
38
|
import type { RuntimeModel } from "../model/factory";
|
|
30
39
|
import { DiffusionGemmaModel } from "../model/diffusion-gemma";
|
|
31
|
-
import
|
|
40
|
+
import { KVCache, RotatingKVCache } from "../model/gemma4-base";
|
|
41
|
+
import type { GenerateOptions, GenerateStats, TokenLogprobs } from "../generate";
|
|
32
42
|
import { makeSampler, toLogprobs } from "../sampler";
|
|
33
43
|
import { BatchScheduler } from "./batch-scheduler";
|
|
34
44
|
|
|
@@ -46,8 +56,13 @@ class AsyncMutex {
|
|
|
46
56
|
|
|
47
57
|
export type Vision = { embeddings: MlxArray; imageMask: MlxArray };
|
|
48
58
|
|
|
49
|
-
/** Per-token sink: returning `false` halts this generation (stop sequence).
|
|
50
|
-
|
|
59
|
+
/** Per-token sink: returning `false` halts this generation (stop sequence).
|
|
60
|
+
* `logprobs` is only populated on the serial lane when the request asked for
|
|
61
|
+
* logprobs capture (GenerateOptions.logprobs / topLogprobs). */
|
|
62
|
+
export type OnToken = (
|
|
63
|
+
token: number,
|
|
64
|
+
logprobs?: TokenLogprobs,
|
|
65
|
+
) => void | boolean | Promise<void | boolean>;
|
|
51
66
|
|
|
52
67
|
/** The serial lane — exactly today's runGeneration (prompt-cache reuse + the
|
|
53
68
|
* generate() pipeline). The gateway calls it under the mutex. */
|
|
@@ -67,12 +82,29 @@ export interface RequestShape {
|
|
|
67
82
|
userSeed: boolean;
|
|
68
83
|
/** KV quantization is active (kvConfig/kvBits) — batched is bf16-only in v1. */
|
|
69
84
|
kvQuant: boolean;
|
|
85
|
+
/** Any of the mlx-lm sampler/processor extensions is active: min_p, XTC,
|
|
86
|
+
* logit_bias, presence/frequency penalty. Safe v1: they ALL route to the
|
|
87
|
+
* serial lane alongside repetition penalty. min_p/XTC are per-row samplers
|
|
88
|
+
* and could batch (the batched lane already builds a per-row sampler);
|
|
89
|
+
* keeping them serial until the batched path grows per-row logits
|
|
90
|
+
* processors keeps one gate for the whole family. */
|
|
91
|
+
hasLogitsExtras: boolean;
|
|
92
|
+
/** The request asked for logprobs/top_logprobs capture. Batch-lane logprobs
|
|
93
|
+
* are deferred (the scheduler's per-row sampler doesn't capture or read
|
|
94
|
+
* back logprob arrays yet), so these route to the serial lane like the
|
|
95
|
+
* other mlx-lm request extensions above. */
|
|
96
|
+
wantsLogprobs: boolean;
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
export class GenerationGateway {
|
|
73
100
|
readonly #mutex = new AsyncMutex();
|
|
74
101
|
readonly #batch: number;
|
|
75
102
|
#scheduler: BatchScheduler | null = null;
|
|
103
|
+
/** Serial-lane requests waiting for / holding the mutex. While > 0 the
|
|
104
|
+
* scheduler pauses admission (drain) so they can't be starved. */
|
|
105
|
+
#serialWaiters = 0;
|
|
106
|
+
/** Lazy, memoized: can this model's caches do the dynamic-B ops? */
|
|
107
|
+
#cacheBatchable: boolean | null = null;
|
|
76
108
|
|
|
77
109
|
constructor(
|
|
78
110
|
private readonly model: RuntimeModel,
|
|
@@ -92,21 +124,60 @@ export class GenerationGateway {
|
|
|
92
124
|
return this.#scheduler?.activeRows ?? 0;
|
|
93
125
|
}
|
|
94
126
|
|
|
127
|
+
/** Cache-capability gate (mirrors mlx-lm server.py's all-caches-have-merge
|
|
128
|
+
* check): the scheduler's dynamic-B ops (temporalView/merge/filter) exist
|
|
129
|
+
* only on KVCache and RotatingKVCache. A hybrid model (Qwen3.5's SSMCache)
|
|
130
|
+
* routes serial until the batched-SSM port (batching-v2-plan item h). */
|
|
131
|
+
#modelCachesBatchable(): boolean {
|
|
132
|
+
if (this.#cacheBatchable === null) {
|
|
133
|
+
const proto = this.model.makeCache(); // fresh caches hold no buffers
|
|
134
|
+
this.#cacheBatchable = proto.every(
|
|
135
|
+
(c) => c instanceof KVCache || c instanceof RotatingKVCache,
|
|
136
|
+
);
|
|
137
|
+
for (const c of proto) c.dispose();
|
|
138
|
+
}
|
|
139
|
+
return this.#cacheBatchable;
|
|
140
|
+
}
|
|
141
|
+
|
|
95
142
|
/** Decide whether a request joins the batch or runs serially. */
|
|
96
143
|
willBatch(shape: RequestShape): boolean {
|
|
97
144
|
// DiffusionGemma is non-autoregressive — the batch scheduler assumes the AR
|
|
98
145
|
// KV-cache decode path, so it always runs serially through generate().
|
|
99
146
|
if (this.model instanceof DiffusionGemmaModel) return false;
|
|
147
|
+
if (!this.batchingEnabled) return false;
|
|
148
|
+
if (!this.#modelCachesBatchable()) return false;
|
|
100
149
|
return (
|
|
101
|
-
this.batchingEnabled &&
|
|
102
150
|
!shape.hasVision &&
|
|
103
151
|
!shape.hasAdapters &&
|
|
104
152
|
!shape.hasRepetitionPenalty &&
|
|
153
|
+
!shape.hasLogitsExtras &&
|
|
154
|
+
!shape.wantsLogprobs &&
|
|
105
155
|
!shape.userSeed &&
|
|
106
156
|
!shape.kvQuant
|
|
107
157
|
);
|
|
108
158
|
}
|
|
109
159
|
|
|
160
|
+
/** Run `fn` with exclusive ownership of the GPU + shared model state (the
|
|
161
|
+
* serial lane's lock). THE single mutual-exclusion domain: the serial
|
|
162
|
+
* generation path, the curve /generate + /signal endpoints, and adapter
|
|
163
|
+
* mount/unmount all come through here, so nothing runs concurrently with
|
|
164
|
+
* batched decode steps (or with each other). Registers as a serial waiter
|
|
165
|
+
* so the scheduler drains (stops admitting) until `fn` has run. */
|
|
166
|
+
async runExclusive<T>(fn: () => Promise<T>): Promise<T> {
|
|
167
|
+
this.#serialWaiters++;
|
|
168
|
+
try {
|
|
169
|
+
const release = await this.#mutex.acquire();
|
|
170
|
+
try {
|
|
171
|
+
return await fn();
|
|
172
|
+
} finally {
|
|
173
|
+
release();
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
this.#serialWaiters--;
|
|
177
|
+
if (this.#serialWaiters === 0) this.#scheduler?.kick(); // resume admission
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
110
181
|
/** Run one generation on the appropriate lane. onToken is invoked per emitted
|
|
111
182
|
* token (its `false` halts); resolves with stats when the generation ends. */
|
|
112
183
|
async run(
|
|
@@ -116,14 +187,8 @@ export class GenerationGateway {
|
|
|
116
187
|
vision: Vision | undefined,
|
|
117
188
|
shape: RequestShape,
|
|
118
189
|
): Promise<GenerateStats> {
|
|
119
|
-
if (!this.willBatch(shape))
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
return await this.serialRun(promptIds, options, onToken, vision);
|
|
123
|
-
} finally {
|
|
124
|
-
release();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
190
|
+
if (!this.willBatch(shape))
|
|
191
|
+
return this.runExclusive(() => this.serialRun(promptIds, options, onToken, vision));
|
|
127
192
|
|
|
128
193
|
// Per-row sampler, mirroring generate()'s sampleStep (no logits processors:
|
|
129
194
|
// repetition penalty was excluded by willBatch). Greedy (temperature 0) is
|
|
@@ -158,6 +223,9 @@ export class GenerationGateway {
|
|
|
158
223
|
this.#scheduler = new BatchScheduler(this.model, {
|
|
159
224
|
maxBatch: this.#batch,
|
|
160
225
|
lock: { acquire: () => this.#mutex.acquire() },
|
|
226
|
+
// Drain: pause admission while any serial-lane request waits; the
|
|
227
|
+
// runExclusive finally-block kick()s the loop back awake.
|
|
228
|
+
admissionHeld: () => this.#serialWaiters > 0,
|
|
161
229
|
});
|
|
162
230
|
return this.#scheduler;
|
|
163
231
|
}
|