mlx-bun 0.0.8 → 0.0.10

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 (144) hide show
  1. package/README.md +112 -20
  2. package/docs/design/batching-perf-path.md +232 -0
  3. package/docs/design/batching-v2-plan.md +515 -0
  4. package/docs/design/bucketing-stage.md +196 -0
  5. package/docs/design/compat-cli-surface-design.md +11 -11
  6. package/docs/design/docs-reorg-plan.md +1 -1
  7. package/docs/design/dreaming-nightly-pipeline.md +291 -0
  8. package/docs/design/dspark-speculative-decoding.md +106 -0
  9. package/docs/design/generic-model-support.md +381 -0
  10. package/docs/design/memory-inference-path.md +422 -0
  11. package/docs/design/memory-synthesis.md +287 -0
  12. package/docs/design/minicpm5-decode-megakernel.md +484 -0
  13. package/docs/design/mlx-lm-tool-parity-plan.md +535 -0
  14. package/docs/design/omlx-adoption-map.md +90 -0
  15. package/docs/design/orpo-dynamic-lambda.md +41 -0
  16. package/docs/design/orpo-training.md +35 -5
  17. package/docs/design/parallel-slots.md +12 -1
  18. package/docs/design/parity-tier-dag.md +13 -5
  19. package/docs/design/segmented-backward-training.md +15 -0
  20. package/docs/design/spec-decode-larger-targets.md +229 -0
  21. package/docs/design/ssd-kv-cold-tier.md +158 -0
  22. package/docs/design/tauri-desktop-app.md +2 -2
  23. package/docs/design/the-dreaming-handoff.md +141 -0
  24. package/docs/design/the-dreaming-master-plan.md +446 -0
  25. package/docs/design/write-pipeline-entity.md +468 -0
  26. package/docs/investigations/curve-bisector-routing.md +1 -1
  27. package/docs/investigations/decode-roofline-lookagain.md +348 -0
  28. package/docs/investigations/dspark-handoff.md +58 -0
  29. package/docs/investigations/kernel-perf-review-2026-07.md +171 -0
  30. package/docs/planning/PRODUCT_ROADMAP.md +2 -2
  31. package/docs/planning/curve-sampler-research-plan.md +331 -0
  32. package/docs/planning/memory-docs-and-dag-plan.md +185 -0
  33. package/docs/planning/pi-integration-review.md +178 -0
  34. package/docs/planning/release-notes-v0.0.10.md +57 -0
  35. package/docs/planning/release-notes-v0.0.9.md +122 -0
  36. package/docs/planning/web-ui-pass-plan.md +213 -0
  37. package/docs/planning/website-readme-pass-plan.md +224 -0
  38. package/docs/reference/cli.md +255 -0
  39. package/docs/reference/embedding.md +7 -2
  40. package/docs/reference/library-api.md +22 -3
  41. package/docs/reference/memory.md +5 -5
  42. package/docs/reference/models.md +137 -0
  43. package/docs/reference/orpo-quickstart.md +6 -0
  44. package/docs/reference/server-api.md +187 -9
  45. package/docs/reference/server-config.md +43 -9
  46. package/docs/reference/training.md +31 -12
  47. package/package.json +1 -1
  48. package/src/anthropic.ts +22 -0
  49. package/src/assets/curve-designer.html +1 -1
  50. package/src/chat-template.ts +25 -7
  51. package/src/cli.ts +1077 -90
  52. package/src/config.ts +4 -1
  53. package/src/download.ts +99 -8
  54. package/src/embed.ts +77 -0
  55. package/src/eval/perplexity.ts +164 -0
  56. package/src/eval/runner.ts +15 -6
  57. package/src/eval/tasks/bfcl.ts +1 -1
  58. package/src/generate.ts +149 -18
  59. package/src/harness-pi.ts +43 -17
  60. package/src/index.ts +2 -0
  61. package/src/kv-store.ts +240 -98
  62. package/src/lora.ts +2 -2
  63. package/src/memory/article.ts +558 -0
  64. package/src/memory/chunk-validation.ts +150 -0
  65. package/src/memory/chunk.ts +305 -12
  66. package/src/memory/cluster.ts +344 -17
  67. package/src/memory/coverage.ts +134 -0
  68. package/src/memory/crosslink.ts +0 -0
  69. package/src/memory/db.ts +184 -45
  70. package/src/memory/entity.ts +203 -0
  71. package/src/memory/gate.ts +187 -0
  72. package/src/memory/history.ts +152 -0
  73. package/src/memory/ingest.ts +324 -11
  74. package/src/memory/model.ts +321 -0
  75. package/src/memory/normalize.ts +671 -0
  76. package/src/memory/parse.ts +22 -0
  77. package/src/memory/pipeline.ts +242 -30
  78. package/src/memory/prompts.ts +42 -0
  79. package/src/memory/query.ts +334 -0
  80. package/src/memory/reconcile.ts +734 -0
  81. package/src/memory/redirect.ts +108 -0
  82. package/src/memory/reindex.ts +325 -0
  83. package/src/memory/resolve.ts +307 -0
  84. package/src/memory/route.ts +272 -0
  85. package/src/memory/schedule.ts +6 -4
  86. package/src/memory/stages.ts +774 -0
  87. package/src/memory/synthesize.ts +1629 -20
  88. package/src/memory/tools.ts +446 -96
  89. package/src/memory/vault.ts +462 -35
  90. package/src/memory/wikify.ts +1057 -16
  91. package/src/mlx/array.ts +21 -0
  92. package/src/mlx/ffi.ts +3 -0
  93. package/src/mlx/ops.ts +72 -0
  94. package/src/model/compiled-decode.ts +43 -8
  95. package/src/model/factory.ts +29 -8
  96. package/src/model/flash-attention.ts +4 -2
  97. package/src/model/fused-decode-kernel.ts +14 -7
  98. package/src/model/fused-mlp-kernel.ts +169 -0
  99. package/src/model/fused-swiglu-kernel.ts +240 -0
  100. package/src/model/gemma4-base.ts +96 -7
  101. package/src/model/gemma4.ts +33 -2
  102. package/src/model/minicpm5.ts +106 -8
  103. package/src/model/qwen3-delta.ts +48 -1
  104. package/src/model/qwen3.ts +276 -0
  105. package/src/model/qwen3_5.ts +11 -2
  106. package/src/model/steel-linear-kernel.ts +96 -0
  107. package/src/model/support.ts +38 -8
  108. package/src/model/universal/archs.ts +332 -0
  109. package/src/model/universal/dense.ts +576 -0
  110. package/src/model/universal/modules.ts +163 -0
  111. package/src/model/universal/rope.ts +366 -0
  112. package/src/pi-provider.ts +1 -1
  113. package/src/pi-terminal.ts +29 -8
  114. package/src/pi-web.ts +19 -2
  115. package/src/prompt-cache.ts +55 -9
  116. package/src/quantize/config-writer.ts +1 -1
  117. package/src/registry.ts +219 -8
  118. package/src/responses.ts +23 -0
  119. package/src/sampler.ts +220 -9
  120. package/src/serve/batch-scheduler.ts +307 -83
  121. package/src/serve/generation-gateway.ts +156 -29
  122. package/src/server.ts +746 -73
  123. package/src/spec/drafter.ts +81 -49
  124. package/src/spec/dspark/data-dflash.ts +156 -0
  125. package/src/spec/dspark/data.ts +201 -0
  126. package/src/spec/dspark/generate-dflash.ts +193 -0
  127. package/src/spec/dspark/generate.ts +290 -0
  128. package/src/spec/dspark/loss.ts +213 -0
  129. package/src/spec/dspark/module-dflash.ts +361 -0
  130. package/src/spec/dspark/module.ts +540 -0
  131. package/src/spec/dspark/sample.ts +112 -0
  132. package/src/spec/generate.ts +40 -19
  133. package/src/ssd-cache.ts +213 -0
  134. package/src/status-page.html +1 -1
  135. package/src/tokenizer.ts +7 -0
  136. package/src/train/flash-cce.ts +81 -23
  137. package/src/train/fuse.ts +243 -0
  138. package/src/train/index.ts +3 -0
  139. package/src/train/job.ts +8 -0
  140. package/src/train/loss.ts +341 -40
  141. package/src/train/prefix-shared.ts +62 -11
  142. package/src/train/segmented.ts +215 -55
  143. package/src/train/trainer.ts +112 -38
  144. package/src/web/skills/memory/SKILL.md +40 -17
