@ai-sdk/quiverai 2.0.43 → 2.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/index.d.ts +25 -2
- package/dist/index.js +698 -14
- package/dist/index.js.map +1 -1
- package/docs/180-quiverai.mdx +125 -13
- package/package.json +3 -2
- package/src/index.ts +5 -0
- package/src/prepare-quiverai-image-reference.ts +161 -0
- package/src/quiverai-image-model-options.ts +41 -1
- package/src/quiverai-image-model.ts +684 -3
- package/src/quiverai-image-settings.ts +3 -1
package/dist/index.js
CHANGED
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
|
|
12
12
|
// src/quiverai-image-model.ts
|
|
13
13
|
import {
|
|
14
|
-
InvalidArgumentError
|
|
14
|
+
InvalidArgumentError as InvalidArgumentError2
|
|
15
15
|
} from "@ai-sdk/provider";
|
|
16
16
|
import {
|
|
17
17
|
combineHeaders,
|
|
18
|
-
|
|
18
|
+
convertBase64ToUint8Array as convertBase64ToUint8Array2,
|
|
19
|
+
convertUint8ArrayToBase64 as convertUint8ArrayToBase642,
|
|
19
20
|
createJsonErrorResponseHandler,
|
|
20
21
|
createJsonResponseHandler,
|
|
21
22
|
parseProviderOptions,
|
|
@@ -41,8 +42,12 @@ var quiveraiImageModelOptionsSchema = lazySchema(
|
|
|
41
42
|
* - `generate`: Text-to-SVG generation. Requires `prompt`.
|
|
42
43
|
* - `vectorize`: Convert an input raster image into an SVG. Requires a
|
|
43
44
|
* single image in `prompt.images` / `files`.
|
|
45
|
+
* - `animate`: Animate an input SVG. Requires a single SVG in
|
|
46
|
+
* `prompt.images` / `files`; the text prompt is optional.
|
|
47
|
+
* - `edit`: Edit a single SVG from `prompt.images` using `prompt.text`
|
|
48
|
+
* as the required instruction.
|
|
44
49
|
*/
|
|
45
|
-
operation: z.enum(["generate", "vectorize"]).optional(),
|
|
50
|
+
operation: z.enum(["generate", "vectorize", "animate", "edit"]).optional(),
|
|
46
51
|
/**
|
|
47
52
|
* Extra style guidance for prompt-based generation.
|
|
48
53
|
*/
|
|
@@ -51,6 +56,20 @@ var quiveraiImageModelOptionsSchema = lazySchema(
|
|
|
51
56
|
* Reasoning effort applied to generation or vectorization.
|
|
52
57
|
*/
|
|
53
58
|
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Optional reference images for SVG editing. Use
|
|
61
|
+
* `prepareQuiverAIImageReference` to convert binary inputs.
|
|
62
|
+
*/
|
|
63
|
+
referenceImages: z.array(
|
|
64
|
+
z.union([
|
|
65
|
+
z.object({ url: z.string().min(1) }).strict(),
|
|
66
|
+
z.object({ base64: z.string().min(1).max(16777216) }).strict()
|
|
67
|
+
])
|
|
68
|
+
).max(4).optional(),
|
|
69
|
+
/**
|
|
70
|
+
* Maximum number of edit review and redo steps (0-5).
|
|
71
|
+
*/
|
|
72
|
+
maxReviewSteps: z.number().int().min(0).max(5).optional(),
|
|
54
73
|
/**
|
|
55
74
|
* SVG root attributes requested for generation or vectorization.
|
|
56
75
|
*/
|
|
@@ -79,6 +98,14 @@ var quiveraiImageModelOptionsSchema = lazySchema(
|
|
|
79
98
|
* The legacy upper bound of 131072 is retained for other model IDs.
|
|
80
99
|
*/
|
|
81
100
|
maxOutputTokens: z.number().int().min(1).max(131072).optional(),
|
|
101
|
+
/**
|
|
102
|
+
* Provider orchestrator token budget for SVG editing (1-65536).
|
|
103
|
+
*/
|
|
104
|
+
orchestratorMaxOutputTokens: z.number().int().min(1).max(65536).optional(),
|
|
105
|
+
/**
|
|
106
|
+
* Provider shallow edit token budget for SVG editing (1-65536).
|
|
107
|
+
*/
|
|
108
|
+
shallowMaxOutputTokens: z.number().int().min(1).max(65536).optional(),
|
|
82
109
|
/**
|
|
83
110
|
* Whether to auto-crop the input image before vectorization.
|
|
84
111
|
* Only used when `operation` is `vectorize`.
|
|
@@ -93,6 +120,113 @@ var quiveraiImageModelOptionsSchema = lazySchema(
|
|
|
93
120
|
)
|
|
94
121
|
);
|
|
95
122
|
|
|
123
|
+
// src/prepare-quiverai-image-reference.ts
|
|
124
|
+
import { InvalidArgumentError } from "@ai-sdk/provider";
|
|
125
|
+
import {
|
|
126
|
+
convertBase64ToUint8Array,
|
|
127
|
+
convertUint8ArrayToBase64,
|
|
128
|
+
detectMediaType
|
|
129
|
+
} from "@ai-sdk/provider-utils";
|
|
130
|
+
var MAX_REFERENCE_BASE64_LENGTH = 16777216;
|
|
131
|
+
var MAX_REFERENCE_BYTES = 12582912;
|
|
132
|
+
var supportedReferenceMediaTypes = /* @__PURE__ */ new Set([
|
|
133
|
+
"image/gif",
|
|
134
|
+
"image/jpeg",
|
|
135
|
+
"image/png",
|
|
136
|
+
"image/svg+xml",
|
|
137
|
+
"image/webp"
|
|
138
|
+
]);
|
|
139
|
+
function prepareQuiverAIImageReference(input) {
|
|
140
|
+
if (input instanceof URL) {
|
|
141
|
+
return { url: validateQuiverAIImageUrl(input.toString()) };
|
|
142
|
+
}
|
|
143
|
+
if (typeof input === "string") {
|
|
144
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(input)) {
|
|
145
|
+
return { url: validateQuiverAIImageUrl(input) };
|
|
146
|
+
}
|
|
147
|
+
if (input.startsWith("data:")) {
|
|
148
|
+
const match = /^data:([^;,]+);base64,(.+)$/s.exec(input);
|
|
149
|
+
if (match == null || !supportedReferenceMediaTypes.has(match[1])) {
|
|
150
|
+
throw new InvalidArgumentError({
|
|
151
|
+
argument: "input",
|
|
152
|
+
message: "QuiverAI reference image data URLs must use base64 encoding and a supported image media type."
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
validateQuiverAIReferenceBase64(match[2], "input");
|
|
156
|
+
return { base64: match[2] };
|
|
157
|
+
}
|
|
158
|
+
validateQuiverAIReferenceBase64(input, "input");
|
|
159
|
+
return { base64: input };
|
|
160
|
+
}
|
|
161
|
+
const data = input instanceof ArrayBuffer ? new Uint8Array(input) : new Uint8Array(input);
|
|
162
|
+
validateQuiverAIReferenceBytes(data, "input");
|
|
163
|
+
return { base64: convertUint8ArrayToBase64(data) };
|
|
164
|
+
}
|
|
165
|
+
function validateQuiverAIImageUrl(url) {
|
|
166
|
+
let parsed;
|
|
167
|
+
try {
|
|
168
|
+
parsed = new URL(url);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
throw new InvalidArgumentError({
|
|
171
|
+
argument: "url",
|
|
172
|
+
message: "QuiverAI image URLs must be valid HTTP or HTTPS URLs."
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
176
|
+
throw new InvalidArgumentError({
|
|
177
|
+
argument: "url",
|
|
178
|
+
message: "QuiverAI image URLs must use HTTP or HTTPS."
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return parsed.toString();
|
|
182
|
+
}
|
|
183
|
+
function validateQuiverAIReferenceBase64(base64, argument) {
|
|
184
|
+
if (base64.length === 0 || base64.length > MAX_REFERENCE_BASE64_LENGTH) {
|
|
185
|
+
throw new InvalidArgumentError({
|
|
186
|
+
argument,
|
|
187
|
+
message: `QuiverAI reference images must contain 1-${MAX_REFERENCE_BASE64_LENGTH} base64 characters.`
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
let data;
|
|
191
|
+
try {
|
|
192
|
+
data = convertBase64ToUint8Array(base64);
|
|
193
|
+
} catch (cause) {
|
|
194
|
+
throw new InvalidArgumentError({
|
|
195
|
+
argument,
|
|
196
|
+
message: "QuiverAI reference image data must be valid base64.",
|
|
197
|
+
cause
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
validateQuiverAIReferenceBytes(data, argument);
|
|
201
|
+
}
|
|
202
|
+
function validateQuiverAIReferenceBytes(data, argument) {
|
|
203
|
+
if (data.length === 0 || data.length > MAX_REFERENCE_BYTES) {
|
|
204
|
+
throw new InvalidArgumentError({
|
|
205
|
+
argument,
|
|
206
|
+
message: `QuiverAI reference images must decode to 1-${MAX_REFERENCE_BYTES} bytes.`
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const mediaType = isSvg(data) ? "image/svg+xml" : detectMediaType({ data, topLevelType: "image" });
|
|
210
|
+
if (mediaType == null || !supportedReferenceMediaTypes.has(mediaType)) {
|
|
211
|
+
throw new InvalidArgumentError({
|
|
212
|
+
argument,
|
|
213
|
+
message: "QuiverAI reference images must be PNG, JPEG, WebP, GIF, or SVG data."
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function isSvg(data) {
|
|
218
|
+
let text;
|
|
219
|
+
try {
|
|
220
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
221
|
+
} catch (e) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
const normalized = text.replace(/^\uFEFF/, "").trim();
|
|
225
|
+
return /^(?:<\?xml[\s\S]*?\?>\s*)?(?:<!--[\s\S]*?-->\s*)?(?:<!DOCTYPE[\s\S]*?>\s*)?<svg[\s>]/i.test(
|
|
226
|
+
normalized
|
|
227
|
+
) && (/<\/svg>\s*$/i.test(normalized) || /<svg(?:\s[^>]*)?\/>\s*$/is.test(normalized));
|
|
228
|
+
}
|
|
229
|
+
|
|
96
230
|
// src/quiverai-image-model.ts
|
|
97
231
|
var QuiverAIImageModel = class _QuiverAIImageModel {
|
|
98
232
|
constructor(modelId, config) {
|
|
@@ -138,6 +272,7 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
|
|
|
138
272
|
n,
|
|
139
273
|
prompt,
|
|
140
274
|
files,
|
|
275
|
+
mask,
|
|
141
276
|
operation,
|
|
142
277
|
options: quiveraiOptions != null ? quiveraiOptions : {}
|
|
143
278
|
});
|
|
@@ -164,7 +299,13 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
|
|
|
164
299
|
...response.credits != null && { credits: response.credits },
|
|
165
300
|
images: response.data.map((image, index) => ({
|
|
166
301
|
index,
|
|
167
|
-
mimeType: image.mime_type
|
|
302
|
+
mimeType: image.mime_type,
|
|
303
|
+
...image.loop_period_ms !== void 0 && {
|
|
304
|
+
loopPeriodMs: image.loop_period_ms
|
|
305
|
+
},
|
|
306
|
+
...image.opening_animation_ms !== void 0 && {
|
|
307
|
+
openingAnimationMs: image.opening_animation_ms
|
|
308
|
+
}
|
|
168
309
|
}))
|
|
169
310
|
}
|
|
170
311
|
},
|
|
@@ -184,7 +325,16 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
|
|
|
184
325
|
}
|
|
185
326
|
};
|
|
186
327
|
function getOperationPath(operation) {
|
|
187
|
-
|
|
328
|
+
switch (operation) {
|
|
329
|
+
case "generate":
|
|
330
|
+
return "/svgs/generations";
|
|
331
|
+
case "vectorize":
|
|
332
|
+
return "/svgs/vectorizations";
|
|
333
|
+
case "edit":
|
|
334
|
+
return "/svgs/edits";
|
|
335
|
+
case "animate":
|
|
336
|
+
return "/svgs/animations";
|
|
337
|
+
}
|
|
188
338
|
}
|
|
189
339
|
function getGenerateReferenceLimit(modelId) {
|
|
190
340
|
return ["arrow-1", "arrow-1.0", "arrow-1.1"].includes(modelId) ? 4 : 16;
|
|
@@ -194,19 +344,79 @@ function toQuiverAIImageReference(image) {
|
|
|
194
344
|
return { url: image.url };
|
|
195
345
|
}
|
|
196
346
|
return {
|
|
197
|
-
base64: typeof image.data === "string" ? image.data :
|
|
347
|
+
base64: typeof image.data === "string" ? image.data : convertUint8ArrayToBase642(image.data)
|
|
198
348
|
};
|
|
199
349
|
}
|
|
350
|
+
var maxAnimationSourceBase64Length = 1066668;
|
|
351
|
+
function toQuiverAIAnimationSource(image) {
|
|
352
|
+
var _a;
|
|
353
|
+
if (image.type === "url") {
|
|
354
|
+
let url;
|
|
355
|
+
try {
|
|
356
|
+
url = new URL(image.url);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
throw new InvalidArgumentError2({
|
|
359
|
+
argument: "files",
|
|
360
|
+
message: "QuiverAI animate requires a valid HTTP or HTTPS SVG URL."
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
364
|
+
throw new InvalidArgumentError2({
|
|
365
|
+
argument: "files",
|
|
366
|
+
message: "QuiverAI animate requires an HTTP or HTTPS SVG URL."
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return { url: image.url };
|
|
370
|
+
}
|
|
371
|
+
let base64;
|
|
372
|
+
let bytes;
|
|
373
|
+
if (typeof image.data === "string") {
|
|
374
|
+
const dataUrlMatch = /^data:image\/svg\+xml(?:;[^,]*)?;base64,([\s\S]+)$/i.exec(image.data);
|
|
375
|
+
const encodedData = (_a = dataUrlMatch == null ? void 0 : dataUrlMatch[1]) != null ? _a : image.data;
|
|
376
|
+
try {
|
|
377
|
+
bytes = convertBase64ToUint8Array2(encodedData);
|
|
378
|
+
} catch (e) {
|
|
379
|
+
throw new InvalidArgumentError2({
|
|
380
|
+
argument: "files",
|
|
381
|
+
message: "QuiverAI animate requires the source SVG string to be valid base64 or an SVG data URL."
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
base64 = convertUint8ArrayToBase642(bytes);
|
|
385
|
+
} else {
|
|
386
|
+
bytes = image.data;
|
|
387
|
+
base64 = convertUint8ArrayToBase642(bytes);
|
|
388
|
+
}
|
|
389
|
+
if (!isSvg2(bytes)) {
|
|
390
|
+
throw new InvalidArgumentError2({
|
|
391
|
+
argument: "files",
|
|
392
|
+
message: "QuiverAI animate requires the input file to contain SVG data."
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
if (base64.length > maxAnimationSourceBase64Length) {
|
|
396
|
+
throw new InvalidArgumentError2({
|
|
397
|
+
argument: "files",
|
|
398
|
+
message: `QuiverAI animate accepts at most ${maxAnimationSourceBase64Length} base64 characters for the source SVG.`
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return { base64 };
|
|
402
|
+
}
|
|
403
|
+
function isSvg2(data) {
|
|
404
|
+
const head = new TextDecoder("utf-8", { fatal: false }).decode(data.subarray(0, 4096)).trimStart();
|
|
405
|
+
return /^(?:(?:<\?xml[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE[\s\S]*?>)\s*)*<svg(?:\s|>)/i.test(
|
|
406
|
+
head
|
|
407
|
+
);
|
|
408
|
+
}
|
|
200
409
|
function buildRequestBody({
|
|
201
410
|
modelId,
|
|
202
411
|
n,
|
|
203
412
|
prompt,
|
|
204
413
|
files,
|
|
414
|
+
mask,
|
|
205
415
|
operation,
|
|
206
416
|
options
|
|
207
417
|
}) {
|
|
208
418
|
if ((modelId === "arrow-2" || modelId === "arrow-2-telos") && options.maxOutputTokens != null && options.maxOutputTokens > 65536) {
|
|
209
|
-
throw new
|
|
419
|
+
throw new InvalidArgumentError2({
|
|
210
420
|
argument: "maxOutputTokens",
|
|
211
421
|
message: `QuiverAI model "${modelId}" supports at most 65536 output tokens.`
|
|
212
422
|
});
|
|
@@ -220,9 +430,12 @@ function buildRequestBody({
|
|
|
220
430
|
attributes: options.attributes,
|
|
221
431
|
stream: false
|
|
222
432
|
};
|
|
433
|
+
if (operation !== "edit") {
|
|
434
|
+
rejectEditOnlyOptions(operation, options);
|
|
435
|
+
}
|
|
223
436
|
if (operation === "generate") {
|
|
224
437
|
if (prompt == null || prompt.trim().length === 0) {
|
|
225
|
-
throw new
|
|
438
|
+
throw new InvalidArgumentError2({
|
|
226
439
|
argument: "prompt",
|
|
227
440
|
message: "QuiverAI image generation requires a non-empty prompt for generateImage."
|
|
228
441
|
});
|
|
@@ -230,7 +443,7 @@ function buildRequestBody({
|
|
|
230
443
|
const references = files == null ? void 0 : files.map(toQuiverAIImageReference);
|
|
231
444
|
const maxReferences = getGenerateReferenceLimit(modelId);
|
|
232
445
|
if (references != null && references.length > maxReferences) {
|
|
233
|
-
throw new
|
|
446
|
+
throw new InvalidArgumentError2({
|
|
234
447
|
argument: "files",
|
|
235
448
|
message: `QuiverAI generate supports up to ${maxReferences} reference images for model "${modelId}".`
|
|
236
449
|
});
|
|
@@ -244,20 +457,40 @@ function buildRequestBody({
|
|
|
244
457
|
references
|
|
245
458
|
};
|
|
246
459
|
}
|
|
460
|
+
if (operation === "edit") {
|
|
461
|
+
return buildEditRequestBody({
|
|
462
|
+
modelId,
|
|
463
|
+
n,
|
|
464
|
+
prompt,
|
|
465
|
+
files,
|
|
466
|
+
mask,
|
|
467
|
+
options
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (operation === "animate") {
|
|
471
|
+
return buildAnimationRequestBody({
|
|
472
|
+
modelId,
|
|
473
|
+
n,
|
|
474
|
+
prompt,
|
|
475
|
+
files,
|
|
476
|
+
mask,
|
|
477
|
+
options
|
|
478
|
+
});
|
|
479
|
+
}
|
|
247
480
|
if (files == null || files.length === 0) {
|
|
248
|
-
throw new
|
|
481
|
+
throw new InvalidArgumentError2({
|
|
249
482
|
argument: "files",
|
|
250
483
|
message: 'QuiverAI vectorize requires an input image. Pass an image in the generateImage prompt and set providerOptions.quiverai.operation to "vectorize".'
|
|
251
484
|
});
|
|
252
485
|
}
|
|
253
486
|
if (files.length > 1) {
|
|
254
|
-
throw new
|
|
487
|
+
throw new InvalidArgumentError2({
|
|
255
488
|
argument: "files",
|
|
256
489
|
message: "QuiverAI vectorize accepts a single input image."
|
|
257
490
|
});
|
|
258
491
|
}
|
|
259
492
|
if (n !== 1) {
|
|
260
|
-
throw new
|
|
493
|
+
throw new InvalidArgumentError2({
|
|
261
494
|
argument: "n",
|
|
262
495
|
message: "QuiverAI vectorize returns one SVG per request. Set maxImagesPerCall to 1 in generateImage to vectorize multiple times."
|
|
263
496
|
});
|
|
@@ -270,6 +503,454 @@ function buildRequestBody({
|
|
|
270
503
|
target_size: options.targetSize
|
|
271
504
|
};
|
|
272
505
|
}
|
|
506
|
+
function buildAnimationRequestBody({
|
|
507
|
+
modelId,
|
|
508
|
+
n,
|
|
509
|
+
prompt,
|
|
510
|
+
files,
|
|
511
|
+
mask,
|
|
512
|
+
options
|
|
513
|
+
}) {
|
|
514
|
+
if (modelId !== "arrow-2" && modelId !== "arrow-2-telos") {
|
|
515
|
+
throw new InvalidArgumentError2({
|
|
516
|
+
argument: "modelId",
|
|
517
|
+
message: 'QuiverAI animate is supported by the "arrow-2" and "arrow-2-telos" models.'
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
if (files == null || files.length === 0) {
|
|
521
|
+
throw new InvalidArgumentError2({
|
|
522
|
+
argument: "files",
|
|
523
|
+
message: "QuiverAI animate requires exactly one source SVG in prompt.images."
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
if (files.length !== 1) {
|
|
527
|
+
throw new InvalidArgumentError2({
|
|
528
|
+
argument: "files",
|
|
529
|
+
message: "QuiverAI animate accepts exactly one source SVG in prompt.images."
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
if (n !== 1) {
|
|
533
|
+
throw new InvalidArgumentError2({
|
|
534
|
+
argument: "n",
|
|
535
|
+
message: "QuiverAI animate returns one SVG per request. Set maxImagesPerCall to 1 in generateImage to animate multiple times."
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
if (mask != null) {
|
|
539
|
+
throw new InvalidArgumentError2({
|
|
540
|
+
argument: "mask",
|
|
541
|
+
message: "QuiverAI animate does not support masks."
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (prompt != null && prompt.trim().length === 0) {
|
|
545
|
+
throw new InvalidArgumentError2({
|
|
546
|
+
argument: "prompt",
|
|
547
|
+
message: "QuiverAI animate requires a non-empty prompt when an animation instruction is provided."
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
const unsupportedOptions = [
|
|
551
|
+
["instructions", options.instructions],
|
|
552
|
+
["topP", options.topP],
|
|
553
|
+
["presencePenalty", options.presencePenalty],
|
|
554
|
+
["attributes", options.attributes],
|
|
555
|
+
["autoCrop", options.autoCrop],
|
|
556
|
+
["targetSize", options.targetSize]
|
|
557
|
+
].filter((option) => {
|
|
558
|
+
return option[1] !== void 0;
|
|
559
|
+
});
|
|
560
|
+
if (unsupportedOptions.length > 0) {
|
|
561
|
+
throw new InvalidArgumentError2({
|
|
562
|
+
argument: `providerOptions.quiverai.${unsupportedOptions[0][0]}`,
|
|
563
|
+
message: `QuiverAI animate does not support providerOptions.quiverai.${unsupportedOptions[0][0]}.`
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
model: modelId,
|
|
568
|
+
svg_source: toQuiverAIAnimationSource(files[0]),
|
|
569
|
+
prompt,
|
|
570
|
+
temperature: options.temperature,
|
|
571
|
+
max_output_tokens: options.maxOutputTokens,
|
|
572
|
+
reasoning_effort: options.reasoningEffort,
|
|
573
|
+
stream: false
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
var editModelIds = /* @__PURE__ */ new Set(["arrow-2", "arrow-2-telos"]);
|
|
577
|
+
var MAX_EDIT_SVG_BYTES = 2e5;
|
|
578
|
+
function buildEditRequestBody({
|
|
579
|
+
modelId,
|
|
580
|
+
n,
|
|
581
|
+
prompt,
|
|
582
|
+
files,
|
|
583
|
+
mask,
|
|
584
|
+
options
|
|
585
|
+
}) {
|
|
586
|
+
var _a;
|
|
587
|
+
if (!editModelIds.has(modelId)) {
|
|
588
|
+
throw new InvalidArgumentError2({
|
|
589
|
+
argument: "modelId",
|
|
590
|
+
message: 'QuiverAI SVG editing is supported by the "arrow-2" and "arrow-2-telos" models.'
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (prompt == null || prompt.trim().length === 0) {
|
|
594
|
+
throw new InvalidArgumentError2({
|
|
595
|
+
argument: "prompt",
|
|
596
|
+
message: "QuiverAI SVG editing requires a non-empty instruction in generateImage prompt.text."
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
if (prompt.length > 4e3) {
|
|
600
|
+
throw new InvalidArgumentError2({
|
|
601
|
+
argument: "prompt",
|
|
602
|
+
message: "QuiverAI SVG editing instructions must contain at most 4000 characters."
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
if (files == null || files.length === 0) {
|
|
606
|
+
throw new InvalidArgumentError2({
|
|
607
|
+
argument: "files",
|
|
608
|
+
message: "QuiverAI SVG editing requires one source SVG in generateImage prompt.images."
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (files.length !== 1) {
|
|
612
|
+
throw new InvalidArgumentError2({
|
|
613
|
+
argument: "files",
|
|
614
|
+
message: "QuiverAI SVG editing accepts exactly one source SVG."
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (n !== 1) {
|
|
618
|
+
throw new InvalidArgumentError2({
|
|
619
|
+
argument: "n",
|
|
620
|
+
message: "QuiverAI SVG editing returns exactly one SVG per request. Set maxImagesPerCall to 1 in generateImage to edit multiple times."
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
if (mask != null) {
|
|
624
|
+
throw new InvalidArgumentError2({
|
|
625
|
+
argument: "mask",
|
|
626
|
+
message: "QuiverAI SVG editing does not support masks."
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
const unsupportedOptions = [
|
|
630
|
+
["instructions", options.instructions],
|
|
631
|
+
["attributes", options.attributes],
|
|
632
|
+
["topP", options.topP],
|
|
633
|
+
["presencePenalty", options.presencePenalty],
|
|
634
|
+
["autoCrop", options.autoCrop],
|
|
635
|
+
["targetSize", options.targetSize]
|
|
636
|
+
].flatMap(([name, value]) => value == null ? [] : [name]);
|
|
637
|
+
if (unsupportedOptions.length > 0) {
|
|
638
|
+
throw new InvalidArgumentError2({
|
|
639
|
+
argument: "providerOptions",
|
|
640
|
+
message: `QuiverAI SVG editing does not support these provider options: ${unsupportedOptions.join(
|
|
641
|
+
", "
|
|
642
|
+
)}.`
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const referenceImages = (_a = options.referenceImages) == null ? void 0 : _a.map((reference, index) => {
|
|
646
|
+
if ("url" in reference) {
|
|
647
|
+
return {
|
|
648
|
+
url: validateQuiverAIImageUrl(reference.url)
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
validateQuiverAIReferenceBase64(
|
|
652
|
+
reference.base64,
|
|
653
|
+
`providerOptions.quiverai.referenceImages[${index}]`
|
|
654
|
+
);
|
|
655
|
+
return { base64: reference.base64 };
|
|
656
|
+
});
|
|
657
|
+
const settings = {
|
|
658
|
+
max_output_tokens: options.maxOutputTokens,
|
|
659
|
+
orchestrator_max_output_tokens: options.orchestratorMaxOutputTokens,
|
|
660
|
+
shallow_max_output_tokens: options.shallowMaxOutputTokens,
|
|
661
|
+
temperature: options.temperature
|
|
662
|
+
};
|
|
663
|
+
const hasSettings = Object.values(settings).some((value) => value != null);
|
|
664
|
+
return {
|
|
665
|
+
model: modelId,
|
|
666
|
+
prompt,
|
|
667
|
+
...toQuiverAIEditSource(files[0]),
|
|
668
|
+
reference_images: referenceImages,
|
|
669
|
+
max_review_steps: options.maxReviewSteps,
|
|
670
|
+
reasoning_effort: options.reasoningEffort,
|
|
671
|
+
...hasSettings && { settings },
|
|
672
|
+
stream: false
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function rejectEditOnlyOptions(operation, options) {
|
|
676
|
+
const editOnlyOptions = [
|
|
677
|
+
["referenceImages", options.referenceImages],
|
|
678
|
+
["maxReviewSteps", options.maxReviewSteps],
|
|
679
|
+
["orchestratorMaxOutputTokens", options.orchestratorMaxOutputTokens],
|
|
680
|
+
["shallowMaxOutputTokens", options.shallowMaxOutputTokens]
|
|
681
|
+
].flatMap(([name, value]) => value == null ? [] : [name]);
|
|
682
|
+
if (editOnlyOptions.length > 0) {
|
|
683
|
+
throw new InvalidArgumentError2({
|
|
684
|
+
argument: "providerOptions",
|
|
685
|
+
message: `QuiverAI ${operation} does not support these edit-only provider options: ${editOnlyOptions.join(
|
|
686
|
+
", "
|
|
687
|
+
)}.`
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
function toQuiverAIEditSource(file) {
|
|
692
|
+
if (file.type === "url") {
|
|
693
|
+
return {
|
|
694
|
+
svg_source: {
|
|
695
|
+
url: validateQuiverAIImageUrl(file.url)
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
let data;
|
|
700
|
+
try {
|
|
701
|
+
data = typeof file.data === "string" ? convertBase64ToUint8Array2(file.data) : file.data;
|
|
702
|
+
} catch (cause) {
|
|
703
|
+
throw new InvalidArgumentError2({
|
|
704
|
+
argument: "files",
|
|
705
|
+
message: "QuiverAI SVG source data must be valid base64 or binary data.",
|
|
706
|
+
cause
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
if (data.length === 0 || data.length > MAX_EDIT_SVG_BYTES) {
|
|
710
|
+
throw new InvalidArgumentError2({
|
|
711
|
+
argument: "files",
|
|
712
|
+
message: `QuiverAI SVG source data must contain 1-${MAX_EDIT_SVG_BYTES} bytes.`
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
let svg;
|
|
716
|
+
try {
|
|
717
|
+
svg = new TextDecoder("utf-8", { fatal: true }).decode(data);
|
|
718
|
+
} catch (cause) {
|
|
719
|
+
throw new InvalidArgumentError2({
|
|
720
|
+
argument: "files",
|
|
721
|
+
message: "QuiverAI SVG source data must be valid UTF-8.",
|
|
722
|
+
cause
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
if (svg.length > MAX_EDIT_SVG_BYTES || !isSvgMarkup(svg)) {
|
|
726
|
+
throw new InvalidArgumentError2({
|
|
727
|
+
argument: "files",
|
|
728
|
+
message: "QuiverAI SVG source data must contain a complete SVG document."
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
svg_source: {
|
|
733
|
+
base64: convertUint8ArrayToBase642(data)
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
function isSvgMarkup(svg) {
|
|
738
|
+
var _a;
|
|
739
|
+
const document = svg.replace(/^\uFEFF/, "");
|
|
740
|
+
const elements = [];
|
|
741
|
+
let position = 0;
|
|
742
|
+
let rootSeen = false;
|
|
743
|
+
let rootClosed = false;
|
|
744
|
+
let doctypeSeen = false;
|
|
745
|
+
while (position < document.length) {
|
|
746
|
+
if (document[position] !== "<") {
|
|
747
|
+
const nextTag = document.indexOf("<", position);
|
|
748
|
+
const end = nextTag === -1 ? document.length : nextTag;
|
|
749
|
+
const text = document.slice(position, end);
|
|
750
|
+
if (elements.length === 0 && text.trim().length > 0 || text.includes("]]>") || !hasValidXmlReferences(text)) {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
position = end;
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (document.startsWith("<!--", position)) {
|
|
757
|
+
const commentEnd = document.indexOf("-->", position + 4);
|
|
758
|
+
if (commentEnd === -1 || document.slice(position + 4, commentEnd).includes("--")) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
position = commentEnd + 3;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (document.startsWith("<?", position)) {
|
|
765
|
+
const instructionEnd = document.indexOf("?>", position + 2);
|
|
766
|
+
if (instructionEnd === -1) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
position = instructionEnd + 2;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (document.startsWith("<![CDATA[", position)) {
|
|
773
|
+
if (elements.length === 0) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
const cdataEnd = document.indexOf("]]>", position + 9);
|
|
777
|
+
if (cdataEnd === -1) {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
position = cdataEnd + 3;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
if (document.slice(position, position + 9).toUpperCase() === "<!DOCTYPE") {
|
|
784
|
+
if (rootSeen || doctypeSeen || elements.length > 0 || !/[\t\n\r ]/.test((_a = document[position + 9]) != null ? _a : "")) {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
const doctypeName = readXmlName(
|
|
788
|
+
document,
|
|
789
|
+
skipXmlWhitespace(document, position + 9)
|
|
790
|
+
);
|
|
791
|
+
if ((doctypeName == null ? void 0 : doctypeName.name.toLowerCase()) !== "svg") {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
const doctypeEnd = findDoctypeEnd(document, doctypeName.end);
|
|
795
|
+
if (doctypeEnd === -1) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
doctypeSeen = true;
|
|
799
|
+
position = doctypeEnd;
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
if (document.startsWith("<!", position)) {
|
|
803
|
+
return false;
|
|
804
|
+
}
|
|
805
|
+
if (document.startsWith("</", position)) {
|
|
806
|
+
const closingTag = readXmlName(document, position + 2);
|
|
807
|
+
if (closingTag == null) {
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
let tagEnd = skipXmlWhitespace(document, closingTag.end);
|
|
811
|
+
if (document[tagEnd] !== ">") {
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
const expectedTag = elements.pop();
|
|
815
|
+
if (expectedTag !== closingTag.name) {
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
tagEnd += 1;
|
|
819
|
+
if (elements.length === 0) {
|
|
820
|
+
rootClosed = true;
|
|
821
|
+
}
|
|
822
|
+
position = tagEnd;
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
if (rootClosed) {
|
|
826
|
+
return false;
|
|
827
|
+
}
|
|
828
|
+
const openingTag = readXmlName(document, position + 1);
|
|
829
|
+
if (openingTag == null) {
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
if (!rootSeen) {
|
|
833
|
+
if (openingTag.name.toLowerCase() !== "svg") {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
rootSeen = true;
|
|
837
|
+
}
|
|
838
|
+
const attributes = /* @__PURE__ */ new Set();
|
|
839
|
+
let tagPosition = openingTag.end;
|
|
840
|
+
while (tagPosition < document.length) {
|
|
841
|
+
const beforeWhitespace = tagPosition;
|
|
842
|
+
tagPosition = skipXmlWhitespace(document, tagPosition);
|
|
843
|
+
if (document.startsWith("/>", tagPosition)) {
|
|
844
|
+
tagPosition += 2;
|
|
845
|
+
if (elements.length === 0) {
|
|
846
|
+
rootClosed = true;
|
|
847
|
+
}
|
|
848
|
+
position = tagPosition;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
if (document[tagPosition] === ">") {
|
|
852
|
+
elements.push(openingTag.name);
|
|
853
|
+
position = tagPosition + 1;
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
if (tagPosition === beforeWhitespace) {
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
const attribute = readXmlName(document, tagPosition);
|
|
860
|
+
if (attribute == null || attributes.has(attribute.name)) {
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
attributes.add(attribute.name);
|
|
864
|
+
tagPosition = skipXmlWhitespace(document, attribute.end);
|
|
865
|
+
if (document[tagPosition] !== "=") {
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
tagPosition = skipXmlWhitespace(document, tagPosition + 1);
|
|
869
|
+
const quote = document[tagPosition];
|
|
870
|
+
if (quote !== '"' && quote !== "'") {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
const valueEnd = document.indexOf(quote, tagPosition + 1);
|
|
874
|
+
if (valueEnd === -1 || document.slice(tagPosition + 1, valueEnd).includes("<") || !hasValidXmlReferences(document.slice(tagPosition + 1, valueEnd))) {
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
tagPosition = valueEnd + 1;
|
|
878
|
+
}
|
|
879
|
+
if (tagPosition >= document.length && position !== document.length) {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return rootSeen && rootClosed && elements.length === 0;
|
|
884
|
+
}
|
|
885
|
+
function isXmlNameStart(character) {
|
|
886
|
+
return character != null && /[A-Z_a-z:\u0080-\uFFFF]/.test(character);
|
|
887
|
+
}
|
|
888
|
+
function isXmlNameCharacter(character) {
|
|
889
|
+
return character != null && /[-.0-9A-Z_a-z:\u00B7\u0080-\uFFFF]/.test(character);
|
|
890
|
+
}
|
|
891
|
+
function readXmlName(value, position) {
|
|
892
|
+
if (!isXmlNameStart(value[position])) {
|
|
893
|
+
return void 0;
|
|
894
|
+
}
|
|
895
|
+
const start = position;
|
|
896
|
+
position += 1;
|
|
897
|
+
while (isXmlNameCharacter(value[position])) {
|
|
898
|
+
position += 1;
|
|
899
|
+
}
|
|
900
|
+
return {
|
|
901
|
+
name: value.slice(start, position),
|
|
902
|
+
end: position
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function skipXmlWhitespace(value, position) {
|
|
906
|
+
var _a;
|
|
907
|
+
while (/[\t\n\r ]/.test((_a = value[position]) != null ? _a : "")) {
|
|
908
|
+
position += 1;
|
|
909
|
+
}
|
|
910
|
+
return position;
|
|
911
|
+
}
|
|
912
|
+
function hasValidXmlReferences(value) {
|
|
913
|
+
let position = value.indexOf("&");
|
|
914
|
+
while (position !== -1) {
|
|
915
|
+
const end = value.indexOf(";", position + 1);
|
|
916
|
+
if (end === -1) {
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
const reference = value.slice(position + 1, end);
|
|
920
|
+
if (!/^#\d+$/.test(reference) && !/^#x[\dA-Fa-f]+$/.test(reference) && !/^[A-Z_a-z:\u0080-\uFFFF][-.0-9A-Z_a-z:\u00B7\u0080-\uFFFF]*$/.test(
|
|
921
|
+
reference
|
|
922
|
+
)) {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
position = value.indexOf("&", end + 1);
|
|
926
|
+
}
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
function findDoctypeEnd(value, position) {
|
|
930
|
+
let subsetDepth = 0;
|
|
931
|
+
let quote;
|
|
932
|
+
while (position < value.length) {
|
|
933
|
+
const character = value[position];
|
|
934
|
+
if (quote != null) {
|
|
935
|
+
if (character === quote) {
|
|
936
|
+
quote = void 0;
|
|
937
|
+
}
|
|
938
|
+
} else if (character === '"' || character === "'") {
|
|
939
|
+
quote = character;
|
|
940
|
+
} else if (character === "[") {
|
|
941
|
+
subsetDepth += 1;
|
|
942
|
+
} else if (character === "]") {
|
|
943
|
+
if (subsetDepth === 0) {
|
|
944
|
+
return -1;
|
|
945
|
+
}
|
|
946
|
+
subsetDepth -= 1;
|
|
947
|
+
} else if (character === ">" && subsetDepth === 0) {
|
|
948
|
+
return position + 1;
|
|
949
|
+
}
|
|
950
|
+
position += 1;
|
|
951
|
+
}
|
|
952
|
+
return -1;
|
|
953
|
+
}
|
|
273
954
|
function collectWarnings({
|
|
274
955
|
size,
|
|
275
956
|
aspectRatio,
|
|
@@ -314,7 +995,9 @@ var svgUsageSchema = z2.object({
|
|
|
314
995
|
});
|
|
315
996
|
var svgDocumentSchema = z2.object({
|
|
316
997
|
svg: z2.string().min(1),
|
|
317
|
-
mime_type: z2.literal("image/svg+xml")
|
|
998
|
+
mime_type: z2.literal("image/svg+xml"),
|
|
999
|
+
loop_period_ms: z2.number().int().nonnegative().nullish(),
|
|
1000
|
+
opening_animation_ms: z2.number().int().nonnegative().nullish()
|
|
318
1001
|
});
|
|
319
1002
|
var svgGenerationResponseSchema = z2.object({
|
|
320
1003
|
id: z2.string().min(1),
|
|
@@ -336,7 +1019,7 @@ var quiveraiFailedResponseHandler = createJsonErrorResponseHandler({
|
|
|
336
1019
|
});
|
|
337
1020
|
|
|
338
1021
|
// src/version.ts
|
|
339
|
-
var VERSION = true ? "2.0.
|
|
1022
|
+
var VERSION = true ? "2.0.45" : "0.0.0-test";
|
|
340
1023
|
|
|
341
1024
|
// src/quiverai-provider.ts
|
|
342
1025
|
var defaultBaseURL = "https://api.quiver.ai/v1";
|
|
@@ -389,6 +1072,7 @@ var quiverai = createQuiverAI();
|
|
|
389
1072
|
export {
|
|
390
1073
|
VERSION,
|
|
391
1074
|
createQuiverAI,
|
|
1075
|
+
prepareQuiverAIImageReference,
|
|
392
1076
|
quiverai
|
|
393
1077
|
};
|
|
394
1078
|
//# sourceMappingURL=index.js.map
|