@ai-sdk/quiverai 0.0.0 → 1.0.1
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 +14 -0
- package/LICENSE +13 -0
- package/README.md +50 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +351 -0
- package/dist/index.js.map +1 -0
- package/docs/180-quiverai.mdx +204 -0
- package/package.json +76 -1
- package/src/index.ts +8 -0
- package/src/quiverai-image-model-options.ts +67 -0
- package/src/quiverai-image-model.ts +314 -0
- package/src/quiverai-image-settings.ts +17 -0
- package/src/quiverai-provider.ts +115 -0
- package/src/version.ts +6 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @ai-sdk/quiverai
|
|
2
|
+
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [942f2f8]
|
|
8
|
+
- @ai-sdk/provider-utils@4.0.28
|
|
9
|
+
|
|
10
|
+
## 1.0.0
|
|
11
|
+
|
|
12
|
+
### Major Changes
|
|
13
|
+
|
|
14
|
+
- fb477bd: Add `@ai-sdk/quiverai` provider for QuiverAI SVG image generation and vectorization.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2023 Vercel, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# AI SDK - QuiverAI Provider
|
|
2
|
+
|
|
3
|
+
The **QuiverAI provider** for the [AI SDK](https://ai-sdk.dev/docs) adds image generation support for the [QuiverAI](https://quiver.ai/) API. QuiverAI generates SVG documents from text prompts and can also vectorize raster images.
|
|
4
|
+
|
|
5
|
+
> **Deploying to Vercel?** With Vercel's AI Gateway you can access many models from other providers — no additional packages, API keys, or extra cost. [Get started with AI Gateway](https://vercel.com/ai-gateway).
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
The QuiverAI provider is available in the `@ai-sdk/quiverai` module. You can install it with:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @ai-sdk/quiverai
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Skill for Coding Agents
|
|
16
|
+
|
|
17
|
+
If you use coding agents such as Claude Code or Cursor, we highly recommend adding the AI SDK skill to your repository:
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
npx skills add vercel/ai
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Provider Instance
|
|
24
|
+
|
|
25
|
+
You can import the default provider instance `quiverai` from `@ai-sdk/quiverai`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { quiverai } from '@ai-sdk/quiverai';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Image Generation Example
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { quiverai } from '@ai-sdk/quiverai';
|
|
35
|
+
import { generateImage } from 'ai';
|
|
36
|
+
import fs from 'fs';
|
|
37
|
+
|
|
38
|
+
const { image } = await generateImage({
|
|
39
|
+
model: quiverai.image('arrow-1.1'),
|
|
40
|
+
prompt: 'A logo for the next AI Design startup',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const filename = `image-${Date.now()}.svg`;
|
|
44
|
+
fs.writeFileSync(filename, image.uint8Array);
|
|
45
|
+
console.log(`Image saved to ${filename}`);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
Please check out the **[QuiverAI provider](https://ai-sdk.dev/providers/ai-sdk-providers/quiverai)** for more information.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ProviderV3, ImageModelV3 } from '@ai-sdk/provider';
|
|
2
|
+
import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
|
|
3
|
+
import { FetchFunction, InferSchema } from '@ai-sdk/provider-utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* QuiverAI image model identifier.
|
|
7
|
+
*
|
|
8
|
+
* @see https://quiver.ai/
|
|
9
|
+
*/
|
|
10
|
+
type QuiverAIImageModelId = 'arrow-1' | 'arrow-1.1' | 'arrow-1.1-max' | (string & {});
|
|
11
|
+
|
|
12
|
+
interface QuiverAIProviderSettings {
|
|
13
|
+
/**
|
|
14
|
+
* QuiverAI API key. Default value is taken from the `QUIVERAI_API_KEY`
|
|
15
|
+
* environment variable.
|
|
16
|
+
*/
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Base URL for the API calls. Defaults to `https://api.quiver.ai/v1` and
|
|
20
|
+
* falls back to the `QUIVERAI_BASE_URL` environment variable.
|
|
21
|
+
*/
|
|
22
|
+
baseURL?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Custom headers to include in the requests.
|
|
25
|
+
*/
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Custom fetch implementation. You can use it as a middleware to intercept
|
|
29
|
+
* requests, or to provide a custom fetch implementation for e.g. testing.
|
|
30
|
+
*/
|
|
31
|
+
fetch?: FetchFunction;
|
|
32
|
+
}
|
|
33
|
+
interface QuiverAIProvider extends ProviderV3 {
|
|
34
|
+
/**
|
|
35
|
+
* Creates a model for image generation.
|
|
36
|
+
*/
|
|
37
|
+
image(modelId: QuiverAIImageModelId): ImageModelV3;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a model for image generation.
|
|
40
|
+
*/
|
|
41
|
+
imageModel(modelId: QuiverAIImageModelId): ImageModelV3;
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use `embeddingModel` instead.
|
|
44
|
+
*/
|
|
45
|
+
textEmbeddingModel(modelId: string): never;
|
|
46
|
+
}
|
|
47
|
+
declare function createQuiverAI(options?: QuiverAIProviderSettings): QuiverAIProvider;
|
|
48
|
+
declare const quiverai: QuiverAIProvider;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Provider options schema for QuiverAI image generation.
|
|
52
|
+
*
|
|
53
|
+
* @see https://quiver.ai/
|
|
54
|
+
*/
|
|
55
|
+
declare const quiveraiImageModelOptionsSchema: _ai_sdk_provider_utils.LazySchema<{
|
|
56
|
+
operation?: "generate" | "vectorize" | undefined;
|
|
57
|
+
instructions?: string | undefined;
|
|
58
|
+
temperature?: number | undefined;
|
|
59
|
+
topP?: number | undefined;
|
|
60
|
+
presencePenalty?: number | null | undefined;
|
|
61
|
+
maxOutputTokens?: number | undefined;
|
|
62
|
+
autoCrop?: boolean | undefined;
|
|
63
|
+
targetSize?: number | undefined;
|
|
64
|
+
}>;
|
|
65
|
+
type QuiverAIImageModelOptions = InferSchema<typeof quiveraiImageModelOptionsSchema>;
|
|
66
|
+
|
|
67
|
+
declare const VERSION: string;
|
|
68
|
+
|
|
69
|
+
export { type QuiverAIImageModelId, type QuiverAIImageModelOptions, type QuiverAIProvider, type QuiverAIProviderSettings, VERSION, createQuiverAI, quiverai };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// src/quiverai-provider.ts
|
|
2
|
+
import {
|
|
3
|
+
NoSuchModelError
|
|
4
|
+
} from "@ai-sdk/provider";
|
|
5
|
+
import {
|
|
6
|
+
loadApiKey,
|
|
7
|
+
loadOptionalSetting,
|
|
8
|
+
withoutTrailingSlash,
|
|
9
|
+
withUserAgentSuffix
|
|
10
|
+
} from "@ai-sdk/provider-utils";
|
|
11
|
+
|
|
12
|
+
// src/quiverai-image-model.ts
|
|
13
|
+
import {
|
|
14
|
+
InvalidArgumentError
|
|
15
|
+
} from "@ai-sdk/provider";
|
|
16
|
+
import {
|
|
17
|
+
combineHeaders,
|
|
18
|
+
convertUint8ArrayToBase64,
|
|
19
|
+
createJsonErrorResponseHandler,
|
|
20
|
+
createJsonResponseHandler,
|
|
21
|
+
parseProviderOptions,
|
|
22
|
+
postJsonToApi
|
|
23
|
+
} from "@ai-sdk/provider-utils";
|
|
24
|
+
import { z as z2 } from "zod/v4";
|
|
25
|
+
|
|
26
|
+
// src/quiverai-image-model-options.ts
|
|
27
|
+
import {
|
|
28
|
+
lazySchema,
|
|
29
|
+
zodSchema
|
|
30
|
+
} from "@ai-sdk/provider-utils";
|
|
31
|
+
import { z } from "zod/v4";
|
|
32
|
+
var quiveraiImageModelOptionsSchema = lazySchema(
|
|
33
|
+
() => zodSchema(
|
|
34
|
+
z.object({
|
|
35
|
+
/**
|
|
36
|
+
* The operation to perform. Defaults to `generate`.
|
|
37
|
+
*
|
|
38
|
+
* - `generate`: Text-to-SVG generation. Requires `prompt`.
|
|
39
|
+
* - `vectorize`: Convert an input raster image into an SVG. Requires a
|
|
40
|
+
* single image in `prompt.images` / `files`.
|
|
41
|
+
*/
|
|
42
|
+
operation: z.enum(["generate", "vectorize"]).optional(),
|
|
43
|
+
/**
|
|
44
|
+
* Extra style guidance for prompt-based generation.
|
|
45
|
+
*/
|
|
46
|
+
instructions: z.string().min(1).optional(),
|
|
47
|
+
/**
|
|
48
|
+
* Sampling temperature (0-2).
|
|
49
|
+
*/
|
|
50
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
51
|
+
/**
|
|
52
|
+
* Nucleus sampling top-p (0-1).
|
|
53
|
+
*/
|
|
54
|
+
topP: z.number().min(0).max(1).optional(),
|
|
55
|
+
/**
|
|
56
|
+
* Presence penalty (-2 to 2).
|
|
57
|
+
*/
|
|
58
|
+
presencePenalty: z.number().min(-2).max(2).nullable().optional(),
|
|
59
|
+
/**
|
|
60
|
+
* Maximum number of output tokens (1 - 131072).
|
|
61
|
+
*/
|
|
62
|
+
maxOutputTokens: z.number().int().min(1).max(131072).optional(),
|
|
63
|
+
/**
|
|
64
|
+
* Whether to auto-crop the input image before vectorization.
|
|
65
|
+
* Only used when `operation` is `vectorize`.
|
|
66
|
+
*/
|
|
67
|
+
autoCrop: z.boolean().optional(),
|
|
68
|
+
/**
|
|
69
|
+
* Target canvas size in pixels for vectorization (128 - 4096).
|
|
70
|
+
* Only used when `operation` is `vectorize`.
|
|
71
|
+
*/
|
|
72
|
+
targetSize: z.number().int().min(128).max(4096).optional()
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// src/quiverai-image-model.ts
|
|
78
|
+
var QuiverAIImageModel = class {
|
|
79
|
+
constructor(modelId, config) {
|
|
80
|
+
this.modelId = modelId;
|
|
81
|
+
this.config = config;
|
|
82
|
+
this.specificationVersion = "v3";
|
|
83
|
+
this.maxImagesPerCall = 16;
|
|
84
|
+
}
|
|
85
|
+
get provider() {
|
|
86
|
+
return this.config.provider;
|
|
87
|
+
}
|
|
88
|
+
async doGenerate({
|
|
89
|
+
prompt,
|
|
90
|
+
n,
|
|
91
|
+
files,
|
|
92
|
+
mask,
|
|
93
|
+
size,
|
|
94
|
+
aspectRatio,
|
|
95
|
+
seed,
|
|
96
|
+
providerOptions,
|
|
97
|
+
headers,
|
|
98
|
+
abortSignal
|
|
99
|
+
}) {
|
|
100
|
+
var _a, _b, _c, _d, _e, _f;
|
|
101
|
+
const quiveraiOptions = await parseProviderOptions({
|
|
102
|
+
provider: "quiverai",
|
|
103
|
+
providerOptions,
|
|
104
|
+
schema: quiveraiImageModelOptionsSchema
|
|
105
|
+
});
|
|
106
|
+
const operation = (_a = quiveraiOptions == null ? void 0 : quiveraiOptions.operation) != null ? _a : "generate";
|
|
107
|
+
const warnings = collectWarnings({ size, aspectRatio, seed, mask });
|
|
108
|
+
const body = buildRequestBody({
|
|
109
|
+
modelId: this.modelId,
|
|
110
|
+
n,
|
|
111
|
+
prompt,
|
|
112
|
+
files,
|
|
113
|
+
operation,
|
|
114
|
+
options: quiveraiOptions != null ? quiveraiOptions : {}
|
|
115
|
+
});
|
|
116
|
+
const currentDate = (_d = (_c = (_b = this.config._internal) == null ? void 0 : _b.currentDate) == null ? void 0 : _c.call(_b)) != null ? _d : /* @__PURE__ */ new Date();
|
|
117
|
+
const combinedHeaders = combineHeaders((_f = (_e = this.config).headers) == null ? void 0 : _f.call(_e), headers);
|
|
118
|
+
const { value: response, responseHeaders } = await postJsonToApi({
|
|
119
|
+
url: `${this.config.baseURL}${getOperationPath(operation)}`,
|
|
120
|
+
headers: combinedHeaders,
|
|
121
|
+
body,
|
|
122
|
+
failedResponseHandler: quiveraiFailedResponseHandler,
|
|
123
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
124
|
+
svgGenerationResponseSchema
|
|
125
|
+
),
|
|
126
|
+
abortSignal,
|
|
127
|
+
fetch: this.config.fetch
|
|
128
|
+
});
|
|
129
|
+
const encoder = new TextEncoder();
|
|
130
|
+
const images = response.data.map((image) => encoder.encode(image.svg));
|
|
131
|
+
return {
|
|
132
|
+
images,
|
|
133
|
+
warnings,
|
|
134
|
+
providerMetadata: {
|
|
135
|
+
quiverai: {
|
|
136
|
+
images: response.data.map((image, index) => ({
|
|
137
|
+
index,
|
|
138
|
+
mimeType: image.mime_type
|
|
139
|
+
}))
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
response: {
|
|
143
|
+
timestamp: response.created ? new Date(response.created * 1e3) : currentDate,
|
|
144
|
+
modelId: this.modelId,
|
|
145
|
+
headers: responseHeaders
|
|
146
|
+
},
|
|
147
|
+
...response.usage && {
|
|
148
|
+
usage: {
|
|
149
|
+
inputTokens: response.usage.input_tokens,
|
|
150
|
+
outputTokens: response.usage.output_tokens,
|
|
151
|
+
totalTokens: response.usage.total_tokens
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
function getOperationPath(operation) {
|
|
158
|
+
return operation === "generate" ? "/svgs/generations" : "/svgs/vectorizations";
|
|
159
|
+
}
|
|
160
|
+
function getGenerateReferenceLimit(modelId) {
|
|
161
|
+
return modelId === "arrow-1.1-max" ? 16 : 4;
|
|
162
|
+
}
|
|
163
|
+
function toQuiverAIImageReference(image) {
|
|
164
|
+
if (image.type === "url") {
|
|
165
|
+
return { url: image.url };
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
base64: typeof image.data === "string" ? image.data : convertUint8ArrayToBase64(image.data)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function buildRequestBody({
|
|
172
|
+
modelId,
|
|
173
|
+
n,
|
|
174
|
+
prompt,
|
|
175
|
+
files,
|
|
176
|
+
operation,
|
|
177
|
+
options
|
|
178
|
+
}) {
|
|
179
|
+
const sharedOptions = {
|
|
180
|
+
temperature: options.temperature,
|
|
181
|
+
top_p: options.topP,
|
|
182
|
+
presence_penalty: options.presencePenalty,
|
|
183
|
+
max_output_tokens: options.maxOutputTokens,
|
|
184
|
+
stream: false
|
|
185
|
+
};
|
|
186
|
+
if (operation === "generate") {
|
|
187
|
+
if (prompt == null || prompt.trim().length === 0) {
|
|
188
|
+
throw new InvalidArgumentError({
|
|
189
|
+
argument: "prompt",
|
|
190
|
+
message: "QuiverAI image generation requires a non-empty prompt for generateImage."
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const references = files == null ? void 0 : files.map(toQuiverAIImageReference);
|
|
194
|
+
const maxReferences = getGenerateReferenceLimit(modelId);
|
|
195
|
+
if (references != null && references.length > maxReferences) {
|
|
196
|
+
throw new InvalidArgumentError({
|
|
197
|
+
argument: "files",
|
|
198
|
+
message: `QuiverAI generate supports up to ${maxReferences} reference images for model "${modelId}".`
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
model: modelId,
|
|
203
|
+
n,
|
|
204
|
+
prompt,
|
|
205
|
+
...sharedOptions,
|
|
206
|
+
instructions: options.instructions,
|
|
207
|
+
references
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (files == null || files.length === 0) {
|
|
211
|
+
throw new InvalidArgumentError({
|
|
212
|
+
argument: "files",
|
|
213
|
+
message: 'QuiverAI vectorize requires an input image. Pass an image in the generateImage prompt and set providerOptions.quiverai.operation to "vectorize".'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (files.length > 1) {
|
|
217
|
+
throw new InvalidArgumentError({
|
|
218
|
+
argument: "files",
|
|
219
|
+
message: "QuiverAI vectorize accepts a single input image."
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
model: modelId,
|
|
224
|
+
n,
|
|
225
|
+
image: toQuiverAIImageReference(files[0]),
|
|
226
|
+
...sharedOptions,
|
|
227
|
+
auto_crop: options.autoCrop,
|
|
228
|
+
target_size: options.targetSize
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function collectWarnings({
|
|
232
|
+
size,
|
|
233
|
+
aspectRatio,
|
|
234
|
+
seed,
|
|
235
|
+
mask
|
|
236
|
+
}) {
|
|
237
|
+
const warnings = [];
|
|
238
|
+
if (size != null) {
|
|
239
|
+
warnings.push({
|
|
240
|
+
type: "unsupported",
|
|
241
|
+
feature: "size",
|
|
242
|
+
details: "QuiverAI SVG generation does not support the `size` option. The setting was ignored."
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (aspectRatio != null) {
|
|
246
|
+
warnings.push({
|
|
247
|
+
type: "unsupported",
|
|
248
|
+
feature: "aspectRatio",
|
|
249
|
+
details: "QuiverAI SVG generation does not support the `aspectRatio` option. The setting was ignored."
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (seed != null) {
|
|
253
|
+
warnings.push({
|
|
254
|
+
type: "unsupported",
|
|
255
|
+
feature: "seed",
|
|
256
|
+
details: "QuiverAI SVG generation does not support the `seed` option. The setting was ignored."
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (mask != null) {
|
|
260
|
+
warnings.push({
|
|
261
|
+
type: "unsupported",
|
|
262
|
+
feature: "mask",
|
|
263
|
+
details: "QuiverAI SVG generation does not support masks. The mask was ignored."
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return warnings;
|
|
267
|
+
}
|
|
268
|
+
var svgUsageSchema = z2.object({
|
|
269
|
+
total_tokens: z2.number().int().nonnegative(),
|
|
270
|
+
input_tokens: z2.number().int().nonnegative(),
|
|
271
|
+
output_tokens: z2.number().int().nonnegative()
|
|
272
|
+
});
|
|
273
|
+
var svgDocumentSchema = z2.object({
|
|
274
|
+
svg: z2.string().min(1),
|
|
275
|
+
mime_type: z2.literal("image/svg+xml")
|
|
276
|
+
});
|
|
277
|
+
var svgGenerationResponseSchema = z2.object({
|
|
278
|
+
id: z2.string().min(1),
|
|
279
|
+
created: z2.number().int().nonnegative(),
|
|
280
|
+
data: z2.array(svgDocumentSchema).min(1),
|
|
281
|
+
usage: svgUsageSchema.nullish()
|
|
282
|
+
});
|
|
283
|
+
var quiveraiErrorSchema = z2.object({
|
|
284
|
+
status: z2.number().int(),
|
|
285
|
+
code: z2.string().min(1),
|
|
286
|
+
message: z2.string().min(1),
|
|
287
|
+
request_id: z2.string().min(1)
|
|
288
|
+
});
|
|
289
|
+
var quiveraiFailedResponseHandler = createJsonErrorResponseHandler({
|
|
290
|
+
errorSchema: quiveraiErrorSchema,
|
|
291
|
+
errorToMessage: (error) => error.message,
|
|
292
|
+
isRetryable: (response) => response.status === 429 || response.status >= 500
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// src/version.ts
|
|
296
|
+
var VERSION = true ? "1.0.1" : "0.0.0-test";
|
|
297
|
+
|
|
298
|
+
// src/quiverai-provider.ts
|
|
299
|
+
var defaultBaseURL = "https://api.quiver.ai/v1";
|
|
300
|
+
function createQuiverAI(options = {}) {
|
|
301
|
+
var _a;
|
|
302
|
+
const baseURL = (_a = withoutTrailingSlash(
|
|
303
|
+
loadOptionalSetting({
|
|
304
|
+
settingValue: options.baseURL,
|
|
305
|
+
environmentVariableName: "QUIVERAI_BASE_URL"
|
|
306
|
+
})
|
|
307
|
+
)) != null ? _a : defaultBaseURL;
|
|
308
|
+
const getHeaders = () => withUserAgentSuffix(
|
|
309
|
+
{
|
|
310
|
+
Authorization: `Bearer ${loadApiKey({
|
|
311
|
+
apiKey: options.apiKey,
|
|
312
|
+
environmentVariableName: "QUIVERAI_API_KEY",
|
|
313
|
+
description: "QuiverAI"
|
|
314
|
+
})}`,
|
|
315
|
+
...options.headers
|
|
316
|
+
},
|
|
317
|
+
`ai-sdk/quiverai/${VERSION}`
|
|
318
|
+
);
|
|
319
|
+
const createImageModel = (modelId) => new QuiverAIImageModel(modelId, {
|
|
320
|
+
provider: "quiverai.image",
|
|
321
|
+
baseURL,
|
|
322
|
+
headers: getHeaders,
|
|
323
|
+
fetch: options.fetch
|
|
324
|
+
});
|
|
325
|
+
const embeddingModel = (modelId) => {
|
|
326
|
+
throw new NoSuchModelError({
|
|
327
|
+
modelId,
|
|
328
|
+
modelType: "embeddingModel"
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
return {
|
|
332
|
+
specificationVersion: "v3",
|
|
333
|
+
image: createImageModel,
|
|
334
|
+
imageModel: createImageModel,
|
|
335
|
+
languageModel: (modelId) => {
|
|
336
|
+
throw new NoSuchModelError({
|
|
337
|
+
modelId,
|
|
338
|
+
modelType: "languageModel"
|
|
339
|
+
});
|
|
340
|
+
},
|
|
341
|
+
embeddingModel,
|
|
342
|
+
textEmbeddingModel: embeddingModel
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
var quiverai = createQuiverAI();
|
|
346
|
+
export {
|
|
347
|
+
VERSION,
|
|
348
|
+
createQuiverAI,
|
|
349
|
+
quiverai
|
|
350
|
+
};
|
|
351
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/quiverai-provider.ts","../src/quiverai-image-model.ts","../src/quiverai-image-model-options.ts","../src/version.ts"],"sourcesContent":["import {\n NoSuchModelError,\n type ImageModelV3,\n type ProviderV3,\n} from '@ai-sdk/provider';\nimport {\n loadApiKey,\n loadOptionalSetting,\n withoutTrailingSlash,\n withUserAgentSuffix,\n type FetchFunction,\n} from '@ai-sdk/provider-utils';\nimport { QuiverAIImageModel } from './quiverai-image-model';\nimport type { QuiverAIImageModelId } from './quiverai-image-settings';\nimport { VERSION } from './version';\n\nexport interface QuiverAIProviderSettings {\n /**\n * QuiverAI API key. Default value is taken from the `QUIVERAI_API_KEY`\n * environment variable.\n */\n apiKey?: string;\n\n /**\n * Base URL for the API calls. Defaults to `https://api.quiver.ai/v1` and\n * falls back to the `QUIVERAI_BASE_URL` environment variable.\n */\n baseURL?: string;\n\n /**\n * Custom headers to include in the requests.\n */\n headers?: Record<string, string>;\n\n /**\n * Custom fetch implementation. You can use it as a middleware to intercept\n * requests, or to provide a custom fetch implementation for e.g. testing.\n */\n fetch?: FetchFunction;\n}\n\nexport interface QuiverAIProvider extends ProviderV3 {\n /**\n * Creates a model for image generation.\n */\n image(modelId: QuiverAIImageModelId): ImageModelV3;\n\n /**\n * Creates a model for image generation.\n */\n imageModel(modelId: QuiverAIImageModelId): ImageModelV3;\n\n /**\n * @deprecated Use `embeddingModel` instead.\n */\n textEmbeddingModel(modelId: string): never;\n}\n\nconst defaultBaseURL = 'https://api.quiver.ai/v1';\n\nexport function createQuiverAI(\n options: QuiverAIProviderSettings = {},\n): QuiverAIProvider {\n const baseURL =\n withoutTrailingSlash(\n loadOptionalSetting({\n settingValue: options.baseURL,\n environmentVariableName: 'QUIVERAI_BASE_URL',\n }),\n ) ?? defaultBaseURL;\n\n const getHeaders = () =>\n withUserAgentSuffix(\n {\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'QUIVERAI_API_KEY',\n description: 'QuiverAI',\n })}`,\n ...options.headers,\n },\n `ai-sdk/quiverai/${VERSION}`,\n );\n\n const createImageModel = (modelId: QuiverAIImageModelId) =>\n new QuiverAIImageModel(modelId, {\n provider: 'quiverai.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n\n const embeddingModel = (modelId: string) => {\n throw new NoSuchModelError({\n modelId,\n modelType: 'embeddingModel',\n });\n };\n\n return {\n specificationVersion: 'v3',\n image: createImageModel,\n imageModel: createImageModel,\n languageModel: (modelId: string) => {\n throw new NoSuchModelError({\n modelId,\n modelType: 'languageModel',\n });\n },\n embeddingModel,\n textEmbeddingModel: embeddingModel,\n };\n}\n\nexport const quiverai = createQuiverAI();\n","import {\n InvalidArgumentError,\n type ImageModelV3,\n type ImageModelV3CallOptions,\n type ImageModelV3File,\n type SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n convertUint8ArrayToBase64,\n createJsonErrorResponseHandler,\n createJsonResponseHandler,\n parseProviderOptions,\n postJsonToApi,\n type FetchFunction,\n} from '@ai-sdk/provider-utils';\nimport { z } from 'zod/v4';\nimport {\n quiveraiImageModelOptionsSchema,\n type QuiverAIImageModelOptions,\n} from './quiverai-image-model-options';\nimport type {\n QuiverAIImageModelId,\n QuiverAIOperation,\n} from './quiverai-image-settings';\n\ninterface QuiverAIImageModelConfig {\n provider: string;\n baseURL: string;\n headers?: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\nexport class QuiverAIImageModel implements ImageModelV3 {\n readonly specificationVersion = 'v3';\n readonly maxImagesPerCall = 16;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: QuiverAIImageModelId,\n private readonly config: QuiverAIImageModelConfig,\n ) {}\n\n async doGenerate({\n prompt,\n n,\n files,\n mask,\n size,\n aspectRatio,\n seed,\n providerOptions,\n headers,\n abortSignal,\n }: ImageModelV3CallOptions): Promise<\n Awaited<ReturnType<ImageModelV3['doGenerate']>>\n > {\n const quiveraiOptions = await parseProviderOptions({\n provider: 'quiverai',\n providerOptions,\n schema: quiveraiImageModelOptionsSchema,\n });\n\n const operation: QuiverAIOperation =\n quiveraiOptions?.operation ?? 'generate';\n\n const warnings = collectWarnings({ size, aspectRatio, seed, mask });\n\n const body = buildRequestBody({\n modelId: this.modelId,\n n,\n prompt,\n files,\n operation,\n options: quiveraiOptions ?? {},\n });\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n const combinedHeaders = combineHeaders(this.config.headers?.(), headers);\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}${getOperationPath(operation)}`,\n headers: combinedHeaders,\n body,\n failedResponseHandler: quiveraiFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n svgGenerationResponseSchema,\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const encoder = new TextEncoder();\n const images = response.data.map(image => encoder.encode(image.svg));\n\n return {\n images,\n warnings,\n providerMetadata: {\n quiverai: {\n images: response.data.map((image, index) => ({\n index,\n mimeType: image.mime_type,\n })),\n },\n },\n response: {\n timestamp: response.created\n ? new Date(response.created * 1000)\n : currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n ...(response.usage && {\n usage: {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n totalTokens: response.usage.total_tokens,\n },\n }),\n };\n }\n}\n\nfunction getOperationPath(operation: QuiverAIOperation) {\n return operation === 'generate'\n ? '/svgs/generations'\n : '/svgs/vectorizations';\n}\n\nfunction getGenerateReferenceLimit(modelId: string) {\n return modelId === 'arrow-1.1-max' ? 16 : 4;\n}\n\nfunction toQuiverAIImageReference(image: ImageModelV3File) {\n if (image.type === 'url') {\n return { url: image.url };\n }\n return {\n base64:\n typeof image.data === 'string'\n ? image.data\n : convertUint8ArrayToBase64(image.data),\n };\n}\n\nfunction buildRequestBody({\n modelId,\n n,\n prompt,\n files,\n operation,\n options,\n}: {\n modelId: string;\n n: number;\n prompt: string | undefined;\n files: ImageModelV3File[] | undefined;\n operation: QuiverAIOperation;\n options: QuiverAIImageModelOptions;\n}) {\n const sharedOptions = {\n temperature: options.temperature,\n top_p: options.topP,\n presence_penalty: options.presencePenalty,\n max_output_tokens: options.maxOutputTokens,\n stream: false as const,\n };\n\n if (operation === 'generate') {\n if (prompt == null || prompt.trim().length === 0) {\n throw new InvalidArgumentError({\n argument: 'prompt',\n message:\n 'QuiverAI image generation requires a non-empty prompt for generateImage.',\n });\n }\n\n const references = files?.map(toQuiverAIImageReference);\n const maxReferences = getGenerateReferenceLimit(modelId);\n\n if (references != null && references.length > maxReferences) {\n throw new InvalidArgumentError({\n argument: 'files',\n message: `QuiverAI generate supports up to ${maxReferences} reference images for model \"${modelId}\".`,\n });\n }\n\n return {\n model: modelId,\n n,\n prompt,\n ...sharedOptions,\n instructions: options.instructions,\n references,\n };\n }\n\n if (files == null || files.length === 0) {\n throw new InvalidArgumentError({\n argument: 'files',\n message:\n 'QuiverAI vectorize requires an input image. Pass an image in the generateImage prompt and set providerOptions.quiverai.operation to \"vectorize\".',\n });\n }\n\n if (files.length > 1) {\n throw new InvalidArgumentError({\n argument: 'files',\n message: 'QuiverAI vectorize accepts a single input image.',\n });\n }\n\n return {\n model: modelId,\n n,\n image: toQuiverAIImageReference(files[0]),\n ...sharedOptions,\n auto_crop: options.autoCrop,\n target_size: options.targetSize,\n };\n}\n\nfunction collectWarnings({\n size,\n aspectRatio,\n seed,\n mask,\n}: {\n size: `${number}x${number}` | undefined;\n aspectRatio: `${number}:${number}` | undefined;\n seed: number | undefined;\n mask: ImageModelV3File | undefined;\n}): SharedV3Warning[] {\n const warnings: SharedV3Warning[] = [];\n\n if (size != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'size',\n details:\n 'QuiverAI SVG generation does not support the `size` option. The setting was ignored.',\n });\n }\n\n if (aspectRatio != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'aspectRatio',\n details:\n 'QuiverAI SVG generation does not support the `aspectRatio` option. The setting was ignored.',\n });\n }\n\n if (seed != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'seed',\n details:\n 'QuiverAI SVG generation does not support the `seed` option. The setting was ignored.',\n });\n }\n\n if (mask != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'mask',\n details:\n 'QuiverAI SVG generation does not support masks. The mask was ignored.',\n });\n }\n\n return warnings;\n}\n\n// Limited schemas focused on what is needed for the implementation.\n// This approach limits breakages when the API changes and keeps the\n// response handler efficient.\nconst svgUsageSchema = z.object({\n total_tokens: z.number().int().nonnegative(),\n input_tokens: z.number().int().nonnegative(),\n output_tokens: z.number().int().nonnegative(),\n});\n\nconst svgDocumentSchema = z.object({\n svg: z.string().min(1),\n mime_type: z.literal('image/svg+xml'),\n});\n\nconst svgGenerationResponseSchema = z.object({\n id: z.string().min(1),\n created: z.number().int().nonnegative(),\n data: z.array(svgDocumentSchema).min(1),\n usage: svgUsageSchema.nullish(),\n});\n\nconst quiveraiErrorSchema = z.object({\n status: z.number().int(),\n code: z.string().min(1),\n message: z.string().min(1),\n request_id: z.string().min(1),\n});\n\nexport const quiveraiFailedResponseHandler = createJsonErrorResponseHandler({\n errorSchema: quiveraiErrorSchema,\n errorToMessage: error => error.message,\n isRetryable: response => response.status === 429 || response.status >= 500,\n});\n","import {\n lazySchema,\n zodSchema,\n type InferSchema,\n} from '@ai-sdk/provider-utils';\nimport { z } from 'zod/v4';\n\n/**\n * Provider options schema for QuiverAI image generation.\n *\n * @see https://quiver.ai/\n */\nexport const quiveraiImageModelOptionsSchema = lazySchema(() =>\n zodSchema(\n z.object({\n /**\n * The operation to perform. Defaults to `generate`.\n *\n * - `generate`: Text-to-SVG generation. Requires `prompt`.\n * - `vectorize`: Convert an input raster image into an SVG. Requires a\n * single image in `prompt.images` / `files`.\n */\n operation: z.enum(['generate', 'vectorize']).optional(),\n\n /**\n * Extra style guidance for prompt-based generation.\n */\n instructions: z.string().min(1).optional(),\n\n /**\n * Sampling temperature (0-2).\n */\n temperature: z.number().min(0).max(2).optional(),\n\n /**\n * Nucleus sampling top-p (0-1).\n */\n topP: z.number().min(0).max(1).optional(),\n\n /**\n * Presence penalty (-2 to 2).\n */\n presencePenalty: z.number().min(-2).max(2).nullable().optional(),\n\n /**\n * Maximum number of output tokens (1 - 131072).\n */\n maxOutputTokens: z.number().int().min(1).max(131072).optional(),\n\n /**\n * Whether to auto-crop the input image before vectorization.\n * Only used when `operation` is `vectorize`.\n */\n autoCrop: z.boolean().optional(),\n\n /**\n * Target canvas size in pixels for vectorization (128 - 4096).\n * Only used when `operation` is `vectorize`.\n */\n targetSize: z.number().int().min(128).max(4096).optional(),\n }),\n ),\n);\n\nexport type QuiverAIImageModelOptions = InferSchema<\n typeof quiveraiImageModelOptionsSchema\n>;\n","// Version string of this package injected at build time.\ndeclare const __PACKAGE_VERSION__: string | undefined;\nexport const VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n"],"mappings":"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","names":["z","z"]}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: QuiverAI
|
|
3
|
+
description: Learn how to use QuiverAI models with the AI SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# QuiverAI Provider
|
|
7
|
+
|
|
8
|
+
[QuiverAI](https://quiver.ai/) generates SVG documents directly from text prompts and can also vectorize raster images into clean SVGs. The QuiverAI provider for the AI SDK exposes both operations through `generateImage`.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
The QuiverAI provider is available via the `@ai-sdk/quiverai` module. You can install it with
|
|
13
|
+
|
|
14
|
+
<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
|
|
15
|
+
<Tab>
|
|
16
|
+
<Snippet text="pnpm add @ai-sdk/quiverai" dark />
|
|
17
|
+
</Tab>
|
|
18
|
+
<Tab>
|
|
19
|
+
<Snippet text="npm install @ai-sdk/quiverai" dark />
|
|
20
|
+
</Tab>
|
|
21
|
+
<Tab>
|
|
22
|
+
<Snippet text="yarn add @ai-sdk/quiverai" dark />
|
|
23
|
+
</Tab>
|
|
24
|
+
|
|
25
|
+
<Tab>
|
|
26
|
+
<Snippet text="bun add @ai-sdk/quiverai" dark />
|
|
27
|
+
</Tab>
|
|
28
|
+
</Tabs>
|
|
29
|
+
|
|
30
|
+
## Provider Instance
|
|
31
|
+
|
|
32
|
+
You can import the default provider instance `quiverai` from `@ai-sdk/quiverai`:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { quiverai } from '@ai-sdk/quiverai';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you need a customized setup, you can import `createQuiverAI` and create a provider instance with your settings:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createQuiverAI } from '@ai-sdk/quiverai';
|
|
42
|
+
|
|
43
|
+
const quiverai = createQuiverAI({
|
|
44
|
+
apiKey: 'your-api-key', // optional, defaults to QUIVERAI_API_KEY environment variable
|
|
45
|
+
baseURL: 'custom-url', // optional, defaults to QUIVERAI_BASE_URL or https://api.quiver.ai/v1
|
|
46
|
+
headers: {
|
|
47
|
+
/* custom headers */
|
|
48
|
+
}, // optional
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You can use the following optional settings to customize the QuiverAI provider instance:
|
|
53
|
+
|
|
54
|
+
- **baseURL** _string_
|
|
55
|
+
|
|
56
|
+
Use a different URL prefix for API calls, e.g. to use proxy servers.
|
|
57
|
+
The default prefix is `https://api.quiver.ai/v1`. It also reads `QUIVERAI_BASE_URL` from the environment.
|
|
58
|
+
|
|
59
|
+
- **apiKey** _string_
|
|
60
|
+
|
|
61
|
+
API key that is sent as a `Bearer` token in the `Authorization` header.
|
|
62
|
+
It defaults to the `QUIVERAI_API_KEY` environment variable.
|
|
63
|
+
|
|
64
|
+
- **headers** _Record<string,string>_
|
|
65
|
+
|
|
66
|
+
Custom headers to include in the requests.
|
|
67
|
+
|
|
68
|
+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
|
|
69
|
+
|
|
70
|
+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
|
|
71
|
+
You can use it as a middleware to intercept requests,
|
|
72
|
+
or to provide a custom fetch implementation for e.g. testing.
|
|
73
|
+
|
|
74
|
+
## Image Models
|
|
75
|
+
|
|
76
|
+
You can create QuiverAI image models using the `.image()` factory method.
|
|
77
|
+
For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
|
|
78
|
+
|
|
79
|
+
### Basic Usage
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { quiverai } from '@ai-sdk/quiverai';
|
|
83
|
+
import { generateImage } from 'ai';
|
|
84
|
+
import fs from 'fs';
|
|
85
|
+
|
|
86
|
+
const { image } = await generateImage({
|
|
87
|
+
model: quiverai.image('arrow-1.1'),
|
|
88
|
+
prompt: 'A logo for the next AI Design startup',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const filename = `image-${Date.now()}.svg`;
|
|
92
|
+
fs.writeFileSync(filename, image.uint8Array);
|
|
93
|
+
console.log(`Saved SVG to ${filename}`);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
QuiverAI returns SVG documents. The generated SVG bytes are available through `result.image.uint8Array` (or `result.images` when generating multiple).
|
|
97
|
+
|
|
98
|
+
### Model Capabilities
|
|
99
|
+
|
|
100
|
+
QuiverAI exposes three Arrow models:
|
|
101
|
+
|
|
102
|
+
| Model | Description |
|
|
103
|
+
| --------------- | -------------------------------------------------------------------------------- |
|
|
104
|
+
| `arrow-1` | Base text-to-SVG model. Accepts up to 4 reference images. |
|
|
105
|
+
| `arrow-1.1` | Improved text-to-SVG model. Accepts up to 4 reference images. |
|
|
106
|
+
| `arrow-1.1-max` | Higher quality variant with extended context. Accepts up to 16 reference images. |
|
|
107
|
+
|
|
108
|
+
### Provider Options
|
|
109
|
+
|
|
110
|
+
You can fine-tune the request using `providerOptions.quiverai`:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { quiverai, type QuiverAIImageModelOptions } from '@ai-sdk/quiverai';
|
|
114
|
+
import { generateImage } from 'ai';
|
|
115
|
+
|
|
116
|
+
await generateImage({
|
|
117
|
+
model: quiverai.image('arrow-1.1'),
|
|
118
|
+
prompt: 'A geometric unicorn icon',
|
|
119
|
+
providerOptions: {
|
|
120
|
+
quiverai: {
|
|
121
|
+
instructions: 'Use a flat monochrome style with clean geometry.',
|
|
122
|
+
temperature: 0.4,
|
|
123
|
+
topP: 0.95,
|
|
124
|
+
maxOutputTokens: 4096,
|
|
125
|
+
} satisfies QuiverAIImageModelOptions,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Supported options:
|
|
131
|
+
|
|
132
|
+
- **operation** _'generate' | 'vectorize'_
|
|
133
|
+
|
|
134
|
+
Choose between text-to-SVG generation (`generate`, default) and image-to-SVG vectorization (`vectorize`).
|
|
135
|
+
|
|
136
|
+
- **instructions** _string_
|
|
137
|
+
|
|
138
|
+
Extra style guidance for prompt-based generation.
|
|
139
|
+
|
|
140
|
+
- **temperature** _number_ (0-2)
|
|
141
|
+
|
|
142
|
+
Sampling temperature.
|
|
143
|
+
|
|
144
|
+
- **topP** _number_ (0-1)
|
|
145
|
+
|
|
146
|
+
Nucleus sampling top-p value.
|
|
147
|
+
|
|
148
|
+
- **presencePenalty** _number_ (-2 to 2)
|
|
149
|
+
|
|
150
|
+
Presence penalty.
|
|
151
|
+
|
|
152
|
+
- **maxOutputTokens** _number_ (1-131072)
|
|
153
|
+
|
|
154
|
+
Maximum number of output tokens.
|
|
155
|
+
|
|
156
|
+
- **autoCrop** _boolean_
|
|
157
|
+
|
|
158
|
+
When vectorizing, automatically crop the input image. Only used with `operation: 'vectorize'`.
|
|
159
|
+
|
|
160
|
+
- **targetSize** _number_ (128-4096)
|
|
161
|
+
|
|
162
|
+
When vectorizing, target canvas size in pixels. Only used with `operation: 'vectorize'`.
|
|
163
|
+
|
|
164
|
+
### Reference Images
|
|
165
|
+
|
|
166
|
+
Pass reference images through `prompt.images`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
await generateImage({
|
|
170
|
+
model: quiverai.image('arrow-1.1'),
|
|
171
|
+
prompt: {
|
|
172
|
+
text: 'A geometric unicorn icon',
|
|
173
|
+
images: ['https://example.com/reference-1.png'],
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`arrow-1` and `arrow-1.1` accept up to 4 reference images. `arrow-1.1-max` accepts up to 16.
|
|
179
|
+
|
|
180
|
+
### Vectorizing a Raster Image
|
|
181
|
+
|
|
182
|
+
Set `operation` to `vectorize` and pass a single image in `prompt.images`:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { quiverai, type QuiverAIImageModelOptions } from '@ai-sdk/quiverai';
|
|
186
|
+
import { generateImage } from 'ai';
|
|
187
|
+
import fs from 'fs';
|
|
188
|
+
|
|
189
|
+
const { image } = await generateImage({
|
|
190
|
+
model: quiverai.image('arrow-1.1'),
|
|
191
|
+
prompt: {
|
|
192
|
+
images: [fs.readFileSync('./logo.png')],
|
|
193
|
+
},
|
|
194
|
+
providerOptions: {
|
|
195
|
+
quiverai: {
|
|
196
|
+
operation: 'vectorize',
|
|
197
|
+
autoCrop: true,
|
|
198
|
+
targetSize: 1024,
|
|
199
|
+
} satisfies QuiverAIImageModelOptions,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
fs.writeFileSync('logo.svg', image.uint8Array);
|
|
204
|
+
```
|
package/package.json
CHANGED
|
@@ -1 +1,76 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-sdk/quiverai",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**/*",
|
|
11
|
+
"docs/**/*",
|
|
12
|
+
"src",
|
|
13
|
+
"!src/**/*.test.ts",
|
|
14
|
+
"!src/**/*.test-d.ts",
|
|
15
|
+
"!src/**/__snapshots__",
|
|
16
|
+
"!src/**/__fixtures__",
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"directories": {
|
|
21
|
+
"doc": "./docs"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json",
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@ai-sdk/provider": "3.0.10",
|
|
33
|
+
"@ai-sdk/provider-utils": "4.0.28"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "20.17.24",
|
|
37
|
+
"tsup": "^8.5.1",
|
|
38
|
+
"typescript": "5.8.3",
|
|
39
|
+
"zod": "3.25.76",
|
|
40
|
+
"@ai-sdk/test-server": "1.0.5",
|
|
41
|
+
"@vercel/ai-tsconfig": "0.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"zod": "^3.25.76 || ^4.1.8"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"provenance": true
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://ai-sdk.dev/docs",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/vercel/ai",
|
|
57
|
+
"directory": "packages/quiverai"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/vercel/ai/issues"
|
|
61
|
+
},
|
|
62
|
+
"keywords": [
|
|
63
|
+
"ai"
|
|
64
|
+
],
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
67
|
+
"build:watch": "pnpm clean && tsup --watch",
|
|
68
|
+
"clean": "del-cli dist docs *.tsbuildinfo",
|
|
69
|
+
"type-check": "tsc --build",
|
|
70
|
+
"test": "pnpm test:node && pnpm test:edge",
|
|
71
|
+
"test:update": "pnpm test:node -u",
|
|
72
|
+
"test:watch": "vitest --config vitest.node.config.js",
|
|
73
|
+
"test:edge": "vitest --config vitest.edge.config.js --run",
|
|
74
|
+
"test:node": "vitest --config vitest.node.config.js --run"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createQuiverAI, quiverai } from './quiverai-provider';
|
|
2
|
+
export type {
|
|
3
|
+
QuiverAIProvider,
|
|
4
|
+
QuiverAIProviderSettings,
|
|
5
|
+
} from './quiverai-provider';
|
|
6
|
+
export type { QuiverAIImageModelId } from './quiverai-image-settings';
|
|
7
|
+
export type { QuiverAIImageModelOptions } from './quiverai-image-model-options';
|
|
8
|
+
export { VERSION } from './version';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lazySchema,
|
|
3
|
+
zodSchema,
|
|
4
|
+
type InferSchema,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
6
|
+
import { z } from 'zod/v4';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Provider options schema for QuiverAI image generation.
|
|
10
|
+
*
|
|
11
|
+
* @see https://quiver.ai/
|
|
12
|
+
*/
|
|
13
|
+
export const quiveraiImageModelOptionsSchema = lazySchema(() =>
|
|
14
|
+
zodSchema(
|
|
15
|
+
z.object({
|
|
16
|
+
/**
|
|
17
|
+
* The operation to perform. Defaults to `generate`.
|
|
18
|
+
*
|
|
19
|
+
* - `generate`: Text-to-SVG generation. Requires `prompt`.
|
|
20
|
+
* - `vectorize`: Convert an input raster image into an SVG. Requires a
|
|
21
|
+
* single image in `prompt.images` / `files`.
|
|
22
|
+
*/
|
|
23
|
+
operation: z.enum(['generate', 'vectorize']).optional(),
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extra style guidance for prompt-based generation.
|
|
27
|
+
*/
|
|
28
|
+
instructions: z.string().min(1).optional(),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sampling temperature (0-2).
|
|
32
|
+
*/
|
|
33
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Nucleus sampling top-p (0-1).
|
|
37
|
+
*/
|
|
38
|
+
topP: z.number().min(0).max(1).optional(),
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Presence penalty (-2 to 2).
|
|
42
|
+
*/
|
|
43
|
+
presencePenalty: z.number().min(-2).max(2).nullable().optional(),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Maximum number of output tokens (1 - 131072).
|
|
47
|
+
*/
|
|
48
|
+
maxOutputTokens: z.number().int().min(1).max(131072).optional(),
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether to auto-crop the input image before vectorization.
|
|
52
|
+
* Only used when `operation` is `vectorize`.
|
|
53
|
+
*/
|
|
54
|
+
autoCrop: z.boolean().optional(),
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Target canvas size in pixels for vectorization (128 - 4096).
|
|
58
|
+
* Only used when `operation` is `vectorize`.
|
|
59
|
+
*/
|
|
60
|
+
targetSize: z.number().int().min(128).max(4096).optional(),
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
export type QuiverAIImageModelOptions = InferSchema<
|
|
66
|
+
typeof quiveraiImageModelOptionsSchema
|
|
67
|
+
>;
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InvalidArgumentError,
|
|
3
|
+
type ImageModelV3,
|
|
4
|
+
type ImageModelV3CallOptions,
|
|
5
|
+
type ImageModelV3File,
|
|
6
|
+
type SharedV3Warning,
|
|
7
|
+
} from '@ai-sdk/provider';
|
|
8
|
+
import {
|
|
9
|
+
combineHeaders,
|
|
10
|
+
convertUint8ArrayToBase64,
|
|
11
|
+
createJsonErrorResponseHandler,
|
|
12
|
+
createJsonResponseHandler,
|
|
13
|
+
parseProviderOptions,
|
|
14
|
+
postJsonToApi,
|
|
15
|
+
type FetchFunction,
|
|
16
|
+
} from '@ai-sdk/provider-utils';
|
|
17
|
+
import { z } from 'zod/v4';
|
|
18
|
+
import {
|
|
19
|
+
quiveraiImageModelOptionsSchema,
|
|
20
|
+
type QuiverAIImageModelOptions,
|
|
21
|
+
} from './quiverai-image-model-options';
|
|
22
|
+
import type {
|
|
23
|
+
QuiverAIImageModelId,
|
|
24
|
+
QuiverAIOperation,
|
|
25
|
+
} from './quiverai-image-settings';
|
|
26
|
+
|
|
27
|
+
interface QuiverAIImageModelConfig {
|
|
28
|
+
provider: string;
|
|
29
|
+
baseURL: string;
|
|
30
|
+
headers?: () => Record<string, string>;
|
|
31
|
+
fetch?: FetchFunction;
|
|
32
|
+
_internal?: {
|
|
33
|
+
currentDate?: () => Date;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class QuiverAIImageModel implements ImageModelV3 {
|
|
38
|
+
readonly specificationVersion = 'v3';
|
|
39
|
+
readonly maxImagesPerCall = 16;
|
|
40
|
+
|
|
41
|
+
get provider(): string {
|
|
42
|
+
return this.config.provider;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
readonly modelId: QuiverAIImageModelId,
|
|
47
|
+
private readonly config: QuiverAIImageModelConfig,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
async doGenerate({
|
|
51
|
+
prompt,
|
|
52
|
+
n,
|
|
53
|
+
files,
|
|
54
|
+
mask,
|
|
55
|
+
size,
|
|
56
|
+
aspectRatio,
|
|
57
|
+
seed,
|
|
58
|
+
providerOptions,
|
|
59
|
+
headers,
|
|
60
|
+
abortSignal,
|
|
61
|
+
}: ImageModelV3CallOptions): Promise<
|
|
62
|
+
Awaited<ReturnType<ImageModelV3['doGenerate']>>
|
|
63
|
+
> {
|
|
64
|
+
const quiveraiOptions = await parseProviderOptions({
|
|
65
|
+
provider: 'quiverai',
|
|
66
|
+
providerOptions,
|
|
67
|
+
schema: quiveraiImageModelOptionsSchema,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const operation: QuiverAIOperation =
|
|
71
|
+
quiveraiOptions?.operation ?? 'generate';
|
|
72
|
+
|
|
73
|
+
const warnings = collectWarnings({ size, aspectRatio, seed, mask });
|
|
74
|
+
|
|
75
|
+
const body = buildRequestBody({
|
|
76
|
+
modelId: this.modelId,
|
|
77
|
+
n,
|
|
78
|
+
prompt,
|
|
79
|
+
files,
|
|
80
|
+
operation,
|
|
81
|
+
options: quiveraiOptions ?? {},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
85
|
+
const combinedHeaders = combineHeaders(this.config.headers?.(), headers);
|
|
86
|
+
|
|
87
|
+
const { value: response, responseHeaders } = await postJsonToApi({
|
|
88
|
+
url: `${this.config.baseURL}${getOperationPath(operation)}`,
|
|
89
|
+
headers: combinedHeaders,
|
|
90
|
+
body,
|
|
91
|
+
failedResponseHandler: quiveraiFailedResponseHandler,
|
|
92
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
93
|
+
svgGenerationResponseSchema,
|
|
94
|
+
),
|
|
95
|
+
abortSignal,
|
|
96
|
+
fetch: this.config.fetch,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const encoder = new TextEncoder();
|
|
100
|
+
const images = response.data.map(image => encoder.encode(image.svg));
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
images,
|
|
104
|
+
warnings,
|
|
105
|
+
providerMetadata: {
|
|
106
|
+
quiverai: {
|
|
107
|
+
images: response.data.map((image, index) => ({
|
|
108
|
+
index,
|
|
109
|
+
mimeType: image.mime_type,
|
|
110
|
+
})),
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
response: {
|
|
114
|
+
timestamp: response.created
|
|
115
|
+
? new Date(response.created * 1000)
|
|
116
|
+
: currentDate,
|
|
117
|
+
modelId: this.modelId,
|
|
118
|
+
headers: responseHeaders,
|
|
119
|
+
},
|
|
120
|
+
...(response.usage && {
|
|
121
|
+
usage: {
|
|
122
|
+
inputTokens: response.usage.input_tokens,
|
|
123
|
+
outputTokens: response.usage.output_tokens,
|
|
124
|
+
totalTokens: response.usage.total_tokens,
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getOperationPath(operation: QuiverAIOperation) {
|
|
132
|
+
return operation === 'generate'
|
|
133
|
+
? '/svgs/generations'
|
|
134
|
+
: '/svgs/vectorizations';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getGenerateReferenceLimit(modelId: string) {
|
|
138
|
+
return modelId === 'arrow-1.1-max' ? 16 : 4;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function toQuiverAIImageReference(image: ImageModelV3File) {
|
|
142
|
+
if (image.type === 'url') {
|
|
143
|
+
return { url: image.url };
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
base64:
|
|
147
|
+
typeof image.data === 'string'
|
|
148
|
+
? image.data
|
|
149
|
+
: convertUint8ArrayToBase64(image.data),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function buildRequestBody({
|
|
154
|
+
modelId,
|
|
155
|
+
n,
|
|
156
|
+
prompt,
|
|
157
|
+
files,
|
|
158
|
+
operation,
|
|
159
|
+
options,
|
|
160
|
+
}: {
|
|
161
|
+
modelId: string;
|
|
162
|
+
n: number;
|
|
163
|
+
prompt: string | undefined;
|
|
164
|
+
files: ImageModelV3File[] | undefined;
|
|
165
|
+
operation: QuiverAIOperation;
|
|
166
|
+
options: QuiverAIImageModelOptions;
|
|
167
|
+
}) {
|
|
168
|
+
const sharedOptions = {
|
|
169
|
+
temperature: options.temperature,
|
|
170
|
+
top_p: options.topP,
|
|
171
|
+
presence_penalty: options.presencePenalty,
|
|
172
|
+
max_output_tokens: options.maxOutputTokens,
|
|
173
|
+
stream: false as const,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if (operation === 'generate') {
|
|
177
|
+
if (prompt == null || prompt.trim().length === 0) {
|
|
178
|
+
throw new InvalidArgumentError({
|
|
179
|
+
argument: 'prompt',
|
|
180
|
+
message:
|
|
181
|
+
'QuiverAI image generation requires a non-empty prompt for generateImage.',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const references = files?.map(toQuiverAIImageReference);
|
|
186
|
+
const maxReferences = getGenerateReferenceLimit(modelId);
|
|
187
|
+
|
|
188
|
+
if (references != null && references.length > maxReferences) {
|
|
189
|
+
throw new InvalidArgumentError({
|
|
190
|
+
argument: 'files',
|
|
191
|
+
message: `QuiverAI generate supports up to ${maxReferences} reference images for model "${modelId}".`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
model: modelId,
|
|
197
|
+
n,
|
|
198
|
+
prompt,
|
|
199
|
+
...sharedOptions,
|
|
200
|
+
instructions: options.instructions,
|
|
201
|
+
references,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (files == null || files.length === 0) {
|
|
206
|
+
throw new InvalidArgumentError({
|
|
207
|
+
argument: 'files',
|
|
208
|
+
message:
|
|
209
|
+
'QuiverAI vectorize requires an input image. Pass an image in the generateImage prompt and set providerOptions.quiverai.operation to "vectorize".',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (files.length > 1) {
|
|
214
|
+
throw new InvalidArgumentError({
|
|
215
|
+
argument: 'files',
|
|
216
|
+
message: 'QuiverAI vectorize accepts a single input image.',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
model: modelId,
|
|
222
|
+
n,
|
|
223
|
+
image: toQuiverAIImageReference(files[0]),
|
|
224
|
+
...sharedOptions,
|
|
225
|
+
auto_crop: options.autoCrop,
|
|
226
|
+
target_size: options.targetSize,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function collectWarnings({
|
|
231
|
+
size,
|
|
232
|
+
aspectRatio,
|
|
233
|
+
seed,
|
|
234
|
+
mask,
|
|
235
|
+
}: {
|
|
236
|
+
size: `${number}x${number}` | undefined;
|
|
237
|
+
aspectRatio: `${number}:${number}` | undefined;
|
|
238
|
+
seed: number | undefined;
|
|
239
|
+
mask: ImageModelV3File | undefined;
|
|
240
|
+
}): SharedV3Warning[] {
|
|
241
|
+
const warnings: SharedV3Warning[] = [];
|
|
242
|
+
|
|
243
|
+
if (size != null) {
|
|
244
|
+
warnings.push({
|
|
245
|
+
type: 'unsupported',
|
|
246
|
+
feature: 'size',
|
|
247
|
+
details:
|
|
248
|
+
'QuiverAI SVG generation does not support the `size` option. The setting was ignored.',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (aspectRatio != null) {
|
|
253
|
+
warnings.push({
|
|
254
|
+
type: 'unsupported',
|
|
255
|
+
feature: 'aspectRatio',
|
|
256
|
+
details:
|
|
257
|
+
'QuiverAI SVG generation does not support the `aspectRatio` option. The setting was ignored.',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (seed != null) {
|
|
262
|
+
warnings.push({
|
|
263
|
+
type: 'unsupported',
|
|
264
|
+
feature: 'seed',
|
|
265
|
+
details:
|
|
266
|
+
'QuiverAI SVG generation does not support the `seed` option. The setting was ignored.',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (mask != null) {
|
|
271
|
+
warnings.push({
|
|
272
|
+
type: 'unsupported',
|
|
273
|
+
feature: 'mask',
|
|
274
|
+
details:
|
|
275
|
+
'QuiverAI SVG generation does not support masks. The mask was ignored.',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return warnings;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Limited schemas focused on what is needed for the implementation.
|
|
283
|
+
// This approach limits breakages when the API changes and keeps the
|
|
284
|
+
// response handler efficient.
|
|
285
|
+
const svgUsageSchema = z.object({
|
|
286
|
+
total_tokens: z.number().int().nonnegative(),
|
|
287
|
+
input_tokens: z.number().int().nonnegative(),
|
|
288
|
+
output_tokens: z.number().int().nonnegative(),
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const svgDocumentSchema = z.object({
|
|
292
|
+
svg: z.string().min(1),
|
|
293
|
+
mime_type: z.literal('image/svg+xml'),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const svgGenerationResponseSchema = z.object({
|
|
297
|
+
id: z.string().min(1),
|
|
298
|
+
created: z.number().int().nonnegative(),
|
|
299
|
+
data: z.array(svgDocumentSchema).min(1),
|
|
300
|
+
usage: svgUsageSchema.nullish(),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const quiveraiErrorSchema = z.object({
|
|
304
|
+
status: z.number().int(),
|
|
305
|
+
code: z.string().min(1),
|
|
306
|
+
message: z.string().min(1),
|
|
307
|
+
request_id: z.string().min(1),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
export const quiveraiFailedResponseHandler = createJsonErrorResponseHandler({
|
|
311
|
+
errorSchema: quiveraiErrorSchema,
|
|
312
|
+
errorToMessage: error => error.message,
|
|
313
|
+
isRetryable: response => response.status === 429 || response.status >= 500,
|
|
314
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuiverAI image model identifier.
|
|
3
|
+
*
|
|
4
|
+
* @see https://quiver.ai/
|
|
5
|
+
*/
|
|
6
|
+
export type QuiverAIImageModelId =
|
|
7
|
+
| 'arrow-1'
|
|
8
|
+
| 'arrow-1.1'
|
|
9
|
+
| 'arrow-1.1-max'
|
|
10
|
+
| (string & {});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* QuiverAI image operation:
|
|
14
|
+
* - `generate`: Text-to-SVG generation (default).
|
|
15
|
+
* - `vectorize`: Convert a raster image into an SVG.
|
|
16
|
+
*/
|
|
17
|
+
export type QuiverAIOperation = 'generate' | 'vectorize';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoSuchModelError,
|
|
3
|
+
type ImageModelV3,
|
|
4
|
+
type ProviderV3,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
loadApiKey,
|
|
8
|
+
loadOptionalSetting,
|
|
9
|
+
withoutTrailingSlash,
|
|
10
|
+
withUserAgentSuffix,
|
|
11
|
+
type FetchFunction,
|
|
12
|
+
} from '@ai-sdk/provider-utils';
|
|
13
|
+
import { QuiverAIImageModel } from './quiverai-image-model';
|
|
14
|
+
import type { QuiverAIImageModelId } from './quiverai-image-settings';
|
|
15
|
+
import { VERSION } from './version';
|
|
16
|
+
|
|
17
|
+
export interface QuiverAIProviderSettings {
|
|
18
|
+
/**
|
|
19
|
+
* QuiverAI API key. Default value is taken from the `QUIVERAI_API_KEY`
|
|
20
|
+
* environment variable.
|
|
21
|
+
*/
|
|
22
|
+
apiKey?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Base URL for the API calls. Defaults to `https://api.quiver.ai/v1` and
|
|
26
|
+
* falls back to the `QUIVERAI_BASE_URL` environment variable.
|
|
27
|
+
*/
|
|
28
|
+
baseURL?: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom headers to include in the requests.
|
|
32
|
+
*/
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom fetch implementation. You can use it as a middleware to intercept
|
|
37
|
+
* requests, or to provide a custom fetch implementation for e.g. testing.
|
|
38
|
+
*/
|
|
39
|
+
fetch?: FetchFunction;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface QuiverAIProvider extends ProviderV3 {
|
|
43
|
+
/**
|
|
44
|
+
* Creates a model for image generation.
|
|
45
|
+
*/
|
|
46
|
+
image(modelId: QuiverAIImageModelId): ImageModelV3;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a model for image generation.
|
|
50
|
+
*/
|
|
51
|
+
imageModel(modelId: QuiverAIImageModelId): ImageModelV3;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated Use `embeddingModel` instead.
|
|
55
|
+
*/
|
|
56
|
+
textEmbeddingModel(modelId: string): never;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const defaultBaseURL = 'https://api.quiver.ai/v1';
|
|
60
|
+
|
|
61
|
+
export function createQuiverAI(
|
|
62
|
+
options: QuiverAIProviderSettings = {},
|
|
63
|
+
): QuiverAIProvider {
|
|
64
|
+
const baseURL =
|
|
65
|
+
withoutTrailingSlash(
|
|
66
|
+
loadOptionalSetting({
|
|
67
|
+
settingValue: options.baseURL,
|
|
68
|
+
environmentVariableName: 'QUIVERAI_BASE_URL',
|
|
69
|
+
}),
|
|
70
|
+
) ?? defaultBaseURL;
|
|
71
|
+
|
|
72
|
+
const getHeaders = () =>
|
|
73
|
+
withUserAgentSuffix(
|
|
74
|
+
{
|
|
75
|
+
Authorization: `Bearer ${loadApiKey({
|
|
76
|
+
apiKey: options.apiKey,
|
|
77
|
+
environmentVariableName: 'QUIVERAI_API_KEY',
|
|
78
|
+
description: 'QuiverAI',
|
|
79
|
+
})}`,
|
|
80
|
+
...options.headers,
|
|
81
|
+
},
|
|
82
|
+
`ai-sdk/quiverai/${VERSION}`,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const createImageModel = (modelId: QuiverAIImageModelId) =>
|
|
86
|
+
new QuiverAIImageModel(modelId, {
|
|
87
|
+
provider: 'quiverai.image',
|
|
88
|
+
baseURL,
|
|
89
|
+
headers: getHeaders,
|
|
90
|
+
fetch: options.fetch,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const embeddingModel = (modelId: string) => {
|
|
94
|
+
throw new NoSuchModelError({
|
|
95
|
+
modelId,
|
|
96
|
+
modelType: 'embeddingModel',
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
specificationVersion: 'v3',
|
|
102
|
+
image: createImageModel,
|
|
103
|
+
imageModel: createImageModel,
|
|
104
|
+
languageModel: (modelId: string) => {
|
|
105
|
+
throw new NoSuchModelError({
|
|
106
|
+
modelId,
|
|
107
|
+
modelType: 'languageModel',
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
embeddingModel,
|
|
111
|
+
textEmbeddingModel: embeddingModel,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const quiverai = createQuiverAI();
|