@@ -28,7 +28,7 @@ import * as ops from "../mlx/ops";
28
28
  import { createCausalMask, type Cache, type Mask } from "../model/gemma4-base";
29
29
  import { MiniCPM5Model, setMiniCpmPrefixPlan } from "../model/minicpm5";
30
30
  import { Gemma4Model, setGemmaPrefixPlan } from "../model/gemma4";
31
- import { orpoLossFromLogps, fusedRespLogpMean, type ChunkCtx } from "./loss";
31
+ import { orpoLossFromLogps, fusedRespLogpMean, combineFullNll, logitsFromHiddenPadM, type ChunkCtx, type SftScope } from "./loss";
32
32
  import type { DpoBatch } from "./dataset";
33
33
 
34
34
  /** A model that can project final-norm hidden states to vocab logits — the only
@@ -63,17 +63,40 @@ export function blockSparseMask(P: number, Rc: number, Rr: number): MlxArray {
63
63
  * MiniCPM5's runLayerRange calls makeMask once and disposes the returned arr. */
64
64
  export class PrefixSharedCache implements Cache {
65
65
  offset = 0;
66
- constructor(private readonly P: number, private readonly Rc: number, private readonly Rr: number) {}
66
+ constructor(
67
+ private readonly P: number, private readonly Rc: number, private readonly Rr: number,
68
+ // Step-shared mask memo (kernel-review backlog #9): the [T,T] block-sparse
69
+ // mask is IDENTICAL for every layer, segment forward, and backward
70
+ // recompute of a step, but each runLayerRange call rebuilt it from its
71
+ // elementwise ops (2·nSeg+ rebuilds per step in the segmented prefix
72
+ // class). With a memo it is built once; makeMask hands out cheap slice
73
+ // VIEWS (consumers dispose their view — runLayerRange does), and the
74
+ // first cache disposed frees the memo. Use prefixSharedCaches() to build
75
+ // a memo-sharing batch.
76
+ private readonly memo?: { mask: MlxArray | null },
77
+ ) {}
67
78
  updateAndFetch(k: MlxArray, v: MlxArray): [MlxArray, MlxArray] {
68
79
  return [k.slice([0, 0, 0, 0], k.shape), v.slice([0, 0, 0, 0], v.shape)];
69
80
  }
70
81
  makeMask(_N: number, _windowSize: number | null): Mask {
71
- return { mode: "array", arr: blockSparseMask(this.P, this.Rc, this.Rr) };
82
+ if (!this.memo) return { mode: "array", arr: blockSparseMask(this.P, this.Rc, this.Rr) };
83
+ if (!this.memo.mask) this.memo.mask = blockSparseMask(this.P, this.Rc, this.Rr);
84
+ const m = this.memo.mask;
85
+ return { mode: "array", arr: m.slice([0, 0], m.shape) };
72
86
  }
73
87
  state(): MlxArray[] { return []; }
74
88
  isTrimmable(): boolean { return true; }
75
89
  trim(_n: number): void { /* offset pinned at 0 */ }
76
- dispose(): void { /* owns no arrays */ }
90
+ dispose(): void {
91
+ if (this.memo?.mask) { this.memo.mask.dispose(); this.memo.mask = null; }
92
+ }
93
+ }
94
+
95
+ /** One PrefixSharedCache per layer, all sharing a single block-sparse-mask
96
+ * memo for the step (see the constructor note). */
97
+ export function prefixSharedCaches(nLayers: number, P: number, Rc: number, Rr: number): Cache[] {
98
+ const memo: { mask: MlxArray | null } = { mask: null };
99
+ return Array.from({ length: nLayers }, () => new PrefixSharedCache(P, Rc, Rr, memo));
77
100
  }
