bleam 0.0.6 → 0.0.8

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