ai-retry 0.10.3 → 0.11.0
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/README.md +134 -15
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +90 -55
- package/dist/retryables/index.d.mts +1 -1
- package/dist/{types-TotEvw-5.d.mts → types-D4wXoA4D.d.mts} +67 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -138,6 +138,58 @@ const retryableModel = createRetryable({
|
|
|
138
138
|
|
|
139
139
|
In this example, if the base model fails with code 429 or a service overloaded error, it will retry with `gpt-4-mini` on Azure. In any other error case, it will fallback to `claude-3-haiku-20240307` on Anthropic. If the order would be reversed, the static retryable would catch all errors first, and the dynamic retryable would never be reached.
|
|
140
140
|
|
|
141
|
+
#### Errors vs Results
|
|
142
|
+
|
|
143
|
+
Dynamic retryables can be further divided based on what triggers them:
|
|
144
|
+
|
|
145
|
+
- **Error-based retryables** handle API errors where the request throws an error (e.g., timeouts, rate limits, service unavailable, etc.)
|
|
146
|
+
- **Result-based retryables** handle successful responses that still need retrying (e.g., content filtering, guardrails, etc.)
|
|
147
|
+
|
|
148
|
+
Both types of retryables have the same interface and receive the current attempt as context. You can use the `isErrorAttempt` and `isResultAttempt` type guards to check the type of the current attempt.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { generateText } from 'ai';
|
|
152
|
+
import { createRetryable, isErrorAttempt, isResultAttempt } from 'ai-retry';
|
|
153
|
+
import type { Retryable } from 'ai-retry';
|
|
154
|
+
|
|
155
|
+
// Error-based retryable: handles thrown errors (e.g., timeouts, rate limits)
|
|
156
|
+
const errorBasedRetry: Retryable = (context) => {
|
|
157
|
+
if (isErrorAttempt(context.current)) {
|
|
158
|
+
const { error } = context.current;
|
|
159
|
+
// The request threw an error - e.g., network timeout, 429 rate limit
|
|
160
|
+
console.log('Request failed with error:', error);
|
|
161
|
+
return { model: anthropic('claude-3-haiku-20240307') };
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Result-based retryable: handles successful responses that need retrying
|
|
167
|
+
const resultBasedRetry: Retryable = (context) => {
|
|
168
|
+
if (isResultAttempt(context.current)) {
|
|
169
|
+
const { result } = context.current;
|
|
170
|
+
// The request succeeded, but the response indicates a problem
|
|
171
|
+
if (result.finishReason === 'content-filter') {
|
|
172
|
+
console.log('Content was filtered, trying different model');
|
|
173
|
+
return { model: openai('gpt-4') };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const retryableModel = createRetryable({
|
|
180
|
+
model: azure('gpt-4-mini'),
|
|
181
|
+
retries: [
|
|
182
|
+
// Error-based: catches thrown errors like timeouts, rate limits, etc.
|
|
183
|
+
errorBasedRetry,
|
|
184
|
+
|
|
185
|
+
// Result-based: catches successful responses that need retrying
|
|
186
|
+
resultBasedRetry,
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Result-based retryables are only available for generate calls like `generateText` and `generateObject`. They are not available for streaming calls like `streamText` and `streamObject`.
|
|
192
|
+
|
|
141
193
|
#### Fallbacks
|
|
142
194
|
|
|
143
195
|
If you don't need precise error matching with custom logic and just want to fallback to different models on any error, you can simply provide a list of models.
|
|
@@ -324,11 +376,23 @@ Handle service overload errors (status code 529) by switching to a provider.
|
|
|
324
376
|
import { serviceOverloaded } from 'ai-retry/retryables';
|
|
325
377
|
|
|
326
378
|
const retryableModel = createRetryable({
|
|
327
|
-
model:
|
|
379
|
+
model: anthropic('claude-sonnet-4-0'),
|
|
328
380
|
retries: [
|
|
329
|
-
|
|
381
|
+
// Retry with delay and exponential backoff
|
|
382
|
+
serviceOverloaded(anthropic('claude-sonnet-4-0'), {
|
|
383
|
+
delay: 5_000,
|
|
384
|
+
backoffFactor: 2,
|
|
385
|
+
maxAttempts: 5,
|
|
386
|
+
}),
|
|
387
|
+
// Or switch to a different provider
|
|
388
|
+
serviceOverloaded(openai('gpt-4')),
|
|
330
389
|
],
|
|
331
390
|
});
|
|
391
|
+
|
|
392
|
+
const result = streamText({
|
|
393
|
+
model: retryableModel,
|
|
394
|
+
prompt: 'Write a story about a robot...',
|
|
395
|
+
});
|
|
332
396
|
```
|
|
333
397
|
|
|
334
398
|
#### Service Unavailable
|
|
@@ -537,6 +601,58 @@ const result = await generateText({
|
|
|
537
601
|
|
|
538
602
|
The retry's `providerOptions` will completely replace the original ones during retry attempts. This works for all model types (language and embedding) and all operations (generate, stream, embed).
|
|
539
603
|
|
|
604
|
+
#### Call Options
|
|
605
|
+
|
|
606
|
+
You can override various call options when retrying requests. This is useful for adjusting parameters like temperature, max tokens, or even the prompt itself for retry attempts. Call options are specified in the `options` field of the retry object.
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
const retryableModel = createRetryable({
|
|
610
|
+
model: openai('gpt-4'),
|
|
611
|
+
retries: [
|
|
612
|
+
{
|
|
613
|
+
model: anthropic('claude-3-haiku'),
|
|
614
|
+
options: {
|
|
615
|
+
// Override generation parameters for more deterministic output
|
|
616
|
+
temperature: 0.3,
|
|
617
|
+
topP: 0.9,
|
|
618
|
+
maxOutputTokens: 500,
|
|
619
|
+
// Set a seed for reproducibility
|
|
620
|
+
seed: 42,
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
],
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
The following options can be overridden:
|
|
628
|
+
|
|
629
|
+
> [!NOTE]
|
|
630
|
+
> Override options completely replace the original values (they are not merged). If you don't specify an option, the original value from the request is used.
|
|
631
|
+
|
|
632
|
+
##### Language Model Options
|
|
633
|
+
|
|
634
|
+
| Option | Description |
|
|
635
|
+
|--------|-------------|
|
|
636
|
+
| [`prompt`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#prompt) | Override the entire prompt for the retry |
|
|
637
|
+
| [`temperature`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#temperature) | Temperature setting for controlling randomness |
|
|
638
|
+
| [`topP`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#topp) | Nucleus sampling parameter |
|
|
639
|
+
| [`topK`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#topk) | Top-K sampling parameter |
|
|
640
|
+
| [`maxOutputTokens`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#max-output-tokens) | Maximum number of tokens to generate |
|
|
641
|
+
| [`seed`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#seed) | Random seed for deterministic generation |
|
|
642
|
+
| [`stopSequences`](https://ai-sdk.dev/docs/reference/ai-sdk-types/generate-text#stopsequences) | Stop sequences to end generation |
|
|
643
|
+
| [`presencePenalty`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#presencepenalty) | Presence penalty for reducing repetition |
|
|
644
|
+
| [`frequencyPenalty`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#frequencypenalty) | Frequency penalty for reducing repetition |
|
|
645
|
+
| [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#headers) | Additional HTTP headers |
|
|
646
|
+
| [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-types/generate-text#provideroptions) | Provider-specific options |
|
|
647
|
+
|
|
648
|
+
##### Embedding Model Options
|
|
649
|
+
|
|
650
|
+
| Option | Description |
|
|
651
|
+
|--------|-------------|
|
|
652
|
+
| [`values`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#values) | Override the values to embed |
|
|
653
|
+
| [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#headers) | Additional HTTP headers |
|
|
654
|
+
| [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#provideroptions) | Provider-specific options |
|
|
655
|
+
|
|
540
656
|
#### Logging
|
|
541
657
|
|
|
542
658
|
You can use the following callbacks to log retry attempts and errors:
|
|
@@ -603,7 +719,7 @@ type Retryable = (
|
|
|
603
719
|
|
|
604
720
|
#### `Retry`
|
|
605
721
|
|
|
606
|
-
A `Retry` specifies the model to retry and optional settings
|
|
722
|
+
A `Retry` specifies the model to retry and optional settings. The available options depend on the model type (language model or embedding model).
|
|
607
723
|
|
|
608
724
|
```typescript
|
|
609
725
|
interface Retry {
|
|
@@ -612,18 +728,11 @@ interface Retry {
|
|
|
612
728
|
delay?: number; // Delay in milliseconds before retrying
|
|
613
729
|
backoffFactor?: number; // Multiplier for exponential backoff
|
|
614
730
|
timeout?: number; // Timeout in milliseconds for the retry attempt
|
|
615
|
-
providerOptions?: ProviderOptions; //
|
|
731
|
+
providerOptions?: ProviderOptions; // @deprecated - use options.providerOptions instead
|
|
732
|
+
options?: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions; // Call options to override for this retry
|
|
616
733
|
}
|
|
617
734
|
```
|
|
618
735
|
|
|
619
|
-
**Options:**
|
|
620
|
-
- `model`: The model to use for the retry attempt.
|
|
621
|
-
- `maxAttempts`: Maximum number of times this model can be retried. Default is 1.
|
|
622
|
-
- `delay`: Delay in milliseconds to wait before retrying. The delay respects abort signals from the request.
|
|
623
|
-
- `backoffFactor`: Multiplier for exponential backoff (`delay × backoffFactor^attempt`). If not provided, uses fixed delay.
|
|
624
|
-
- `timeout`: Timeout in milliseconds for creating a fresh `AbortSignal.timeout()` for the retry attempt. This replaces any existing abort signal.
|
|
625
|
-
- `providerOptions`: Provider-specific options that override the original request's provider options during retry attempts.
|
|
626
|
-
|
|
627
736
|
#### `RetryContext`
|
|
628
737
|
|
|
629
738
|
The `RetryContext` object contains information about the current attempt and all previous attempts.
|
|
@@ -637,13 +746,23 @@ interface RetryContext {
|
|
|
637
746
|
|
|
638
747
|
#### `RetryAttempt`
|
|
639
748
|
|
|
640
|
-
A `RetryAttempt` represents a single attempt with a specific model, which can be either an error or a successful result that triggered a retry.
|
|
749
|
+
A `RetryAttempt` represents a single attempt with a specific model, which can be either an error or a successful result that triggered a retry. Each attempt includes the call options that were used for that specific attempt. For retry attempts, this will reflect any overridden options from the retry configuration.
|
|
641
750
|
|
|
642
751
|
```typescript
|
|
643
752
|
// For both language and embedding models
|
|
644
753
|
type RetryAttempt =
|
|
645
|
-
| {
|
|
646
|
-
|
|
754
|
+
| {
|
|
755
|
+
type: 'error';
|
|
756
|
+
error: unknown;
|
|
757
|
+
model: LanguageModelV2 | EmbeddingModelV2;
|
|
758
|
+
options: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions;
|
|
759
|
+
}
|
|
760
|
+
| {
|
|
761
|
+
type: 'result';
|
|
762
|
+
result: LanguageModelV2Generate;
|
|
763
|
+
model: LanguageModelV2;
|
|
764
|
+
options: LanguageModelV2CallOptions;
|
|
765
|
+
};
|
|
647
766
|
|
|
648
767
|
// Note: Result-based retries only apply to language models, not embedding models
|
|
649
768
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as RetryResultAttempt, a as LanguageModel, b as RetryableOptions, c as LanguageModelPrompt, d as LanguageModelStreamPart, f as Retries, g as RetryErrorAttempt, h as RetryContext, i as EmbeddingModelRetryCallOptions, l as LanguageModelRetryCallOptions, m as RetryAttempt, n as EmbeddingModelCallOptions, o as LanguageModelCallOptions, p as Retry, r as EmbeddingModelEmbed, s as LanguageModelGenerate, t as EmbeddingModel, u as LanguageModelStream, v as Retryable, y as RetryableModelOptions } from "./types-D4wXoA4D.mjs";
|
|
2
2
|
import * as _ai_sdk_provider0 from "@ai-sdk/provider";
|
|
3
3
|
|
|
4
4
|
//#region src/create-retryable-model.d.ts
|
|
@@ -70,4 +70,4 @@ declare const isStreamContentPart: (part: LanguageModelStreamPart) => part is _a
|
|
|
70
70
|
*/
|
|
71
71
|
declare const isRetry: <MODEL extends LanguageModel | EmbeddingModel>(value: unknown) => value is Retry<MODEL>;
|
|
72
72
|
//#endregion
|
|
73
|
-
export { EmbeddingModel, EmbeddingModelCallOptions, EmbeddingModelEmbed, LanguageModel, LanguageModelCallOptions, LanguageModelGenerate, LanguageModelStream, LanguageModelStreamPart, Retries, Retry, RetryAttempt, RetryContext, RetryErrorAttempt, RetryResultAttempt, Retryable, RetryableModelOptions, RetryableOptions, createRetryable, getModelKey, isEmbeddingModelV2, isErrorAttempt, isGenerateResult, isLanguageModelV2, isModelV2, isObject, isResultAttempt, isRetry, isStreamContentPart, isStreamResult, isString };
|
|
73
|
+
export { EmbeddingModel, EmbeddingModelCallOptions, EmbeddingModelEmbed, EmbeddingModelRetryCallOptions, LanguageModel, LanguageModelCallOptions, LanguageModelGenerate, LanguageModelPrompt, LanguageModelRetryCallOptions, LanguageModelStream, LanguageModelStreamPart, Retries, Retry, RetryAttempt, RetryContext, RetryErrorAttempt, RetryResultAttempt, Retryable, RetryableModelOptions, RetryableOptions, createRetryable, getModelKey, isEmbeddingModelV2, isErrorAttempt, isGenerateResult, isLanguageModelV2, isModelV2, isObject, isResultAttempt, isRetry, isStreamContentPart, isStreamResult, isString };
|
package/dist/index.mjs
CHANGED
|
@@ -112,6 +112,19 @@ var RetryableEmbeddingModel = class {
|
|
|
112
112
|
return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
|
+
* Get the retry call options overrides from a retry configuration.
|
|
116
|
+
*/
|
|
117
|
+
getRetryCallOptions(callOptions, currentRetry) {
|
|
118
|
+
const retryOptions = currentRetry?.options ?? {};
|
|
119
|
+
return {
|
|
120
|
+
...callOptions,
|
|
121
|
+
values: retryOptions.values ?? callOptions.values,
|
|
122
|
+
headers: retryOptions.headers ?? callOptions.headers,
|
|
123
|
+
providerOptions: retryOptions.providerOptions ?? currentRetry?.providerOptions ?? callOptions.providerOptions,
|
|
124
|
+
abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : callOptions.abortSignal
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
115
128
|
* Execute a function with retry logic for handling errors
|
|
116
129
|
*/
|
|
117
130
|
async withRetry(input) {
|
|
@@ -142,13 +155,17 @@ var RetryableEmbeddingModel = class {
|
|
|
142
155
|
};
|
|
143
156
|
this.options.onRetry?.(context);
|
|
144
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the retry call options overrides for this attempt
|
|
160
|
+
*/
|
|
161
|
+
const retryCallOptions = this.getRetryCallOptions(input.callOptions, currentRetry);
|
|
145
162
|
try {
|
|
146
163
|
return {
|
|
147
|
-
result: await input.fn(
|
|
164
|
+
result: await input.fn(retryCallOptions),
|
|
148
165
|
attempts
|
|
149
166
|
};
|
|
150
167
|
} catch (error) {
|
|
151
|
-
const { retryModel, attempt } = await this.handleError(error, attempts);
|
|
168
|
+
const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
|
|
152
169
|
attempts.push(attempt);
|
|
153
170
|
if (retryModel.delay) {
|
|
154
171
|
/**
|
|
@@ -160,7 +177,7 @@ var RetryableEmbeddingModel = class {
|
|
|
160
177
|
* - Attempt 3: 4000ms
|
|
161
178
|
*/
|
|
162
179
|
const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
|
|
163
|
-
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal:
|
|
180
|
+
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
|
|
164
181
|
}
|
|
165
182
|
this.currentModel = retryModel.model;
|
|
166
183
|
currentRetry = retryModel;
|
|
@@ -170,11 +187,12 @@ var RetryableEmbeddingModel = class {
|
|
|
170
187
|
/**
|
|
171
188
|
* Handle an error and determine if a retry is needed
|
|
172
189
|
*/
|
|
173
|
-
async handleError(error, attempts) {
|
|
190
|
+
async handleError(error, attempts, callOptions) {
|
|
174
191
|
const errorAttempt = {
|
|
175
192
|
type: "error",
|
|
176
193
|
error,
|
|
177
|
-
model: this.currentModel
|
|
194
|
+
model: this.currentModel,
|
|
195
|
+
options: callOptions
|
|
178
196
|
};
|
|
179
197
|
/**
|
|
180
198
|
* Save the current attempt
|
|
@@ -199,7 +217,7 @@ var RetryableEmbeddingModel = class {
|
|
|
199
217
|
attempt: errorAttempt
|
|
200
218
|
};
|
|
201
219
|
}
|
|
202
|
-
async doEmbed(
|
|
220
|
+
async doEmbed(callOptions) {
|
|
203
221
|
/**
|
|
204
222
|
* Always start with the original model
|
|
205
223
|
*/
|
|
@@ -207,17 +225,12 @@ var RetryableEmbeddingModel = class {
|
|
|
207
225
|
/**
|
|
208
226
|
* If retries are disabled, bypass retry machinery entirely
|
|
209
227
|
*/
|
|
210
|
-
if (this.isDisabled()) return this.currentModel.doEmbed(
|
|
228
|
+
if (this.isDisabled()) return this.currentModel.doEmbed(callOptions);
|
|
211
229
|
const { result } = await this.withRetry({
|
|
212
|
-
fn: async (
|
|
213
|
-
|
|
214
|
-
...options,
|
|
215
|
-
providerOptions: currentRetry?.providerOptions ?? options.providerOptions,
|
|
216
|
-
abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : options.abortSignal
|
|
217
|
-
};
|
|
218
|
-
return this.currentModel.doEmbed(callOptions);
|
|
230
|
+
fn: async (retryCallOptions) => {
|
|
231
|
+
return this.currentModel.doEmbed(retryCallOptions);
|
|
219
232
|
},
|
|
220
|
-
|
|
233
|
+
callOptions
|
|
221
234
|
});
|
|
222
235
|
return result;
|
|
223
236
|
}
|
|
@@ -252,6 +265,27 @@ var RetryableLanguageModel = class {
|
|
|
252
265
|
return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
|
|
253
266
|
}
|
|
254
267
|
/**
|
|
268
|
+
* Get the retry call options overrides from a retry configuration.
|
|
269
|
+
*/
|
|
270
|
+
getRetryCallOptions(callOptions, currentRetry) {
|
|
271
|
+
const retryOptions = currentRetry?.options ?? {};
|
|
272
|
+
return {
|
|
273
|
+
...callOptions,
|
|
274
|
+
prompt: retryOptions.prompt ?? callOptions.prompt,
|
|
275
|
+
maxOutputTokens: retryOptions.maxOutputTokens ?? callOptions.maxOutputTokens,
|
|
276
|
+
temperature: retryOptions.temperature ?? callOptions.temperature,
|
|
277
|
+
stopSequences: retryOptions.stopSequences ?? callOptions.stopSequences,
|
|
278
|
+
topP: retryOptions.topP ?? callOptions.topP,
|
|
279
|
+
topK: retryOptions.topK ?? callOptions.topK,
|
|
280
|
+
presencePenalty: retryOptions.presencePenalty ?? callOptions.presencePenalty,
|
|
281
|
+
frequencyPenalty: retryOptions.frequencyPenalty ?? callOptions.frequencyPenalty,
|
|
282
|
+
seed: retryOptions.seed ?? callOptions.seed,
|
|
283
|
+
headers: retryOptions.headers ?? callOptions.headers,
|
|
284
|
+
providerOptions: retryOptions.providerOptions ?? currentRetry?.providerOptions ?? callOptions.providerOptions,
|
|
285
|
+
abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : callOptions.abortSignal
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
255
289
|
* Execute a function with retry logic for handling errors
|
|
256
290
|
*/
|
|
257
291
|
async withRetry(input) {
|
|
@@ -262,7 +296,7 @@ var RetryableLanguageModel = class {
|
|
|
262
296
|
/**
|
|
263
297
|
* Track current retry configuration.
|
|
264
298
|
*/
|
|
265
|
-
let currentRetry;
|
|
299
|
+
let currentRetry = input.currentRetry;
|
|
266
300
|
while (true) {
|
|
267
301
|
/**
|
|
268
302
|
* The previous attempt that triggered a retry, or undefined if this is the first attempt
|
|
@@ -282,16 +316,20 @@ var RetryableLanguageModel = class {
|
|
|
282
316
|
};
|
|
283
317
|
this.options.onRetry?.(context);
|
|
284
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Get the retry call options overrides for this attempt
|
|
321
|
+
*/
|
|
322
|
+
const retryCallOptions = this.getRetryCallOptions(input.callOptions, currentRetry);
|
|
285
323
|
try {
|
|
286
324
|
/**
|
|
287
325
|
* Call the function that may need to be retried
|
|
288
326
|
*/
|
|
289
|
-
const result = await input.fn(
|
|
327
|
+
const result = await input.fn(retryCallOptions);
|
|
290
328
|
/**
|
|
291
329
|
* Check if the result should trigger a retry (only for generate results, not streams)
|
|
292
330
|
*/
|
|
293
331
|
if (isGenerateResult(result)) {
|
|
294
|
-
const { retryModel, attempt } = await this.handleResult(result, attempts);
|
|
332
|
+
const { retryModel, attempt } = await this.handleResult(result, attempts, retryCallOptions);
|
|
295
333
|
attempts.push(attempt);
|
|
296
334
|
if (retryModel) {
|
|
297
335
|
if (retryModel.delay) {
|
|
@@ -304,7 +342,7 @@ var RetryableLanguageModel = class {
|
|
|
304
342
|
* - Attempt 3: 4000ms
|
|
305
343
|
*/
|
|
306
344
|
const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
|
|
307
|
-
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal:
|
|
345
|
+
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
|
|
308
346
|
}
|
|
309
347
|
this.currentModel = retryModel.model;
|
|
310
348
|
currentRetry = retryModel;
|
|
@@ -319,7 +357,7 @@ var RetryableLanguageModel = class {
|
|
|
319
357
|
attempts
|
|
320
358
|
};
|
|
321
359
|
} catch (error) {
|
|
322
|
-
const { retryModel, attempt } = await this.handleError(error, attempts);
|
|
360
|
+
const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
|
|
323
361
|
attempts.push(attempt);
|
|
324
362
|
if (retryModel.delay) {
|
|
325
363
|
/**
|
|
@@ -327,7 +365,7 @@ var RetryableLanguageModel = class {
|
|
|
327
365
|
* The delay grows exponentially: baseDelay * backoffFactor^attempts
|
|
328
366
|
*/
|
|
329
367
|
const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
|
|
330
|
-
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal:
|
|
368
|
+
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
|
|
331
369
|
}
|
|
332
370
|
this.currentModel = retryModel.model;
|
|
333
371
|
currentRetry = retryModel;
|
|
@@ -337,11 +375,12 @@ var RetryableLanguageModel = class {
|
|
|
337
375
|
/**
|
|
338
376
|
* Handle a successful result and determine if a retry is needed
|
|
339
377
|
*/
|
|
340
|
-
async handleResult(result, attempts) {
|
|
378
|
+
async handleResult(result, attempts, callOptions) {
|
|
341
379
|
const resultAttempt = {
|
|
342
380
|
type: "result",
|
|
343
381
|
result,
|
|
344
|
-
model: this.currentModel
|
|
382
|
+
model: this.currentModel,
|
|
383
|
+
options: callOptions
|
|
345
384
|
};
|
|
346
385
|
const context = {
|
|
347
386
|
current: resultAttempt,
|
|
@@ -355,11 +394,12 @@ var RetryableLanguageModel = class {
|
|
|
355
394
|
/**
|
|
356
395
|
* Handle an error and determine if a retry is needed
|
|
357
396
|
*/
|
|
358
|
-
async handleError(error, attempts) {
|
|
397
|
+
async handleError(error, attempts, callOptions) {
|
|
359
398
|
const errorAttempt = {
|
|
360
399
|
type: "error",
|
|
361
400
|
error,
|
|
362
|
-
model: this.currentModel
|
|
401
|
+
model: this.currentModel,
|
|
402
|
+
options: callOptions
|
|
363
403
|
};
|
|
364
404
|
/**
|
|
365
405
|
* Save the current attempt
|
|
@@ -384,7 +424,7 @@ var RetryableLanguageModel = class {
|
|
|
384
424
|
attempt: errorAttempt
|
|
385
425
|
};
|
|
386
426
|
}
|
|
387
|
-
async doGenerate(
|
|
427
|
+
async doGenerate(callOptions) {
|
|
388
428
|
/**
|
|
389
429
|
* Always start with the original model
|
|
390
430
|
*/
|
|
@@ -392,21 +432,16 @@ var RetryableLanguageModel = class {
|
|
|
392
432
|
/**
|
|
393
433
|
* If retries are disabled, bypass retry machinery entirely
|
|
394
434
|
*/
|
|
395
|
-
if (this.isDisabled()) return this.currentModel.doGenerate(
|
|
435
|
+
if (this.isDisabled()) return this.currentModel.doGenerate(callOptions);
|
|
396
436
|
const { result } = await this.withRetry({
|
|
397
|
-
fn: async (
|
|
398
|
-
|
|
399
|
-
...options,
|
|
400
|
-
providerOptions: currentRetry?.providerOptions ?? options.providerOptions,
|
|
401
|
-
abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : options.abortSignal
|
|
402
|
-
};
|
|
403
|
-
return this.currentModel.doGenerate(callOptions);
|
|
437
|
+
fn: async (retryCallOptions) => {
|
|
438
|
+
return this.currentModel.doGenerate(retryCallOptions);
|
|
404
439
|
},
|
|
405
|
-
|
|
440
|
+
callOptions
|
|
406
441
|
});
|
|
407
442
|
return result;
|
|
408
443
|
}
|
|
409
|
-
async doStream(
|
|
444
|
+
async doStream(callOptions) {
|
|
410
445
|
/**
|
|
411
446
|
* Always start with the original model
|
|
412
447
|
*/
|
|
@@ -414,22 +449,21 @@ var RetryableLanguageModel = class {
|
|
|
414
449
|
/**
|
|
415
450
|
* If retries are disabled, bypass retry machinery entirely
|
|
416
451
|
*/
|
|
417
|
-
if (this.isDisabled()) return this.currentModel.doStream(
|
|
452
|
+
if (this.isDisabled()) return this.currentModel.doStream(callOptions);
|
|
418
453
|
/**
|
|
419
454
|
* Perform the initial call to doStream with retry logic to handle errors before any data is streamed.
|
|
420
455
|
*/
|
|
421
456
|
let { result, attempts } = await this.withRetry({
|
|
422
|
-
fn: async (
|
|
423
|
-
|
|
424
|
-
...options,
|
|
425
|
-
providerOptions: currentRetry?.providerOptions ?? options.providerOptions,
|
|
426
|
-
abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : options.abortSignal
|
|
427
|
-
};
|
|
428
|
-
return this.currentModel.doStream(callOptions);
|
|
457
|
+
fn: async (retryCallOptions) => {
|
|
458
|
+
return this.currentModel.doStream(retryCallOptions);
|
|
429
459
|
},
|
|
430
|
-
|
|
460
|
+
callOptions
|
|
431
461
|
});
|
|
432
462
|
/**
|
|
463
|
+
* Track the current retry model for computing call options in the stream handler
|
|
464
|
+
*/
|
|
465
|
+
let currentRetry;
|
|
466
|
+
/**
|
|
433
467
|
* Wrap the original stream to handle retries if an error occurs during streaming.
|
|
434
468
|
*/
|
|
435
469
|
const retryableStream = new ReadableStream({ start: async (controller) => {
|
|
@@ -459,11 +493,15 @@ var RetryableLanguageModel = class {
|
|
|
459
493
|
controller.close();
|
|
460
494
|
break;
|
|
461
495
|
} catch (error) {
|
|
496
|
+
/**
|
|
497
|
+
* Get the retry call options for the failed attempt
|
|
498
|
+
*/
|
|
499
|
+
const retryCallOptions = this.getRetryCallOptions(callOptions, currentRetry);
|
|
462
500
|
/**
|
|
463
501
|
* Check if the error from the stream can be retried.
|
|
464
502
|
* Otherwise it will rethrow the error.
|
|
465
503
|
*/
|
|
466
|
-
const { retryModel, attempt } = await this.handleError(error, attempts);
|
|
504
|
+
const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
|
|
467
505
|
/**
|
|
468
506
|
* Save the attempt
|
|
469
507
|
*/
|
|
@@ -474,24 +512,21 @@ var RetryableLanguageModel = class {
|
|
|
474
512
|
* The delay grows exponentially: baseDelay * backoffFactor^attempts
|
|
475
513
|
*/
|
|
476
514
|
const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
|
|
477
|
-
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal:
|
|
515
|
+
await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
|
|
478
516
|
}
|
|
479
517
|
this.currentModel = retryModel.model;
|
|
518
|
+
currentRetry = retryModel;
|
|
480
519
|
/**
|
|
481
520
|
* Retry the request by calling doStream again.
|
|
482
521
|
* This will create a new stream.
|
|
483
522
|
*/
|
|
484
523
|
const retriedResult = await this.withRetry({
|
|
485
|
-
fn: async () => {
|
|
486
|
-
|
|
487
|
-
...options,
|
|
488
|
-
providerOptions: retryModel.providerOptions ?? options.providerOptions,
|
|
489
|
-
abortSignal: retryModel.timeout ? AbortSignal.timeout(retryModel.timeout) : options.abortSignal
|
|
490
|
-
};
|
|
491
|
-
return this.currentModel.doStream(callOptions);
|
|
524
|
+
fn: async (retryCallOptions$1) => {
|
|
525
|
+
return this.currentModel.doStream(retryCallOptions$1);
|
|
492
526
|
},
|
|
527
|
+
callOptions,
|
|
493
528
|
attempts,
|
|
494
|
-
|
|
529
|
+
currentRetry
|
|
495
530
|
});
|
|
496
531
|
/**
|
|
497
532
|
* Cancel the previous reader and stream if we are retrying
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as LanguageModel, b as RetryableOptions, t as EmbeddingModel, v as Retryable } from "../types-D4wXoA4D.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/retryables/content-filter-triggered.d.ts
|
|
4
4
|
|
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EmbeddingModelV2, LanguageModelV2, LanguageModelV2CallOptions, LanguageModelV2StreamPart } from "@ai-sdk/provider";
|
|
1
|
+
import { EmbeddingModelV2, LanguageModelV2, LanguageModelV2CallOptions, LanguageModelV2Prompt, LanguageModelV2StreamPart, SharedV2ProviderOptions } from "@ai-sdk/provider";
|
|
3
2
|
|
|
4
3
|
//#region src/types.d.ts
|
|
5
4
|
type LanguageModel = LanguageModelV2;
|
|
6
|
-
type EmbeddingModel<VALUE =
|
|
5
|
+
type EmbeddingModel<VALUE = any> = EmbeddingModelV2<VALUE>;
|
|
7
6
|
type LanguageModelCallOptions = LanguageModelV2CallOptions;
|
|
8
7
|
type LanguageModelStreamPart = LanguageModelV2StreamPart;
|
|
8
|
+
type LanguageModelPrompt = LanguageModelV2Prompt;
|
|
9
|
+
type LanguageModelGenerate = Awaited<ReturnType<LanguageModel['doGenerate']>>;
|
|
10
|
+
type LanguageModelStream = Awaited<ReturnType<LanguageModel['doStream']>>;
|
|
11
|
+
type EmbeddingModelCallOptions<VALUE = any> = Parameters<EmbeddingModel<VALUE>['doEmbed']>[0];
|
|
12
|
+
type EmbeddingModelEmbed<VALUE = any> = Awaited<ReturnType<EmbeddingModel<VALUE>['doEmbed']>>;
|
|
9
13
|
/**
|
|
10
|
-
*
|
|
14
|
+
* Call options that can be overridden during retry for language models.
|
|
11
15
|
*/
|
|
12
|
-
|
|
13
|
-
model: MODEL;
|
|
14
|
-
retries: Retries<MODEL>;
|
|
15
|
-
disabled?: boolean | (() => boolean);
|
|
16
|
-
onError?: (context: RetryContext<MODEL>) => void;
|
|
17
|
-
onRetry?: (context: RetryContext<MODEL>) => void;
|
|
18
|
-
}
|
|
16
|
+
type LanguageModelRetryCallOptions = Partial<Pick<LanguageModelCallOptions, 'prompt' | 'maxOutputTokens' | 'temperature' | 'stopSequences' | 'topP' | 'topK' | 'presencePenalty' | 'frequencyPenalty' | 'seed' | 'headers' | 'providerOptions'>>;
|
|
19
17
|
/**
|
|
20
|
-
*
|
|
18
|
+
* Call options that can be overridden during retry for embedding models.
|
|
21
19
|
*/
|
|
22
|
-
type
|
|
23
|
-
/**
|
|
24
|
-
* Current attempt that caused the retry
|
|
25
|
-
*/
|
|
26
|
-
current: RetryAttempt<MODEL>;
|
|
27
|
-
/**
|
|
28
|
-
* All attempts made so far, including the current one
|
|
29
|
-
*/
|
|
30
|
-
attempts: Array<RetryAttempt<MODEL>>;
|
|
31
|
-
};
|
|
20
|
+
type EmbeddingModelRetryCallOptions<VALUE = any> = Partial<Pick<EmbeddingModelCallOptions<VALUE>, 'values' | 'headers' | 'providerOptions'>>;
|
|
32
21
|
/**
|
|
33
22
|
* A retry attempt with an error
|
|
34
23
|
*/
|
|
@@ -37,6 +26,10 @@ type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel> = {
|
|
|
37
26
|
error: unknown;
|
|
38
27
|
result?: undefined;
|
|
39
28
|
model: MODEL;
|
|
29
|
+
/**
|
|
30
|
+
* The call options used for this attempt.
|
|
31
|
+
*/
|
|
32
|
+
options: MODEL extends LanguageModel ? LanguageModelCallOptions : EmbeddingModelCallOptions<any>;
|
|
40
33
|
};
|
|
41
34
|
/**
|
|
42
35
|
* A retry attempt with a successful result
|
|
@@ -46,21 +39,71 @@ type RetryResultAttempt = {
|
|
|
46
39
|
result: LanguageModelGenerate;
|
|
47
40
|
error?: undefined;
|
|
48
41
|
model: LanguageModel;
|
|
42
|
+
/**
|
|
43
|
+
* The call options used for this attempt.
|
|
44
|
+
*/
|
|
45
|
+
options: LanguageModelCallOptions;
|
|
49
46
|
};
|
|
50
47
|
/**
|
|
51
48
|
* A retry attempt with either an error or a result and the model used
|
|
52
49
|
*/
|
|
53
50
|
type RetryAttempt<MODEL extends LanguageModel | EmbeddingModel> = RetryErrorAttempt<MODEL> | RetryResultAttempt;
|
|
51
|
+
/**
|
|
52
|
+
* The context provided to Retryables with the current attempt and all previous attempts.
|
|
53
|
+
*/
|
|
54
|
+
type RetryContext<MODEL extends LanguageModel | EmbeddingModel> = {
|
|
55
|
+
/**
|
|
56
|
+
* Current attempt that caused the retry
|
|
57
|
+
*/
|
|
58
|
+
current: RetryAttempt<MODEL>;
|
|
59
|
+
/**
|
|
60
|
+
* All attempts made so far, including the current one
|
|
61
|
+
*/
|
|
62
|
+
attempts: Array<RetryAttempt<MODEL>>;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Options for creating a retryable model.
|
|
66
|
+
*/
|
|
67
|
+
interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
|
|
68
|
+
model: MODEL;
|
|
69
|
+
retries: Retries<MODEL>;
|
|
70
|
+
disabled?: boolean | (() => boolean);
|
|
71
|
+
onError?: (context: RetryContext<MODEL>) => void;
|
|
72
|
+
onRetry?: (context: RetryContext<MODEL>) => void;
|
|
73
|
+
}
|
|
54
74
|
/**
|
|
55
75
|
* A model to retry with and the maximum number of attempts for that model.
|
|
56
76
|
*/
|
|
57
77
|
type Retry<MODEL extends LanguageModel | EmbeddingModel> = {
|
|
58
78
|
model: MODEL;
|
|
79
|
+
/**
|
|
80
|
+
* Maximum number of attempts for this model.
|
|
81
|
+
*/
|
|
59
82
|
maxAttempts?: number;
|
|
83
|
+
/**
|
|
84
|
+
* Delay in milliseconds before retrying.
|
|
85
|
+
*/
|
|
60
86
|
delay?: number;
|
|
87
|
+
/**
|
|
88
|
+
* Factor to multiply the delay by for exponential backoff.
|
|
89
|
+
*/
|
|
61
90
|
backoffFactor?: number;
|
|
62
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Timeout in milliseconds for the retry request.
|
|
93
|
+
* Creates a new AbortSignal with this timeout.
|
|
94
|
+
*/
|
|
63
95
|
timeout?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Call options to override for this retry.
|
|
98
|
+
*/
|
|
99
|
+
options?: MODEL extends LanguageModel ? Partial<LanguageModelRetryCallOptions> : Partial<EmbeddingModelRetryCallOptions>;
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated Use `options.providerOptions` instead.
|
|
102
|
+
* Provider options to override for this retry.
|
|
103
|
+
* If both `providerOptions` and `options.providerOptions` are set,
|
|
104
|
+
* `options.providerOptions` takes precedence.
|
|
105
|
+
*/
|
|
106
|
+
providerOptions?: SharedV2ProviderOptions;
|
|
64
107
|
};
|
|
65
108
|
/**
|
|
66
109
|
* A function that determines whether to retry with a different model based on the current attempt and all previous attempts.
|
|
@@ -68,9 +111,5 @@ type Retry<MODEL extends LanguageModel | EmbeddingModel> = {
|
|
|
68
111
|
type Retryable<MODEL extends LanguageModel | EmbeddingModel> = (context: RetryContext<MODEL>) => Retry<MODEL> | Promise<Retry<MODEL> | undefined> | undefined;
|
|
69
112
|
type Retries<MODEL extends LanguageModel | EmbeddingModel> = Array<Retryable<MODEL> | Retry<MODEL> | MODEL>;
|
|
70
113
|
type RetryableOptions<MODEL extends LanguageModel | EmbeddingModel> = Partial<Omit<Retry<MODEL>, 'model'>>;
|
|
71
|
-
type LanguageModelGenerate = Awaited<ReturnType<LanguageModel['doGenerate']>>;
|
|
72
|
-
type LanguageModelStream = Awaited<ReturnType<LanguageModel['doStream']>>;
|
|
73
|
-
type EmbeddingModelCallOptions<VALUE> = Parameters<EmbeddingModel<VALUE>['doEmbed']>[0];
|
|
74
|
-
type EmbeddingModelEmbed<VALUE = any> = Awaited<ReturnType<EmbeddingModel<VALUE>['doEmbed']>>;
|
|
75
114
|
//#endregion
|
|
76
|
-
export {
|
|
115
|
+
export { RetryResultAttempt as _, LanguageModel as a, RetryableOptions as b, LanguageModelPrompt as c, LanguageModelStreamPart as d, Retries as f, RetryErrorAttempt as g, RetryContext as h, EmbeddingModelRetryCallOptions as i, LanguageModelRetryCallOptions as l, RetryAttempt as m, EmbeddingModelCallOptions as n, LanguageModelCallOptions as o, Retry as p, EmbeddingModelEmbed as r, LanguageModelGenerate as s, EmbeddingModel as t, LanguageModelStream as u, Retryable as v, RetryableModelOptions as y };
|