bleam 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/ai.cjs +3984 -25
  2. package/dist/ai.d.cts +503 -20
  3. package/dist/ai.d.ts +503 -20
  4. package/dist/ai.js +3946 -26
  5. package/dist/animated.cjs +1 -1
  6. package/dist/animated.js +1 -1
  7. package/dist/app.cjs +92 -0
  8. package/dist/app.js +92 -0
  9. package/dist/cli.cjs +653 -86
  10. package/dist/cli.d.cts +89 -1
  11. package/dist/cli.d.ts +89 -1
  12. package/dist/cli.js +654 -88
  13. package/dist/{config-ClDGHNb3.d.ts → config-Cms0rvqg.d.ts} +1 -0
  14. package/dist/{config-D3gPaVMb.d.cts → config-CufOVJV3.d.cts} +1 -0
  15. package/dist/config.d.cts +1 -1
  16. package/dist/config.d.ts +1 -1
  17. package/dist/crypto.d.cts +0 -2
  18. package/dist/crypto.d.ts +1 -1
  19. package/dist/dev-protocol.d.cts +14 -1
  20. package/dist/dev-protocol.d.ts +14 -1
  21. package/dist/files-Bo7h9fik.cjs +468 -0
  22. package/dist/files-Ds1wT8C2.js +445 -0
  23. package/dist/files-DwA7pzr3.d.cts +99 -0
  24. package/dist/files-VrkQlKIT.d.ts +99 -0
  25. package/dist/files.cjs +7 -0
  26. package/dist/files.d.cts +2 -0
  27. package/dist/files.d.ts +2 -0
  28. package/dist/files.js +4 -0
  29. package/dist/index.d.cts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/native-sqlite-xcGdamRD.js +64 -0
  32. package/dist/native-sqlite-yQLD5s9i.cjs +66 -0
  33. package/dist/platform.cjs +27 -0
  34. package/dist/platform.d.cts +39 -0
  35. package/dist/platform.d.ts +39 -0
  36. package/dist/platform.js +21 -0
  37. package/dist/runtime.d.cts +1 -6
  38. package/dist/runtime.d.ts +1 -6
  39. package/dist/{schema-ipsECAso.d.ts → schema-DOOjfXvs.d.ts} +1 -1
  40. package/dist/{schema-BOuUHcqo.d.cts → schema-ENSMX_1t.d.cts} +1 -1
  41. package/dist/schema.cjs +1 -1
  42. package/dist/schema.d.cts +1 -1
  43. package/dist/schema.d.ts +1 -1
  44. package/dist/schema.js +1 -1
  45. package/dist/state-Bx0VlTlO.cjs +852 -0
  46. package/dist/state-CAwe-Vw1.js +767 -0
  47. package/dist/state.cjs +17 -763
  48. package/dist/state.d.cts +107 -77
  49. package/dist/state.d.ts +107 -77
  50. package/dist/state.js +4 -745
  51. package/dist/ui-1WepaMS4.d.cts +92 -0
  52. package/dist/ui-234Plg7Z.d.cts +92 -0
  53. package/dist/ui-BJmXhz9Q.d.ts +92 -0
  54. package/dist/ui-Dd7SXdbg.d.cts +92 -0
  55. package/dist/ui-Ds88eETu.d.ts +92 -0
  56. package/dist/ui-TaqnB5SP.d.ts +92 -0
  57. package/dist/ui-WQvnXL0W.d.ts +92 -0
  58. package/dist/ui.cjs +286 -35
  59. package/dist/ui.d.cts +2 -30
  60. package/dist/ui.d.ts +2 -30
  61. package/dist/ui.js +283 -36
  62. package/dist/window.cjs +596 -0
  63. package/dist/window.d.cts +63 -0
  64. package/dist/window.d.ts +63 -0
  65. package/dist/window.js +589 -0
  66. package/package.json +16 -14
  67. package/templates/basic/app/index.tsx +8 -5
  68. package/templates/foundation-models/app/index.tsx +32 -0
  69. package/templates/foundation-models/app.config.ts +5 -0
  70. package/templates/image-generation/app/index.tsx +39 -0
  71. package/templates/image-generation/app.config.ts +5 -0
  72. package/templates/state/app/index.tsx +30 -0
  73. package/templates/state/app.config.ts +5 -0
  74. package/dist/chrome.cjs +0 -265
  75. package/dist/chrome.d.cts +0 -19
  76. package/dist/chrome.d.ts +0 -19
  77. package/dist/chrome.js +0 -262
  78. package/dist/fs.cjs +0 -13
  79. package/dist/fs.d.cts +0 -8
  80. package/dist/fs.d.ts +0 -8
  81. package/dist/fs.js +0 -12
  82. package/dist/native-macos-D6p2SmUj.js +0 -20
  83. package/dist/native-macos-DLwo_ii9.cjs +0 -31
  84. package/dist/native-runtime-CbPzrs8j.d.cts +0 -33
  85. package/dist/native-runtime-DxHSrsTX.d.ts +0 -33
  86. package/dist/native.cjs +0 -135
  87. package/dist/native.d.cts +0 -34
  88. package/dist/native.d.ts +0 -34
  89. package/dist/native.js +0 -135
  90. /package/dist/{schema-DVmoD9JL.js → schema-Bo5Jvqus.js} +0 -0
  91. /package/dist/{schema-bxeeCyLn.cjs → schema-rQ13mrpD.cjs} +0 -0
package/dist/ai.js CHANGED
@@ -1,6 +1,13 @@
1
- import { NativeModules } from "react-native";
1
+ import "./native-runtime-C85Nuc4F.js";
2
+ import "./crypto-BB92-Upx.js";
3
+ import { i as files } from "./files-Ds1wT8C2.js";
4
+ import { f as atom, p as useAtom } from "./state-CAwe-Vw1.js";
5
+ import "./schema-Bo5Jvqus.js";
6
+ import { requireNativeModule } from "expo";
7
+ import { useRef, useState } from "react";
8
+ import { Image } from "react-native";
2
9
 
