mlx-bun 0.0.7 → 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/diffusion-gemma-port.md +602 -0
- 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 +54 -3
- package/src/diffusion/diffusion-generate.ts +436 -0
- package/src/download.ts +99 -8
- package/src/embed.ts +77 -0
- package/src/eval/perplexity.ts +164 -0
- package/src/eval/runner.ts +19 -7
- package/src/eval/tasks/bfcl.ts +1 -1
- package/src/generate.ts +196 -19
- 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 +11 -0
- package/src/mlx/ops.ts +113 -0
- package/src/model/compiled-decode.ts +43 -8
- package/src/model/diffusion-gemma.ts +1026 -0
- package/src/model/factory.ts +35 -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 +45 -7
- 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 +90 -18
- package/src/server.ts +643 -50
- 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/diffusion-lora.ts +216 -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/vision/diffusion-vision.ts +620 -0
- package/src/web/skills/memory/SKILL.md +40 -17
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// Faithful DFlash drafter (DSpark paper §3.1, Eq 2–3) — the REAL KV-injection
|
|
2
|
+
// architecture, in a parallel file so the v1 single-vector module stays intact
|
|
3
|
+
// as the baseline. Selected by variant="dflash" in the checkpoint metadata.
|
|
4
|
+
//
|
|
5
|
+
// vs v1 (module.ts): v1 collapsed the target context to ONE vector fused as a
|
|
6
|
+
// sequence token before layer 0. This builds the paper's mechanism:
|
|
7
|
+
// Eq 2: H_ctx = RMSNorm(W_c · [H^{l1};…;H^{lm}]) — m tapped target layers,
|
|
8
|
+
// over the FULL context, projected into the draft width.
|
|
9
|
+
// Eq 3: every draft layer i forms K_i=[W_i^K H_ctx; W_i^K H_d],
|
|
10
|
+
// V_i=[W_i^V H_ctx; W_i^V H_d], Q_i=W_i^Q H_d — the block attends
|
|
11
|
+
// bidirectionally over [context ++ block]; context is read-only memory
|
|
12
|
+
// re-projected fresh at every layer, never a token that gets rewritten.
|
|
13
|
+
// The draft projects the target's HIDDEN STATES with its OWN Wk/Wv (not the
|
|
14
|
+
// target's K/V — that's the separate GemmaAssistantDrafter baseline).
|
|
15
|
+
|
|
16
|
+
import { MlxArray } from "../../mlx/array";
|
|
17
|
+
import { Dtype } from "../../mlx/ffi";
|
|
18
|
+
import * as ops from "../../mlx/ops";
|
|
19
|
+
import type { Gemma4Model } from "../../model/gemma4";
|
|
20
|
+
import { loadAdapterTensors } from "../../lora";
|
|
21
|
+
import { writeShardedSafetensors, type NamedTensor } from "../../quantize/safetensors-writer";
|
|
22
|
+
import { processLogits, sampleToken, KeyStream, type DSparkSampleConfig } from "./sample";
|
|
23
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
|
|
26
|
+
export interface DflashConfig {
|
|
27
|
+
gamma: number;
|
|
28
|
+
dDraft: number;
|
|
29
|
+
/** Backbone depth (paper default 5). */
|
|
30
|
+
nLayers: number;
|
|
31
|
+
nHeads: number;
|
|
32
|
+
markovRank: number;
|
|
33
|
+
ffMult: number;
|
|
34
|
+
/** Target layer indices tapped for H_ctx (Eq 2). nLayers-of-target..sentinel.
|
|
35
|
+
* For e4b (42 layers): index 0..41 = post-layer residual, 42 = post-finalNorm. */
|
|
36
|
+
tapLayers: number[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const DEFAULT_DFLASH_CONFIG: DflashConfig = {
|
|
40
|
+
gamma: 5,
|
|
41
|
+
dDraft: 1024,
|
|
42
|
+
nLayers: 5,
|
|
43
|
+
nHeads: 8,
|
|
44
|
+
markovRank: 256,
|
|
45
|
+
ffMult: 2,
|
|
46
|
+
tapLayers: [20, 31, 41, 42],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export interface TargetDims { hiddenSize: number; vocabSize: number; eps: number }
|
|
50
|
+
|
|
51
|
+
export interface DflashTrainOut { draftLogits: MlxArray; conf: MlxArray }
|
|
52
|
+
export interface DflashDraftBlock { tokens: number[]; conf: number[]; draftLogits: MlxArray }
|
|
53
|
+
export interface DflashDraftOpts { sample?: DSparkSampleConfig; keys?: KeyStream }
|
|
54
|
+
|
|
55
|
+
const CDT = Dtype.float32;
|
|
56
|
+
const NEG = -1e9;
|
|
57
|
+
|
|
58
|
+
function disposing(old: MlxArray, next: MlxArray): MlxArray { old.dispose(); return next; }
|
|
59
|
+
|
|
60
|
+
function makeRng(seed: number): () => number {
|
|
61
|
+
let s = seed >>> 0 || 0x9e3779b9;
|
|
62
|
+
return () => { s ^= s << 13; s >>>= 0; s ^= s >> 17; s ^= s << 5; s >>>= 0; return s / 0x100000000; };
|
|
63
|
+
}
|
|
64
|
+
function normalArray(rng: () => number, shape: number[], std: number): MlxArray {
|
|
65
|
+
const n = shape.reduce((a, b) => a * b, 1);
|
|
66
|
+
const out = new Float32Array(n);
|
|
67
|
+
for (let i = 0; i < n; i += 2) {
|
|
68
|
+
const u1 = Math.max(rng(), 1e-12), u2 = rng();
|
|
69
|
+
const r = Math.sqrt(-2 * Math.log(u1));
|
|
70
|
+
out[i] = r * Math.cos(2 * Math.PI * u2) * std;
|
|
71
|
+
if (i + 1 < n) out[i + 1] = r * Math.sin(2 * Math.PI * u2) * std;
|
|
72
|
+
}
|
|
73
|
+
return MlxArray.fromFloat32(out, shape);
|
|
74
|
+
}
|
|
75
|
+
const zerosArray = (shape: number[]) => MlxArray.fromFloat32(new Float32Array(shape.reduce((a, b) => a * b, 1)), shape);
|
|
76
|
+
const constArray = (shape: number[], v: number) => MlxArray.fromFloat32(new Float32Array(shape.reduce((a, b) => a * b, 1)).fill(v), shape);
|
|
77
|
+
|
|
78
|
+
export class DflashDrafter {
|
|
79
|
+
readonly cfg: DflashConfig;
|
|
80
|
+
readonly dims: TargetDims;
|
|
81
|
+
readonly targetId: string;
|
|
82
|
+
readonly m: number; // number of tapped layers
|
|
83
|
+
#p = new Map<string, MlxArray>();
|
|
84
|
+
readonly names: string[];
|
|
85
|
+
|
|
86
|
+
private constructor(cfg: DflashConfig, dims: TargetDims, targetId: string) {
|
|
87
|
+
this.cfg = cfg;
|
|
88
|
+
this.dims = dims;
|
|
89
|
+
this.targetId = targetId;
|
|
90
|
+
this.m = cfg.tapLayers.length;
|
|
91
|
+
if (cfg.dDraft % cfg.nHeads !== 0) throw new Error(`dDraft ${cfg.dDraft} not divisible by nHeads ${cfg.nHeads}`);
|
|
92
|
+
this.names = buildNames(cfg);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static dimsOf(model: Gemma4Model): TargetDims {
|
|
96
|
+
const t = model.config.text;
|
|
97
|
+
return { hiddenSize: t.hiddenSize, vocabSize: t.vocabSize, eps: t.rmsNormEps };
|
|
98
|
+
}
|
|
99
|
+
static init(model: Gemma4Model, cfg: DflashConfig, targetId: string, seed = 0): DflashDrafter {
|
|
100
|
+
return DflashDrafter.initFromDims(DflashDrafter.dimsOf(model), cfg, targetId, seed);
|
|
101
|
+
}
|
|
102
|
+
static initFromDims(dims: TargetDims, cfg: DflashConfig, targetId: string, seed = 0): DflashDrafter {
|
|
103
|
+
const d = new DflashDrafter(cfg, dims, targetId);
|
|
104
|
+
const rng = makeRng(seed);
|
|
105
|
+
const { dDraft, nLayers, markovRank: r, ffMult } = cfg;
|
|
106
|
+
const dInter = ffMult * dDraft;
|
|
107
|
+
const { hiddenSize: H, vocabSize: V } = dims;
|
|
108
|
+
const mH = d.m * H;
|
|
109
|
+
const set = (n: string, a: MlxArray) => d.#p.set(n, a.eval());
|
|
110
|
+
for (let i = 0; i < nLayers; i++) {
|
|
111
|
+
set(`bb.${i}.attn_norm`, constArray([dDraft], 1.0));
|
|
112
|
+
for (const w of ["q", "k", "v", "o"]) set(`bb.${i}.${w}`, normalArray(rng, [dDraft, dDraft], 0.02));
|
|
113
|
+
set(`bb.${i}.ff_norm`, constArray([dDraft], 1.0));
|
|
114
|
+
set(`bb.${i}.gate`, normalArray(rng, [dDraft, dInter], 0.02));
|
|
115
|
+
set(`bb.${i}.up`, normalArray(rng, [dDraft, dInter], 0.02));
|
|
116
|
+
set(`bb.${i}.down`, normalArray(rng, [dInter, dDraft], 0.02));
|
|
117
|
+
}
|
|
118
|
+
set("W_c", normalArray(rng, [mH, dDraft], 0.02)); // Eq 2 projection
|
|
119
|
+
set("ctx_norm", constArray([dDraft], 1.0));
|
|
120
|
+
set("tok_proj", normalArray(rng, [H, dDraft], 0.02));
|
|
121
|
+
set("mask_emb", normalArray(rng, [cfg.gamma - 1, dDraft], 0.02));
|
|
122
|
+
set("block_pos", normalArray(rng, [cfg.gamma, dDraft], 0.02));
|
|
123
|
+
set("out_norm", constArray([dDraft], 1.0));
|
|
124
|
+
set("out_proj", normalArray(rng, [dDraft, H], 0.02));
|
|
125
|
+
set("markov.w1", normalArray(rng, [V, r], 0.02));
|
|
126
|
+
set("markov.w2", zerosArray([r, V])); // W2=0 → starts as pure parallel DFlash
|
|
127
|
+
set("conf.w", zerosArray([dDraft + r, 1]));
|
|
128
|
+
set("conf.b", zerosArray([1]));
|
|
129
|
+
return d;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get(name: string): MlxArray { const a = this.#p.get(name); if (!a) throw new Error(`param ${name}`); return a; }
|
|
133
|
+
flatParams(): MlxArray[] { return this.names.map((n) => this.get(n)); }
|
|
134
|
+
useParams<T>(primals: MlxArray[], fn: () => T): T {
|
|
135
|
+
const saved = this.names.map((n) => this.get(n));
|
|
136
|
+
this.names.forEach((n, i) => this.#p.set(n, primals[i]!));
|
|
137
|
+
try { return fn(); } finally { this.names.forEach((n, i) => this.#p.set(n, saved[i]!)); }
|
|
138
|
+
}
|
|
139
|
+
installParam(i: number, p: MlxArray): void { this.#p.set(this.names[i]!, p); }
|
|
140
|
+
|
|
141
|
+
// --- Eq 2: context construction ---
|
|
142
|
+
/** hCtx [A, Lctx, m*H] (m tapped layers concatenated on the feature axis) →
|
|
143
|
+
* H_ctx [A, Lctx, d]. */
|
|
144
|
+
#buildContext(hCtx: MlxArray): MlxArray {
|
|
145
|
+
const f = hCtx.dtype === CDT ? hCtx : hCtx.astype(CDT);
|
|
146
|
+
const proj = ops.matmul(f, this.get("W_c")); // [A,Lctx,d]
|
|
147
|
+
if (f !== hCtx) f.dispose();
|
|
148
|
+
const out = ops.rmsNorm(proj, this.get("ctx_norm"), this.dims.eps);
|
|
149
|
+
proj.dispose();
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Block hidden H_d [A, γ, d]: anchor at pos 0, mask tokens after. */
|
|
154
|
+
#buildBlock(anchorEmb: MlxArray, A: number): MlxArray {
|
|
155
|
+
const { gamma, dDraft } = this.cfg;
|
|
156
|
+
const bp = this.get("block_pos"); // [γ,d]
|
|
157
|
+
const af = anchorEmb.dtype === CDT ? anchorEmb : anchorEmb.astype(CDT);
|
|
158
|
+
let anchor = ops.matmul(af, this.get("tok_proj")); // [A,d]
|
|
159
|
+
if (af !== anchorEmb) af.dispose();
|
|
160
|
+
const p0 = bp.slice([0, 0], [1, dDraft]);
|
|
161
|
+
anchor = disposing(anchor, ops.add(anchor, p0)); p0.dispose();
|
|
162
|
+
anchor = disposing(anchor, ops.reshape(anchor, [A, 1, dDraft]));
|
|
163
|
+
const parts = [anchor];
|
|
164
|
+
if (gamma > 1) {
|
|
165
|
+
const me = this.get("mask_emb"); // [γ-1,d]
|
|
166
|
+
const pm = bp.slice([1, 0], [gamma, dDraft]); // [γ-1,d]
|
|
167
|
+
let masks = ops.add(me, pm); pm.dispose(); // [γ-1,d]
|
|
168
|
+
masks = disposing(masks, ops.reshape(masks, [1, gamma - 1, dDraft]));
|
|
169
|
+
const zerosA = MlxArray.fromFloat32(new Float32Array(A * dDraft), [A, 1, dDraft]);
|
|
170
|
+
const masksA = ops.add(masks, zerosA); // broadcast → [A,γ-1,d]
|
|
171
|
+
masks.dispose(); zerosA.dispose();
|
|
172
|
+
parts.push(masksA);
|
|
173
|
+
}
|
|
174
|
+
const H_d = ops.concatAxis(parts, 1); // [A,γ,d]
|
|
175
|
+
for (const p of parts) p.dispose();
|
|
176
|
+
return H_d;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Additive attention bias [A,1,1,Lctx+γ] from a context key-padding mask
|
|
180
|
+
* ([A,Lctx], 1=real/0=pad). Context pad cols → -1e9; real ctx + all block
|
|
181
|
+
* cols → 0. null when no padding (inference, batch 1). */
|
|
182
|
+
#maskBias(ctxMask: MlxArray | null, A: number, Lctx: number): MlxArray | null {
|
|
183
|
+
if (!ctxMask) return null;
|
|
184
|
+
const mf = ctxMask.dtype === CDT ? ctxMask : ctxMask.astype(CDT);
|
|
185
|
+
const one = MlxArray.fromFloat32(new Float32Array([1]), [1]);
|
|
186
|
+
const sub = ops.sub(mf, one); one.dispose(); // real→0, pad→-1
|
|
187
|
+
if (mf !== ctxMask) mf.dispose();
|
|
188
|
+
const bias = ops.mulScalar(sub, -NEG); sub.dispose(); // real→0, pad→-1e9 (-NEG = 1e9; (-1)*1e9=-1e9)
|
|
189
|
+
let ctxBias = ops.reshape(bias, [A, 1, 1, Lctx]); bias.dispose();
|
|
190
|
+
const blockZeros = MlxArray.fromFloat32(new Float32Array(A * this.cfg.gamma), [A, 1, 1, this.cfg.gamma]);
|
|
191
|
+
const full = ops.concatAxis([ctxBias, blockZeros], 3); // [A,1,1,Lctx+γ]
|
|
192
|
+
ctxBias.dispose(); blockZeros.dispose();
|
|
193
|
+
return full;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Eq 3: one KV-injection layer. H_ctx read-only; H_d is queries+updated ---
|
|
197
|
+
#layer(i: number, H_d: MlxArray, H_ctx: MlxArray, maskBias: MlxArray | null, A: number, Lctx: number): MlxArray {
|
|
198
|
+
const { dDraft, nHeads, gamma } = this.cfg;
|
|
199
|
+
const hd = dDraft / nHeads;
|
|
200
|
+
const eps = this.dims.eps;
|
|
201
|
+
const wk = this.get(`bb.${i}.k`), wv = this.get(`bb.${i}.v`);
|
|
202
|
+
|
|
203
|
+
const residual = H_d;
|
|
204
|
+
const nd = ops.rmsNorm(H_d, this.get(`bb.${i}.attn_norm`), eps);
|
|
205
|
+
const nc = ops.rmsNorm(H_ctx, this.get(`bb.${i}.attn_norm`), eps); // SAME norm as block
|
|
206
|
+
|
|
207
|
+
// Q from block only
|
|
208
|
+
let q = ops.matmul(nd, this.get(`bb.${i}.q`)); // [A,γ,d]
|
|
209
|
+
q = disposing(q, ops.reshape(q, [A, gamma, nHeads, hd]));
|
|
210
|
+
q = disposing(q, ops.transposeAxes(q, [0, 2, 1, 3])); // [A,nHeads,γ,hd]
|
|
211
|
+
|
|
212
|
+
// K/V = [Wk·H_ctx ; Wk·H_d] (context first, then block)
|
|
213
|
+
const kc = ops.matmul(nc, wk), kd = ops.matmul(nd, wk);
|
|
214
|
+
const vc = ops.matmul(nc, wv), vd = ops.matmul(nd, wv);
|
|
215
|
+
nd.dispose(); nc.dispose();
|
|
216
|
+
const shape4 = (x: MlxArray) => { const r = ops.reshape(x, [A, Lctx + gamma, nHeads, hd]); x.dispose(); const t = ops.transposeAxes(r, [0, 2, 1, 3]); r.dispose(); return t; };
|
|
217
|
+
let K = ops.concatAxis([kc, kd], 1); kc.dispose(); kd.dispose(); // [A,Lctx+γ,d]
|
|
218
|
+
let V = ops.concatAxis([vc, vd], 1); vc.dispose(); vd.dispose();
|
|
219
|
+
K = shape4(K); V = shape4(V); // [A,nHeads,Lctx+γ,hd]
|
|
220
|
+
|
|
221
|
+
let attn = ops.sdpa(q, K, V, Math.pow(hd, -0.5), maskBias ? "array" : "", maskBias);
|
|
222
|
+
q.dispose(); K.dispose(); V.dispose();
|
|
223
|
+
attn = disposing(attn, ops.transposeAxes(attn, [0, 2, 1, 3])); // [A,γ,nHeads,hd]
|
|
224
|
+
attn = disposing(attn, ops.reshape(attn, [A, gamma, dDraft]));
|
|
225
|
+
attn = disposing(attn, ops.matmul(attn, this.get(`bb.${i}.o`)));
|
|
226
|
+
let H = ops.add(residual, attn); residual.dispose(); attn.dispose();
|
|
227
|
+
|
|
228
|
+
// gated-GELU FFN on the block only
|
|
229
|
+
const res2 = H;
|
|
230
|
+
const n2 = ops.rmsNorm(H, this.get(`bb.${i}.ff_norm`), eps);
|
|
231
|
+
const g = ops.matmul(n2, this.get(`bb.${i}.gate`));
|
|
232
|
+
const u = ops.matmul(n2, this.get(`bb.${i}.up`));
|
|
233
|
+
n2.dispose();
|
|
234
|
+
const act = ops.geluApprox(g); g.dispose();
|
|
235
|
+
let mlp = ops.mul(act, u); act.dispose(); u.dispose();
|
|
236
|
+
mlp = disposing(mlp, ops.matmul(mlp, this.get(`bb.${i}.down`)));
|
|
237
|
+
const out = ops.add(res2, mlp); res2.dispose(); mlp.dispose();
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** hCtx [A,Lctx,m*H], anchorEmb [A,H], ctxMask [A,Lctx]|null → block [A,γ,d]. */
|
|
242
|
+
#backbone(hCtx: MlxArray, anchorEmb: MlxArray, ctxMask: MlxArray | null, A: number): MlxArray {
|
|
243
|
+
const Lctx = hCtx.shape[1]!;
|
|
244
|
+
const H_ctx = this.#buildContext(hCtx);
|
|
245
|
+
let H_d = this.#buildBlock(anchorEmb, A);
|
|
246
|
+
const maskBias = this.#maskBias(ctxMask, A, Lctx);
|
|
247
|
+
for (let i = 0; i < this.cfg.nLayers; i++) H_d = disposing(H_d, this.#layer(i, H_d, H_ctx, maskBias, A, Lctx));
|
|
248
|
+
H_ctx.dispose(); maskBias?.dispose();
|
|
249
|
+
H_d = disposing(H_d, ops.rmsNorm(H_d, this.get("out_norm"), this.dims.eps));
|
|
250
|
+
return H_d;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
#baseLogits(model: Gemma4Model, block: MlxArray): MlxArray {
|
|
254
|
+
const hOut = ops.matmul(block, this.get("out_proj")); // [A,γ,H]
|
|
255
|
+
const hBf = hOut.astype(model.embed.scales.dtype); hOut.dispose();
|
|
256
|
+
const logits = model.logitsFromHidden(hBf); hBf.dispose();
|
|
257
|
+
return logits;
|
|
258
|
+
}
|
|
259
|
+
#markov(prevToks: MlxArray): { B: MlxArray; emb1: MlxArray } {
|
|
260
|
+
const emb1 = ops.takeAxis(this.get("markov.w1"), prevToks, 0); // [A,γ,r]
|
|
261
|
+
const B = ops.matmul(emb1, this.get("markov.w2")); // [A,γ,V]
|
|
262
|
+
return { B, emb1 };
|
|
263
|
+
}
|
|
264
|
+
#confidence(block: MlxArray, emb1: MlxArray): MlxArray {
|
|
265
|
+
const inp = ops.concatAxis([block, emb1], 2);
|
|
266
|
+
let z = ops.matmul(inp, this.get("conf.w")); inp.dispose();
|
|
267
|
+
z = disposing(z, ops.add(z, this.get("conf.b")));
|
|
268
|
+
z = disposing(z, ops.sigmoid(z));
|
|
269
|
+
const A = z.shape[0]!, G = z.shape[1]!;
|
|
270
|
+
const c = ops.reshape(z, [A, G]); z.dispose();
|
|
271
|
+
return c;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Parallel training forward (teacher-forced Markov).
|
|
275
|
+
* hCtx [A,Lctx,m*H], ctxMask [A,Lctx]|null, anchorEmb [A,H], prevToks [A,γ]. */
|
|
276
|
+
forwardTrain(model: Gemma4Model, hCtx: MlxArray, ctxMask: MlxArray | null, anchorEmb: MlxArray, prevToks: MlxArray): DflashTrainOut {
|
|
277
|
+
const A = hCtx.shape[0]!;
|
|
278
|
+
const block = this.#backbone(hCtx, anchorEmb, ctxMask, A);
|
|
279
|
+
const U = this.#baseLogits(model, block);
|
|
280
|
+
const Uf = U.astype(CDT); U.dispose();
|
|
281
|
+
const { B, emb1 } = this.#markov(prevToks);
|
|
282
|
+
const draftLogits = disposing(Uf, ops.add(Uf, B)); B.dispose();
|
|
283
|
+
const conf = this.#confidence(block, emb1);
|
|
284
|
+
block.dispose(); emb1.dispose();
|
|
285
|
+
return { draftLogits, conf };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Inference: parallel backbone once, then sequential Markov sampling.
|
|
289
|
+
* hCtx [1,Lctx,m*H] (full current context), anchorTok the bonus token. */
|
|
290
|
+
forwardInfer(model: Gemma4Model, hCtx: MlxArray, anchorTok: number, gamma: number, opts: DflashDraftOpts = {}): DflashDraftBlock {
|
|
291
|
+
const { dDraft, markovRank: r } = this.cfg;
|
|
292
|
+
const V = this.dims.vocabSize;
|
|
293
|
+
const sample = opts.sample && opts.sample.temperature > 0 ? opts.sample : null;
|
|
294
|
+
const keys = opts.keys ?? new KeyStream(opts.sample?.seed ?? 0);
|
|
295
|
+
|
|
296
|
+
const ids = ops.fromInt32([anchorTok], [1, 1]);
|
|
297
|
+
const ae3 = model.embed.encode(ids); ids.dispose();
|
|
298
|
+
const anchorEmb = ops.reshape(ae3, [1, this.dims.hiddenSize]); ae3.dispose();
|
|
299
|
+
const block = this.#backbone(hCtx, anchorEmb, null, 1); // [1,γ,d]
|
|
300
|
+
anchorEmb.dispose();
|
|
301
|
+
const U = this.#baseLogits(model, block);
|
|
302
|
+
const Uf = U.astype(CDT); U.dispose();
|
|
303
|
+
|
|
304
|
+
const w1 = this.get("markov.w1"), w2 = this.get("markov.w2");
|
|
305
|
+
const tokens: number[] = [], conf: number[] = [];
|
|
306
|
+
const perPos: MlxArray[] = [];
|
|
307
|
+
let prevTok = anchorTok;
|
|
308
|
+
for (let k = 0; k < gamma; k++) {
|
|
309
|
+
const Uk = ops.reshape(Uf.slice([0, k, 0], [1, k + 1, V]), [1, V]);
|
|
310
|
+
const prevIdx = ops.fromInt32([prevTok], [1]);
|
|
311
|
+
const e1 = ops.takeAxis(w1, prevIdx, 0); prevIdx.dispose();
|
|
312
|
+
const Bk = ops.matmul(e1, w2);
|
|
313
|
+
const logitsK = ops.add(Uk, Bk); Uk.dispose(); Bk.dispose();
|
|
314
|
+
let tok: number;
|
|
315
|
+
if (sample) { const sc = processLogits(logitsK, sample); tok = sampleToken(sc, keys.next()); sc.dispose(); }
|
|
316
|
+
else { const am = ops.argmaxAxis(logitsK, -1); tok = ops.itemUint32(am); am.dispose(); }
|
|
317
|
+
perPos.push(ops.reshape(logitsK, [1, 1, V])); logitsK.dispose();
|
|
318
|
+
const hk = ops.reshape(block.slice([0, k, 0], [1, k + 1, dDraft]), [1, dDraft]);
|
|
319
|
+
const ci = ops.concatAxis([hk, e1], 1); hk.dispose(); e1.dispose();
|
|
320
|
+
let cz = ops.matmul(ci, this.get("conf.w")); ci.dispose();
|
|
321
|
+
cz = disposing(cz, ops.add(cz, this.get("conf.b")));
|
|
322
|
+
cz = disposing(cz, ops.sigmoid(cz));
|
|
323
|
+
conf.push(cz.toFloat32()[0]!); cz.dispose();
|
|
324
|
+
tokens.push(tok); prevTok = tok;
|
|
325
|
+
}
|
|
326
|
+
Uf.dispose(); block.dispose();
|
|
327
|
+
const draftLogits = ops.concatAxis(perPos, 1); for (const a of perPos) a.dispose();
|
|
328
|
+
return { tokens, conf, draftLogits };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
save(dir: string): void {
|
|
332
|
+
mkdirSync(dir, { recursive: true });
|
|
333
|
+
const tensors: NamedTensor[] = this.names.map((name) => ({ name, array: this.get(name) }));
|
|
334
|
+
writeShardedSafetensors(dir, tensors);
|
|
335
|
+
writeFileSync(join(dir, "dspark.json"), JSON.stringify({
|
|
336
|
+
kind: "dspark-drafter", variant: "dflash", version: 1,
|
|
337
|
+
target_id: this.targetId, config: this.cfg, dims: this.dims, param_names: this.names,
|
|
338
|
+
}, null, 2));
|
|
339
|
+
}
|
|
340
|
+
static load(dir: string): DflashDrafter {
|
|
341
|
+
const meta = JSON.parse(readFileSync(join(dir, "dspark.json"), "utf8")) as {
|
|
342
|
+
variant?: string; config: DflashConfig; dims: TargetDims; target_id: string;
|
|
343
|
+
};
|
|
344
|
+
if (meta.variant !== "dflash") throw new Error(`${dir} is not a dflash drafter (variant=${meta.variant})`);
|
|
345
|
+
const d = new DflashDrafter(meta.config, meta.dims, meta.target_id);
|
|
346
|
+
const tensors = loadAdapterTensors(join(dir, "model.safetensors"));
|
|
347
|
+
try {
|
|
348
|
+
for (const name of d.names) { const a = tensors.get(name); if (!a) throw new Error(`missing ${name}`); d.#p.set(name, a); }
|
|
349
|
+
} finally { for (const [name, arr] of tensors) if (!d.names.includes(name)) arr.dispose(); }
|
|
350
|
+
return d;
|
|
351
|
+
}
|
|
352
|
+
dispose(): void { for (const a of this.#p.values()) a.dispose(); this.#p.clear(); }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function buildNames(cfg: DflashConfig): string[] {
|
|
356
|
+
const names: string[] = [];
|
|
357
|
+
for (let i = 0; i < cfg.nLayers; i++)
|
|
358
|
+
names.push(`bb.${i}.attn_norm`, `bb.${i}.q`, `bb.${i}.k`, `bb.${i}.v`, `bb.${i}.o`, `bb.${i}.ff_norm`, `bb.${i}.gate`, `bb.${i}.up`, `bb.${i}.down`);
|
|
359
|
+
names.push("W_c", "ctx_norm", "tok_proj", "mask_emb", "block_pos", "out_norm", "out_proj", "markov.w1", "markov.w2", "conf.w", "conf.b");
|
|
360
|
+
return names;
|
|
361
|
+
}
|