@tryhamster/gerbil 1.6.0 → 1.6.2

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 (50) hide show
  1. package/dist/{architectures-Ol7yUmKf.mjs → architectures-CpmH4Gha.mjs} +86 -82
  2. package/dist/architectures-CpmH4Gha.mjs.map +1 -0
  3. package/dist/browser/index.js.map +1 -1
  4. package/dist/cli.mjs +8 -8
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/frameworks/express.mjs +1 -1
  7. package/dist/frameworks/fastify.mjs +1 -1
  8. package/dist/frameworks/hono.mjs +1 -1
  9. package/dist/frameworks/next.d.mts +2 -2
  10. package/dist/frameworks/next.mjs +1 -1
  11. package/dist/frameworks/trpc.mjs +1 -1
  12. package/dist/{gerbil-_68_JSGV.d.mts → gerbil--YDNklsY.d.mts} +2 -2
  13. package/dist/{gerbil-_68_JSGV.d.mts.map → gerbil--YDNklsY.d.mts.map} +1 -1
  14. package/dist/{gerbil-DsgYlkgf.mjs → gerbil-BcBaLPai.mjs} +2 -2
  15. package/dist/{gerbil-DsgYlkgf.mjs.map → gerbil-BcBaLPai.mjs.map} +1 -1
  16. package/dist/gerbil-CTkp26G3.mjs +4 -0
  17. package/dist/gpu/hooks.d.mts +1 -1
  18. package/dist/gpu/index.d.mts +1 -1
  19. package/dist/gpu/index.mjs +3 -3
  20. package/dist/{gpu-DfUBMUGe.mjs → gpu-BZVtzX9N.mjs} +54 -9
  21. package/dist/gpu-BZVtzX9N.mjs.map +1 -0
  22. package/dist/{index-BdbeBDTz.d.mts → index-CZVIHkaH.d.mts} +23 -2
  23. package/dist/index-CZVIHkaH.d.mts.map +1 -0
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.mjs +5 -5
  26. package/dist/integrations/ai-sdk.mjs +1 -1
  27. package/dist/integrations/langchain.mjs +1 -1
  28. package/dist/integrations/llamaindex.mjs +1 -1
  29. package/dist/integrations/mcp.d.mts +2 -2
  30. package/dist/integrations/mcp.mjs +4 -4
  31. package/dist/{mcp-D3XRaVAc.mjs → mcp-LmQ6uqnx.mjs} +3 -3
  32. package/dist/{mcp-D3XRaVAc.mjs.map → mcp-LmQ6uqnx.mjs.map} +1 -1
  33. package/dist/{moonshine-stt-bExSnd2o.mjs → moonshine-stt-BFIm_Xxk.mjs} +891 -33
  34. package/dist/moonshine-stt-BFIm_Xxk.mjs.map +1 -0
  35. package/dist/moonshine-stt-D7BqG2pU.mjs +4 -0
  36. package/dist/{one-liner-UvMt5g0G.mjs → one-liner-BYVB7jU5.mjs} +2 -2
  37. package/dist/{one-liner-UvMt5g0G.mjs.map → one-liner-BYVB7jU5.mjs.map} +1 -1
  38. package/dist/repl-CYlW8byH.mjs +9 -0
  39. package/dist/skills/index.d.mts +4 -4
  40. package/dist/skills/index.mjs +3 -3
  41. package/dist/{skills-BJUofoaY.mjs → skills-DWMVEKKj.mjs} +2 -2
  42. package/dist/{skills-BJUofoaY.mjs.map → skills-DWMVEKKj.mjs.map} +1 -1
  43. package/package.json +1 -1
  44. package/dist/architectures-Ol7yUmKf.mjs.map +0 -1
  45. package/dist/gerbil-CNyb67ca.mjs +0 -4
  46. package/dist/gpu-DfUBMUGe.mjs.map +0 -1
  47. package/dist/index-BdbeBDTz.d.mts.map +0 -1
  48. package/dist/moonshine-stt-B0dwDCgT.mjs +0 -4
  49. package/dist/moonshine-stt-bExSnd2o.mjs.map +0 -1
  50. package/dist/repl-B5xHAr9n.mjs +0 -9
@@ -1,4 +1,4 @@
1
- import { C as quantizeInt4, D as createDefaultHFKeyMapper, S as DEFAULT_GROUP_SIZE, T as DTYPE_BYTES, a as generateMoonshineEncoderGraph, b as nanoCodecWeightMap, i as generateMoonshineDecoderGraph, o as moonshineEncoderFrames, p as foldNanoCodecWeightNorm, s as parseMoonshineConfig, t as generateGraph, w as CANONICAL_KEYS } from "./architectures-Ol7yUmKf.mjs";
1
+ import { C as quantizeInt4, D as createDefaultHFKeyMapper, S as DEFAULT_GROUP_SIZE, T as DTYPE_BYTES, a as generateMoonshineEncoderGraph, b as nanoCodecWeightMap, i as generateMoonshineDecoderGraph, o as moonshineEncoderFrames, p as foldNanoCodecWeightNorm, s as parseMoonshineConfig, t as generateGraph, w as CANONICAL_KEYS } from "./architectures-CpmH4Gha.mjs";
2
2
 
3
3
  //#region src/gpu/device.ts
4
4
  const COPY_SRC = 4;
@@ -2341,6 +2341,98 @@ fn main(
2341
2341
  }
2342
2342
  }
2343
2343
  `;
2344
+ const WGSL_DUAL_RMSNORM_ROPE = `\
2345
+ struct Params {
2346
+ rows0: u32, // T * num_q_heads
2347
+ rows1: u32, // T * num_kv_heads
2348
+ head_dim: u32,
2349
+ eps_bits: u32,
2350
+ num_q_heads: u32,
2351
+ num_kv_heads: u32,
2352
+ rope_base_bits: u32,
2353
+ position_offset: u32,
2354
+ rope_half: u32,
2355
+ rope_denom: u32,
2356
+ rope_active_pairs: u32,
2357
+ _pad: u32,
2358
+ }
2359
+
2360
+ @group(0) @binding(0) var<storage, read> input0: array<f32>;
2361
+ @group(0) @binding(1) var<storage, read> weight0: array<f32>;
2362
+ @group(0) @binding(2) var<storage, read> input1: array<f32>;
2363
+ @group(0) @binding(3) var<storage, read> weight1: array<f32>;
2364
+ @group(0) @binding(4) var<storage, read_write> output0: array<f32>;
2365
+ @group(0) @binding(5) var<storage, read_write> output1: array<f32>;
2366
+ @group(0) @binding(6) var<storage, read> params: Params;
2367
+
2368
+ var<workgroup> shared_sum: array<f32, 256>;
2369
+ var<workgroup> shared_row: array<f32, 256>;
2370
+
2371
+ @compute @workgroup_size(256)
2372
+ fn main(
2373
+ @builtin(local_invocation_id) lid: vec3u,
2374
+ @builtin(workgroup_id) wid: vec3u,
2375
+ ) {
2376
+ let wg = wid.x;
2377
+ if (wg >= params.rows0 + params.rows1) { return; }
2378
+
2379
+ let tid = lid.x;
2380
+ let hd = params.head_dim;
2381
+ let eps = bitcast<f32>(params.eps_bits);
2382
+
2383
+ let is1 = wg >= params.rows0;
2384
+ let row = select(wg, wg - params.rows0, is1);
2385
+ let row_offset = row * hd;
2386
+ let heads = select(params.num_q_heads, params.num_kv_heads, is1);
2387
+ let pos = row / heads;
2388
+
2389
+ // ── Phase 1: row sum of squares ──
2390
+ var val: f32 = 0.0;
2391
+ if (tid < hd) {
2392
+ if (is1) { val = input1[row_offset + tid]; } else { val = input0[row_offset + tid]; }
2393
+ }
2394
+ shared_sum[tid] = val * val;
2395
+ workgroupBarrier();
2396
+ var stride: u32 = 128u;
2397
+ while (stride > 0u) {
2398
+ if (tid < stride) { shared_sum[tid] += shared_sum[tid + stride]; }
2399
+ workgroupBarrier();
2400
+ stride = stride / 2u;
2401
+ }
2402
+ let rms = sqrt(shared_sum[0] / f32(hd) + eps);
2403
+
2404
+ // ── Phase 2: normalized row into shared memory ──
2405
+ if (tid < hd) {
2406
+ var w: f32;
2407
+ if (is1) { w = weight1[tid]; } else { w = weight0[tid]; }
2408
+ shared_row[tid] = (val / rms) * w;
2409
+ }
2410
+ workgroupBarrier();
2411
+
2412
+ // ── Phase 3: rotate_half rotation from shared pairs, write out ──
2413
+ if (tid < hd) {
2414
+ let rope_half = params.rope_half;
2415
+ var out_val = shared_row[tid];
2416
+ if (tid < 2u * rope_half) {
2417
+ let is_hi = tid >= rope_half;
2418
+ let pair_idx = select(tid, tid - rope_half, is_hi);
2419
+ var cos_val = 1.0;
2420
+ var sin_val = 0.0;
2421
+ if (pair_idx < params.rope_active_pairs) {
2422
+ let rope_base = bitcast<f32>(params.rope_base_bits);
2423
+ let position = f32(pos + params.position_offset);
2424
+ let freq = position / pow(rope_base, f32(2u * pair_idx) / f32(params.rope_denom));
2425
+ cos_val = cos(freq);
2426
+ sin_val = sin(freq);
2427
+ }
2428
+ let lo = shared_row[pair_idx];
2429
+ let hi = shared_row[pair_idx + rope_half];
2430
+ out_val = select(lo * cos_val - hi * sin_val, lo * sin_val + hi * cos_val, is_hi);
2431
+ }
2432
+ if (is1) { output1[row_offset + tid] = out_val; } else { output0[row_offset + tid] = out_val; }
2433
+ }
2434
+ }
2435
+ `;
2344
2436
  const WGSL_LAYERNORM = `\