78
101
 
79
102
  /** Length-normalized mean log-prob over the predictions at hidden positions
@@ -87,7 +110,7 @@ function gatheredLogpMean(model: LogitProjector, h: MlxArray, gatherIdx: number[
87
110
  const hSel = ops.takeAxis(h, idxArr, 1); // [1, M, hidden]
88
111
  idxArr.dispose();
89
112
  if (hSel.shape[2] !== hidden) throw new Error("gatheredLogpMean: hidden mismatch");
90
- const logits = model.logitsFromHidden(hSel); // [1, M, V]
113
+ const logits = logitsFromHiddenPadM(model, hSel); // [1, M, V] (M in {2,3} padded — mlx qmm bug)
91
114
  hSel.dispose();
92
115
  const V = logits.shape[2]!;
93
116
  const logits2d = ops.reshape(logits, [M, V]);
@@ -147,6 +170,26 @@ export function prefixGatherIdx(P: number, Rc: number, Rr: number): { chosenIdx:
147
170
  return { chosenIdx, rejectedIdx };
148
171
  }
149
172
 
173
+ /** sft_scope:"full" over the prefix-shared concat: the full-scope chosen NLL
174
+ * gathers the PROMPT predictions too — hidden position t (t = 0..P-2) predicts
175
+ * prompt token t+1 — and combines them with the response mean ℓw:
176
+ * NLL_full = -((P-1)·promptMean + Rc·ℓw) / (P-1+Rc). The prompt head routes
177
+ * through the same branchLogpMeanGathered tier as the responses (whole-vocab /
178
+ * fused / flash), sharing the one concat forward's hiddens. Returns [1]
179
+ * (caller owns); lw is NOT disposed. P=1 (no prompt predictions) → -ℓw. */
180
+ export function prefixFullNll(
181
+ model: LogitProjector, h: MlxArray, promptIds: number[], lw: MlxArray, Rc: number, chunk?: ChunkCtx,
182
+ ): MlxArray {
183
+ const P = promptIds.length;
184
+ if (P <= 1) return combineFullNll(null, lw, 0, Rc);
185
+ const promptIdx = Array.from({ length: P - 1 }, (_, k) => k); // H[k] predicts promptIds[k+1]
186
+ const promptTargets = promptIds.slice(1);
187
+ const pm = branchLogpMeanGathered(model, h, promptIdx, promptTargets, chunk); // [1]
188
+ const nllFull = combineFullNll(pm, lw, P - 1, Rc);
189
+ pm.dispose();
190
+ return nllFull;
191
+ }
192
+
150
193
  /** ORPO loss via the shared prompt-prefix single forward (MiniCPM5, B=1).
151
194
  * `promptIds` is the shared prefix; `chosenResp`/`rejectedResp` are the response
152
195
  * continuations. Returns the scalar loss (caller owns). Differentiable through
@@ -155,7 +198,7 @@ export function prefixGatherIdx(P: number, Rc: number, Rr: number): { chosenIdx:
155
198
  export function orpoLossPrefixShared(
156
199
  model: MiniCPM5Model,
157
200
  promptIds: number[], chosenResp: number[], rejectedResp: number[],
158
- lambda: number, chunk?: ChunkCtx,
201
+ lambda: number, chunk?: ChunkCtx, sftScope: SftScope = "response",
159
202
  ): MlxArray {
160
203
  const P = promptIds.length, Rc = chosenResp.length, Rr = rejectedResp.length;
161
204
  if (P < 1 || Rc < 1 || Rr < 1) throw new Error("orpoLossPrefixShared: need P,Rc,Rr >= 1");
@@ -165,7 +208,7 @@ export function orpoLossPrefixShared(
165
208
  concat.set(chosenResp, P);
166
209
  concat.set(rejectedResp, P + Rc);
167
210
  const ids = MlxArray.fromInt32(concat, [1, T]);
168
- const caches: Cache[] = model.layers.map(() => new PrefixSharedCache(P, Rc, Rr));
211
+ const caches: Cache[] = prefixSharedCaches(model.layers.length, P, Rc, Rr);
169
212
 
170
213
  let h: MlxArray;
171
214
  setMiniCpmPrefixPlan({ P, Rc, Rr });
@@ -188,10 +231,14 @@ export function orpoLossPrefixShared(
188
231
  if (keepForBwd) chunk!.sink.push(h);
189
232
  const lw = branchLogpMeanGathered(model, h, chosenIdx, chosenResp, chunk);
190
233
  const lr = branchLogpMeanGathered(model, h, rejectedIdx, rejectedResp, chunk);
234
+ // sft_scope:"full": add the prompt predictions (H[0..P-2] → promptIds[1..P-1])
235
+ // from the SAME concat forward; ℓw/ℓr stay response-only for the odds ratio.
236
+ const nllFull = sftScope === "full" ? prefixFullNll(model, h, promptIds, lw, Rc, chunk) : null;
191
237
  if (!keepForBwd) h.dispose();
192
- const loss = orpoLossFromLogps(lw, lr, lambda);
238
+ const loss = orpoLossFromLogps(lw, lr, lambda, nllFull ?? undefined);
193
239
  lw.dispose();
194
240
  lr.dispose();
241
+ nllFull?.dispose();
195
242
  return loss;
196
243
  }
197
244
 
@@ -206,7 +253,7 @@ export function prefixSharedLogps(
206
253
  const concat = new Int32Array(T);
207
254
  concat.set(promptIds, 0); concat.set(chosenResp, P); concat.set(rejectedResp, P + Rc);
208
255
  const ids = MlxArray.fromInt32(concat, [1, T]);
209
- const caches: Cache[] = model.layers.map(() => new PrefixSharedCache(P, Rc, Rr));
256
+ const caches: Cache[] = prefixSharedCaches(model.layers.length, P, Rc, Rr);
210
257
  setMiniCpmPrefixPlan({ P, Rc, Rr });
211
258
  let h: MlxArray;
212
259
  try { h = model.forwardHidden(ids, caches); }
@@ -349,7 +396,7 @@ class Gemma4PrefixSharedCache implements Cache {
349
396
  export function orpoLossPrefixSharedGemma(
350
397
  model: Gemma4Model,
351
398
  promptIds: number[], chosenResp: number[], rejectedResp: number[],
352
- lambda: number, chunk?: ChunkCtx,
399
+ lambda: number, chunk?: ChunkCtx, sftScope: SftScope = "response",
353
400
  ): MlxArray {
354
401
  const P = promptIds.length, Rc = chosenResp.length, Rr = rejectedResp.length;
355
402
  if (P < 1 || Rc < 1 || Rr < 1) throw new Error("orpoLossPrefixSharedGemma: need P,Rc,Rr >= 1");
@@ -384,10 +431,14 @@ export function orpoLossPrefixSharedGemma(
384
431
  if (keepForBwd) chunk!.sink.push(h);
385
432
  const lw = branchLogpMeanGathered(model, h, chosenIdx, chosenResp, chunk);
386
433
  const lr = branchLogpMeanGathered(model, h, rejectedIdx, rejectedResp, chunk);
434
+ // sft_scope:"full": prompt predictions from the same concat forward (see
435
+ // orpoLossPrefixShared); ℓw/ℓr stay response-only for the odds ratio.
436
+ const nllFull = sftScope === "full" ? prefixFullNll(model, h, promptIds, lw, Rc, chunk) : null;
387
437
  if (!keepForBwd) h.dispose();
388
- const loss = orpoLossFromLogps(lw, lr, lambda);
438
+ const loss = orpoLossFromLogps(lw, lr, lambda, nllFull ?? undefined);
389
439
  lw.dispose();
390
440
  lr.dispose();
441
+ nllFull?.dispose();
391
442
  return loss;
392
443
  }
393
444