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.
Files changed (133) hide show
  1. package/README.md +112 -20
  2. package/docs/design/batching-v2-plan.md +515 -0
  3. package/docs/design/bucketing-stage.md +196 -0
  4. package/docs/design/compat-cli-surface-design.md +11 -11
  5. package/docs/design/docs-reorg-plan.md +1 -1
  6. package/docs/design/dreaming-nightly-pipeline.md +291 -0
  7. package/docs/design/dspark-speculative-decoding.md +106 -0
  8. package/docs/design/generic-model-support.md +381 -0
  9. package/docs/design/memory-inference-path.md +422 -0
  10. package/docs/design/memory-synthesis.md +287 -0
  11. package/docs/design/minicpm5-decode-megakernel.md +484 -0
  12. package/docs/design/mlx-lm-tool-parity-plan.md +535 -0
  13. package/docs/design/orpo-dynamic-lambda.md +41 -0
  14. package/docs/design/orpo-training.md +35 -5
  15. package/docs/design/parallel-slots.md +12 -1
  16. package/docs/design/parity-tier-dag.md +13 -5
  17. package/docs/design/spec-decode-larger-targets.md +229 -0
  18. package/docs/design/tauri-desktop-app.md +2 -2
  19. package/docs/design/the-dreaming-handoff.md +141 -0
  20. package/docs/design/the-dreaming-master-plan.md +446 -0
  21. package/docs/design/write-pipeline-entity.md +468 -0
  22. package/docs/investigations/curve-bisector-routing.md +1 -1
  23. package/docs/investigations/decode-roofline-lookagain.md +262 -0
  24. package/docs/investigations/dspark-handoff.md +58 -0
  25. package/docs/investigations/kernel-perf-review-2026-07.md +92 -0
  26. package/docs/planning/PRODUCT_ROADMAP.md +2 -2
  27. package/docs/planning/curve-sampler-research-plan.md +331 -0
  28. package/docs/planning/memory-docs-and-dag-plan.md +185 -0
  29. package/docs/planning/pi-integration-review.md +178 -0
  30. package/docs/planning/release-notes-v0.0.9.md +122 -0
  31. package/docs/planning/web-ui-pass-plan.md +213 -0
  32. package/docs/planning/website-readme-pass-plan.md +224 -0
  33. package/docs/reference/cli.md +255 -0
  34. package/docs/reference/embedding.md +7 -2
  35. package/docs/reference/library-api.md +22 -3
  36. package/docs/reference/memory.md +5 -5
  37. package/docs/reference/models.md +137 -0
  38. package/docs/reference/orpo-quickstart.md +6 -0
  39. package/docs/reference/server-api.md +187 -9
  40. package/docs/reference/server-config.md +40 -9
  41. package/docs/reference/training.md +25 -11
  42. package/package.json +1 -1
  43. package/src/anthropic.ts +22 -0
  44. package/src/assets/curve-designer.html +1 -1
  45. package/src/chat-template.ts +25 -7
  46. package/src/cli.ts +1029 -86
  47. package/src/config.ts +4 -1
  48. package/src/download.ts +99 -8
  49. package/src/embed.ts +77 -0
  50. package/src/eval/perplexity.ts +164 -0
  51. package/src/eval/runner.ts +15 -6
  52. package/src/eval/tasks/bfcl.ts +1 -1
  53. package/src/generate.ts +141 -18
  54. package/src/harness-pi.ts +43 -17
  55. package/src/index.ts +2 -0
  56. package/src/lora.ts +2 -2
  57. package/src/memory/article.ts +558 -0
  58. package/src/memory/chunk-validation.ts +150 -0
  59. package/src/memory/chunk.ts +305 -12
  60. package/src/memory/cluster.ts +344 -17
  61. package/src/memory/coverage.ts +134 -0
  62. package/src/memory/crosslink.ts +0 -0
  63. package/src/memory/db.ts +184 -45
  64. package/src/memory/entity.ts +203 -0
  65. package/src/memory/gate.ts +187 -0
  66. package/src/memory/history.ts +152 -0
  67. package/src/memory/ingest.ts +324 -11
  68. package/src/memory/model.ts +321 -0
  69. package/src/memory/normalize.ts +671 -0
  70. package/src/memory/parse.ts +22 -0
  71. package/src/memory/pipeline.ts +242 -30
  72. package/src/memory/prompts.ts +42 -0
  73. package/src/memory/query.ts +334 -0
  74. package/src/memory/reconcile.ts +734 -0
  75. package/src/memory/redirect.ts +108 -0
  76. package/src/memory/reindex.ts +325 -0
  77. package/src/memory/resolve.ts +307 -0
  78. package/src/memory/route.ts +272 -0
  79. package/src/memory/schedule.ts +6 -4
  80. package/src/memory/stages.ts +774 -0
  81. package/src/memory/synthesize.ts +1629 -20
  82. package/src/memory/tools.ts +446 -96
  83. package/src/memory/vault.ts +462 -35
  84. package/src/memory/wikify.ts +1057 -16
  85. package/src/mlx/ffi.ts +3 -0
  86. package/src/mlx/ops.ts +46 -0
  87. package/src/model/compiled-decode.ts +43 -8
  88. package/src/model/factory.ts +29 -8
  89. package/src/model/flash-attention.ts +4 -2
  90. package/src/model/fused-decode-kernel.ts +14 -7
  91. package/src/model/fused-mlp-kernel.ts +169 -0
  92. package/src/model/fused-swiglu-kernel.ts +240 -0
  93. package/src/model/gemma4-base.ts +62 -6
  94. package/src/model/gemma4.ts +33 -2
  95. package/src/model/minicpm5.ts +106 -8
  96. package/src/model/qwen3.ts +276 -0
  97. package/src/model/steel-linear-kernel.ts +96 -0
  98. package/src/model/support.ts +38 -8
  99. package/src/model/universal/archs.ts +332 -0
  100. package/src/model/universal/dense.ts +576 -0
  101. package/src/model/universal/modules.ts +163 -0
  102. package/src/model/universal/rope.ts +366 -0
  103. package/src/pi-provider.ts +1 -1
  104. package/src/pi-terminal.ts +29 -8
  105. package/src/pi-web.ts +19 -2
  106. package/src/quantize/config-writer.ts +1 -1
  107. package/src/registry.ts +215 -7
  108. package/src/responses.ts +23 -0
  109. package/src/sampler.ts +220 -9
  110. package/src/serve/batch-scheduler.ts +272 -80
  111. package/src/serve/generation-gateway.ts +86 -18
  112. package/src/server.ts +612 -49
  113. package/src/spec/drafter.ts +81 -49
  114. package/src/spec/dspark/data-dflash.ts +156 -0
  115. package/src/spec/dspark/data.ts +201 -0
  116. package/src/spec/dspark/generate-dflash.ts +193 -0
  117. package/src/spec/dspark/generate.ts +290 -0
  118. package/src/spec/dspark/loss.ts +213 -0
  119. package/src/spec/dspark/module-dflash.ts +361 -0
  120. package/src/spec/dspark/module.ts +540 -0
  121. package/src/spec/dspark/sample.ts +112 -0
  122. package/src/spec/generate.ts +40 -19
  123. package/src/status-page.html +1 -1
  124. package/src/tokenizer.ts +7 -0
  125. package/src/train/flash-cce.ts +70 -15
  126. package/src/train/fuse.ts +243 -0
  127. package/src/train/index.ts +3 -0
  128. package/src/train/job.ts +8 -0
  129. package/src/train/loss.ts +289 -30
  130. package/src/train/prefix-shared.ts +33 -5
  131. package/src/train/segmented.ts +67 -11
  132. package/src/train/trainer.ts +92 -28
  133. 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 drains
15
- // the batch (waits for it to empty), then runs solo; queued batchable requests
16
- // wait for that solo run to finish. (mlx-lm's drain behavior.)
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 bf16 KV (no kv-quant — the
19
- // batched scheduler runs bf16; mixed-precision-KV batching is the novel-combo
20
- // L2 follow-up) AND no vision AND no LoRA adapters AND no repetition penalty
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 type { GenerateOptions, GenerateStats } from "../generate";
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
- export type OnToken = (token: number) => void | boolean | Promise<void | boolean>;
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
- const release = await this.#mutex.acquire();
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
  }