2345
2437
  // Layer Normalization: output = ((x - mean) / sqrt(var + eps)) * weight + bias
2346
2438
 
@@ -4235,6 +4327,235 @@ fn main(@builtin(global_invocation_id) gid: vec3u) {
4235
4327
  output[t * params.channels + c] = sum / (1.0 + exp(max(-sum, -80.0)));
4236
4328
  }
4237
4329
  `;
4330
+ const WGSL_CAUSAL_CONV1D_SILU_UPDATE = `\
4331
+ struct Params {
4332
+ seq_len: u32,
4333
+ channels: u32,
4334
+ kernel_size: u32,
4335
+ _pad: u32,
4336
+ }
4337
+
4338
+ @group(0) @binding(0) var<storage, read> input: array<f32>;
4339
+ @group(0) @binding(1) var<storage, read> weight: array<f32>;
4340
+ @group(0) @binding(2) var<storage, read_write> conv_state: array<f32>;
4341
+ @group(0) @binding(3) var<storage, read_write> output: array<f32>;
4342
+ @group(0) @binding(4) var<storage, read> params: Params;
4343
+
4344
+ @compute @workgroup_size(256)
4345
+ fn main(@builtin(global_invocation_id) gid: vec3u) {
4346
+ let idx = gid.x;
4347
+ let total = params.seq_len * params.channels;
4348
+ if (idx >= total) { return; }
4349
+
4350
+ let t = idx / params.channels;
4351
+ let c = idx % params.channels;
4352
+ let state_size = params.kernel_size - 1u;
4353
+
4354
+ // Snapshot this channel's rolling state (kernel_size <= 8) before any write.
4355
+ var olds: array<f32, 8>;
4356
+ for (var s: u32 = 0u; s < state_size; s = s + 1u) {
4357
+ olds[s] = conv_state[s * params.channels + c];
4358
+ }
4359
+
4360
+ var sum: f32 = 0.0;
4361
+ for (var k: u32 = 0u; k < params.kernel_size; k = k + 1u) {
4362
+ let src_t = i32(t) - i32(k);
4363
+ var in_val: f32 = 0.0;
4364
+ if (src_t >= 0) {
4365
+ in_val = input[u32(src_t) * params.channels + c];
4366
+ } else {
4367
+ let state_t = i32(state_size) + src_t;
4368
+ if (state_t >= 0) {
4369
+ in_val = olds[u32(state_t)];
4370
+ }
4371
+ }
4372
+ let w_val = weight[c * params.kernel_size + (params.kernel_size - 1u - k)];
4373
+ sum += in_val * w_val;
4374
+ }
4375
+
4376
+ // Fused SiLU: silu(x) = x / (1 + exp(-x))
4377
+ output[t * params.channels + c] = sum / (1.0 + exp(max(-sum, -80.0)));
4378
+
4379
+ // Rolling-state shift (decode T=1): new state = [olds[1..], input[0,c]].
4380
+ if (params.seq_len == 1u && t == 0u) {
4381
+ for (var s: u32 = 0u; s + 1u < state_size; s = s + 1u) {
4382
+ conv_state[s * params.channels + c] = olds[s + 1u];
4383
+ }
4384
+ if (state_size > 0u) {
4385
+ conv_state[(state_size - 1u) * params.channels + c] = input[c];
4386
+ }
4387
+ }
4388
+ }
4389
+ `;
4390
+ const WGSL_CAUSAL_CONV1D_GATED_UPDATE = `\
4391
+ struct Params {
4392
+ seq_len: u32,
4393
+ channels: u32,
4394
+ kernel_size: u32,
4395
+ _pad: u32,
4396
+ }
4397
+
4398
+ @group(0) @binding(0) var<storage, read> input: array<f32>;
4399
+ @group(0) @binding(1) var<storage, read> weight: array<f32>;
4400
+ @group(0) @binding(2) var<storage, read_write> conv_state: array<f32>;
4401
+ @group(0) @binding(3) var<storage, read> gate: array<f32>;
4402
+ @group(0) @binding(4) var<storage, read_write> output: array<f32>;
4403
+ @group(0) @binding(5) var<storage, read> params: Params;
4404
+
4405
+ @compute @workgroup_size(256)
4406
+ fn main(@builtin(global_invocation_id) gid: vec3u) {
4407
+ let idx = gid.x;
4408
+ let total = params.seq_len * params.channels;
4409
+ if (idx >= total) { return; }
4410
+
4411
+ let t = idx / params.channels;
4412
+ let c = idx % params.channels;
4413
+ let state_size = params.kernel_size - 1u;
4414
+
4415
+ var olds: array<f32, 8>;
4416
+ for (var s: u32 = 0u; s < state_size; s = s + 1u) {
4417
+ olds[s] = conv_state[s * params.channels + c];
4418
+ }
4419
+
4420
+ var sum: f32 = 0.0;
4421
+ for (var k: u32 = 0u; k < params.kernel_size; k = k + 1u) {
4422
+ let src_t = i32(t) - i32(k);
4423
+ var in_val: f32 = 0.0;
4424
+ if (src_t >= 0) {
4425
+ in_val = input[u32(src_t) * params.channels + c];
4426
+ } else {
4427
+ let state_t = i32(state_size) + src_t;
4428
+ if (state_t >= 0) {
4429
+ in_val = olds[u32(state_t)];
4430
+ }
4431
+ }
4432
+ let w_val = weight[c * params.kernel_size + (params.kernel_size - 1u - k)];
4433
+ sum += in_val * w_val;
4434
+ }
4435
+
4436
+ output[t * params.channels + c] = gate[idx] * sum;
4437
+
4438
+ if (params.seq_len == 1u && t == 0u) {
4439
+ for (var s: u32 = 0u; s + 1u < state_size; s = s + 1u) {
4440
+ conv_state[s * params.channels + c] = olds[s + 1u];
4441
+ }
4442
+ if (state_size > 0u) {
4443
+ conv_state[(state_size - 1u) * params.channels + c] = input[c];
4444
+ }
4445
+ }
4446
+ }
4447
+ `;
4448
+ const WGSL_CAUSAL_CONV1D_GATED_BCX = `\
4449
+ struct Params {
4450
+ seq_len: u32,
4451
+ channels: u32,
4452
+ kernel_size: u32,
4453
+ in_width: u32, // bcx row stride = 3 * channels
4454
+ }
4455
+
4456
+ @group(0) @binding(0) var<storage, read> bcx: array<f32>;
4457
+ @group(0) @binding(1) var<storage, read> weight: array<f32>;
4458
+ @group(0) @binding(2) var<storage, read_write> conv_state: array<f32>;
4459
+ @group(0) @binding(3) var<storage, read_write> output: array<f32>;
4460
+ @group(0) @binding(4) var<storage, read> params: Params;
4461
+
4462
+ fn bx_at(t: u32, c: u32) -> f32 {
4463
+ let row = t * params.in_width;
4464
+ return bcx[row + c] * bcx[row + 2u * params.channels + c];
4465
+ }
4466
+
4467
+ @compute @workgroup_size(256)
4468
+ fn main(@builtin(global_invocation_id) gid: vec3u) {
4469
+ let idx = gid.x;
4470
+ let total = params.seq_len * params.channels;
4471
+ if (idx >= total) { return; }
4472
+
4473
+ let t = idx / params.channels;
4474
+ let c = idx % params.channels;
4475
+ let state_size = params.kernel_size - 1u;
4476
+
4477
+ // Snapshot this channel's rolling state (kernel_size <= 8) before any write.
4478
+ var olds: array<f32, 8>;
4479
+ for (var s: u32 = 0u; s < state_size; s = s + 1u) {
4480
+ olds[s] = conv_state[s * params.channels + c];
4481
+ }
4482
+
4483
+ var sum: f32 = 0.0;
4484
+ for (var k: u32 = 0u; k < params.kernel_size; k = k + 1u) {
4485
+ let src_t = i32(t) - i32(k);
4486
+ var in_val: f32 = 0.0;
4487
+ if (src_t >= 0) {
4488
+ in_val = bx_at(u32(src_t), c);
4489
+ } else {
4490
+ let state_t = i32(state_size) + src_t;
4491
+ if (state_t >= 0) {
4492
+ in_val = olds[u32(state_t)];
4493
+ }
4494
+ }
4495
+ let w_val = weight[c * params.kernel_size + (params.kernel_size - 1u - k)];
4496
+ sum += in_val * w_val;
4497
+ }
4498
+
4499
+ // Fused multiplicative gate from the C block.
4500
+ output[t * params.channels + c] = bcx[t * params.in_width + params.channels + c] * sum;
4501
+
4502
+ // Rolling-state shift (decode T=1 only; prefill uses ConvStateUpdateBcx).
4503
+ if (params.seq_len == 1u && t == 0u) {
4504
+ for (var s: u32 = 0u; s + 1u < state_size; s = s + 1u) {
4505
+ conv_state[s * params.channels + c] = olds[s + 1u];
4506
+ }
4507
+ if (state_size > 0u) {
4508
+ conv_state[(state_size - 1u) * params.channels + c] = bx_at(0u, c);
4509
+ }
4510
+ }
4511
+ }
4512
+ `;
4513
+ const WGSL_CONV_STATE_UPDATE_BCX = `\
4514
+ // ConvStateUpdate reading raw bcx (computes B*x for the copied timesteps).
4515
+ struct Params {
4516
+ seq_len: u32,
4517
+ channels: u32,
4518
+ state_size: u32,
4519
+ in_width: u32,
4520
+ }
4521
+
4522
+ @group(0) @binding(0) var<storage, read> bcx: array<f32>;
4523
+ @group(0) @binding(1) var<storage, read_write> conv_state: array<f32>;
4524
+ @group(0) @binding(2) var<storage, read> params: Params;
4525
+
4526
+ fn bx_at(t: u32, c: u32) -> f32 {
4527
+ let row = t * params.in_width;
4528
+ return bcx[row + c] * bcx[row + 2u * params.channels + c];
4529
+ }
4530
+
4531
+ @compute @workgroup_size(256)
4532
+ fn main(@builtin(global_invocation_id) gid: vec3u) {
4533
+ let c = gid.x;
4534
+ if (c >= params.channels) { return; }
4535
+ let ss = params.state_size;
4536
+
4537
+ if (params.seq_len >= ss) {
4538
+ for (var s: u32 = 0u; s < ss; s = s + 1u) {
4539
+ conv_state[s * params.channels + c] = bx_at(params.seq_len - ss + s, c);
4540
+ }
4541
+ } else {
4542
+ var olds: array<f32, 8>;
4543
+ for (var s: u32 = 0u; s < ss; s = s + 1u) {
4544
+ olds[s] = conv_state[s * params.channels + c];
4545
+ }
4546
+ let shift = ss - params.seq_len;
4547
+ for (var s: u32 = 0u; s < ss; s = s + 1u) {
4548
+ var val: f32 = 0.0;
4549
+ if (s < shift) {
4550
+ val = olds[s + params.seq_len];
4551
+ } else {
4552
+ val = bx_at(s - shift, c);
4553
+ }
4554
+ conv_state[s * params.channels + c] = val;
4555
+ }
4556
+ }
4557
+ }
4558
+ `;
4238
4559
  const WGSL_CAUSAL_CONV1D_GATED = `\
