@thinletterio/vqweb 0.1.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.
package/dist/vqweb.js ADDED
@@ -0,0 +1,4121 @@
1
+ /* @thinletterio/vqweb 0.1.0 — vector-quantised query encoders in the browser on WebGPU. Apache-2.0, https://thinletter.io
2
+ * Bundles: client/vqweb runtime (thinletter, Apache-2.0), @huggingface/tokenizers 0.2.0 (Apache-2.0), a Q2_K decoder ported from llama.cpp gguf-py (MIT). See NOTICE. */
3
+
4
+ // ../../client/vqweb/embed.js
5
+ var F16_TABLE = (() => {
6
+ const t = new Float32Array(65536);
7
+ for (let h = 0; h < 65536; h++) {
8
+ const s = h & 32768 ? -1 : 1, e = h >> 10 & 31, m = h & 1023;
9
+ let v;
10
+ if (e === 0) v = m * 2 ** -24;
11
+ else if (e === 31) v = m ? NaN : Infinity;
12
+ else v = (1 + m / 1024) * 2 ** (e - 15);
13
+ t[h] = s * v;
14
+ }
15
+ return t;
16
+ })();
17
+ function f16ArrayToF32(u16) {
18
+ const out = new Float32Array(u16.length);
19
+ for (let i = 0; i < u16.length; i++) out[i] = F16_TABLE[u16[i]];
20
+ return out;
21
+ }
22
+ var _f32 = new Float32Array(1);
23
+ var _u32 = new Uint32Array(_f32.buffer);
24
+ function f32ToF16(x) {
25
+ _f32[0] = x;
26
+ const b = _u32[0];
27
+ const sign = b >>> 16 & 32768;
28
+ let e = b >>> 23 & 255, m = b & 8388607;
29
+ if (e === 255) return sign | 31744 | (m ? 512 : 0);
30
+ let ne = e - 127 + 15;
31
+ if (ne >= 31) return sign | 31744;
32
+ if (ne <= 0) {
33
+ if (ne < -10) return sign;
34
+ m |= 8388608;
35
+ const shift = 14 - ne;
36
+ let half2 = m >>> shift;
37
+ const rem2 = m & (1 << shift) - 1, halfway = 1 << shift - 1;
38
+ if (rem2 > halfway || rem2 === halfway && half2 & 1) half2++;
39
+ return sign | half2;
40
+ }
41
+ let half = ne << 10 | m >>> 13;
42
+ const rem = m & 8191;
43
+ if (rem > 4096 || rem === 4096 && half & 1) half++;
44
+ return sign | half;
45
+ }
46
+ var Q2K_BLOCK = 256;
47
+ var Q2K_BYTES = 84;
48
+ var fround = Math.fround;
49
+ function decodeQ2KBlock(src, off, out, outOff) {
50
+ const d = F16_TABLE[src[off + 80] | src[off + 81] << 8];
51
+ const dmin = F16_TABLE[src[off + 82] | src[off + 83] << 8];
52
+ for (let sb = 0; sb < 16; sb++) {
53
+ const sc = src[off + sb];
54
+ const dl = fround(d * (sc & 15)), ml = fround(dmin * (sc >> 4));
55
+ const n0 = sb * 16;
56
+ const half = n0 >> 7, shift = n0 >> 5 & 3;
57
+ const qbase = off + 16 + half * 32;
58
+ for (let j = 0; j < 16; j++) {
59
+ const n = n0 + j;
60
+ const q = src[qbase + (n & 31)] >> 2 * shift & 3;
61
+ out[outOff + n] = fround(fround(dl * q) - ml);
62
+ }
63
+ }
64
+ }
65
+ function decodeQ2KRow(src, off, cols, out, outOff) {
66
+ for (let b = 0; b < cols / Q2K_BLOCK; b++) decodeQ2KBlock(src, off + b * Q2K_BYTES, out, outOff + b * Q2K_BLOCK);
67
+ }
68
+ var TokenTable = class {
69
+ /**
70
+ * @param {Uint8Array} q2kBytes raw `token_embd` bytes, [rows, cols/256*84]
71
+ * @param {Uint32Array} ids `token_embd.ids`
72
+ * @param {number} hidden cols (1024)
73
+ */
74
+ constructor(q2kBytes, ids, hidden) {
75
+ this.bytes = q2kBytes;
76
+ this.ids = ids;
77
+ this.hidden = hidden;
78
+ this.rowBytes = hidden / Q2K_BLOCK * Q2K_BYTES;
79
+ if (q2kBytes.length !== ids.length * this.rowBytes) throw new Error(`token table: ${q2kBytes.length} bytes != ${ids.length} rows x ${this.rowBytes}`);
80
+ this.rowOf = /* @__PURE__ */ new Map();
81
+ for (let i = 0; i < ids.length; i++) this.rowOf.set(ids[i], i);
82
+ }
83
+ get rows() {
84
+ return this.ids.length;
85
+ }
86
+ /** decoded row i (stored index) as Float32Array[hidden]. */
87
+ row(i, out = new Float32Array(this.hidden), outOff = 0) {
88
+ decodeQ2KRow(this.bytes, i * this.rowBytes, this.hidden, out, outOff);
89
+ return out;
90
+ }
91
+ /** token ids (already re-tokenised onto the trimmed vocabulary) -> Float32Array [T, hidden]. */
92
+ lookup(tokenIds) {
93
+ const T = tokenIds.length, out = new Float32Array(T * this.hidden);
94
+ for (let t = 0; t < T; t++) {
95
+ const r = this.rowOf.get(tokenIds[t]);
96
+ if (r === void 0) throw new Error(`token id ${tokenIds[t]} is not in the trimmed table (re-tokenise with VqwTokenizer first)`);
97
+ this.row(r, out, t * this.hidden);
98
+ }
99
+ return out;
100
+ }
101
+ /** the same as f16 bits (Uint16Array [T, hidden]) for an f16 residual stream. */
102
+ lookupF16(tokenIds) {
103
+ const f = this.lookup(tokenIds), out = new Uint16Array(f.length);
104
+ for (let i = 0; i < f.length; i++) out[i] = f32ToF16(f[i]);
105
+ return out;
106
+ }
107
+ };
108
+
109
+ // ../../client/vqweb/container.js
110
+ var MAGIC = "VQW1";
111
+ var TYPED = { f16: Uint16Array, f32: Float32Array, u8: Uint8Array, i8: Int8Array, u16: Uint16Array, u32: Uint32Array, i32: Int32Array };
112
+ var LIN = ["q", "k", "v", "o", "gate", "up", "down"];
113
+ var NORMS = ["attn_norm", "ffn_norm", "attn_q_norm", "attn_k_norm"];
114
+ function parseVqw(data) {
115
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
116
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
117
+ if (String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3]) !== MAGIC) throw new Error("not a .vqw file (bad magic)");
118
+ const L = dv.getUint32(8, true);
119
+ const header = JSON.parse(new TextDecoder("utf-8").decode(bytes.subarray(12, 12 + L)));
120
+ const dataStart = 12 + L;
121
+ if (dataStart % 64) throw new Error(`data start ${dataStart} is not 64-byte aligned`);
122
+ const raw = (name) => {
123
+ const t = header.tensors[name];
124
+ if (!t) throw new Error(`tensor ${name} not in container`);
125
+ return bytes.subarray(dataStart + t.offset, dataStart + t.offset + t.nbytes);
126
+ };
127
+ const view = (name) => {
128
+ const t = header.tensors[name], r = raw(name);
129
+ if (t.dtype === "q2_k" || t.dtype === "u8") return r;
130
+ const C = TYPED[t.dtype];
131
+ if (!C) throw new Error(`unknown dtype ${t.dtype} of ${name}`);
132
+ if (r.byteOffset % C.BYTES_PER_ELEMENT !== 0) return new C(r.slice().buffer);
133
+ return new C(r.buffer, r.byteOffset, r.byteLength / C.BYTES_PER_ELEMENT);
134
+ };
135
+ return { header, dataStart, bytes, raw, view };
136
+ }
137
+ async function fetchVqw(url, onProgress) {
138
+ const r = await fetch(url);
139
+ if (!r.ok) throw new Error(`fetch ${url}: ${r.status}`);
140
+ const total = Number(r.headers.get("Content-Length") || 0);
141
+ if (!r.body || !onProgress) return await r.arrayBuffer();
142
+ const reader = r.body.getReader();
143
+ const chunks = [];
144
+ let loaded = 0;
145
+ for (; ; ) {
146
+ const { done, value } = await reader.read();
147
+ if (done) break;
148
+ chunks.push(value);
149
+ loaded += value.byteLength;
150
+ onProgress(loaded, total);
151
+ }
152
+ const out = new Uint8Array(loaded);
153
+ let o = 0;
154
+ for (const c of chunks) {
155
+ out.set(c, o);
156
+ o += c.byteLength;
157
+ }
158
+ return out.buffer;
159
+ }
160
+ var align4 = (n) => n + 3 & ~3;
161
+ var VqwContainer = class _VqwContainer {
162
+ /**
163
+ * @param {ReturnType<parseVqw>} parsed
164
+ * @param {GPUDevice} device
165
+ */
166
+ constructor(parsed, device) {
167
+ this.parsed = parsed;
168
+ this.header = parsed.header;
169
+ this.device = device;
170
+ const m = this.header.model;
171
+ this.model = {
172
+ arch: m.arch,
173
+ hidden: m.hidden,
174
+ layers: m.layers,
175
+ heads: m.heads,
176
+ kvHeads: m.kv_heads,
177
+ headDim: m.head_dim,
178
+ intermediate: m.intermediate,
179
+ ropeTheta: m.rope_theta,
180
+ rmsEps: m.rms_eps,
181
+ pooling: m.pooling,
182
+ maxLen: m.max_len ?? 512,
183
+ prompt: m.prompt ?? "",
184
+ addEos: !!m.add_eos,
185
+ eosId: m.eosId ?? m.eos_id ?? null,
186
+ teacher: m.teacher,
187
+ truncated: !!m.truncated
188
+ };
189
+ this.rotation = { rounds: this.header.rotation.rounds, block: this.header.rotation.block, sizes: this.header.rotation.sizes, side: this.header.rotation.side ?? "in" };
190
+ this.quant = this.header.quant;
191
+ this.gpu = /* @__PURE__ */ new Map();
192
+ this.gpuBytes = 0;
193
+ this.tokens = new TokenTable(parsed.view("token_embd"), parsed.view("token_embd.ids"), this.model.hidden);
194
+ this._upload();
195
+ }
196
+ static async load(url, device, { onProgress } = {}) {
197
+ const buf = await fetchVqw(url, onProgress);
198
+ return new _VqwContainer(parseVqw(buf), device);
199
+ }
200
+ _buffer(name, bytes, usage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST) {
201
+ const size = align4(bytes.byteLength);
202
+ const b = this.device.createBuffer({ label: name, size, usage, mappedAtCreation: true });
203
+ new Uint8Array(b.getMappedRange()).set(bytes);
204
+ b.unmap();
205
+ this.gpu.set(name, b);
206
+ this.gpuBytes += size;
207
+ return b;
208
+ }
209
+ _upload() {
210
+ const p = this.parsed, T = this.header.tensors;
211
+ for (let li = 0; li < this.model.layers; li++) {
212
+ for (const nm of LIN) for (const part of ["codebook", "idx", "scale"]) {
213
+ const n = `blk.${li}.${nm}.${part}`;
214
+ this._buffer(n, p.raw(n));
215
+ }
216
+ for (const nm of NORMS) {
217
+ const n = `blk.${li}.${nm}`;
218
+ this._buffer(n, p.raw(n));
219
+ }
220
+ }
221
+ this._buffer("output_norm", p.raw("output_norm"));
222
+ this.prefix = null;
223
+ if (T["prefix.ids"]) {
224
+ const ids = Array.from(p.view("prefix.ids"));
225
+ const P = ids.length, KD = this.model.kvHeads * this.model.headDim, L = this.model.layers;
226
+ const k16 = p.view("prefix.k"), v16 = p.view("prefix.v");
227
+ if (k16.length !== L * P * KD || v16.length !== L * P * KD) throw new Error(`prefix.k/v size ${k16.length} != ${L}x${P}x${KD}`);
228
+ const k32 = f16ArrayToF32(k16), v32 = f16ArrayToF32(v16);
229
+ for (let li = 0; li < L; li++) {
230
+ this._buffer(`prefix.k.${li}`, new Uint8Array(k32.buffer, li * P * KD * 4, P * KD * 4));
231
+ this._buffer(`prefix.v.${li}`, new Uint8Array(v32.buffer, li * P * KD * 4, P * KD * 4));
232
+ }
233
+ this.prefix = { ids, tokens: P, text: this.header.prefix?.text ?? "" };
234
+ }
235
+ for (const n of this.rotation.sizes) {
236
+ const s = p.view(`rot.${n}.signs`), q = p.view(`rot.${n}.perm`);
237
+ this._buffer(`rot.${n}.signs`, new Uint8Array(Float32Array.from(s).buffer));
238
+ this._buffer(`rot.${n}.perm`, new Uint8Array(Uint32Array.from(q).buffer));
239
+ const blk = T[`rot.${n}.signs`].block ?? this.rotation.block;
240
+ if (blk !== this.rotation.block) throw new Error(`rotation block ${blk} != header block ${this.rotation.block}`);
241
+ }
242
+ }
243
+ /** Metadata + buffers of one linear (`blk.3.gate`). */
244
+ linear(prefix) {
245
+ const meta = this.header.tensors[`${prefix}.idx`];
246
+ const cb = this.header.tensors[`${prefix}.codebook`];
247
+ const bits = meta.bits_per_index, k = meta.k ?? cb.shape[1];
248
+ if (1 << bits !== k) throw new Error(`${prefix}: ${bits}-bit indices but K=${k}`);
249
+ const dim = meta.dim ?? 4;
250
+ if (![2, 4].includes(dim) || (meta.blocksize ?? 256) !== 256) throw new Error(`${prefix}: dim ${meta.dim} / blocksize ${meta.blocksize} unsupported (kernel: dim 2 or 4, blocksize 256)`);
251
+ if (dim === 2 && bits > 12) throw new Error(`${prefix}: dim 2 with ${bits}-bit indices unsupported (two indices must fit 24 bits)`);
252
+ return {
253
+ prefix,
254
+ rows: meta.rows,
255
+ cols: meta.cols,
256
+ groups: meta.groups,
257
+ words: meta.shape[1],
258
+ k,
259
+ bits,
260
+ dim,
261
+ nblk: cb.shape[0],
262
+ codebook: this.gpu.get(`${prefix}.codebook`),
263
+ idx: this.gpu.get(`${prefix}.idx`),
264
+ scale: this.gpu.get(`${prefix}.scale`)
265
+ };
266
+ }
267
+ norm(name) {
268
+ const b = this.gpu.get(name);
269
+ if (!b) throw new Error(`norm ${name} missing`);
270
+ return b;
271
+ }
272
+ /** Stored prefix K / V buffers of block li (M6), or null when the container has no prefix. */
273
+ prefixKV(li) {
274
+ return this.prefix ? { k: this.gpu.get(`prefix.k.${li}`), v: this.gpu.get(`prefix.v.${li}`) } : null;
275
+ }
276
+ rot(n) {
277
+ const signs = this.gpu.get(`rot.${n}.signs`), perms = this.gpu.get(`rot.${n}.perm`);
278
+ if (!signs) throw new Error(`no rotation of size ${n} in the container`);
279
+ return { n, rounds: this.rotation.rounds, block: this.rotation.block, signs, perms };
280
+ }
281
+ /** Forget the file's ArrayBuffer after the upload: the token table (the only CPU-side tensor) is copied out first, so
282
+ * a caller that holds no other reference lets the ~100 MiB file be garbage-collected (memory after load, spec section 1). */
283
+ release() {
284
+ if (!this.parsed) return;
285
+ const p = this.parsed;
286
+ this.tokens = new TokenTable(p.view("token_embd").slice(), Uint32Array.from(p.view("token_embd.ids")), this.model.hidden);
287
+ this.parsed = null;
288
+ }
289
+ destroy() {
290
+ for (const b of this.gpu.values()) b.destroy();
291
+ this.gpu.clear();
292
+ }
293
+ };
294
+
295
+ // ../../client/vqweb/runtime.js
296
+ var KERNEL_FILES = ["rmsnorm", "fwht_rotate", "vq_matmul", "vq_matmul_v1", "qk_norm_rope", "attention", "swiglu", "pool_normalize"];
297
+ var U = "uniform";
298
+ var RO = "read-only-storage";
299
+ var RW = "storage";
300
+ async function requestDevice({ powerPreference = "low-power", label = "vqweb" } = {}) {
301
+ if (!navigator.gpu) throw new Error("WebGPU is not available (navigator.gpu missing)");
302
+ const adapter = await navigator.gpu.requestAdapter({ powerPreference });
303
+ if (!adapter) throw new Error("no WebGPU adapter");
304
+ if (!adapter.features.has("shader-f16")) throw new Error("adapter has no shader-f16");
305
+ const info = {
306
+ vendor: adapter.info?.vendor,
307
+ architecture: adapter.info?.architecture,
308
+ device: adapter.info?.device,
309
+ description: adapter.info?.description,
310
+ shader_f16: true,
311
+ subgroups: adapter.features.has("subgroups"),
312
+ timestamp_query: adapter.features.has("timestamp-query"),
313
+ maxComputeWorkgroupStorageSize: adapter.limits.maxComputeWorkgroupStorageSize,
314
+ maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
315
+ maxBufferSize: adapter.limits.maxBufferSize,
316
+ maxComputeInvocationsPerWorkgroup: adapter.limits.maxComputeInvocationsPerWorkgroup
317
+ };
318
+ const requiredFeatures = ["shader-f16"];
319
+ if (adapter.features.has("timestamp-query")) requiredFeatures.push("timestamp-query");
320
+ const requiredLimits = {};
321
+ if (adapter.limits.maxComputeWorkgroupStorageSize > 16384) requiredLimits.maxComputeWorkgroupStorageSize = adapter.limits.maxComputeWorkgroupStorageSize;
322
+ const device = await adapter.requestDevice({ label, requiredFeatures, requiredLimits });
323
+ return { adapter, device, info };
324
+ }
325
+ async function loadKernelSources(baseUrl = new URL("./kernels/", import.meta.url)) {
326
+ const out = {};
327
+ await Promise.all(KERNEL_FILES.map(async (n) => {
328
+ const r = await fetch(new URL(`${n}.wgsl`, baseUrl));
329
+ if (!r.ok) throw new Error(`kernel ${n}.wgsl: ${r.status}`);
330
+ out[n] = await r.text();
331
+ }));
332
+ return out;
333
+ }
334
+ function fill(src, vars) {
335
+ return src.replace(/\{\{(\w+)\}\}/g, (m, k) => {
336
+ if (!(k in vars)) throw new Error(`template variable ${k} missing`);
337
+ return String(vars[k]);
338
+ });
339
+ }
340
+ var f32lit = (x) => {
341
+ const s = String(x);
342
+ return /[.eE]/.test(s) ? s : s + ".0";
343
+ };
344
+ var VqwRuntime = class _VqwRuntime {
345
+ /**
346
+ * @param {import('./container.js').VqwContainer} container
347
+ * @param {GPUDevice} device
348
+ * @param {object} opts resid: 'f32' | 'f16'; tmax: max tokens (default model.max_len); tile: vq_matmul tile
349
+ * {rwg, tt, cb, mr, mt} (M4 kernel, default 64,32,64,4,4) or {kernel: 'v1', rwg, lanes, tpt} (M2 kernel);
350
+ * sources: kernel sources (default: fetched next to this module)
351
+ */
352
+ constructor(container, device, opts = {}) {
353
+ this.c = container;
354
+ this.device = device;
355
+ this.m = container.model;
356
+ this.resid = opts.resid ?? "f32";
357
+ this.tmax = opts.tmax ?? this.m.maxLen;
358
+ this.P = (opts.usePrefix ?? true) && container.prefix ? container.prefix.tokens : 0;
359
+ this.prefixIds = this.P ? container.prefix.ids : null;
360
+ const t = opts.tile || {};
361
+ this.tile = t.kernel === "v1" ? Object.assign({ kernel: "v1", rwg: 32, lanes: 8, tpt: 2 }, t) : Object.assign({ kernel: "v2", rwg: 64, tt: 32, cb: 64, mr: 4, mt: 4 }, t);
362
+ if (this.tile.kernel === "v2") {
363
+ const { rwg, tt, cb, mr, mt } = this.tile;
364
+ const wgs = rwg / mr * (tt / mt), gs = cb / 4;
365
+ if (256 % cb || cb % 4 || rwg % mr || tt % mt || wgs > 1024 || wgs % rwg || gs % (wgs / rwg)) throw new Error(`bad vq_matmul tile ${JSON.stringify(this.tile)}`);
366
+ }
367
+ this.sources = opts.sources || null;
368
+ this.pipelines = /* @__PURE__ */ new Map();
369
+ this.layouts = /* @__PURE__ */ new Map();
370
+ this.dispatchesPerBlock = 17;
371
+ if (this.m.headDim % 2 || this.m.heads % this.m.kvHeads) throw new Error("unsupported head configuration");
372
+ }
373
+ static async create(container, device, opts = {}) {
374
+ const rt = new _VqwRuntime(container, device, opts);
375
+ if (!rt.sources) rt.sources = await loadKernelSources(opts.kernelBase);
376
+ rt._buildPipelines();
377
+ rt._allocate();
378
+ rt._bindAll();
379
+ await device.queue.onSubmittedWorkDone();
380
+ return rt;
381
+ }
382
+ // ---------------------------------------------------------------------------------------------- pipelines
383
+ _layout(key, kinds) {
384
+ let l = this.layouts.get(key);
385
+ if (!l) {
386
+ l = this.device.createBindGroupLayout({ label: key, entries: kinds.map((type, binding) => ({ binding, visibility: GPUShaderStage.COMPUTE, buffer: { type } })) });
387
+ this.layouts.set(key, l);
388
+ }
389
+ return l;
390
+ }
391
+ _pipeline(key, file, vars, kinds) {
392
+ let p = this.pipelines.get(key);
393
+ if (p) return p;
394
+ const code = fill(this.sources[file], vars);
395
+ const module = this.device.createShaderModule({ label: key, code });
396
+ const layout = this._layout(kinds.join(","), kinds);
397
+ p = { pipeline: this.device.createComputePipeline({ label: key, layout: this.device.createPipelineLayout({ bindGroupLayouts: [layout] }), compute: { module, entryPoint: "main" } }), layout, key, module };
398
+ this.pipelines.set(key, p);
399
+ return p;
400
+ }
401
+ _buildPipelines() {
402
+ const m = this.m, R = this.resid;
403
+ this.pRms = this._pipeline(`rmsnorm/${R}`, "rmsnorm", { N: m.hidden, EPS: f32lit(m.rmsEps), IN_T: R }, [U, RO, RO, RW]);
404
+ this.pRot = {};
405
+ for (const n of this.c.rotation.sizes) {
406
+ const B = this.c.rotation.block;
407
+ this.pRot[n] = this._pipeline(`fwht/${n}`, "fwht_rotate", { N: n, B, R: this.c.rotation.rounds, INV_SQRT_B: f32lit(1 / Math.sqrt(B)) }, [U, RO, RO, RO, RW]);
408
+ }
409
+ this.pQk = this._qkPipeline(true, true);
410
+ this.pAttn = this._attnPipeline(false);
411
+ this.pSwiglu = this._pipeline("swiglu", "swiglu", { I: m.intermediate }, [U, RO, RO, RW]);
412
+ this.pPool = this._pipeline("pool", "pool_normalize", { N: m.hidden }, [U, RO, RW]);
413
+ }
414
+ _matmulPipeline(lin, add) {
415
+ const { rows, cols, k, bits, words } = lin;
416
+ const dim = lin.dim || 4;
417
+ const t = this.tile;
418
+ const OUT_T = add ? this.resid : "f32";
419
+ const CBT = dim === 4 ? "vec4<f16>" : "vec2<f16>";
420
+ const DECODE = dim === 4 ? "ws[(g * MR + di) * NTR + dtr] = vec4<f32>(cbs[v & MASK]);" : "let hi = select(0u, iw[min(base + wi + 1u, RWG * WPB - 1u)], sh > 0u) << ((32u - sh) & 31u); let v2 = (iw[base + wi] >> sh) | hi; let i0 = v2 & MASK; let i1 = (v2 >> W) & MASK; ws[(g * MR + di) * NTR + dtr] = vec4<f32>(vec4<f16>(cbs[i0], cbs[i1]));";
421
+ const common = { ROWS: rows, COLS: cols, K: k, W: bits, MASK: (1 << bits) - 1, WORDS: words, OUT_T, DIM: dim, CBT, DECODE };
422
+ if (t.kernel === "v1") {
423
+ if (dim !== 4) throw new Error("vq_matmul v1 kernel supports dim 4 only");
424
+ const key2 = `vq_matmul/${rows}x${cols}/k${k}/${add ? "add-" + this.resid : "store"}/v1-${t.rwg}x${t.lanes}x${t.tpt}`;
425
+ const OUT_STMT2 = add ? `y[t * ROWS + r] = ${OUT_T}(f32(y[t * ROWS + r]) + acc[k]);` : "y[t * ROWS + r] = acc[k];";
426
+ return this._pipeline(key2, "vq_matmul_v1", { ...common, RWG: t.rwg, LANES: t.lanes, TPT: t.tpt, WGS: t.rwg * t.lanes, OUT_STMT: OUT_STMT2 }, [U, RO, RO, RO, RO, RW]);
427
+ }
428
+ if (rows % t.rwg) throw new Error(`vq_matmul: ${rows} rows not a multiple of rwg=${t.rwg}`);
429
+ const key = `vq_matmul/${rows}x${cols}/k${k}/d${dim}/${add ? "add-" + this.resid : "store"}/${t.rwg}x${t.tt}x${t.cb}x${t.mr}x${t.mt}`;
430
+ const OUT_STMT = add ? `y[t * ROWS + r] = ${OUT_T}(f32(y[t * ROWS + r]) + acc[i][j]);` : "y[t * ROWS + r] = acc[i][j];";
431
+ return this._pipeline(key, "vq_matmul", { ...common, RWG: t.rwg, TT: t.tt, CB: t.cb, MR: t.mr, MT: t.mt, WGS: t.rwg / t.mr * (t.tt / t.mt), OUT_STMT }, [U, RO, RO, RO, RO, RW]);
432
+ }
433
+ _qkPipeline(rope, inplace) {
434
+ const m = this.m;
435
+ const vars = { NH: m.heads, NKV: m.kvHeads, HD: m.headDim, HALF: m.headDim / 2, EPS: f32lit(m.rmsEps), DO_ROPE: rope ? "true" : "false" };
436
+ const kinds = [U, RW, RW, RO, RO, RO, RO];
437
+ if (inplace) {
438
+ vars.OUT_BINDINGS = "";
439
+ vars.OUT_STMT = "if (isq) { q[base + i] = an; q[base + i + HALF] = bn; } else { k[base + i] = an; k[base + i + HALF] = bn; }";
440
+ } else {
441
+ vars.OUT_BINDINGS = "@group(0) @binding(7) var<storage, read_write> qo: array<f32>;\n@group(0) @binding(8) var<storage, read_write> ko: array<f32>;";
442
+ vars.OUT_STMT = "if (isq) { qo[base + i] = an; qo[base + i + HALF] = bn; } else { ko[base + i] = an; ko[base + i + HALF] = bn; }";
443
+ kinds.push(RW, RW);
444
+ }
445
+ return this._pipeline(`qk/${rope ? "rope" : "norm"}/${inplace ? "inplace" : "out"}`, "qk_norm_rope", vars, kinds);
446
+ }
447
+ _attnPipeline(probs) {
448
+ const m = this.m;
449
+ const vars = {
450
+ NH: m.heads,
451
+ NKV: m.kvHeads,
452
+ HD: m.headDim,
453
+ PMAX: Math.max(this.P, 1),
454
+ TMAX: this.P + this.tmax,
455
+ SCALE: f32lit(Math.pow(m.headDim, -0.5)),
456
+ WRITE_PROBS: probs ? "true" : "false",
457
+ PROBS_BINDING: probs ? "@group(0) @binding(7) var<storage, read_write> probs: array<f32>;" : "",
458
+ PROBS_STMT: probs ? "probs[(h * T + t) * W + j] = select(0.0, sc[j] * inv, j <= last);" : ""
459
+ };
460
+ const kinds = [U, RO, RO, RO, RW, RO, RO];
461
+ if (probs) kinds.push(RW);
462
+ return this._pipeline(`attention/${probs ? "probs" : "plain"}/P${this.P}`, "attention", vars, kinds);
463
+ }
464
+ // ---------------------------------------------------------------------------------------------- buffers
465
+ _buf(label, bytes, extra = 0) {
466
+ const b = this.device.createBuffer({ label, size: Math.max(16, bytes + 3 & ~3), usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | extra });
467
+ this.actBytes += b.size;
468
+ return b;
469
+ }
470
+ _allocate() {
471
+ const m = this.m, T = this.tmax, rb = this.resid === "f16" ? 2 : 4;
472
+ this.actBytes = 0;
473
+ const H = m.hidden, QD = m.heads * m.headDim, KD = m.kvHeads * m.headDim, I = m.intermediate;
474
+ this.b = {
475
+ uni: this.device.createBuffer({ label: "params", size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }),
476
+ x: this._buf("x (residual)", T * H * rb),
477
+ h: this._buf("h (norm out)", T * H * 4),
478
+ hr: this._buf("hr (rot, f16)", T * H * 2),
479
+ q: this._buf("q", T * QD * 4),
480
+ k: this._buf("k", T * KD * 4),
481
+ v: this._buf("v", T * KD * 4),
482
+ attn: this._buf("attn_out", T * QD * 4),
483
+ orot: this._buf("o_rot (f16)", T * QD * 2),
484
+ g: this._buf("gate", T * I * 4),
485
+ u: this._buf("up", T * I * 4),
486
+ a: this._buf("swiglu", T * I * 4),
487
+ ar: this._buf("down_rot (f16)", T * I * 2),
488
+ emb: this._buf("embedding", H * 4),
489
+ cos: this._buf("rope cos", T * (m.headDim / 2) * 4),
490
+ sin: this._buf("rope sin", T * (m.headDim / 2) * 4),
491
+ stage: this.device.createBuffer({ label: "readback", size: H * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST })
492
+ };
493
+ if (!this.P) {
494
+ this.b.kp0 = this._buf("prefix k (none)", 16);
495
+ this.b.vp0 = this._buf("prefix v (none)", 16);
496
+ }
497
+ this.ropeRows = 0;
498
+ this.ropeOffset = -1;
499
+ }
500
+ _bg(p, buffers, label) {
501
+ return this.device.createBindGroup({ label, layout: p.layout, entries: buffers.map((buffer, binding) => ({ binding, resource: { buffer } })) });
502
+ }
503
+ /** [prefix K, prefix V] buffers of block li for the attention bind group (dummies when the runtime uses no prefix). */
504
+ _prefixPair(li) {
505
+ if (!this.P) return [this.b.kp0, this.b.vp0];
506
+ const pk = this.c.prefixKV(li);
507
+ return [pk.k, pk.v];
508
+ }
509
+ _bindAll() {
510
+ const c = this.c, b = this.b, m = this.m;
511
+ const rotBG = (n, src, dst) => {
512
+ const r = c.rot(n);
513
+ return this._bg(this.pRot[n], [b.uni, src, r.signs, r.perms, dst], `rot${n}`);
514
+ };
515
+ this.blocks = [];
516
+ for (let li = 0; li < m.layers; li++) {
517
+ const lin = (nm) => c.linear(`blk.${li}.${nm}`);
518
+ const mm = (nm, src, dst, add) => {
519
+ const l = lin(nm);
520
+ const p = this._matmulPipeline(l, add);
521
+ return { p, bg: this._bg(p, [b.uni, src, l.codebook, l.idx, l.scale, dst], `blk.${li}.${nm}`), rows: l.rows };
522
+ };
523
+ const blk = {
524
+ attnNorm: this._bg(this.pRms, [b.uni, b.x, c.norm(`blk.${li}.attn_norm`), b.h], `blk.${li}.attn_norm`),
525
+ rot1024: rotBG(m.hidden, b.h, b.hr),
526
+ q: mm("q", b.hr, b.q, false),
527
+ k: mm("k", b.hr, b.k, false),
528
+ v: mm("v", b.hr, b.v, false),
529
+ qk: this._bg(this.pQk, [b.uni, b.q, b.k, c.norm(`blk.${li}.attn_q_norm`), c.norm(`blk.${li}.attn_k_norm`), b.cos, b.sin], `blk.${li}.qk`),
530
+ attn: this._bg(this.pAttn, [b.uni, b.q, b.k, b.v, b.attn, ...this._prefixPair(li)], `blk.${li}.attn`),
531
+ rot2048: rotBG(m.heads * m.headDim, b.attn, b.orot),
532
+ o: mm("o", b.orot, b.x, true),
533
+ ffnNorm: this._bg(this.pRms, [b.uni, b.x, c.norm(`blk.${li}.ffn_norm`), b.h], `blk.${li}.ffn_norm`),
534
+ gate: mm("gate", b.hr, b.g, false),
535
+ up: mm("up", b.hr, b.u, false),
536
+ swiglu: this._bg(this.pSwiglu, [b.uni, b.g, b.u, b.a], `blk.${li}.swiglu`),
537
+ rot3072: rotBG(m.intermediate, b.a, b.ar),
538
+ down: mm("down", b.ar, b.x, true),
539
+ li
540
+ };
541
+ this.blocks.push(blk);
542
+ }
543
+ this.outNorm = this._bg(this.pRms, [b.uni, b.x, c.norm("output_norm"), b.h], "output_norm");
544
+ this.pool = this._bg(this.pPool, [b.uni, b.h, b.emb], "pool");
545
+ }
546
+ _debugSetup() {
547
+ if (this.dbg) return;
548
+ const m = this.m, T = this.tmax, b = this.b;
549
+ const qn = this._buf("q_norm (debug)", T * m.heads * m.headDim * 4), kn = this._buf("k_norm (debug)", T * m.kvHeads * m.headDim * 4);
550
+ const probs = this._buf("attn_probs (debug)", m.heads * T * (this.P + T) * 4);
551
+ const pQkNorm = this._qkPipeline(false, false), pAttnP = this._attnPipeline(true);
552
+ this.dbg = { qn, kn, probs, pQkNorm, pAttnP, blocks: this.blocks.map((blk) => ({
553
+ qkNorm: this._bg(pQkNorm, [b.uni, b.q, b.k, this.c.norm(`blk.${blk.li}.attn_q_norm`), this.c.norm(`blk.${blk.li}.attn_k_norm`), b.cos, b.sin, qn, kn], `dbg qk ${blk.li}`),
554
+ attn: this._bg(pAttnP, [b.uni, b.q, b.k, b.v, b.attn, ...this._prefixPair(blk.li), probs], `dbg attn ${blk.li}`)
555
+ })) };
556
+ }
557
+ // ---------------------------------------------------------------------------------------------- per query
558
+ /** RoPE cos/sin rows 0..T-1 as f32, mimicking the reference's fp32 evaluation order (inv_freq, t*inv_freq, cos) with Math.fround. */
559
+ _ropeTable(T, offset = 0) {
560
+ if (T <= this.ropeRows && offset === this.ropeOffset) return;
561
+ const m = this.m, half = m.headDim / 2, fr = Math.fround;
562
+ const cos = new Float32Array(T * half), sin = new Float32Array(T * half);
563
+ const inv = new Float32Array(half);
564
+ for (let j = 0; j < half; j++) inv[j] = fr(1 / fr(Math.pow(m.ropeTheta, fr(2 * j / m.headDim))));
565
+ for (let t = 0; t < T; t++) for (let j = 0; j < half; j++) {
566
+ const a = fr((offset + t) * inv[j]);
567
+ cos[t * half + j] = Math.cos(a);
568
+ sin[t * half + j] = Math.sin(a);
569
+ }
570
+ this.device.queue.writeBuffer(this.b.cos, 0, cos);
571
+ this.device.queue.writeBuffer(this.b.sin, 0, sin);
572
+ this.ropeRows = T;
573
+ this.ropeOffset = offset;
574
+ }
575
+ /** M6: ids that start with the stored prefix are cut; returns {ids: query tokens, PP: prefix length used (0 = plain path)}. */
576
+ _splitPrefix(ids) {
577
+ const pre = this.prefixIds;
578
+ if (!pre || ids.length <= pre.length) return { ids, PP: 0 };
579
+ for (let i = 0; i < pre.length; i++) if (ids[i] !== pre[i]) return { ids, PP: 0 };
580
+ return { ids: ids.slice(pre.length), PP: pre.length };
581
+ }
582
+ /**
583
+ * Embed one token-id sequence (already re-tokenised onto the trimmed table).
584
+ * @returns {Promise<{embedding: Float32Array, ms: number, T: number, captures?: Map<string, {data: Float32Array, shape: number[]}>}>}
585
+ */
586
+ async embed(idsIn, { debug = false, profile = false } = {}) {
587
+ const t0 = performance.now();
588
+ const { ids, PP } = this._splitPrefix(idsIn);
589
+ const T = ids.length, m = this.m, b = this.b, dev = this.device;
590
+ if (T < 1 || T > this.tmax) throw new Error(`T=${T} outside 1..${this.tmax}`);
591
+ profile = profile && dev.features.has("timestamp-query");
592
+ const nDispatch = m.layers * (this.dispatchesPerBlock + (debug ? 2 : 0)) + 2;
593
+ const qset = profile ? dev.createQuerySet({ type: "timestamp", count: 2 * nDispatch }) : null;
594
+ const kinds = [];
595
+ const x0 = this.resid === "f16" ? this.c.tokens.lookupF16(ids) : this.c.tokens.lookup(ids);
596
+ dev.queue.writeBuffer(b.x, 0, x0);
597
+ this._ropeTable(T, PP);
598
+ dev.queue.writeBuffer(b.uni, 0, new Uint32Array([T, debug ? 1 : 0, PP, 0]));
599
+ if (debug) this._debugSetup();
600
+ const H = m.hidden, QD = m.heads * m.headDim, KD = m.kvHeads * m.headDim, I = m.intermediate;
601
+ const enc = dev.createCommandEncoder({ label: "query" });
602
+ const caps = [];
603
+ let pass = null;
604
+ const capture = (name, buffer, dtype, shape) => {
605
+ if (!debug) return;
606
+ const n = shape.reduce((p, q) => p * q, 1), bytes = n * (dtype === "f16" ? 2 : 4);
607
+ const stage = dev.createBuffer({ label: `capture ${name}`, size: Math.max(16, bytes + 3 & ~3), usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
608
+ enc.copyBufferToBuffer(buffer, 0, stage, 0, bytes + 3 & ~3);
609
+ caps.push({ name, dtype, shape, bytes, stage });
610
+ };
611
+ const run = (p, bg, gx, gy = 1) => {
612
+ if (!pass) {
613
+ const i = kinds.length;
614
+ pass = enc.beginComputePass(profile ? { timestampWrites: { querySet: qset, beginningOfPassWriteIndex: 2 * i, endOfPassWriteIndex: 2 * i + 1 } } : {});
615
+ }
616
+ kinds.push(p.key.split("/")[0]);
617
+ pass.setPipeline(p.pipeline);
618
+ pass.setBindGroup(0, bg);
619
+ pass.dispatchWorkgroups(gx, gy);
620
+ if (debug || profile) {
621
+ pass.end();
622
+ pass = null;
623
+ }
624
+ };
625
+ const tokTile = this.tile.kernel === "v1" ? this.tile.lanes * this.tile.tpt : this.tile.tt;
626
+ const mm = (op) => run(op.p, op.bg, op.rows / this.tile.rwg, Math.ceil(T / tokTile));
627
+ const rT = this.resid === "f16" ? "f16" : "f32";
628
+ capture("embed", b.x, rT, [T, H]);
629
+ for (const blk of this.blocks) {
630
+ const pre = `blk.${blk.li}.`;
631
+ run(this.pRms, blk.attnNorm, T);
632
+ capture(pre + "attn_norm", b.h, "f32", [T, H]);
633
+ run(this.pRot[H], blk.rot1024, T);
634
+ capture(pre + "attn_rot", b.hr, "f16", [T, H]);
635
+ mm(blk.q);
636
+ capture(pre + "q", b.q, "f32", [T, QD]);
637
+ mm(blk.k);
638
+ capture(pre + "k", b.k, "f32", [T, KD]);
639
+ mm(blk.v);
640
+ capture(pre + "v", b.v, "f32", [T, KD]);
641
+ if (debug) {
642
+ run(this.dbg.pQkNorm, this.dbg.blocks[blk.li].qkNorm, T, m.heads + m.kvHeads);
643
+ capture(pre + "q_norm", this.dbg.qn, "f32", [T, m.heads, m.headDim]);
644
+ capture(pre + "k_norm", this.dbg.kn, "f32", [T, m.kvHeads, m.headDim]);
645
+ }
646
+ run(this.pQk, blk.qk, T, m.heads + m.kvHeads);
647
+ capture(pre + "q_rope", b.q, "f32", [T, m.heads, m.headDim]);
648
+ capture(pre + "k_rope", b.k, "f32", [T, m.kvHeads, m.headDim]);
649
+ if (debug) {
650
+ run(this.dbg.pAttnP, this.dbg.blocks[blk.li].attn, T, m.heads);
651
+ capture(pre + "attn_probs", this.dbg.probs, "f32", [m.heads, T, PP + T]);
652
+ } else run(this.pAttn, blk.attn, T, m.heads);
653
+ capture(pre + "attn_out", b.attn, "f32", [T, QD]);
654
+ run(this.pRot[QD], blk.rot2048, T);
655
+ capture(pre + "o_rot", b.orot, "f16", [T, QD]);
656
+ mm(blk.o);
657
+ capture(pre + "resid_attn", b.x, rT, [T, H]);
658
+ run(this.pRms, blk.ffnNorm, T);
659
+ capture(pre + "ffn_norm", b.h, "f32", [T, H]);
660
+ run(this.pRot[H], blk.rot1024, T);
661
+ capture(pre + "ffn_rot", b.hr, "f16", [T, H]);
662
+ mm(blk.gate);
663
+ capture(pre + "gate", b.g, "f32", [T, I]);
664
+ mm(blk.up);
665
+ capture(pre + "up", b.u, "f32", [T, I]);
666
+ run(this.pSwiglu, blk.swiglu, Math.ceil(T * I / 256));
667
+ capture(pre + "swiglu", b.a, "f32", [T, I]);
668
+ run(this.pRot[I], blk.rot3072, T);
669
+ capture(pre + "down_rot", b.ar, "f16", [T, I]);
670
+ mm(blk.down);
671
+ capture(pre + "resid_ffn", b.x, rT, [T, H]);
672
+ }
673
+ run(this.pRms, this.outNorm, T);
674
+ capture("output_norm", b.h, "f32", [T, H]);
675
+ run(this.pPool, this.pool, 1);
676
+ capture("embedding", b.emb, "f32", [H]);
677
+ if (pass) {
678
+ pass.end();
679
+ pass = null;
680
+ }
681
+ enc.copyBufferToBuffer(b.emb, 0, b.stage, 0, H * 4);
682
+ let qbuf = null, qstage = null;
683
+ if (profile) {
684
+ qbuf = dev.createBuffer({ label: "timestamps", size: 16 * nDispatch, usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC });
685
+ qstage = dev.createBuffer({ label: "timestamps readback", size: 16 * nDispatch, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
686
+ enc.resolveQuerySet(qset, 0, 2 * kinds.length, qbuf, 0);
687
+ enc.copyBufferToBuffer(qbuf, 0, qstage, 0, 16 * kinds.length);
688
+ }
689
+ dev.queue.submit([enc.finish()]);
690
+ await b.stage.mapAsync(GPUMapMode.READ);
691
+ const embedding = new Float32Array(b.stage.getMappedRange().slice(0));
692
+ b.stage.unmap();
693
+ const out = { embedding, T, PP, ms: performance.now() - t0, dispatches: kinds.length };
694
+ if (profile) {
695
+ await qstage.mapAsync(GPUMapMode.READ);
696
+ const ts = new BigUint64Array(qstage.getMappedRange().slice(0));
697
+ qstage.unmap();
698
+ qstage.destroy();
699
+ qbuf.destroy();
700
+ qset.destroy();
701
+ const byKind = {};
702
+ let total = 0;
703
+ kinds.forEach((k, i) => {
704
+ const ms = Number(ts[2 * i + 1] - ts[2 * i]) / 1e6;
705
+ byKind[k] = (byKind[k] || 0) + ms;
706
+ total += ms;
707
+ });
708
+ out.profile = { by_kind_ms: byKind, gpu_total_ms: total, span_ms: Number(ts[2 * kinds.length - 1] - ts[0]) / 1e6 };
709
+ }
710
+ if (debug) {
711
+ await Promise.all(caps.map((cp) => cp.stage.mapAsync(GPUMapMode.READ)));
712
+ out.captures = /* @__PURE__ */ new Map();
713
+ for (const cp of caps) {
714
+ const all = cp.stage.getMappedRange();
715
+ const data = cp.dtype === "f16" ? f16ArrayToF32(new Uint16Array(all, 0, cp.bytes / 2)) : new Float32Array(all.slice(0, cp.bytes));
716
+ out.captures.set(cp.name, { data, shape: cp.shape, dtype: cp.dtype });
717
+ cp.stage.unmap();
718
+ cp.stage.destroy();
719
+ }
720
+ }
721
+ return out;
722
+ }
723
+ destroy() {
724
+ for (const v of Object.values(this.b)) v.destroy();
725
+ }
726
+ };
727
+
728
+ // ../../client/vqweb/vendor/tokenizers/tokenizers.mjs
729
+ var DictionarySplitter = class {
730
+ /**
731
+ * @param dictionary The dictionary of words to use for splitting.
732
+ */
733
+ constructor(dictionary) {
734
+ this.trie = this._build_trie(dictionary);
735
+ }
736
+ /**
737
+ * Builds a trie from the given dictionary.
738
+ * @param dictionary The dictionary of words to build the trie from.
739
+ * @returns The root node of the trie.
740
+ * @private
741
+ */
742
+ _build_trie(dictionary) {
743
+ const trie = /* @__PURE__ */ Object.create(null);
744
+ for (const word of dictionary) {
745
+ let node = trie;
746
+ for (let i = 0; i < word.length; ++i) {
747
+ const char = word[i];
748
+ node = node[char] ??= /* @__PURE__ */ Object.create(null);
749
+ }
750
+ node.end = word;
751
+ }
752
+ return trie;
753
+ }
754
+ /**
755
+ * Splits the input text into tokens based on the dictionary.
756
+ * @param text The input text to split.
757
+ * @returns An array of tokens.
758
+ */
759
+ split(text) {
760
+ const result = [];
761
+ const n = text.length;
762
+ let start = 0;
763
+ let i = 0;
764
+ while (i < n) {
765
+ let node = this.trie;
766
+ let match = null;
767
+ let j = i;
768
+ while (j < n && (node = node[text[j]])) {
769
+ if (node.end) {
770
+ match = node.end;
771
+ }
772
+ ++j;
773
+ }
774
+ if (match) {
775
+ if (i > start) {
776
+ result.push(text.slice(start, i));
777
+ }
778
+ result.push(match);
779
+ i += match.length;
780
+ start = i;
781
+ } else {
782
+ ++i;
783
+ }
784
+ }
785
+ if (start < n) {
786
+ result.push(text.slice(start));
787
+ }
788
+ return result;
789
+ }
790
+ };
791
+ var DictionarySplitter_default = DictionarySplitter;
792
+ var AddedToken = class {
793
+ /**
794
+ * Creates a new instance of AddedToken.
795
+ * @param config Added token configuration object.
796
+ */
797
+ constructor(config) {
798
+ this.content = config.content;
799
+ this.id = config.id;
800
+ this.single_word = config.single_word ?? false;
801
+ this.lstrip = config.lstrip ?? false;
802
+ this.rstrip = config.rstrip ?? false;
803
+ this.special = config.special ?? false;
804
+ this.normalized = config.normalized ?? !this.special;
805
+ }
806
+ };
807
+ var AddedToken_default = AddedToken;
808
+ var compile_unicode_regexp = (source, flags) => {
809
+ try {
810
+ return new RegExp(source, flags);
811
+ } catch (error) {
812
+ if (!(error instanceof SyntaxError)) throw error;
813
+ const property_names = /* @__PURE__ */ new Map();
814
+ const rewritten = source.replace(
815
+ /(\\[pP])\{([^}=]+)\}/g,
816
+ (text, p, n, offset) => {
817
+ let preceding_backslashes = 0;
818
+ for (let i = offset - 1; i >= 0 && source[i] === "\\"; --i) {
819
+ ++preceding_backslashes;
820
+ }
821
+ if (preceding_backslashes % 2 === 1) return text;
822
+ let property_name = property_names.get(n);
823
+ if (property_name === void 0) {
824
+ try {
825
+ new RegExp(`\\p{${n}}`, "u");
826
+ property_name = n;
827
+ } catch {
828
+ property_name = `Script=${n}`;
829
+ }
830
+ property_names.set(n, property_name);
831
+ }
832
+ return `${p}{${property_name}}`;
833
+ }
834
+ );
835
+ if (rewritten === source) throw error;
836
+ try {
837
+ return new RegExp(rewritten, flags);
838
+ } catch {
839
+ throw error;
840
+ }
841
+ }
842
+ };
843
+ var clean_up_tokenization = (text) => text.replace(/ \./g, ".").replace(/ \?/g, "?").replace(/ \!/g, "!").replace(/ ,/g, ",").replace(/ \' /g, "'").replace(/ n't/g, "n't").replace(/ 'm/g, "'m").replace(/ 's/g, "'s").replace(/ 've/g, "'ve").replace(/ 're/g, "'re");
844
+ var create_pattern = (pattern, invert = true) => {
845
+ if (pattern.Regex !== void 0) {
846
+ const regex = rewrite_oniguruma_to_js(
847
+ normalize_bloom_split_char_class(pattern.Regex)
848
+ );
849
+ return compile_unicode_regexp(regex, "gu");
850
+ } else if (pattern.String !== void 0) {
851
+ const escaped = escape_reg_exp(pattern.String);
852
+ return new RegExp(invert ? escaped : `(${escaped})`, "gu");
853
+ } else {
854
+ console.warn("Unknown pattern type:", pattern);
855
+ return null;
856
+ }
857
+ };
858
+ var UNICODE_WORD_CHARS_IN_CLASS = "\\p{Alphabetic}\\p{M}\\p{Nd}\\p{Pc}";
859
+ var UNICODE_WORD_CHARS = `${UNICODE_WORD_CHARS_IN_CLASS}\\u00B2\\u00B3\\u00B9\\u00BC-\\u00BE`;
860
+ var UNICODE_WORD_CLASS = `[${UNICODE_WORD_CHARS}]`;
861
+ var UNICODE_NON_WORD_CLASS = `[^${UNICODE_WORD_CHARS}]`;
862
+ var UNICODE_WORD_BOUNDARY = `(?:(?<!${UNICODE_WORD_CLASS})(?=${UNICODE_WORD_CLASS})|(?<=${UNICODE_WORD_CLASS})(?!${UNICODE_WORD_CLASS}))`;
863
+ var UNICODE_NON_WORD_BOUNDARY = `(?:(?<!${UNICODE_WORD_CLASS})(?!${UNICODE_WORD_CLASS})|(?<=${UNICODE_WORD_CLASS})(?=${UNICODE_WORD_CLASS}))`;
864
+ var LINE_START_ANCHOR = "(?:(?<![\\s\\S])|(?<=\\n))";
865
+ var LINE_END_ANCHOR = "(?:(?=\\n)|(?![\\s\\S]))";
866
+ var HEX_DIGIT_CHARS = "0-9A-Fa-f";
867
+ var ESCAPE_REWRITES = /* @__PURE__ */ new Map([
868
+ ["A", "(?<![\\s\\S])"],
869
+ ["z", "(?![\\s\\S])"],
870
+ ["Z", "(?=\\n?(?![\\s\\S]))"],
871
+ // \Z permits a single optional final \n (not \r\n)
872
+ ["h", `[${HEX_DIGIT_CHARS}]`],
873
+ ["H", `[^${HEX_DIGIT_CHARS}]`],
874
+ ["w", UNICODE_WORD_CLASS],
875
+ ["W", UNICODE_NON_WORD_CLASS],
876
+ ["d", "\\p{Nd}"],
877
+ ["D", "\\P{Nd}"],
878
+ ["s", "\\p{White_Space}"],
879
+ // JS \s wrongly adds U+FEFF and misses \x85
880
+ ["S", "\\P{White_Space}"],
881
+ ["b", UNICODE_WORD_BOUNDARY],
882
+ ["B", UNICODE_NON_WORD_BOUNDARY],
883
+ ["a", "\\x07"],
884
+ ["e", "\\x1B"]
885
+ ]);
886
+ var CLASS_ESCAPE_REWRITES = /* @__PURE__ */ new Map([
887
+ ["h", HEX_DIGIT_CHARS],
888
+ ["w", UNICODE_WORD_CHARS_IN_CLASS],
889
+ ["d", "\\p{Nd}"],
890
+ ["D", "\\P{Nd}"],
891
+ ["s", "\\p{White_Space}"],
892
+ ["S", "\\P{White_Space}"],
893
+ ["a", "\\x07"],
894
+ ["e", "\\x1B"]
895
+ ]);
896
+ var CLASS_COMPLEMENT_ALTERNATIVES = /* @__PURE__ */ new Map([
897
+ ["W", `[^${UNICODE_WORD_CHARS_IN_CLASS}]`],
898
+ ["H", `[^${HEX_DIGIT_CHARS}]`]
899
+ ]);
900
+ var RAW_WHITESPACE_ESCAPES = /* @__PURE__ */ new Map([
901
+ ["\n", "\\n"],
902
+ ["\r", "\\r"],
903
+ [" ", "\\t"],
904
+ ["\f", "\\f"],
905
+ ["\v", "\\v"]
906
+ ]);
907
+ var POSIX_CLASS_FRAGMENTS = /* @__PURE__ */ new Map([
908
+ ["alpha", "\\p{Alphabetic}"],
909
+ ["alnum", "\\p{Alphabetic}\\p{Nd}"],
910
+ ["digit", "\\p{Nd}"],
911
+ ["lower", "\\p{Lowercase}"],
912
+ ["upper", "\\p{Uppercase}"],
913
+ ["space", "\\p{White_Space}"],
914
+ ["blank", "\\t\\p{Zs}"],
915
+ ["punct", "\\p{P}\\p{S}"],
916
+ ["cntrl", "\\p{Cc}"],
917
+ ["word", UNICODE_WORD_CHARS_IN_CLASS],
918
+ ["xdigit", HEX_DIGIT_CHARS]
919
+ ]);
920
+ var JS_SYNTAX_CHARS = "^$\\.*+?()[]{}|/";
921
+ var GROUP_PREFIX_RE = /^\(\?(?:<[=!]|<[A-Za-z_][A-Za-z0-9_]*>|[:=!>])/;
922
+ var BRACED_ESCAPE_RE = /^\\([pPxu])\{([^}]*)\}/;
923
+ var QUANTIFIER_BRACE_RE = /^\{(\d+(?:,\d*)?|,\d+)\}/;
924
+ var POSIX_BRACKET_RE = /^\[:(\^?)(\p{Alphabetic}+):\]/u;
925
+ var EMPTY_NEGATED_POSIX_BRACKET_RE = /^\[:\^:\]/;
926
+ var UNSUPPORTED_POSIX_BRACKET_RE = /^\[(?:\.[^\]]*\.\]|=[^\]]*=\])/;
927
+ var FIXED_WIDTH_ESCAPE_RE = /^(?:\\x[0-9A-Fa-f]{2}|\\u[0-9A-Fa-f]{4}|\\c[A-Za-z])/;
928
+ var is_ascii_letter = (char) => char >= "A" && char <= "Z" || char >= "a" && char <= "z";
929
+ var character_at = (text, index) => String.fromCodePoint(text.codePointAt(index));
930
+ var get_ascii_folded_hex_atom = (hex) => {
931
+ if (!/^[0-9A-Fa-f]{1,8}$/.test(hex)) return null;
932
+ const code_point = Number.parseInt(hex, 16);
933
+ if (code_point > 127) return null;
934
+ const letter = String.fromCharCode(code_point);
935
+ return is_ascii_letter(letter) ? `[${letter.toLowerCase()}${letter.toUpperCase()}]` : null;
936
+ };
937
+ var normalize_bloom_split_char_class = (regex) => regex.replace(/\[\^\(\\s\|\[([^\]]+)\]\)\]/g, "[^()|\\s$1]");
938
+ var ANY_CODE_POINT = "[\\s\\S]";
939
+ var MAX_CHARACTER_CLASS_NESTING_DEPTH = 256;
940
+ var create_character_class_operand = () => ({
941
+ fragment: "",
942
+ alternatives: [],
943
+ tail: null,
944
+ contains_complex_set: false
945
+ });
946
+ var throw_character_class_range_error = (index) => {
947
+ throw new SyntaxError(
948
+ `Unsupported range with a set-valued character-class operand at index ${index}`
949
+ );
950
+ };
951
+ var add_character_class_atom = (operand, atom, index, contains_complex_set = true) => {
952
+ if (operand.tail === "range") throw_character_class_range_error(index);
953
+ operand.alternatives.push(atom);
954
+ operand.tail = "set";
955
+ operand.contains_complex_set = operand.contains_complex_set || contains_complex_set;
956
+ };
957
+ var append_character_class_fragment = (operand, fragment, set_valued = false, index = -1) => {
958
+ if (set_valued && operand.tail === "range") {
959
+ throw_character_class_range_error(index);
960
+ }
961
+ if (operand.tail === "range") {
962
+ operand.fragment += fragment;
963
+ operand.tail = "complete_range";
964
+ return;
965
+ }
966
+ operand.fragment += fragment;
967
+ operand.tail = set_valued ? "set" : "scalar";
968
+ operand.contains_complex_set = operand.contains_complex_set || set_valued;
969
+ };
970
+ var rewrite_character_class_escape = (regex, index, operand) => {
971
+ const braced = BRACED_ESCAPE_RE.exec(regex.slice(index));
972
+ if (braced) {
973
+ const [text, kind, body] = braced;
974
+ if (kind === "P" && body === "Word") {
975
+ add_character_class_atom(
976
+ operand,
977
+ `[^${UNICODE_WORD_CHARS_IN_CLASS}]`,
978
+ index
979
+ );
980
+ } else {
981
+ const replacement2 = kind === "x" ? `\\u{${body}}` : kind === "p" && body === "Word" ? UNICODE_WORD_CHARS_IN_CLASS : text;
982
+ append_character_class_fragment(
983
+ operand,
984
+ replacement2,
985
+ kind === "p" || kind === "P",
986
+ index
987
+ );
988
+ }
989
+ return index + text.length;
990
+ }
991
+ const fixed_width = FIXED_WIDTH_ESCAPE_RE.exec(regex.slice(index));
992
+ if (fixed_width) {
993
+ append_character_class_fragment(operand, fixed_width[0]);
994
+ return index + fixed_width[0].length;
995
+ }
996
+ if (index + 1 >= regex.length) {
997
+ throw new SyntaxError(
998
+ `Unterminated escape in character class at index ${index}`
999
+ );
1000
+ }
1001
+ const next = character_at(regex, index + 1);
1002
+ const next_end = index + 1 + next.length;
1003
+ const raw_whitespace = RAW_WHITESPACE_ESCAPES.get(next);
1004
+ if (raw_whitespace !== void 0) {
1005
+ append_character_class_fragment(operand, raw_whitespace);
1006
+ return next_end;
1007
+ }
1008
+ const complement = CLASS_COMPLEMENT_ALTERNATIVES.get(next);
1009
+ if (complement !== void 0) {
1010
+ add_character_class_atom(operand, complement, index);
1011
+ return next_end;
1012
+ }
1013
+ const rewrite = CLASS_ESCAPE_REWRITES.get(next);
1014
+ let replacement;
1015
+ let set_valued = false;
1016
+ if (rewrite !== void 0) {
1017
+ replacement = rewrite;
1018
+ set_valued = next !== "a" && next !== "e";
1019
+ } else if (/[A-Za-z0-9]/.test(next)) {
1020
+ replacement = `\\${next}`;
1021
+ } else if (JS_SYNTAX_CHARS.includes(next) || next === "-") {
1022
+ replacement = `\\${next}`;
1023
+ } else {
1024
+ replacement = next;
1025
+ }
1026
+ append_character_class_fragment(operand, replacement, set_valued, index);
1027
+ return next_end;
1028
+ };
1029
+ var compile_character_set_union = (pieces) => {
1030
+ if (pieces.length === 1) return pieces[0];
1031
+ return `(?:(?=(?:${pieces.join("|")}))${ANY_CODE_POINT})`;
1032
+ };
1033
+ var compile_character_class_operand = (operand) => {
1034
+ const pieces = operand.fragment.length === 0 ? operand.alternatives : [`[${operand.fragment}]`, ...operand.alternatives];
1035
+ return compile_character_set_union(pieces);
1036
+ };
1037
+ var get_ascii_fold_additions = (positive_atom) => {
1038
+ const membership = compile_unicode_regexp(`^(?:${positive_atom})$`, "u");
1039
+ let additions = "";
1040
+ for (let offset = 0; offset < 26; ++offset) {
1041
+ const upper = String.fromCharCode(65 + offset);
1042
+ const lower = String.fromCharCode(97 + offset);
1043
+ const has_upper = membership.test(upper);
1044
+ const has_lower = membership.test(lower);
1045
+ if (has_upper !== has_lower) additions += has_upper ? lower : upper;
1046
+ }
1047
+ return additions;
1048
+ };
1049
+ var parse_character_class = (regex, start, ascii_fold, apply_ascii_fold = true, nesting_depth = 1) => {
1050
+ if (nesting_depth > MAX_CHARACTER_CLASS_NESTING_DEPTH) {
1051
+ throw new SyntaxError(
1052
+ `Maximum character-class nesting depth of ${MAX_CHARACTER_CLASS_NESTING_DEPTH} exceeded at index ${start}`
1053
+ );
1054
+ }
1055
+ let i = start + 1;
1056
+ const negated = regex[i] === "^";
1057
+ if (negated) ++i;
1058
+ const first_content_index = i;
1059
+ const operands = [create_character_class_operand()];
1060
+ let operand = operands[0];
1061
+ let contains_nested_negated_complex_set = false;
1062
+ while (i < regex.length) {
1063
+ const char = character_at(regex, i);
1064
+ if (char === "\\") {
1065
+ i = rewrite_character_class_escape(regex, i, operand);
1066
+ continue;
1067
+ }
1068
+ if (char === "]") {
1069
+ if (i === first_content_index) {
1070
+ append_character_class_fragment(operand, "\\]");
1071
+ ++i;
1072
+ continue;
1073
+ }
1074
+ if (operand.tail === null) {
1075
+ if (operands.length > 1) {
1076
+ throw new SyntaxError(
1077
+ `Malformed character-class intersection with an empty operand at index ${i}`
1078
+ );
1079
+ }
1080
+ throw new SyntaxError(`Empty character class at index ${start}`);
1081
+ }
1082
+ const contains_complex_set = operands.some(
1083
+ (candidate) => candidate.contains_complex_set
1084
+ );
1085
+ if (negated && operands.length > 1 && contains_nested_negated_complex_set) {
1086
+ throw new SyntaxError(
1087
+ `Unsupported outer-negated character-class intersection with a nested negated class containing a Unicode property, POSIX class, or shorthand at index ${start}`
1088
+ );
1089
+ }
1090
+ const first_atom = compile_character_class_operand(operands[0]);
1091
+ let positive_atom = first_atom;
1092
+ if (operands.length > 1) {
1093
+ let lookaheads = "";
1094
+ for (let j = 1; j < operands.length; ++j) {
1095
+ lookaheads += `(?=${compile_character_class_operand(operands[j])})`;
1096
+ }
1097
+ positive_atom = `(?:${lookaheads}${first_atom})`;
1098
+ }
1099
+ const is_direct_class = operands.length === 1 && operand.alternatives.length === 0;
1100
+ let direct_fragment = operand.fragment;
1101
+ if (ascii_fold && apply_ascii_fold) {
1102
+ const additions = get_ascii_fold_additions(positive_atom);
1103
+ if (additions.length > 0) {
1104
+ if (is_direct_class) {
1105
+ direct_fragment += additions;
1106
+ positive_atom = `[${direct_fragment}]`;
1107
+ } else {
1108
+ positive_atom = compile_character_set_union([
1109
+ positive_atom,
1110
+ `[${additions}]`
1111
+ ]);
1112
+ }
1113
+ }
1114
+ }
1115
+ const atom = negated ? is_direct_class ? `[^${direct_fragment}]` : `(?:(?!${positive_atom})${ANY_CODE_POINT})` : positive_atom;
1116
+ return {
1117
+ atom,
1118
+ end: i + 1,
1119
+ negated,
1120
+ contains_complex_set,
1121
+ contains_nested_negated_complex_set
1122
+ };
1123
+ }
1124
+ if (regex.startsWith("&&", i)) {
1125
+ if (operand.tail === null) {
1126
+ throw new SyntaxError(
1127
+ `Malformed character-class intersection with an empty operand at index ${i}`
1128
+ );
1129
+ }
1130
+ operand = create_character_class_operand();
1131
+ operands.push(operand);
1132
+ i += 2;
1133
+ continue;
1134
+ }
1135
+ if (char === "[") {
1136
+ const suffix = regex.slice(i);
1137
+ if (EMPTY_NEGATED_POSIX_BRACKET_RE.test(suffix)) {
1138
+ throw new SyntaxError(
1139
+ `Malformed empty negated POSIX character class at index ${i}`
1140
+ );
1141
+ }
1142
+ const posix = POSIX_BRACKET_RE.exec(suffix);
1143
+ if (posix) {
1144
+ const [, posix_negated, name] = posix;
1145
+ const fragment = POSIX_CLASS_FRAGMENTS.get(name);
1146
+ if (fragment === void 0) {
1147
+ throw new SyntaxError(
1148
+ `Unsupported POSIX character class "${name}" at index ${i}`
1149
+ );
1150
+ }
1151
+ if (ascii_fold && posix_negated && (name === "lower" || name === "upper")) {
1152
+ throw new SyntaxError(
1153
+ `Unsupported negated POSIX ${name} class inside an inline case-insensitive group`
1154
+ );
1155
+ }
1156
+ if (posix_negated) {
1157
+ add_character_class_atom(operand, `[^${fragment}]`, i);
1158
+ } else {
1159
+ append_character_class_fragment(operand, fragment, true, i);
1160
+ }
1161
+ i += posix[0].length;
1162
+ continue;
1163
+ }
1164
+ if (UNSUPPORTED_POSIX_BRACKET_RE.test(suffix)) {
1165
+ throw new SyntaxError(
1166
+ `Unsupported POSIX collating or equivalence bracket expression at index ${i}`
1167
+ );
1168
+ }
1169
+ const nested = parse_character_class(
1170
+ regex,
1171
+ i,
1172
+ ascii_fold,
1173
+ false,
1174
+ nesting_depth + 1
1175
+ );
1176
+ add_character_class_atom(
1177
+ operand,
1178
+ nested.atom,
1179
+ i,
1180
+ nested.contains_complex_set
1181
+ );
1182
+ contains_nested_negated_complex_set ||= nested.contains_nested_negated_complex_set || nested.negated && nested.contains_complex_set;
1183
+ i = nested.end;
1184
+ continue;
1185
+ }
1186
+ if (char === "-") {
1187
+ const is_terminal_literal = regex[i + 1] === "]" || regex.startsWith("&&", i + 1);
1188
+ if (operand.tail === "set" && !is_terminal_literal) {
1189
+ throw_character_class_range_error(i);
1190
+ }
1191
+ if (operand.tail === null || operand.tail === "range" || operand.tail === "complete_range" || is_terminal_literal) {
1192
+ append_character_class_fragment(operand, "\\-");
1193
+ } else {
1194
+ operand.fragment += "-";
1195
+ operand.tail = "range";
1196
+ }
1197
+ ++i;
1198
+ continue;
1199
+ }
1200
+ append_character_class_fragment(
1201
+ operand,
1202
+ char === "^" && operand.fragment.length === 0 ? "\\^" : char
1203
+ );
1204
+ i += char.length;
1205
+ }
1206
+ throw new SyntaxError(
1207
+ `${operands.length > 1 ? "Unterminated character-class intersection" : "Unterminated character class"} at index ${start}`
1208
+ );
1209
+ };
1210
+ var rewrite_oniguruma_to_js = (regex) => {
1211
+ let out = "";
1212
+ let atom_start = -1;
1213
+ let last_was_quantifier = false;
1214
+ let ascii_fold = false;
1215
+ const group_states = [];
1216
+ const emit_atom = (text) => {
1217
+ atom_start = out.length;
1218
+ out += text;
1219
+ last_was_quantifier = false;
1220
+ };
1221
+ for (let i = 0; i < regex.length; ) {
1222
+ const char = character_at(regex, i);
1223
+ if (char === "\\") {
1224
+ const braced = BRACED_ESCAPE_RE.exec(regex.slice(i));
1225
+ if (braced) {
1226
+ const [text, kind, body] = braced;
1227
+ let replacement2 = text;
1228
+ if (kind === "x") {
1229
+ const code_point_escape = `\\u{${body}}`;
1230
+ replacement2 = ascii_fold ? get_ascii_folded_hex_atom(body) ?? code_point_escape : code_point_escape;
1231
+ } else if (body === "Word") {
1232
+ replacement2 = kind === "p" ? UNICODE_WORD_CLASS : UNICODE_NON_WORD_CLASS;
1233
+ }
1234
+ emit_atom(replacement2);
1235
+ i += text.length;
1236
+ continue;
1237
+ }
1238
+ const fixed_width = FIXED_WIDTH_ESCAPE_RE.exec(regex.slice(i));
1239
+ if (fixed_width) {
1240
+ const text = fixed_width[0];
1241
+ const replacement2 = ascii_fold && text[1] !== "c" ? get_ascii_folded_hex_atom(text.slice(2)) ?? text : text;
1242
+ emit_atom(replacement2);
1243
+ i += text.length;
1244
+ continue;
1245
+ }
1246
+ if (i + 1 >= regex.length) {
1247
+ out += char;
1248
+ break;
1249
+ }
1250
+ const next = character_at(regex, i + 1);
1251
+ i += 1 + next.length;
1252
+ if (next === "G") {
1253
+ continue;
1254
+ }
1255
+ const raw_whitespace = RAW_WHITESPACE_ESCAPES.get(next);
1256
+ if (raw_whitespace !== void 0) {
1257
+ emit_atom(raw_whitespace);
1258
+ continue;
1259
+ }
1260
+ const rewrite = ESCAPE_REWRITES.get(next);
1261
+ let replacement;
1262
+ if (rewrite !== void 0) {
1263
+ replacement = rewrite;
1264
+ } else if (/[A-Za-z0-9]/.test(next)) {
1265
+ replacement = `\\${next}`;
1266
+ } else if (JS_SYNTAX_CHARS.includes(next)) {
1267
+ replacement = `\\${next}`;
1268
+ } else {
1269
+ replacement = next;
1270
+ }
1271
+ emit_atom(replacement);
1272
+ continue;
1273
+ }
1274
+ switch (char) {
1275
+ case "[": {
1276
+ const parsed = parse_character_class(regex, i, ascii_fold);
1277
+ emit_atom(parsed.atom);
1278
+ i = parsed.end;
1279
+ continue;
1280
+ }
1281
+ case "]":
1282
+ emit_atom("\\]");
1283
+ ++i;
1284
+ continue;
1285
+ case ".":
1286
+ emit_atom("[^\\n]");
1287
+ ++i;
1288
+ continue;
1289
+ case "^":
1290
+ emit_atom(LINE_START_ANCHOR);
1291
+ ++i;
1292
+ continue;
1293
+ case "$":
1294
+ emit_atom(LINE_END_ANCHOR);
1295
+ ++i;
1296
+ continue;
1297
+ case "(": {
1298
+ const inline_case_insensitive = regex.startsWith("(?i:", i);
1299
+ const source_prefix = inline_case_insensitive ? "(?i:" : GROUP_PREFIX_RE.exec(regex.slice(i))?.[0] ?? "(";
1300
+ const output_prefix = inline_case_insensitive ? "(?:" : source_prefix === "(?>" ? "(?:" : source_prefix;
1301
+ group_states.push([out.length, ascii_fold]);
1302
+ if (inline_case_insensitive) ascii_fold = true;
1303
+ out += output_prefix;
1304
+ last_was_quantifier = false;
1305
+ i += source_prefix.length;
1306
+ continue;
1307
+ }
1308
+ case ")":
1309
+ out += char;
1310
+ [atom_start, ascii_fold] = group_states.pop() ?? [-1, false];
1311
+ last_was_quantifier = false;
1312
+ ++i;
1313
+ continue;
1314
+ case "|":
1315
+ out += char;
1316
+ atom_start = -1;
1317
+ last_was_quantifier = false;
1318
+ ++i;
1319
+ continue;
1320
+ case "{": {
1321
+ const quant = QUANTIFIER_BRACE_RE.exec(regex.slice(i));
1322
+ if (!quant || atom_start < 0) {
1323
+ emit_atom("\\{");
1324
+ ++i;
1325
+ continue;
1326
+ }
1327
+ const body = quant[1].startsWith(",") ? `0${quant[1]}` : quant[1];
1328
+ i += quant[0].length;
1329
+ const following = regex[i];
1330
+ if (following === "+" || following === "*") {
1331
+ out = `${out.slice(0, atom_start)}(?:${out.slice(atom_start)}{${body}})${following}`;
1332
+ ++i;
1333
+ } else {
1334
+ out += `{${body}}`;
1335
+ }
1336
+ last_was_quantifier = true;
1337
+ continue;
1338
+ }
1339
+ case "}":
1340
+ emit_atom("\\}");
1341
+ ++i;
1342
+ continue;
1343
+ case "+":
1344
+ if (last_was_quantifier) {
1345
+ ++i;
1346
+ continue;
1347
+ }
1348
+ out += char;
1349
+ last_was_quantifier = true;
1350
+ ++i;
1351
+ continue;
1352
+ case "*":
1353
+ case "?":
1354
+ out += char;
1355
+ last_was_quantifier = true;
1356
+ ++i;
1357
+ continue;
1358
+ default:
1359
+ emit_atom(
1360
+ ascii_fold && is_ascii_letter(char) ? `[${char.toLowerCase()}${char.toUpperCase()}]` : char
1361
+ );
1362
+ i += char.length;
1363
+ continue;
1364
+ }
1365
+ }
1366
+ return out;
1367
+ };
1368
+ var escape_reg_exp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1369
+ var fuse_unk = (arr, tokens_to_ids, unk_token_id) => {
1370
+ const fused = [];
1371
+ let i = 0;
1372
+ while (i < arr.length) {
1373
+ fused.push(arr[i]);
1374
+ const token_id = tokens_to_ids.get(arr[i]) ?? unk_token_id;
1375
+ if (token_id !== unk_token_id) {
1376
+ ++i;
1377
+ continue;
1378
+ }
1379
+ while (++i < arr.length && (tokens_to_ids.get(arr[i]) ?? unk_token_id) === unk_token_id) {
1380
+ if (tokens_to_ids.get(fused.at(-1)) !== unk_token_id) {
1381
+ fused[fused.length - 1] += arr[i];
1382
+ }
1383
+ }
1384
+ }
1385
+ return fused;
1386
+ };
1387
+ var is_chinese_char = (cp) => cp >= 19968 && cp <= 40959 || cp >= 13312 && cp <= 19903 || cp >= 131072 && cp <= 173791 || cp >= 173824 && cp <= 177983 || cp >= 177984 && cp <= 178207 || cp >= 178208 && cp <= 183983 || cp >= 63744 && cp <= 64255 || cp >= 194560 && cp <= 195103;
1388
+ var is_integral_number = (x) => Number.isInteger(x) || typeof x === "bigint";
1389
+ var len = (s) => {
1390
+ let length = 0;
1391
+ for (const c of s) ++length;
1392
+ return length;
1393
+ };
1394
+ var lowercase_and_remove_accents = (text) => remove_accents(text.toLowerCase());
1395
+ var merge_arrays = (...arrs) => Array.prototype.concat.apply([], arrs);
1396
+ var object_to_map = (obj) => new Map(Object.entries(obj));
1397
+ var regex_split = (text, regex) => {
1398
+ const result = [];
1399
+ let prev = 0;
1400
+ for (const match of text.matchAll(regex)) {
1401
+ const full_match = match[0];
1402
+ if (prev < match.index) {
1403
+ result.push(text.slice(prev, match.index));
1404
+ }
1405
+ if (full_match.length > 0) {
1406
+ result.push(full_match);
1407
+ }
1408
+ prev = match.index + full_match.length;
1409
+ }
1410
+ if (prev < text.length) {
1411
+ result.push(text.slice(prev));
1412
+ }
1413
+ return result;
1414
+ };
1415
+ var remove_accents = (text) => text.replace(/\p{M}/gu, "");
1416
+ var validate_object = (obj, name, required_keys = []) => {
1417
+ if (!obj || Array.isArray(obj) || typeof obj !== "object") {
1418
+ return `${name} must be a valid object`;
1419
+ }
1420
+ for (const key of required_keys) {
1421
+ if (!(key in obj)) {
1422
+ return `${name} must contain a "${key}" property`;
1423
+ }
1424
+ }
1425
+ return null;
1426
+ };
1427
+ var whitespace_split = (text) => text.match(/\S+/g) || [];
1428
+ var Callable = class {
1429
+ /**
1430
+ * Creates a new instance of the Callable class.
1431
+ */
1432
+ constructor() {
1433
+ const closure = function(...args) {
1434
+ return closure._call(...args);
1435
+ };
1436
+ return Object.setPrototypeOf(closure, new.target.prototype);
1437
+ }
1438
+ };
1439
+ var Callable_default = Callable;
1440
+ var Normalizer = class extends Callable_default {
1441
+ /**
1442
+ * @param config The configuration object for the normalizer.
1443
+ */
1444
+ constructor(config) {
1445
+ super();
1446
+ this.config = config;
1447
+ }
1448
+ /**
1449
+ * Alias for {@link Normalizer#normalize}.
1450
+ * @param text The text to normalize.
1451
+ * @returns The normalized text.
1452
+ */
1453
+ _call(text) {
1454
+ return this.normalize(text);
1455
+ }
1456
+ };
1457
+ var Normalizer_default = Normalizer;
1458
+ var BertNormalizer = class extends Normalizer_default {
1459
+ /**
1460
+ * Adds whitespace around any CJK (Chinese, Japanese, or Korean) character in the input text.
1461
+ *
1462
+ * @param text The input text to tokenize.
1463
+ * @returns The tokenized text with whitespace added around CJK characters.
1464
+ */
1465
+ tokenize_chinese_chars(text) {
1466
+ const output = [];
1467
+ for (let i = 0; i < text.length; ++i) {
1468
+ const char = text[i];
1469
+ const cp = char.charCodeAt(0);
1470
+ if (is_chinese_char(cp)) {
1471
+ output.push(" ");
1472
+ output.push(char);
1473
+ output.push(" ");
1474
+ } else {
1475
+ output.push(char);
1476
+ }
1477
+ }
1478
+ return output.join("");
1479
+ }
1480
+ /**
1481
+ * Strips accents from the given text.
1482
+ * @param text The text to strip accents from.
1483
+ * @returns The text with accents removed.
1484
+ */
1485
+ strip_accents(text) {
1486
+ return text.normalize("NFD").replace(/\p{Mn}/gu, "");
1487
+ }
1488
+ /**
1489
+ * Checks whether `char` is a control character.
1490
+ * @param char The character to check.
1491
+ * @returns Whether `char` is a control character.
1492
+ */
1493
+ is_control(char) {
1494
+ switch (char) {
1495
+ case " ":
1496
+ case "\n":
1497
+ case "\r":
1498
+ return false;
1499
+ default:
1500
+ return /^\p{Cc}|\p{Cf}|\p{Co}|\p{Cs}$/u.test(char);
1501
+ }
1502
+ }
1503
+ /**
1504
+ * Performs invalid character removal and whitespace cleanup on text.
1505
+ * @param text The text to clean.
1506
+ * @returns The cleaned text.
1507
+ */
1508
+ clean_text(text) {
1509
+ const output = [];
1510
+ for (const char of text) {
1511
+ const cp = char.charCodeAt(0);
1512
+ if (cp === 0 || cp === 65533 || this.is_control(char)) {
1513
+ continue;
1514
+ }
1515
+ if (/^\s$/.test(char)) {
1516
+ output.push(" ");
1517
+ } else {
1518
+ output.push(char);
1519
+ }
1520
+ }
1521
+ return output.join("");
1522
+ }
1523
+ /**
1524
+ * Normalizes the given text based on the configuration.
1525
+ * @param text The text to normalize.
1526
+ * @returns The normalized text.
1527
+ */
1528
+ normalize(text) {
1529
+ if (this.config.clean_text) {
1530
+ text = this.clean_text(text);
1531
+ }
1532
+ if (this.config.handle_chinese_chars) {
1533
+ text = this.tokenize_chinese_chars(text);
1534
+ }
1535
+ if (this.config.lowercase) {
1536
+ text = text.toLowerCase();
1537
+ if (this.config.strip_accents !== false) {
1538
+ text = this.strip_accents(text);
1539
+ }
1540
+ } else if (this.config.strip_accents) {
1541
+ text = this.strip_accents(text);
1542
+ }
1543
+ return text;
1544
+ }
1545
+ };
1546
+ var BertNormalizer_default = BertNormalizer;
1547
+ var Precompiled = class extends Normalizer_default {
1548
+ /**
1549
+ * Create a new instance of Precompiled normalizer.
1550
+ * @param config The configuration object.
1551
+ */
1552
+ constructor(config) {
1553
+ super(config);
1554
+ this.charsmap = config.precompiled_charsmap ?? null;
1555
+ }
1556
+ /**
1557
+ * Normalizes the given text by applying the precompiled charsmap.
1558
+ * @param text The text to normalize.
1559
+ * @returns The normalized text.
1560
+ */
1561
+ normalize(text) {
1562
+ text = text.replace(
1563
+ /[\u0001-\u0008\u000B\u000E-\u001F\u007F\u008F\u009F]/gm,
1564
+ ""
1565
+ );
1566
+ text = text.replace(
1567
+ /[\u0009\u000A\u000C\u000D\u00A0\u1680\u2000-\u200F\u2028\u2029\u202F\u205F\u2581\u3000\uFEFF\uFFFD]/gm,
1568
+ " "
1569
+ );
1570
+ if (text.includes("\uFF5E")) {
1571
+ const parts = text.split("\uFF5E");
1572
+ text = parts.map((part) => part.normalize("NFKC")).join("\uFF5E");
1573
+ } else {
1574
+ text = text.normalize("NFKC");
1575
+ }
1576
+ return text;
1577
+ }
1578
+ };
1579
+ var Precompiled_default = Precompiled;
1580
+ var Sequence = class extends Normalizer_default {
1581
+ /**
1582
+ * Create a new instance of NormalizerSequence.
1583
+ * @param config The configuration object.
1584
+ */
1585
+ constructor(config) {
1586
+ super(config);
1587
+ this.normalizers = (config.normalizers ?? []).map(
1588
+ (x) => create_normalizer_default(x)
1589
+ );
1590
+ }
1591
+ /**
1592
+ * Apply a sequence of Normalizers to the input text.
1593
+ * @param text The text to normalize.
1594
+ * @returns The normalized text.
1595
+ */
1596
+ normalize(text) {
1597
+ return this.normalizers.reduce((t, normalizer) => {
1598
+ return normalizer ? normalizer.normalize(t) : t;
1599
+ }, text);
1600
+ }
1601
+ };
1602
+ var Sequence_default = Sequence;
1603
+ var Replace = class extends Normalizer_default {
1604
+ /**
1605
+ * @param config The configuration object for the normalizer.
1606
+ */
1607
+ constructor(config) {
1608
+ super(config);
1609
+ this.pattern = create_pattern(this.config.pattern ?? {});
1610
+ }
1611
+ /**
1612
+ * Normalize the input text by replacing the pattern with the content.
1613
+ * @param text The input text to be normalized.
1614
+ * @returns The normalized text after replacing the pattern with the content.
1615
+ */
1616
+ normalize(text) {
1617
+ return this.pattern === null ? text : text.replaceAll(this.pattern, this.config.content ?? "");
1618
+ }
1619
+ };
1620
+ var Replace_default = Replace;
1621
+ var UnicodeNormalizer = class extends Normalizer_default {
1622
+ constructor() {
1623
+ super(...arguments);
1624
+ this.form = "NFC";
1625
+ }
1626
+ /**
1627
+ * Normalize the input text by applying Unicode normalization.
1628
+ * @param text The input text to be normalized.
1629
+ * @returns The normalized text.
1630
+ */
1631
+ normalize(text) {
1632
+ text = text.normalize(this.form);
1633
+ return text;
1634
+ }
1635
+ };
1636
+ var UnicodeNormalizer_default = UnicodeNormalizer;
1637
+ var NFC = class extends UnicodeNormalizer_default {
1638
+ constructor() {
1639
+ super(...arguments);
1640
+ this.form = "NFC";
1641
+ }
1642
+ };
1643
+ var NFC_default = NFC;
1644
+ var NFD = class extends UnicodeNormalizer_default {
1645
+ constructor() {
1646
+ super(...arguments);
1647
+ this.form = "NFD";
1648
+ }
1649
+ };
1650
+ var NFD_default = NFD;
1651
+ var NFKC = class extends UnicodeNormalizer_default {
1652
+ constructor() {
1653
+ super(...arguments);
1654
+ this.form = "NFKC";
1655
+ }
1656
+ };
1657
+ var NFKC_default = NFKC;
1658
+ var NFKD = class extends UnicodeNormalizer_default {
1659
+ constructor() {
1660
+ super(...arguments);
1661
+ this.form = "NFKD";
1662
+ }
1663
+ };
1664
+ var NFKD_default = NFKD;
1665
+ var Strip = class extends Normalizer_default {
1666
+ /**
1667
+ * Strip leading and/or trailing whitespace from the input text.
1668
+ * @param text The input text.
1669
+ * @returns The normalized text.
1670
+ */
1671
+ normalize(text) {
1672
+ if (this.config.strip_left && this.config.strip_right) {
1673
+ text = text.trim();
1674
+ } else {
1675
+ if (this.config.strip_left) {
1676
+ text = text.trimStart();
1677
+ }
1678
+ if (this.config.strip_right) {
1679
+ text = text.trimEnd();
1680
+ }
1681
+ }
1682
+ return text;
1683
+ }
1684
+ };
1685
+ var Strip_default = Strip;
1686
+ var StripAccents = class extends Normalizer_default {
1687
+ /**
1688
+ * Remove all accents from the text.
1689
+ * @param text The input text.
1690
+ * @returns The normalized text without accents.
1691
+ */
1692
+ normalize(text) {
1693
+ return remove_accents(text);
1694
+ }
1695
+ };
1696
+ var StripAccents_default = StripAccents;
1697
+ var Lowercase = class extends Normalizer_default {
1698
+ /**
1699
+ * Lowercases the input string.
1700
+ * @param {string} text The text to normalize.
1701
+ * @returns {string} The normalized text.
1702
+ */
1703
+ normalize(text) {
1704
+ return text.toLowerCase();
1705
+ }
1706
+ };
1707
+ var Lowercase_default = Lowercase;
1708
+ var Prepend = class extends Normalizer_default {
1709
+ /**
1710
+ * Prepends the input string.
1711
+ * @param text The text to normalize.
1712
+ * @returns The normalized text.
1713
+ */
1714
+ normalize(text) {
1715
+ text = this.config.prepend + text;
1716
+ return text;
1717
+ }
1718
+ };
1719
+ var Prepend_default = Prepend;
1720
+ function create_normalizer(config) {
1721
+ if (config === null) return null;
1722
+ switch (config.type) {
1723
+ case "BertNormalizer":
1724
+ return new BertNormalizer_default(config);
1725
+ case "Precompiled":
1726
+ return new Precompiled_default(config);
1727
+ case "Sequence":
1728
+ return new Sequence_default(config);
1729
+ case "Replace":
1730
+ return new Replace_default(config);
1731
+ case "NFC":
1732
+ return new NFC_default(config);
1733
+ case "NFD":
1734
+ return new NFD_default(config);
1735
+ case "NFKC":
1736
+ return new NFKC_default(config);
1737
+ case "NFKD":
1738
+ return new NFKD_default(config);
1739
+ case "Strip":
1740
+ return new Strip_default(config);
1741
+ case "StripAccents":
1742
+ return new StripAccents_default(config);
1743
+ case "Lowercase":
1744
+ return new Lowercase_default(config);
1745
+ case "Prepend":
1746
+ return new Prepend_default(config);
1747
+ default:
1748
+ throw new Error(`Unknown Normalizer type: ${config.type}`);
1749
+ }
1750
+ }
1751
+ var create_normalizer_default = create_normalizer;
1752
+ var PreTokenizer = class extends Callable_default {
1753
+ /**
1754
+ * Tokenizes the given text into pre-tokens.
1755
+ * @param text The text or array of texts to pre-tokenize.
1756
+ * @param options Additional options for the pre-tokenization logic.
1757
+ * @returns An array of pre-tokens.
1758
+ */
1759
+ pre_tokenize(text, options) {
1760
+ return (Array.isArray(text) ? text.map((x) => this.pre_tokenize_text(x, options)) : this.pre_tokenize_text(text, options)).flat();
1761
+ }
1762
+ /**
1763
+ * Alias for {@link PreTokenizer#pre_tokenize}.
1764
+ * @param text The text or array of texts to pre-tokenize.
1765
+ * @param options Additional options for the pre-tokenization logic.
1766
+ * @returns An array of pre-tokens.
1767
+ */
1768
+ _call(text, options) {
1769
+ return this.pre_tokenize(text, options);
1770
+ }
1771
+ };
1772
+ var PreTokenizer_default = PreTokenizer;
1773
+ var BYTES_TO_UNICODE = (() => {
1774
+ const bs = [
1775
+ ...Array.from(
1776
+ { length: "~".charCodeAt(0) - "!".charCodeAt(0) + 1 },
1777
+ (_, i) => i + "!".charCodeAt(0)
1778
+ ),
1779
+ ...Array.from(
1780
+ { length: "\xAC".charCodeAt(0) - "\xA1".charCodeAt(0) + 1 },
1781
+ (_, i) => i + "\xA1".charCodeAt(0)
1782
+ ),
1783
+ ...Array.from(
1784
+ { length: "\xFF".charCodeAt(0) - "\xAE".charCodeAt(0) + 1 },
1785
+ (_, i) => i + "\xAE".charCodeAt(0)
1786
+ )
1787
+ ];
1788
+ const cs = bs.slice();
1789
+ let n = 0;
1790
+ for (let b = 0; b < 256; ++b) {
1791
+ if (!bs.includes(b)) {
1792
+ bs.push(b);
1793
+ cs.push(256 + n);
1794
+ n += 1;
1795
+ }
1796
+ }
1797
+ const ccs = cs.map((n2) => String.fromCharCode(n2));
1798
+ return Object.fromEntries(bs.map((b, i) => [b, ccs[i]]));
1799
+ })();
1800
+ var reverse_dictionary = (data) => Object.fromEntries(Object.entries(data).map(([key, value]) => [value, key]));
1801
+ var UNICODE_TO_BYTES = reverse_dictionary(BYTES_TO_UNICODE);
1802
+ var PUNCTUATION_REGEX = "\\p{P}\\u0021-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E";
1803
+ var ByteLevel = class extends PreTokenizer_default {
1804
+ /**
1805
+ * Creates a new instance of the `ByteLevelPreTokenizer` class.
1806
+ * @param config The configuration object.
1807
+ */
1808
+ constructor(config) {
1809
+ super();
1810
+ this.config = config;
1811
+ this.add_prefix_space = this.config.add_prefix_space ?? false;
1812
+ this.trim_offsets = this.config.trim_offsets ?? false;
1813
+ this.use_regex = this.config.use_regex ?? true;
1814
+ this.pattern = /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
1815
+ this.byte_encoder = BYTES_TO_UNICODE;
1816
+ this.text_encoder = new TextEncoder();
1817
+ }
1818
+ /**
1819
+ * Tokenizes a single piece of text using byte-level tokenization.
1820
+ * @param text The text to tokenize.
1821
+ * @param options Additional options for the pre-tokenization logic.
1822
+ * @returns An array of tokens.
1823
+ */
1824
+ pre_tokenize_text(text, options) {
1825
+ if (this.add_prefix_space && !text.startsWith(" ")) {
1826
+ text = " " + text;
1827
+ }
1828
+ const tokens = this.use_regex ? text.match(this.pattern) || [] : [text];
1829
+ return tokens.map(
1830
+ (token) => Array.from(
1831
+ this.text_encoder.encode(token),
1832
+ (byte) => this.byte_encoder[byte]
1833
+ ).join("")
1834
+ );
1835
+ }
1836
+ };
1837
+ var ByteLevel_default = ByteLevel;
1838
+ var Whitespace = class extends PreTokenizer_default {
1839
+ /**
1840
+ * Pre-tokenizes the input text by splitting it on word boundaries.
1841
+ * @param text The text to be pre-tokenized.
1842
+ * @param options Additional options for the pre-tokenization logic.
1843
+ * @returns An array of tokens produced by splitting the input text on whitespace.
1844
+ */
1845
+ pre_tokenize_text(text, options) {
1846
+ return text.match(/\w+|[^\w\s]+/g) || [];
1847
+ }
1848
+ };
1849
+ var Whitespace_default = Whitespace;
1850
+ var Metaspace = class extends PreTokenizer_default {
1851
+ /**
1852
+ * @param config The configuration object for the MetaspacePreTokenizer.
1853
+ */
1854
+ constructor(config) {
1855
+ super();
1856
+ this.replacement = config.replacement ?? "\u2581";
1857
+ this.str_rep = config.str_rep || this.replacement;
1858
+ this.prepend_scheme = config.prepend_scheme ?? "always";
1859
+ }
1860
+ /**
1861
+ * This method takes a string, replaces spaces with the replacement character,
1862
+ * adds a prefix space if requested, and returns a new list of tokens.
1863
+ * @param text The text to pre-tokenize.
1864
+ * @param options The options for the pre-tokenization.
1865
+ * @returns A new list of pre-tokenized tokens.
1866
+ */
1867
+ pre_tokenize_text(text, options) {
1868
+ const { section_index = void 0 } = options ?? {};
1869
+ let normalized = text.replaceAll(" ", this.str_rep);
1870
+ if (
1871
+ // We add a prefix space if:
1872
+ // (1) The normalized token does not already start with the replacement character.
1873
+ !normalized.startsWith(this.replacement) && // and (2) either:
1874
+ // (a) prepend_scheme is 'always'
1875
+ // (b) prepend_scheme is 'first' and this is the first section
1876
+ (this.prepend_scheme === "always" || this.prepend_scheme === "first" && section_index === 0)
1877
+ ) {
1878
+ normalized = this.str_rep + normalized;
1879
+ }
1880
+ return [normalized];
1881
+ }
1882
+ };
1883
+ var Metaspace_default = Metaspace;
1884
+ var Split = class extends PreTokenizer_default {
1885
+ /**
1886
+ * @param config The configuration options for the pre-tokenizer.
1887
+ */
1888
+ constructor(config) {
1889
+ super();
1890
+ this.config = config;
1891
+ this.pattern = create_pattern(
1892
+ this.config.pattern ?? {},
1893
+ this.config.invert ?? true
1894
+ );
1895
+ }
1896
+ /**
1897
+ * Tokenizes text by splitting it using the given pattern.
1898
+ * @param text The text to tokenize.
1899
+ * @returns An array of tokens.
1900
+ */
1901
+ pre_tokenize_text(text) {
1902
+ if (this.pattern === null) {
1903
+ return [];
1904
+ }
1905
+ if (this.config.invert) {
1906
+ return (text.match(this.pattern) || []).filter((x) => x);
1907
+ } else if (this.config.behavior?.toLowerCase() === "removed") {
1908
+ return text.split(this.pattern).filter((x) => x);
1909
+ } else {
1910
+ return regex_split(text, this.pattern);
1911
+ }
1912
+ }
1913
+ };
1914
+ var Split_default = Split;
1915
+ var Punctuation = class extends PreTokenizer_default {
1916
+ /**
1917
+ * @param config The configuration options for the pre-tokenizer.
1918
+ */
1919
+ constructor(config) {
1920
+ super();
1921
+ this.config = config;
1922
+ this.pattern = new RegExp(
1923
+ `[^${PUNCTUATION_REGEX}]+|[${PUNCTUATION_REGEX}]+`,
1924
+ "gu"
1925
+ );
1926
+ }
1927
+ /**
1928
+ * Tokenizes text by splitting it using the given pattern.
1929
+ * @param text The text to tokenize.
1930
+ * @returns An array of tokens.
1931
+ */
1932
+ pre_tokenize_text(text) {
1933
+ return text.match(this.pattern) || [];
1934
+ }
1935
+ };
1936
+ var Punctuation_default = Punctuation;
1937
+ var Digits = class extends PreTokenizer_default {
1938
+ /**
1939
+ * @param config The configuration options for the pre-tokenizer.
1940
+ */
1941
+ constructor(config) {
1942
+ super();
1943
+ this.config = config;
1944
+ const digit_pattern = `[^\\d]+|\\d${this.config.individual_digits ? "" : "+"}`;
1945
+ this.pattern = new RegExp(digit_pattern, "gu");
1946
+ }
1947
+ /**
1948
+ * Tokenizes text by splitting it using the given pattern.
1949
+ * @param text The text to tokenize.
1950
+ * @returns An array of tokens.
1951
+ */
1952
+ pre_tokenize_text(text) {
1953
+ return text.match(this.pattern) || [];
1954
+ }
1955
+ };
1956
+ var Digits_default = Digits;
1957
+ var BertPreTokenizer = class extends PreTokenizer_default {
1958
+ /**
1959
+ * A PreTokenizer that splits text into wordpieces using a basic tokenization scheme
1960
+ * similar to that used in the original implementation of BERT.
1961
+ */
1962
+ constructor() {
1963
+ super();
1964
+ this.pattern = new RegExp(
1965
+ `[^\\s${PUNCTUATION_REGEX}]+|[${PUNCTUATION_REGEX}]`,
1966
+ "gu"
1967
+ );
1968
+ }
1969
+ /**
1970
+ * Tokenizes a single text using the BERT pre-tokenization scheme.
1971
+ *
1972
+ * @param text The text to tokenize.
1973
+ * @param options Additional options for the pre-tokenization logic.
1974
+ * @returns An array of tokens.
1975
+ */
1976
+ pre_tokenize_text(text, options) {
1977
+ return text.trim().match(this.pattern) || [];
1978
+ }
1979
+ };
1980
+ var BertPreTokenizer_default = BertPreTokenizer;
1981
+ var Replace2 = class extends PreTokenizer_default {
1982
+ /**
1983
+ * @param config The configuration options for the pre-tokenizer.
1984
+ */
1985
+ constructor(config) {
1986
+ super();
1987
+ this.config = config;
1988
+ this.pattern = create_pattern(this.config.pattern ?? {});
1989
+ this.content = this.config.content ?? "";
1990
+ }
1991
+ /**
1992
+ * Pre-tokenizes the input text by replacing certain characters.
1993
+ * @param text The text to be pre-tokenized.
1994
+ * @returns An array of tokens produced by replacing certain characters.
1995
+ */
1996
+ pre_tokenize_text(text) {
1997
+ if (this.pattern === null) {
1998
+ return [text];
1999
+ }
2000
+ return [text.replaceAll(this.pattern, this.config.content ?? "")];
2001
+ }
2002
+ };
2003
+ var Replace_default2 = Replace2;
2004
+ var Sequence2 = class extends PreTokenizer_default {
2005
+ /**
2006
+ * Creates an instance of PreTokenizerSequence.
2007
+ * @param config The configuration object for the pre-tokenizer sequence.
2008
+ */
2009
+ constructor(config) {
2010
+ super();
2011
+ this.tokenizers = (config.pretokenizers ?? []).map(
2012
+ (x) => create_pre_tokenizer_default(x)
2013
+ );
2014
+ }
2015
+ /**
2016
+ * Applies each pre-tokenizer in the sequence to the input text in turn.
2017
+ * @param text The text to pre-tokenize.
2018
+ * @param options Additional options for the pre-tokenization logic.
2019
+ * @returns The pre-tokenized text.
2020
+ */
2021
+ pre_tokenize_text(text, options) {
2022
+ return this.tokenizers.reduce(
2023
+ (pre_tokenized_text, tokenizer) => {
2024
+ return tokenizer ? tokenizer.pre_tokenize(pre_tokenized_text, options) : pre_tokenized_text;
2025
+ },
2026
+ [text]
2027
+ );
2028
+ }
2029
+ };
2030
+ var Sequence_default2 = Sequence2;
2031
+ var WhitespaceSplit = class extends PreTokenizer_default {
2032
+ /**
2033
+ * Pre-tokenizes the input text by splitting it on whitespace characters.
2034
+ * @param text The text to be pre-tokenized.
2035
+ * @returns An array of tokens produced by splitting the input text on whitespace.
2036
+ */
2037
+ pre_tokenize_text(text) {
2038
+ return whitespace_split(text);
2039
+ }
2040
+ };
2041
+ var WhitespaceSplit_default = WhitespaceSplit;
2042
+ var FixedLength = class extends PreTokenizer_default {
2043
+ /**
2044
+ * @param config The configuration options for the pre-tokenizer.
2045
+ */
2046
+ constructor(config) {
2047
+ super();
2048
+ this.config = config;
2049
+ this._length = config.length;
2050
+ }
2051
+ /**
2052
+ * Pre-tokenizes the input text by splitting it into fixed-length tokens.
2053
+ * @param text The text to be pre-tokenized.
2054
+ * @returns An array of tokens produced by splitting the input text into fixed-length tokens.
2055
+ */
2056
+ pre_tokenize_text(text) {
2057
+ const tokens = [];
2058
+ for (let i = 0; i < text.length; i += this._length) {
2059
+ tokens.push(text.slice(i, i + this._length));
2060
+ }
2061
+ return tokens;
2062
+ }
2063
+ };
2064
+ var FixedLength_default = FixedLength;
2065
+ function create_pre_tokenizer(config) {
2066
+ if (config === null) return null;
2067
+ switch (config.type) {
2068
+ case "BertPreTokenizer":
2069
+ return new BertPreTokenizer_default();
2070
+ case "Sequence":
2071
+ return new Sequence_default2(config);
2072
+ case "Whitespace":
2073
+ return new Whitespace_default();
2074
+ case "WhitespaceSplit":
2075
+ return new WhitespaceSplit_default();
2076
+ case "Metaspace":
2077
+ return new Metaspace_default(config);
2078
+ case "ByteLevel":
2079
+ return new ByteLevel_default(config);
2080
+ case "Split":
2081
+ return new Split_default(config);
2082
+ case "Punctuation":
2083
+ return new Punctuation_default(config);
2084
+ case "Digits":
2085
+ return new Digits_default(config);
2086
+ case "Replace":
2087
+ return new Replace_default2(config);
2088
+ case "FixedLength":
2089
+ return new FixedLength_default(config);
2090
+ default:
2091
+ throw new Error(`Unknown PreTokenizer type: ${config.type}`);
2092
+ }
2093
+ }
2094
+ var create_pre_tokenizer_default = create_pre_tokenizer;
2095
+ var TokenizerModel = class extends Callable_default {
2096
+ /**
2097
+ * Creates a new instance of TokenizerModel.
2098
+ * @param config The configuration object for the TokenizerModel.
2099
+ */
2100
+ constructor(config) {
2101
+ super();
2102
+ this.config = config;
2103
+ this.vocab = [];
2104
+ this.tokens_to_ids = /* @__PURE__ */ new Map();
2105
+ this.unk_token_id = void 0;
2106
+ this.unk_token = void 0;
2107
+ this.end_of_word_suffix = void 0;
2108
+ this.fuse_unk = this.config.fuse_unk ?? false;
2109
+ }
2110
+ /**
2111
+ * Internal function to call the TokenizerModel instance.
2112
+ * @param tokens The tokens to encode.
2113
+ * @returns The encoded tokens.
2114
+ */
2115
+ _call(tokens) {
2116
+ let result = this.encode(tokens);
2117
+ if (this.fuse_unk) {
2118
+ result = fuse_unk(result, this.tokens_to_ids, this.unk_token_id);
2119
+ }
2120
+ return result;
2121
+ }
2122
+ };
2123
+ var TokenizerModel_default = TokenizerModel;
2124
+ var WordPieceTokenizer = class extends TokenizerModel_default {
2125
+ /**
2126
+ * @param config The configuration object.
2127
+ */
2128
+ constructor(config) {
2129
+ super(config);
2130
+ this.max_input_chars_per_word = 100;
2131
+ this.tokens_to_ids = object_to_map(config.vocab);
2132
+ this.unk_token_id = this.tokens_to_ids.get(config.unk_token);
2133
+ this.unk_token = config.unk_token;
2134
+ this.max_input_chars_per_word = config.max_input_chars_per_word ?? 100;
2135
+ this.vocab = new Array(this.tokens_to_ids.size);
2136
+ for (const [key, value] of this.tokens_to_ids) {
2137
+ this.vocab[value] = key;
2138
+ }
2139
+ }
2140
+ /**
2141
+ * Encodes an array of tokens using WordPiece encoding.
2142
+ * @param tokens The tokens to encode.
2143
+ * @returns An array of encoded tokens.
2144
+ */
2145
+ encode(tokens) {
2146
+ const output_tokens = [];
2147
+ for (const token of tokens) {
2148
+ const chars = [...token];
2149
+ if (chars.length > this.max_input_chars_per_word) {
2150
+ output_tokens.push(this.unk_token);
2151
+ continue;
2152
+ }
2153
+ let is_unknown = false;
2154
+ let start = 0;
2155
+ const sub_tokens = [];
2156
+ while (start < chars.length) {
2157
+ let end = chars.length;
2158
+ let current_substring = null;
2159
+ while (start < end) {
2160
+ let substr = chars.slice(start, end).join("");
2161
+ if (start > 0) {
2162
+ substr = this.config.continuing_subword_prefix + substr;
2163
+ }
2164
+ if (this.tokens_to_ids.has(substr)) {
2165
+ current_substring = substr;
2166
+ break;
2167
+ }
2168
+ --end;
2169
+ }
2170
+ if (current_substring === null) {
2171
+ is_unknown = true;
2172
+ break;
2173
+ }
2174
+ sub_tokens.push(current_substring);
2175
+ start = end;
2176
+ }
2177
+ if (is_unknown) {
2178
+ output_tokens.push(this.unk_token);
2179
+ } else {
2180
+ output_tokens.push(...sub_tokens);
2181
+ }
2182
+ }
2183
+ return output_tokens;
2184
+ }
2185
+ };
2186
+ var WordPiece_default = WordPieceTokenizer;
2187
+ var CharTrieNode = class _CharTrieNode {
2188
+ /**
2189
+ * Create a new CharTrieNode.
2190
+ * @param is_leaf Whether the node is a leaf node or not.
2191
+ * @param children A map containing the node's children, where the key is a character and the value is a `CharTrieNode`.
2192
+ */
2193
+ constructor(is_leaf, children) {
2194
+ this.is_leaf = is_leaf;
2195
+ this.children = children;
2196
+ }
2197
+ /**
2198
+ * Returns a new `CharTrieNode` instance with default values.
2199
+ * @returns A new `CharTrieNode` instance with `is_leaf` set to `false` and an empty `children` map.
2200
+ */
2201
+ static default() {
2202
+ return new _CharTrieNode(false, /* @__PURE__ */ new Map());
2203
+ }
2204
+ };
2205
+ var CharTrie = class {
2206
+ constructor() {
2207
+ this.root = CharTrieNode.default();
2208
+ }
2209
+ /**
2210
+ * Adds one or more `texts` to the trie.
2211
+ * @param texts The strings to add to the trie.
2212
+ */
2213
+ extend(texts) {
2214
+ for (const text of texts) {
2215
+ this.push(text);
2216
+ }
2217
+ }
2218
+ /**
2219
+ * Adds text to the trie.
2220
+ * @param text The string to add to the trie.
2221
+ */
2222
+ push(text) {
2223
+ let node = this.root;
2224
+ for (const ch of text) {
2225
+ let child = node.children.get(ch);
2226
+ if (child === void 0) {
2227
+ child = CharTrieNode.default();
2228
+ node.children.set(ch, child);
2229
+ }
2230
+ node = child;
2231
+ }
2232
+ node.is_leaf = true;
2233
+ }
2234
+ /**
2235
+ * Searches the trie for stored strings that match `chars` starting at `start`.
2236
+ * @param chars The input characters to search.
2237
+ * @param start The index to start searching from.
2238
+ * @yields Each stored string that is a prefix of `chars` starting at `start`.
2239
+ */
2240
+ *common_prefix_search(chars, start = 0) {
2241
+ let node = this.root;
2242
+ if (node === void 0) return;
2243
+ let prefix = "";
2244
+ for (let i = start; i < chars.length; ++i) {
2245
+ const ch = chars[i];
2246
+ prefix += ch;
2247
+ node = node.children.get(ch);
2248
+ if (node === void 0) return;
2249
+ if (node.is_leaf) {
2250
+ yield prefix;
2251
+ }
2252
+ }
2253
+ }
2254
+ };
2255
+ var CharTrie_default = CharTrie;
2256
+ var TokenLatticeNode = class _TokenLatticeNode {
2257
+ /**
2258
+ * Represents a node in a token lattice for a given sentence.
2259
+ * @param token_id The ID of the token associated with this node.
2260
+ * @param node_id The ID of this node.
2261
+ * @param pos The starting position of the token in the sentence.
2262
+ * @param length The length of the token.
2263
+ * @param score The score associated with the token.
2264
+ */
2265
+ constructor(token_id, node_id, pos, length, score) {
2266
+ this.token_id = token_id;
2267
+ this.node_id = node_id;
2268
+ this.pos = pos;
2269
+ this.length = length;
2270
+ this.score = score;
2271
+ this.prev = null;
2272
+ this.backtrace_score = 0;
2273
+ }
2274
+ /**
2275
+ * Returns a clone of this node.
2276
+ * @returns A clone of this node.
2277
+ */
2278
+ clone() {
2279
+ const n = new _TokenLatticeNode(
2280
+ this.token_id,
2281
+ this.node_id,
2282
+ this.pos,
2283
+ this.length,
2284
+ this.score
2285
+ );
2286
+ n.prev = this.prev;
2287
+ n.backtrace_score = this.backtrace_score;
2288
+ return n;
2289
+ }
2290
+ };
2291
+ var TokenLattice = class {
2292
+ /**
2293
+ * Creates a new TokenLattice instance.
2294
+ *
2295
+ * @param sentence The input sentence to be tokenized.
2296
+ * @param bos_token_id The beginning-of-sequence token ID.
2297
+ * @param eos_token_id The end-of-sequence token ID.
2298
+ */
2299
+ constructor(sentence, bos_token_id, eos_token_id) {
2300
+ this.chars = Array.from(sentence);
2301
+ this.len = this.chars.length;
2302
+ this.bos_token_id = bos_token_id;
2303
+ this.eos_token_id = eos_token_id;
2304
+ this.nodes = [];
2305
+ this.begin_nodes = Array.from(
2306
+ { length: this.len + 1 },
2307
+ () => []
2308
+ );
2309
+ this.end_nodes = Array.from({ length: this.len + 1 }, () => []);
2310
+ const bos = new TokenLatticeNode(this.bos_token_id ?? 0, 0, 0, 0, 0);
2311
+ const eos = new TokenLatticeNode(
2312
+ this.eos_token_id ?? 0,
2313
+ 1,
2314
+ this.len,
2315
+ 0,
2316
+ 0
2317
+ );
2318
+ this.nodes.push(bos.clone());
2319
+ this.nodes.push(eos.clone());
2320
+ this.begin_nodes[this.len].push(eos);
2321
+ this.end_nodes[0].push(bos);
2322
+ }
2323
+ /**
2324
+ * Inserts a new token node into the token lattice.
2325
+ *
2326
+ * @param pos The starting position of the token.
2327
+ * @param length The length of the token.
2328
+ * @param score The score of the token.
2329
+ * @param token_id The token ID of the token.
2330
+ */
2331
+ insert(pos, length, score, token_id) {
2332
+ const node_id = this.nodes.length;
2333
+ const node = new TokenLatticeNode(token_id, node_id, pos, length, score);
2334
+ this.begin_nodes[pos].push(node);
2335
+ this.end_nodes[pos + length].push(node);
2336
+ this.nodes.push(node);
2337
+ }
2338
+ /**
2339
+ * Implements the Viterbi algorithm to compute the most likely sequence of tokens.
2340
+ *
2341
+ * @returns The most likely sequence of tokens.
2342
+ */
2343
+ viterbi() {
2344
+ const len2 = this.len;
2345
+ let pos = 0;
2346
+ while (pos <= len2) {
2347
+ if (this.begin_nodes[pos].length == 0) {
2348
+ return [];
2349
+ }
2350
+ for (let rnode of this.begin_nodes[pos]) {
2351
+ rnode.prev = null;
2352
+ let best_score = 0;
2353
+ let best_node = null;
2354
+ for (let lnode of this.end_nodes[pos]) {
2355
+ const score = lnode.backtrace_score + rnode.score;
2356
+ if (best_node === null || score > best_score) {
2357
+ best_node = lnode.clone();
2358
+ best_score = score;
2359
+ }
2360
+ }
2361
+ if (best_node !== null) {
2362
+ rnode.prev = best_node;
2363
+ rnode.backtrace_score = best_score;
2364
+ } else {
2365
+ return [];
2366
+ }
2367
+ }
2368
+ ++pos;
2369
+ }
2370
+ const results = [];
2371
+ const root = this.begin_nodes[len2][0];
2372
+ const prev = root.prev;
2373
+ if (prev === null) {
2374
+ return [];
2375
+ }
2376
+ let node = prev.clone();
2377
+ while (node.prev !== null) {
2378
+ results.push(node.clone());
2379
+ const n = node.clone();
2380
+ node = n.prev.clone();
2381
+ }
2382
+ results.reverse();
2383
+ return results;
2384
+ }
2385
+ /**
2386
+ * Get the text piece for a given node.
2387
+ * @param node The node to get the piece for.
2388
+ * @returns The array of nodes representing the most likely sequence of tokens.
2389
+ */
2390
+ piece(node) {
2391
+ return this.chars.slice(node.pos, node.pos + node.length).join("");
2392
+ }
2393
+ /**
2394
+ * @returns The most likely sequence of tokens.
2395
+ */
2396
+ tokens() {
2397
+ const nodes = this.viterbi();
2398
+ return nodes.map((x) => this.piece(x));
2399
+ }
2400
+ /**
2401
+ * @returns The most likely sequence of token ids.
2402
+ */
2403
+ token_ids() {
2404
+ const nodes = this.viterbi();
2405
+ return nodes.map((x) => x.token_id);
2406
+ }
2407
+ };
2408
+ var TokenLattice_default = TokenLattice;
2409
+ function min(arr) {
2410
+ if (arr.length === 0) throw new Error("Array must not be empty");
2411
+ let min_value = arr[0];
2412
+ let index_of_min = 0;
2413
+ for (let i = 1; i < arr.length; ++i) {
2414
+ if (arr[i] < min_value) {
2415
+ min_value = arr[i];
2416
+ index_of_min = i;
2417
+ }
2418
+ }
2419
+ return [min_value, index_of_min];
2420
+ }
2421
+ var Unigram = class extends TokenizerModel_default {
2422
+ /**
2423
+ * Create a new Unigram tokenizer model.
2424
+ * @param config The configuration object for the Unigram model.
2425
+ * @param eos_token
2426
+ */
2427
+ constructor(config, eos_token) {
2428
+ super(config);
2429
+ const vocab_size = config.vocab.length;
2430
+ this.vocab = new Array(vocab_size);
2431
+ this.scores = new Array(vocab_size);
2432
+ for (let i = 0; i < vocab_size; ++i) {
2433
+ [this.vocab[i], this.scores[i]] = config.vocab[i];
2434
+ }
2435
+ this.unk_token_id = config.unk_id;
2436
+ this.unk_token = this.vocab[config.unk_id];
2437
+ this.tokens_to_ids = new Map(this.vocab.map((x, i) => [x, i]));
2438
+ this.bos_token = " ";
2439
+ this.bos_token_id = this.tokens_to_ids.get(this.bos_token);
2440
+ this.eos_token = eos_token;
2441
+ this.eos_token_id = this.tokens_to_ids.get(this.eos_token);
2442
+ this.unk_token = this.vocab[this.unk_token_id];
2443
+ this.min_score = min(this.scores)[0];
2444
+ this.unk_score = this.min_score - 10;
2445
+ this.scores[this.unk_token_id] = this.unk_score;
2446
+ this.trie = new CharTrie_default();
2447
+ this.trie.extend(this.vocab);
2448
+ this.fuse_unk = true;
2449
+ }
2450
+ /**
2451
+ * Populates lattice nodes.
2452
+ * @param lattice The token lattice to populate with nodes.
2453
+ */
2454
+ populate_nodes(lattice) {
2455
+ const chars = lattice.chars;
2456
+ const mblen = 1;
2457
+ let begin_pos = 0;
2458
+ while (begin_pos < chars.length) {
2459
+ let has_single_node = false;
2460
+ const prefixed_tokens = this.trie.common_prefix_search(chars, begin_pos);
2461
+ for (const token of prefixed_tokens) {
2462
+ const token_id = this.tokens_to_ids.get(token);
2463
+ const token_score = this.scores[token_id];
2464
+ const n = len(token);
2465
+ lattice.insert(begin_pos, n, token_score, token_id);
2466
+ if (!has_single_node && n === mblen) {
2467
+ has_single_node = true;
2468
+ }
2469
+ }
2470
+ if (!has_single_node) {
2471
+ lattice.insert(begin_pos, mblen, this.unk_score, this.unk_token_id);
2472
+ }
2473
+ begin_pos += mblen;
2474
+ }
2475
+ }
2476
+ /**
2477
+ * Encodes an array of tokens into an array of subtokens using the unigram model.
2478
+ *
2479
+ * @param normalized The normalized string.
2480
+ * @returns An array of subtokens obtained by encoding the input tokens using the unigram model.
2481
+ */
2482
+ tokenize(normalized) {
2483
+ const lattice = new TokenLattice_default(
2484
+ normalized,
2485
+ this.bos_token_id,
2486
+ this.eos_token_id
2487
+ );
2488
+ this.populate_nodes(lattice);
2489
+ return lattice.tokens();
2490
+ }
2491
+ /**
2492
+ * Encodes an array of tokens using Unigram encoding.
2493
+ * @param tokens The tokens to encode.
2494
+ * @returns An array of encoded tokens.
2495
+ */
2496
+ encode(tokens) {
2497
+ const to_return = [];
2498
+ for (const token of tokens) {
2499
+ const tokenized = this.tokenize(token);
2500
+ to_return.push(...tokenized);
2501
+ }
2502
+ return to_return;
2503
+ }
2504
+ };
2505
+ var Unigram_default = Unigram;
2506
+ var PriorityQueue = class {
2507
+ /**
2508
+ * Create a new PriorityQueue.
2509
+ * @param comparator Comparator function to determine priority. Defaults to a MaxHeap.
2510
+ * @param max_size Maximum size of the queue. Defaults to Infinity.
2511
+ */
2512
+ constructor(comparator = (a, b) => a > b, max_size = Infinity) {
2513
+ this._heap = [];
2514
+ this._comparator = comparator;
2515
+ this._max_size = max_size;
2516
+ }
2517
+ /**
2518
+ * The size of the queue
2519
+ */
2520
+ get size() {
2521
+ return this._heap.length;
2522
+ }
2523
+ /**
2524
+ * Check if the queue is empty.
2525
+ * @returns `true` if the queue is empty, `false` otherwise.
2526
+ */
2527
+ is_empty() {
2528
+ return this.size === 0;
2529
+ }
2530
+ /**
2531
+ * Return the element with the highest priority in the queue.
2532
+ * @returns The highest priority element in the queue.
2533
+ */
2534
+ peek() {
2535
+ return this._heap[0];
2536
+ }
2537
+ /**
2538
+ * Add one or more elements to the queue.
2539
+ * @param values The values to push into the queue.
2540
+ * @returns The new size of the queue.
2541
+ */
2542
+ push(...values) {
2543
+ return this.extend(values);
2544
+ }
2545
+ /**
2546
+ * Add multiple elements to the queue.
2547
+ * @param values The values to push into the queue.
2548
+ * @returns The new size of the queue.
2549
+ */
2550
+ extend(values) {
2551
+ for (const value of values) {
2552
+ if (this.size < this._max_size) {
2553
+ this._heap.push(value);
2554
+ this._sift_up();
2555
+ } else {
2556
+ const smallest = this._smallest();
2557
+ if (this._comparator(value, this._heap[smallest])) {
2558
+ this._heap[smallest] = value;
2559
+ this._sift_up_from(smallest);
2560
+ }
2561
+ }
2562
+ }
2563
+ return this.size;
2564
+ }
2565
+ /**
2566
+ * Remove and return the element with the highest priority in the queue.
2567
+ * @returns The element with the highest priority in the queue.
2568
+ */
2569
+ pop() {
2570
+ const popped_value = this.peek();
2571
+ const bottom = this.size - 1;
2572
+ if (bottom > 0) {
2573
+ this._swap(0, bottom);
2574
+ }
2575
+ this._heap.pop();
2576
+ this._sift_down();
2577
+ return popped_value;
2578
+ }
2579
+ /**
2580
+ * Replace the element with the highest priority in the queue with a new value.
2581
+ * @param value The new value.
2582
+ * @returns The replaced value.
2583
+ */
2584
+ replace(value) {
2585
+ const replaced_value = this.peek();
2586
+ this._heap[0] = value;
2587
+ this._sift_down();
2588
+ return replaced_value;
2589
+ }
2590
+ /**
2591
+ * Compute the index for the parent of the node at index `i`.
2592
+ * @param i The index of the node to get the parent of.
2593
+ * @returns The index of the parent node.
2594
+ * @private
2595
+ */
2596
+ _parent(i) {
2597
+ return (i + 1 >>> 1) - 1;
2598
+ }
2599
+ /**
2600
+ * Compute the index for the left child of the node at index `i`.
2601
+ * @param i The index of the node to get the left child of.
2602
+ * @returns The index of the left child.
2603
+ * @private
2604
+ */
2605
+ _left(i) {
2606
+ return (i << 1) + 1;
2607
+ }
2608
+ /**
2609
+ * Compute the index for the right child of the node at index `i`.
2610
+ * @param i The index of the node to get the right child of.
2611
+ * @returns The index of the right child.
2612
+ * @private
2613
+ */
2614
+ _right(i) {
2615
+ return i + 1 << 1;
2616
+ }
2617
+ /**
2618
+ * Check if the element at index `i` is greater than the element at index `j`.
2619
+ * @param i The index of the first element to compare.
2620
+ * @param j The index of the second element to compare.
2621
+ * @returns `true` if the element at index `i` is greater than the element at index `j`, `false` otherwise.
2622
+ * @private
2623
+ */
2624
+ _greater(i, j) {
2625
+ return this._comparator(this._heap[i], this._heap[j]);
2626
+ }
2627
+ /**
2628
+ * Swap the elements at indices `i` and `j`.
2629
+ * @param i The index of the first element to swap.
2630
+ * @param j The index of the second element to swap.
2631
+ * @private
2632
+ */
2633
+ _swap(i, j) {
2634
+ const temp = this._heap[i];
2635
+ this._heap[i] = this._heap[j];
2636
+ this._heap[j] = temp;
2637
+ }
2638
+ /**
2639
+ * Maintain the heap property by updating positions in the heap,
2640
+ * starting at the last element and moving up the heap.
2641
+ * @private
2642
+ */
2643
+ _sift_up() {
2644
+ this._sift_up_from(this.size - 1);
2645
+ }
2646
+ /**
2647
+ * Helper function to sift up from a given node.
2648
+ * @param node The index of the node to start sifting up from.
2649
+ */
2650
+ _sift_up_from(node) {
2651
+ while (node > 0 && this._greater(node, this._parent(node))) {
2652
+ this._swap(node, this._parent(node));
2653
+ node = this._parent(node);
2654
+ }
2655
+ }
2656
+ /**
2657
+ * Maintain the heap property by updating positions in the heap,
2658
+ * starting at the first element and moving down the heap.
2659
+ * @private
2660
+ */
2661
+ _sift_down() {
2662
+ let node = 0;
2663
+ while (this._left(node) < this.size && this._greater(this._left(node), node) || this._right(node) < this.size && this._greater(this._right(node), node)) {
2664
+ const max_child = this._right(node) < this.size && this._greater(this._right(node), this._left(node)) ? this._right(node) : this._left(node);
2665
+ this._swap(node, max_child);
2666
+ node = max_child;
2667
+ }
2668
+ }
2669
+ /**
2670
+ * Get the index of the smallest element in the heap. Since we use an array-based heap,
2671
+ * the index can be computed without needing to traverse the heap.
2672
+ * @private
2673
+ */
2674
+ _smallest() {
2675
+ return 2 ** Math.floor(Math.log2(this.size)) - 1;
2676
+ }
2677
+ };
2678
+ var PriorityQueue_default = PriorityQueue;
2679
+ var LRUCache = class {
2680
+ /**
2681
+ * Creates an LRUCache instance.
2682
+ * @param capacity The maximum number of items the cache can hold.
2683
+ */
2684
+ constructor(capacity) {
2685
+ this.capacity = capacity;
2686
+ this.cache = /* @__PURE__ */ new Map();
2687
+ }
2688
+ /**
2689
+ * Retrieves the value associated with the given key and marks the key as recently used.
2690
+ * @param key The key to retrieve.
2691
+ * @returns The value associated with the key, or undefined if the key does not exist.
2692
+ */
2693
+ get(key) {
2694
+ if (!this.cache.has(key)) return void 0;
2695
+ const value = this.cache.get(key);
2696
+ this.cache.delete(key);
2697
+ this.cache.set(key, value);
2698
+ return value;
2699
+ }
2700
+ /**
2701
+ * Inserts or updates the key-value pair in the cache.
2702
+ * If the key already exists, it is updated and marked as recently used.
2703
+ * If the cache exceeds its capacity, the least recently used item is evicted.
2704
+ * @param key The key to add or update.
2705
+ * @param value The value to associate with the key.
2706
+ */
2707
+ put(key, value) {
2708
+ if (this.cache.has(key)) {
2709
+ this.cache.delete(key);
2710
+ }
2711
+ this.cache.set(key, value);
2712
+ if (this.cache.size > this.capacity) {
2713
+ this.cache.delete(this.cache.keys().next().value);
2714
+ }
2715
+ }
2716
+ /**
2717
+ * Clears the cache.
2718
+ */
2719
+ clear() {
2720
+ this.cache.clear();
2721
+ }
2722
+ };
2723
+ var LRUCache_default = LRUCache;
2724
+ var BPE = class extends TokenizerModel_default {
2725
+ /**
2726
+ * Create a BPE instance.
2727
+ * @param config The configuration object for BPE.
2728
+ */
2729
+ constructor(config) {
2730
+ super(config);
2731
+ this.tokens_to_ids = object_to_map(config.vocab);
2732
+ this.unk_token_id = this.tokens_to_ids.get(config.unk_token);
2733
+ this.unk_token = config.unk_token;
2734
+ this.vocab = new Array(this.tokens_to_ids.size);
2735
+ for (const [key, value] of this.tokens_to_ids) {
2736
+ this.vocab[value] = key;
2737
+ }
2738
+ const use_new_merge_format = Array.isArray(config.merges[0]);
2739
+ this.merges = use_new_merge_format ? config.merges : config.merges.map(
2740
+ (x) => x.split(" ", 2)
2741
+ );
2742
+ this.bpe_ranks = new Map(this.merges.map((x, i) => [JSON.stringify(x), i]));
2743
+ this.end_of_word_suffix = config.end_of_word_suffix;
2744
+ this.continuing_subword_suffix = config.continuing_subword_suffix ?? null;
2745
+ this.byte_fallback = this.config.byte_fallback ?? false;
2746
+ if (this.byte_fallback) {
2747
+ this.text_encoder = new TextEncoder();
2748
+ }
2749
+ this.ignore_merges = this.config.ignore_merges ?? false;
2750
+ this.max_length_to_cache = 256;
2751
+ this.cache_capacity = 1e4;
2752
+ this.cache = new LRUCache_default(this.cache_capacity);
2753
+ }
2754
+ /**
2755
+ * Clears the cache.
2756
+ */
2757
+ clear_cache() {
2758
+ this.cache.clear();
2759
+ }
2760
+ /**
2761
+ * Apply Byte-Pair-Encoding (BPE) to a given token. Efficient heap-based priority
2762
+ * queue implementation adapted from https://github.com/belladoreai/llama-tokenizer-js.
2763
+ * @param token The token to encode.
2764
+ * @returns The BPE encoded tokens.
2765
+ */
2766
+ bpe(token) {
2767
+ if (token.length === 0) {
2768
+ return [];
2769
+ }
2770
+ const cached = this.cache.get(token);
2771
+ if (cached !== void 0) {
2772
+ return cached;
2773
+ }
2774
+ const word = Array.from(token);
2775
+ if (this.end_of_word_suffix) {
2776
+ word[word.length - 1] += this.end_of_word_suffix;
2777
+ }
2778
+ let result = [];
2779
+ if (word.length > 1) {
2780
+ const queue = new PriorityQueue_default((a, b) => a.score < b.score);
2781
+ let starting_node = {
2782
+ token: word[0],
2783
+ bias: 0,
2784
+ prev: null,
2785
+ next: null
2786
+ };
2787
+ let previous_node = starting_node;
2788
+ for (let i = 1; i < word.length; ++i) {
2789
+ const current_node = {
2790
+ bias: i / word.length,
2791
+ // Add fractional component to break ties
2792
+ token: word[i],
2793
+ prev: previous_node,
2794
+ next: null
2795
+ };
2796
+ previous_node.next = current_node;
2797
+ this.add_node(queue, previous_node);
2798
+ previous_node = current_node;
2799
+ }
2800
+ while (!queue.is_empty()) {
2801
+ const node = queue.pop();
2802
+ if (node.deleted || !node.next || node.next.deleted) continue;
2803
+ node.deleted = true;
2804
+ node.next.deleted = true;
2805
+ if (node.prev) {
2806
+ const new_previous_node = { ...node.prev };
2807
+ node.prev.deleted = true;
2808
+ node.prev = new_previous_node;
2809
+ if (new_previous_node.prev) {
2810
+ new_previous_node.prev.next = new_previous_node;
2811
+ } else {
2812
+ starting_node = new_previous_node;
2813
+ }
2814
+ }
2815
+ const merged = {
2816
+ token: node.token + node.next.token,
2817
+ bias: node.bias,
2818
+ prev: node.prev,
2819
+ next: node.next.next
2820
+ };
2821
+ if (merged.prev) {
2822
+ merged.prev.next = merged;
2823
+ this.add_node(queue, merged.prev);
2824
+ } else {
2825
+ starting_node = merged;
2826
+ }
2827
+ if (merged.next) {
2828
+ merged.next.prev = merged;
2829
+ this.add_node(queue, merged);
2830
+ }
2831
+ }
2832
+ for (let current_node = starting_node; current_node !== null; current_node = current_node.next) {
2833
+ result.push(current_node.token);
2834
+ }
2835
+ } else {
2836
+ result = word;
2837
+ }
2838
+ if (this.continuing_subword_suffix) {
2839
+ for (let i = 0; i < result.length - 1; ++i) {
2840
+ result[i] += this.continuing_subword_suffix;
2841
+ }
2842
+ }
2843
+ if (token.length < this.max_length_to_cache) {
2844
+ this.cache.put(token, result);
2845
+ }
2846
+ return result;
2847
+ }
2848
+ /**
2849
+ * Helper function to add a node to the priority queue.
2850
+ * @param queue
2851
+ * @param node
2852
+ */
2853
+ add_node(queue, node) {
2854
+ const rank = this.bpe_ranks.get(
2855
+ JSON.stringify([node.token, node.next.token])
2856
+ );
2857
+ if (rank !== void 0) {
2858
+ node.score = rank + node.bias;
2859
+ queue.push(node);
2860
+ }
2861
+ }
2862
+ /**
2863
+ * Encodes the input sequence of tokens using the BPE algorithm and returns the resulting subword tokens.
2864
+ * @param tokens The input sequence of tokens to encode.
2865
+ * @returns The resulting subword tokens after applying the BPE algorithm to the input sequence of tokens.
2866
+ */
2867
+ encode(tokens) {
2868
+ const output_tokens = [];
2869
+ for (const token of tokens) {
2870
+ if (this.ignore_merges && this.tokens_to_ids.has(token)) {
2871
+ output_tokens.push(token);
2872
+ continue;
2873
+ }
2874
+ const bpe_token_list = this.bpe(token);
2875
+ for (const t of bpe_token_list) {
2876
+ if (this.tokens_to_ids.has(t)) {
2877
+ output_tokens.push(t);
2878
+ } else if (this.byte_fallback) {
2879
+ const byte_tokens = Array.from(this.text_encoder.encode(t)).map(
2880
+ (x) => `<0x${x.toString(16).toUpperCase().padStart(2, "0")}>`
2881
+ );
2882
+ if (byte_tokens.every((x) => this.tokens_to_ids.has(x))) {
2883
+ output_tokens.push(...byte_tokens);
2884
+ } else if (this.unk_token != null) {
2885
+ output_tokens.push(this.unk_token);
2886
+ }
2887
+ } else if (this.unk_token != null) {
2888
+ output_tokens.push(this.unk_token);
2889
+ }
2890
+ }
2891
+ }
2892
+ return output_tokens;
2893
+ }
2894
+ };
2895
+ var BPE_default = BPE;
2896
+ var Legacy = class extends TokenizerModel_default {
2897
+ /**
2898
+ * Create a Legacy tokenizer model instance.
2899
+ * @param config The configuration object for Legacy tokenizer model.
2900
+ * @param more_config Additional configuration object for the Legacy tokenizer model.
2901
+ */
2902
+ constructor(config, more_config) {
2903
+ super(config);
2904
+ const vocab = config.vocab;
2905
+ this.tokens_to_ids = object_to_map(
2906
+ more_config.target_lang ? vocab[more_config.target_lang] : vocab
2907
+ );
2908
+ this.bos_token = more_config.bos_token;
2909
+ this.bos_token_id = this.tokens_to_ids.get(this.bos_token);
2910
+ this.eos_token = more_config.eos_token;
2911
+ this.eos_token_id = this.tokens_to_ids.get(this.eos_token);
2912
+ this.pad_token = more_config.pad_token;
2913
+ this.pad_token_id = this.tokens_to_ids.get(this.pad_token);
2914
+ this.unk_token = more_config.unk_token;
2915
+ this.unk_token_id = this.tokens_to_ids.get(this.unk_token);
2916
+ this.vocab = new Array(this.tokens_to_ids.size);
2917
+ for (const [key, value] of this.tokens_to_ids) {
2918
+ this.vocab[value] = key;
2919
+ }
2920
+ }
2921
+ encode(tokens) {
2922
+ return tokens;
2923
+ }
2924
+ };
2925
+ var Legacy_default = Legacy;
2926
+ function create_tokenizer_model(model_config, config) {
2927
+ switch (model_config.type) {
2928
+ case "WordPiece":
2929
+ return new WordPiece_default(model_config);
2930
+ case "Unigram":
2931
+ return new Unigram_default(model_config, config.eos_token);
2932
+ case "BPE":
2933
+ return new BPE_default(model_config);
2934
+ default:
2935
+ if (model_config.vocab) {
2936
+ if (Array.isArray(model_config.vocab)) {
2937
+ return new Unigram_default(model_config, config.eos_token);
2938
+ } else if (Object.hasOwn(model_config, "continuing_subword_prefix") && Object.hasOwn(model_config, "unk_token")) {
2939
+ if (Object.hasOwn(model_config, "merges")) {
2940
+ return new BPE_default(model_config);
2941
+ } else {
2942
+ return new WordPiece_default(model_config);
2943
+ }
2944
+ } else {
2945
+ return new Legacy_default(model_config, {
2946
+ target_lang: config.target_lang,
2947
+ bos_token: config.bos_token,
2948
+ eos_token: config.eos_token,
2949
+ pad_token: config.pad_token,
2950
+ unk_token: config.unk_token
2951
+ });
2952
+ }
2953
+ }
2954
+ throw new Error(
2955
+ `Unknown TokenizerModel type: ${model_config?.type}`
2956
+ );
2957
+ }
2958
+ }
2959
+ var create_tokenizer_model_default = create_tokenizer_model;
2960
+ var PostProcessor = class extends Callable_default {
2961
+ /**
2962
+ * @param config The configuration for the post-processor.
2963
+ */
2964
+ constructor(config) {
2965
+ super();
2966
+ this.config = config;
2967
+ }
2968
+ /**
2969
+ * Alias for {@link PostProcessor#post_process}.
2970
+ * @param tokens The text or array of texts to post-process.
2971
+ * @param args Additional arguments required by the post-processing logic.
2972
+ * @returns The post-processed tokens.
2973
+ */
2974
+ _call(tokens, ...args) {
2975
+ return this.post_process(tokens, ...args);
2976
+ }
2977
+ };
2978
+ var PostProcessor_default = PostProcessor;
2979
+ var TemplateProcessing = class extends PostProcessor_default {
2980
+ /**
2981
+ * Replaces special tokens in the template with actual tokens.
2982
+ * @param tokens The list of tokens for the first sequence.
2983
+ * @param tokens_pair The list of tokens for the second sequence (optional).
2984
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
2985
+ * @returns An object containing the list of tokens with the special tokens replaced with actual tokens.
2986
+ */
2987
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
2988
+ const type = tokens_pair === null ? this.config.single : this.config.pair;
2989
+ let processed_tokens = [];
2990
+ let types = [];
2991
+ for (const item of type) {
2992
+ if ("SpecialToken" in item) {
2993
+ if (add_special_tokens) {
2994
+ processed_tokens.push(item.SpecialToken.id);
2995
+ types.push(item.SpecialToken.type_id);
2996
+ }
2997
+ } else if ("Sequence" in item) {
2998
+ if (item.Sequence.id === "A") {
2999
+ processed_tokens = merge_arrays(processed_tokens, tokens);
3000
+ types = merge_arrays(
3001
+ types,
3002
+ new Array(tokens.length).fill(item.Sequence.type_id)
3003
+ );
3004
+ } else if (item.Sequence.id === "B") {
3005
+ processed_tokens = merge_arrays(processed_tokens, tokens_pair);
3006
+ types = merge_arrays(
3007
+ types,
3008
+ new Array(tokens_pair.length).fill(item.Sequence.type_id)
3009
+ );
3010
+ }
3011
+ }
3012
+ }
3013
+ return { tokens: processed_tokens, token_type_ids: types };
3014
+ }
3015
+ };
3016
+ var TemplateProcessing_default = TemplateProcessing;
3017
+ var ByteLevel2 = class extends PostProcessor_default {
3018
+ /**
3019
+ * Post process the given tokens.
3020
+ * @param tokens The list of tokens for the first sequence.
3021
+ * @param tokens_pair The list of tokens for the second sequence (optional).
3022
+ * @returns An object containing the post-processed tokens.
3023
+ */
3024
+ post_process(tokens, tokens_pair = null) {
3025
+ return { tokens, tokens_pair };
3026
+ }
3027
+ };
3028
+ var ByteLevel_default2 = ByteLevel2;
3029
+ var BertProcessing = class extends PostProcessor_default {
3030
+ /**
3031
+ * @param config The configuration for the post-processor.
3032
+ * @param config.cls The special tokens to add to the beginning of the input.
3033
+ * @param config.sep The special tokens to add to the end of the input.
3034
+ */
3035
+ constructor(config) {
3036
+ super(config);
3037
+ this.sep = config.sep;
3038
+ this.cls = config.cls;
3039
+ }
3040
+ /**
3041
+ * Adds the special tokens to the beginning and end of the input.
3042
+ * @param tokens The input tokens.
3043
+ * @param tokens_pair An optional second set of input tokens.
3044
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
3045
+ * @returns The post-processed tokens with the special tokens added to the beginning and end.
3046
+ */
3047
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
3048
+ if (add_special_tokens) {
3049
+ tokens = merge_arrays([this.cls[0]], tokens, [this.sep[0]]);
3050
+ }
3051
+ let token_type_ids = new Array(tokens.length).fill(0);
3052
+ if (tokens_pair) {
3053
+ const middle = [];
3054
+ const after = add_special_tokens ? [this.sep[0]] : [];
3055
+ tokens = merge_arrays(tokens, middle, tokens_pair, after);
3056
+ token_type_ids = merge_arrays(
3057
+ token_type_ids,
3058
+ new Array(tokens_pair.length + middle.length + after.length).fill(1)
3059
+ );
3060
+ }
3061
+ return { tokens, token_type_ids };
3062
+ }
3063
+ };
3064
+ var BertProcessing_default = BertProcessing;
3065
+ var RobertaProcessing = class extends PostProcessor_default {
3066
+ /**
3067
+ * @param config The configuration for the post-processor.
3068
+ * @param config.cls The special tokens to add to the beginning of the input.
3069
+ * @param config.sep The special tokens to add to the end of the input.
3070
+ */
3071
+ constructor(config) {
3072
+ super(config);
3073
+ this.sep = config.sep;
3074
+ this.cls = config.cls;
3075
+ }
3076
+ /**
3077
+ * Adds the special tokens to the beginning and end of the input.
3078
+ * @param tokens The input tokens.
3079
+ * @param tokens_pair An optional second set of input tokens.
3080
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
3081
+ * @returns The post-processed tokens with the special tokens added to the beginning and end.
3082
+ */
3083
+ post_process(tokens, tokens_pair, add_special_tokens = true) {
3084
+ if (add_special_tokens) {
3085
+ tokens = merge_arrays([this.cls[0]], tokens, [this.sep[0]]);
3086
+ }
3087
+ let token_type_ids = new Array(tokens.length).fill(0);
3088
+ if (tokens_pair) {
3089
+ const middle = add_special_tokens ? [this.sep[0]] : [];
3090
+ const after = add_special_tokens ? [this.sep[0]] : [];
3091
+ tokens = merge_arrays(tokens, middle, tokens_pair, after);
3092
+ token_type_ids = merge_arrays(
3093
+ token_type_ids,
3094
+ new Array(tokens_pair.length + middle.length + after.length).fill(1)
3095
+ );
3096
+ }
3097
+ return { tokens, token_type_ids };
3098
+ }
3099
+ };
3100
+ var RobertaProcessing_default = RobertaProcessing;
3101
+ var Sequence3 = class extends PostProcessor_default {
3102
+ /**
3103
+ * Creates a new instance of Sequence post-processor.
3104
+ * @param config The configuration object.
3105
+ */
3106
+ constructor(config) {
3107
+ super(config);
3108
+ this.processors = (config.processors ?? []).map((x) => create_post_processor_default(x));
3109
+ }
3110
+ /**
3111
+ * Post process the given tokens.
3112
+ * @param tokens The list of tokens for the first sequence.
3113
+ * @param tokens_pair The list of tokens for the second sequence (optional).
3114
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
3115
+ * @returns An object containing the post-processed tokens.
3116
+ */
3117
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
3118
+ let processed_tokens = { tokens, tokens_pair };
3119
+ for (const processor of this.processors) {
3120
+ processed_tokens = processor.post_process(
3121
+ processed_tokens.tokens,
3122
+ processed_tokens.tokens_pair,
3123
+ add_special_tokens
3124
+ );
3125
+ }
3126
+ return processed_tokens;
3127
+ }
3128
+ };
3129
+ var Sequence_default3 = Sequence3;
3130
+ function create_post_processor(config) {
3131
+ if (config === null) return null;
3132
+ switch (config.type) {
3133
+ case "TemplateProcessing":
3134
+ return new TemplateProcessing_default(config);
3135
+ case "ByteLevel":
3136
+ return new ByteLevel_default2(config);
3137
+ case "BertProcessing":
3138
+ return new BertProcessing_default(config);
3139
+ case "RobertaProcessing":
3140
+ return new RobertaProcessing_default(config);
3141
+ case "Sequence":
3142
+ return new Sequence_default3(config);
3143
+ default:
3144
+ throw new Error(`Unknown PostProcessor type: ${config.type}`);
3145
+ }
3146
+ }
3147
+ var create_post_processor_default = create_post_processor;
3148
+ var Decoder = class extends Callable_default {
3149
+ /**
3150
+ * Creates an instance of `Decoder`.
3151
+ * @param config The configuration object.
3152
+ **/
3153
+ constructor(config) {
3154
+ super();
3155
+ this.config = config;
3156
+ this.added_tokens = [];
3157
+ this.end_of_word_suffix = null;
3158
+ this.trim_offsets = "trim_offsets" in config ? config.trim_offsets : false;
3159
+ }
3160
+ /**
3161
+ * Calls the `decode` method.
3162
+ *
3163
+ * @param tokens The list of tokens.
3164
+ * @returns The decoded string.
3165
+ */
3166
+ _call(tokens) {
3167
+ return this.decode(tokens);
3168
+ }
3169
+ /**
3170
+ * Decodes a list of tokens.
3171
+ * @param tokens The list of tokens.
3172
+ * @returns The decoded string.
3173
+ */
3174
+ decode(tokens) {
3175
+ return this.decode_chain(tokens).join("");
3176
+ }
3177
+ };
3178
+ var Decoder_default = Decoder;
3179
+ var ByteLevel3 = class extends Decoder_default {
3180
+ /**
3181
+ * Create a `ByteLevelDecoder` object.
3182
+ */
3183
+ constructor(config) {
3184
+ super(config);
3185
+ this.byte_decoder = UNICODE_TO_BYTES;
3186
+ this.text_decoder = new TextDecoder("utf-8", {
3187
+ fatal: false,
3188
+ // eslint-disable-next-line @typescript-eslint/naming-convention
3189
+ ignoreBOM: true
3190
+ });
3191
+ this.end_of_word_suffix = null;
3192
+ }
3193
+ /**
3194
+ * Convert an array of tokens to string by decoding each byte.
3195
+ * @param tokens Array of tokens to be decoded.
3196
+ * @returns The decoded string.
3197
+ */
3198
+ convert_tokens_to_string(tokens) {
3199
+ const text = tokens.join("");
3200
+ const byte_array = new Uint8Array(
3201
+ [...text].map((c) => this.byte_decoder[c])
3202
+ );
3203
+ return this.text_decoder.decode(byte_array);
3204
+ }
3205
+ decode_chain(tokens) {
3206
+ const sub_texts = [];
3207
+ let current_sub_text = [];
3208
+ for (const token of tokens) {
3209
+ if (this.added_tokens.find((x) => x.content === token) !== void 0) {
3210
+ if (current_sub_text.length > 0) {
3211
+ sub_texts.push(this.convert_tokens_to_string(current_sub_text));
3212
+ current_sub_text = [];
3213
+ }
3214
+ sub_texts.push(token);
3215
+ } else {
3216
+ current_sub_text.push(token);
3217
+ }
3218
+ }
3219
+ if (current_sub_text.length > 0) {
3220
+ sub_texts.push(this.convert_tokens_to_string(current_sub_text));
3221
+ }
3222
+ return sub_texts;
3223
+ }
3224
+ };
3225
+ var ByteLevel_default3 = ByteLevel3;
3226
+ var WordPiece = class extends Decoder_default {
3227
+ /**
3228
+ * Creates a new instance of WordPieceDecoder.
3229
+ * @param config The configuration object.
3230
+ */
3231
+ constructor(config) {
3232
+ super(config);
3233
+ this.cleanup = config.cleanup;
3234
+ }
3235
+ decode_chain(tokens) {
3236
+ return tokens.map((token, i) => {
3237
+ if (i !== 0) {
3238
+ const prefix = this.config.prefix;
3239
+ if (prefix && token.startsWith(prefix)) {
3240
+ token = token.replace(prefix, "");
3241
+ } else {
3242
+ token = " " + token;
3243
+ }
3244
+ }
3245
+ if (this.cleanup) {
3246
+ token = clean_up_tokenization(token);
3247
+ }
3248
+ return token;
3249
+ });
3250
+ }
3251
+ };
3252
+ var WordPiece_default2 = WordPiece;
3253
+ var Metaspace2 = class extends Decoder_default {
3254
+ /**
3255
+ * Constructs a new MetaspaceDecoder object.
3256
+ * @param config The configuration object for the MetaspaceDecoder.
3257
+ */
3258
+ constructor(config) {
3259
+ super(config);
3260
+ this.replacement = config.replacement ?? "\u2581";
3261
+ }
3262
+ decode_chain(tokens) {
3263
+ const result = [];
3264
+ for (let i = 0; i < tokens.length; ++i) {
3265
+ let normalized = tokens[i].replaceAll(this.replacement, " ");
3266
+ if (i == 0 && normalized.startsWith(" ")) {
3267
+ normalized = normalized.substring(1);
3268
+ }
3269
+ result.push(normalized);
3270
+ }
3271
+ return result;
3272
+ }
3273
+ };
3274
+ var Metaspace_default2 = Metaspace2;
3275
+ var BPE2 = class extends Decoder_default {
3276
+ constructor(config) {
3277
+ super(config);
3278
+ this.suffix = config.suffix ?? "";
3279
+ }
3280
+ decode_chain(tokens) {
3281
+ return tokens.map((token, i) => {
3282
+ return token.replaceAll(this.suffix, i === tokens.length - 1 ? "" : " ");
3283
+ });
3284
+ }
3285
+ };
3286
+ var BPE_default2 = BPE2;
3287
+ var CTC = class extends Decoder_default {
3288
+ constructor(config) {
3289
+ super(config);
3290
+ this.pad_token = config.pad_token ?? "";
3291
+ this.word_delimiter_token = config.word_delimiter_token ?? "";
3292
+ this.cleanup = config.cleanup;
3293
+ }
3294
+ /**
3295
+ * Converts a connectionist-temporal-classification (CTC) output tokens into a single string.
3296
+ * @param tokens Array of tokens to be decoded.
3297
+ * @returns The decoded string.
3298
+ */
3299
+ convert_tokens_to_string(tokens) {
3300
+ if (tokens.length === 0) return "";
3301
+ const grouped_tokens = [tokens[0]];
3302
+ for (let i = 1; i < tokens.length; ++i) {
3303
+ if (tokens[i] !== grouped_tokens.at(-1)) {
3304
+ grouped_tokens.push(tokens[i]);
3305
+ }
3306
+ }
3307
+ const filtered_tokens = grouped_tokens.filter(
3308
+ (token) => token !== this.pad_token
3309
+ );
3310
+ let text = filtered_tokens.join("");
3311
+ if (this.cleanup) {
3312
+ text = clean_up_tokenization(text).replaceAll(this.word_delimiter_token, " ").trim();
3313
+ }
3314
+ return text;
3315
+ }
3316
+ decode_chain(tokens) {
3317
+ return [this.convert_tokens_to_string(tokens)];
3318
+ }
3319
+ };
3320
+ var CTC_default = CTC;
3321
+ var Sequence4 = class extends Decoder_default {
3322
+ /**
3323
+ * Creates a new instance of DecoderSequence.
3324
+ * @param config The configuration object.
3325
+ */
3326
+ constructor(config) {
3327
+ super(config);
3328
+ this.decoders = (config.decoders ?? []).map((x) => create_decoder_default(x));
3329
+ }
3330
+ decode_chain(tokens) {
3331
+ return this.decoders.reduce((toks, decoder) => {
3332
+ return decoder.decode_chain(toks);
3333
+ }, tokens);
3334
+ }
3335
+ };
3336
+ var Sequence_default4 = Sequence4;
3337
+ var Replace3 = class extends Decoder_default {
3338
+ /**
3339
+ * @param config The configuration object for the decoder.
3340
+ */
3341
+ constructor(config) {
3342
+ super(config);
3343
+ this.pattern = create_pattern(this.config.pattern);
3344
+ }
3345
+ decode_chain(tokens) {
3346
+ const content = this.config.content ?? "";
3347
+ const pattern = this.pattern;
3348
+ return pattern === null ? tokens : tokens.map((token) => token.replaceAll(pattern, content));
3349
+ }
3350
+ };
3351
+ var Replace_default3 = Replace3;
3352
+ var Fuse = class extends Decoder_default {
3353
+ decode_chain(tokens) {
3354
+ return [tokens.join("")];
3355
+ }
3356
+ };
3357
+ var Fuse_default = Fuse;
3358
+ var Strip2 = class extends Decoder_default {
3359
+ constructor(config) {
3360
+ super(config);
3361
+ this.content = config.content ?? "";
3362
+ this.start = config.start ?? 0;
3363
+ this.stop = config.stop ?? 0;
3364
+ }
3365
+ decode_chain(tokens) {
3366
+ return tokens.map((token) => {
3367
+ let start_cut = 0;
3368
+ for (let i = 0; i < this.start; ++i) {
3369
+ if (token[i] === this.content) {
3370
+ start_cut = i + 1;
3371
+ continue;
3372
+ } else {
3373
+ break;
3374
+ }
3375
+ }
3376
+ let stop_cut = token.length;
3377
+ for (let i = 0; i < this.stop; ++i) {
3378
+ const index = token.length - i - 1;
3379
+ if (token[index] === this.content) {
3380
+ stop_cut = index;
3381
+ continue;
3382
+ } else {
3383
+ break;
3384
+ }
3385
+ }
3386
+ return token.slice(start_cut, stop_cut);
3387
+ });
3388
+ }
3389
+ };
3390
+ var Strip_default2 = Strip2;
3391
+ var ByteFallback = class extends Decoder_default {
3392
+ constructor(config) {
3393
+ super(config);
3394
+ this.text_decoder = new TextDecoder();
3395
+ }
3396
+ decode_chain(tokens) {
3397
+ const new_tokens = [];
3398
+ let previous_byte_tokens = [];
3399
+ for (const token of tokens) {
3400
+ let bytes = null;
3401
+ if (token.length === 6 && token.startsWith("<0x") && token.endsWith(">")) {
3402
+ const byte = parseInt(token.slice(3, 5), 16);
3403
+ if (!isNaN(byte)) {
3404
+ bytes = byte;
3405
+ }
3406
+ }
3407
+ if (bytes !== null) {
3408
+ previous_byte_tokens.push(bytes);
3409
+ } else {
3410
+ if (previous_byte_tokens.length > 0) {
3411
+ const string = this.text_decoder.decode(
3412
+ Uint8Array.from(previous_byte_tokens)
3413
+ );
3414
+ new_tokens.push(string);
3415
+ previous_byte_tokens = [];
3416
+ }
3417
+ new_tokens.push(token);
3418
+ }
3419
+ }
3420
+ if (previous_byte_tokens.length > 0) {
3421
+ const string = this.text_decoder.decode(
3422
+ Uint8Array.from(previous_byte_tokens)
3423
+ );
3424
+ new_tokens.push(string);
3425
+ previous_byte_tokens = [];
3426
+ }
3427
+ return new_tokens;
3428
+ }
3429
+ };
3430
+ var ByteFallback_default = ByteFallback;
3431
+ function create_decoder(config) {
3432
+ if (config === null) return null;
3433
+ switch (config.type) {
3434
+ case "ByteLevel":
3435
+ return new ByteLevel_default3(config);
3436
+ case "WordPiece":
3437
+ return new WordPiece_default2(config);
3438
+ case "Metaspace":
3439
+ return new Metaspace_default2(config);
3440
+ case "BPEDecoder":
3441
+ return new BPE_default2(config);
3442
+ case "CTC":
3443
+ return new CTC_default(config);
3444
+ case "Sequence":
3445
+ return new Sequence_default4(config);
3446
+ case "Replace":
3447
+ return new Replace_default3(config);
3448
+ case "Fuse":
3449
+ return new Fuse_default(config);
3450
+ case "Strip":
3451
+ return new Strip_default2(config);
3452
+ case "ByteFallback":
3453
+ return new ByteFallback_default(config);
3454
+ default:
3455
+ throw new Error(`Unknown Decoder type: ${config.type}`);
3456
+ }
3457
+ }
3458
+ var create_decoder_default = create_decoder;
3459
+ var Tokenizer = class {
3460
+ constructor(tokenizer, config) {
3461
+ const tokenizer_error = validate_object(tokenizer, "Tokenizer", [
3462
+ "model",
3463
+ "decoder",
3464
+ "post_processor",
3465
+ "pre_tokenizer",
3466
+ "normalizer"
3467
+ ]);
3468
+ if (tokenizer_error) {
3469
+ throw new Error(tokenizer_error);
3470
+ }
3471
+ const config_error = validate_object(config, "Config");
3472
+ if (config_error) {
3473
+ throw new Error(config_error);
3474
+ }
3475
+ this.tokenizer = tokenizer;
3476
+ this.config = config;
3477
+ this.normalizer = create_normalizer_default(this.tokenizer.normalizer);
3478
+ this.pre_tokenizer = create_pre_tokenizer_default(this.tokenizer.pre_tokenizer);
3479
+ this.model = create_tokenizer_model_default(this.tokenizer.model, this.config);
3480
+ this.post_processor = create_post_processor_default(this.tokenizer.post_processor);
3481
+ this.decoder = create_decoder_default(this.tokenizer.decoder);
3482
+ this.special_tokens = [];
3483
+ this.all_special_ids = [];
3484
+ this.added_tokens = [];
3485
+ const unnormalized_contents = [];
3486
+ const normalized_contents = [];
3487
+ this.added_tokens_map = /* @__PURE__ */ new Map();
3488
+ for (const added_token of this.tokenizer.added_tokens) {
3489
+ const token = new AddedToken_default(added_token);
3490
+ this.added_tokens.push(token);
3491
+ this.model.tokens_to_ids.set(token.content, token.id);
3492
+ this.model.vocab[token.id] = token.content;
3493
+ if (token.special) {
3494
+ this.special_tokens.push(token.content);
3495
+ this.all_special_ids.push(token.id);
3496
+ }
3497
+ this.added_tokens_map.set(token.content, token);
3498
+ if (token.normalized && this.normalizer !== null) {
3499
+ const normalized_content = this.normalizer(token.content);
3500
+ normalized_contents.push(normalized_content);
3501
+ this.added_tokens_map.set(normalized_content, token);
3502
+ } else {
3503
+ unnormalized_contents.push(token.content);
3504
+ }
3505
+ }
3506
+ (this.config.additional_special_tokens ?? []).forEach((token) => {
3507
+ if (!this.special_tokens.includes(token)) this.special_tokens.push(token);
3508
+ });
3509
+ if (this.decoder) {
3510
+ this.decoder.added_tokens = this.added_tokens;
3511
+ this.decoder.end_of_word_suffix = this.model.end_of_word_suffix;
3512
+ }
3513
+ this.splitter_unnormalized = new DictionarySplitter_default(unnormalized_contents);
3514
+ this.splitter_normalized = new DictionarySplitter_default(normalized_contents);
3515
+ this.remove_space = this.config.remove_space;
3516
+ this.clean_up_tokenization_spaces = this.config.clean_up_tokenization_spaces ?? true;
3517
+ this.do_lowercase_and_remove_accent = this.config.do_lowercase_and_remove_accent ?? false;
3518
+ }
3519
+ // Implementation
3520
+ encode(text, {
3521
+ text_pair = null,
3522
+ add_special_tokens = true,
3523
+ return_token_type_ids = null
3524
+ } = {}) {
3525
+ const { tokens, token_type_ids } = this.tokenize_helper(text, {
3526
+ text_pair,
3527
+ add_special_tokens
3528
+ });
3529
+ const input_ids = tokens.map(
3530
+ (t) => this.added_tokens_map.get(t)?.id ?? this.model.tokens_to_ids.get(t) ?? this.model.unk_token_id
3531
+ );
3532
+ const result = {
3533
+ ids: input_ids,
3534
+ tokens,
3535
+ attention_mask: new Array(input_ids.length).fill(1)
3536
+ };
3537
+ if (return_token_type_ids && token_type_ids) {
3538
+ result.token_type_ids = token_type_ids;
3539
+ }
3540
+ return result;
3541
+ }
3542
+ decode(token_ids, options = {}) {
3543
+ if (!Array.isArray(token_ids) || token_ids.length === 0 || !is_integral_number(token_ids[0])) {
3544
+ throw Error("token_ids must be a non-empty array of integers.");
3545
+ }
3546
+ let tokens = token_ids.map(
3547
+ (i) => this.model.vocab[Number(i)] ?? this.model.unk_token
3548
+ );
3549
+ if (options.skip_special_tokens) {
3550
+ tokens = tokens.filter((x) => !this.special_tokens.includes(x));
3551
+ }
3552
+ let decoded = this.decoder ? this.decoder(tokens) : tokens.join(" ");
3553
+ if (this.decoder && this.decoder.end_of_word_suffix) {
3554
+ decoded = decoded.replaceAll(this.decoder.end_of_word_suffix, " ");
3555
+ if (options.skip_special_tokens) {
3556
+ decoded = decoded.trim();
3557
+ }
3558
+ }
3559
+ if (options.clean_up_tokenization_spaces ?? this.clean_up_tokenization_spaces) {
3560
+ decoded = clean_up_tokenization(decoded);
3561
+ }
3562
+ return decoded;
3563
+ }
3564
+ /**
3565
+ * Converts a string into a sequence of tokens.
3566
+ * @param text The sequence to be encoded.
3567
+ * @param options An optional object containing the following properties:
3568
+ * @returns The list of tokens.
3569
+ */
3570
+ tokenize(text, { text_pair = null, add_special_tokens = false } = {}) {
3571
+ return this.tokenize_helper(text, { text_pair, add_special_tokens }).tokens;
3572
+ }
3573
+ encode_text(text) {
3574
+ if (text === null) {
3575
+ return null;
3576
+ }
3577
+ const sections = this.splitter_unnormalized.split(text);
3578
+ sections.forEach((section, i) => {
3579
+ const added_token = this.added_tokens_map.get(section);
3580
+ if (added_token) {
3581
+ if (added_token.lstrip && i > 0) {
3582
+ sections[i - 1] = sections[i - 1].trimEnd();
3583
+ }
3584
+ if (added_token.rstrip && i < sections.length - 1) {
3585
+ sections[i + 1] = sections[i + 1].trimStart();
3586
+ }
3587
+ }
3588
+ });
3589
+ return sections.flatMap((processed_text, section_index) => {
3590
+ if (processed_text.length === 0) {
3591
+ return [];
3592
+ }
3593
+ if (this.added_tokens_map.has(processed_text)) {
3594
+ return [processed_text];
3595
+ }
3596
+ if (this.remove_space === true) {
3597
+ processed_text = processed_text.trim().split(/\s+/).join(" ");
3598
+ }
3599
+ if (this.do_lowercase_and_remove_accent) {
3600
+ processed_text = lowercase_and_remove_accents(processed_text);
3601
+ }
3602
+ if (this.normalizer !== null) {
3603
+ processed_text = this.normalizer(processed_text);
3604
+ }
3605
+ if (processed_text.length === 0) {
3606
+ return [];
3607
+ }
3608
+ const subsections = this.splitter_normalized.split(processed_text);
3609
+ subsections.forEach((subsection, j) => {
3610
+ const added_token = this.added_tokens_map.get(subsection);
3611
+ if (added_token) {
3612
+ if (added_token.lstrip && j > 0) {
3613
+ subsections[j - 1] = subsections[j - 1].trimEnd();
3614
+ }
3615
+ if (added_token.rstrip && j < subsections.length - 1) {
3616
+ subsections[j + 1] = subsections[j + 1].trimStart();
3617
+ }
3618
+ }
3619
+ });
3620
+ return subsections.flatMap((subsection) => {
3621
+ if (subsection.length === 0) {
3622
+ return [];
3623
+ }
3624
+ if (this.added_tokens_map.has(subsection)) {
3625
+ return [subsection];
3626
+ }
3627
+ const section_tokens = this.pre_tokenizer !== null ? this.pre_tokenizer(subsection, {
3628
+ section_index
3629
+ }) : [subsection];
3630
+ return this.model(section_tokens);
3631
+ });
3632
+ });
3633
+ }
3634
+ tokenize_helper(text, { text_pair = null, add_special_tokens = true }) {
3635
+ const tokens1 = this.encode_text(text);
3636
+ const tokens2 = this.encode_text(text_pair || null);
3637
+ return this.post_processor ? this.post_processor(tokens1, tokens2, add_special_tokens) : { tokens: merge_arrays(tokens1 ?? [], tokens2 ?? []) };
3638
+ }
3639
+ /**
3640
+ * Converts a token string to its corresponding token ID.
3641
+ * @param token The token string to convert.
3642
+ * @returns The token ID, or undefined if the token is not in the vocabulary.
3643
+ */
3644
+ token_to_id(token) {
3645
+ return this.model.tokens_to_ids.get(token);
3646
+ }
3647
+ /**
3648
+ * Converts a token ID to its corresponding token string.
3649
+ * @param id The token ID to convert.
3650
+ * @returns The token string, or undefined if the ID is not in the vocabulary.
3651
+ */
3652
+ id_to_token(id) {
3653
+ return this.model.vocab[id];
3654
+ }
3655
+ /**
3656
+ * Returns a mapping of token IDs to AddedToken objects for all added tokens.
3657
+ * @returns A Map where keys are token IDs and values are AddedToken objects.
3658
+ */
3659
+ get_added_tokens_decoder() {
3660
+ const decoder = /* @__PURE__ */ new Map();
3661
+ for (const token of this.added_tokens) {
3662
+ decoder.set(token.id, token);
3663
+ }
3664
+ return decoder;
3665
+ }
3666
+ /**
3667
+ * Get the underlying vocabulary
3668
+ * @param with_added_tokens Whether to include the added tokens
3669
+ * @returns The vocabulary
3670
+ */
3671
+ get_vocab(with_added_tokens = true) {
3672
+ const vocab = /* @__PURE__ */ new Map();
3673
+ for (let i = 0; i < this.model.vocab.length; ++i) {
3674
+ const token = this.model.vocab[i];
3675
+ if (with_added_tokens || !this.added_tokens_map.has(token)) {
3676
+ vocab.set(token, i);
3677
+ }
3678
+ }
3679
+ return vocab;
3680
+ }
3681
+ };
3682
+ var Tokenizer_default = Tokenizer;
3683
+
3684
+ // ../../client/vqweb/tokenizer.js
3685
+ var VqwTokenizer = class {
3686
+ /**
3687
+ * @param {object} tokenizerJSON parsed tokenizer.json
3688
+ * @param {object} tokenizerConfig parsed tokenizer_config.json
3689
+ * @param {object} header the .vqw JSON header (model.prompt / max_len / add_eos / eos_id, tokenizer.byte_ids / unk_id)
3690
+ * @param {Iterable<number>} keptIds token ids present in the trimmed table (tensor token_embd.ids)
3691
+ */
3692
+ constructor(tokenizerJSON, tokenizerConfig, header, keptIds) {
3693
+ this.tok = new Tokenizer_default(tokenizerJSON, tokenizerConfig);
3694
+ this.prompt = header.model.prompt ?? "";
3695
+ this.maxLen = header.model.max_len ?? 512;
3696
+ this.addEos = !!header.model.add_eos;
3697
+ this.eosId = header.model.eos_id ?? null;
3698
+ this.byteIds = header.tokenizer.byte_ids;
3699
+ this.unkId = header.tokenizer.unk_id ?? null;
3700
+ this.allowed = new Set(keptIds);
3701
+ this.utf8 = new TextEncoder();
3702
+ this.decodeCache = /* @__PURE__ */ new Map();
3703
+ }
3704
+ /** HF `tok(text, truncation=True, max_length=max_len)["input_ids"]` (special tokens added by the post-processor). */
3705
+ encodeFull(text) {
3706
+ let ids = this.tok.encode(text, { add_special_tokens: true }).ids;
3707
+ if (ids.length > this.maxLen) {
3708
+ ids = this.addEos ? ids.slice(0, this.maxLen - 1).concat([this.eosId]) : ids.slice(0, this.maxLen);
3709
+ }
3710
+ return ids;
3711
+ }
3712
+ /** Python `tok.decode([id])`: no special-token skipping, no clean-up (tokenizer_config says clean_up_tokenization_spaces=false). */
3713
+ decodeOne(id) {
3714
+ let s = this.decodeCache.get(id);
3715
+ if (s === void 0) {
3716
+ s = this.tok.decode([id], { skip_special_tokens: false, clean_up_tokenization_spaces: false });
3717
+ this.decodeCache.set(id, s);
3718
+ }
3719
+ return s;
3720
+ }
3721
+ /** vocab_trim.retokenize: every id outside the table -> byte tokens of its surface string. */
3722
+ retokenize(ids) {
3723
+ const out = [];
3724
+ for (const id of ids) {
3725
+ if (this.allowed.has(id)) {
3726
+ out.push(id);
3727
+ continue;
3728
+ }
3729
+ const bytes = this.utf8.encode(this.decodeOne(id));
3730
+ const fb = [];
3731
+ for (const b of bytes) {
3732
+ const t = this.byteIds[b];
3733
+ if (t != null) fb.push(t);
3734
+ }
3735
+ if (fb.length) out.push(...fb);
3736
+ else out.push(this.unkId ?? id);
3737
+ }
3738
+ return out;
3739
+ }
3740
+ /** query text (WITHOUT the prompt) -> token ids for the model. */
3741
+ encode(query) {
3742
+ return this.retokenize(this.encodeFull(this.prompt + query));
3743
+ }
3744
+ };
3745
+
3746
+ // src/kernels.js
3747
+ var KERNELS = {
3748
+ "rmsnorm": "// RMSNorm over the last axis with an f16 weight: y = w * (x * rsqrt(mean(x^2) + eps)) (scripts/vq_reference.py: rmsnorm)\n// One workgroup (256 threads) per token, N = {{N}} values per token (4 per thread), f32 reduction in workgroup memory.\n// Input dtype {{IN_T}} (f32 or f16 residual stream), output f32 (feeds fwht_rotate, which produces the f16 matmul input).\nenable f16;\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> x: array<{{IN_T}}>; // [T, N]\n@group(0) @binding(2) var<storage, read> w: array<f16>; // [N]\n@group(0) @binding(3) var<storage, read_write> y: array<f32>; // [T, N]\nconst N: u32 = {{N}}u;\nconst EPS: f32 = {{EPS}};\nconst WG: u32 = 256u;\nconst PER: u32 = N / WG;\nvar<workgroup> red: array<f32, WG>;\n\n@compute @workgroup_size(256)\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let t = wg.x; let i = lid.x;\n if (t >= P.T) { return; }\n var v: array<f32, PER>;\n var s: f32 = 0.0;\n for (var k = 0u; k < PER; k++) { let val = f32(x[t * N + i * PER + k]); v[k] = val; s += val * val; }\n red[i] = s;\n workgroupBarrier();\n for (var st = WG / 2u; st > 0u; st >>= 1u) { if (i < st) { red[i] += red[i + st]; } workgroupBarrier(); }\n let inv = 1.0 / sqrt(red[0] / f32(N) + EPS);\n for (var k = 0u; k < PER; k++) { y[t * N + i * PER + k] = f32(w[i * PER + k]) * (v[k] * inv); }\n}\n",
3749
+ "fwht_rotate": "// Input-side structured rotation x @ M (scripts/fast_rotation.py: apply_MT with the STORED signs / permutations):\n// for round r in 0..R-1: x *= signs[r]; x = blockFWHT_B(x) / sqrt(B); x = x[perm[r]] (gather)\n// One workgroup (256 threads) per token, the whole N = {{N}} vector in workgroup memory (f32, N*4 bytes: 4 / 8 / 12 KB for\n// 1024 / 2048 / 3072): the Walsh-Hadamard butterflies run per B = {{B}} block, the permutation gathers across blocks.\n// Input f32 [T, N], output f16 [T, N] (the matmul input). Butterfly order = the reference's fwht (stack(a+c, a-c)), the\n// 1/sqrt(B) scaling is applied once per round in the gather (exact for B a power of 4).\nenable f16;\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> x: array<f32>; // [T, N]\n@group(0) @binding(2) var<storage, read> signs: array<f32>; // [R, N] (+-1)\n@group(0) @binding(3) var<storage, read> perms: array<u32>; // [R, N]\n@group(0) @binding(4) var<storage, read_write> y: array<f16>; // [T, N]\nconst N: u32 = {{N}}u;\nconst B: u32 = {{B}}u;\nconst R: u32 = {{R}}u;\nconst WG: u32 = 256u;\nconst PER: u32 = N / WG; // values per thread\nconst HALF: u32 = B / 2u; // butterfly pairs per block\nconst PPT: u32 = (N / 2u) / WG; // pairs per thread\nconst INV_SQRT_B: f32 = {{INV_SQRT_B}};\nvar<workgroup> buf: array<f32, N>;\n\n@compute @workgroup_size(256)\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let t = wg.x; let i = lid.x;\n if (t >= P.T) { return; }\n for (var k = 0u; k < PER; k++) { let j = i + k * WG; buf[j] = x[t * N + j] * signs[j]; }\n workgroupBarrier();\n for (var r = 0u; r < R; r++) {\n // butterflies: h = 1, 2, ..., B/2; pair (a, a + h) with a = block*B + (w >> lh) << (lh + 1) | (w & (h - 1))\n var lh = 0u;\n for (var h = 1u; h < B; h <<= 1u) {\n for (var k = 0u; k < PPT; k++) {\n let p = i + k * WG;\n let blk = p / HALF; let w = p % HALF;\n let a = blk * B + ((w >> lh) << (lh + 1u)) + (w & (h - 1u));\n let b = a + h;\n let u = buf[a]; let v = buf[b];\n buf[a] = u + v; buf[b] = u - v;\n }\n lh++;\n workgroupBarrier();\n }\n // permutation gather (P^T), 1/sqrt(B), and the next round's signs; in place via registers\n var reg: array<f32, PER>;\n for (var k = 0u; k < PER; k++) { let j = i + k * WG; reg[k] = buf[perms[r * N + j]] * INV_SQRT_B; }\n workgroupBarrier();\n for (var k = 0u; k < PER; k++) {\n let j = i + k * WG;\n var v = reg[k];\n if (r + 1u < R) { v = v * signs[(r + 1u) * N + j]; }\n buf[j] = v;\n }\n workgroupBarrier();\n }\n for (var k = 0u; k < PER; k++) { let j = i + k * WG; y[t * N + j] = f16(buf[j]); }\n}\n",
3750
+ "vq_matmul": "// VQ linear in the rotated basis: y[t, r] = sum_c W[r, c] * f32(x[t, c]), W[r, c] = f32(codebook[c/256][idx[r, c/4]][c%4]) * f32(scale[r, c/256])\n// (scripts/vqw_format.py: dequant_vq; the product of two f16 numbers is exact in f32, so W here equals the reference's W bit\n// for bit). Shapes are compile-time: ROWS x COLS, K-entry codebooks of dimension DIM (4 = the released format; 2 = the 3-3.5 bpw\n// points, two indices per 4-column quad, the vec4 below is the concatenation of two codebook vec2 entries) per 256-column block, W-bit indices packed\n// little-endian into WORDS uint32 words per row (bit j of group g at bit g*W + j; a 6/7-bit index may straddle two words).\n//\n// M4 tiling (decode once per workgroup, reuse across every token of the tile): a workgroup owns RWG rows x TT tokens and walks\n// the columns CB at a time. Per 256-column block the codebook and the block's packed index words of the RWG rows are staged\n// (one coalesced load: whole cache lines, so the 110 MiB of indices stream from memory exactly once per query for T <= TT).\n// Per step the threads (1) decode the RWG x CB/4 indices of the step from the staged words into ws (vec4<f32> codebook values),\n// (2) convert the TT x CB/4 x vec4<f16> x tile into xs (f32), and (3) each thread accumulates an MR x MT micro-tile of outputs\n// with dot(vec4<f32>, vec4<f32>): part += dot(cb, x) over the 64 groups of the block, then acc += scale * part at the block's\n// end -- the M2 kernel's summation order per output element, so the results are the same to the bit. Layouts: ws [group][row]\n// interleaved so the lanes of a subgroup (consecutive rows) read consecutive vec4s; xs [token][group] (reads are subgroup\n// broadcasts, the staging loads coalesced). Subgroups whose tokens are beyond T skip the FMAs (rows are the fast lane index).\n// Grid: (ROWS / RWG, ceil(T / TT)).\n// OUT_MODE store: y f32 [T, ROWS]; add: y[t, r] += acc (the residual stream, dtype RESID_T) -- o_proj and down_proj.\nenable f16;\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> x: array<vec4<f16>>; // [T, COLS/4]\n@group(0) @binding(2) var<storage, read> cb: array<{{CBT}}>; // [NBLK, K], vec4<f16> (dim 4) or vec2<f16> (dim 2)\n@group(0) @binding(3) var<storage, read> idx: array<u32>; // [ROWS, WORDS]\n@group(0) @binding(4) var<storage, read> sc: array<f16>; // [ROWS, NBLK]\n@group(0) @binding(5) var<storage, read_write> y: array<{{OUT_T}}>; // [T, ROWS]\nconst ROWS: u32 = {{ROWS}}u;\nconst COLS: u32 = {{COLS}}u;\nconst K: u32 = {{K}}u;\nconst W: u32 = {{W}}u;\nconst MASK: u32 = {{MASK}}u;\nconst WORDS: u32 = {{WORDS}}u;\nconst NBLK: u32 = COLS / 256u;\nconst DIM: u32 = {{DIM}}u; // codebook dimension (4 or 2)\nconst GPQ: u32 = 4u / DIM; // index groups per 4-column quad (1 or 2)\nconst GPB: u32 = 64u; // quads (of 4 columns) per 256-column block\nconst RWG: u32 = {{RWG}}u; // rows per workgroup\nconst TT: u32 = {{TT}}u; // tokens per workgroup\nconst CB: u32 = {{CB}}u; // columns per staging step (divides 256)\nconst GS: u32 = CB / 4u; // quads per step\nconst MR: u32 = {{MR}}u; // micro-tile rows per thread\nconst MT: u32 = {{MT}}u; // micro-tile tokens per thread\nconst NTR: u32 = RWG / MR; // threads along rows (the fast index: a subgroup shares its tokens, so whole\nconst NTC: u32 = TT / MT; // subgroups skip the FMAs when their tokens are beyond T)\nconst WGS: u32 = NTR * NTC;\nconst GPT: u32 = GS / (WGS / RWG); // consecutive quads decoded per thread and step (WGS is a multiple of RWG)\nconst WPB: u32 = GPB * GPQ * W / 32u; // packed index words per row and 256-column block (a block's bits are word-aligned)\nvar<workgroup> cbs: array<{{CBT}}, K>;\nvar<workgroup> iw: array<u32, RWG * WPB>; // the block's packed indices of the workgroup's rows (one coalesced load per block)\nvar<workgroup> ws: array<vec4<f32>, GS * RWG>; // [group][i][tr] = row tr*MR + i, codebook value (exact), scale applied at the block fold\nvar<workgroup> xs: array<vec4<f32>, TT * GS>; // [token][group] (reads are subgroup broadcasts, writes / global loads coalesced)\n\n@compute @workgroup_size({{WGS}})\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let tr = lid.x % NTR; // this thread's rows: tr*MR + i (within the workgroup)\n let t0 = (lid.x / NTR) * MT; // first token\n let rbase = wg.x * RWG;\n let tbase = wg.y * TT;\n let live = tbase + t0 < P.T; // any real token in this micro-tile\n var acc: array<array<f32, MT>, MR>; // sum over the finished 256-column blocks\n var part: array<array<f32, MT>, MR>; // the current block (two-level summation as in the M2 kernel / the reference's blocked GEMM)\n for (var i = 0u; i < MR; i++) { for (var j = 0u; j < MT; j++) { acc[i][j] = 0.0; part[i][j] = 0.0; } }\n var blk = 0xffffffffu;\n for (var g0 = 0u; g0 < COLS / 4u; g0 += GS) {\n let b = g0 / GPB;\n workgroupBarrier(); // previous step's reads are done before the tiles are overwritten\n if (b != blk) { // new 256-column block: its codebook and its packed index words\n for (var e = lid.x; e < K; e += WGS) { cbs[e] = cb[b * K + e]; }\n for (var e = lid.x; e < RWG * WPB; e += WGS) { iw[e] = idx[(rbase + e / WPB) * WORDS + b * WPB + e % WPB]; }\n blk = b;\n workgroupBarrier();\n }\n // (1) decode from iw: thread -> one row (dtr, di) and GPT consecutive groups; ws entry (g*MR + di)*NTR + dtr\n // (consecutive threads write consecutive entries)\n {\n let dtr = lid.x % NTR; let di = (lid.x / NTR) % MR; let gslot = lid.x / RWG;\n let base = (dtr * MR + di) * WPB;\n for (var m = 0u; m < GPT; m++) {\n let g = gslot * GPT + m; // quad within the step\n let p = (g0 - b * GPB + g) * GPQ * W; // bit position of the quad's first index within the block's words\n let wi = p >> 5u; let sh = p & 31u;\n var v = iw[base + wi] >> sh;\n if (sh + W > 32u) { v |= iw[base + wi + 1u] << (32u - sh); }\n {{DECODE}}\n }\n }\n // (2) x tile [token][group]: consecutive threads = consecutive groups of one token row; tokens beyond T read as zero\n for (var e = lid.x; e < TT * GS; e += WGS) {\n let t = tbase + e / GS; let g = e % GS;\n if (t < P.T) { xs[e] = vec4<f32>(x[t * (COLS / 4u) + g0 + g]); } else { xs[e] = vec4<f32>(0.0); }\n }\n workgroupBarrier();\n // (3) micro-tile FMAs\n if (live) {\n for (var g = 0u; g < GS; g++) {\n var xv: array<vec4<f32>, MT>;\n for (var j = 0u; j < MT; j++) { xv[j] = xs[(t0 + j) * GS + g]; }\n for (var i = 0u; i < MR; i++) {\n let wv = ws[(g * MR + i) * NTR + tr];\n for (var j = 0u; j < MT; j++) { part[i][j] += dot(wv, xv[j]); }\n }\n }\n if ((g0 + GS) % GPB == 0u) { // block finished: scale and fold (the M2 kernel's order: acc += s * sum_block)\n for (var i = 0u; i < MR; i++) {\n let s = f32(sc[(rbase + tr * MR + i) * NBLK + b]);\n for (var j = 0u; j < MT; j++) { acc[i][j] += s * part[i][j]; part[i][j] = 0.0; }\n }\n }\n }\n }\n for (var j = 0u; j < MT; j++) {\n let t = tbase + t0 + j;\n if (t < P.T) {\n for (var i = 0u; i < MR; i++) { let r = rbase + tr * MR + i; {{OUT_STMT}} }\n }\n }\n}\n",
3751
+ "vq_matmul_v1": "// VQ linear in the rotated basis: y[t, r] = sum_b f32(scale[r, b]) * sum_{c in block b} f32(codebook[b][idx[r, c/4]][c%4]) * f32(x[t, c])\n// (scripts/vqw_format.py: dequant_vq, without ever materialising W). Shapes are compile-time: ROWS x COLS, K-entry\n// codebooks of dimension 4 per 256-column block (NBLK = COLS/256 blocks, GPB = 64 groups per block), W-bit indices packed\n// little-endian into WORDS uint32 words per row (bit j of group g at bit g*W + j; a 6/7-bit index may straddle two words).\n//\n// Tiling: a workgroup owns RWG rows x TT = LANES*TPT tokens; thread = (row, lane), TPT tokens per thread (f32 accumulators).\n// Per column block: the block's codebook (K x vec4<f16>, <= 2 KB) and the x tile (TT x 64 vec4<f16>, TT*512 bytes) are staged\n// in workgroup memory, then each thread streams its row's 64 indices and does TPT dot(vec4) FMAs per index. Inputs f16,\n// products / sums f32. Grid: (ROWS / RWG, ceil(T / TT)) -- every token of the query is in flight in one dispatch.\n// OUT_MODE store: y f32 [T, ROWS]; add: y[t, r] += acc (the residual stream, dtype RESID_T) -- o_proj and down_proj.\nenable f16;\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> x: array<vec4<f16>>; // [T, COLS/4]\n@group(0) @binding(2) var<storage, read> cb: array<vec4<f16>>; // [NBLK, K]\n@group(0) @binding(3) var<storage, read> idx: array<u32>; // [ROWS, WORDS]\n@group(0) @binding(4) var<storage, read> sc: array<f16>; // [ROWS, NBLK]\n@group(0) @binding(5) var<storage, read_write> y: array<{{OUT_T}}>; // [T, ROWS]\nconst ROWS: u32 = {{ROWS}}u;\nconst COLS: u32 = {{COLS}}u;\nconst K: u32 = {{K}}u;\nconst W: u32 = {{W}}u;\nconst MASK: u32 = {{MASK}}u;\nconst WORDS: u32 = {{WORDS}}u;\nconst NBLK: u32 = COLS / 256u;\nconst GPB: u32 = 64u;\nconst RWG: u32 = {{RWG}}u;\nconst LANES: u32 = {{LANES}}u;\nconst TPT: u32 = {{TPT}}u;\nconst TT: u32 = LANES * TPT;\nconst WGS: u32 = RWG * LANES;\nvar<workgroup> cbs: array<vec4<f16>, K>;\nvar<workgroup> xs: array<vec4<f16>, TT * GPB>;\n\n@compute @workgroup_size({{WGS}})\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let lr = lid.x / LANES; let lane = lid.x % LANES;\n let r = wg.x * RWG + lr;\n let t0 = wg.y * TT;\n let base = r * WORDS;\n var acc: array<f32, TPT>;\n for (var k = 0u; k < TPT; k++) { acc[k] = 0.0; }\n for (var b = 0u; b < NBLK; b++) {\n workgroupBarrier();\n for (var e = lid.x; e < K; e += WGS) { cbs[e] = cb[b * K + e]; }\n for (var e = lid.x; e < TT * GPB; e += WGS) {\n let tk = e / GPB; let g = e % GPB; let t = t0 + tk;\n if (t < P.T) { xs[e] = x[t * (COLS / 4u) + b * GPB + g]; } else { xs[e] = vec4<f16>(0.0h); }\n }\n workgroupBarrier();\n var part: array<f32, TPT>;\n for (var k = 0u; k < TPT; k++) { part[k] = 0.0; }\n var p = b * GPB * W; // bit position of this block's first index in the row stream\n for (var g = 0u; g < GPB; g++) {\n let wi = p >> 5u; let sh = p & 31u;\n var v = idx[base + wi] >> sh;\n if (sh + W > 32u) { v |= idx[base + wi + 1u] << (32u - sh); }\n let c = vec4<f32>(cbs[v & MASK]);\n for (var k = 0u; k < TPT; k++) { part[k] += dot(c, vec4<f32>(xs[(lane * TPT + k) * GPB + g])); }\n p += W;\n }\n let s = f32(sc[r * NBLK + b]);\n for (var k = 0u; k < TPT; k++) { acc[k] += s * part[k]; }\n }\n for (var k = 0u; k < TPT; k++) {\n let t = t0 + lane * TPT + k;\n if (t < P.T) { {{OUT_STMT}} }\n }\n}\n",
3752
+ "qk_norm_rope": "// Qwen3 per-head q/k RMSNorm (weight [HD] shared by all heads) fused with RoPE (HF rotate_half convention):\n// xn = w * (x * rsqrt(mean_d(x^2) + eps)); out[i] = xn[i]*cos[i] - xn[i+HD/2]*sin[i]; out[i+HD/2] = xn[i+HD/2]*cos[i] + xn[i]*sin[i]\n// cos/sin [T, HD/2] are computed on the CPU in double precision (positions 0..T-1, inv_freq_j = theta^(-2j/HD)).\n// One workgroup of HD/2 = {{HALF}} threads per (token, head); grid (T, NH + NKV): workgroups 0..NH-1 are q heads, the rest k heads.\n// DO_ROPE = 0 is the debug variant that materialises the normalised q/k (reference names q_norm / k_norm);\n// INPLACE = 1 writes back into the input buffers (production), else into qo / ko.\nenable f16;\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read_write> q: array<f32>; // [T, NH*HD]\n@group(0) @binding(2) var<storage, read_write> k: array<f32>; // [T, NKV*HD]\n@group(0) @binding(3) var<storage, read> wq: array<f16>; // [HD]\n@group(0) @binding(4) var<storage, read> wk: array<f16>; // [HD]\n@group(0) @binding(5) var<storage, read> cosT: array<f32>; // [T, HD/2]\n@group(0) @binding(6) var<storage, read> sinT: array<f32>; // [T, HD/2]\n{{OUT_BINDINGS}}\nconst NH: u32 = {{NH}}u;\nconst NKV: u32 = {{NKV}}u;\nconst HD: u32 = {{HD}}u;\nconst HALF: u32 = HD / 2u;\nconst EPS: f32 = {{EPS}};\nconst DO_ROPE: bool = {{DO_ROPE}};\nvar<workgroup> red: array<f32, HALF>;\n\n@compute @workgroup_size({{HALF}})\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let t = wg.x; let hh = wg.y; let i = lid.x;\n if (t >= P.T) { return; }\n let isq = hh < NH;\n let head = select(hh - NH, hh, isq);\n let nheads = select(NKV, NH, isq);\n let base = (t * nheads + head) * HD;\n var a: f32; var b: f32; var wa: f32; var wb: f32;\n if (isq) { a = q[base + i]; b = q[base + i + HALF]; wa = f32(wq[i]); wb = f32(wq[i + HALF]); }\n else { a = k[base + i]; b = k[base + i + HALF]; wa = f32(wk[i]); wb = f32(wk[i + HALF]); }\n red[i] = a * a + b * b;\n workgroupBarrier();\n for (var st = HALF / 2u; st > 0u; st >>= 1u) { if (i < st) { red[i] += red[i + st]; } workgroupBarrier(); }\n let inv = 1.0 / sqrt(red[0] / f32(HD) + EPS);\n var an = wa * (a * inv); var bn = wb * (b * inv);\n if (DO_ROPE) {\n let c = cosT[t * HALF + i]; let s = sinT[t * HALF + i];\n let oa = an * c - bn * s;\n let ob = bn * c + an * s;\n an = oa; bn = ob;\n }\n {{OUT_STMT}}\n}\n",
3753
+ "attention": "// Causal GQA attention, all tokens of the query at once, f32 throughout, with an optional stored prefix (M6):\n// keys / values 0..PP-1 come from the container's prefix.k / prefix.v (the prompt, computed by the fp model, already normed + RoPE'd),\n// keys / values PP.. are the query tokens of this pass (k / v buffers, row j - PP). The query token t sits at position PP + t:\n// s[j] = (q[t,h,:] . key_j[h / (NH/NKV), :]) * HD^-0.5 for j <= PP + t; p = softmax_j(s); o[t,h,:] = sum_j p[j] value_j\n// PP = 0 (uniform) is the plain path (no prefix, or ids that do not start with it). PMAX = {{PMAX}} is the stored prefix length,\n// TMAX = {{TMAX}} the score array size (PMAX + max query tokens).\n// One workgroup of HD = {{HD}} threads per (query token t, head h); scores in workgroup memory; max / sum reductions in f32\n// (the reference: torch.softmax on fp32 scores with the -inf mask over the query part).\n// WRITE_PROBS = 1 (debug) also stores the probabilities as [NH, T, PP + T] f32 (zeros above the diagonal; reference attn_probs).\nenable f16;\nstruct Params { T: u32, debug: u32, PP: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> q: array<f32>; // [T, NH*HD] (after q-norm + RoPE at positions PP..PP+T-1)\n@group(0) @binding(2) var<storage, read> k: array<f32>; // [T, NKV*HD]\n@group(0) @binding(3) var<storage, read> v: array<f32>; // [T, NKV*HD]\n@group(0) @binding(4) var<storage, read_write> o: array<f32>; // [T, NH*HD]\n@group(0) @binding(5) var<storage, read> kp: array<f32>; // [PMAX, NKV*HD] stored prefix keys (f32 on the GPU)\n@group(0) @binding(6) var<storage, read> vp: array<f32>; // [PMAX, NKV*HD] stored prefix values\n{{PROBS_BINDING}}\nconst NH: u32 = {{NH}}u;\nconst NKV: u32 = {{NKV}}u;\nconst HD: u32 = {{HD}}u;\nconst PMAX: u32 = {{PMAX}}u;\nconst TMAX: u32 = {{TMAX}}u;\nconst SCALE: f32 = {{SCALE}};\nconst WRITE_PROBS: bool = {{WRITE_PROBS}};\nvar<workgroup> qs: array<f32, HD>;\nvar<workgroup> sc: array<f32, TMAX>;\nvar<workgroup> red: array<f32, HD>;\n\nfn key_at(j: u32, kvh: u32, d: u32, PP: u32) -> f32 {\n if (j < PP) { return kp[(j * NKV + kvh) * HD + d]; }\n return k[((j - PP) * NKV + kvh) * HD + d];\n}\nfn val_at(j: u32, kvh: u32, d: u32, PP: u32) -> f32 {\n if (j < PP) { return vp[(j * NKV + kvh) * HD + d]; }\n return v[((j - PP) * NKV + kvh) * HD + d];\n}\n\n@compute @workgroup_size({{HD}})\nfn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {\n let t = wg.x; let h = wg.y; let i = lid.x;\n let T = P.T; let PP = P.PP;\n if (t >= T) { return; }\n let kvh = h / (NH / NKV);\n let last = PP + t; // last visible key index\n qs[i] = q[(t * NH + h) * HD + i];\n workgroupBarrier();\n // scores (thread i owns j = i, i + HD, ...)\n var m: f32 = -3.0e38;\n for (var j = i; j <= last; j += HD) {\n var s: f32 = 0.0;\n for (var d = 0u; d < HD; d++) { s += qs[d] * key_at(j, kvh, d, PP); }\n s = s * SCALE;\n sc[j] = s;\n m = max(m, s);\n }\n red[i] = m;\n workgroupBarrier();\n for (var st = HD / 2u; st > 0u; st >>= 1u) { if (i < st) { red[i] = max(red[i], red[i + st]); } workgroupBarrier(); }\n let mx = red[0];\n workgroupBarrier();\n var sum: f32 = 0.0;\n for (var j = i; j <= last; j += HD) { let e = exp(sc[j] - mx); sc[j] = e; sum += e; }\n red[i] = sum;\n workgroupBarrier();\n for (var st = HD / 2u; st > 0u; st >>= 1u) { if (i < st) { red[i] += red[i + st]; } workgroupBarrier(); }\n let inv = 1.0 / red[0];\n if (WRITE_PROBS) {\n let W = PP + T;\n for (var j = i; j < W; j += HD) { {{PROBS_STMT}} }\n }\n // output: thread i = dimension d\n var acc: f32 = 0.0;\n for (var j = 0u; j <= last; j++) { acc += (sc[j] * inv) * val_at(j, kvh, i, PP); }\n o[(t * NH + h) * HD + i] = acc;\n}\n",
3754
+ "swiglu": "// SwiGLU gate: a = silu(g) * u = (g / (1 + exp(-g))) * u, elementwise over [T, I], f32.\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> g: array<f32>;\n@group(0) @binding(2) var<storage, read> u: array<f32>;\n@group(0) @binding(3) var<storage, read_write> a: array<f32>;\nconst I: u32 = {{I}}u;\n\n@compute @workgroup_size(256)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let n = gid.x;\n if (n >= P.T * I) { return; }\n let x = g[n];\n a[n] = (x / (1.0 + exp(-x))) * u[n];\n}\n",
3755
+ "pool_normalize": "// Last-token pooling + L2 normalisation: e = x[T-1] / max(||x[T-1]||, 1e-12) (torch.nn.functional.normalize), N = {{N}}.\n// One workgroup of 256 threads; the input is the output of the final RMSNorm, f32 [T, N].\nstruct Params { T: u32, debug: u32, pad0: u32, pad1: u32 };\n@group(0) @binding(0) var<uniform> P: Params;\n@group(0) @binding(1) var<storage, read> x: array<f32>; // [T, N]\n@group(0) @binding(2) var<storage, read_write> e: array<f32>; // [N]\nconst N: u32 = {{N}}u;\nconst WG: u32 = 256u;\nconst PER: u32 = N / WG;\nvar<workgroup> red: array<f32, WG>;\n\n@compute @workgroup_size(256)\nfn main(@builtin(local_invocation_id) lid: vec3<u32>) {\n let i = lid.x;\n let base = (P.T - 1u) * N;\n var v: array<f32, PER>;\n var s: f32 = 0.0;\n for (var k = 0u; k < PER; k++) { let val = x[base + i * PER + k]; v[k] = val; s += val * val; }\n red[i] = s;\n workgroupBarrier();\n for (var st = WG / 2u; st > 0u; st >>= 1u) { if (i < st) { red[i] += red[i + st]; } workgroupBarrier(); }\n let inv = 1.0 / max(sqrt(red[0]), 1e-12);\n for (var k = 0u; k < PER; k++) { e[i * PER + k] = v[k] * inv; }\n}\n"
3756
+ };
3757
+
3758
+ // src/loader.js
3759
+ var basename = (url) => {
3760
+ const u = new URL(url, typeof location !== "undefined" ? location.href : "http://localhost/");
3761
+ return decodeURIComponent(u.pathname.split("/").filter(Boolean).pop() || "model");
3762
+ };
3763
+ async function opfsRoot() {
3764
+ try {
3765
+ if (typeof navigator === "undefined" || !navigator.storage?.getDirectory) return null;
3766
+ return await navigator.storage.getDirectory();
3767
+ } catch (e) {
3768
+ return null;
3769
+ }
3770
+ }
3771
+ async function opfsGet(name, size) {
3772
+ try {
3773
+ const root = await opfsRoot();
3774
+ if (!root) return null;
3775
+ const f = await (await root.getFileHandle(name)).getFile();
3776
+ return size == null || f.size === size ? f : null;
3777
+ } catch (e) {
3778
+ return null;
3779
+ }
3780
+ }
3781
+ async function opfsPut(name, parts) {
3782
+ try {
3783
+ const root = await opfsRoot();
3784
+ if (!root) return null;
3785
+ const fh = await root.getFileHandle(name, { create: true });
3786
+ const w = await fh.createWritable();
3787
+ for (const p of parts) await w.write(p);
3788
+ await w.close();
3789
+ return await fh.getFile();
3790
+ } catch (e) {
3791
+ return null;
3792
+ }
3793
+ }
3794
+ async function opfsRemove(name) {
3795
+ const root = await opfsRoot();
3796
+ if (!root) return false;
3797
+ try {
3798
+ await root.removeEntry(`${name}.meta.json`);
3799
+ } catch (e) {
3800
+ }
3801
+ try {
3802
+ await root.removeEntry(name);
3803
+ return true;
3804
+ } catch (e) {
3805
+ return false;
3806
+ }
3807
+ }
3808
+ async function fetchWithProgress(url, onProgress, expectedBytes = null) {
3809
+ const r = await fetch(url);
3810
+ if (!r.ok) throw new Error(`fetch ${url}: HTTP ${r.status}`);
3811
+ const total = expectedBytes ?? Number(r.headers.get("Content-Length") || 0) ?? 0;
3812
+ if (!r.body) {
3813
+ const b = new Uint8Array(await r.arrayBuffer());
3814
+ onProgress?.(b.byteLength, total || b.byteLength);
3815
+ return b;
3816
+ }
3817
+ const reader = r.body.getReader();
3818
+ const chunks = [];
3819
+ let loaded = 0;
3820
+ for (; ; ) {
3821
+ const { done, value } = await reader.read();
3822
+ if (done) break;
3823
+ chunks.push(value);
3824
+ loaded += value.byteLength;
3825
+ onProgress?.(loaded, total || loaded);
3826
+ }
3827
+ const out = new Uint8Array(loaded);
3828
+ let o = 0;
3829
+ for (const c of chunks) {
3830
+ out.set(c, o);
3831
+ o += c.byteLength;
3832
+ }
3833
+ return out;
3834
+ }
3835
+ var isManifest = (url) => /\.chunks\.json(\?.*)?$/i.test(String(url));
3836
+ async function loadModelBytes(url, { cache = "opfs", onProgress } = {}) {
3837
+ const useCache = cache === "opfs";
3838
+ if (isManifest(url)) {
3839
+ const r = await fetch(url);
3840
+ if (!r.ok) throw new Error(`manifest ${url}: HTTP ${r.status}`);
3841
+ const manifest = await r.json();
3842
+ const name2 = manifest.file, total = manifest.size_bytes;
3843
+ if (useCache) {
3844
+ const f = await opfsGet(name2, total);
3845
+ if (f) {
3846
+ onProgress?.(total, total);
3847
+ return { buffer: await f.arrayBuffer(), bytes: total, cached: true, persisted: true, name: name2 };
3848
+ }
3849
+ }
3850
+ const parts = [];
3851
+ let loaded = 0;
3852
+ const one = async (c) => {
3853
+ const b = await fetchWithProgress(new URL(c.url, url).href, (l) => onProgress?.(loaded + l, total), c.bytes);
3854
+ if (b.byteLength !== c.bytes) throw new Error(`chunk ${c.url}: got ${b.byteLength} bytes, expected ${c.bytes}`);
3855
+ loaded += b.byteLength;
3856
+ return b;
3857
+ };
3858
+ const pending = [];
3859
+ let next = 0;
3860
+ for (let i = 0; i < manifest.chunks.length; i++) {
3861
+ while (pending.length < 3 && next < manifest.chunks.length) pending.push(one(manifest.chunks[next++]));
3862
+ parts.push(await pending.shift());
3863
+ }
3864
+ const out = new Uint8Array(total);
3865
+ let o = 0;
3866
+ for (const p of parts) {
3867
+ out.set(p, o);
3868
+ o += p.byteLength;
3869
+ }
3870
+ if (o !== total) throw new Error(`assembled ${o} bytes, expected ${total}`);
3871
+ const persisted2 = useCache ? !!await opfsPut(name2, [out]) : false;
3872
+ return { buffer: out.buffer, bytes: total, cached: false, persisted: persisted2, name: name2 };
3873
+ }
3874
+ const name = basename(url), metaName = `${name}.meta.json`;
3875
+ if (useCache) {
3876
+ const meta = await opfsGet(metaName, null).then((f) => f ? f.text() : null).then((t) => t ? JSON.parse(t) : null).catch(() => null);
3877
+ if (meta && meta.url === String(url) && meta.bytes > 0) {
3878
+ const f = await opfsGet(name, meta.bytes);
3879
+ if (f) {
3880
+ onProgress?.(f.size, f.size);
3881
+ return { buffer: await f.arrayBuffer(), bytes: f.size, cached: true, persisted: true, name };
3882
+ }
3883
+ }
3884
+ }
3885
+ const bytes = await fetchWithProgress(url, onProgress);
3886
+ let persisted = false;
3887
+ if (useCache) {
3888
+ persisted = !!await opfsPut(name, [bytes]);
3889
+ if (persisted) await opfsPut(metaName, [JSON.stringify({ url: String(url), bytes: bytes.byteLength, stored: (/* @__PURE__ */ new Date()).toISOString() })]);
3890
+ }
3891
+ return { buffer: bytes.buffer, bytes: bytes.byteLength, cached: false, persisted, name };
3892
+ }
3893
+ async function loadJsonCached(url, { cache = "opfs", cacheName = null } = {}) {
3894
+ const name = cacheName ?? basename(url);
3895
+ if (cache === "opfs") {
3896
+ const f = await opfsGet(name, null);
3897
+ if (f) {
3898
+ try {
3899
+ return JSON.parse(await f.text());
3900
+ } catch (e) {
3901
+ await opfsRemove(name);
3902
+ }
3903
+ }
3904
+ }
3905
+ const r = await fetch(url);
3906
+ if (r.status === 404) return null;
3907
+ if (!r.ok) throw new Error(`fetch ${url}: HTTP ${r.status}`);
3908
+ const text = await r.text();
3909
+ if (cache === "opfs") await opfsPut(name, [text]);
3910
+ return JSON.parse(text);
3911
+ }
3912
+ async function loadTokenizerFiles(containerUrl, header, { tokenizerUrl, tokenizerConfigUrl, cache = "opfs" } = {}) {
3913
+ const base = new URL(containerUrl, typeof location !== "undefined" ? location.href : "http://localhost/");
3914
+ const tokName = header.tokenizer?.file ?? "tokenizer.json", cfgName = header.tokenizer?.config ?? "tokenizer_config.json";
3915
+ const teacher = header.model?.teacher ?? "model";
3916
+ const tokUrl = tokenizerUrl ?? new URL(tokName, base).href, cfgUrl = tokenizerConfigUrl ?? new URL(cfgName, base).href;
3917
+ let [tj, tc] = await Promise.all([loadJsonCached(tokUrl, { cache, cacheName: `tokenizer-${teacher}.json` }), loadJsonCached(cfgUrl, { cache, cacheName: `tokenizer_config-${teacher}.json` })]);
3918
+ let fallback = false;
3919
+ if (!tj && !tokenizerUrl && tokName !== "tokenizer.json") {
3920
+ tj = await loadJsonCached(new URL("tokenizer.json", base).href, { cache, cacheName: `tokenizer-${teacher}.json` });
3921
+ fallback = true;
3922
+ }
3923
+ if (!tc && !tokenizerConfigUrl && cfgName !== "tokenizer_config.json") {
3924
+ tc = await loadJsonCached(new URL("tokenizer_config.json", base).href, { cache, cacheName: `tokenizer_config-${teacher}.json` });
3925
+ fallback = true;
3926
+ }
3927
+ if (!tj) throw new Error(`tokenizer file ${tokUrl} not found (the container header names ${tokName}; pass tokenizerUrl)`);
3928
+ if (!tc) throw new Error(`tokenizer config ${cfgUrl} not found (the container header names ${cfgName}; pass tokenizerConfigUrl)`);
3929
+ return { tokenizerJSON: tj, tokenizerConfig: tc, fallback };
3930
+ }
3931
+
3932
+ // src/index.js
3933
+ var VERSION = "0.1.0";
3934
+ var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
3935
+ function l2normalize(v) {
3936
+ let s = 0;
3937
+ for (let i = 0; i < v.length; i++) s += v[i] * v[i];
3938
+ const inv = s > 0 ? 1 / Math.sqrt(s) : 0;
3939
+ const out = new Float32Array(v.length);
3940
+ for (let i = 0; i < v.length; i++) out[i] = v[i] * inv;
3941
+ return out;
3942
+ }
3943
+ async function probeWebGPU({ powerPreference = "low-power" } = {}) {
3944
+ const out = { ok: false, reason: null, adapter: null, shaderF16: null };
3945
+ if (typeof navigator === "undefined" || !navigator.gpu) {
3946
+ out.reason = "WebGPU is not available in this browser (navigator.gpu is missing)";
3947
+ return out;
3948
+ }
3949
+ let ad;
3950
+ try {
3951
+ ad = await navigator.gpu.requestAdapter({ powerPreference });
3952
+ } catch (e) {
3953
+ out.reason = `requestAdapter failed: ${e?.message ?? e}`;
3954
+ return out;
3955
+ }
3956
+ if (!ad) {
3957
+ out.reason = "no WebGPU adapter (GPU blocklisted or WebGPU disabled)";
3958
+ return out;
3959
+ }
3960
+ const info = ad.info ?? {};
3961
+ out.adapter = { vendor: info.vendor, architecture: info.architecture, device: info.device, description: info.description };
3962
+ out.shaderF16 = ad.features.has("shader-f16");
3963
+ if (!out.shaderF16) {
3964
+ out.reason = "the WebGPU adapter has no shader-f16 (the kernels need 16-bit floats)";
3965
+ return out;
3966
+ }
3967
+ out.ok = true;
3968
+ return out;
3969
+ }
3970
+ var VqwClient = class {
3971
+ constructor({ device, ownsDevice, adapter, container, runtime, tokenizer, file, timing, tokenizerFallback }) {
3972
+ this.device = device;
3973
+ this._ownsDevice = ownsDevice;
3974
+ this.container = container;
3975
+ this.runtime = runtime;
3976
+ this.tokenizer = tokenizer;
3977
+ const m = container.model, q = container.quant, h = container.header;
3978
+ this.info = {
3979
+ file: { url: file.url, name: file.name, bytes: file.bytes, cached: file.cached, persisted: file.persisted },
3980
+ model: {
3981
+ teacher: m.teacher,
3982
+ hf: h.model?.hf ?? null,
3983
+ arch: m.arch,
3984
+ layers: m.layers,
3985
+ hidden: m.hidden,
3986
+ prompt: m.prompt,
3987
+ addEos: m.addEos,
3988
+ maxLen: m.maxLen,
3989
+ prefixTokens: container.prefix ? container.prefix.tokens : 0,
3990
+ pooling: m.pooling
3991
+ },
3992
+ quant: { method: q.method, bits: q.bits, k: q.k, dim: q.dim ?? 4, bpwBlocks: q.bpw_blocks ?? null, calib: q.calib ?? null, blocksize: q.blocksize ?? 256 },
3993
+ tokenizer: { rows: container.tokens.rows, full: h.tokenizer?.vocab_full ?? null, file: h.tokenizer?.file ?? "tokenizer.json", fallback: tokenizerFallback },
3994
+ adapter,
3995
+ gpuBytes: container.gpuBytes + runtime.actBytes,
3996
+ weightBytes: container.gpuBytes,
3997
+ activationBytes: runtime.actBytes,
3998
+ timing,
3999
+ version: VERSION
4000
+ };
4001
+ this._disposed = false;
4002
+ }
4003
+ /** query text (without the prompt: the container applies its own) -> token ids on the container's table. */
4004
+ tokenize(text) {
4005
+ this._check();
4006
+ return this.tokenizer.encode(text);
4007
+ }
4008
+ /** Raw runtime call on token ids: {embedding (not normalised), T, ms, ...}. opts.profile adds GPU timestamps where supported. */
4009
+ embedTokens(ids, opts = {}) {
4010
+ this._check();
4011
+ return this.runtime.embed(ids, opts);
4012
+ }
4013
+ /** One query -> L2-normalised Float32Array(hidden). */
4014
+ async embed(text) {
4015
+ const r = await this.embedTokens(this.tokenize(String(text)));
4016
+ return l2normalize(r.embedding);
4017
+ }
4018
+ /** Several queries, one after another (the runtime is batch 1). */
4019
+ async embedMany(texts) {
4020
+ const out = [];
4021
+ for (const t of texts) out.push(await this.embed(t));
4022
+ return out;
4023
+ }
4024
+ /** Like embed() but also returns the token count and the wall-clock milliseconds of the GPU pass. */
4025
+ async embedWithStats(text) {
4026
+ const t0 = now(), ids = this.tokenize(String(text));
4027
+ const r = await this.embedTokens(ids);
4028
+ return { embedding: l2normalize(r.embedding), tokens: ids.length, ms: now() - t0, gpuMs: r.ms };
4029
+ }
4030
+ get disposed() {
4031
+ return this._disposed;
4032
+ }
4033
+ _check() {
4034
+ if (this._disposed) throw new Error("VqwClient is disposed");
4035
+ }
4036
+ /** Free the GPU buffers (and the device when this client created it). */
4037
+ dispose() {
4038
+ if (this._disposed) return;
4039
+ this._disposed = true;
4040
+ try {
4041
+ this.runtime.destroy();
4042
+ } catch (e) {
4043
+ }
4044
+ try {
4045
+ this.container.destroy();
4046
+ } catch (e) {
4047
+ }
4048
+ if (this._ownsDevice) {
4049
+ try {
4050
+ this.device.destroy();
4051
+ } catch (e) {
4052
+ }
4053
+ }
4054
+ }
4055
+ };
4056
+ async function loadVqwClient(url, opts = {}) {
4057
+ const { cache = "opfs", onProgress, tokenizerUrl, tokenizerConfigUrl, powerPreference = "low-power", warmup = true } = opts;
4058
+ const progress = (phase, loaded, total) => {
4059
+ try {
4060
+ onProgress?.({ phase, loaded, total });
4061
+ } catch (e) {
4062
+ }
4063
+ };
4064
+ const t0 = now();
4065
+ let device = opts.device, adapter = null, ownsDevice = false;
4066
+ if (!device) {
4067
+ const r = await requestDevice({ powerPreference, label: "vqweb" });
4068
+ device = r.device;
4069
+ adapter = r.info;
4070
+ ownsDevice = true;
4071
+ } else if (!device.features.has("shader-f16")) throw new Error("the given GPUDevice has no shader-f16");
4072
+ const file = await loadModelBytes(url, { cache, onProgress: (l, t) => progress("download", l, t) });
4073
+ const tDown = now();
4074
+ progress("upload", 0, 1);
4075
+ let buffer = file.buffer;
4076
+ const container = new VqwContainer(parseVqw(buffer), device);
4077
+ const tUp = now();
4078
+ progress("tokenizer", 0, 1);
4079
+ const tf = await loadTokenizerFiles(url, container.header, { tokenizerUrl, tokenizerConfigUrl, cache });
4080
+ const tokenizer = new VqwTokenizer(tf.tokenizerJSON, tf.tokenizerConfig, container.header, container.tokens.ids);
4081
+ const tTok = now();
4082
+ progress("pipelines", 0, 1);
4083
+ const runtime = await VqwRuntime.create(container, device, { sources: KERNELS, ...opts.runtime || {} });
4084
+ const tPipe = now();
4085
+ container.release();
4086
+ buffer = null;
4087
+ const timing = { download_s: file.cached ? 0 : (tDown - t0) / 1e3, upload_s: (tUp - tDown) / 1e3, tokenizer_s: (tTok - tUp) / 1e3, pipelines_s: (tPipe - tTok) / 1e3, warmup_s: 0 };
4088
+ const client = new VqwClient({ device, ownsDevice, adapter, container, runtime, tokenizer, file: { ...file, url }, timing, tokenizerFallback: tf.fallback });
4089
+ if (warmup) {
4090
+ progress("warmup", 0, 1);
4091
+ await client.embed("warm-up");
4092
+ timing.warmup_s = (now() - tPipe) / 1e3;
4093
+ }
4094
+ progress("ready", 1, 1);
4095
+ return client;
4096
+ }
4097
+ async function clearCache(urlOrName) {
4098
+ const name = decodeURIComponent(String(urlOrName).split("?")[0].split("/").filter(Boolean).pop() || "").replace(/\.chunks\.json$/, "");
4099
+ return opfsRemove(name);
4100
+ }
4101
+ export {
4102
+ KERNELS,
4103
+ VERSION,
4104
+ VqwClient,
4105
+ VqwContainer,
4106
+ VqwRuntime,
4107
+ VqwTokenizer,
4108
+ clearCache,
4109
+ fetchVqw,
4110
+ fetchWithProgress,
4111
+ l2normalize,
4112
+ loadModelBytes,
4113
+ loadTokenizerFiles,
4114
+ loadVqwClient,
4115
+ opfsGet,
4116
+ opfsPut,
4117
+ opfsRemove,
4118
+ parseVqw,
4119
+ probeWebGPU,
4120
+ requestDevice
4121
+ };