@tryhamster/gerbil 1.4.1 → 1.6.0

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 (41) hide show
  1. package/dist/browser/index.d.ts.map +1 -1
  2. package/dist/browser/index.js +10 -0
  3. package/dist/browser/index.js.map +1 -1
  4. package/dist/cli.mjs +7 -7
  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.mjs +1 -1
  10. package/dist/frameworks/trpc.mjs +1 -1
  11. package/dist/gerbil-CNyb67ca.mjs +4 -0
  12. package/dist/{gerbil-DGrCwGtJ.mjs → gerbil-DsgYlkgf.mjs} +12 -2
  13. package/dist/gerbil-DsgYlkgf.mjs.map +1 -0
  14. package/dist/gpu/hooks.d.mts.map +1 -1
  15. package/dist/gpu/index.mjs +2 -2
  16. package/dist/{gpu-DbB321DN.mjs → gpu-DfUBMUGe.mjs} +2 -2
  17. package/dist/{gpu-DbB321DN.mjs.map → gpu-DfUBMUGe.mjs.map} +1 -1
  18. package/dist/index-BdbeBDTz.d.mts.map +1 -1
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +4 -4
  21. package/dist/integrations/ai-sdk.mjs +1 -1
  22. package/dist/integrations/langchain.mjs +1 -1
  23. package/dist/integrations/llamaindex.mjs +1 -1
  24. package/dist/integrations/mcp.mjs +4 -4
  25. package/dist/{mcp-DcE3wn-L.mjs → mcp-D3XRaVAc.mjs} +3 -3
  26. package/dist/{mcp-DcE3wn-L.mjs.map → mcp-D3XRaVAc.mjs.map} +1 -1
  27. package/dist/moonshine-stt-B0dwDCgT.mjs +4 -0
  28. package/dist/{moonshine-stt-BGmctcdO.mjs → moonshine-stt-bExSnd2o.mjs} +271 -66
  29. package/dist/moonshine-stt-bExSnd2o.mjs.map +1 -0
  30. package/dist/{one-liner-DTGg6zdm.mjs → one-liner-UvMt5g0G.mjs} +2 -2
  31. package/dist/{one-liner-DTGg6zdm.mjs.map → one-liner-UvMt5g0G.mjs.map} +1 -1
  32. package/dist/{repl-Ci0zXCT_.mjs → repl-B5xHAr9n.mjs} +3 -3
  33. package/dist/skills/index.d.mts +2 -2
  34. package/dist/skills/index.mjs +3 -3
  35. package/dist/{skills-Dsf0op66.mjs → skills-BJUofoaY.mjs} +2 -2
  36. package/dist/{skills-Dsf0op66.mjs.map → skills-BJUofoaY.mjs.map} +1 -1
  37. package/package.json +1 -1
  38. package/dist/gerbil-CUzVDAU7.mjs +0 -4
  39. package/dist/gerbil-DGrCwGtJ.mjs.map +0 -1
  40. package/dist/moonshine-stt-BGmctcdO.mjs.map +0 -1
  41. package/dist/moonshine-stt-QoNJeCzR.mjs +0 -4