4239
4560
  // Causal 1D depthwise convolution with a fused multiplicative output gate:
4240
4561
  // output = gate * conv(input)
@@ -6138,31 +6459,106 @@ const MATVEC_INT4_SPEC = {
6138
6459
  ]);
6139
6460
  }
6140
6461
  };
6141
- const GATED_MATVEC_INT4_SPEC = {
6142
- shaderCode: WGSL_GATED_MATVEC_INT4,
6143
- entryPoint: "main",
6144
- bindings: [
6145
- { type: "storage-read" },
6146
- { type: "storage-read" },
6147
- { type: "storage-read" },
6148
- { type: "storage-read" },
6149
- { type: "storage-read" },
6150
- { type: "storage-read-write" },
6151
- { type: "uniform" }
6152
- ],
6153
- getDispatchSize(op) {
6154
- const N = op.attributes.N;
6155
- return [
6156
- cdiv(N, 8),
6157
- 1,
6158
- 1
6159
- ];
6160
- },
6161
- buildParams(op) {
6162
- const K = op.attributes.K;
6163
- const N = op.attributes.N;
6164
- const group_size = op.attributes.group_size;
6165
- return buildUniformBuffer([
6462
+ function deriveMatvecGeometry(nTile, kThreads) {
6463
+ return {
6464
+ ...MATVEC_INT4_SPEC,
6465
+ shaderCode: WGSL_MATVEC_INT4.replace("const N_TILE: u32 = 16u;", `const N_TILE: u32 = ${nTile}u;`).replace("const K_THREADS: u32 = 16u;", `const K_THREADS: u32 = ${kThreads}u;`),
6466
+ getDispatchSize(op) {
6467
+ const N = op.attributes.N;
6468
+ return [
6469
+ cdiv(N, nTile),
6470
+ 1,
6471
+ 1
6472
+ ];
6473
+ }
6474
+ };
6475
+ }
6476
+ function deriveMatvecF16Compute() {
6477
+ let code = WGSL_MATVEC_INT4;
6478
+ code = "enable f16;\n" + code;
6479
+ code = code.replace(` let bq = B_q[b_off_v + v];
6480
+ let g = (v * 4u) / packed_per_group;
6481
+ let scale = scales[col_g_base + g];
6482
+ let zero = zeros[col_g_base + g];
6483
+ let a_base = v * 8u;
6484
+
6485
+ for (var j: u32 = 0u; j < 4u; j++) {
6486
+ let packed = bq[j];
6487
+ // Manual nibble extraction (avoids unpack4xU8 — broken on Safari/WebKit)
6488
+ let n0 = f32(packed & 0xFu);
6489
+ let n1 = f32((packed >> 4u) & 0xFu);
6490
+ let n2 = f32((packed >> 8u) & 0xFu);
6491
+ let n3 = f32((packed >> 12u) & 0xFu);
6492
+ let n4 = f32((packed >> 16u) & 0xFu);
6493
+ let n5 = f32((packed >> 20u) & 0xFu);
6494
+ let n6 = f32((packed >> 24u) & 0xFu);
6495
+ let n7 = f32((packed >> 28u) & 0xFu);
6496
+
6497
+ let b0 = vec4f(n0 - zero, n1 - zero, n2 - zero, n3 - zero) * scale;
6498
+ let b1 = vec4f(n4 - zero, n5 - zero, n6 - zero, n7 - zero) * scale;
6499
+
6500
+ sum += dot(A[a_base + j * 2u], b0) + dot(A[a_base + j * 2u + 1u], b1);
6501
+ }`, ` let bq = B_q[b_off_v + v];
6502
+ let g = (v * 4u) / packed_per_group;
6503
+ let scale_h = f16(scales[col_g_base + g]);
6504
+ let zero_h = f16(zeros[col_g_base + g]);
6505
+ let a_base = v * 8u;
6506
+
6507
+ for (var j: u32 = 0u; j < 4u; j++) {
6508
+ let packed = bq[j];
6509
+ // Manual nibble extraction (avoids unpack4xU8 — broken on Safari/WebKit)
6510
+ let n0 = f16(packed & 0xFu);
6511
+ let n1 = f16((packed >> 4u) & 0xFu);
6512
+ let n2 = f16((packed >> 8u) & 0xFu);
6513
+ let n3 = f16((packed >> 12u) & 0xFu);
6514
+ let n4 = f16((packed >> 16u) & 0xFu);
6515
+ let n5 = f16((packed >> 20u) & 0xFu);
6516
+ let n6 = f16((packed >> 24u) & 0xFu);
6517
+ let n7 = f16((packed >> 28u) & 0xFu);
6518
+
6519
+ let b0 = vec4<f16>(n0 - zero_h, n1 - zero_h, n2 - zero_h, n3 - zero_h) * scale_h;
6520
+ let b1 = vec4<f16>(n4 - zero_h, n5 - zero_h, n6 - zero_h, n7 - zero_h) * scale_h;
6521
+
6522
+ let a0 = vec4<f16>(A[a_base + j * 2u]);
6523
+ let a1 = vec4<f16>(A[a_base + j * 2u + 1u]);
6524
+ sum += f32(dot(a0, b0)) + f32(dot(a1, b1));
6525
+ }`);
6526
+ if (!code.includes("scale_h")) throw new Error("f16c derivation failed");
6527
+ return {
6528
+ ...MATVEC_INT4_SPEC,
6529
+ shaderCode: code
6530
+ };
6531
+ }
6532
+ const MATVEC_INT4_GEO_VARIANTS = {
6533
+ "8_32": deriveMatvecGeometry(8, 32),
6534
+ "32_8": deriveMatvecGeometry(32, 8),
6535
+ f16c: deriveMatvecF16Compute()
6536
+ };
6537
+ const GATED_MATVEC_INT4_SPEC = {
6538
+ shaderCode: WGSL_GATED_MATVEC_INT4,
6539
+ entryPoint: "main",
6540
+ bindings: [
6541
+ { type: "storage-read" },
6542
+ { type: "storage-read" },
6543
+ { type: "storage-read" },
6544
+ { type: "storage-read" },
6545
+ { type: "storage-read" },
6546
+ { type: "storage-read-write" },
6547
+ { type: "uniform" }
6548
+ ],
6549
+ getDispatchSize(op) {
6550
+ const N = op.attributes.N;
6551
+ return [
6552
+ cdiv(N, 8),
6553
+ 1,
6554
+ 1
6555
+ ];
6556
+ },
6557
+ buildParams(op) {
6558
+ const K = op.attributes.K;
6559
+ const N = op.attributes.N;
6560
+ const group_size = op.attributes.group_size;
6561
+ return buildUniformBuffer([
6166
6562
  K,
6167
6563
  N,
6168
6564
  group_size,
@@ -6461,6 +6857,62 @@ const embeddingInt4Spec = {
6461
6857
  ]);
6462
6858
  }
6463
6859
  };
6860
+ const DUAL_RMSNORM_ROPE_SPEC = {
6861
+ shaderCode: WGSL_DUAL_RMSNORM_ROPE,
6862
+ entryPoint: "main",
6863
+ bindings: [
6864
+ { type: "storage-read" },
6865
+ { type: "storage-read" },
6866
+ { type: "storage-read" },
6867
+ { type: "storage-read" },
6868
+ { type: "storage-read-write" },
6869
+ { type: "storage-read-write" },
6870
+ { type: "uniform" }
6871
+ ],
6872
+ getDispatchSize(op, resolvedShapes) {
6873
+ const num_q_heads = op.attributes.num_q_heads;
6874
+ const num_kv_heads = op.attributes.num_kv_heads;
6875
+ const seqRef = op.attributes.seq_len_tensor;
6876
+ let T;
6877
+ if (seqRef && resolvedShapes[seqRef]) T = resolvedShapes[seqRef][0];
6878
+ else T = op.attributes.seq_len ?? 1;
6879
+ return [
6880
+ T * (num_q_heads + num_kv_heads),
6881
+ 1,
6882
+ 1
6883
+ ];
6884
+ },
6885
+ buildParams(op, resolvedShapes, context) {
6886
+ const head_dim = op.attributes.hidden_size;
6887
+ const eps = op.attributes.eps ?? 1e-6;
6888
+ const num_q_heads = op.attributes.num_q_heads;
6889
+ const num_kv_heads = op.attributes.num_kv_heads;
6890
+ const rope_base = op.attributes.rope_base;
6891
+ const rope_dim = op.attributes.rope_dim ?? head_dim;
6892
+ const rope_half = op.attributes.rope_half ?? rope_dim / 2;
6893
+ const rope_denom = op.attributes.rope_denom ?? rope_dim;
6894
+ const rope_active_pairs = op.attributes.rope_active_pairs ?? rope_dim / 2;
6895
+ const position_offset = context?.seqPos ?? op.attributes.position_offset ?? 0;
6896
+ const seqRef = op.attributes.seq_len_tensor;
6897
+ let T;
6898
+ if (seqRef && resolvedShapes[seqRef]) T = resolvedShapes[seqRef][0];
6899
+ else T = op.attributes.seq_len ?? 1;
6900
+ return buildUniformBuffer([
6901
+ T * num_q_heads,
6902
+ T * num_kv_heads,
6903
+ head_dim,
6904
+ f32BitsToU32(eps),
6905
+ num_q_heads,
6906
+ num_kv_heads,
6907
+ f32BitsToU32(rope_base),
6908
+ position_offset,
6909
+ rope_half,
6910
+ rope_denom,
6911
+ rope_active_pairs,
6912
+ 0
6913
+ ]);
6914
+ }
6915
+ };
6464
6916
  const ropeSpec = {
6465
6917
  shaderCode: WGSL_ROPE,
6466
6918
  entryPoint: "main",
@@ -6965,6 +7417,134 @@ const causalConv1dSiluSpec = {
6965
7417
  ]);
6966
7418
  }
6967
7419
  };
7420
+ const convUpdateDispatch = {
7421
+ getDispatchSize(op, resolvedShapes) {
7422
+ const channels = op.attributes.channels;
7423
+ const ref = op.attributes.seq_len_tensor;
7424
+ let seq_len;
7425
+ if (ref && resolvedShapes[ref]) seq_len = resolvedShapes[ref][0];
7426
+ else seq_len = op.attributes.seq_len;
7427
+ return [
7428
+ cdiv(seq_len * channels, 256),
7429
+ 1,
7430
+ 1
7431
+ ];
7432
+ },
7433
+ buildParams(op, resolvedShapes) {
7434
+ const channels = op.attributes.channels;
7435
+ const kernel_size = op.attributes.kernel_size;
7436
+ const ref = op.attributes.seq_len_tensor;
7437
+ let seq_len;
7438
+ if (ref && resolvedShapes[ref]) seq_len = resolvedShapes[ref][0];
7439
+ else seq_len = op.attributes.seq_len;
7440
+ return buildUniformBuffer([
7441
+ seq_len,
7442
+ channels,
7443
+ kernel_size,
7444
+ 0
7445
+ ]);
7446
+ }
7447
+ };
7448
+ const CAUSAL_CONV1D_SILU_UPDATE_SPEC = {
7449
+ shaderCode: WGSL_CAUSAL_CONV1D_SILU_UPDATE,
7450
+ entryPoint: "main",
7451
+ bindings: [
7452
+ { type: "storage-read" },
7453
+ { type: "storage-read" },
7454
+ { type: "storage-read-write" },
7455
+ { type: "storage-read-write" },
7456
+ { type: "uniform" }
7457
+ ],
7458
+ getDispatchSize: convUpdateDispatch.getDispatchSize,
7459
+ buildParams: convUpdateDispatch.buildParams
7460
+ };
7461
+ const CAUSAL_CONV1D_GATED_UPDATE_SPEC = {
7462
+ shaderCode: WGSL_CAUSAL_CONV1D_GATED_UPDATE,
7463
+ entryPoint: "main",
7464
+ bindings: [
7465
+ { type: "storage-read" },
7466
+ { type: "storage-read" },
7467
+ { type: "storage-read-write" },
7468
+ { type: "storage-read" },
7469
+ { type: "storage-read-write" },
7470
+ { type: "uniform" }
7471
+ ],
7472
+ getDispatchSize: convUpdateDispatch.getDispatchSize,
7473
+ buildParams: convUpdateDispatch.buildParams
7474
+ };
7475
+ const bcxDispatch = (op, resolvedShapes) => {
7476
+ const channels = op.attributes.channels;
7477
+ const ref = op.attributes.seq_len_tensor;
7478
+ let seq_len;
7479
+ if (ref && resolvedShapes[ref]) seq_len = resolvedShapes[ref][0];
7480
+ else seq_len = op.attributes.seq_len ?? 1;
7481
+ return {
7482
+ channels,
7483
+ seq_len
7484
+ };
7485
+ };
7486
+ const CAUSAL_CONV1D_GATED_BCX_SPEC = {
7487
+ shaderCode: WGSL_CAUSAL_CONV1D_GATED_BCX,
7488
+ entryPoint: "main",
7489
+ bindings: [
7490
+ { type: "storage-read" },
7491
+ { type: "storage-read" },
7492
+ { type: "storage-read-write" },
7493
+ { type: "storage-read-write" },
7494
+ { type: "uniform" }
7495
+ ],
7496
+ getDispatchSize(op, resolvedShapes) {
7497
+ const { channels, seq_len } = bcxDispatch(op, resolvedShapes);
7498
+ return [
7499
+ cdiv(seq_len * channels, 256),
7500
+ 1,
7501
+ 1
7502
+ ];
7503
+ },
7504
+ buildParams(op, resolvedShapes) {
7505
+ const { channels, seq_len } = bcxDispatch(op, resolvedShapes);
7506
+ const kernel_size = op.attributes.kernel_size;
7507
+ const in_width = op.attributes.in_width;
7508
+ return buildUniformBuffer([
7509
+ seq_len,
7510
+ channels,
7511
+ kernel_size,
7512
+ in_width
7513
+ ]);
7514
+ }
7515
+ };
7516
+ const CONV_STATE_UPDATE_BCX_SPEC = {
7517
+ shaderCode: WGSL_CONV_STATE_UPDATE_BCX,
7518
+ entryPoint: "main",
7519
+ bindings: [
7520
+ { type: "storage-read" },
7521
+ { type: "storage-read-write" },
7522
+ { type: "uniform" }
7523
+ ],
7524
+ getDispatchSize(op) {
7525
+ const channels = op.attributes.channels;
7526
+ return [
7527
+ cdiv(channels, 256),
7528
+ 1,
7529
+ 1
7530
+ ];
7531
+ },
7532
+ buildParams(op, resolvedShapes) {
7533
+ const channels = op.attributes.channels;
7534
+ const state_size = op.attributes.state_size;
7535
+ const in_width = op.attributes.in_width;
7536
+ const ref = op.attributes.T_tensor;
7537
+ let T;
7538
+ if (ref && resolvedShapes[ref]) T = resolvedShapes[ref][0];
7539
+ else T = op.attributes.T ?? 1;
7540
+ return buildUniformBuffer([
7541
+ T,
7542
+ channels,
7543
+ state_size,
7544
+ in_width
7545
+ ]);
7546
+ }
7547
+ };
6968
7548
  const causalConv1dGatedSpec = {
6969
7549
  shaderCode: WGSL_CAUSAL_CONV1D_GATED,
6970
7550
  entryPoint: "main",
@@ -8124,6 +8704,8 @@ const KERNEL_REGISTRY = {
8124
8704
  CausalConv1d: causalConv1dSpec,
8125
8705
  CausalConv1dSiLU: causalConv1dSiluSpec,
8126
8706
  CausalConv1dGated: causalConv1dGatedSpec,
8707
+ CausalConv1dGatedBcx: CAUSAL_CONV1D_GATED_BCX_SPEC,
8708
+ ConvStateUpdateBcx: CONV_STATE_UPDATE_BCX_SPEC,
8127
8709
  SigmoidGate: sigmoidGateSpec,
8128
8710
  MambaSSM: mambaSSMSpec,
8129
8711
  KVCacheAppend: kvCacheAppendSpec,
@@ -8383,7 +8965,9 @@ var Executor = class Executor {
8383
8965
  this.dispatchEntries.push(prefillEntry);
8384
8966
  if (node.opType === "MatMul" || node.opType === "MatMulInt4") {
8385
8967
  const useSubgroups = globalThis.process?.env?.GERBIL_MATVEC_SUBGROUPS === "1" && this.ctx.hasSubgroups && !this.ctx.isWebKitWebGPU;
8386
- const mvSpec = node.opType === "MatMulInt4" ? useSubgroups ? MATVEC_INT4_SUBGROUPS_SPEC : MATVEC_INT4_SPEC : useSubgroups ? MATVEC_SUBGROUPS_SPEC : MATVEC_SPEC;
8968
+ const geoKey = globalThis.GERBIL_MATVEC_GEO;
8969
+ const geoSpec = geoKey ? MATVEC_INT4_GEO_VARIANTS[geoKey] : void 0;
8970
+ const mvSpec = node.opType === "MatMulInt4" ? useSubgroups ? MATVEC_INT4_SUBGROUPS_SPEC : geoSpec ?? MATVEC_INT4_SPEC : useSubgroups ? MATVEC_SUBGROUPS_SPEC : MATVEC_SPEC;
8387
8971
  const mvPipeline = getOrCreatePipeline(this.ctx, `matvec_${nodeId}`, mvSpec.shaderCode, mvSpec.entryPoint);
8388
8972
  const mvUniformData = mvSpec.buildParams(node, dummyShapes, { seqPos: 0 });
8389
8973
  const mvUniform = createUniformBuffer(this.ctx, `mv_uniform_${nodeId}`, mvUniformData);
@@ -8406,7 +8990,10 @@ var Executor = class Executor {
8406
8990
  this.fuseDualKVCacheAppendEntries();
8407
8991
  this.fuseGatedOProjDecodeEntries();
8408
8992
  this.fuseSwiGLUGatedProjDecodeEntries();
8993
+ this.fuseDualRMSNormRoPEDecodeEntries();
8409
8994
  this.fuseDualRMSNormDecodeEntries();
8995
+ this.fuseConvStateUpdateDecodeEntries();
8996
+ this.fuseAddRMSNormDecodeEntries();
8410
8997
  console.log(`[executor] decode: ${this.decodeEntries.length} dispatches/token`);
8411
8998
  const logitsBuffer = this.getBuffer("logits");
8412
8999
  if (logitsBuffer) {
@@ -8479,7 +9066,7 @@ var Executor = class Executor {
8479
9066
  p.end();
8480
9067
  }
8481
9068
  this.ctx.device.queue.submit([enc.finish()]);
8482
- await this.ctx.device.queue.onSubmittedWorkDone();
9069
+ if (globalThis.GERBIL_NO_DRAIN == null) await this.ctx.device.queue.onSubmittedWorkDone();
8483
9070
  }
8484
9071
  const logitsBuffer = this.getBuffer("logits");
8485
9072
  if (logitsBuffer) {
@@ -8671,7 +9258,7 @@ var Executor = class Executor {
8671
9258
  p.end();
8672
9259
  }
8673
9260
  this.ctx.device.queue.submit([enc.finish()]);
8674
- await this.ctx.device.queue.onSubmittedWorkDone();
9261
+ if (globalThis.GERBIL_NO_DRAIN == null) await this.ctx.device.queue.onSubmittedWorkDone();
8675
9262
  }
8676
9263
  const copyEnc = this.ctx.device.createCommandEncoder();
8677
9264
  copyEnc.copyBufferToBuffer(embeddingBuffer, 0, readback, 0, byteLen);
@@ -8735,7 +9322,7 @@ var Executor = class Executor {
8735
9322
  pass.end();
8736
9323
  }
8737
9324
  this.ctx.device.queue.submit([enc.finish()]);
8738
- await this.ctx.device.queue.onSubmittedWorkDone();
9325
+ if (globalThis.GERBIL_NO_DRAIN == null) await this.ctx.device.queue.onSubmittedWorkDone();
8739
9326
  }
8740
9327
  const copyEnc = this.ctx.device.createCommandEncoder();
8741
9328
  copyEnc.copyBufferToBuffer(outBuffer, 0, readback, 0, byteLen);
@@ -8791,7 +9378,7 @@ var Executor = class Executor {
8791
9378
  pass.end();
8792
9379
  }
8793
9380
  this.ctx.device.queue.submit([enc.finish()]);
8794
- await this.ctx.device.queue.onSubmittedWorkDone();
9381
+ if (globalThis.GERBIL_NO_DRAIN == null) await this.ctx.device.queue.onSubmittedWorkDone();
8795
9382
  }
8796
9383
  } else {
8797
9384
  const encoder = this.ctx.device.createCommandEncoder({ label: "runGraphStep" });
@@ -8830,6 +9417,21 @@ var Executor = class Executor {
8830
9417
  */
8831
9418
  async forwardArgmax(inputIds) {
8832
9419
  const T = inputIds.length;
9420
+ const budget = this.needsMultiEncoder && globalThis.GERBIL_BUDGET != null ? globalThis.__webkitBudget ??= {
9421
+ uniformMs: 0,
9422
+ encodeMs: 0,
9423
+ drainMs: 0,
9424
+ tailMs: 0,
9425
+ mapMs: 0,
9426
+ tokens: 0
9427
+ } : null;
9428
+ let tMark = budget ? performance.now() : 0;
9429
+ const mark = (key) => {
9430
+ if (!budget) return;
9431
+ const now = performance.now();
9432
+ budget[key] += now - tMark;
9433
+ tMark = now;
9434
+ };
8833
9435
  const resolvedShapes = this.resolveShapes(T);
8834
9436
  const runtimeContext = { seqPos: this.seqPos };
8835
9437
  this.ctx.device.queue.writeBuffer(this.inputIdsBuffer, 0, inputIds);
@@ -8863,7 +9465,9 @@ var Executor = class Executor {
8863
9465
  this.ctx.device.queue.writeBuffer(this.argmaxEntry.uniformBuffer, 0, argmaxParams);
8864
9466
  }
8865
9467
  if (this.needsMultiEncoder) {
9468
+ mark("uniformMs");
8866
9469
  const group = this.webkitGroupSize;
9470
+ const perOp = budget && group === 1 ? globalThis.__webkitBudgetByOp ??= {} : null;
8867
9471
  for (let start = 0; start < this.decodeEntries.length; start += group) {
8868
9472
  const enc = this.ctx.device.createCommandEncoder();
8869
9473
  const end = Math.min(start + group, this.decodeEntries.length);
@@ -8874,8 +9478,22 @@ var Executor = class Executor {
8874
9478
  p.dispatchWorkgroups(...argmaxDispatchSizes[i]);
8875
9479
  p.end();
8876
9480
  }
9481
+ mark("encodeMs");
8877
9482
  this.ctx.device.queue.submit([enc.finish()]);
8878
- await this.ctx.device.queue.onSubmittedWorkDone();
9483
+ if (globalThis.GERBIL_NO_DRAIN == null) {
9484
+ const t0 = perOp ? performance.now() : 0;
9485
+ await this.ctx.device.queue.onSubmittedWorkDone();
9486
+ if (perOp) {
9487
+ const op = this.decodeEntries[start].node.opType;
9488
+ const cell = perOp[op] ??= {
9489
+ ms: 0,
9490
+ n: 0
9491
+ };
9492
+ cell.ms += performance.now() - t0;
9493
+ cell.n += 1;
9494
+ }
9495
+ }
9496
+ mark("drainMs");
8879
9497
  }
8880
9498
  if (this.argmaxEntry) {
8881
9499
  const enc = this.ctx.device.createCommandEncoder();
@@ -8907,10 +9525,13 @@ var Executor = class Executor {
8907
9525
  encoder.copyBufferToBuffer(this.argmaxResultBuffer, 0, this.argmaxReadback, 0, 4);
8908
9526
  this.ctx.device.queue.submit([encoder.finish()]);
8909
9527
  }
9528
+ mark("tailMs");
8910
9529
  await this.argmaxReadback.mapAsync(MAP_MODE_READ$1, 0, 4);
8911
9530
  const mapped = this.argmaxReadback.getMappedRange(0, 4);
8912
9531
  const tokenId = new Uint32Array(mapped.slice(0))[0];
8913
9532
  this.argmaxReadback.unmap();
9533
+ mark("mapMs");
9534
+ if (budget) budget.tokens += 1;
8914
9535
  this.seqPos += T;
8915
9536
  return tokenId;
8916
9537
  }
@@ -9748,6 +10369,243 @@ var Executor = class Executor {
9748
10369
  * WebKit risk: low — same single-workgroup reduction as the proven RMSNorm
9749
10370
  * kernel, only the row→input routing is added.
9750
10371
  */
10372
+ /**
10373
+ * Fuse [CausalConv1dSiLU|CausalConv1dGated → ConvStateUpdate] into one
10374
+ * dispatch (decode entries only). At T=1 the conv read and the rolling-state
10375
+ * write for a channel live in the same thread, so the fused kernel is
10376
+ * race-free; prefill (T>1) keeps the two-dispatch path.
10377
+ */
10378
+ fuseConvStateUpdateDecodeEntries() {
10379
+ let fusionCount = 0;
10380
+ for (let i = 0; i < this.decodeEntries.length - 1; i++) {
10381
+ const e1 = this.decodeEntries[i];
10382
+ const e2 = this.decodeEntries[i + 1];
10383
+ if (e1.node.opType === "CausalConv1dGatedBcx" && e2.node.opType === "ConvStateUpdateBcx" && e2.node.inputs[0] === e1.node.inputs[0] && e2.node.outputs[0] === e1.node.inputs[2] && e1.node.attributes.kernel_size <= 8) {
10384
+ this.decodeEntries.splice(i + 1, 1);
10385
+ fusionCount++;
10386
+ continue;
10387
+ }
10388
+ const isSilu = e1.node.opType === "CausalConv1dSiLU";
10389
+ const isGated = e1.node.opType === "CausalConv1dGated";
10390
+ if (!isSilu && !isGated) continue;
10391
+ if (e2.node.opType !== "ConvStateUpdate") continue;
10392
+ if (e2.node.inputs[0] !== e1.node.inputs[0]) continue;
10393
+ if (e2.node.outputs[0] !== e1.node.inputs[2]) continue;
10394
+ const kernelSize = e1.node.attributes.kernel_size;
10395
+ if (e2.node.attributes.state_size !== kernelSize - 1) continue;
10396
+ if (kernelSize > 8) continue;
10397
+ const inputBuf = this.getBuffer(e1.node.inputs[0]);
10398
+ const weightBuf = this.getBuffer(e1.node.inputs[1]);
10399
+ const stateBuf = this.getBuffer(e1.node.inputs[2]);
10400
+ const gateBuf = isGated ? this.getBuffer(e1.node.inputs[3]) : null;
10401
+ const outBuf = this.getBuffer(e1.node.outputs[0]);
10402
+ if (!inputBuf || !weightBuf || !stateBuf || !outBuf || isGated && !gateBuf) continue;
10403
+ if (outBuf === inputBuf || outBuf === stateBuf || stateBuf === inputBuf) continue;
10404
+ if (gateBuf && (gateBuf === outBuf || gateBuf === stateBuf)) continue;
10405
+ const fusedSpec = isSilu ? CAUSAL_CONV1D_SILU_UPDATE_SPEC : CAUSAL_CONV1D_GATED_UPDATE_SPEC;
10406
+ const fusedPipeline = getOrCreatePipeline(this.ctx, `conv_update_${e1.nodeId}`, fusedSpec.shaderCode, fusedSpec.entryPoint);
10407
+ const fusedUniformData = fusedSpec.buildParams(e1.node, {}, { seqPos: 0 });
10408
+ const fusedUniform = createUniformBuffer(this.ctx, `uniform_conv_update_${e1.nodeId}`, fusedUniformData);
10409
+ const bufferList = isSilu ? [
10410
+ { buffer: inputBuf },
10411
+ { buffer: weightBuf },
10412
+ { buffer: stateBuf },
10413
+ { buffer: outBuf },
10414
+ { buffer: fusedUniform }
10415
+ ] : [
10416
+ { buffer: inputBuf },
10417
+ { buffer: weightBuf },
10418
+ { buffer: stateBuf },
10419
+ { buffer: gateBuf },
10420
+ { buffer: outBuf },
10421
+ { buffer: fusedUniform }
10422
+ ];
10423
+ const fusedBindGroup = createBindGroup(this.ctx, fusedPipeline, bufferList, `bg_conv_update_${e1.nodeId}`);
10424
+ const fusedEntry = {
10425
+ nodeId: `fused_conv_update_${e1.nodeId}`,
10426
+ node: e1.node,
10427
+ spec: fusedSpec,
10428
+ pipeline: fusedPipeline,
10429
+ bindGroup: fusedBindGroup,
10430
+ uniformBuffer: fusedUniform,
10431
+ lastParamsBytes: null,
10432
+ lastDispatchSize: null,
10433
+ buffers: bufferList
10434
+ };
10435
+ this.decodeEntries.splice(i, 2, fusedEntry);
10436
+ fusionCount++;
10437
+ }
10438
+ if (fusionCount > 0) console.log(`[executor] Fused ${fusionCount} conv+state-update pairs (saved ${fusionCount} dispatches)`);
10439
+ }
10440
+ /**
10441
+ * Fuse [per-head q_norm RMSNorm, k_norm RMSNorm, in-place RoPE] into ONE
10442
+ * DualRMSNormRoPE dispatch. Numerically identical (norm then rotate, same
10443
+ * math); requires head_dim <= 256 and RoPE operating in-place on exactly the
10444
+ * two norm outputs. Saves 2 dispatches per full-attention layer — a mobile
10445
+ * (WebKit submit-per-dispatch) win, desktop-neutral.
10446
+ */
10447
+ /**
10448
+ * Fuse [Add → RMSNorm] into a single ResidualRMSNorm dispatch: the add's sum
10449
+ * is still materialized (sum_output — downstream residual chain reads it) and
10450
+ * the norm result is written in the same dispatch. Numerically identical.
10451
+ */
10452
+ fuseAddRMSNormDecodeEntries() {
10453
+ let fusionCount = 0;
10454
+ for (let i = 0; i < this.decodeEntries.length - 1; i++) {
10455
+ const e0 = this.decodeEntries[i];
10456
+ const e1 = this.decodeEntries[i + 1];
10457
+ if (e0.node.opType !== "Add" || e1.node.opType !== "RMSNorm") continue;
10458
+ if (e1.node.inputs[0] !== e0.node.outputs[0]) continue;
10459
+ const a = e0.node.inputs[0];
10460
+ const b = e0.node.inputs[1];
10461
+ const sumOut = e0.node.outputs[0];
10462
+ const normW = e1.node.inputs[1];
10463
+ const normOut = e1.node.outputs[0];
10464
+ const aBuf = this.getBuffer(a);
10465
+ const bBuf = this.getBuffer(b);
10466
+ const sumBuf = this.getBuffer(sumOut);
10467
+ const wBuf = this.getBuffer(normW);
10468
+ const outBuf = this.getBuffer(normOut);
10469
+ if (!aBuf || !bBuf || !sumBuf || !wBuf || !outBuf) continue;
10470
+ const bufs = [
10471
+ aBuf,
10472
+ bBuf,
10473
+ wBuf,
10474
+ sumBuf,
10475
+ outBuf
10476
+ ];
10477
+ if (new Set(bufs).size !== bufs.length) continue;
10478
+ const fusedNode = {
10479
+ id: `fused_addnorm_${e0.nodeId}`,
10480
+ opType: "ResidualRMSNorm",
10481
+ inputs: [
10482
+ a,
10483
+ b,
10484
+ normW
10485
+ ],
10486
+ outputs: [sumOut, normOut],
10487
+ attributes: {
10488
+ hidden_size: e1.node.attributes.hidden_size,
10489
+ eps: e1.node.attributes.eps,
10490
+ seq_len_tensor: e1.node.attributes.seq_len_tensor ?? e0.node.attributes.count_tensor
10491
+ }
10492
+ };
10493
+ const fusedSpec = KERNEL_REGISTRY.ResidualRMSNorm;
10494
+ if (!fusedSpec) continue;
10495
+ const fusedPipeline = getOrCreatePipeline(this.ctx, `add_norm_${e0.nodeId}`, fusedSpec.shaderCode, fusedSpec.entryPoint);
10496
+ const dummyShapes = this.resolveShapes(1);
10497
+ const fusedUniformData = fusedSpec.buildParams(fusedNode, dummyShapes, { seqPos: 0 });
10498
+ const fusedUniform = createUniformBuffer(this.ctx, `uniform_add_norm_${e0.nodeId}`, fusedUniformData);
10499
+ const bufferEntries = [
10500
+ { buffer: aBuf },
10501
+ { buffer: bBuf },
10502
+ { buffer: wBuf },
10503
+ { buffer: sumBuf },
10504
+ { buffer: outBuf },
10505
+ { buffer: fusedUniform }
10506
+ ];
10507
+ const fusedBindGroup = createBindGroup(this.ctx, fusedPipeline, bufferEntries, `bg_add_norm_${e0.nodeId}`);
10508
+ this.decodeEntries.splice(i, 2, {
10509
+ nodeId: fusedNode.id,
10510
+ node: fusedNode,
10511
+ spec: fusedSpec,
10512
+ pipeline: fusedPipeline,
10513
+ bindGroup: fusedBindGroup,
10514
+ uniformBuffer: fusedUniform,
10515
+ lastParamsBytes: null,
10516
+ lastDispatchSize: null,
10517
+ buffers: bufferEntries
10518
+ });
10519
+ fusionCount++;
10520
+ }
10521
+ if (fusionCount > 0) console.log(`[executor] Fused ${fusionCount} add+RMSNorm pairs (saved ${fusionCount} dispatches)`);
10522
+ }
10523
+ fuseDualRMSNormRoPEDecodeEntries() {
10524
+ let fusionCount = 0;
10525
+ for (let i = 0; i < this.decodeEntries.length - 2; i++) {
10526
+ const e0 = this.decodeEntries[i];
10527
+ const e1 = this.decodeEntries[i + 1];
10528
+ const e2 = this.decodeEntries[i + 2];
10529
+ if (e0.node.opType !== "RMSNorm" || e1.node.opType !== "RMSNorm") continue;
10530
+ if (e2.node.opType !== "RoPE") continue;
10531
+ if (e2.node.attributes.interleaved === true) continue;
10532
+ if (e0.node.attributes.hidden_size !== e1.node.attributes.hidden_size) continue;
10533
+ if (e0.node.attributes.eps !== e1.node.attributes.eps) continue;
10534
+ const headDim = e0.node.attributes.hidden_size;
10535
+ if (headDim > 256) continue;
10536
+ if (e2.node.attributes.head_dim !== headDim) continue;
10537
+ const out0 = e0.node.outputs[0];
10538
+ const out1 = e1.node.outputs[0];
10539
+ if (e2.node.inputs[0] !== out0 || e2.node.inputs[1] !== out1) continue;
10540
+ if (e2.node.outputs[0] !== out0 || e2.node.outputs[1] !== out1) continue;
10541
+ const in0 = e0.node.inputs[0];
10542
+ const w0 = e0.node.inputs[1];
10543
+ const in1 = e1.node.inputs[0];
10544
+ const w1 = e1.node.inputs[1];
10545
+ const in0Buf = this.getBuffer(in0);
10546
+ const w0Buf = this.getBuffer(w0);
10547
+ const out0Buf = this.getBuffer(out0);
10548
+ const in1Buf = this.getBuffer(in1);
10549
+ const w1Buf = this.getBuffer(w1);
10550
+ const out1Buf = this.getBuffer(out1);
10551
+ if (!in0Buf || !w0Buf || !out0Buf || !in1Buf || !w1Buf || !out1Buf) continue;
10552
+ if (out0Buf === out1Buf) continue;
10553
+ if (in0Buf === out1Buf || in1Buf === out0Buf) continue;
10554
+ const fusedNode = {
10555
+ id: `fused_dualnorm_rope_${e0.nodeId}`,
10556
+ opType: "RMSNorm",
10557
+ inputs: [
10558
+ in0,
10559
+ w0,
10560
+ in1,
10561
+ w1
10562
+ ],
10563
+ outputs: [out0, out1],
10564
+ attributes: {
10565
+ hidden_size: headDim,
10566
+ eps: e0.node.attributes.eps,
10567
+ seq_len_tensor: e0.node.attributes.seq_len_tensor,
10568
+ seq_len: e0.node.attributes.seq_len,
10569
+ num_q_heads: e2.node.attributes.num_q_heads,
10570
+ num_kv_heads: e2.node.attributes.num_kv_heads,
10571
+ rope_base: e2.node.attributes.rope_base,
10572
+ rope_dim: e2.node.attributes.rope_dim,
10573
+ rope_half: e2.node.attributes.rope_half,
10574
+ rope_denom: e2.node.attributes.rope_denom,
10575
+ rope_active_pairs: e2.node.attributes.rope_active_pairs
10576
+ }
10577
+ };
10578
+ const fusedSpec = DUAL_RMSNORM_ROPE_SPEC;
10579
+ const fusedPipeline = getOrCreatePipeline(this.ctx, `dual_norm_rope_${e0.nodeId}`, fusedSpec.shaderCode, fusedSpec.entryPoint);
10580
+ const dummyShapes = this.resolveShapes(1);
10581
+ const fusedUniformData = fusedSpec.buildParams(fusedNode, dummyShapes, { seqPos: 0 });
10582
+ const fusedUniform = createUniformBuffer(this.ctx, `uniform_dual_norm_rope_${e0.nodeId}`, fusedUniformData);
10583
+ const bufferEntries = [
10584
+ { buffer: in0Buf },
10585
+ { buffer: w0Buf },
10586
+ { buffer: in1Buf },
10587
+ { buffer: w1Buf },
10588
+ { buffer: out0Buf },
10589
+ { buffer: out1Buf },
10590
+ { buffer: fusedUniform }
10591
+ ];
10592
+ const fusedBindGroup = createBindGroup(this.ctx, fusedPipeline, bufferEntries, `bg_dual_norm_rope_${e0.nodeId}`);
10593
+ const fusedEntry = {
10594
+ nodeId: fusedNode.id,
10595
+ node: fusedNode,
10596
+ spec: fusedSpec,
10597
+ pipeline: fusedPipeline,
10598
+ bindGroup: fusedBindGroup,
10599
+ uniformBuffer: fusedUniform,
10600
+ lastParamsBytes: null,
10601
+ lastDispatchSize: null,
10602
+ buffers: bufferEntries
10603
+ };
10604
+ this.decodeEntries.splice(i, 3, fusedEntry);
10605
+ fusionCount++;
10606
+ }
10607
+ if (fusionCount > 0) console.log(`[executor] Fused ${fusionCount} norm+norm+RoPE triples (saved ${fusionCount * 2} dispatches)`);
10608
+ }
9751
10609
  fuseDualRMSNormDecodeEntries() {
9752
10610
  let fusionCount = 0;
9753
10611
  for (let i = 0; i < this.decodeEntries.length - 1; i++) {
@@ -13425,4 +14283,4 @@ function selectGraphWeights(graph, weights) {
13425
14283
 
13426
14284
  //#endregion
13427
14285
  export { verifyGPU as S, createStorageBuffer as _, loadMoonshine as a, getOrCreatePipeline as b, quantizeBackboneInt4 as c, Tokenizer as d, Executor as f, createBindGroup as g, clearPipelineCache as h, loadModel as i, quantizeKaniBackbone as l, MATMUL_BIAS_F16C_SPEC as m, MoonshineEncoderExecutor as n, loadOuteTTS as o, KERNEL_REGISTRY as p, loadKaniTTS as r, loadParlerTTS as s, MoonshineSTT as t, remapPrunedToken as u, createUniformBuffer as v, initGPU as x, destroyBuffers as y };
13428
- //# sourceMappingURL=moonshine-stt-bExSnd2o.mjs.map
14286
+ //# sourceMappingURL=moonshine-stt-BFIm_Xxk.mjs.map