3
- //#region src/ai.ts
10
+ //#region src/ai/errors.ts
4
11
  var AIError = class extends Error {
5
12
  constructor(code, message, cause) {
6
13
  super(message);
@@ -9,6 +16,3759 @@ var AIError = class extends Error {
9
16
  this.name = "AIError";
10
17
  }
11
18
  };
19
+ var ImageGenerationError = class extends AIError {
20
+ stage;
21
+ constructor(stage, message, cause) {
22
+ super("image_generation_failed", message, cause);
23
+ this.name = "ImageGenerationError";
24
+ this.stage = stage;
25
+ }
26
+ };
27
+ function isRecord(value) {
28
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
29
+ }
30
+ function mapNativeError(error) {
31
+ const code = isRecord(error) && typeof error.code === "string" ? error.code : "";
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ if (code === "UNSUPPORTED_OS") return new AIError("unsupported_os", message, error);
34
+ if (code === "MODEL_UNAVAILABLE") return new AIError("model_unavailable", message, error);
35
+ if (code === "INVALID_SCHEMA") return new AIError("invalid_schema", message, error);
36
+ if (code === "INVALID_MESSAGE") return new AIError("invalid_message", message, error);
37
+ if (code === "INVALID_MODEL") return new AIError("invalid_model", message, error);
38
+ if (code === "IMAGE_GENERATION_ERROR") return new AIError("image_generation_failed", message, error);
39
+ if (code === "CONTEXT_WINDOW_EXCEEDED") return new AIError("context_window_exceeded", message, error);
40
+ if (code === "GENERATION_CANCELED") return new AIError("generation_canceled", message, error);
41
+ if (code === "CONCURRENT_REQUEST") return new AIError("concurrent_request", message, error);
42
+ if (code === "GUARDRAIL_VIOLATION") return new AIError("guardrail_violation", message, error);
43
+ if (code === "UNSUPPORTED_LANGUAGE_OR_LOCALE") return new AIError("unsupported_language_or_locale", message, error);
44
+ if (code === "RATE_LIMITED") return new AIError("rate_limited", message, error);
45
+ if (code === "TOOL_ERROR") return new AIError("tool_failed", message, error);
46
+ return new AIError("generation_failed", message, error);
47
+ }
48
+
49
+ //#endregion
50
+ //#region src/ai/image-batch.ts
51
+ function batchImageSeeds(count, seed) {
52
+ if (!Number.isInteger(count) || count < 1 || count > 64) throw new AIError("invalid_message", "Batch count must be an integer from 1 to 64");
53
+ const first = seed ?? Math.floor(Math.random() * 4294967296);
54
+ return Array.from({ length: count }, (_, index) => first + index >>> 0);
55
+ }
56
+
57
+ //#endregion
58
+ //#region src/ai/image-jobs.ts
59
+ var ImageGenerationQueue = class {
60
+ jobs = /* @__PURE__ */ new Map();
61
+ pending = [];
62
+ running;
63
+ constructor(executor) {
64
+ this.executor = executor;
65
+ }
66
+ setExecutor(executor) {
67
+ this.executor = executor;
68
+ }
69
+ schedule(options) {
70
+ const id = crypto.randomUUID();
71
+ let resolve;
72
+ let reject;
73
+ const promise = new Promise((onResolve, onReject) => {
74
+ resolve = onResolve;
75
+ reject = onReject;
76
+ });
77
+ const record = {
78
+ snapshot: {
79
+ id,
80
+ status: "queued",
81
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
82
+ },
83
+ options,
84
+ controller: new AbortController(),
85
+ listeners: /* @__PURE__ */ new Set(),
86
+ promise,
87
+ resolve,
88
+ reject
89
+ };
90
+ this.jobs.set(id, record);
91
+ this.pending.push(record);
92
+ if (options.signal) {
93
+ const cancel = () => this.cancel(id);
94
+ options.signal.addEventListener("abort", cancel, { once: true });
95
+ record.removeSignalListener = () => options.signal?.removeEventListener("abort", cancel);
96
+ if (options.signal.aborted) cancel();
97
+ }
98
+ this.runNext();
99
+ return this.handle(record);
100
+ }
101
+ get(id) {
102
+ const record = this.jobs.get(id);
103
+ return record ? this.handle(record) : void 0;
104
+ }
105
+ list() {
106
+ return [...this.jobs.values()].map(({ snapshot }) => ({ ...snapshot }));
107
+ }
108
+ cancel(id) {
109
+ const record = this.jobs.get(id);
110
+ if (!record || !["queued", "running"].includes(record.snapshot.status)) return false;
111
+ record.controller.abort();
112
+ if (record.snapshot.status === "queued") {
113
+ const index = this.pending.indexOf(record);
114
+ if (index >= 0) this.pending.splice(index, 1);
115
+ const error = canceledError();
116
+ this.update(record, {
117
+ status: "canceled",
118
+ error
119
+ });
120
+ record.removeSignalListener?.();
121
+ record.reject(error);
122
+ }
123
+ return true;
124
+ }
125
+ handle(record) {
126
+ return {
127
+ id: record.snapshot.id,
128
+ promise: record.promise,
129
+ cancel: () => this.cancel(record.snapshot.id),
130
+ getSnapshot: () => ({ ...record.snapshot }),
131
+ subscribe: (listener) => {
132
+ record.listeners.add(listener);
133
+ listener({ ...record.snapshot });
134
+ return () => record.listeners.delete(listener);
135
+ }
136
+ };
137
+ }
138
+ async runNext() {
139
+ if (this.running) return;
140
+ const record = this.pending.shift();
141
+ if (!record) return;
142
+ this.running = record;
143
+ this.update(record, {
144
+ status: "running",
145
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
146
+ });
147
+ try {
148
+ const result = await this.executor({
149
+ ...record.options,
150
+ signal: record.controller.signal,
151
+ onProgress: (progress) => {
152
+ record.options.onProgress?.(progress);
153
+ this.progress(record, progress);
154
+ }
155
+ });
156
+ this.update(record, {
157
+ status: "completed",
158
+ result,
159
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
160
+ });
161
+ record.resolve(result);
162
+ } catch (error) {
163
+ const canceled = record.controller.signal.aborted;
164
+ const failure = canceled ? canceledError(error) : error;
165
+ this.update(record, {
166
+ status: canceled ? "canceled" : "failed",
167
+ error: failure,
168
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
169
+ });
170
+ record.reject(failure);
171
+ } finally {
172
+ record.removeSignalListener?.();
173
+ this.running = void 0;
174
+ this.runNext();
175
+ }
176
+ }
177
+ progress(record, progress) {
178
+ this.update(record, { progress });
179
+ }
180
+ update(record, update) {
181
+ record.snapshot = {
182
+ ...record.snapshot,
183
+ ...update
184
+ };
185
+ for (const listener of record.listeners) listener({ ...record.snapshot });
186
+ }
187
+ };
188
+ function canceledError(cause) {
189
+ return new AIError("generation_canceled", "Image generation canceled", cause);
190
+ }
191
+
192
+ //#endregion
193
+ //#region src/ai/image-models.ts
194
+ const flux2Klein4B8BitAbliterated = {
195
+ formatVersion: 1,
196
+ model: "flux2-klein-4b-8bit-abliterated",
197
+ directory: "flux2-klein-4b-8bit-abliterated",
198
+ metadata: {
199
+ displayName: "Flux.2 Klein 4B Abliterated",
200
+ family: "flux2-klein",
201
+ precision: "8-bit",
202
+ estimatedMemoryGB: 12,
203
+ quality: "high",
204
+ speed: "fast",
205
+ capabilities: [
206
+ "text-to-image",
207
+ "image-to-image",
208
+ "inpainting",
209
+ "outpainting",
210
+ "references",
211
+ "lora"
212
+ ]
213
+ },
214
+ components: {
215
+ textEncoder: {
216
+ repo: "mlx-community/Josiefied-Qwen3-4B-abliterated-v1-8bit",
217
+ revision: "main",
218
+ path: "text-encoder",
219
+ files: [
220
+ "config.json",
221
+ "tokenizer.json",
222
+ "tokenizer_config.json",
223
+ "*.safetensors",
224
+ "*.safetensors.index.json"
225
+ ]
226
+ },
227
+ transformer: {
228
+ repo: "moxin-org/FLUX.2-klein-4B-8bit-mlx",
229
+ revision: "main",
230
+ path: "transformer",
231
+ files: ["config.json", "quantized_8bit.safetensors"]
232
+ },
233
+ vae: {
234
+ repo: "black-forest-labs/FLUX.2-klein-4B",
235
+ revision: "main",
236
+ path: "vae",
237
+ files: [
238
+ "config.json",
239
+ "diffusion_pytorch_model.safetensors",
240
+ "diffusion_pytorch_model.safetensors.index.json",
241
+ "diffusion_pytorch_model-*.safetensors"
242
+ ]
243
+ }
244
+ }
245
+ };
246
+ const imageModelManifests = [flux2Klein4B8BitAbliterated];
247
+ function imageModelManifest(model) {
248
+ return imageModelManifests.find((manifest) => manifest.model === model);
249
+ }
250
+ const flux2KleinModelFiles = [
251
+ {
252
+ repo: flux2Klein4B8BitAbliterated.components.textEncoder.repo,
253
+ revision: flux2Klein4B8BitAbliterated.components.textEncoder.revision,
254
+ source: "config.json",
255
+ destination: "text-encoder/config.json",
256
+ bytes: 937
257
+ },
258
+ {
259
+ repo: flux2Klein4B8BitAbliterated.components.textEncoder.repo,
260
+ revision: flux2Klein4B8BitAbliterated.components.textEncoder.revision,
261
+ source: "tokenizer.json",
262
+ destination: "text-encoder/tokenizer.json",
263
+ bytes: 11422654
264
+ },
265
+ {
266
+ repo: flux2Klein4B8BitAbliterated.components.textEncoder.repo,
267
+ revision: flux2Klein4B8BitAbliterated.components.textEncoder.revision,
268
+ source: "tokenizer_config.json",
269
+ destination: "text-encoder/tokenizer_config.json",
270
+ bytes: 9706
271
+ },
272
+ {
273
+ repo: flux2Klein4B8BitAbliterated.components.textEncoder.repo,
274
+ revision: flux2Klein4B8BitAbliterated.components.textEncoder.revision,
275
+ source: "model.safetensors",
276
+ destination: "text-encoder/model.safetensors",
277
+ bytes: 4274158989
278
+ },
279
+ {
280
+ repo: flux2Klein4B8BitAbliterated.components.textEncoder.repo,
281
+ revision: flux2Klein4B8BitAbliterated.components.textEncoder.revision,
282
+ source: "model.safetensors.index.json",
283
+ destination: "text-encoder/model.safetensors.index.json",
284
+ bytes: 63924
285
+ },
286
+ {
287
+ repo: flux2Klein4B8BitAbliterated.components.transformer.repo,
288
+ revision: flux2Klein4B8BitAbliterated.components.transformer.revision,
289
+ source: "transformer/config.json",
290
+ destination: "transformer/config.json",
291
+ bytes: 541
292
+ },
293
+ {
294
+ repo: flux2Klein4B8BitAbliterated.components.transformer.repo,
295
+ revision: flux2Klein4B8BitAbliterated.components.transformer.revision,
296
+ source: "transformer/quantized_8bit.safetensors",
297
+ destination: "transformer/quantized_8bit.safetensors",
298
+ bytes: 4360050983
299
+ },
300
+ {
301
+ repo: flux2Klein4B8BitAbliterated.components.vae.repo,
302
+ revision: flux2Klein4B8BitAbliterated.components.vae.revision,
303
+ source: "vae/config.json",
304
+ destination: "vae/config.json",
305
+ bytes: 821
306
+ },
307
+ {
308
+ repo: flux2Klein4B8BitAbliterated.components.vae.repo,
309
+ revision: flux2Klein4B8BitAbliterated.components.vae.revision,
310
+ source: "vae/diffusion_pytorch_model.safetensors",
311
+ destination: "vae/diffusion_pytorch_model.safetensors",
312
+ bytes: 168120878
313
+ }
314
+ ];
315
+ const flux2KleinTotalBytes = flux2KleinModelFiles.reduce((total, file) => total + file.bytes, 0);
316
+ function joinModelPath(root, component) {
317
+ return `${root.replace(/\/+$/, "")}/${component.replace(/^\/+/, "")}`;
318
+ }
319
+ function fluxImageModelPaths(modelRoot, manifest = flux2Klein4B8BitAbliterated) {
320
+ return {
321
+ modelRoot,
322
+ textEncoderPath: joinModelPath(modelRoot, manifest.components.textEncoder.path),
323
+ transformerPath: joinModelPath(modelRoot, manifest.components.transformer.path),
324
+ vaePath: joinModelPath(modelRoot, manifest.components.vae.path)
325
+ };
326
+ }
327
+
328
+ //#endregion
329
+ //#region src/ai/image-schedule.ts
330
+ function imageSigmaSchedule(schedules) {
331
+ const configured = schedules.filter((schedule) => schedule !== void 0);
332
+ if (configured.length === 0) return void 0;
333
+ const first = configured[0];
334
+ if (first.length === 0 || first.some((sigma, index) => !Number.isFinite(sigma) || sigma <= 0 || sigma > 1 || index > 0 && sigma >= first[index - 1])) throw new AIError("invalid_model", "LoRA sigmaSchedule must contain descending values greater than 0 and at most 1");
335
+ if (configured.some((schedule) => schedule.length !== first.length || schedule.some((sigma, index) => sigma !== first[index]))) throw new AIError("invalid_model", "Selected LoRAs require conflicting sigma schedules");
336
+ return [...first];
337
+ }
338
+
339
+ //#endregion
340
+ //#region src/ai/image-upscale.ts
341
+ function normalizeUpscaleScale(value) {
342
+ const scale = value ?? 2;
343
+ if (!Number.isFinite(scale) || scale <= 1 || scale > 4) throw new AIError("invalid_model", "Upscale scale must be greater than 1 and at most 4");
344
+ return scale;
345
+ }
346
+
347
+ //#endregion
348
+ //#region src/ai/native.ts
349
+ function bridge() {
350
+ try {
351
+ return requireNativeModule("AI");
352
+ } catch (error) {
353
+ throw new AIError("unsupported_runtime", "bleam/ai requires the Bleam native runtime", error);
354
+ }
355
+ }
356
+
357
+ //#endregion
358
+ //#region src/ai/tokenizers.mjs
359
+ var DictionarySplitter = class {
360
+ /**
361
+ * @param dictionary The dictionary of words to use for splitting.
362
+ */
363
+ constructor(dictionary) {
364
+ this.trie = this._build_trie(dictionary);
365
+ }
366
+ /**
367
+ * Builds a trie from the given dictionary.
368
+ * @param dictionary The dictionary of words to build the trie from.
369
+ * @returns The root node of the trie.
370
+ * @private
371
+ */
372
+ _build_trie(dictionary) {
373
+ const trie = /* @__PURE__ */ Object.create(null);
374
+ for (const word of dictionary) {
375
+ let node = trie;
376
+ for (let i = 0; i < word.length; ++i) {
377
+ const char = word[i];
378
+ node = node[char] ??= /* @__PURE__ */ Object.create(null);
379
+ }
380
+ node.end = word;
381
+ }
382
+ return trie;
383
+ }
384
+ /**
385
+ * Splits the input text into tokens based on the dictionary.
386
+ * @param text The input text to split.
387
+ * @returns An array of tokens.
388
+ */
389
+ split(text) {
390
+ const result = [];
391
+ const n = text.length;
392
+ let start = 0;
393
+ let i = 0;
394
+ while (i < n) {
395
+ let node = this.trie;
396
+ let match = null;
397
+ let j = i;
398
+ while (j < n && (node = node[text[j]])) {
399
+ if (node.end) match = node.end;
400
+ ++j;
401
+ }
402
+ if (match) {
403
+ if (i > start) result.push(text.slice(start, i));
404
+ result.push(match);
405
+ i += match.length;
406
+ start = i;
407
+ } else ++i;
408
+ }
409
+ if (start < n) result.push(text.slice(start));
410
+ return result;
411
+ }
412
+ };
413
+ var DictionarySplitter_default = DictionarySplitter;
414
+ var AddedToken = class {
415
+ /**
416
+ * Creates a new instance of AddedToken.
417
+ * @param config Added token configuration object.
418
+ */
419
+ constructor(config) {
420
+ this.content = config.content;
421
+ this.id = config.id;
422
+ this.single_word = config.single_word ?? false;
423
+ this.lstrip = config.lstrip ?? false;
424
+ this.rstrip = config.rstrip ?? false;
425
+ this.special = config.special ?? false;
426
+ this.normalized = config.normalized ?? !this.special;
427
+ }
428
+ };
429
+ var AddedToken_default = AddedToken;
430
+ var BYTES_TO_UNICODE = (() => {
431
+ const bs = [
432
+ ...Array.from({ length: "~".charCodeAt(0) - "!".charCodeAt(0) + 1 }, (_, i) => i + "!".charCodeAt(0)),
433
+ ...Array.from({ length: "¬".charCodeAt(0) - "¡".charCodeAt(0) + 1 }, (_, i) => i + "¡".charCodeAt(0)),
434
+ ...Array.from({ length: "ÿ".charCodeAt(0) - "®".charCodeAt(0) + 1 }, (_, i) => i + "®".charCodeAt(0))
435
+ ];
436
+ const cs = bs.slice();
437
+ let n = 0;
438
+ for (let b = 0; b < 256; ++b) if (!bs.includes(b)) {
439
+ bs.push(b);
440
+ cs.push(256 + n);
441
+ n += 1;
442
+ }
443
+ const ccs = cs.map((n2) => String.fromCharCode(n2));
444
+ return Object.fromEntries(bs.map((b, i) => [b, ccs[i]]));
445
+ })();
446
+ var reverse_dictionary = (data) => Object.fromEntries(Object.entries(data).map(([key, value]) => [value, key]));
447
+ var UNICODE_TO_BYTES = reverse_dictionary(BYTES_TO_UNICODE);
448
+ var BLOOM_SPLIT_CHARS = ".,!?…。,、।۔،";
449
+ var PROBLEMATIC_REGEX_MAP = /* @__PURE__ */ new Map([
450
+ ["(?i:'s|'t|'re|'ve|'m|'ll|'d)", "(?:'([sS]|[tT]|[rR][eE]|[vV][eE]|[mM]|[lL][lL]|[dD]))"],
451
+ ["(?i:[sdmt]|ll|ve|re)", "(?:[sS]|[dD]|[mM]|[tT]|[lL][lL]|[vV][eE]|[rR][eE])"],
452
+ ["[^\\r\\n\\p{L}\\p{N}]?+", "[^\\r\\n\\p{L}\\p{N}]?"],
453
+ ["[^\\s\\p{L}\\p{N}]++", "[^\\s\\p{L}\\p{N}]+"],
454
+ ["(?>\\p{Nd}{510})", "(?:\\p{Nd}{510})"],
455
+ ["\\p{Nd}{3}+", "(?:\\p{Nd}{3})+"],
456
+ ["\\G", ""],
457
+ [` ?[^(\\s|[${BLOOM_SPLIT_CHARS}])]+`, ` ?[^\\s${BLOOM_SPLIT_CHARS}]+`]
458
+ ]);
459
+ var PUNCTUATION_REGEX = "\\p{P}\\u0021-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E";
460
+ 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");
461
+ var create_pattern = (pattern, invert = true) => {
462
+ if (pattern.Regex !== void 0) {
463
+ let regex = pattern.Regex.replace(/\\([#&~])/g, "$1");
464
+ regex = regex.replace(/\\A/g, "^").replace(/\\z/g, "$").replace(/\\Z/g, "(?=\\r?\\n?$)");
465
+ for (const [key, value] of PROBLEMATIC_REGEX_MAP) regex = regex.replaceAll(key, value);
466
+ try {
467
+ return new RegExp(regex, "gu");
468
+ } catch (error) {
469
+ if (!(error instanceof SyntaxError) || !error.message.toLowerCase().includes("invalid property name")) throw error;
470
+ let changed = false;
471
+ const fixed = regex.replace(/(\\[pP])\{([^}=]+)\}/g, (_, p, n) => {
472
+ try {
473
+ new RegExp(`\\p{${n}}`, "u");
474
+ return `${p}{${n}}`;
475
+ } catch {
476
+ changed = true;
477
+ return `${p}{Script=${n}}`;
478
+ }
479
+ });
480
+ if (!changed) throw error;
481
+ try {
482
+ return new RegExp(fixed, "gu");
483
+ } catch (e) {
484
+ throw error;
485
+ }
486
+ }
487
+ } else if (pattern.String !== void 0) {
488
+ const escaped = escape_reg_exp(pattern.String);
489
+ return new RegExp(invert ? escaped : `(${escaped})`, "gu");
490
+ } else {
491
+ console.warn("Unknown pattern type:", pattern);
492
+ return null;
493
+ }
494
+ };
495
+ var escape_reg_exp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
496
+ var fuse_unk = (arr, tokens_to_ids, unk_token_id) => {
497
+ const fused = [];
498
+ let i = 0;
499
+ while (i < arr.length) {
500
+ fused.push(arr[i]);
501
+ if ((tokens_to_ids.get(arr[i]) ?? unk_token_id) !== unk_token_id) {
502
+ ++i;
503
+ continue;
504
+ }
505
+ while (++i < arr.length && (tokens_to_ids.get(arr[i]) ?? unk_token_id) === unk_token_id) if (tokens_to_ids.get(fused.at(-1)) !== unk_token_id) fused[fused.length - 1] += arr[i];
506
+ }
507
+ return fused;
508
+ };
509
+ 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;
510
+ var is_integral_number = (x) => Number.isInteger(x) || typeof x === "bigint";
511
+ var len = (s) => {
512
+ let length = 0;
513
+ for (const c of s) ++length;
514
+ return length;
515
+ };
516
+ var lowercase_and_remove_accents = (text) => remove_accents(text.toLowerCase());
517
+ var merge_arrays = (...arrs) => Array.prototype.concat.apply([], arrs);
518
+ var object_to_map = (obj) => new Map(Object.entries(obj));
519
+ var regex_split = (text, regex) => {
520
+ const result = [];
521
+ let prev = 0;
522
+ for (const match of text.matchAll(regex)) {
523
+ const full_match = match[0];
524
+ if (prev < match.index) result.push(text.slice(prev, match.index));
525
+ if (full_match.length > 0) result.push(full_match);
526
+ prev = match.index + full_match.length;
527
+ }
528
+ if (prev < text.length) result.push(text.slice(prev));
529
+ return result;
530
+ };
531
+ var remove_accents = (text) => text.replace(/\p{M}/gu, "");
532
+ var validate_object = (obj, name, required_keys = []) => {
533
+ if (!obj || Array.isArray(obj) || typeof obj !== "object") return `${name} must be a valid object`;
534
+ for (const key of required_keys) if (!(key in obj)) return `${name} must contain a "${key}" property`;
535
+ return null;
536
+ };
537
+ var whitespace_split = (text) => text.match(/\S+/g) || [];
538
+ var Callable = class {
539
+ /**
540
+ * Creates a new instance of the Callable class.
541
+ */
542
+ constructor() {
543
+ const closure = function(...args) {
544
+ return closure._call(...args);
545
+ };
546
+ return Object.setPrototypeOf(closure, new.target.prototype);
547
+ }
548
+ };
549
+ var Callable_default = Callable;
550
+ var Normalizer = class extends Callable_default {
551
+ /**
552
+ * @param config The configuration object for the normalizer.
553
+ */
554
+ constructor(config) {
555
+ super();
556
+ this.config = config;
557
+ }
558
+ /**
559
+ * Alias for {@link Normalizer#normalize}.
560
+ * @param text The text to normalize.
561
+ * @returns The normalized text.
562
+ */
563
+ _call(text) {
564
+ return this.normalize(text);
565
+ }
566
+ };
567
+ var Normalizer_default = Normalizer;
568
+ var BertNormalizer = class extends Normalizer_default {
569
+ /**
570
+ * Adds whitespace around any CJK (Chinese, Japanese, or Korean) character in the input text.
571
+ *
572
+ * @param text The input text to tokenize.
573
+ * @returns The tokenized text with whitespace added around CJK characters.
574
+ */
575
+ tokenize_chinese_chars(text) {
576
+ const output = [];
577
+ for (let i = 0; i < text.length; ++i) {
578
+ const char = text[i];
579
+ if (is_chinese_char(char.charCodeAt(0))) {
580
+ output.push(" ");
581
+ output.push(char);
582
+ output.push(" ");
583
+ } else output.push(char);
584
+ }
585
+ return output.join("");
586
+ }
587
+ /**
588
+ * Strips accents from the given text.
589
+ * @param text The text to strip accents from.
590
+ * @returns The text with accents removed.
591
+ */
592
+ strip_accents(text) {
593
+ return text.normalize("NFD").replace(/\p{Mn}/gu, "");
594
+ }
595
+ /**
596
+ * Checks whether `char` is a control character.
597
+ * @param char The character to check.
598
+ * @returns Whether `char` is a control character.
599
+ */
600
+ is_control(char) {
601
+ switch (char) {
602
+ case " ":
603
+ case "\n":
604
+ case "\r": return false;
605
+ default: return /^\p{Cc}|\p{Cf}|\p{Co}|\p{Cs}$/u.test(char);
606
+ }
607
+ }
608
+ /**
609
+ * Performs invalid character removal and whitespace cleanup on text.
610
+ * @param text The text to clean.
611
+ * @returns The cleaned text.
612
+ */
613
+ clean_text(text) {
614
+ const output = [];
615
+ for (const char of text) {
616
+ const cp = char.charCodeAt(0);
617
+ if (cp === 0 || cp === 65533 || this.is_control(char)) continue;
618
+ if (/^\s$/.test(char)) output.push(" ");
619
+ else output.push(char);
620
+ }
621
+ return output.join("");
622
+ }
623
+ /**
624
+ * Normalizes the given text based on the configuration.
625
+ * @param text The text to normalize.
626
+ * @returns The normalized text.
627
+ */
628
+ normalize(text) {
629
+ if (this.config.clean_text) text = this.clean_text(text);
630
+ if (this.config.handle_chinese_chars) text = this.tokenize_chinese_chars(text);
631
+ if (this.config.lowercase) {
632
+ text = text.toLowerCase();
633
+ if (this.config.strip_accents !== false) text = this.strip_accents(text);
634
+ } else if (this.config.strip_accents) text = this.strip_accents(text);
635
+ return text;
636
+ }
637
+ };
638
+ var BertNormalizer_default = BertNormalizer;
639
+ var Precompiled = class extends Normalizer_default {
640
+ /**
641
+ * Create a new instance of Precompiled normalizer.
642
+ * @param config The configuration object.
643
+ */
644
+ constructor(config) {
645
+ super(config);
646
+ this.charsmap = config.precompiled_charsmap ?? null;
647
+ }
648
+ /**
649
+ * Normalizes the given text by applying the precompiled charsmap.
650
+ * @param text The text to normalize.
651
+ * @returns The normalized text.
652
+ */
653
+ normalize(text) {
654
+ text = text.replace(/[\u0001-\u0008\u000B\u000E-\u001F\u007F\u008F\u009F]/gm, "");
655
+ text = text.replace(/[\u0009\u000A\u000C\u000D\u00A0\u1680\u2000-\u200F\u2028\u2029\u202F\u205F\u2581\u3000\uFEFF\uFFFD]/gm, " ");
656
+ if (text.includes("~")) text = text.split("~").map((part) => part.normalize("NFKC")).join("~");
657
+ else text = text.normalize("NFKC");
658
+ return text;
659
+ }
660
+ };
661
+ var Precompiled_default = Precompiled;
662
+ var Sequence = class extends Normalizer_default {
663
+ /**
664
+ * Create a new instance of NormalizerSequence.
665
+ * @param config The configuration object.
666
+ */
667
+ constructor(config) {
668
+ super(config);
669
+ this.normalizers = (config.normalizers ?? []).map((x) => create_normalizer_default(x));
670
+ }
671
+ /**
672
+ * Apply a sequence of Normalizers to the input text.
673
+ * @param text The text to normalize.
674
+ * @returns The normalized text.
675
+ */
676
+ normalize(text) {
677
+ return this.normalizers.reduce((t, normalizer) => {
678
+ return normalizer ? normalizer.normalize(t) : t;
679
+ }, text);
680
+ }
681
+ };
682
+ var Sequence_default = Sequence;
683
+ var Replace = class extends Normalizer_default {
684
+ /**
685
+ * Normalize the input text by replacing the pattern with the content.
686
+ * @param text The input text to be normalized.
687
+ * @returns The normalized text after replacing the pattern with the content.
688
+ */
689
+ normalize(text) {
690
+ const pattern = create_pattern(this.config.pattern ?? {});
691
+ return pattern === null ? text : text.replaceAll(pattern, this.config.content ?? "");
692
+ }
693
+ };
694
+ var Replace_default = Replace;
695
+ var UnicodeNormalizer = class extends Normalizer_default {
696
+ constructor() {
697
+ super(...arguments);
698
+ /**
699
+ * The Unicode normalization form to apply.
700
+ * Should be one of: 'NFC', 'NFD', 'NFKC', or 'NFKD'.
701
+ */
702
+ this.form = "NFC";
703
+ }
704
+ /**
705
+ * Normalize the input text by applying Unicode normalization.
706
+ * @param text The input text to be normalized.
707
+ * @returns The normalized text.
708
+ */
709
+ normalize(text) {
710
+ text = text.normalize(this.form);
711
+ return text;
712
+ }
713
+ };
714
+ var UnicodeNormalizer_default = UnicodeNormalizer;
715
+ var NFC = class extends UnicodeNormalizer_default {
716
+ constructor() {
717
+ super(...arguments);
718
+ this.form = "NFC";
719
+ }
720
+ };
721
+ var NFC_default = NFC;
722
+ var NFD = class extends UnicodeNormalizer_default {
723
+ constructor() {
724
+ super(...arguments);
725
+ this.form = "NFD";
726
+ }
727
+ };
728
+ var NFD_default = NFD;
729
+ var NFKC = class extends UnicodeNormalizer_default {
730
+ constructor() {
731
+ super(...arguments);
732
+ this.form = "NFKC";
733
+ }
734
+ };
735
+ var NFKC_default = NFKC;
736
+ var NFKD = class extends UnicodeNormalizer_default {
737
+ constructor() {
738
+ super(...arguments);
739
+ this.form = "NFKD";
740
+ }
741
+ };
742
+ var NFKD_default = NFKD;
743
+ var Strip = class extends Normalizer_default {
744
+ /**
745
+ * Strip leading and/or trailing whitespace from the input text.
746
+ * @param text The input text.
747
+ * @returns The normalized text.
748
+ */
749
+ normalize(text) {
750
+ if (this.config.strip_left && this.config.strip_right) text = text.trim();
751
+ else {
752
+ if (this.config.strip_left) text = text.trimStart();
753
+ if (this.config.strip_right) text = text.trimEnd();
754
+ }
755
+ return text;
756
+ }
757
+ };
758
+ var Strip_default = Strip;
759
+ var StripAccents = class extends Normalizer_default {
760
+ /**
761
+ * Remove all accents from the text.
762
+ * @param text The input text.
763
+ * @returns The normalized text without accents.
764
+ */
765
+ normalize(text) {
766
+ return remove_accents(text);
767
+ }
768
+ };
769
+ var StripAccents_default = StripAccents;
770
+ var Lowercase = class extends Normalizer_default {
771
+ /**
772
+ * Lowercases the input string.
773
+ * @param {string} text The text to normalize.
774
+ * @returns {string} The normalized text.
775
+ */
776
+ normalize(text) {
777
+ return text.toLowerCase();
778
+ }
779
+ };
780
+ var Lowercase_default = Lowercase;
781
+ var Prepend = class extends Normalizer_default {
782
+ /**
783
+ * Prepends the input string.
784
+ * @param text The text to normalize.
785
+ * @returns The normalized text.
786
+ */
787
+ normalize(text) {
788
+ text = this.config.prepend + text;
789
+ return text;
790
+ }
791
+ };
792
+ var Prepend_default = Prepend;
793
+ function create_normalizer(config) {
794
+ if (config === null) return null;
795
+ switch (config.type) {
796
+ case "BertNormalizer": return new BertNormalizer_default(config);
797
+ case "Precompiled": return new Precompiled_default(config);
798
+ case "Sequence": return new Sequence_default(config);
799
+ case "Replace": return new Replace_default(config);
800
+ case "NFC": return new NFC_default(config);
801
+ case "NFD": return new NFD_default(config);
802
+ case "NFKC": return new NFKC_default(config);
803
+ case "NFKD": return new NFKD_default(config);
804
+ case "Strip": return new Strip_default(config);
805
+ case "StripAccents": return new StripAccents_default(config);
806
+ case "Lowercase": return new Lowercase_default(config);
807
+ case "Prepend": return new Prepend_default(config);
808
+ default: throw new Error(`Unknown Normalizer type: ${config.type}`);
809
+ }
810
+ }
811
+ var create_normalizer_default = create_normalizer;
812
+ var PreTokenizer = class extends Callable_default {
813
+ /**
814
+ * Tokenizes the given text into pre-tokens.
815
+ * @param text The text or array of texts to pre-tokenize.
816
+ * @param options Additional options for the pre-tokenization logic.
817
+ * @returns An array of pre-tokens.
818
+ */
819
+ pre_tokenize(text, options) {
820
+ return (Array.isArray(text) ? text.map((x) => this.pre_tokenize_text(x, options)) : this.pre_tokenize_text(text, options)).flat();
821
+ }
822
+ /**
823
+ * Alias for {@link PreTokenizer#pre_tokenize}.
824
+ * @param text The text or array of texts to pre-tokenize.
825
+ * @param options Additional options for the pre-tokenization logic.
826
+ * @returns An array of pre-tokens.
827
+ */
828
+ _call(text, options) {
829
+ return this.pre_tokenize(text, options);
830
+ }
831
+ };
832
+ var PreTokenizer_default = PreTokenizer;
833
+ var ByteLevel = class extends PreTokenizer_default {
834
+ /**
835
+ * Creates a new instance of the `ByteLevelPreTokenizer` class.
836
+ * @param config The configuration object.
837
+ */
838
+ constructor(config) {
839
+ super();
840
+ this.config = config;
841
+ this.add_prefix_space = this.config.add_prefix_space ?? false;
842
+ this.trim_offsets = this.config.trim_offsets ?? false;
843
+ this.use_regex = this.config.use_regex ?? true;
844
+ this.pattern = /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
845
+ this.byte_encoder = BYTES_TO_UNICODE;
846
+ this.text_encoder = new TextEncoder();
847
+ }
848
+ /**
849
+ * Tokenizes a single piece of text using byte-level tokenization.
850
+ * @param text The text to tokenize.
851
+ * @param options Additional options for the pre-tokenization logic.
852
+ * @returns An array of tokens.
853
+ */
854
+ pre_tokenize_text(text, options) {
855
+ if (this.add_prefix_space && !text.startsWith(" ")) text = " " + text;
856
+ return (this.use_regex ? text.match(this.pattern) || [] : [text]).map((token) => Array.from(this.text_encoder.encode(token), (byte) => this.byte_encoder[byte]).join(""));
857
+ }
858
+ };
859
+ var ByteLevel_default = ByteLevel;
860
+ var Whitespace = class extends PreTokenizer_default {
861
+ /**
862
+ * Pre-tokenizes the input text by splitting it on word boundaries.
863
+ * @param text The text to be pre-tokenized.
864
+ * @param options Additional options for the pre-tokenization logic.
865
+ * @returns An array of tokens produced by splitting the input text on whitespace.
866
+ */
867
+ pre_tokenize_text(text, options) {
868
+ return text.match(/\w+|[^\w\s]+/g) || [];
869
+ }
870
+ };
871
+ var Whitespace_default = Whitespace;
872
+ var Metaspace = class extends PreTokenizer_default {
873
+ /**
874
+ * @param config The configuration object for the MetaspacePreTokenizer.
875
+ */
876
+ constructor(config) {
877
+ super();
878
+ this.replacement = config.replacement ?? "▁";
879
+ this.str_rep = config.str_rep || this.replacement;
880
+ this.prepend_scheme = config.prepend_scheme ?? "always";
881
+ }
882
+ /**
883
+ * This method takes a string, replaces spaces with the replacement character,
884
+ * adds a prefix space if requested, and returns a new list of tokens.
885
+ * @param text The text to pre-tokenize.
886
+ * @param options The options for the pre-tokenization.
887
+ * @returns A new list of pre-tokenized tokens.
888
+ */
889
+ pre_tokenize_text(text, options) {
890
+ const { section_index = void 0 } = options ?? {};
891
+ let normalized = text.replaceAll(" ", this.str_rep);
892
+ if (!normalized.startsWith(this.replacement) && (this.prepend_scheme === "always" || this.prepend_scheme === "first" && section_index === 0)) normalized = this.str_rep + normalized;
893
+ return [normalized];
894
+ }
895
+ };
896
+ var Metaspace_default = Metaspace;
897
+ var Split = class extends PreTokenizer_default {
898
+ /**
899
+ * @param config The configuration options for the pre-tokenizer.
900
+ */
901
+ constructor(config) {
902
+ super();
903
+ this.config = config;
904
+ this.pattern = create_pattern(this.config.pattern ?? {}, this.config.invert ?? true);
905
+ }
906
+ /**
907
+ * Tokenizes text by splitting it using the given pattern.
908
+ * @param text The text to tokenize.
909
+ * @returns An array of tokens.
910
+ */
911
+ pre_tokenize_text(text) {
912
+ if (this.pattern === null) return [];
913
+ if (this.config.invert) return text.match(this.pattern) || [];
914
+ else if (this.config.behavior?.toLowerCase() === "removed") return text.split(this.pattern).filter((x) => x);
915
+ else return regex_split(text, this.pattern);
916
+ }
917
+ };
918
+ var Split_default = Split;
919
+ var Punctuation = class extends PreTokenizer_default {
920
+ /**
921
+ * @param config The configuration options for the pre-tokenizer.
922
+ */
923
+ constructor(config) {
924
+ super();
925
+ this.config = config;
926
+ this.pattern = new RegExp(`[^${PUNCTUATION_REGEX}]+|[${PUNCTUATION_REGEX}]+`, "gu");
927
+ }
928
+ /**
929
+ * Tokenizes text by splitting it using the given pattern.
930
+ * @param text The text to tokenize.
931
+ * @returns An array of tokens.
932
+ */
933
+ pre_tokenize_text(text) {
934
+ return text.match(this.pattern) || [];
935
+ }
936
+ };
937
+ var Punctuation_default = Punctuation;
938
+ var Digits = class extends PreTokenizer_default {
939
+ /**
940
+ * @param config The configuration options for the pre-tokenizer.
941
+ */
942
+ constructor(config) {
943
+ super();
944
+ this.config = config;
945
+ const digit_pattern = `[^\\d]+|\\d${this.config.individual_digits ? "" : "+"}`;
946
+ this.pattern = new RegExp(digit_pattern, "gu");
947
+ }
948
+ /**
949
+ * Tokenizes text by splitting it using the given pattern.
950
+ * @param text The text to tokenize.
951
+ * @returns An array of tokens.
952
+ */
953
+ pre_tokenize_text(text) {
954
+ return text.match(this.pattern) || [];
955
+ }
956
+ };
957
+ var Digits_default = Digits;
958
+ var BertPreTokenizer = class extends PreTokenizer_default {
959
+ /**
960
+ * A PreTokenizer that splits text into wordpieces using a basic tokenization scheme
961
+ * similar to that used in the original implementation of BERT.
962
+ */
963
+ constructor() {
964
+ super();
965
+ this.pattern = new RegExp(`[^\\s${PUNCTUATION_REGEX}]+|[${PUNCTUATION_REGEX}]`, "gu");
966
+ }
967
+ /**
968
+ * Tokenizes a single text using the BERT pre-tokenization scheme.
969
+ *
970
+ * @param text The text to tokenize.
971
+ * @param options Additional options for the pre-tokenization logic.
972
+ * @returns An array of tokens.
973
+ */
974
+ pre_tokenize_text(text, options) {
975
+ return text.trim().match(this.pattern) || [];
976
+ }
977
+ };
978
+ var BertPreTokenizer_default = BertPreTokenizer;
979
+ var Replace2 = class extends PreTokenizer_default {
980
+ /**
981
+ * @param config The configuration options for the pre-tokenizer.
982
+ */
983
+ constructor(config) {
984
+ super();
985
+ this.config = config;
986
+ this.pattern = create_pattern(this.config.pattern ?? {});
987
+ this.content = this.config.content ?? "";
988
+ }
989
+ /**
990
+ * Pre-tokenizes the input text by replacing certain characters.
991
+ * @param text The text to be pre-tokenized.
992
+ * @returns An array of tokens produced by replacing certain characters.
993
+ */
994
+ pre_tokenize_text(text) {
995
+ if (this.pattern === null) return [text];
996
+ return [text.replaceAll(this.pattern, this.config.content ?? "")];
997
+ }
998
+ };
999
+ var Replace_default2 = Replace2;
1000
+ var Sequence2 = class extends PreTokenizer_default {
1001
+ /**
1002
+ * Creates an instance of PreTokenizerSequence.
1003
+ * @param config The configuration object for the pre-tokenizer sequence.
1004
+ */
1005
+ constructor(config) {
1006
+ super();
1007
+ this.tokenizers = (config.pretokenizers ?? []).map((x) => create_pre_tokenizer_default(x));
1008
+ }
1009
+ /**
1010
+ * Applies each pre-tokenizer in the sequence to the input text in turn.
1011
+ * @param text The text to pre-tokenize.
1012
+ * @param options Additional options for the pre-tokenization logic.
1013
+ * @returns The pre-tokenized text.
1014
+ */
1015
+ pre_tokenize_text(text, options) {
1016
+ return this.tokenizers.reduce((pre_tokenized_text, tokenizer) => {
1017
+ return tokenizer ? tokenizer.pre_tokenize(pre_tokenized_text, options) : pre_tokenized_text;
1018
+ }, [text]);
1019
+ }
1020
+ };
1021
+ var Sequence_default2 = Sequence2;
1022
+ var WhitespaceSplit = class extends PreTokenizer_default {
1023
+ /**
1024
+ * Pre-tokenizes the input text by splitting it on whitespace characters.
1025
+ * @param text The text to be pre-tokenized.
1026
+ * @returns An array of tokens produced by splitting the input text on whitespace.
1027
+ */
1028
+ pre_tokenize_text(text) {
1029
+ return whitespace_split(text);
1030
+ }
1031
+ };
1032
+ var WhitespaceSplit_default = WhitespaceSplit;
1033
+ var FixedLength = class extends PreTokenizer_default {
1034
+ /**
1035
+ * @param config The configuration options for the pre-tokenizer.
1036
+ */
1037
+ constructor(config) {
1038
+ super();
1039
+ this.config = config;
1040
+ this._length = config.length;
1041
+ }
1042
+ /**
1043
+ * Pre-tokenizes the input text by splitting it into fixed-length tokens.
1044
+ * @param text The text to be pre-tokenized.
1045
+ * @returns An array of tokens produced by splitting the input text into fixed-length tokens.
1046
+ */
1047
+ pre_tokenize_text(text) {
1048
+ const tokens = [];
1049
+ for (let i = 0; i < text.length; i += this._length) tokens.push(text.slice(i, i + this._length));
1050
+ return tokens;
1051
+ }
1052
+ };
1053
+ var FixedLength_default = FixedLength;
1054
+ function create_pre_tokenizer(config) {
1055
+ if (config === null) return null;
1056
+ switch (config.type) {
1057
+ case "BertPreTokenizer": return new BertPreTokenizer_default();
1058
+ case "Sequence": return new Sequence_default2(config);
1059
+ case "Whitespace": return new Whitespace_default();
1060
+ case "WhitespaceSplit": return new WhitespaceSplit_default();
1061
+ case "Metaspace": return new Metaspace_default(config);
1062
+ case "ByteLevel": return new ByteLevel_default(config);
1063
+ case "Split": return new Split_default(config);
1064
+ case "Punctuation": return new Punctuation_default(config);
1065
+ case "Digits": return new Digits_default(config);
1066
+ case "Replace": return new Replace_default2(config);
1067
+ case "FixedLength": return new FixedLength_default(config);
1068
+ default: throw new Error(`Unknown PreTokenizer type: ${config.type}`);
1069
+ }
1070
+ }
1071
+ var create_pre_tokenizer_default = create_pre_tokenizer;
1072
+ var TokenizerModel = class extends Callable_default {
1073
+ /**
1074
+ * Creates a new instance of TokenizerModel.
1075
+ * @param config The configuration object for the TokenizerModel.
1076
+ */
1077
+ constructor(config) {
1078
+ super();
1079
+ this.config = config;
1080
+ this.vocab = [];
1081
+ this.tokens_to_ids = /* @__PURE__ */ new Map();
1082
+ this.unk_token_id = void 0;
1083
+ this.unk_token = void 0;
1084
+ this.end_of_word_suffix = void 0;
1085
+ this.fuse_unk = this.config.fuse_unk ?? false;
1086
+ }
1087
+ /**
1088
+ * Internal function to call the TokenizerModel instance.
1089
+ * @param tokens The tokens to encode.
1090
+ * @returns The encoded tokens.
1091
+ */
1092
+ _call(tokens) {
1093
+ let result = this.encode(tokens);
1094
+ if (this.fuse_unk) result = fuse_unk(result, this.tokens_to_ids, this.unk_token_id);
1095
+ return result;
1096
+ }
1097
+ };
1098
+ var TokenizerModel_default = TokenizerModel;
1099
+ var WordPieceTokenizer = class extends TokenizerModel_default {
1100
+ /**
1101
+ * @param config The configuration object.
1102
+ */
1103
+ constructor(config) {
1104
+ super(config);
1105
+ /** The maximum number of characters per word. */
1106
+ this.max_input_chars_per_word = 100;
1107
+ this.tokens_to_ids = object_to_map(config.vocab);
1108
+ this.unk_token_id = this.tokens_to_ids.get(config.unk_token);
1109
+ this.unk_token = config.unk_token;
1110
+ this.max_input_chars_per_word = config.max_input_chars_per_word ?? 100;
1111
+ this.vocab = new Array(this.tokens_to_ids.size);
1112
+ for (const [key, value] of this.tokens_to_ids) this.vocab[value] = key;
1113
+ }
1114
+ /**
1115
+ * Encodes an array of tokens using WordPiece encoding.
1116
+ * @param tokens The tokens to encode.
1117
+ * @returns An array of encoded tokens.
1118
+ */
1119
+ encode(tokens) {
1120
+ const output_tokens = [];
1121
+ for (const token of tokens) {
1122
+ const chars = [...token];
1123
+ if (chars.length > this.max_input_chars_per_word) {
1124
+ output_tokens.push(this.unk_token);
1125
+ continue;
1126
+ }
1127
+ let is_unknown = false;
1128
+ let start = 0;
1129
+ const sub_tokens = [];
1130
+ while (start < chars.length) {
1131
+ let end = chars.length;
1132
+ let current_substring = null;
1133
+ while (start < end) {
1134
+ let substr = chars.slice(start, end).join("");
1135
+ if (start > 0) substr = this.config.continuing_subword_prefix + substr;
1136
+ if (this.tokens_to_ids.has(substr)) {
1137
+ current_substring = substr;
1138
+ break;
1139
+ }
1140
+ --end;
1141
+ }
1142
+ if (current_substring === null) {
1143
+ is_unknown = true;
1144
+ break;
1145
+ }
1146
+ sub_tokens.push(current_substring);
1147
+ start = end;
1148
+ }
1149
+ if (is_unknown) output_tokens.push(this.unk_token);
1150
+ else output_tokens.push(...sub_tokens);
1151
+ }
1152
+ return output_tokens;
1153
+ }
1154
+ };
1155
+ var WordPiece_default = WordPieceTokenizer;
1156
+ var CharTrieNode = class _CharTrieNode {
1157
+ /**
1158
+ * Create a new CharTrieNode.
1159
+ * @param is_leaf Whether the node is a leaf node or not.
1160
+ * @param children A map containing the node's children, where the key is a character and the value is a `CharTrieNode`.
1161
+ */
1162
+ constructor(is_leaf, children) {
1163
+ this.is_leaf = is_leaf;
1164
+ this.children = children;
1165
+ }
1166
+ /**
1167
+ * Returns a new `CharTrieNode` instance with default values.
1168
+ * @returns A new `CharTrieNode` instance with `is_leaf` set to `false` and an empty `children` map.
1169
+ */
1170
+ static default() {
1171
+ return new _CharTrieNode(false, /* @__PURE__ */ new Map());
1172
+ }
1173
+ };
1174
+ var CharTrie = class {
1175
+ constructor() {
1176
+ this.root = CharTrieNode.default();
1177
+ }
1178
+ /**
1179
+ * Adds one or more `texts` to the trie.
1180
+ * @param texts The strings to add to the trie.
1181
+ */
1182
+ extend(texts) {
1183
+ for (const text of texts) this.push(text);
1184
+ }
1185
+ /**
1186
+ * Adds text to the trie.
1187
+ * @param text The string to add to the trie.
1188
+ */
1189
+ push(text) {
1190
+ let node = this.root;
1191
+ for (const ch of text) {
1192
+ let child = node.children.get(ch);
1193
+ if (child === void 0) {
1194
+ child = CharTrieNode.default();
1195
+ node.children.set(ch, child);
1196
+ }
1197
+ node = child;
1198
+ }
1199
+ node.is_leaf = true;
1200
+ }
1201
+ /**
1202
+ * Searches the trie for all strings with a common prefix of `text`.
1203
+ * @param text The common prefix to search for.
1204
+ * @yields Each string in the trie that has `text` as a prefix.
1205
+ */
1206
+ *common_prefix_search(text) {
1207
+ let node = this.root;
1208
+ if (node === void 0) return;
1209
+ let prefix = "";
1210
+ for (const ch of text) {
1211
+ prefix += ch;
1212
+ node = node.children.get(ch);
1213
+ if (node === void 0) return;
1214
+ if (node.is_leaf) yield prefix;
1215
+ }
1216
+ }
1217
+ };
1218
+ var CharTrie_default = CharTrie;
1219
+ var TokenLatticeNode = class _TokenLatticeNode {
1220
+ /**
1221
+ * Represents a node in a token lattice for a given sentence.
1222
+ * @param token_id The ID of the token associated with this node.
1223
+ * @param node_id The ID of this node.
1224
+ * @param pos The starting position of the token in the sentence.
1225
+ * @param length The length of the token.
1226
+ * @param score The score associated with the token.
1227
+ */
1228
+ constructor(token_id, node_id, pos, length, score) {
1229
+ this.token_id = token_id;
1230
+ this.node_id = node_id;
1231
+ this.pos = pos;
1232
+ this.length = length;
1233
+ this.score = score;
1234
+ this.prev = null;
1235
+ this.backtrace_score = 0;
1236
+ }
1237
+ /**
1238
+ * Returns a clone of this node.
1239
+ * @returns A clone of this node.
1240
+ */
1241
+ clone() {
1242
+ const n = new _TokenLatticeNode(this.token_id, this.node_id, this.pos, this.length, this.score);
1243
+ n.prev = this.prev;
1244
+ n.backtrace_score = this.backtrace_score;
1245
+ return n;
1246
+ }
1247
+ };
1248
+ var TokenLattice = class {
1249
+ /**
1250
+ * Creates a new TokenLattice instance.
1251
+ *
1252
+ * @param sentence The input sentence to be tokenized.
1253
+ * @param bos_token_id The beginning-of-sequence token ID.
1254
+ * @param eos_token_id The end-of-sequence token ID.
1255
+ */
1256
+ constructor(sentence, bos_token_id, eos_token_id) {
1257
+ this.chars = Array.from(sentence);
1258
+ this.len = this.chars.length;
1259
+ this.bos_token_id = bos_token_id;
1260
+ this.eos_token_id = eos_token_id;
1261
+ this.nodes = [];
1262
+ this.begin_nodes = Array.from({ length: this.len + 1 }, () => []);
1263
+ this.end_nodes = Array.from({ length: this.len + 1 }, () => []);
1264
+ const bos = new TokenLatticeNode(this.bos_token_id ?? 0, 0, 0, 0, 0);
1265
+ const eos = new TokenLatticeNode(this.eos_token_id ?? 0, 1, this.len, 0, 0);
1266
+ this.nodes.push(bos.clone());
1267
+ this.nodes.push(eos.clone());
1268
+ this.begin_nodes[this.len].push(eos);
1269
+ this.end_nodes[0].push(bos);
1270
+ }
1271
+ /**
1272
+ * Inserts a new token node into the token lattice.
1273
+ *
1274
+ * @param pos The starting position of the token.
1275
+ * @param length The length of the token.
1276
+ * @param score The score of the token.
1277
+ * @param token_id The token ID of the token.
1278
+ */
1279
+ insert(pos, length, score, token_id) {
1280
+ const node_id = this.nodes.length;
1281
+ const node = new TokenLatticeNode(token_id, node_id, pos, length, score);
1282
+ this.begin_nodes[pos].push(node);
1283
+ this.end_nodes[pos + length].push(node);
1284
+ this.nodes.push(node);
1285
+ }
1286
+ /**
1287
+ * Implements the Viterbi algorithm to compute the most likely sequence of tokens.
1288
+ *
1289
+ * @returns The most likely sequence of tokens.
1290
+ */
1291
+ viterbi() {
1292
+ const len2 = this.len;
1293
+ let pos = 0;
1294
+ while (pos <= len2) {
1295
+ if (this.begin_nodes[pos].length == 0) return [];
1296
+ for (let rnode of this.begin_nodes[pos]) {
1297
+ rnode.prev = null;
1298
+ let best_score = 0;
1299
+ let best_node = null;
1300
+ for (let lnode of this.end_nodes[pos]) {
1301
+ const score = lnode.backtrace_score + rnode.score;
1302
+ if (best_node === null || score > best_score) {
1303
+ best_node = lnode.clone();
1304
+ best_score = score;
1305
+ }
1306
+ }
1307
+ if (best_node !== null) {
1308
+ rnode.prev = best_node;
1309
+ rnode.backtrace_score = best_score;
1310
+ } else return [];
1311
+ }
1312
+ ++pos;
1313
+ }
1314
+ const results = [];
1315
+ const prev = this.begin_nodes[len2][0].prev;
1316
+ if (prev === null) return [];
1317
+ let node = prev.clone();
1318
+ while (node.prev !== null) {
1319
+ results.push(node.clone());
1320
+ node = node.clone().prev.clone();
1321
+ }
1322
+ results.reverse();
1323
+ return results;
1324
+ }
1325
+ /**
1326
+ * Get the text piece for a given node.
1327
+ * @param node The node to get the piece for.
1328
+ * @returns The array of nodes representing the most likely sequence of tokens.
1329
+ */
1330
+ piece(node) {
1331
+ return this.chars.slice(node.pos, node.pos + node.length).join("");
1332
+ }
1333
+ /**
1334
+ * @returns The most likely sequence of tokens.
1335
+ */
1336
+ tokens() {
1337
+ return this.viterbi().map((x) => this.piece(x));
1338
+ }
1339
+ /**
1340
+ * @returns The most likely sequence of token ids.
1341
+ */
1342
+ token_ids() {
1343
+ return this.viterbi().map((x) => x.token_id);
1344
+ }
1345
+ };
1346
+ var TokenLattice_default = TokenLattice;
1347
+ function min(arr) {
1348
+ if (arr.length === 0) throw new Error("Array must not be empty");
1349
+ let min_value = arr[0];
1350
+ let index_of_min = 0;
1351
+ for (let i = 1; i < arr.length; ++i) if (arr[i] < min_value) {
1352
+ min_value = arr[i];
1353
+ index_of_min = i;
1354
+ }
1355
+ return [min_value, index_of_min];
1356
+ }
1357
+ var Unigram = class extends TokenizerModel_default {
1358
+ /**
1359
+ * Create a new Unigram tokenizer model.
1360
+ * @param config The configuration object for the Unigram model.
1361
+ * @param eos_token
1362
+ */
1363
+ constructor(config, eos_token) {
1364
+ super(config);
1365
+ const vocab_size = config.vocab.length;
1366
+ this.vocab = new Array(vocab_size);
1367
+ this.scores = new Array(vocab_size);
1368
+ for (let i = 0; i < vocab_size; ++i) [this.vocab[i], this.scores[i]] = config.vocab[i];
1369
+ this.unk_token_id = config.unk_id;
1370
+ this.unk_token = this.vocab[config.unk_id];
1371
+ this.tokens_to_ids = new Map(this.vocab.map((x, i) => [x, i]));
1372
+ this.bos_token = " ";
1373
+ this.bos_token_id = this.tokens_to_ids.get(this.bos_token);
1374
+ this.eos_token = eos_token;
1375
+ this.eos_token_id = this.tokens_to_ids.get(this.eos_token);
1376
+ this.unk_token = this.vocab[this.unk_token_id];
1377
+ this.min_score = min(this.scores)[0];
1378
+ this.unk_score = this.min_score - 10;
1379
+ this.scores[this.unk_token_id] = this.unk_score;
1380
+ this.trie = new CharTrie_default();
1381
+ this.trie.extend(this.vocab);
1382
+ this.fuse_unk = true;
1383
+ }
1384
+ /**
1385
+ * Populates lattice nodes.
1386
+ * @param lattice The token lattice to populate with nodes.
1387
+ */
1388
+ populate_nodes(lattice) {
1389
+ const chars = lattice.chars;
1390
+ const mblen = 1;
1391
+ let begin_pos = 0;
1392
+ while (begin_pos < chars.length) {
1393
+ let has_single_node = false;
1394
+ const tokens = [];
1395
+ const sliced = chars.slice(begin_pos).join("");
1396
+ const prefixed_tokens = this.trie.common_prefix_search(sliced);
1397
+ for (const token of prefixed_tokens) {
1398
+ tokens.push(token);
1399
+ const token_id = this.tokens_to_ids.get(token);
1400
+ const token_score = this.scores[token_id];
1401
+ const n = len(token);
1402
+ lattice.insert(begin_pos, n, token_score, token_id);
1403
+ if (!has_single_node && n === mblen) has_single_node = true;
1404
+ }
1405
+ if (!has_single_node) lattice.insert(begin_pos, mblen, this.unk_score, this.unk_token_id);
1406
+ begin_pos += mblen;
1407
+ }
1408
+ }
1409
+ /**
1410
+ * Encodes an array of tokens into an array of subtokens using the unigram model.
1411
+ *
1412
+ * @param normalized The normalized string.
1413
+ * @returns An array of subtokens obtained by encoding the input tokens using the unigram model.
1414
+ */
1415
+ tokenize(normalized) {
1416
+ const lattice = new TokenLattice_default(normalized, this.bos_token_id, this.eos_token_id);
1417
+ this.populate_nodes(lattice);
1418
+ return lattice.tokens();
1419
+ }
1420
+ /**
1421
+ * Encodes an array of tokens using Unigram encoding.
1422
+ * @param tokens The tokens to encode.
1423
+ * @returns An array of encoded tokens.
1424
+ */
1425
+ encode(tokens) {
1426
+ const to_return = [];
1427
+ for (const token of tokens) {
1428
+ const tokenized = this.tokenize(token);
1429
+ to_return.push(...tokenized);
1430
+ }
1431
+ return to_return;
1432
+ }
1433
+ };
1434
+ var Unigram_default = Unigram;
1435
+ var PriorityQueue = class {
1436
+ /**
1437
+ * Create a new PriorityQueue.
1438
+ * @param comparator Comparator function to determine priority. Defaults to a MaxHeap.
1439
+ * @param max_size Maximum size of the queue. Defaults to Infinity.
1440
+ */
1441
+ constructor(comparator = (a, b) => a > b, max_size = Infinity) {
1442
+ this._heap = [];
1443
+ this._comparator = comparator;
1444
+ this._max_size = max_size;
1445
+ }
1446
+ /**
1447
+ * The size of the queue
1448
+ */
1449
+ get size() {
1450
+ return this._heap.length;
1451
+ }
1452
+ /**
1453
+ * Check if the queue is empty.
1454
+ * @returns `true` if the queue is empty, `false` otherwise.
1455
+ */
1456
+ is_empty() {
1457
+ return this.size === 0;
1458
+ }
1459
+ /**
1460
+ * Return the element with the highest priority in the queue.
1461
+ * @returns The highest priority element in the queue.
1462
+ */
1463
+ peek() {
1464
+ return this._heap[0];
1465
+ }
1466
+ /**
1467
+ * Add one or more elements to the queue.
1468
+ * @param values The values to push into the queue.
1469
+ * @returns The new size of the queue.
1470
+ */
1471
+ push(...values) {
1472
+ return this.extend(values);
1473
+ }
1474
+ /**
1475
+ * Add multiple elements to the queue.
1476
+ * @param values The values to push into the queue.
1477
+ * @returns The new size of the queue.
1478
+ */
1479
+ extend(values) {
1480
+ for (const value of values) if (this.size < this._max_size) {
1481
+ this._heap.push(value);
1482
+ this._sift_up();
1483
+ } else {
1484
+ const smallest = this._smallest();
1485
+ if (this._comparator(value, this._heap[smallest])) {
1486
+ this._heap[smallest] = value;
1487
+ this._sift_up_from(smallest);
1488
+ }
1489
+ }
1490
+ return this.size;
1491
+ }
1492
+ /**
1493
+ * Remove and return the element with the highest priority in the queue.
1494
+ * @returns The element with the highest priority in the queue.
1495
+ */
1496
+ pop() {
1497
+ const popped_value = this.peek();
1498
+ const bottom = this.size - 1;
1499
+ if (bottom > 0) this._swap(0, bottom);
1500
+ this._heap.pop();
1501
+ this._sift_down();
1502
+ return popped_value;
1503
+ }
1504
+ /**
1505
+ * Replace the element with the highest priority in the queue with a new value.
1506
+ * @param value The new value.
1507
+ * @returns The replaced value.
1508
+ */
1509
+ replace(value) {
1510
+ const replaced_value = this.peek();
1511
+ this._heap[0] = value;
1512
+ this._sift_down();
1513
+ return replaced_value;
1514
+ }
1515
+ /**
1516
+ * Compute the index for the parent of the node at index `i`.
1517
+ * @param i The index of the node to get the parent of.
1518
+ * @returns The index of the parent node.
1519
+ * @private
1520
+ */
1521
+ _parent(i) {
1522
+ return (i + 1 >>> 1) - 1;
1523
+ }
1524
+ /**
1525
+ * Compute the index for the left child of the node at index `i`.
1526
+ * @param i The index of the node to get the left child of.
1527
+ * @returns The index of the left child.
1528
+ * @private
1529
+ */
1530
+ _left(i) {
1531
+ return (i << 1) + 1;
1532
+ }
1533
+ /**
1534
+ * Compute the index for the right child of the node at index `i`.
1535
+ * @param i The index of the node to get the right child of.
1536
+ * @returns The index of the right child.
1537
+ * @private
1538
+ */
1539
+ _right(i) {
1540
+ return i + 1 << 1;
1541
+ }
1542
+ /**
1543
+ * Check if the element at index `i` is greater than the element at index `j`.
1544
+ * @param i The index of the first element to compare.
1545
+ * @param j The index of the second element to compare.
1546
+ * @returns `true` if the element at index `i` is greater than the element at index `j`, `false` otherwise.
1547
+ * @private
1548
+ */
1549
+ _greater(i, j) {
1550
+ return this._comparator(this._heap[i], this._heap[j]);
1551
+ }
1552
+ /**
1553
+ * Swap the elements at indices `i` and `j`.
1554
+ * @param i The index of the first element to swap.
1555
+ * @param j The index of the second element to swap.
1556
+ * @private
1557
+ */
1558
+ _swap(i, j) {
1559
+ const temp = this._heap[i];
1560
+ this._heap[i] = this._heap[j];
1561
+ this._heap[j] = temp;
1562
+ }
1563
+ /**
1564
+ * Maintain the heap property by updating positions in the heap,
1565
+ * starting at the last element and moving up the heap.
1566
+ * @private
1567
+ */
1568
+ _sift_up() {
1569
+ this._sift_up_from(this.size - 1);
1570
+ }
1571
+ /**
1572
+ * Helper function to sift up from a given node.
1573
+ * @param node The index of the node to start sifting up from.
1574
+ */
1575
+ _sift_up_from(node) {
1576
+ while (node > 0 && this._greater(node, this._parent(node))) {
1577
+ this._swap(node, this._parent(node));
1578
+ node = this._parent(node);
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Maintain the heap property by updating positions in the heap,
1583
+ * starting at the first element and moving down the heap.
1584
+ * @private
1585
+ */
1586
+ _sift_down() {
1587
+ let node = 0;
1588
+ while (this._left(node) < this.size && this._greater(this._left(node), node) || this._right(node) < this.size && this._greater(this._right(node), node)) {
1589
+ const max_child = this._right(node) < this.size && this._greater(this._right(node), this._left(node)) ? this._right(node) : this._left(node);
1590
+ this._swap(node, max_child);
1591
+ node = max_child;
1592
+ }
1593
+ }
1594
+ /**
1595
+ * Get the index of the smallest element in the heap. Since we use an array-based heap,
1596
+ * the index can be computed without needing to traverse the heap.
1597
+ * @private
1598
+ */
1599
+ _smallest() {
1600
+ return 2 ** Math.floor(Math.log2(this.size)) - 1;
1601
+ }
1602
+ };
1603
+ var PriorityQueue_default = PriorityQueue;
1604
+ var LRUCache = class {
1605
+ /**
1606
+ * Creates an LRUCache instance.
1607
+ * @param capacity The maximum number of items the cache can hold.
1608
+ */
1609
+ constructor(capacity) {
1610
+ this.capacity = capacity;
1611
+ this.cache = /* @__PURE__ */ new Map();
1612
+ }
1613
+ /**
1614
+ * Retrieves the value associated with the given key and marks the key as recently used.
1615
+ * @param key The key to retrieve.
1616
+ * @returns The value associated with the key, or undefined if the key does not exist.
1617
+ */
1618
+ get(key) {
1619
+ if (!this.cache.has(key)) return void 0;
1620
+ const value = this.cache.get(key);
1621
+ this.cache.delete(key);
1622
+ this.cache.set(key, value);
1623
+ return value;
1624
+ }
1625
+ /**
1626
+ * Inserts or updates the key-value pair in the cache.
1627
+ * If the key already exists, it is updated and marked as recently used.
1628
+ * If the cache exceeds its capacity, the least recently used item is evicted.
1629
+ * @param key The key to add or update.
1630
+ * @param value The value to associate with the key.
1631
+ */
1632
+ put(key, value) {
1633
+ if (this.cache.has(key)) this.cache.delete(key);
1634
+ this.cache.set(key, value);
1635
+ if (this.cache.size > this.capacity) this.cache.delete(this.cache.keys().next().value);
1636
+ }
1637
+ /**
1638
+ * Clears the cache.
1639
+ */
1640
+ clear() {
1641
+ this.cache.clear();
1642
+ }
1643
+ };
1644
+ var LRUCache_default = LRUCache;
1645
+ var BPE = class extends TokenizerModel_default {
1646
+ /**
1647
+ * Create a BPE instance.
1648
+ * @param config The configuration object for BPE.
1649
+ */
1650
+ constructor(config) {
1651
+ super(config);
1652
+ this.tokens_to_ids = object_to_map(config.vocab);
1653
+ this.unk_token_id = this.tokens_to_ids.get(config.unk_token);
1654
+ this.unk_token = config.unk_token;
1655
+ this.vocab = new Array(this.tokens_to_ids.size);
1656
+ for (const [key, value] of this.tokens_to_ids) this.vocab[value] = key;
1657
+ this.merges = Array.isArray(config.merges[0]) ? config.merges : config.merges.map((x) => x.split(" ", 2));
1658
+ this.bpe_ranks = new Map(this.merges.map((x, i) => [JSON.stringify(x), i]));
1659
+ this.end_of_word_suffix = config.end_of_word_suffix;
1660
+ this.continuing_subword_suffix = config.continuing_subword_suffix ?? null;
1661
+ this.byte_fallback = this.config.byte_fallback ?? false;
1662
+ if (this.byte_fallback) this.text_encoder = new TextEncoder();
1663
+ this.ignore_merges = this.config.ignore_merges ?? false;
1664
+ this.max_length_to_cache = 256;
1665
+ this.cache_capacity = 1e4;
1666
+ this.cache = new LRUCache_default(this.cache_capacity);
1667
+ }
1668
+ /**
1669
+ * Clears the cache.
1670
+ */
1671
+ clear_cache() {
1672
+ this.cache.clear();
1673
+ }
1674
+ /**
1675
+ * Apply Byte-Pair-Encoding (BPE) to a given token. Efficient heap-based priority
1676
+ * queue implementation adapted from https://github.com/belladoreai/llama-tokenizer-js.
1677
+ * @param token The token to encode.
1678
+ * @returns The BPE encoded tokens.
1679
+ */
1680
+ bpe(token) {
1681
+ if (token.length === 0) return [];
1682
+ const cached = this.cache.get(token);
1683
+ if (cached !== void 0) return cached;
1684
+ const word = Array.from(token);
1685
+ if (this.end_of_word_suffix) word[word.length - 1] += this.end_of_word_suffix;
1686
+ let result = [];
1687
+ if (word.length > 1) {
1688
+ const queue = new PriorityQueue_default((a, b) => a.score < b.score);
1689
+ let starting_node = {
1690
+ token: word[0],
1691
+ bias: 0,
1692
+ prev: null,
1693
+ next: null
1694
+ };
1695
+ let previous_node = starting_node;
1696
+ for (let i = 1; i < word.length; ++i) {
1697
+ const current_node = {
1698
+ bias: i / word.length,
1699
+ token: word[i],
1700
+ prev: previous_node,
1701
+ next: null
1702
+ };
1703
+ previous_node.next = current_node;
1704
+ this.add_node(queue, previous_node);
1705
+ previous_node = current_node;
1706
+ }
1707
+ while (!queue.is_empty()) {
1708
+ const node = queue.pop();
1709
+ if (node.deleted || !node.next || node.next.deleted) continue;
1710
+ node.deleted = true;
1711
+ node.next.deleted = true;
1712
+ if (node.prev) {
1713
+ const new_previous_node = { ...node.prev };
1714
+ node.prev.deleted = true;
1715
+ node.prev = new_previous_node;
1716
+ if (new_previous_node.prev) new_previous_node.prev.next = new_previous_node;
1717
+ else starting_node = new_previous_node;
1718
+ }
1719
+ const merged = {
1720
+ token: node.token + node.next.token,
1721
+ bias: node.bias,
1722
+ prev: node.prev,
1723
+ next: node.next.next
1724
+ };
1725
+ if (merged.prev) {
1726
+ merged.prev.next = merged;
1727
+ this.add_node(queue, merged.prev);
1728
+ } else starting_node = merged;
1729
+ if (merged.next) {
1730
+ merged.next.prev = merged;
1731
+ this.add_node(queue, merged);
1732
+ }
1733
+ }
1734
+ for (let current_node = starting_node; current_node !== null; current_node = current_node.next) result.push(current_node.token);
1735
+ } else result = word;
1736
+ if (this.continuing_subword_suffix) for (let i = 0; i < result.length - 1; ++i) result[i] += this.continuing_subword_suffix;
1737
+ if (token.length < this.max_length_to_cache) this.cache.put(token, result);
1738
+ return result;
1739
+ }
1740
+ /**
1741
+ * Helper function to add a node to the priority queue.
1742
+ * @param queue
1743
+ * @param node
1744
+ */
1745
+ add_node(queue, node) {
1746
+ const rank = this.bpe_ranks.get(JSON.stringify([node.token, node.next.token]));
1747
+ if (rank !== void 0) {
1748
+ node.score = rank + node.bias;
1749
+ queue.push(node);
1750
+ }
1751
+ }
1752
+ /**
1753
+ * Encodes the input sequence of tokens using the BPE algorithm and returns the resulting subword tokens.
1754
+ * @param tokens The input sequence of tokens to encode.
1755
+ * @returns The resulting subword tokens after applying the BPE algorithm to the input sequence of tokens.
1756
+ */
1757
+ encode(tokens) {
1758
+ const output_tokens = [];
1759
+ for (const token of tokens) {
1760
+ if (this.ignore_merges && this.tokens_to_ids.has(token)) {
1761
+ output_tokens.push(token);
1762
+ continue;
1763
+ }
1764
+ const bpe_token_list = this.bpe(token);
1765
+ for (const t of bpe_token_list) if (this.tokens_to_ids.has(t)) output_tokens.push(t);
1766
+ else if (this.byte_fallback) {
1767
+ const byte_tokens = Array.from(this.text_encoder.encode(t)).map((x) => `<0x${x.toString(16).toUpperCase().padStart(2, "0")}>`);
1768
+ if (byte_tokens.every((x) => this.tokens_to_ids.has(x))) output_tokens.push(...byte_tokens);
1769
+ else if (this.unk_token != null) output_tokens.push(this.unk_token);
1770
+ } else if (this.unk_token != null) output_tokens.push(this.unk_token);
1771
+ }
1772
+ return output_tokens;
1773
+ }
1774
+ };
1775
+ var BPE_default = BPE;
1776
+ var Legacy = class extends TokenizerModel_default {
1777
+ /**
1778
+ * Create a Legacy tokenizer model instance.
1779
+ * @param config The configuration object for Legacy tokenizer model.
1780
+ * @param more_config Additional configuration object for the Legacy tokenizer model.
1781
+ */
1782
+ constructor(config, more_config) {
1783
+ super(config);
1784
+ const vocab = config.vocab;
1785
+ this.tokens_to_ids = object_to_map(more_config.target_lang ? vocab[more_config.target_lang] : vocab);
1786
+ this.bos_token = more_config.bos_token;
1787
+ this.bos_token_id = this.tokens_to_ids.get(this.bos_token);
1788
+ this.eos_token = more_config.eos_token;
1789
+ this.eos_token_id = this.tokens_to_ids.get(this.eos_token);
1790
+ this.pad_token = more_config.pad_token;
1791
+ this.pad_token_id = this.tokens_to_ids.get(this.pad_token);
1792
+ this.unk_token = more_config.unk_token;
1793
+ this.unk_token_id = this.tokens_to_ids.get(this.unk_token);
1794
+ this.vocab = new Array(this.tokens_to_ids.size);
1795
+ for (const [key, value] of this.tokens_to_ids) this.vocab[value] = key;
1796
+ }
1797
+ encode(tokens) {
1798
+ return tokens;
1799
+ }
1800
+ };
1801
+ var Legacy_default = Legacy;
1802
+ function create_tokenizer_model(model_config, config) {
1803
+ switch (model_config.type) {
1804
+ case "WordPiece": return new WordPiece_default(model_config);
1805
+ case "Unigram": return new Unigram_default(model_config, config.eos_token);
1806
+ case "BPE": return new BPE_default(model_config);
1807
+ default:
1808
+ if (model_config.vocab) if (Array.isArray(model_config.vocab)) return new Unigram_default(model_config, config.eos_token);
1809
+ else if (Object.hasOwn(model_config, "continuing_subword_prefix") && Object.hasOwn(model_config, "unk_token")) if (Object.hasOwn(model_config, "merges")) return new BPE_default(model_config);
1810
+ else return new WordPiece_default(model_config);
1811
+ else return new Legacy_default(model_config, {
1812
+ target_lang: config.target_lang,
1813
+ bos_token: config.bos_token,
1814
+ eos_token: config.eos_token,
1815
+ pad_token: config.pad_token,
1816
+ unk_token: config.unk_token
1817
+ });
1818
+ throw new Error(`Unknown TokenizerModel type: ${model_config?.type}`);
1819
+ }
1820
+ }
1821
+ var create_tokenizer_model_default = create_tokenizer_model;
1822
+ var PostProcessor = class extends Callable_default {
1823
+ /**
1824
+ * @param config The configuration for the post-processor.
1825
+ */
1826
+ constructor(config) {
1827
+ super();
1828
+ this.config = config;
1829
+ }
1830
+ /**
1831
+ * Alias for {@link PostProcessor#post_process}.
1832
+ * @param tokens The text or array of texts to post-process.
1833
+ * @param args Additional arguments required by the post-processing logic.
1834
+ * @returns The post-processed tokens.
1835
+ */
1836
+ _call(tokens, ...args) {
1837
+ return this.post_process(tokens, ...args);
1838
+ }
1839
+ };
1840
+ var PostProcessor_default = PostProcessor;
1841
+ var TemplateProcessing = class extends PostProcessor_default {
1842
+ /**
1843
+ * Replaces special tokens in the template with actual tokens.
1844
+ * @param tokens The list of tokens for the first sequence.
1845
+ * @param tokens_pair The list of tokens for the second sequence (optional).
1846
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
1847
+ * @returns An object containing the list of tokens with the special tokens replaced with actual tokens.
1848
+ */
1849
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
1850
+ const type = tokens_pair === null ? this.config.single : this.config.pair;
1851
+ let processed_tokens = [];
1852
+ let types = [];
1853
+ for (const item of type) if ("SpecialToken" in item) {
1854
+ if (add_special_tokens) {
1855
+ processed_tokens.push(item.SpecialToken.id);
1856
+ types.push(item.SpecialToken.type_id);
1857
+ }
1858
+ } else if ("Sequence" in item) {
1859
+ if (item.Sequence.id === "A") {
1860
+ processed_tokens = merge_arrays(processed_tokens, tokens);
1861
+ types = merge_arrays(types, new Array(tokens.length).fill(item.Sequence.type_id));
1862
+ } else if (item.Sequence.id === "B") {
1863
+ processed_tokens = merge_arrays(processed_tokens, tokens_pair);
1864
+ types = merge_arrays(types, new Array(tokens_pair.length).fill(item.Sequence.type_id));
1865
+ }
1866
+ }
1867
+ return {
1868
+ tokens: processed_tokens,
1869
+ token_type_ids: types
1870
+ };
1871
+ }
1872
+ };
1873
+ var TemplateProcessing_default = TemplateProcessing;
1874
+ var ByteLevel2 = class extends PostProcessor_default {
1875
+ /**
1876
+ * Post process the given tokens.
1877
+ * @param tokens The list of tokens for the first sequence.
1878
+ * @param tokens_pair The list of tokens for the second sequence (optional).
1879
+ * @returns An object containing the post-processed tokens.
1880
+ */
1881
+ post_process(tokens, tokens_pair = null) {
1882
+ return {
1883
+ tokens,
1884
+ tokens_pair
1885
+ };
1886
+ }
1887
+ };
1888
+ var ByteLevel_default2 = ByteLevel2;
1889
+ var BertProcessing = class extends PostProcessor_default {
1890
+ /**
1891
+ * @param config The configuration for the post-processor.
1892
+ * @param config.cls The special tokens to add to the beginning of the input.
1893
+ * @param config.sep The special tokens to add to the end of the input.
1894
+ */
1895
+ constructor(config) {
1896
+ super(config);
1897
+ this.sep = config.sep;
1898
+ this.cls = config.cls;
1899
+ }
1900
+ /**
1901
+ * Adds the special tokens to the beginning and end of the input.
1902
+ * @param tokens The input tokens.
1903
+ * @param tokens_pair An optional second set of input tokens.
1904
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
1905
+ * @returns The post-processed tokens with the special tokens added to the beginning and end.
1906
+ */
1907
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
1908
+ if (add_special_tokens) tokens = merge_arrays([this.cls[0]], tokens, [this.sep[0]]);
1909
+ let token_type_ids = new Array(tokens.length).fill(0);
1910
+ if (tokens_pair) {
1911
+ const middle = [];
1912
+ const after = add_special_tokens ? [this.sep[0]] : [];
1913
+ tokens = merge_arrays(tokens, middle, tokens_pair, after);
1914
+ token_type_ids = merge_arrays(token_type_ids, new Array(tokens_pair.length + middle.length + after.length).fill(1));
1915
+ }
1916
+ return {
1917
+ tokens,
1918
+ token_type_ids
1919
+ };
1920
+ }
1921
+ };
1922
+ var BertProcessing_default = BertProcessing;
1923
+ var RobertaProcessing = class extends PostProcessor_default {
1924
+ /**
1925
+ * @param config The configuration for the post-processor.
1926
+ * @param config.cls The special tokens to add to the beginning of the input.
1927
+ * @param config.sep The special tokens to add to the end of the input.
1928
+ */
1929
+ constructor(config) {
1930
+ super(config);
1931
+ this.sep = config.sep;
1932
+ this.cls = config.cls;
1933
+ }
1934
+ /**
1935
+ * Adds the special tokens to the beginning and end of the input.
1936
+ * @param tokens The input tokens.
1937
+ * @param tokens_pair An optional second set of input tokens.
1938
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
1939
+ * @returns The post-processed tokens with the special tokens added to the beginning and end.
1940
+ */
1941
+ post_process(tokens, tokens_pair, add_special_tokens = true) {
1942
+ if (add_special_tokens) tokens = merge_arrays([this.cls[0]], tokens, [this.sep[0]]);
1943
+ let token_type_ids = new Array(tokens.length).fill(0);
1944
+ if (tokens_pair) {
1945
+ const middle = add_special_tokens ? [this.sep[0]] : [];
1946
+ const after = add_special_tokens ? [this.sep[0]] : [];
1947
+ tokens = merge_arrays(tokens, middle, tokens_pair, after);
1948
+ token_type_ids = merge_arrays(token_type_ids, new Array(tokens_pair.length + middle.length + after.length).fill(1));
1949
+ }
1950
+ return {
1951
+ tokens,
1952
+ token_type_ids
1953
+ };
1954
+ }
1955
+ };
1956
+ var RobertaProcessing_default = RobertaProcessing;
1957
+ var Sequence3 = class extends PostProcessor_default {
1958
+ /**
1959
+ * Creates a new instance of Sequence post-processor.
1960
+ * @param config The configuration object.
1961
+ */
1962
+ constructor(config) {
1963
+ super(config);
1964
+ this.processors = (config.processors ?? []).map((x) => create_post_processor_default(x));
1965
+ }
1966
+ /**
1967
+ * Post process the given tokens.
1968
+ * @param tokens The list of tokens for the first sequence.
1969
+ * @param tokens_pair The list of tokens for the second sequence (optional).
1970
+ * @param add_special_tokens Whether to add the special tokens to the beginning and end of the input.
1971
+ * @returns An object containing the post-processed tokens.
1972
+ */
1973
+ post_process(tokens, tokens_pair = null, add_special_tokens = true) {
1974
+ let processed_tokens = {
1975
+ tokens,
1976
+ tokens_pair
1977
+ };
1978
+ for (const processor of this.processors) processed_tokens = processor.post_process(processed_tokens.tokens, processed_tokens.tokens_pair, add_special_tokens);
1979
+ return processed_tokens;
1980
+ }
1981
+ };
1982
+ var Sequence_default3 = Sequence3;
1983
+ function create_post_processor(config) {
1984
+ if (config === null) return null;
1985
+ switch (config.type) {
1986
+ case "TemplateProcessing": return new TemplateProcessing_default(config);
1987
+ case "ByteLevel": return new ByteLevel_default2(config);
1988
+ case "BertProcessing": return new BertProcessing_default(config);
1989
+ case "RobertaProcessing": return new RobertaProcessing_default(config);
1990
+ case "Sequence": return new Sequence_default3(config);
1991
+ default: throw new Error(`Unknown PostProcessor type: ${config.type}`);
1992
+ }
1993
+ }
1994
+ var create_post_processor_default = create_post_processor;
1995
+ var Decoder = class extends Callable_default {
1996
+ /**
1997
+ * Creates an instance of `Decoder`.
1998
+ * @param config The configuration object.
1999
+ **/
2000
+ constructor(config) {
2001
+ super();
2002
+ this.config = config;
2003
+ this.added_tokens = [];
2004
+ this.end_of_word_suffix = null;
2005
+ this.trim_offsets = "trim_offsets" in config ? config.trim_offsets : false;
2006
+ }
2007
+ /**
2008
+ * Calls the `decode` method.
2009
+ *
2010
+ * @param tokens The list of tokens.
2011
+ * @returns The decoded string.
2012
+ */
2013
+ _call(tokens) {
2014
+ return this.decode(tokens);
2015
+ }
2016
+ /**
2017
+ * Decodes a list of tokens.
2018
+ * @param tokens The list of tokens.
2019
+ * @returns The decoded string.
2020
+ */
2021
+ decode(tokens) {
2022
+ return this.decode_chain(tokens).join("");
2023
+ }
2024
+ };
2025
+ var Decoder_default = Decoder;
2026
+ var ByteLevel3 = class extends Decoder_default {
2027
+ /**
2028
+ * Create a `ByteLevelDecoder` object.
2029
+ */
2030
+ constructor(config) {
2031
+ super(config);
2032
+ this.byte_decoder = UNICODE_TO_BYTES;
2033
+ this.text_decoder = new TextDecoder("utf-8", {
2034
+ fatal: false,
2035
+ ignoreBOM: true
2036
+ });
2037
+ this.end_of_word_suffix = null;
2038
+ }
2039
+ /**
2040
+ * Convert an array of tokens to string by decoding each byte.
2041
+ * @param tokens Array of tokens to be decoded.
2042
+ * @returns The decoded string.
2043
+ */
2044
+ convert_tokens_to_string(tokens) {
2045
+ const text = tokens.join("");
2046
+ const byte_array = new Uint8Array([...text].map((c) => this.byte_decoder[c]));
2047
+ return this.text_decoder.decode(byte_array);
2048
+ }
2049
+ decode_chain(tokens) {
2050
+ const sub_texts = [];
2051
+ let current_sub_text = [];
2052
+ for (const token of tokens) if (this.added_tokens.find((x) => x.content === token) !== void 0) {
2053
+ if (current_sub_text.length > 0) {
2054
+ sub_texts.push(this.convert_tokens_to_string(current_sub_text));
2055
+ current_sub_text = [];
2056
+ }
2057
+ sub_texts.push(token);
2058
+ } else current_sub_text.push(token);
2059
+ if (current_sub_text.length > 0) sub_texts.push(this.convert_tokens_to_string(current_sub_text));
2060
+ return sub_texts;
2061
+ }
2062
+ };
2063
+ var ByteLevel_default3 = ByteLevel3;
2064
+ var WordPiece = class extends Decoder_default {
2065
+ /**
2066
+ * Creates a new instance of WordPieceDecoder.
2067
+ * @param config The configuration object.
2068
+ */
2069
+ constructor(config) {
2070
+ super(config);
2071
+ this.cleanup = config.cleanup;
2072
+ }
2073
+ decode_chain(tokens) {
2074
+ return tokens.map((token, i) => {
2075
+ if (i !== 0) {
2076
+ const prefix = this.config.prefix;
2077
+ if (prefix && token.startsWith(prefix)) token = token.replace(prefix, "");
2078
+ else token = " " + token;
2079
+ }
2080
+ if (this.cleanup) token = clean_up_tokenization(token);
2081
+ return token;
2082
+ });
2083
+ }
2084
+ };
2085
+ var WordPiece_default2 = WordPiece;
2086
+ var Metaspace2 = class extends Decoder_default {
2087
+ /**
2088
+ * Constructs a new MetaspaceDecoder object.
2089
+ * @param config The configuration object for the MetaspaceDecoder.
2090
+ */
2091
+ constructor(config) {
2092
+ super(config);
2093
+ this.replacement = config.replacement ?? "▁";
2094
+ }
2095
+ decode_chain(tokens) {
2096
+ const result = [];
2097
+ for (let i = 0; i < tokens.length; ++i) {
2098
+ let normalized = tokens[i].replaceAll(this.replacement, " ");
2099
+ if (i == 0 && normalized.startsWith(" ")) normalized = normalized.substring(1);
2100
+ result.push(normalized);
2101
+ }
2102
+ return result;
2103
+ }
2104
+ };
2105
+ var Metaspace_default2 = Metaspace2;
2106
+ var BPE2 = class extends Decoder_default {
2107
+ constructor(config) {
2108
+ super(config);
2109
+ this.suffix = config.suffix ?? "";
2110
+ }
2111
+ decode_chain(tokens) {
2112
+ return tokens.map((token, i) => {
2113
+ return token.replaceAll(this.suffix, i === tokens.length - 1 ? "" : " ");
2114
+ });
2115
+ }
2116
+ };
2117
+ var BPE_default2 = BPE2;
2118
+ var CTC = class extends Decoder_default {
2119
+ constructor(config) {
2120
+ super(config);
2121
+ this.pad_token = config.pad_token ?? "";
2122
+ this.word_delimiter_token = config.word_delimiter_token ?? "";
2123
+ this.cleanup = config.cleanup;
2124
+ }
2125
+ /**
2126
+ * Converts a connectionist-temporal-classification (CTC) output tokens into a single string.
2127
+ * @param tokens Array of tokens to be decoded.
2128
+ * @returns The decoded string.
2129
+ */
2130
+ convert_tokens_to_string(tokens) {
2131
+ if (tokens.length === 0) return "";
2132
+ const grouped_tokens = [tokens[0]];
2133
+ for (let i = 1; i < tokens.length; ++i) if (tokens[i] !== grouped_tokens.at(-1)) grouped_tokens.push(tokens[i]);
2134
+ let text = grouped_tokens.filter((token) => token !== this.pad_token).join("");
2135
+ if (this.cleanup) text = clean_up_tokenization(text).replaceAll(this.word_delimiter_token, " ").trim();
2136
+ return text;
2137
+ }
2138
+ decode_chain(tokens) {
2139
+ return [this.convert_tokens_to_string(tokens)];
2140
+ }
2141
+ };
2142
+ var CTC_default = CTC;
2143
+ var Sequence4 = class extends Decoder_default {
2144
+ /**
2145
+ * Creates a new instance of DecoderSequence.
2146
+ * @param config The configuration object.
2147
+ */
2148
+ constructor(config) {
2149
+ super(config);
2150
+ this.decoders = (config.decoders ?? []).map((x) => create_decoder_default(x));
2151
+ }
2152
+ decode_chain(tokens) {
2153
+ return this.decoders.reduce((toks, decoder) => {
2154
+ return decoder.decode_chain(toks);
2155
+ }, tokens);
2156
+ }
2157
+ };
2158
+ var Sequence_default4 = Sequence4;
2159
+ var Replace3 = class extends Decoder_default {
2160
+ decode_chain(tokens) {
2161
+ const pattern = create_pattern(this.config.pattern);
2162
+ const content = this.config.content ?? "";
2163
+ return pattern === null ? tokens : tokens.map((token) => token.replaceAll(pattern, content));
2164
+ }
2165
+ };
2166
+ var Replace_default3 = Replace3;
2167
+ var Fuse = class extends Decoder_default {
2168
+ decode_chain(tokens) {
2169
+ return [tokens.join("")];
2170
+ }
2171
+ };
2172
+ var Fuse_default = Fuse;
2173
+ var Strip2 = class extends Decoder_default {
2174
+ constructor(config) {
2175
+ super(config);
2176
+ this.content = config.content ?? "";
2177
+ this.start = config.start ?? 0;
2178
+ this.stop = config.stop ?? 0;
2179
+ }
2180
+ decode_chain(tokens) {
2181
+ return tokens.map((token) => {
2182
+ let start_cut = 0;
2183
+ for (let i = 0; i < this.start; ++i) if (token[i] === this.content) {
2184
+ start_cut = i + 1;
2185
+ continue;
2186
+ } else break;
2187
+ let stop_cut = token.length;
2188
+ for (let i = 0; i < this.stop; ++i) {
2189
+ const index = token.length - i - 1;
2190
+ if (token[index] === this.content) {
2191
+ stop_cut = index;
2192
+ continue;
2193
+ } else break;
2194
+ }
2195
+ return token.slice(start_cut, stop_cut);
2196
+ });
2197
+ }
2198
+ };
2199
+ var Strip_default2 = Strip2;
2200
+ var ByteFallback = class extends Decoder_default {
2201
+ constructor(config) {
2202
+ super(config);
2203
+ this.text_decoder = new TextDecoder();
2204
+ }
2205
+ decode_chain(tokens) {
2206
+ const new_tokens = [];
2207
+ let previous_byte_tokens = [];
2208
+ for (const token of tokens) {
2209
+ let bytes = null;
2210
+ if (token.length === 6 && token.startsWith("<0x") && token.endsWith(">")) {
2211
+ const byte = parseInt(token.slice(3, 5), 16);
2212
+ if (!isNaN(byte)) bytes = byte;
2213
+ }
2214
+ if (bytes !== null) previous_byte_tokens.push(bytes);
2215
+ else {
2216
+ if (previous_byte_tokens.length > 0) {
2217
+ const string = this.text_decoder.decode(Uint8Array.from(previous_byte_tokens));
2218
+ new_tokens.push(string);
2219
+ previous_byte_tokens = [];
2220
+ }
2221
+ new_tokens.push(token);
2222
+ }
2223
+ }
2224
+ if (previous_byte_tokens.length > 0) {
2225
+ const string = this.text_decoder.decode(Uint8Array.from(previous_byte_tokens));
2226
+ new_tokens.push(string);
2227
+ previous_byte_tokens = [];
2228
+ }
2229
+ return new_tokens;
2230
+ }
2231
+ };
2232
+ var ByteFallback_default = ByteFallback;
2233
+ function create_decoder(config) {
2234
+ if (config === null) return null;
2235
+ switch (config.type) {
2236
+ case "ByteLevel": return new ByteLevel_default3(config);
2237
+ case "WordPiece": return new WordPiece_default2(config);
2238
+ case "Metaspace": return new Metaspace_default2(config);
2239
+ case "BPEDecoder": return new BPE_default2(config);
2240
+ case "CTC": return new CTC_default(config);
2241
+ case "Sequence": return new Sequence_default4(config);
2242
+ case "Replace": return new Replace_default3(config);
2243
+ case "Fuse": return new Fuse_default(config);
2244
+ case "Strip": return new Strip_default2(config);
2245
+ case "ByteFallback": return new ByteFallback_default(config);
2246
+ default: throw new Error(`Unknown Decoder type: ${config.type}`);
2247
+ }
2248
+ }
2249
+ var create_decoder_default = create_decoder;
2250
+ var Tokenizer = class {
2251
+ constructor(tokenizer, config) {
2252
+ const tokenizer_error = validate_object(tokenizer, "Tokenizer", [
2253
+ "model",
2254
+ "decoder",
2255
+ "post_processor",
2256
+ "pre_tokenizer",
2257
+ "normalizer"
2258
+ ]);
2259
+ if (tokenizer_error) throw new Error(tokenizer_error);
2260
+ const config_error = validate_object(config, "Config");
2261
+ if (config_error) throw new Error(config_error);
2262
+ this.tokenizer = tokenizer;
2263
+ this.config = config;
2264
+ this.normalizer = create_normalizer_default(this.tokenizer.normalizer);
2265
+ this.pre_tokenizer = create_pre_tokenizer_default(this.tokenizer.pre_tokenizer);
2266
+ this.model = create_tokenizer_model_default(this.tokenizer.model, this.config);
2267
+ this.post_processor = create_post_processor_default(this.tokenizer.post_processor);
2268
+ this.decoder = create_decoder_default(this.tokenizer.decoder);
2269
+ this.special_tokens = [];
2270
+ this.all_special_ids = [];
2271
+ this.added_tokens = [];
2272
+ const unnormalized_contents = [];
2273
+ const normalized_contents = [];
2274
+ this.added_tokens_map = /* @__PURE__ */ new Map();
2275
+ for (const added_token of this.tokenizer.added_tokens) {
2276
+ const token = new AddedToken_default(added_token);
2277
+ this.added_tokens.push(token);
2278
+ this.model.tokens_to_ids.set(token.content, token.id);
2279
+ this.model.vocab[token.id] = token.content;
2280
+ if (token.special) {
2281
+ this.special_tokens.push(token.content);
2282
+ this.all_special_ids.push(token.id);
2283
+ }
2284
+ this.added_tokens_map.set(token.content, token);
2285
+ if (token.normalized && this.normalizer !== null) {
2286
+ const normalized_content = this.normalizer(token.content);
2287
+ normalized_contents.push(normalized_content);
2288
+ this.added_tokens_map.set(normalized_content, token);
2289
+ } else unnormalized_contents.push(token.content);
2290
+ }
2291
+ (this.config.additional_special_tokens ?? []).forEach((token) => {
2292
+ if (!this.special_tokens.includes(token)) this.special_tokens.push(token);
2293
+ });
2294
+ if (this.decoder) {
2295
+ this.decoder.added_tokens = this.added_tokens;
2296
+ this.decoder.end_of_word_suffix = this.model.end_of_word_suffix;
2297
+ }
2298
+ this.splitter_unnormalized = new DictionarySplitter_default(unnormalized_contents);
2299
+ this.splitter_normalized = new DictionarySplitter_default(normalized_contents);
2300
+ this.remove_space = this.config.remove_space;
2301
+ this.clean_up_tokenization_spaces = this.config.clean_up_tokenization_spaces ?? true;
2302
+ this.do_lowercase_and_remove_accent = this.config.do_lowercase_and_remove_accent ?? false;
2303
+ }
2304
+ encode(text, { text_pair = null, add_special_tokens = true, return_token_type_ids = null } = {}) {
2305
+ const { tokens, token_type_ids } = this.tokenize_helper(text, {
2306
+ text_pair,
2307
+ add_special_tokens
2308
+ });
2309
+ const input_ids = tokens.map((t) => this.added_tokens_map.get(t)?.id ?? this.model.tokens_to_ids.get(t) ?? this.model.unk_token_id);
2310
+ const result = {
2311
+ ids: input_ids,
2312
+ tokens,
2313
+ attention_mask: new Array(input_ids.length).fill(1)
2314
+ };
2315
+ if (return_token_type_ids && token_type_ids) result.token_type_ids = token_type_ids;
2316
+ return result;
2317
+ }
2318
+ decode(token_ids, options = {}) {
2319
+ if (!Array.isArray(token_ids) || token_ids.length === 0 || !is_integral_number(token_ids[0])) throw Error("token_ids must be a non-empty array of integers.");
2320
+ let tokens = token_ids.map((i) => this.model.vocab[Number(i)] ?? this.model.unk_token);
2321
+ if (options.skip_special_tokens) tokens = tokens.filter((x) => !this.special_tokens.includes(x));
2322
+ let decoded = this.decoder ? this.decoder(tokens) : tokens.join(" ");
2323
+ if (this.decoder && this.decoder.end_of_word_suffix) {
2324
+ decoded = decoded.replaceAll(this.decoder.end_of_word_suffix, " ");
2325
+ if (options.skip_special_tokens) decoded = decoded.trim();
2326
+ }
2327
+ if (options.clean_up_tokenization_spaces ?? this.clean_up_tokenization_spaces) decoded = clean_up_tokenization(decoded);
2328
+ return decoded;
2329
+ }
2330
+ /**
2331
+ * Converts a string into a sequence of tokens.
2332
+ * @param text The sequence to be encoded.
2333
+ * @param options An optional object containing the following properties:
2334
+ * @returns The list of tokens.
2335
+ */
2336
+ tokenize(text, { text_pair = null, add_special_tokens = false } = {}) {
2337
+ return this.tokenize_helper(text, {
2338
+ text_pair,
2339
+ add_special_tokens
2340
+ }).tokens;
2341
+ }
2342
+ encode_text(text) {
2343
+ if (text === null) return null;
2344
+ const sections = this.splitter_unnormalized.split(text);
2345
+ sections.forEach((section, i) => {
2346
+ const added_token = this.added_tokens_map.get(section);
2347
+ if (added_token) {
2348
+ if (added_token.lstrip && i > 0) sections[i - 1] = sections[i - 1].trimEnd();
2349
+ if (added_token.rstrip && i < sections.length - 1) sections[i + 1] = sections[i + 1].trimStart();
2350
+ }
2351
+ });
2352
+ return sections.flatMap((processed_text, section_index) => {
2353
+ if (processed_text.length === 0) return [];
2354
+ if (this.added_tokens_map.has(processed_text)) return [processed_text];
2355
+ if (this.remove_space === true) processed_text = processed_text.trim().split(/\s+/).join(" ");
2356
+ if (this.do_lowercase_and_remove_accent) processed_text = lowercase_and_remove_accents(processed_text);
2357
+ if (this.normalizer !== null) processed_text = this.normalizer(processed_text);
2358
+ if (processed_text.length === 0) return [];
2359
+ const subsections = this.splitter_normalized.split(processed_text);
2360
+ subsections.forEach((subsection, j) => {
2361
+ const added_token = this.added_tokens_map.get(subsection);
2362
+ if (added_token) {
2363
+ if (added_token.lstrip && j > 0) subsections[j - 1] = subsections[j - 1].trimEnd();
2364
+ if (added_token.rstrip && j < subsections.length - 1) subsections[j + 1] = subsections[j + 1].trimStart();
2365
+ }
2366
+ });
2367
+ return subsections.flatMap((subsection) => {
2368
+ if (subsection.length === 0) return [];
2369
+ if (this.added_tokens_map.has(subsection)) return [subsection];
2370
+ const section_tokens = this.pre_tokenizer !== null ? this.pre_tokenizer(subsection, { section_index }) : [subsection];
2371
+ return this.model(section_tokens);
2372
+ });
2373
+ });
2374
+ }
2375
+ tokenize_helper(text, { text_pair = null, add_special_tokens = true }) {
2376
+ const tokens1 = this.encode_text(text);
2377
+ const tokens2 = this.encode_text(text_pair || null);
2378
+ return this.post_processor ? this.post_processor(tokens1, tokens2, add_special_tokens) : { tokens: merge_arrays(tokens1 ?? [], tokens2 ?? []) };
2379
+ }
2380
+ /**
2381
+ * Converts a token string to its corresponding token ID.
2382
+ * @param token The token string to convert.
2383
+ * @returns The token ID, or undefined if the token is not in the vocabulary.
2384
+ */
2385
+ token_to_id(token) {
2386
+ return this.model.tokens_to_ids.get(token);
2387
+ }
2388
+ /**
2389
+ * Converts a token ID to its corresponding token string.
2390
+ * @param id The token ID to convert.
2391
+ * @returns The token string, or undefined if the ID is not in the vocabulary.
2392
+ */
2393
+ id_to_token(id) {
2394
+ return this.model.vocab[id];
2395
+ }
2396
+ /**
2397
+ * Returns a mapping of token IDs to AddedToken objects for all added tokens.
2398
+ * @returns A Map where keys are token IDs and values are AddedToken objects.
2399
+ */
2400
+ get_added_tokens_decoder() {
2401
+ const decoder = /* @__PURE__ */ new Map();
2402
+ for (const token of this.added_tokens) decoder.set(token.id, token);
2403
+ return decoder;
2404
+ }
2405
+ /**
2406
+ * Get the underlying vocabulary
2407
+ * @param with_added_tokens Whether to include the added tokens
2408
+ * @returns The vocabulary
2409
+ */
2410
+ get_vocab(with_added_tokens = true) {
2411
+ const vocab = /* @__PURE__ */ new Map();
2412
+ for (let i = 0; i < this.model.vocab.length; ++i) {
2413
+ const token = this.model.vocab[i];
2414
+ if (with_added_tokens || !this.added_tokens_map.has(token)) vocab.set(token, i);
2415
+ }
2416
+ return vocab;
2417
+ }
2418
+ };
2419
+ var Tokenizer_default = Tokenizer;
2420
+
2421
+ //#endregion
2422
+ //#region src/ai/image.ts
2423
+ function modelCacheRoot(manifest = flux2Klein4B8BitAbliterated) {
2424
+ return files.directory(files.paths.models, manifest.directory);
2425
+ }
2426
+ function assertImageModel(model) {
2427
+ if (model !== flux2Klein4B8BitAbliterated.model) throw new AIError("invalid_model", `Unsupported image model: ${model}`);
2428
+ }
2429
+ function modelRootFromOptions(modelRoot) {
2430
+ return modelRoot ?? modelCacheRoot().path;
2431
+ }
2432
+ function downloadConcurrency(value) {
2433
+ if (value === void 0) return 2;
2434
+ if (!Number.isInteger(value)) throw new AIError("invalid_model", "concurrency must be an integer");
2435
+ return Math.min(4, Math.max(1, value));
2436
+ }
2437
+ async function runConcurrent(items, concurrency, worker) {
2438
+ let nextIndex = 0;
2439
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
2440
+ while (nextIndex < items.length) {
2441
+ const item = items[nextIndex];
2442
+ nextIndex += 1;
2443
+ await worker(item);
2444
+ }
2445
+ });
2446
+ await Promise.all(workers);
2447
+ }
2448
+ function hfResolveUrl(repo, revision, path) {
2449
+ const encodedRepo = repo.split("/").map(encodeURIComponent).join("/");
2450
+ const encodedPath = path.split("/").map(encodeURIComponent).join("/");
2451
+ return `https://huggingface.co/${encodedRepo}/resolve/${encodeURIComponent(revision)}/${encodedPath}`;
2452
+ }
2453
+ const imageDimensionMultiple = 16;
2454
+ const minimumImageDimension = 64;
2455
+ const defaultImageLongSide = 1152;
2456
+ const aspectRatioValues = {
2457
+ "1:1": [1, 1],
2458
+ "16:9": [16, 9],
2459
+ "9:16": [9, 16],
2460
+ "4:3": [4, 3],
2461
+ "3:2": [3, 2],
2462
+ "2:3": [2, 3],
2463
+ "21:9": [21, 9]
2464
+ };
2465
+ function normalizedDimension(value) {
2466
+ return Math.max(minimumImageDimension, Math.floor(value / imageDimensionMultiple) * imageDimensionMultiple);
2467
+ }
2468
+ function normalizeImageSize(size, maxLongSide = defaultImageLongSide) {
2469
+ if (!Number.isFinite(size.width) || size.width <= 0) throw new AIError("invalid_model", "size.width must be positive");
2470
+ if (!Number.isFinite(size.height) || size.height <= 0) throw new AIError("invalid_model", "size.height must be positive");
2471
+ if (maxLongSide !== false && (!Number.isInteger(maxLongSide) || maxLongSide < minimumImageDimension)) throw new AIError("invalid_model", `maxLongSide must be false or an integer of at least ${minimumImageDimension}`);
2472
+ const scale = maxLongSide === false ? 1 : Math.min(1, maxLongSide / Math.max(size.width, size.height));
2473
+ return {
2474
+ width: normalizedDimension(size.width * scale),
2475
+ height: normalizedDimension(size.height * scale)
2476
+ };
2477
+ }
2478
+ function imageSizeForPreset(aspectRatio, maxLongSide = defaultImageLongSide) {
2479
+ const ratio = aspectRatioValues[aspectRatio];
2480
+ if (!ratio) throw new AIError("invalid_model", `Unsupported image aspect ratio: ${aspectRatio}`);
2481
+ assertPositiveInteger(maxLongSide, "maxLongSide");
2482
+ const [widthRatio, heightRatio] = ratio;
2483
+ return normalizeImageSize(widthRatio >= heightRatio ? {
2484
+ width: maxLongSide,
2485
+ height: maxLongSide * heightRatio / widthRatio
2486
+ } : {
2487
+ width: maxLongSide * widthRatio / heightRatio,
2488
+ height: maxLongSide
2489
+ }, maxLongSide);
2490
+ }
2491
+ function imageLoRARoot() {
2492
+ return files.directory(files.paths.models, "image-loras");
2493
+ }
2494
+ function safeCacheName(value, name) {
2495
+ const trimmed = value.trim();
2496
+ if (!trimmed) throw new AIError("invalid_model", `${name} must not be empty`);
2497
+ return trimmed.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
2498
+ }
2499
+ function sourceFileName(lora) {
2500
+ if (lora.source.type === "local") return lora.source.path.split("/").pop() || `${lora.id}.safetensors`;
2501
+ if (lora.source.type === "url") return lora.source.filename ?? lora.source.url.split("/").pop() ?? `${lora.id}.safetensors`;
2502
+ return lora.source.path?.split("/").pop() || `${lora.id}.safetensors`;
2503
+ }
2504
+ function assertLoRAScale(scale, name = "lora.scale") {
2505
+ const value = scale ?? 1;
2506
+ if (!Number.isFinite(value) || value <= 0) throw new AIError("invalid_model", `${name} must be a positive number`);
2507
+ return value;
2508
+ }
2509
+ function assertLoRADefinition(lora) {
2510
+ safeCacheName(lora.id, "lora.id");
2511
+ assertLoRAScale(lora.scale ?? lora.metadata?.recommendedScale);
2512
+ imageSigmaSchedule([lora.metadata?.sigmaSchedule]);
2513
+ if (lora.metadata?.baseModel !== void 0) assertImageModel(lora.metadata.baseModel);
2514
+ if (lora.metadata?.precision !== void 0 && ![
2515
+ "float16",
2516
+ "bfloat16",
2517
+ "float32"
2518
+ ].includes(lora.metadata.precision)) throw new AIError("invalid_model", "lora.metadata.precision is unsupported");
2519
+ }
2520
+ function assertLoRACompatibility(lora, model) {
2521
+ if (lora.metadata?.baseModel && lora.metadata.baseModel !== model) throw new AIError("invalid_model", `LoRA ${lora.id} targets ${lora.metadata.baseModel}, not ${model}`);
2522
+ }
2523
+ const commonLoRAFileNames = [
2524
+ "pytorch_lora_weights.safetensors",
2525
+ "adapter_model.safetensors",
2526
+ "lora.safetensors",
2527
+ "model.safetensors"
2528
+ ];
2529
+ async function resolveHuggingFaceLoRA(lora, options) {
2530
+ if (lora.source.type !== "huggingface" || lora.source.path) return lora;
2531
+ const revision = lora.source.revision ?? "main";
2532
+ const manifestFile = imageLoRARoot().directory(safeCacheName(lora.id, "lora.id")).file("manifest.json");
2533
+ if ((await manifestFile.stat()).type === "file") {
2534
+ const manifest = await manifestFile.json();
2535
+ if (isRecord(manifest) && isRecord(manifest.source) && manifest.source.type === "huggingface" && manifest.source.repo === lora.source.repo && (manifest.source.revision ?? "main") === revision && typeof manifest.source.path === "string") return {
2536
+ ...lora,
2537
+ source: {
2538
+ ...lora.source,
2539
+ revision,
2540
+ path: manifest.source.path,
2541
+ bytes: lora.source.bytes ?? (typeof manifest.source.bytes === "number" ? manifest.source.bytes : void 0)
2542
+ }
2543
+ };
2544
+ }
2545
+ const encodedRepo = lora.source.repo.split("/").map(encodeURIComponent).join("/");
2546
+ const response = await fetch(`https://huggingface.co/api/models/${encodedRepo}/revision/${encodeURIComponent(revision)}`, {
2547
+ headers: options.accessToken ? { Authorization: `Bearer ${options.accessToken}` } : void 0,
2548
+ signal: options.signal
2549
+ });
2550
+ if (!response.ok) throw new AIError("invalid_model", `Unable to inspect Hugging Face LoRA ${lora.source.repo}: ${response.status}`);
2551
+ const value = await response.json();
2552
+ const siblings = isRecord(value) && Array.isArray(value.siblings) ? value.siblings.flatMap((item) => {
2553
+ if (!isRecord(item) || typeof item.rfilename !== "string") return [];
2554
+ return [{
2555
+ path: item.rfilename,
2556
+ bytes: typeof item.size === "number" ? item.size : void 0
2557
+ }];
2558
+ }) : [];
2559
+ const common = commonLoRAFileNames.map((name) => siblings.find((file) => file.path === name)).find(Boolean);
2560
+ const safetensors = siblings.filter((file) => file.path.endsWith(".safetensors"));
2561
+ const selected = common ?? (safetensors.length === 1 ? safetensors[0] : void 0);
2562
+ if (!selected) {
2563
+ const candidates = safetensors.map((file) => file.path).join(", ");
2564
+ throw new AIError("invalid_model", candidates ? `Multiple LoRA weights found for ${lora.id}; set source.path to one of: ${candidates}` : `No .safetensors LoRA weight found for ${lora.id}`);
2565
+ }
2566
+ return {
2567
+ ...lora,
2568
+ source: {
2569
+ ...lora.source,
2570
+ revision,
2571
+ path: selected.path,
2572
+ bytes: lora.source.bytes ?? selected.bytes
2573
+ }
2574
+ };
2575
+ }
2576
+ function loraUrl(lora) {
2577
+ if (lora.source.type === "url") return lora.source.url;
2578
+ if (lora.source.type === "huggingface") {
2579
+ if (!lora.source.path) return void 0;
2580
+ return hfResolveUrl(lora.source.repo, lora.source.revision ?? "main", lora.source.path);
2581
+ }
2582
+ }
2583
+ function loraExpectedBytes(lora) {
2584
+ return lora.source.type === "local" ? void 0 : lora.source.bytes;
2585
+ }
2586
+ function hasSafetensorsHeader(size) {
2587
+ return size !== void 0 && size > 8;
2588
+ }
2589
+ function loraHeaders(lora, accessToken) {
2590
+ return lora.source.type === "huggingface" && accessToken ? { Authorization: `Bearer ${accessToken}` } : void 0;
2591
+ }
2592
+ function cachedLoRAFile(lora) {
2593
+ const id = safeCacheName(lora.id, "lora.id");
2594
+ return imageLoRARoot().directory(id).file(safeCacheName(sourceFileName(lora), "lora filename"));
2595
+ }
2596
+ async function ensureModelFile(input) {
2597
+ const destination = files.file(input.modelRoot, input.file.destination);
2598
+ const stat = await destination.stat();
2599
+ if (stat.type === "file" && stat.size === input.file.bytes) {
2600
+ input.onProgress?.({
2601
+ model: flux2Klein4B8BitAbliterated.model,
2602
+ file: input.file.destination,
2603
+ index: input.index,
2604
+ total: input.total,
2605
+ bytesWritten: input.file.bytes,
2606
+ totalBytes: input.file.bytes,
2607
+ fraction: 1
2608
+ });
2609
+ return;
2610
+ }
2611
+ await destination.download(hfResolveUrl(input.file.repo, input.file.revision, input.file.source), {
2612
+ headers: input.headers,
2613
+ expectedBytes: input.file.bytes,
2614
+ atomic: true,
2615
+ resume: true,
2616
+ onProgress(progress) {
2617
+ input.onProgress?.({
2618
+ model: flux2Klein4B8BitAbliterated.model,
2619
+ file: input.file.destination,
2620
+ index: input.index,
2621
+ total: input.total,
2622
+ ...progress
2623
+ });
2624
+ }
2625
+ }).result;
2626
+ }
2627
+ async function writePreparedManifest(modelRoot) {
2628
+ await files.file(modelRoot, "manifest.json").write(JSON.stringify(flux2Klein4B8BitAbliterated, null, 2));
2629
+ }
2630
+ async function imageModelState(model, modelRoot) {
2631
+ assertImageModel(model);
2632
+ let bytesPresent = 0;
2633
+ const missingFiles = [];
2634
+ const invalidFiles = [];
2635
+ for (const file of flux2KleinModelFiles) {
2636
+ const stat = await files.file(modelRoot, file.destination).stat();
2637
+ if (stat.type !== "file") {
2638
+ missingFiles.push(file.destination);
2639
+ continue;
2640
+ }
2641
+ bytesPresent += Math.min(stat.size ?? 0, file.bytes);
2642
+ if (stat.size !== file.bytes) invalidFiles.push(file.destination);
2643
+ }
2644
+ const installed = missingFiles.length === 0;
2645
+ const manifestStat = await files.file(modelRoot, "manifest.json").stat();
2646
+ if (manifestStat.type !== "file") missingFiles.push("manifest.json");
2647
+ return {
2648
+ model,
2649
+ modelRoot,
2650
+ paths: fluxImageModelPaths(modelRoot),
2651
+ installed,
2652
+ verified: installed && invalidFiles.length === 0 && manifestStat.type === "file",
2653
+ bytesPresent,
2654
+ totalBytes: flux2KleinTotalBytes,
2655
+ missingFiles,
2656
+ invalidFiles
2657
+ };
2658
+ }
2659
+ function tokenArray(value, name) {
2660
+ const candidate = isRecord(value) && "data" in value ? value.data : value;
2661
+ const values = Array.isArray(candidate) ? candidate : ArrayBuffer.isView(candidate) ? Array.from(candidate) : void 0;
2662
+ if (!values) throw new AIError("invalid_message", `Tokenizer did not return ${name}`);
2663
+ return values.map((token) => {
2664
+ const number = typeof token === "bigint" ? Number(token) : token;
2665
+ if (!Number.isInteger(number) || number < 0) throw new AIError("invalid_message", `${name} must contain token ids`);
2666
+ return number;
2667
+ });
2668
+ }
2669
+ async function createFluxTokenizer(modelRoot) {
2670
+ const paths = fluxImageModelPaths(modelRoot);
2671
+ const tokenizer = new Tokenizer_default(await files.file(paths.textEncoderPath, "tokenizer.json").json(), await files.file(paths.textEncoderPath, "tokenizer_config.json").json());
2672
+ const padTokenId = 151643;
2673
+ return ({ prompt, maxLength }) => {
2674
+ const inputIds = tokenArray(tokenizer.encode(formatQwen3KleinPrompt(prompt)).ids, "inputIds").slice(0, maxLength);
2675
+ const attentionMask = inputIds.map(() => 1);
2676
+ while (inputIds.length < maxLength) {
2677
+ inputIds.push(padTokenId);
2678
+ attentionMask.push(0);
2679
+ }
2680
+ return {
2681
+ inputIds,
2682
+ attentionMask
2683
+ };
2684
+ };
2685
+ }
2686
+ function formatQwen3KleinPrompt(prompt) {
2687
+ return [
2688
+ "<|im_start|>user",
2689
+ `${prompt.replaceAll("[IMG]", "")}<|im_end|>`,
2690
+ "<|im_start|>assistant",
2691
+ "<think>",
2692
+ "",
2693
+ "</think>",
2694
+ ""
2695
+ ].join("\n");
2696
+ }
2697
+ function assertPositiveInteger(value, name) {
2698
+ if (!Number.isInteger(value) || value <= 0) throw new AIError("invalid_model", `${name} must be a positive integer`);
2699
+ }
2700
+ function assertUnitInterval(value, name) {
2701
+ if (!Number.isFinite(value) || value <= 0 || value > 1) throw new AIError("invalid_model", `${name} must be greater than 0 and at most 1`);
2702
+ }
2703
+ function filePathFromUri(value, name) {
2704
+ const path = value.trim();
2705
+ if (!path) throw new AIError("invalid_message", `${name} must not be empty`);
2706
+ if (!path.startsWith("file://")) return path;
2707
+ try {
2708
+ return decodeURIComponent(new URL(path).pathname);
2709
+ } catch (error) {
2710
+ throw new AIError("invalid_message", `Invalid ${name} file URI`, error);
2711
+ }
2712
+ }
2713
+ function assertTokenArray(value, name) {
2714
+ if (value.length === 0) throw new AIError("invalid_message", `${name} must not be empty`);
2715
+ for (const token of value) if (!Number.isInteger(token) || token < 0) throw new AIError("invalid_message", `${name} must contain token ids`);
2716
+ }
2717
+ function imageResult(value, fallbackMetadata) {
2718
+ if (!isRecord(value) || typeof value.uri !== "string" || typeof value.width !== "number" || typeof value.height !== "number" || value.model !== "flux2-klein-4b-8bit-abliterated") throw new AIError("image_generation_failed", "Expected generated image URI");
2719
+ const result = value;
2720
+ const metadata = isRecord(result.metadata) ? result.metadata : {};
2721
+ return {
2722
+ uri: value.uri,
2723
+ width: value.width,
2724
+ height: value.height,
2725
+ model: value.model,
2726
+ metadata: {
2727
+ ...fallbackMetadata,
2728
+ model: metadata.model === value.model ? metadata.model : fallbackMetadata.model,
2729
+ width: typeof metadata.width === "number" ? metadata.width : value.width,
2730
+ height: typeof metadata.height === "number" ? metadata.height : value.height,
2731
+ steps: typeof metadata.steps === "number" ? metadata.steps : fallbackMetadata.steps,
2732
+ guidance: typeof metadata.guidance === "number" ? metadata.guidance : fallbackMetadata.guidance,
2733
+ strength: typeof metadata.strength === "number" ? metadata.strength : fallbackMetadata.strength,
2734
+ seed: typeof metadata.seed === "number" ? metadata.seed : fallbackMetadata.seed,
2735
+ elapsedMs: typeof metadata.elapsedMs === "number" ? metadata.elapsedMs : fallbackMetadata.elapsedMs,
2736
+ appliedLoRAs: Array.isArray(metadata.appliedLoRAs) ? metadata.appliedLoRAs.flatMap((item) => {
2737
+ if (!isRecord(item) || typeof item.path !== "string") return [];
2738
+ return [{
2739
+ id: typeof item.id === "string" ? item.id : void 0,
2740
+ path: item.path,
2741
+ scale: typeof item.scale === "number" ? item.scale : 1
2742
+ }];
2743
+ }) : fallbackMetadata.appliedLoRAs
2744
+ }
2745
+ };
2746
+ }
2747
+ async function withImageStage(stage, operation) {
2748
+ try {
2749
+ return await operation();
2750
+ } catch (error) {
2751
+ if (error instanceof ImageGenerationError) throw error;
2752
+ if (error instanceof AIError && error.code === "generation_canceled") throw error;
2753
+ throw new ImageGenerationError(stage, error instanceof Error ? error.message : String(error), error);
2754
+ }
2755
+ }
2756
+ function imagegenProgress(value) {
2757
+ const currentStep = value.currentStep;
2758
+ const totalSteps = value.totalSteps;
2759
+ if (typeof currentStep !== "number" || !Number.isFinite(currentStep) || typeof totalSteps !== "number" || !Number.isFinite(totalSteps) || totalSteps <= 0) return;
2760
+ return {
2761
+ currentStep,
2762
+ totalSteps,
2763
+ fraction: Math.max(0, Math.min(1, currentStep / totalSteps))
2764
+ };
2765
+ }
2766
+ function throwIfAborted(signal) {
2767
+ if (signal?.aborted) throw new AIError("generation_canceled", "Image generation canceled");
2768
+ }
2769
+ function nativeErrorStage(error) {
2770
+ const stage = (error instanceof Error ? error.message : String(error)).match(/^\[(download|tokenize|encode|denoise|decode|write)]\s/)?.[1] ?? (isRecord(error) && typeof error.stage === "string" ? error.stage : isRecord(error) && isRecord(error.userInfo) && typeof error.userInfo.stage === "string" ? error.userInfo.stage : void 0);
2771
+ return stage === "download" || stage === "tokenize" || stage === "encode" || stage === "denoise" || stage === "decode" || stage === "write" ? stage : void 0;
2772
+ }
2773
+ function nativeErrorMessage(error) {
2774
+ return (error instanceof Error ? error.message : String(error)).replace(/^\[(download|tokenize|encode|denoise|decode|write)]\s*/, "");
2775
+ }
2776
+ async function prepareImageModel(options = {}) {
2777
+ const model = options.model ?? flux2Klein4B8BitAbliterated.model;
2778
+ assertImageModel(model);
2779
+ const root = modelCacheRoot();
2780
+ await root.create({ recursive: true });
2781
+ const headers = options.accessToken ? { Authorization: `Bearer ${options.accessToken}` } : void 0;
2782
+ const total = flux2KleinModelFiles.length;
2783
+ const bytesByFile = /* @__PURE__ */ new Map();
2784
+ const emitProgress = (progress) => {
2785
+ const file = flux2KleinModelFiles.find((candidate) => candidate.destination === progress.file);
2786
+ const bytesWritten = file ? Math.min(progress.bytesWritten, file.bytes) : progress.bytesWritten;
2787
+ bytesByFile.set(progress.file, bytesWritten);
2788
+ const modelBytesWritten = Array.from(bytesByFile.values()).reduce((sum, bytes) => sum + bytes, 0);
2789
+ options.onProgress?.({
2790
+ ...progress,
2791
+ bytesWritten,
2792
+ modelBytesWritten,
2793
+ totalModelBytes: flux2KleinTotalBytes,
2794
+ modelFraction: modelBytesWritten / flux2KleinTotalBytes
2795
+ });
2796
+ };
2797
+ const missingFiles = [];
2798
+ for (const [index, file] of flux2KleinModelFiles.entries()) {
2799
+ const stat = await files.file(root.path, file.destination).stat();
2800
+ if (stat.type === "file" && stat.size === file.bytes) emitProgress({
2801
+ model,
2802
+ file: file.destination,
2803
+ index: index + 1,
2804
+ total,
2805
+ bytesWritten: file.bytes,
2806
+ totalBytes: file.bytes,
2807
+ fraction: 1
2808
+ });
2809
+ else missingFiles.push({
2810
+ file,
2811
+ index: index + 1
2812
+ });
2813
+ }
2814
+ await runConcurrent(missingFiles, downloadConcurrency(options.concurrency), async ({ file, index }) => {
2815
+ await ensureModelFile({
2816
+ modelRoot: root.path,
2817
+ file,
2818
+ headers,
2819
+ index,
2820
+ total,
2821
+ onProgress: emitProgress
2822
+ });
2823
+ });
2824
+ await writePreparedManifest(root.path);
2825
+ return {
2826
+ model,
2827
+ ...fluxImageModelPaths(root.path),
2828
+ tokenizer: await createFluxTokenizer(root.path)
2829
+ };
2830
+ }
2831
+ async function verifyImageModel(options = {}) {
2832
+ return imageModelState(options.model ?? flux2Klein4B8BitAbliterated.model, modelRootFromOptions(options.modelRoot));
2833
+ }
2834
+ async function listImageModels(options = {}) {
2835
+ return [await imageModelState(flux2Klein4B8BitAbliterated.model, modelRootFromOptions(options.modelRoot))];
2836
+ }
2837
+ async function deleteImageModel(options = {}) {
2838
+ assertImageModel(options.model ?? flux2Klein4B8BitAbliterated.model);
2839
+ await files.directory(modelRootFromOptions(options.modelRoot)).delete({ recursive: true });
2840
+ }
2841
+ async function imageMemoryTelemetry() {
2842
+ const value = await bridge().imageMemoryTelemetry?.();
2843
+ if (!value) throw new AIError("image_generation_failed", "Image service is unavailable");
2844
+ return value;
2845
+ }
2846
+ async function unloadImageModel() {
2847
+ return await bridge().unloadImageModel?.() ?? false;
2848
+ }
2849
+ async function upscaleImage(options) {
2850
+ throwIfAborted(options.signal);
2851
+ const scale = normalizeUpscaleScale(options.scale);
2852
+ const value = await bridge().upscaleImage?.({
2853
+ imagePath: filePathFromUri(options.image, "image"),
2854
+ scale
2855
+ });
2856
+ throwIfAborted(options.signal);
2857
+ if (!value || typeof value.uri !== "string" || !Number.isInteger(value.width) || !Number.isInteger(value.height) || value.scale !== scale) throw new AIError("image_generation_failed", "Native image upscaling returned an invalid result");
2858
+ return value;
2859
+ }
2860
+ async function prepareImageLoRA(lora, options = {}) {
2861
+ assertLoRADefinition(lora);
2862
+ const resolvedDefinition = await resolveHuggingFaceLoRA(lora, options);
2863
+ const id = safeCacheName(lora.id, "lora.id");
2864
+ const scale = assertLoRAScale(lora.scale ?? lora.metadata?.recommendedScale);
2865
+ if (resolvedDefinition.source.type === "local") {
2866
+ const path = filePathFromUri(resolvedDefinition.source.path, `lora ${id}`);
2867
+ const stat$1 = await files.file(path).stat();
2868
+ if (stat$1.type !== "file") throw new AIError("invalid_model", `Missing LoRA file: ${path}`);
2869
+ if (!hasSafetensorsHeader(stat$1.size)) throw new AIError("invalid_model", `Invalid LoRA safetensors file: ${path}`);
2870
+ return {
2871
+ id: lora.id,
2872
+ description: lora.description,
2873
+ path,
2874
+ uri: stat$1.uri,
2875
+ scale,
2876
+ source: resolvedDefinition.source,
2877
+ metadata: lora.metadata
2878
+ };
2879
+ }
2880
+ const url = loraUrl(resolvedDefinition);
2881
+ if (!url) throw new AIError("invalid_model", `Unsupported LoRA source: ${lora.id}`);
2882
+ const root = imageLoRARoot().directory(id);
2883
+ await root.create({ recursive: true });
2884
+ const destination = root.file(safeCacheName(sourceFileName(resolvedDefinition), "lora filename"));
2885
+ const expectedBytes = loraExpectedBytes(resolvedDefinition);
2886
+ const stat = await destination.stat();
2887
+ if (stat.type !== "file" || !hasSafetensorsHeader(stat.size) || expectedBytes !== void 0 && stat.size !== expectedBytes) await destination.download(url, {
2888
+ headers: loraHeaders(resolvedDefinition, options.accessToken),
2889
+ expectedBytes,
2890
+ atomic: true,
2891
+ resume: true,
2892
+ signal: options.signal,
2893
+ onProgress(progress) {
2894
+ options.onProgress?.({
2895
+ lora: lora.id,
2896
+ file: destination.path,
2897
+ bytesWritten: progress.bytesWritten,
2898
+ totalBytes: progress.totalBytes,
2899
+ fraction: progress.fraction
2900
+ });
2901
+ }
2902
+ }).result;
2903
+ await root.file("manifest.json").write(JSON.stringify({
2904
+ formatVersion: 1,
2905
+ id: lora.id,
2906
+ file: destination.path.split("/").pop(),
2907
+ source: resolvedDefinition.source,
2908
+ metadata: lora.metadata
2909
+ }, null, 2));
2910
+ const preparedStat = await destination.stat();
2911
+ if (preparedStat.type !== "file" || !hasSafetensorsHeader(preparedStat.size)) throw new AIError("invalid_model", `Invalid LoRA safetensors file: ${destination.path}`);
2912
+ return {
2913
+ id: lora.id,
2914
+ description: lora.description,
2915
+ path: destination.path,
2916
+ uri: preparedStat.uri,
2917
+ scale,
2918
+ source: resolvedDefinition.source,
2919
+ metadata: lora.metadata
2920
+ };
2921
+ }
2922
+ async function verifyImageLoRA(lora) {
2923
+ assertLoRADefinition(lora);
2924
+ const scale = assertLoRAScale(lora.scale ?? lora.metadata?.recommendedScale);
2925
+ let resolvedDefinition = lora;
2926
+ if (lora.source.type === "huggingface" && !lora.source.path) {
2927
+ const manifestFile = imageLoRARoot().directory(safeCacheName(lora.id, "lora.id")).file("manifest.json");
2928
+ if ((await manifestFile.stat()).type === "file") {
2929
+ const manifest = await manifestFile.json();
2930
+ if (isRecord(manifest) && isRecord(manifest.source) && manifest.source.type === "huggingface" && manifest.source.repo === lora.source.repo && (manifest.source.revision ?? "main") === (lora.source.revision ?? "main") && typeof manifest.source.path === "string") resolvedDefinition = {
2931
+ ...lora,
2932
+ source: {
2933
+ ...lora.source,
2934
+ path: manifest.source.path,
2935
+ revision: typeof manifest.source.revision === "string" ? manifest.source.revision : lora.source.revision,
2936
+ bytes: typeof manifest.source.bytes === "number" ? manifest.source.bytes : lora.source.bytes
2937
+ }
2938
+ };
2939
+ }
2940
+ }
2941
+ const file = resolvedDefinition.source.type === "local" ? files.file(filePathFromUri(resolvedDefinition.source.path, `lora ${lora.id}`)) : cachedLoRAFile(resolvedDefinition);
2942
+ const stat = await file.stat();
2943
+ const expectedBytes = loraExpectedBytes(resolvedDefinition);
2944
+ const installed = stat.type === "file";
2945
+ const verified = installed && hasSafetensorsHeader(stat.size) && (expectedBytes === void 0 || stat.size === expectedBytes);
2946
+ return {
2947
+ id: lora.id,
2948
+ description: lora.description,
2949
+ path: file.path,
2950
+ uri: stat.uri,
2951
+ scale,
2952
+ source: resolvedDefinition.source,
2953
+ metadata: lora.metadata,
2954
+ installed,
2955
+ verified,
2956
+ size: stat.size
2957
+ };
2958
+ }
2959
+ async function listImageLoRAs(loras) {
2960
+ return Promise.all(loras.map((lora) => verifyImageLoRA(lora)));
2961
+ }
2962
+ async function deleteImageLoRA(lora) {
2963
+ const id = safeCacheName(typeof lora === "string" ? lora : lora.id, "lora.id");
2964
+ await imageLoRARoot().directory(id).delete({ recursive: true });
2965
+ }
2966
+ async function prepareSelectedImageLoRAs(loras, model, options = {}) {
2967
+ const prepared = [];
2968
+ for (const lora of loras) {
2969
+ assertLoRADefinition(lora);
2970
+ assertLoRACompatibility(lora, model);
2971
+ prepared.push(await prepareImageLoRA(lora, options));
2972
+ }
2973
+ return prepared;
2974
+ }
2975
+ async function generateImage(options) {
2976
+ throwIfAborted(options.signal);
2977
+ const model = options.model ?? flux2Klein4B8BitAbliterated.model;
2978
+ assertImageModel(model);
2979
+ const prompt = options.prompt.trim();
2980
+ if (!prompt) throw new AIError("invalid_message", "Expected prompt");
2981
+ const presetSize = options.aspectRatio ? imageSizeForPreset(options.aspectRatio) : void 0;
2982
+ const requestedSize = normalizeImageSize({
2983
+ width: options.width ?? presetSize?.width ?? 1024,
2984
+ height: options.height ?? presetSize?.height ?? 1024
2985
+ }, false);
2986
+ const width = requestedSize.width;
2987
+ const height = requestedSize.height;
2988
+ const outpaintSize = options.outpaint ? normalizeImageSize({
2989
+ width: options.outpaint.width,
2990
+ height: options.outpaint.height
2991
+ }, false) : void 0;
2992
+ if (options.outpaint && (outpaintSize?.width !== options.outpaint.width || outpaintSize?.height !== options.outpaint.height)) throw new AIError("invalid_model", "outpaint width and height must be multiples of 16");
2993
+ const steps = options.steps ?? 4;
2994
+ const guidance = options.guidance ?? 1;
2995
+ const seed = options.seed ?? Math.floor(Math.random() * 4294967296);
2996
+ const sourceImagePath = options.image ? filePathFromUri(options.image, "image") : void 0;
2997
+ const maskImagePath = options.mask ? filePathFromUri(options.mask, "mask") : void 0;
2998
+ const referenceImagePaths = (options.references ?? []).map((reference, index) => filePathFromUri(reference, `references[${index}]`));
2999
+ const isImageConditioned = Boolean(sourceImagePath) || referenceImagePaths.length > 0;
3000
+ const strength = options.strength ?? (isImageConditioned ? .8 : void 0);
3001
+ if (maskImagePath && !sourceImagePath) throw new AIError("invalid_message", "mask requires image");
3002
+ if (options.maskMode !== void 0 && options.maskMode !== "paint" && options.maskMode !== "preserve") throw new AIError("invalid_model", "maskMode must be paint or preserve");
3003
+ if (options.outpaint && !sourceImagePath) throw new AIError("invalid_message", "outpaint requires image");
3004
+ if (options.outpaint) {
3005
+ assertPositiveInteger(options.outpaint.width, "outpaint.width");
3006
+ assertPositiveInteger(options.outpaint.height, "outpaint.height");
3007
+ const rect = options.outpaint.sourceRect;
3008
+ if (!rect) throw new AIError("invalid_model", "outpaint.sourceRect is required");
3009
+ assertPositiveInteger(rect.width, "outpaint.sourceRect.width");
3010
+ assertPositiveInteger(rect.height, "outpaint.sourceRect.height");
3011
+ if (!Number.isInteger(rect.x) || !Number.isInteger(rect.y)) throw new AIError("invalid_model", "outpaint.sourceRect x and y must be integers");
3012
+ const canvasWidth = outpaintSize?.width ?? options.outpaint.width;
3013
+ const canvasHeight = outpaintSize?.height ?? options.outpaint.height;
3014
+ if (!(rect.x < canvasWidth && rect.y < canvasHeight && rect.x + rect.width > 0 && rect.y + rect.height > 0)) throw new AIError("invalid_model", "outpaint.sourceRect must intersect the canvas");
3015
+ if (!options.outpaint.autoCrop && (rect.x < 0 || rect.y < 0 || rect.x + rect.width > canvasWidth || rect.y + rect.height > canvasHeight)) throw new AIError("invalid_model", "outpaint.sourceRect must fit within the outpaint canvas");
3016
+ }
3017
+ if (options.resizeMode !== void 0 && options.resizeMode !== "stretch" && options.resizeMode !== "fit" && options.resizeMode !== "crop") throw new AIError("invalid_model", "resizeMode must be stretch, fit, or crop");
3018
+ if (options.maxInputLongSide !== void 0 && options.maxInputLongSide !== false && (!Number.isInteger(options.maxInputLongSide) || options.maxInputLongSide < 64)) throw new AIError("invalid_model", "maxInputLongSide must be false or an integer of at least 64");
3019
+ assertPositiveInteger(width, "width");
3020
+ assertPositiveInteger(height, "height");
3021
+ assertPositiveInteger(steps, "steps");
3022
+ if (seed !== void 0 && (!Number.isInteger(seed) || seed < 0)) throw new AIError("invalid_model", "seed must be a non-negative integer");
3023
+ if (!Number.isFinite(guidance) || guidance <= 0) throw new AIError("invalid_model", "guidance must be a positive number");
3024
+ if (referenceImagePaths.length > 3) throw new AIError("invalid_model", "references must contain at most 3 images");
3025
+ if (options.referenceInfluences !== void 0) {
3026
+ if (options.referenceInfluences.length !== referenceImagePaths.length) throw new AIError("invalid_model", "referenceInfluences must match references length");
3027
+ for (const [index, influence] of options.referenceInfluences.entries()) if (!Number.isFinite(influence) || influence < 0) throw new AIError("invalid_model", `referenceInfluences[${index}] must be a non-negative number`);
3028
+ }
3029
+ if (strength !== void 0) assertUnitInterval(strength, "strength");
3030
+ const selectedLoRAs = options.loras?.length ? await withImageStage("download", () => prepareSelectedImageLoRAs(options.loras ?? [], model, {
3031
+ accessToken: options.accessToken,
3032
+ signal: options.signal
3033
+ })) : [];
3034
+ const sigmaSchedule = imageSigmaSchedule(selectedLoRAs.map((lora) => lora.metadata?.sigmaSchedule));
3035
+ if (sigmaSchedule && options.steps !== void 0 && options.steps !== sigmaSchedule.length) throw new AIError("invalid_model", `steps must equal the LoRA sigma schedule length (${sigmaSchedule.length})`);
3036
+ const generationSteps = sigmaSchedule?.length ?? steps;
3037
+ const prepared = options.modelRoot && options.tokenizer ? void 0 : await withImageStage("download", () => prepareImageModel({
3038
+ model,
3039
+ accessToken: options.accessToken,
3040
+ concurrency: options.concurrency,
3041
+ onProgress: options.onModelProgress
3042
+ }));
3043
+ throwIfAborted(options.signal);
3044
+ const tokenizer = options.tokenizer ?? prepared?.tokenizer;
3045
+ const modelRoot = options.modelRoot ?? prepared?.modelRoot;
3046
+ if (!tokenizer || !modelRoot) throw new AIError("invalid_model", "Expected image model root and tokenizer");
3047
+ const tokenized = await withImageStage("tokenize", () => tokenizer({
3048
+ prompt,
3049
+ maxLength: 512
3050
+ }));
3051
+ throwIfAborted(options.signal);
3052
+ await withImageStage("tokenize", () => {
3053
+ assertTokenArray(tokenized.inputIds, "inputIds");
3054
+ assertTokenArray(tokenized.attentionMask, "attentionMask");
3055
+ if (tokenized.inputIds.length !== tokenized.attentionMask.length) throw new AIError("invalid_message", "inputIds and attentionMask must have the same length");
3056
+ });
3057
+ const paths = fluxImageModelPaths(modelRoot);
3058
+ const request = {
3059
+ model,
3060
+ prompt,
3061
+ sourceImagePath,
3062
+ maskImagePath,
3063
+ maskMode: options.maskMode,
3064
+ outpaintWidth: outpaintSize?.width,
3065
+ outpaintHeight: outpaintSize?.height,
3066
+ outpaintSourceX: options.outpaint?.sourceRect.x,
3067
+ outpaintSourceY: options.outpaint?.sourceRect.y,
3068
+ outpaintSourceWidth: options.outpaint?.sourceRect.width,
3069
+ outpaintSourceHeight: options.outpaint?.sourceRect.height,
3070
+ outpaintAutoCrop: options.outpaint?.autoCrop,
3071
+ resizeMode: options.resizeMode,
3072
+ maxInputLongSide: options.maxInputLongSide === false ? 0 : options.maxInputLongSide ?? defaultImageLongSide,
3073
+ referenceImagePaths,
3074
+ referenceInfluences: options.referenceInfluences,
3075
+ loraPaths: selectedLoRAs.map((lora) => lora.path),
3076
+ loraScales: selectedLoRAs.map((lora) => lora.scale),
3077
+ loraIds: selectedLoRAs.map((lora) => lora.id),
3078
+ sigmaSchedule,
3079
+ width,
3080
+ height,
3081
+ steps: generationSteps,
3082
+ guidance,
3083
+ strength,
3084
+ seed,
3085
+ inputIds: tokenized.inputIds,
3086
+ attentionMask: tokenized.attentionMask,
3087
+ ...paths
3088
+ };
3089
+ const fallbackMetadata = {
3090
+ model,
3091
+ width: outpaintSize?.width ?? width,
3092
+ height: outpaintSize?.height ?? height,
3093
+ steps: generationSteps,
3094
+ guidance,
3095
+ strength,
3096
+ seed,
3097
+ appliedLoRAs: selectedLoRAs.map((lora) => ({
3098
+ id: lora.id,
3099
+ path: lora.path,
3100
+ scale: lora.scale
3101
+ })),
3102
+ sigmaSchedule
3103
+ };
3104
+ try {
3105
+ const native = bridge();
3106
+ const cancelNative = () => {
3107
+ native.cancelImagegen?.();
3108
+ };
3109
+ const subscription = options.onProgress ? native.addListener?.("imagegenProgress", (event) => {
3110
+ const progress = imagegenProgress(event);
3111
+ if (progress) options.onProgress?.(progress);
3112
+ }) : void 0;
3113
+ try {
3114
+ options.signal?.addEventListener("abort", cancelNative, { once: true });
3115
+ throwIfAborted(options.signal);
3116
+ const value = await native.generateImage?.(request);
3117
+ throwIfAborted(options.signal);
3118
+ return imageResult(value, fallbackMetadata);
3119
+ } finally {
3120
+ subscription?.remove();
3121
+ options.signal?.removeEventListener("abort", cancelNative);
3122
+ }
3123
+ } catch (error) {
3124
+ if (error instanceof ImageGenerationError) throw error;
3125
+ if (error instanceof AIError) throw error;
3126
+ if (options.signal?.aborted) throw new AIError("generation_canceled", "Image generation canceled", error);
3127
+ const mapped = mapNativeError(error);
3128
+ if (mapped.code === "generation_failed" || mapped.code === "image_generation_failed") throw new ImageGenerationError(nativeErrorStage(error) ?? "denoise", nativeErrorMessage(error), error);
3129
+ throw mapped;
3130
+ }
3131
+ }
3132
+ async function generateImages(options) {
3133
+ const { count,...imageOptions } = options;
3134
+ const results = [];
3135
+ for (const seed of batchImageSeeds(count, options.seed)) {
3136
+ throwIfAborted(options.signal);
3137
+ results.push(await generateImage({
3138
+ ...imageOptions,
3139
+ seed
3140
+ }));
3141
+ }
3142
+ return results;
3143
+ }
3144
+ const imageGenerationQueueKey = Symbol.for("bleam.ai.imageGenerationQueue");
3145
+ function imageGenerationQueue() {
3146
+ const scope = globalThis;
3147
+ const queue = scope[imageGenerationQueueKey] ?? new ImageGenerationQueue(generateImage);
3148
+ queue.setExecutor(generateImage);
3149
+ scope[imageGenerationQueueKey] = queue;
3150
+ return queue;
3151
+ }
3152
+ function scheduleImageGeneration(options) {
3153
+ return imageGenerationQueue().schedule(options);
3154
+ }
3155
+ function getImageGenerationJob(id) {
3156
+ return imageGenerationQueue().get(id);
3157
+ }
3158
+ function listImageGenerationJobs() {
3159
+ return imageGenerationQueue().list();
3160
+ }
3161
+
3162
+ //#endregion
3163
+ //#region src/ai/image-composer.tsx
3164
+ const defaultImageSize = defaultImageLongSide;
3165
+ const defaultStepCount = 4;
3166
+ const minOutpaintScale = .35;
3167
+ const maxOutpaintScale = .95;
3168
+ function roundImageDimension(value) {
3169
+ return normalizeImageSize({
3170
+ width: value,
3171
+ height: value
3172
+ }, false).width;
3173
+ }
3174
+ function imageSizeForAspectRatio(size, maxSide = defaultImageSize) {
3175
+ if (size.width <= 0 || size.height <= 0) return {
3176
+ width: maxSide,
3177
+ height: maxSide
3178
+ };
3179
+ const aspect = size.width / size.height;
3180
+ return normalizeImageSize({
3181
+ width: aspect >= 1 ? maxSide : maxSide * aspect,
3182
+ height: aspect >= 1 ? maxSide / aspect : maxSide
3183
+ }, maxSide);
3184
+ }
3185
+ function roundedImageSize(size) {
3186
+ return normalizeImageSize(size);
3187
+ }
3188
+ function clamp(value, min$1, max) {
3189
+ return Math.min(max, Math.max(min$1, value));
3190
+ }
3191
+ function clampOutpaintRect(rect) {
3192
+ const width = Math.min(defaultImageSize, Math.max(1, Math.round(rect.width)));
3193
+ const height = Math.min(defaultImageSize, Math.max(1, Math.round(rect.height)));
3194
+ return {
3195
+ x: Math.round(clamp(rect.x, 0, defaultImageSize - width)),
3196
+ y: Math.round(clamp(rect.y, 0, defaultImageSize - height)),
3197
+ width,
3198
+ height
3199
+ };
3200
+ }
3201
+ function rectForSourceScale(sourceSize, scale, center) {
3202
+ const aspect = sourceSize.width / sourceSize.height;
3203
+ const longSide = defaultImageSize * clamp(scale, minOutpaintScale, maxOutpaintScale);
3204
+ const width = aspect >= 1 ? longSide : longSide * aspect;
3205
+ const height = aspect >= 1 ? longSide / aspect : longSide;
3206
+ const nextCenter = center ?? {
3207
+ x: defaultImageSize / 2,
3208
+ y: defaultImageSize / 2
3209
+ };
3210
+ return clampOutpaintRect({
3211
+ x: nextCenter.x - width / 2,
3212
+ y: nextCenter.y - height / 2,
3213
+ width,
3214
+ height
3215
+ });
3216
+ }
3217
+ function initialOutpaintRect(sourceSize) {
3218
+ return rectForSourceScale(sourceSize, .72);
3219
+ }
3220
+ function buildImageGenerationRequest(input, options = {}) {
3221
+ const base = {
3222
+ prompt: input.prompt,
3223
+ mode: input.mode,
3224
+ steps: defaultStepCount,
3225
+ guidance: 1,
3226
+ ...options
3227
+ };
3228
+ if (input.mode === "text") {
3229
+ const references = (input.refs ?? []).map((image) => image.path);
3230
+ return {
3231
+ ...base,
3232
+ references: references.length > 0 ? references : void 0,
3233
+ referenceInfluences: references.length > 0 ? references.map(() => 1) : void 0,
3234
+ strength: references.length > 0 ? 1 : void 0,
3235
+ width: defaultImageSize,
3236
+ height: defaultImageSize
3237
+ };
3238
+ }
3239
+ if (input.mode === "subject") {
3240
+ if (!input.subject) throw new Error("Subject mode requires a subject image");
3241
+ const refs = input.refs ?? [];
3242
+ const references = [input.subject, ...refs].map((image) => image.path);
3243
+ const size = imageSizeForAspectRatio(input.subject.size);
3244
+ return {
3245
+ ...base,
3246
+ references,
3247
+ referenceInfluences: references.map(() => 1),
3248
+ strength: 1,
3249
+ width: size.width,
3250
+ height: size.height
3251
+ };
3252
+ }
3253
+ if (input.mode === "edit") {
3254
+ if (!input.source) throw new Error("Edit requires a source image");
3255
+ const references = (input.refs ?? []).map((image) => image.path);
3256
+ const size = roundedImageSize(input.source.size);
3257
+ return {
3258
+ ...base,
3259
+ image: input.source.path,
3260
+ references: references.length > 0 ? references : void 0,
3261
+ referenceInfluences: references.length > 0 ? references.map(() => 1) : void 0,
3262
+ strength: references.length > 0 ? 1 : .35,
3263
+ width: size.width,
3264
+ height: size.height
3265
+ };
3266
+ }
3267
+ if (!input.source) throw new Error("Outpaint requires a source image");
3268
+ if (!input.rect) throw new Error("Outpaint requires a source placement");
3269
+ return {
3270
+ ...base,
3271
+ image: input.source.path,
3272
+ outpaint: {
3273
+ width: defaultImageSize,
3274
+ height: defaultImageSize,
3275
+ sourceRect: input.rect,
3276
+ autoCrop: true
3277
+ },
3278
+ strength: .8,
3279
+ width: defaultImageSize,
3280
+ height: defaultImageSize
3281
+ };
3282
+ }
3283
+ function useImageComposer(modeOrOptions, options = {}) {
3284
+ if (typeof modeOrOptions !== "string") return useImageComposerController(modeOrOptions ?? {});
3285
+ const mode = modeOrOptions;
3286
+ if (mode === "text") return useTextComposer(options);
3287
+ if (mode === "subject") return useSubjectComposer(options);
3288
+ if (mode === "edit") return useEditComposer(options);
3289
+ return useOutpaintComposer(options);
3290
+ }
3291
+ function useImageComposerController(options) {
3292
+ const [mode, setMode] = useAtom(useComposerAtom(options.initialMode ?? "text"));
3293
+ const text = useTextComposer(options);
3294
+ const subject = useSubjectComposer(options);
3295
+ const edit = useEditComposer(options);
3296
+ const outpaint = useOutpaintComposer(options);
3297
+ const active = mode === "text" ? text : mode === "subject" ? subject : mode === "edit" ? edit : outpaint;
3298
+ const error = active.error ?? text.error ?? subject.error ?? edit.error ?? outpaint.error;
3299
+ return {
3300
+ mode,
3301
+ setMode,
3302
+ active,
3303
+ text,
3304
+ subject,
3305
+ edit,
3306
+ outpaint,
3307
+ isPicking: text.isPicking || subject.isPicking || edit.isPicking || outpaint.isPicking,
3308
+ error,
3309
+ request: (prompt) => active.request(prompt),
3310
+ clear() {
3311
+ text.clear();
3312
+ subject.clear();
3313
+ edit.clear();
3314
+ outpaint.clear();
3315
+ }
3316
+ };
3317
+ }
3318
+ function useTextComposer(options) {
3319
+ const store = useComposerAtom({
3320
+ refs: [],
3321
+ isPicking: false,
3322
+ error: null
3323
+ });
3324
+ const [state, setState] = useAtom(store);
3325
+ const maxRefs = options.maxRefs ?? 3;
3326
+ async function pickRefs() {
3327
+ setState((current) => ({
3328
+ ...current,
3329
+ isPicking: true,
3330
+ error: null
3331
+ }));
3332
+ try {
3333
+ const refs = [...store.get().refs, ...await pickImages(options.inputRoot)].slice(0, maxRefs);
3334
+ setState((current) => ({
3335
+ ...current,
3336
+ refs,
3337
+ isPicking: false
3338
+ }));
3339
+ } catch (error) {
3340
+ setState((current) => ({
3341
+ ...current,
3342
+ error: errorFromUnknown(error),
3343
+ isPicking: false
3344
+ }));
3345
+ }
3346
+ }
3347
+ return {
3348
+ mode: "text",
3349
+ ...state,
3350
+ pickRefs,
3351
+ removeRef: (index) => setState((current) => removeRefFromState(current, index)),
3352
+ request: (prompt) => ({
3353
+ mode: "text",
3354
+ prompt,
3355
+ refs: store.get().refs
3356
+ }),
3357
+ clear: () => setState({
3358
+ refs: [],
3359
+ isPicking: false,
3360
+ error: null
3361
+ })
3362
+ };
3363
+ }
3364
+ function useSubjectComposer(options) {
3365
+ const store = useComposerAtom({
3366
+ subject: null,
3367
+ refs: [],
3368
+ isPicking: false,
3369
+ error: null
3370
+ });
3371
+ const [state, setState] = useAtom(store);
3372
+ const maxRefs = options.maxRefs ?? 2;
3373
+ async function pickSubject() {
3374
+ setState((current) => ({
3375
+ ...current,
3376
+ isPicking: true,
3377
+ error: null
3378
+ }));
3379
+ try {
3380
+ const subject = await pickOneImage(options.inputRoot);
3381
+ setState((current) => ({
3382
+ ...current,
3383
+ subject: subject ?? current.subject,
3384
+ isPicking: false
3385
+ }));
3386
+ } catch (error) {
3387
+ setState((current) => ({
3388
+ ...current,
3389
+ error: errorFromUnknown(error),
3390
+ isPicking: false
3391
+ }));
3392
+ }
3393
+ }
3394
+ async function pickRefs() {
3395
+ setState((current) => ({
3396
+ ...current,
3397
+ isPicking: true,
3398
+ error: null
3399
+ }));
3400
+ try {
3401
+ const refs = [...store.get().refs, ...await pickImages(options.inputRoot)].slice(0, maxRefs);
3402
+ setState((current) => ({
3403
+ ...current,
3404
+ refs,
3405
+ isPicking: false
3406
+ }));
3407
+ } catch (error) {
3408
+ setState((current) => ({
3409
+ ...current,
3410
+ error: errorFromUnknown(error),
3411
+ isPicking: false
3412
+ }));
3413
+ }
3414
+ }
3415
+ return {
3416
+ mode: "subject",
3417
+ ...state,
3418
+ pickSubject,
3419
+ removeSubject: () => setState((current) => ({
3420
+ ...current,
3421
+ subject: null
3422
+ })),
3423
+ pickRefs,
3424
+ removeRef: (index) => setState((current) => removeRefFromState(current, index)),
3425
+ request: (prompt) => {
3426
+ const current = store.get();
3427
+ return {
3428
+ mode: "subject",
3429
+ prompt,
3430
+ subject: current.subject,
3431
+ refs: current.refs
3432
+ };
3433
+ },
3434
+ clear: () => setState({
3435
+ subject: null,
3436
+ refs: [],
3437
+ isPicking: false,
3438
+ error: null
3439
+ })
3440
+ };
3441
+ }
3442
+ function useEditComposer(options) {
3443
+ const store = useComposerAtom({
3444
+ source: null,
3445
+ refs: [],
3446
+ isPicking: false,
3447
+ error: null
3448
+ });
3449
+ const [state, setState] = useAtom(store);
3450
+ const maxRefs = options.maxRefs ?? 3;
3451
+ async function pickSource() {
3452
+ setState((current) => ({
3453
+ ...current,
3454
+ isPicking: true,
3455
+ error: null
3456
+ }));
3457
+ try {
3458
+ const source = await pickOneImage(options.inputRoot);
3459
+ setState((current) => ({
3460
+ ...current,
3461
+ source: source ?? current.source,
3462
+ isPicking: false
3463
+ }));
3464
+ } catch (error) {
3465
+ setState((current) => ({
3466
+ ...current,
3467
+ error: errorFromUnknown(error),
3468
+ isPicking: false
3469
+ }));
3470
+ }
3471
+ }
3472
+ async function pickRefs() {
3473
+ setState((current) => ({
3474
+ ...current,
3475
+ isPicking: true,
3476
+ error: null
3477
+ }));
3478
+ try {
3479
+ const refs = [...store.get().refs, ...await pickImages(options.inputRoot)].slice(0, maxRefs);
3480
+ setState((current) => ({
3481
+ ...current,
3482
+ refs,
3483
+ isPicking: false
3484
+ }));
3485
+ } catch (error) {
3486
+ setState((current) => ({
3487
+ ...current,
3488
+ error: errorFromUnknown(error),
3489
+ isPicking: false
3490
+ }));
3491
+ }
3492
+ }
3493
+ return {
3494
+ mode: "edit",
3495
+ ...state,
3496
+ pickSource,
3497
+ removeSource: () => setState((current) => ({
3498
+ ...current,
3499
+ source: null
3500
+ })),
3501
+ pickRefs,
3502
+ removeRef: (index) => setState((current) => removeRefFromState(current, index)),
3503
+ request: (prompt) => {
3504
+ const current = store.get();
3505
+ return {
3506
+ mode: "edit",
3507
+ prompt,
3508
+ source: current.source,
3509
+ refs: current.refs
3510
+ };
3511
+ },
3512
+ clear: () => setState({
3513
+ source: null,
3514
+ refs: [],
3515
+ isPicking: false,
3516
+ error: null
3517
+ })
3518
+ };
3519
+ }
3520
+ function useOutpaintComposer(options) {
3521
+ const store = useComposerAtom({
3522
+ source: null,
3523
+ rect: null,
3524
+ isPicking: false,
3525
+ error: null
3526
+ });
3527
+ const [state, setState] = useAtom(store);
3528
+ async function pickSource() {
3529
+ setState((current) => ({
3530
+ ...current,
3531
+ isPicking: true,
3532
+ error: null
3533
+ }));
3534
+ try {
3535
+ const source = await pickOneImage(options.inputRoot);
3536
+ setState((current) => ({
3537
+ ...current,
3538
+ source: source ?? current.source,
3539
+ rect: source ? initialOutpaintRect(source.size) : current.rect,
3540
+ isPicking: false
3541
+ }));
3542
+ } catch (error) {
3543
+ setState((current) => ({
3544
+ ...current,
3545
+ error: errorFromUnknown(error),
3546
+ isPicking: false
3547
+ }));
3548
+ }
3549
+ }
3550
+ return {
3551
+ mode: "outpaint",
3552
+ ...state,
3553
+ pickSource,
3554
+ removeSource: () => setState((current) => ({
3555
+ ...current,
3556
+ source: null,
3557
+ rect: null
3558
+ })),
3559
+ setRect: (rect) => setState((current) => ({
3560
+ ...current,
3561
+ rect
3562
+ })),
3563
+ request: (prompt) => {
3564
+ const current = store.get();
3565
+ return {
3566
+ mode: "outpaint",
3567
+ prompt,
3568
+ source: current.source,
3569
+ rect: current.rect
3570
+ };
3571
+ },
3572
+ clear: () => setState({
3573
+ source: null,
3574
+ rect: null,
3575
+ isPicking: false,
3576
+ error: null
3577
+ })
3578
+ };
3579
+ }
3580
+ function useImageGeneration() {
3581
+ const [state, setState] = useAtom(useComposerAtom({
3582
+ imageUri: null,
3583
+ result: null,
3584
+ progress: 0,
3585
+ status: "",
3586
+ isGenerating: false,
3587
+ error: null
3588
+ }));
3589
+ const abortControllerRef = useStableRef(null);
3590
+ async function run(input, options = {}) {
3591
+ if (abortControllerRef.current) return null;
3592
+ const abortController = new AbortController();
3593
+ abortControllerRef.current = abortController;
3594
+ setState((current) => ({
3595
+ ...current,
3596
+ imageUri: null,
3597
+ result: null,
3598
+ progress: 0,
3599
+ status: "Generating...",
3600
+ isGenerating: true,
3601
+ error: null
3602
+ }));
3603
+ try {
3604
+ const result = await generateImage(buildImageGenerationRequest(input, {
3605
+ ...options,
3606
+ signal: abortController.signal,
3607
+ onModelProgress(progress) {
3608
+ setState((current) => ({
3609
+ ...current,
3610
+ progress: progress.modelFraction >= 1 ? .15 : progress.modelFraction * .15,
3611
+ status: progress.modelFraction >= 1 ? "Generating..." : modelProgressStatus(progress)
3612
+ }));
3613
+ },
3614
+ onProgress(progress) {
3615
+ setState((current) => ({
3616
+ ...current,
3617
+ progress: .15 + progress.currentStep / progress.totalSteps * .85,
3618
+ status: `Step ${progress.currentStep}/${progress.totalSteps}`
3619
+ }));
3620
+ }
3621
+ }));
3622
+ setState((current) => ({
3623
+ ...current,
3624
+ imageUri: result.uri,
3625
+ result,
3626
+ progress: 1,
3627
+ status: `${result.metadata.width}×${result.metadata.height} • ${result.metadata.steps} steps`,
3628
+ isGenerating: false
3629
+ }));
3630
+ return result;
3631
+ } catch (error) {
3632
+ setState((current) => ({
3633
+ ...current,
3634
+ status: error instanceof Error ? error.message : String(error),
3635
+ error: errorFromUnknown(error),
3636
+ isGenerating: false
3637
+ }));
3638
+ return null;
3639
+ } finally {
3640
+ if (abortControllerRef.current === abortController) abortControllerRef.current = null;
3641
+ }
3642
+ }
3643
+ function cancel() {
3644
+ setState((current) => ({
3645
+ ...current,
3646
+ status: "Canceling..."
3647
+ }));
3648
+ abortControllerRef.current?.abort();
3649
+ }
3650
+ function clear() {
3651
+ setState({
3652
+ imageUri: null,
3653
+ result: null,
3654
+ progress: 0,
3655
+ status: "",
3656
+ isGenerating: false,
3657
+ error: null
3658
+ });
3659
+ }
3660
+ return {
3661
+ ...state,
3662
+ run,
3663
+ cancel,
3664
+ clear
3665
+ };
3666
+ }
3667
+ function useImageModel() {
3668
+ const [state, setState] = useAtom(useComposerAtom({
3669
+ checking: false,
3670
+ state: null,
3671
+ error: null
3672
+ }));
3673
+ async function check() {
3674
+ setState((current) => ({
3675
+ ...current,
3676
+ checking: true,
3677
+ error: null
3678
+ }));
3679
+ try {
3680
+ const [modelState] = await listImageModels();
3681
+ setState({
3682
+ checking: false,
3683
+ state: modelState ?? null,
3684
+ error: null
3685
+ });
3686
+ return modelState ?? null;
3687
+ } catch (error) {
3688
+ setState((current) => ({
3689
+ ...current,
3690
+ checking: false,
3691
+ error: errorFromUnknown(error)
3692
+ }));
3693
+ return null;
3694
+ }
3695
+ }
3696
+ return {
3697
+ ...state,
3698
+ installed: state.state?.installed ?? false,
3699
+ verified: state.state?.verified ?? false,
3700
+ missingFiles: state.state?.missingFiles ?? [],
3701
+ bytesPresent: state.state?.bytesPresent ?? 0,
3702
+ totalBytes: state.state?.totalBytes ?? 0,
3703
+ check
3704
+ };
3705
+ }
3706
+ function useComposerAtom(initialValue) {
3707
+ const ref = useStableRef(null);
3708
+ ref.current ??= atom(initialValue);
3709
+ return ref.current;
3710
+ }
3711
+ function useStableRef(initialValue) {
3712
+ return useRef(initialValue);
3713
+ }
3714
+ function removeRefFromState(state, index) {
3715
+ return {
3716
+ ...state,
3717
+ refs: state.refs.filter((_, i) => i !== index)
3718
+ };
3719
+ }
3720
+ function inputDestination(inputRoot) {
3721
+ return files.directory(inputRoot ?? files.paths.shared, "ImageGenerationInputs", String(Date.now()));
3722
+ }
3723
+ async function pickOneImage(inputRoot) {
3724
+ const [picked] = await files.pick({
3725
+ type: "image",
3726
+ destination: inputDestination(inputRoot)
3727
+ });
3728
+ if (!picked) return null;
3729
+ return Object.assign(picked, { size: await getImageSize(picked.uri) });
3730
+ }
3731
+ async function pickImages(inputRoot) {
3732
+ const picked = await files.pick({
3733
+ type: "image",
3734
+ multiple: true,
3735
+ destination: inputDestination(inputRoot)
3736
+ });
3737
+ return Promise.all(picked.map(async (image) => Object.assign(image, { size: await getImageSize(image.uri) })));
3738
+ }
3739
+ function getImageSize(uri) {
3740
+ return new Promise((resolve, reject) => {
3741
+ Image.getSize(uri, (width, height) => resolve({
3742
+ width,
3743
+ height
3744
+ }), reject);
3745
+ });
3746
+ }
3747
+ function errorFromUnknown(error) {
3748
+ return error instanceof Error ? error : new Error(String(error));
3749
+ }
3750
+ function modelProgressStatus(progress) {
3751
+ return `Model ${Math.round(progress.modelFraction * 100)}% • ${formatBytes(progress.modelBytesWritten)} / ${formatBytes(progress.totalModelBytes)}`;
3752
+ }
3753
+ function formatBytes(value) {
3754
+ if (value < 1024) return `${value} B`;
3755
+ const units = [
3756
+ "KB",
3757
+ "MB",
3758
+ "GB",
3759
+ "TB"
3760
+ ];
3761
+ let size = value / 1024;
3762
+ let index = 0;
3763
+ while (size >= 1024 && index < units.length - 1) {
3764
+ size /= 1024;
3765
+ index += 1;
3766
+ }
3767
+ return `${size.toFixed(size >= 10 ? 1 : 2)} ${units[index]}`;
3768
+ }
3769
+
3770
+ //#endregion
3771
+ //#region src/ai/shape.ts
12
3772
  var ShapeError = class extends Error {
13
3773
  constructor(code, message, issues = []) {
14
3774
  super(message);
@@ -17,9 +3777,6 @@ var ShapeError = class extends Error {
17
3777
  this.name = "ShapeError";
18
3778
  }
19
3779
  };
20
- function isRecord(value) {
21
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
22
- }
23
3780
  function issue(message, path = []) {
24
3781
  return {
25
3782
  message,
@@ -113,6 +3870,7 @@ function shape(definition) {
113
3870
  function schemaToBridgeSchema(schema) {
114
3871
  const schemaWithBridge = schema;
115
3872
  if (typeof schemaWithBridge.toJSONSchema === "function") return schemaWithBridge.toJSONSchema();
3873
+ throw new AIError("invalid_schema", "Structured output schemas must implement toJSONSchema()");
116
3874
  }
117
3875
  function validateStructuredInput(input) {
118
3876
  const result = input.shape["~standard"].validate(input.value);
@@ -124,6 +3882,9 @@ function stableStringify(value) {
124
3882
  if (isRecord(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
125
3883
  return JSON.stringify(value) ?? "undefined";
126
3884
  }
3885
+
3886
+ //#endregion
3887
+ //#region src/ai/text.ts
127
3888
  function promptFromInput(options) {
128
3889
  const parts = [];
129
3890
  if (options.prompt) parts.push(options.prompt);
@@ -132,21 +3893,6 @@ function promptFromInput(options) {
132
3893
  if (parts.length === 0) throw new AIError("invalid_message", "Expected prompt or input");
133
3894
  return parts.join("\n\n");
134
3895
  }
135
- function bridge() {
136
- const nativeBridge = NativeModules.BleamAIBridge;
137
- if (!nativeBridge) throw new AIError("unsupported_runtime", "bleam/ai requires the Bleam native runtime");
138
- return nativeBridge;
139
- }
140
- function mapNativeError(error) {
141
- const code = isRecord(error) && typeof error.code === "string" ? error.code : "";
142
- const message = error instanceof Error ? error.message : String(error);
143
- if (code === "UNSUPPORTED_OS") return new AIError("unsupported_os", message, error);
144
- if (code === "MODEL_UNAVAILABLE") return new AIError("model_unavailable", message, error);
145
- if (code === "INVALID_SCHEMA") return new AIError("invalid_schema", message, error);
146
- if (code === "INVALID_MESSAGE") return new AIError("invalid_message", message, error);
147
- if (code === "CONTEXT_WINDOW_EXCEEDED") return new AIError("context_window_exceeded", message, error);
148
- return new AIError("generation_failed", message, error);
149
- }
150
3896
  async function isAvailable() {
151
3897
  try {
152
3898
  return await bridge().isAvailable?.() ?? false;
@@ -154,17 +3900,82 @@ async function isAvailable() {
154
3900
  return false;
155
3901
  }
156
3902
  }
157
- async function generate(options) {
158
- const output = options.output;
159
- const request = {
3903
+ async function availability() {
3904
+ try {
3905
+ return await bridge().availability?.() ?? {
3906
+ available: false,
3907
+ reason: "unknown"
3908
+ };
3909
+ } catch {
3910
+ return {
3911
+ available: false,
3912
+ reason: "unknown"
3913
+ };
3914
+ }
3915
+ }
3916
+ function generationId() {
3917
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
3918
+ }
3919
+ function nativeRequest(options, id) {
3920
+ if (options.temperature !== void 0 && (!Number.isFinite(options.temperature) || options.temperature < 0)) throw new AIError("invalid_message", "temperature must be a non-negative number");
3921
+ if (options.maxTokens !== void 0 && (!Number.isInteger(options.maxTokens) || options.maxTokens <= 0)) throw new AIError("invalid_message", "maxTokens must be a positive integer");
3922
+ const names = /* @__PURE__ */ new Set();
3923
+ for (const tool of options.tools ?? []) {
3924
+ if (!tool.name.trim()) throw new AIError("invalid_schema", "Tool names must not be empty");
3925
+ if (names.has(tool.name)) throw new AIError("invalid_schema", `Duplicate tool name: ${tool.name}`);
3926
+ names.add(tool.name);
3927
+ }
3928
+ return {
3929
+ id,
160
3930
  system: options.system,
161
3931
  prompt: promptFromInput(options),
162
3932
  temperature: options.temperature,
163
3933
  maxTokens: options.maxTokens,
164
- outputSchema: output ? schemaToBridgeSchema(output) : void 0
3934
+ outputSchema: options.output ? schemaToBridgeSchema(options.output) : void 0,
3935
+ tools: options.tools?.map((tool) => ({
3936
+ name: tool.name,
3937
+ description: tool.description,
3938
+ inputSchema: schemaToBridgeSchema(tool.input)
3939
+ }))
165
3940
  };
3941
+ }
3942
+ function listenForTools(id, tools) {
3943
+ if (!tools?.length) return void 0;
3944
+ const native = bridge();
3945
+ return native.addListener?.("toolCall", (event) => {
3946
+ if (event.generationId !== id) return;
3947
+ const tool = tools.find((candidate) => candidate.name === event.name);
3948
+ (async () => {
3949
+ try {
3950
+ if (!tool) throw new Error(`Unknown tool: ${event.name}`);
3951
+ const validation = tool.input["~standard"].validate(event.arguments);
3952
+ if (validation.issues !== void 0) throw new Error(validation.issues.map((issue$1) => issue$1.message).join("\n"));
3953
+ const output = await tool.execute(validation.value);
3954
+ await native.resolveToolCall?.({
3955
+ generationId: id,
3956
+ callId: event.callId,
3957
+ output: typeof output === "string" ? output : stableStringify(output)
3958
+ });
3959
+ } catch (error) {
3960
+ await native.resolveToolCall?.({
3961
+ generationId: id,
3962
+ callId: event.callId,
3963
+ error: error instanceof Error ? error.message : String(error)
3964
+ });
3965
+ }
3966
+ })();
3967
+ });
3968
+ }
3969
+ async function generate(options) {
3970
+ const output = options.output;
3971
+ const id = generationId();
3972
+ const native = bridge();
3973
+ const toolSubscription = listenForTools(id, options.tools);
3974
+ const abort = () => void native.cancelTextGeneration?.(id);
3975
+ options.signal?.addEventListener("abort", abort, { once: true });
166
3976
  try {
167
- const value = await bridge().generate?.(request);
3977
+ if (options.signal?.aborted) throw new AIError("generation_canceled", "Generation canceled");
3978
+ const value = await native.generate?.(nativeRequest(options, id));
168
3979
  if (output) {
169
3980
  const result = output["~standard"].validate(value);
170
3981
  if (result.issues !== void 0) throw new AIError("validation_failed", result.issues.map((nextIssue) => nextIssue.message).join("\n"), result.issues);
@@ -175,8 +3986,117 @@ async function generate(options) {
175
3986
  } catch (error) {
176
3987
  if (error instanceof AIError) throw error;
177
3988
  throw mapNativeError(error);
3989
+ } finally {
3990
+ options.signal?.removeEventListener("abort", abort);
3991
+ toolSubscription?.remove();
3992
+ }
3993
+ }
3994
+ async function* stream(options) {
3995
+ const id = generationId();
3996
+ const native = bridge();
3997
+ const updates = [];
3998
+ let wake;
3999
+ let settled = false;
4000
+ let failure;
4001
+ const updateSubscription = native.addListener?.("textGenerationUpdate", (event) => {
4002
+ if (event.id !== id) return;
4003
+ updates.push(event.content);
4004
+ wake?.();
4005
+ wake = void 0;
4006
+ });
4007
+ const toolSubscription = listenForTools(id, options.tools);
4008
+ const abort = () => void native.cancelTextGeneration?.(id);
4009
+ options.signal?.addEventListener("abort", abort, { once: true });
4010
+ const completion = native.streamGenerate?.(nativeRequest(options, id)).catch((error) => {
4011
+ failure = error;
4012
+ }).finally(() => {
4013
+ settled = true;
4014
+ wake?.();
4015
+ wake = void 0;
4016
+ });
4017
+ try {
4018
+ if (!completion) throw new AIError("unsupported_runtime", "Streaming is unavailable in this runtime");
4019
+ while (!settled || updates.length > 0) if (updates.length > 0) yield updates.shift();
4020
+ else await new Promise((resolve) => {
4021
+ wake = resolve;
4022
+ });
4023
+ await completion;
4024
+ if (failure) throw mapNativeError(failure);
4025
+ } finally {
4026
+ if (!settled) await native.cancelTextGeneration?.(id);
4027
+ options.signal?.removeEventListener("abort", abort);
4028
+ updateSubscription?.remove();
4029
+ toolSubscription?.remove();
4030
+ }
4031
+ }
4032
+
4033
+ //#endregion
4034
+ //#region src/ai/text-hook.ts
4035
+ const initialState = {
4036
+ text: "",
4037
+ isGenerating: false,
4038
+ error: null
4039
+ };
4040
+ function useTextGeneration() {
4041
+ const [state, setState] = useState(initialState);
4042
+ const abortControllerRef = useRef(null);
4043
+ async function run(options) {
4044
+ if (abortControllerRef.current) return null;
4045
+ const abortController = new AbortController();
4046
+ const abort = () => abortController.abort();
4047
+ abortControllerRef.current = abortController;
4048
+ options.signal?.addEventListener("abort", abort, { once: true });
4049
+ if (options.signal?.aborted) abortController.abort();
4050
+ setState({
4051
+ text: "",
4052
+ isGenerating: true,
4053
+ error: null
4054
+ });
4055
+ let text = "";
4056
+ try {
4057
+ for await (const snapshot of stream({
4058
+ ...options,
4059
+ signal: abortController.signal
4060
+ })) {
4061
+ text = snapshot;
4062
+ setState({
4063
+ text,
4064
+ isGenerating: true,
4065
+ error: null
4066
+ });
4067
+ }
4068
+ setState({
4069
+ text,
4070
+ isGenerating: false,
4071
+ error: null
4072
+ });
4073
+ return text;
4074
+ } catch (error) {
4075
+ setState({
4076
+ text,
4077
+ isGenerating: false,
4078
+ error: error instanceof Error ? error : new Error(String(error))
4079
+ });
4080
+ return null;
4081
+ } finally {
4082
+ options.signal?.removeEventListener("abort", abort);
4083
+ if (abortControllerRef.current === abortController) abortControllerRef.current = null;
4084
+ }
178
4085
  }
4086
+ function cancel() {
4087
+ abortControllerRef.current?.abort();
4088
+ }
4089
+ function clear() {
4090
+ if (abortControllerRef.current) return;
4091
+ setState(initialState);
4092
+ }
4093
+ return {
4094
+ ...state,
4095
+ run,
4096
+ cancel,
4097
+ clear
4098
+ };
179
4099
  }
180
4100
 
181
4101
  //#endregion
182
- export { AIError, generate, isAvailable, shape };
4102
+ export { AIError, ImageGenerationError, availability, buildImageGenerationRequest, clampOutpaintRect, defaultImageLongSide, deleteImageLoRA, deleteImageModel, flux2Klein4B8BitAbliterated, fluxImageModelPaths, generate, generateImage, generateImages, getImageGenerationJob, imageMemoryTelemetry, imageModelManifest, imageModelManifests, imageSizeForAspectRatio, imageSizeForPreset, initialOutpaintRect, isAvailable, listImageGenerationJobs, listImageLoRAs, listImageModels, normalizeImageSize, prepareImageLoRA, prepareImageModel, rectForSourceScale, roundImageDimension, roundedImageSize, scheduleImageGeneration, shape, stream, unloadImageModel, upscaleImage, useImageComposer, useImageGeneration, useImageModel, useTextGeneration, verifyImageLoRA, verifyImageModel };