ai 6.0.279 → 6.0.280
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 +12 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +51 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -24
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.d.mts +3 -2
- package/dist/internal/index.d.ts +3 -2
- package/dist/internal/index.js +8 -5
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +8 -5
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-ai-sdk-core/35-image-generation.mdx +2 -0
- package/docs/07-reference/01-ai-sdk-core/10-generate-image.mdx +2 -1
- package/package.json +4 -4
- package/src/generate-image/generate-image.ts +63 -20
- package/src/util/data-url.ts +6 -1
- package/src/util/prepare-retries.ts +7 -1
- package/src/util/retry-with-exponential-backoff.ts +9 -4
|
@@ -26,11 +26,21 @@ import {
|
|
|
26
26
|
imageMediaTypeSignatures,
|
|
27
27
|
} from '../util/detect-media-type';
|
|
28
28
|
import { prepareRetries } from '../util/prepare-retries';
|
|
29
|
+
import { RetryError } from '../util/retry-error';
|
|
29
30
|
import { VERSION } from '../version';
|
|
30
31
|
import type { GenerateImageResult } from './generate-image-result';
|
|
31
32
|
import { convertDataContentToUint8Array } from '../prompt/data-content';
|
|
32
33
|
import { splitDataUrl } from '../prompt/split-data-url';
|
|
33
34
|
|
|
35
|
+
class RetryableNoImageResultError extends Error {
|
|
36
|
+
constructor() {
|
|
37
|
+
super('No image generated.');
|
|
38
|
+
this.name = 'RetryableNoImageResultError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ImageModelV3Result = Awaited<ReturnType<ImageModelV3['doGenerate']>>;
|
|
43
|
+
|
|
34
44
|
export type GenerateImagePrompt =
|
|
35
45
|
| string
|
|
36
46
|
| {
|
|
@@ -51,7 +61,7 @@ export type GenerateImagePrompt =
|
|
|
51
61
|
* @param seed - Seed for the image generation.
|
|
52
62
|
* @param providerOptions - Additional provider-specific options that are passed through to the provider
|
|
53
63
|
* as body parameters.
|
|
54
|
-
* @param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
|
|
64
|
+
* @param maxRetries - Maximum number of retries per image model call, including retries after unclassified empty responses. Empty responses marked as not retryable by the provider are not retried. Set to 0 to disable retries. Default: 2.
|
|
55
65
|
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
56
66
|
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
57
67
|
*
|
|
@@ -122,7 +132,9 @@ export async function generateImage({
|
|
|
122
132
|
providerOptions?: ProviderOptions;
|
|
123
133
|
|
|
124
134
|
/**
|
|
125
|
-
* Maximum number of retries per image model call
|
|
135
|
+
* Maximum number of retries per image model call, including retries after
|
|
136
|
+
* unclassified empty responses. Empty responses marked as not retryable by
|
|
137
|
+
* the provider are not retried. Set to 0 to disable retries.
|
|
126
138
|
*
|
|
127
139
|
* @default 2
|
|
128
140
|
*/
|
|
@@ -149,6 +161,8 @@ export async function generateImage({
|
|
|
149
161
|
const { retry } = prepareRetries({
|
|
150
162
|
maxRetries: maxRetriesArg,
|
|
151
163
|
abortSignal,
|
|
164
|
+
additionalRetryableError: error =>
|
|
165
|
+
error instanceof RetryableNoImageResultError,
|
|
152
166
|
});
|
|
153
167
|
|
|
154
168
|
// default to 1 if the model has not specified limits on
|
|
@@ -167,26 +181,55 @@ export async function generateImage({
|
|
|
167
181
|
return remainder === 0 ? maxImagesPerCallWithDefault : remainder;
|
|
168
182
|
});
|
|
169
183
|
|
|
170
|
-
const
|
|
171
|
-
callImageCounts.map(async callImageCount =>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
prompt,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
const resultGroups = await Promise.all(
|
|
185
|
+
callImageCounts.map(async callImageCount => {
|
|
186
|
+
const callResults: Array<ImageModelV3Result> = [];
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await retry(async () => {
|
|
190
|
+
const { prompt, files, mask } = normalizePrompt(promptArg);
|
|
191
|
+
|
|
192
|
+
const result = await model.doGenerate({
|
|
193
|
+
prompt,
|
|
194
|
+
files,
|
|
195
|
+
mask,
|
|
196
|
+
n: callImageCount,
|
|
197
|
+
abortSignal,
|
|
198
|
+
headers: headersWithUserAgent,
|
|
199
|
+
size,
|
|
200
|
+
aspectRatio,
|
|
201
|
+
seed,
|
|
202
|
+
providerOptions: providerOptions ?? {},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
callResults.push(result);
|
|
206
|
+
|
|
207
|
+
if (result.images.length === 0 && result.isRetryable !== false) {
|
|
208
|
+
throw new RetryableNoImageResultError();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
186
212
|
});
|
|
187
|
-
|
|
188
|
-
|
|
213
|
+
|
|
214
|
+
return callResults;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const noImageResultError =
|
|
217
|
+
error instanceof RetryableNoImageResultError
|
|
218
|
+
? error
|
|
219
|
+
: RetryError.isInstance(error) &&
|
|
220
|
+
error.lastError instanceof RetryableNoImageResultError
|
|
221
|
+
? error.lastError
|
|
222
|
+
: undefined;
|
|
223
|
+
|
|
224
|
+
if (noImageResultError != null) {
|
|
225
|
+
return callResults;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}),
|
|
189
231
|
);
|
|
232
|
+
const results = resultGroups.flat();
|
|
190
233
|
|
|
191
234
|
// collect result images, warnings, and response metadata
|
|
192
235
|
const images: Array<DefaultGeneratedFile> = [];
|
package/src/util/data-url.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// atob needs to be invoked as a function call, not as a method call.
|
|
2
|
+
// Otherwise Cloudflare will throw a
|
|
3
|
+
// "TypeError: Illegal invocation: function called with incorrect this reference"
|
|
4
|
+
const { atob } = globalThis;
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Converts a data URL of type text/* to a text string.
|
|
3
8
|
*/
|
|
@@ -10,7 +15,7 @@ export function getTextFromDataUrl(dataUrl: string): string {
|
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
try {
|
|
13
|
-
return
|
|
18
|
+
return atob(base64Content);
|
|
14
19
|
} catch (error) {
|
|
15
20
|
throw new Error(`Error decoding data URL`);
|
|
16
21
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
RetryFunction,
|
|
4
|
+
ShouldRetryFunction,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
3
6
|
import { retryWithExponentialBackoffRespectingRetryHeaders } from '../util/retry-with-exponential-backoff';
|
|
4
7
|
|
|
5
8
|
/**
|
|
@@ -8,9 +11,11 @@ import { retryWithExponentialBackoffRespectingRetryHeaders } from '../util/retry
|
|
|
8
11
|
export function prepareRetries({
|
|
9
12
|
maxRetries,
|
|
10
13
|
abortSignal,
|
|
14
|
+
additionalRetryableError,
|
|
11
15
|
}: {
|
|
12
16
|
maxRetries: number | undefined;
|
|
13
17
|
abortSignal: AbortSignal | undefined;
|
|
18
|
+
additionalRetryableError?: ShouldRetryFunction;
|
|
14
19
|
}): {
|
|
15
20
|
maxRetries: number;
|
|
16
21
|
retry: RetryFunction;
|
|
@@ -40,6 +45,7 @@ export function prepareRetries({
|
|
|
40
45
|
retry: retryWithExponentialBackoffRespectingRetryHeaders({
|
|
41
46
|
maxRetries: maxRetriesResult,
|
|
42
47
|
abortSignal,
|
|
48
|
+
additionalRetryableError,
|
|
43
49
|
}),
|
|
44
50
|
};
|
|
45
51
|
}
|
|
@@ -3,6 +3,7 @@ import { GatewayError } from '@ai-sdk/gateway';
|
|
|
3
3
|
import {
|
|
4
4
|
retryWithExponentialBackoff,
|
|
5
5
|
type RetryFunction,
|
|
6
|
+
type ShouldRetryFunction,
|
|
6
7
|
} from '@ai-sdk/provider-utils';
|
|
7
8
|
import { RetryError } from './retry-error';
|
|
8
9
|
|
|
@@ -66,21 +67,25 @@ export const retryWithExponentialBackoffRespectingRetryHeaders = ({
|
|
|
66
67
|
initialDelayInMs = 2000,
|
|
67
68
|
backoffFactor = 2,
|
|
68
69
|
abortSignal,
|
|
70
|
+
additionalRetryableError,
|
|
69
71
|
}: {
|
|
70
72
|
maxRetries?: number;
|
|
71
73
|
initialDelayInMs?: number;
|
|
72
74
|
backoffFactor?: number;
|
|
73
75
|
abortSignal?: AbortSignal;
|
|
76
|
+
additionalRetryableError?: ShouldRetryFunction;
|
|
74
77
|
} = {}): RetryFunction =>
|
|
75
78
|
retryWithExponentialBackoff({
|
|
76
79
|
maxRetries,
|
|
77
80
|
initialDelayInMs,
|
|
78
81
|
backoffFactor,
|
|
79
82
|
abortSignal,
|
|
80
|
-
shouldRetry: error =>
|
|
81
|
-
error instanceof Error &&
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
shouldRetry: async error =>
|
|
84
|
+
(error instanceof Error &&
|
|
85
|
+
((APICallError.isInstance(error) && error.isRetryable === true) ||
|
|
86
|
+
(GatewayError.isInstance(error) && error.isRetryable === true))) ||
|
|
87
|
+
(additionalRetryableError != null &&
|
|
88
|
+
(await additionalRetryableError(error))),
|
|
84
89
|
getDelayInMs: ({ error, exponentialBackoffDelay }) =>
|
|
85
90
|
getRetryDelayInMs({
|
|
86
91
|
error: error as APICallError | GatewayError,
|