@@ -27,7 +27,7 @@ async function initGPU(options) {
27
27
  if (!adapter) throw new Error("No WebGPU adapter found. Your GPU may not support WebGPU, or another tab may be using it. Try closing other tabs.");
28
28
  const hasF16 = adapter.features.has("shader-f16");
29
29
  const hasSubgroups = adapter.features.has("subgroups");
30
- const hasTimestamp = typeof process !== "undefined" && process.env?.GERBIL_PROFILE != null && adapter.features.has("timestamp-query");
30
+ const hasTimestamp = typeof process !== "undefined" && (process.env?.GERBIL_PROFILE != null || process.env?.GERBIL_TS_TOTAL != null) && adapter.features.has("timestamp-query");
31
31
  const requiredFeatures = [];
32
32
  if (hasF16) requiredFeatures.push("shader-f16");
33
33
  if (hasSubgroups) requiredFeatures.push("subgroups");
@@ -3864,8 +3864,10 @@ fn main(
3864
3864
  `;
3865
3865
  const WGSL_SWIGLU_MATVEC_INT4 = `\
3866
3866
  // CONSTRAINT: N_TILE must equal workgroup_size / K_THREADS
3867
- const N_TILE: u32 = 8u;
3868
- const K_THREADS: u32 = 32u;
3867
+ // 16/16 matches the plain-matvec sweep winner (8/32 measured slower at every
3868
+ // production shape; this fused kernel had never been re-swept).
3869
+ const N_TILE: u32 = 16u;
3870
+ const K_THREADS: u32 = 16u;
3869
3871
 
3870
3872
  struct Params {
3871
3873
  K: u32,
@@ -3969,8 +3971,8 @@ fn main(
3969
3971
  }
3970
3972
  `;
3971
3973
  const WGSL_DUAL_MATVEC_INT4 = `\
3972
- const N_TILE: u32 = 8u;
3973
- const K_THREADS: u32 = 32u;
3974
+ const N_TILE: u32 = 16u;
3975
+ const K_THREADS: u32 = 16u;
3974
3976
 
3975
3977
  struct Params {
3976
3978
  K: u32,
@@ -4470,20 +4472,45 @@ struct Params {
4470
4472
  @group(0) @binding(6) var<storage, read_write> output: array<f32>;
4471
4473
  @group(0) @binding(7) var<storage, read> params: Params;
4472
4474
 
4473
- // Shared memory for Q and K vectors (L2-normalized) and reduction
4475
+ // OCCUPANCY FAN-OUT: one workgroup per (head, d-chunk) instead of per head.
4476
+ // Each head's state column d is touched ONLY by the workgroup owning d's chunk
4477
+ // (kv_dot for lane d reads state[:, d]; the rank-1 update writes state[:, d]),
4478
+ // so chunks are fully independent across timesteps — no cross-workgroup sync.
4479
+ // 16 heads → 128 workgroups (vs 16), each 128 threads = D_LANES(16) × K_SPLITS(8).
4480
+ // Requires val_dim % (D_CHUNKS*... ) handled via bounds guards; key_dim <= 128.
4481
+
4482
+ const D_CHUNKS: u32 = 8u; // workgroups per head (must match JS dispatch)
4483
+ const D_LANES: u32 = 16u; // d lanes per workgroup
4484
+ const K_SPLITS: u32 = 8u; // threads cooperating on each d lane's k loop
4485
+
4486
+ // Shared memory for Q and K vectors (L2-normalized), reduction, partials
4474
4487
  var<workgroup> shared_q: array<f32, 128>;
4475
4488
  var<workgroup> shared_k: array<f32, 128>;
4476
4489
  var<workgroup> shared_norm: array<f32, 128>;
4490
+ var<workgroup> shared_partial: array<f32, 128>; // [D_LANES][K_SPLITS]
4491
+ var<workgroup> shared_kvmem: array<f32, D_LANES>;
4477
4492
 
4478
4493
  @compute @workgroup_size(128)
4479
4494
  fn main(
4480
4495
  @builtin(local_invocation_id) lid: vec3u,
4481
4496
  @builtin(workgroup_id) wid: vec3u,
4482
4497
  ) {
4483
- let h = wid.x; // uniform within workgroup
4498
+ let h = wid.x / D_CHUNKS;
4499
+ let chunk = wid.x % D_CHUNKS;
4484
4500
  if (h >= params.num_heads) { return; }
4485
4501
 
4486
- let d = lid.x; // 0..127, all threads participate in barriers
4502
+ let tid = lid.x; // 0..127
4503
+ let d_local = tid % D_LANES; // lane within chunk
4504
+ let k_split = tid / D_LANES; // 0..K_SPLITS-1
4505
+ let d_per_chunk = (params.val_dim + D_CHUNKS - 1u) / D_CHUNKS;
4506
+ // This lane's output dim (may exceed val_dim for ragged sizes — guarded below).
4507
+ // d_per_chunk > D_LANES is unsupported (val_dim > 128 with D_CHUNKS=8).
4508
+ let d = chunk * d_per_chunk + d_local;
4509
+ let d_ok = d < params.val_dim && d_local < d_per_chunk;
4510
+
4511
+ let k_slice = (params.key_dim + K_SPLITS - 1u) / K_SPLITS;
4512
+ let k_begin = k_split * k_slice;
4513
+ let k_end = min(k_begin + k_slice, params.key_dim);
4487
4514
 
4488
4515
  let A_val = -exp(A_log[h]);
4489
4516
  let state_base = h * params.key_dim * params.val_dim;
@@ -4497,37 +4524,48 @@ fn main(
4497
4524
  for (var t: u32 = 0u; t < params.T; t = t + 1u) {
4498
4525
  let qkv_base = t * params.qkv_dim;
4499
4526
 
4500
- // ── Load Q and K into shared memory ──
4501
- shared_q[d] = qkv_conv[qkv_base + q_off + d];
4502
- shared_k[d] = qkv_conv[qkv_base + k_off + d];
4527
+ // ── Load Q and K into shared memory (lane tid covers key_dim <= 128) ──
4528
+ if (tid < params.key_dim) {
4529
+ shared_q[tid] = qkv_conv[qkv_base + q_off + tid];
4530
+ shared_k[tid] = qkv_conv[qkv_base + k_off + tid];
4531
+ shared_norm[tid] = shared_q[tid] * shared_q[tid];
4532
+ } else {
4533
+ shared_norm[tid] = 0.0;
4534
+ }
4503
4535
  workgroupBarrier();
4504
4536
 
4505
4537
  // ── L2-normalize Q (parallel reduction) ──
4506
- shared_norm[d] = shared_q[d] * shared_q[d];
4507
- workgroupBarrier();
4508
4538
  var stride: u32 = 64u;
4509
4539
  while (stride > 0u) {
4510
- if (d < stride) { shared_norm[d] += shared_norm[d + stride]; }
4540
+ if (tid < stride) { shared_norm[tid] += shared_norm[tid + stride]; }
4511
4541
  workgroupBarrier();
4512
4542
  stride /= 2u;
4513
4543
  }
4514
4544
  let q_inv_norm = 1.0 / sqrt(shared_norm[0] + 1e-12);
4515
- shared_q[d] *= q_inv_norm;
4545
+ workgroupBarrier();
4516
4546
 
4517
4547
  // ── L2-normalize K (parallel reduction) ──
4518
- shared_norm[d] = shared_k[d] * shared_k[d];
4548
+ if (tid < params.key_dim) {
4549
+ shared_q[tid] *= q_inv_norm;
4550
+ shared_norm[tid] = shared_k[tid] * shared_k[tid];
4551
+ } else {
4552
+ shared_norm[tid] = 0.0;
4553
+ }
4519
4554
  workgroupBarrier();
4520
4555
  stride = 64u;
4521
4556
  while (stride > 0u) {
4522
- if (d < stride) { shared_norm[d] += shared_norm[d + stride]; }
4557
+ if (tid < stride) { shared_norm[tid] += shared_norm[tid + stride]; }
4523
4558
  workgroupBarrier();
4524
4559
  stride /= 2u;
4525
4560
  }
4526
4561
  let k_inv_norm = 1.0 / sqrt(shared_norm[0] + 1e-12);
4527
- shared_k[d] *= k_inv_norm;
4562
+ workgroupBarrier();
4563
+ if (tid < params.key_dim) {
4564
+ shared_k[tid] *= k_inv_norm;
4565
+ }
4528
4566
  workgroupBarrier();
4529
4567
 
4530
- // ── Compute decay and beta ──
4568
+ // ── Compute decay and beta (redundantly per thread — trivial scalars) ──
4531
4569
  let a_val = a_input[t * params.num_heads + h];
4532
4570
  let b_val = b_input[t * params.num_heads + h];
4533
4571
  let dt_in = a_val + dt_bias[h];
@@ -4536,32 +4574,52 @@ fn main(
4536
4574
  let decay = exp(max(g, -80.0));
4537
4575
  let beta = 1.0 / (1.0 + exp(max(-b_val, -80.0)));
4538
4576
 
4539
- // ── 1+2. Retrieve from memory (decay is a per-step scalar, so it factors
4540
- // out of the dot product): kv_mem[d] = decay * Σ_k state[k,d] * k_norm[k] ──
4541
- let v_val = qkv_conv[qkv_base + v_off + d];
4577
+ // ── 1+2. kv_mem[d] = decay * Σ_k state[k,d] * k_norm[k], k split 8 ways ──
4542
4578
  var kv_dot: f32 = 0.0;
4543
- for (var k: u32 = 0u; k < params.key_dim; k = k + 1u) {
4544
- kv_dot += f32(ssm_state[state_base + k * params.val_dim + d]) * shared_k[k];
4579
+ if (d_ok) {
4580
+ for (var k: u32 = k_begin; k < k_end; k = k + 1u) {
4581
+ kv_dot += f32(ssm_state[state_base + k * params.val_dim + d]) * shared_k[k];
4582
+ }
4545
4583
  }
4546
- let kv_mem = kv_dot * decay;
4547
-
4548
- // ── 3. Delta rule: delta[d] = (v[d] - kv_mem[d]) * beta ──
4549
- let delta_val = (v_val - kv_mem) * beta;
4584
+ shared_partial[d_local * K_SPLITS + k_split] = kv_dot;
4585
+ workgroupBarrier();
4586
+ if (k_split == 0u) {
4587
+ var acc: f32 = 0.0;
4588
+ for (var j: u32 = 0u; j < K_SPLITS; j = j + 1u) {
4589
+ acc += shared_partial[d_local * K_SPLITS + j];
4590
+ }
4591
+ shared_kvmem[d_local] = acc;
4592
+ }
4593
+ workgroupBarrier();
4550
4594
 
4551
- // ── 4+5. Fused decay + rank-1 update + output in ONE pass over state
4552
- // (one read + one write instead of three reads + two writes):
4553
- // state'[k,d] = state[k,d] * decay + k_norm[k] * delta[d]
4554
- // out[d] = Σ_k state'[k,d] * q_norm[k] * scale ──
4555
- var out_val: f32 = 0.0;
4556
- for (var k: u32 = 0u; k < params.key_dim; k = k + 1u) {
4557
- let idx = state_base + k * params.val_dim + d;
4558
- let s = f32(ssm_state[idx]) * decay + shared_k[k] * delta_val;
4559
- ssm_state[idx] = f16(s);
4560
- out_val += s * shared_q[k];
4595
+ // ── 3. Delta rule (each of the d's 8 threads recomputes cheap) ──
4596
+ var delta_val: f32 = 0.0;
4597
+ if (d_ok) {
4598
+ let v_val = qkv_conv[qkv_base + v_off + d];
4599
+ let kv_mem = shared_kvmem[d_local] * decay;
4600
+ delta_val = (v_val - kv_mem) * beta;
4561
4601
  }
4562
- output[t * out_stride + h * params.val_dim + d] = out_val * scale;
4563
4602
 
4564
- workgroupBarrier(); // sync before next timestep loads shared_q/k
4603
+ // ── 4+5. Fused decay + rank-1 update + output, k split 8 ways ──
4604
+ var out_dot: f32 = 0.0;
4605
+ if (d_ok) {
4606
+ for (var k: u32 = k_begin; k < k_end; k = k + 1u) {
4607
+ let idx = state_base + k * params.val_dim + d;
4608
+ let s = f32(ssm_state[idx]) * decay + shared_k[k] * delta_val;
4609
+ ssm_state[idx] = f16(s);
4610
+ out_dot += s * shared_q[k];
4611
+ }
4612
+ }
4613
+ shared_partial[d_local * K_SPLITS + k_split] = out_dot;
4614
+ workgroupBarrier();
4615
+ if (k_split == 0u && d_ok) {
4616
+ var acc: f32 = 0.0;
4617
+ for (var j: u32 = 0u; j < K_SPLITS; j = j + 1u) {
4618
+ acc += shared_partial[d_local * K_SPLITS + j];
4619
+ }
4620
+ output[t * out_stride + h * params.val_dim + d] = acc * scale;
4621
+ }
4622
+ workgroupBarrier(); // sync before next timestep reuses shared arrays
4565
4623
  }
4566
4624
  }
4567
4625
  `;
@@ -6165,7 +6223,7 @@ const SWIGLU_MATVEC_INT4_SPEC = {
6165
6223
  getDispatchSize(op) {
6166
6224
  const N = op.attributes.N;
6167
6225
  return [
6168
- cdiv(N, 8),
6226
+ cdiv(N, 16),
6169
6227
  1,
6170
6228
  1
6171
6229
  ];
@@ -6200,7 +6258,7 @@ const DUAL_MATVEC_INT4_SPEC = {
6200
6258
  getDispatchSize(op) {
6201
6259
  const N = op.attributes.N;
6202
6260
  return [
6203
- cdiv(N, 8),
6261
+ cdiv(N, 16),
6204
6262
  1,
6205
6263
  1
6206
6264
  ];
@@ -7008,7 +7066,14 @@ const mambaSSMSpec = {
7008
7066
  };
7009
7067
  const MAMBA_SSM_F16_SPEC = {
7010
7068
  ...mambaSSMSpec,
7011
- shaderCode: WGSL_MAMBA_SSM_F16
7069
+ shaderCode: WGSL_MAMBA_SSM_F16,
7070
+ getDispatchSize(op) {
7071
+ return [
7072
+ op.attributes.num_heads * 8,
7073
+ 1,
7074
+ 1
7075
+ ];
7076
+ }
7012
7077
  };
7013
7078
  const kvCacheAppendSpec = {
7014
7079
  shaderCode: WGSL_KV_CACHE_APPEND,
@@ -8908,7 +8973,39 @@ var Executor = class Executor {
8908
8973
  }
8909
8974
  const encoder = this.ctx.device.createCommandEncoder({ label: "decode_step" });
8910
8975
  if (tokenId === null) encoder.copyBufferToBuffer(this.argmaxResultBuffer, 0, this.inputIdsBuffer, 0, 4);
8911
- const pass = encoder.beginComputePass({ label: "decode_pass" });
8976
+ const g = globalThis;
8977
+ if (process.env?.GERBIL_TS_TOTAL != null) {
8978
+ g.__tsTotal ??= {
8979
+ ns: 0,
8980
+ steps: 0,
8981
+ seq: 0,
8982
+ inFlight: false
8983
+ };
8984
+ g.__tsTotal.seq++;
8985
+ }
8986
+ const tsTotal = this.ctx.hasTimestamp && typeof process !== "undefined" && process.env?.GERBIL_TS_TOTAL != null && g.__tsTotal.seq % 8 === 0 && !g.__tsTotal.inFlight;
8987
+ if (tsTotal && !this.querySet) {
8988
+ this.querySet = this.ctx.device.createQuerySet({
8989
+ type: "timestamp",
8990
+ count: 2
8991
+ });
8992
+ this.queryResolveBuf = this.ctx.device.createBuffer({
8993
+ size: 16,
8994
+ usage: 516
8995
+ });
8996
+ this.queryReadbackBuf = this.ctx.device.createBuffer({
8997
+ size: 16,
8998
+ usage: 9
8999
+ });
9000
+ }
9001
+ const pass = encoder.beginComputePass(tsTotal ? {
9002
+ label: "decode_pass",
9003
+ timestampWrites: {
9004
+ querySet: this.querySet,
9005
+ beginningOfPassWriteIndex: 0,
9006
+ endOfPassWriteIndex: 1
9007
+ }
9008
+ } : { label: "decode_pass" });
8912
9009
  for (let i = 0; i < this.decodeEntries.length; i++) {
8913
9010
  const entry = this.decodeEntries[i];
8914
9011
  pass.setPipeline(entry.pipeline);
@@ -8922,7 +9019,27 @@ var Executor = class Executor {
8922
9019
  }
8923
9020
  pass.end();
8924
9021
  encoder.copyBufferToBuffer(this.argmaxResultBuffer, 0, this.decodeReadbacks[slot], 0, 4);
9022
+ if (tsTotal) {
9023
+ encoder.resolveQuerySet(this.querySet, 0, 2, this.queryResolveBuf, 0);
9024
+ encoder.copyBufferToBuffer(this.queryResolveBuf, 0, this.queryReadbackBuf, 0, 16);
9025
+ }
8925
9026
  this.ctx.device.queue.submit([encoder.finish()]);
9027
+ if (tsTotal) {
9028
+ const rb = this.queryReadbackBuf;
9029
+ const acc = g.__tsTotal;
9030
+ acc.inFlight = true;
9031
+ (async () => {
9032
+ try {
9033
+ await rb.mapAsync(MAP_MODE_READ$1, 0, 16);
9034
+ const ts = new BigUint64Array(rb.getMappedRange(0, 16).slice(0));
9035
+ rb.unmap();
9036
+ acc.ns += Number(ts[1] - ts[0]);
9037
+ acc.steps += 1;
9038
+ } catch {} finally {
9039
+ acc.inFlight = false;
9040
+ }
9041
+ })();
9042
+ }
8926
9043
  this.seqPos += 1;
8927
9044
  }
8928
9045
  /** Read back the token produced by the pipelined step that used `slot`. */
@@ -10013,6 +10130,47 @@ function repackMLX(mlx) {
10013
10130
  zeros
10014
10131
  };
10015
10132
  }
10133
+ /**
10134
+ * Dequantize an MLX affine-quantized tensor of arbitrary bit width to f32.
10135
+ *
10136
+ * MLX packs the unsigned integer codes as a CONTIGUOUS little-endian bitstream
10137
+ * (verified against ml-explore/mlx `extract_bits`): code `i` occupies bits
10138
+ * `[i*bits, i*bits+bits)` of the per-row byte stream, straddling byte/word
10139
+ * boundaries when `bits` does not divide 8 evenly. Power-of-two bit widths
10140
+ * (4/8) divide evenly so codes never straddle; 6-bit packs 4 codes per 3 bytes.
10141
+ *
10142
+ * Dequant (affine mode): `w = scale * q + bias`, with one (scale, bias) pair per
10143
+ * group of `groupSize` codes along K (the last axis), groups laid out row-major.
10144
+ *
10145
+ * The weight buffer is provided as a Uint32Array (how the store holds it); we
10146
+ * read it as a flat little-endian byte view so the bitstream is contiguous
10147
+ * across the whole row regardless of the 4-byte word boundaries.
10148
+ *
10149
+ * Returns a flat Float32Array of shape [N, K] in row-major order — directly
10150
+ * consumable by `quantizeInt4` for repacking into Gerbil's 4-bit kernels.
10151
+ */
10152
+ function dequantizeMLX(mlx) {
10153
+ const { weight, scales: mlxScales, biases: mlxBiases, N, K, groupSize, bits } = mlx;
10154
+ const groupsPerRow = Math.ceil(K / groupSize);
10155
+ const mask = (1 << bits) - 1;
10156
+ const bytes = new Uint8Array(weight.buffer, weight.byteOffset, weight.byteLength);
10157
+ const bytesPerRow = Math.ceil(K * bits / 8);
10158
+ const out = new Float32Array(N * K);
10159
+ for (let row = 0; row < N; row++) {
10160
+ const rowByteBase = row * bytesPerRow;
10161
+ const rowGroupBase = row * groupsPerRow;
10162
+ const rowOutBase = row * K;
10163
+ for (let col = 0; col < K; col++) {
10164
+ const bitPos = col * bits;
10165
+ const byteIdx = rowByteBase + (bitPos >> 3);
10166
+ const bitOff = bitPos & 7;
10167
+ const q = (bytes[byteIdx] | bytes[byteIdx + 1] << 8 | bytes[byteIdx + 2] << 16) >>> bitOff & mask;
10168
+ const g = rowGroupBase + Math.floor(col / groupSize);
10169
+ out[rowOutBase + col] = mlxScales[g] * q + mlxBiases[g];
10170
+ }
10171
+ }
10172
+ return out;
10173
+ }
10016
10174
 
10017
10175
  //#endregion
10018
10176
  //#region src/gpu/opfs-cache.ts
@@ -10479,6 +10637,19 @@ const UNICODE_TO_BYTE = /* @__PURE__ */ new Map();
10479
10637
  }
10480
10638
  /** SentencePiece metaspace marker (U+2581, "▁") used by T5/Unigram tokenizers. */
10481
10639
  const METASPACE = "▁";
10640
+ /**
10641
+ * Matches the Qwen3.5-style empty-think prefill that a chat template emits after
10642
+ * `add_generation_prompt` (e.g. `{{- '<think>\n\n</think>\n\n' }}`). Used to
10643
+ * decide whether to inject the empty think block — LFM2.5 templates reference
10644
+ * `<think>` only to re-render prior assistant `.thinking` content and must NOT
10645
+ * be matched, so we look for the empty `<think></think>` pair specifically.
10646
+ *
10647
+ * The gap between the tags must tolerate BOTH real whitespace and the LITERAL
10648
+ * backslash-escape text (`\n`, `\r`, `\t`) that survives in the raw Jinja string
10649
+ * source — Qwen3.5 ships the prefill as the two-character `\n` sequences, not
10650
+ * actual newlines, so a plain `\s*` would miss it and drop Qwen's think block.
10651
+ */
10652
+ const EMPTY_THINK_PREFILL = /<think>(?:\s|\\[nrt])*<\/think>/;
10482
10653
  var Tokenizer = class Tokenizer {
10483
10654
  vocab;
10484
10655
  vocabReverse;
@@ -10742,7 +10913,7 @@ var Tokenizer = class Tokenizer {
10742
10913
  for (const msg of messages) parts.push(`<|im_start|>${msg.role}\n${msg.content}<|im_end|>\n`);
10743
10914
  if (addGenPrompt) {
10744
10915
  parts.push(`<|im_start|>assistant\n`);
10745
- if (template ? template.includes("<think>") : this.addedTokens.has("<think>")) parts.push(`<think>\n\n</think>\n\n`);
10916
+ if (template ? EMPTY_THINK_PREFILL.test(template) : this.addedTokens.has("<think>")) parts.push(`<think>\n\n</think>\n\n`);
10746
10917
  }
10747
10918
  return parts.join("");
10748
10919
  }
@@ -12322,6 +12493,12 @@ async function loadModel(options) {
12322
12493
  if (isMLX) {
12323
12494
  onProgress?.(96, 100, "Repacking MLX weights...");
12324
12495
  const mlxGS = quantConfig?.group_size ?? 64;
12496
+ const mlxDefaultBits = quantConfig?.bits ?? 4;
12497
+ const bitsForBase = (baseName) => {
12498
+ if (!quantConfig) return mlxDefaultBits;
12499
+ for (const [key, val] of Object.entries(quantConfig)) if (val && typeof val === "object" && typeof val.bits === "number" && (key === baseName || key.endsWith(`.${baseName}`))) return val.bits;
12500
+ return mlxDefaultBits;
12501
+ };
12325
12502
  for (const node of graph.nodes) {
12326
12503
  if (node.opType !== "MatMulInt4" && node.opType !== "EmbeddingInt4") continue;
12327
12504
  let N, K;
@@ -12341,26 +12518,54 @@ async function loadModel(options) {
12341
12518
  const mlxScales = await store.get(`${baseName}.scales`);
12342
12519
  const mlxBiases = await store.get(`${baseName}.biases`);
12343
12520
  if (!mlxWeight || !mlxScales || !mlxBiases) continue;
12344
- const repacked = repackMLX({
12345
- weight: mlxWeight.data instanceof Uint32Array ? mlxWeight.data : new Uint32Array(mlxWeight.data.buffer, mlxWeight.data.byteOffset, mlxWeight.data.byteLength / 4),
12346
- scales: mlxScales.data instanceof Float32Array ? mlxScales.data : new Float32Array(mlxScales.data.buffer, mlxScales.data.byteOffset, mlxScales.data.byteLength / 4),
12347
- biases: mlxBiases.data instanceof Float32Array ? mlxBiases.data : new Float32Array(mlxBiases.data.buffer, mlxBiases.data.byteOffset, mlxBiases.data.byteLength / 4),
12348
- N,
12349
- K,
12350
- groupSize: mlxGS
12351
- });
12352
- await store.set(qTensorName, {
12353
- data: repacked.packed,
12354
- shape: [repacked.packed.length]
12355
- });
12356
- await store.set(scalesTensorName, {
12357
- data: repacked.scales,
12358
- shape: [repacked.scales.length]
12359
- });
12360
- await store.set(zerosTensorName, {
12361
- data: repacked.zeros,
12362
- shape: [repacked.zeros.length]
12363
- });
12521
+ const weightData = mlxWeight.data instanceof Uint32Array ? mlxWeight.data : new Uint32Array(mlxWeight.data.buffer, mlxWeight.data.byteOffset, mlxWeight.data.byteLength / 4);
12522
+ const scalesData = mlxScales.data instanceof Float32Array ? mlxScales.data : new Float32Array(mlxScales.data.buffer, mlxScales.data.byteOffset, mlxScales.data.byteLength / 4);
12523
+ const biasesData = mlxBiases.data instanceof Float32Array ? mlxBiases.data : new Float32Array(mlxBiases.data.buffer, mlxBiases.data.byteOffset, mlxBiases.data.byteLength / 4);
12524
+ const srcBits = bitsForBase(baseName);
12525
+ if (srcBits === 4) {
12526
+ const repacked = repackMLX({
12527
+ weight: weightData,
12528
+ scales: scalesData,
12529
+ biases: biasesData,
12530
+ N,
12531
+ K,
12532
+ groupSize: mlxGS
12533
+ });
12534
+ await store.set(qTensorName, {
12535
+ data: repacked.packed,
12536
+ shape: [repacked.packed.length]
12537
+ });
12538
+ await store.set(scalesTensorName, {
12539
+ data: repacked.scales,
12540
+ shape: [repacked.scales.length]
12541
+ });
12542
+ await store.set(zerosTensorName, {
12543
+ data: repacked.zeros,
12544
+ shape: [repacked.zeros.length]
12545
+ });
12546
+ } else {
12547
+ const { packed, scales, zeros } = quantizeInt4(dequantizeMLX({
12548
+ weight: weightData,
12549
+ scales: scalesData,
12550
+ biases: biasesData,
12551
+ N,
12552
+ K,
12553
+ groupSize: mlxGS,
12554
+ bits: srcBits
12555
+ }), mlxGS);
12556
+ await store.set(qTensorName, {
12557
+ data: packed,
12558
+ shape: [packed.length]
12559
+ });
12560
+ await store.set(scalesTensorName, {
12561
+ data: scales,
12562
+ shape: [scales.length]
12563
+ });
12564
+ await store.set(zerosTensorName, {
12565
+ data: zeros,
12566
+ shape: [zeros.length]
12567
+ });
12568
+ }
12364
12569
  await store.delete(sourceName);
12365
12570
  await store.delete(`${baseName}.scales`);
12366
12571
  await store.delete(`${baseName}.biases`);
@@ -13220,4 +13425,4 @@ function selectGraphWeights(graph, weights) {
13220
13425
 
13221
13426
  //#endregion
13222
13427
  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 };
13223
- //# sourceMappingURL=moonshine-stt-BGmctcdO.mjs.map
13428
+ //# sourceMappingURL=moonshine-stt-bExSnd2o.mjs.map