ai-retry 1.1.0 → 1.2.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 +57 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +95 -38
- package/dist/retryables/index.d.mts +1 -1
- package/dist/{types-Bty5BU37.d.mts → types-Dk5KMZMd.d.mts} +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -757,6 +757,46 @@ const retryableModel = createRetryable({
|
|
|
757
757
|
});
|
|
758
758
|
```
|
|
759
759
|
|
|
760
|
+
#### Reset
|
|
761
|
+
|
|
762
|
+
By default, every new request starts with the base model, even if a previous request was retried with a different model. The `reset` option changes this behavior by making the last successfully retried model **sticky**, that means subsequent requests will continue using that model instead of switching back to the base model. The reset value controls how long the retry model stays sticky before resetting back to the base model.
|
|
763
|
+
|
|
764
|
+
| Value | Description |
|
|
765
|
+
|-------|-------------|
|
|
766
|
+
| `'after-request'` | Reset immediately after the next request (default) |
|
|
767
|
+
| `` `after-${N}-requests` `` | Keep the retry model for the next **N** requests, then reset |
|
|
768
|
+
| `` `after-${N}-seconds` `` | Keep the retry model for **N** seconds, then reset |
|
|
769
|
+
|
|
770
|
+
**Reset after each request (default)**
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
const retryableModel = createRetryable({
|
|
774
|
+
model: openai('gpt-4o-mini'),
|
|
775
|
+
retries: [anthropic('claude-sonnet-4-20250514')],
|
|
776
|
+
reset: 'after-request', // default — always start with the base model
|
|
777
|
+
});
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Keep the retry model for N requests**
|
|
781
|
+
|
|
782
|
+
```typescript
|
|
783
|
+
const retryableModel = createRetryable({
|
|
784
|
+
model: openai('gpt-4o-mini'),
|
|
785
|
+
retries: [anthropic('claude-sonnet-4-20250514')],
|
|
786
|
+
reset: 'after-5-requests', // use the retry model for 5 more requests before resetting
|
|
787
|
+
});
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
**Keep the retry model for N seconds**
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
const retryableModel = createRetryable({
|
|
794
|
+
model: openai('gpt-4o-mini'),
|
|
795
|
+
retries: [anthropic('claude-sonnet-4-20250514')],
|
|
796
|
+
reset: 'after-30-seconds', // use the retry model for 30 seconds before resetting
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
760
800
|
### Streaming
|
|
761
801
|
|
|
762
802
|
Errors during streaming requests can occur in two ways:
|
|
@@ -777,6 +817,7 @@ interface RetryableModelOptions<MODEL extends LanguageModelV2 | EmbeddingModelV2
|
|
|
777
817
|
model: MODEL;
|
|
778
818
|
retries: Array<Retryable<MODEL> | MODEL>;
|
|
779
819
|
disabled?: boolean | (() => boolean);
|
|
820
|
+
reset?: Reset;
|
|
780
821
|
onError?: (context: RetryContext<MODEL>) => void;
|
|
781
822
|
onRetry?: (context: RetryContext<MODEL>) => void;
|
|
782
823
|
}
|
|
@@ -786,9 +827,25 @@ interface RetryableModelOptions<MODEL extends LanguageModelV2 | EmbeddingModelV2
|
|
|
786
827
|
- `model`: The base model to use for the initial request.
|
|
787
828
|
- `retries`: Array of retryables (functions, models, or retry objects) to attempt on failure.
|
|
788
829
|
- `disabled`: Disable all retry logic. Can be a boolean or function returning boolean. Default: `false` (retries enabled).
|
|
830
|
+
- `reset`: Controls when to reset back to the base model after a successful retry. See [Reset](#reset) for details. Default: `'after-request'`.
|
|
789
831
|
- `onError`: Callback invoked when an error occurs.
|
|
790
832
|
- `onRetry`: Callback invoked before attempting a retry.
|
|
791
833
|
|
|
834
|
+
#### `Reset`
|
|
835
|
+
|
|
836
|
+
Controls when the sticky model resets back to the base model after a successful retry.
|
|
837
|
+
|
|
838
|
+
```ts
|
|
839
|
+
type Reset =
|
|
840
|
+
| 'after-request'
|
|
841
|
+
| `after-${number}-requests`
|
|
842
|
+
| `after-${number}-seconds`;
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
- `'after-request'` — reset immediately after the next request (default).
|
|
846
|
+
- `` `after-${N}-requests` `` — keep the retry model for the next N requests, then reset.
|
|
847
|
+
- `` `after-${N}-seconds` `` — keep the retry model for N seconds, then reset.
|
|
848
|
+
|
|
792
849
|
#### `Retryable`
|
|
793
850
|
|
|
794
851
|
A `Retryable` is a function that receives a `RetryContext` with the current error or result and model and all previous attempts.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as Retryable, S as RetryResultAttempt, T as RetryableOptions, _ as Retries, a as GatewayLanguageModelId, b as RetryContext, c as LanguageModelGenerate, d as LanguageModelStreamPart, f as ProviderOptions, g as ResolvedModel, h as ResolvableModel, i as EmbeddingModelRetryCallOptions, l as LanguageModelRetryCallOptions, m as ResolvableLanguageModel, n as EmbeddingModelCallOptions, o as LanguageModel, p as Reset, r as EmbeddingModelEmbed, s as LanguageModelCallOptions, t as EmbeddingModel, u as LanguageModelStream, v as Retry, w as RetryableModelOptions, x as RetryErrorAttempt, y as RetryAttempt } from "./types-Dk5KMZMd.mjs";
|
|
2
2
|
import * as _ai_sdk_provider0 from "@ai-sdk/provider";
|
|
3
3
|
|
|
4
4
|
//#region src/create-retryable-model.d.ts
|
|
@@ -73,4 +73,4 @@ declare const isAbortError: (error: unknown) => boolean;
|
|
|
73
73
|
*/
|
|
74
74
|
declare const isTimeoutError: (error: unknown) => boolean;
|
|
75
75
|
//#endregion
|
|
76
|
-
export { EmbeddingModel, EmbeddingModelCallOptions, EmbeddingModelEmbed, EmbeddingModelRetryCallOptions, GatewayLanguageModelId, LanguageModel, LanguageModelCallOptions, LanguageModelGenerate, LanguageModelRetryCallOptions, LanguageModelStream, LanguageModelStreamPart, ProviderOptions, ResolvableLanguageModel, ResolvableModel, ResolvedModel, Retries, Retry, RetryAttempt, RetryContext, RetryErrorAttempt, RetryResultAttempt, Retryable, RetryableModelOptions, RetryableOptions, createRetryable, getModelKey, isAbortError, isEmbeddingModel, isErrorAttempt, isGenerateResult, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString, isTimeoutError };
|
|
76
|
+
export { EmbeddingModel, EmbeddingModelCallOptions, EmbeddingModelEmbed, EmbeddingModelRetryCallOptions, GatewayLanguageModelId, LanguageModel, LanguageModelCallOptions, LanguageModelGenerate, LanguageModelRetryCallOptions, LanguageModelStream, LanguageModelStreamPart, ProviderOptions, Reset, ResolvableLanguageModel, ResolvableModel, ResolvedModel, Retries, Retry, RetryAttempt, RetryContext, RetryErrorAttempt, RetryResultAttempt, Retryable, RetryableModelOptions, RetryableOptions, createRetryable, getModelKey, isAbortError, isEmbeddingModel, isErrorAttempt, isGenerateResult, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString, isTimeoutError };
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,86 @@ import { RetryError, gateway } from "ai";
|
|
|
3
3
|
import { delay } from "@ai-sdk/provider-utils";
|
|
4
4
|
import { getErrorMessage } from "@ai-sdk/provider";
|
|
5
5
|
|
|
6
|
+
//#region src/parse-reset.ts
|
|
7
|
+
/**
|
|
8
|
+
* Parses a `Reset` string into a structured object.
|
|
9
|
+
*
|
|
10
|
+
* `'after-request'` is treated as `{ type: 'requests', count: 0 }`,
|
|
11
|
+
* meaning the sticky model expires immediately (default behavior).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* parseReset(`after-request`); // { type: 'requests', count: 0 }
|
|
15
|
+
* parseReset(`after-5-requests`); // { type: 'requests', count: 5 }
|
|
16
|
+
* parseReset(`after-30-seconds`); // { type: 'seconds', count: 30 }
|
|
17
|
+
*/
|
|
18
|
+
function parseReset(reset) {
|
|
19
|
+
if (reset === `after-request`) return {
|
|
20
|
+
type: `requests`,
|
|
21
|
+
count: 0
|
|
22
|
+
};
|
|
23
|
+
const requestsMatch = reset.match(/^after-(\d+)-requests$/);
|
|
24
|
+
if (requestsMatch) return {
|
|
25
|
+
type: `requests`,
|
|
26
|
+
count: Number.parseInt(requestsMatch[1], 10)
|
|
27
|
+
};
|
|
28
|
+
const secondsMatch = reset.match(/^after-(\d+)-seconds$/);
|
|
29
|
+
if (secondsMatch) return {
|
|
30
|
+
type: `seconds`,
|
|
31
|
+
count: Number.parseInt(secondsMatch[1], 10)
|
|
32
|
+
};
|
|
33
|
+
throw new Error(`Invalid reset option: ${reset}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/base-retryable-model.ts
|
|
38
|
+
var BaseRetryableModel = class {
|
|
39
|
+
baseModel;
|
|
40
|
+
currentModel;
|
|
41
|
+
options;
|
|
42
|
+
parsedReset;
|
|
43
|
+
/** The model that last succeeded via retry, used for subsequent requests. */
|
|
44
|
+
stickyState;
|
|
45
|
+
constructor(options) {
|
|
46
|
+
this.options = options;
|
|
47
|
+
this.baseModel = options.model;
|
|
48
|
+
this.currentModel = options.model;
|
|
49
|
+
this.parsedReset = parseReset(options.reset ?? `after-request`);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Determine which model to start the request with,
|
|
53
|
+
* considering the sticky model and reset policy.
|
|
54
|
+
*/
|
|
55
|
+
resolveStartModel() {
|
|
56
|
+
if (!this.stickyState) return this.baseModel;
|
|
57
|
+
if (this.parsedReset.type === `requests`) {
|
|
58
|
+
if (this.stickyState.requestsRemaining > 0) {
|
|
59
|
+
this.stickyState.requestsRemaining--;
|
|
60
|
+
return this.stickyState.model;
|
|
61
|
+
}
|
|
62
|
+
} else if (Date.now() - this.stickyState.setAt < this.parsedReset.count * 1e3) return this.stickyState.model;
|
|
63
|
+
this.stickyState = void 0;
|
|
64
|
+
return this.baseModel;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* After a successful request, update sticky model if a retry occurred.
|
|
68
|
+
*/
|
|
69
|
+
updateStickyModel(startModel) {
|
|
70
|
+
if (this.currentModel !== startModel) this.stickyState = {
|
|
71
|
+
model: this.currentModel,
|
|
72
|
+
setAt: Date.now(),
|
|
73
|
+
requestsRemaining: this.parsedReset.type === `requests` ? this.parsedReset.count : 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if retries are disabled
|
|
78
|
+
*/
|
|
79
|
+
isDisabled() {
|
|
80
|
+
if (this.options.disabled === void 0) return false;
|
|
81
|
+
return typeof this.options.disabled === `function` ? this.options.disabled() : this.options.disabled;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
6
86
|
//#region src/calculate-exponential-backoff.ts
|
|
7
87
|
/**
|
|
8
88
|
* Calculates the exponential backoff delay.
|
|
@@ -100,11 +180,8 @@ function prepareRetryError(error, attempts) {
|
|
|
100
180
|
|
|
101
181
|
//#endregion
|
|
102
182
|
//#region src/retryable-embedding-model.ts
|
|
103
|
-
var RetryableEmbeddingModel = class {
|
|
183
|
+
var RetryableEmbeddingModel = class extends BaseRetryableModel {
|
|
104
184
|
specificationVersion = "v3";
|
|
105
|
-
baseModel;
|
|
106
|
-
currentModel;
|
|
107
|
-
options;
|
|
108
185
|
get modelId() {
|
|
109
186
|
return this.currentModel.modelId;
|
|
110
187
|
}
|
|
@@ -117,18 +194,6 @@ var RetryableEmbeddingModel = class {
|
|
|
117
194
|
get supportsParallelCalls() {
|
|
118
195
|
return this.currentModel.supportsParallelCalls;
|
|
119
196
|
}
|
|
120
|
-
constructor(options) {
|
|
121
|
-
this.options = options;
|
|
122
|
-
this.baseModel = options.model;
|
|
123
|
-
this.currentModel = options.model;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Check if retries are disabled
|
|
127
|
-
*/
|
|
128
|
-
isDisabled() {
|
|
129
|
-
if (this.options.disabled === void 0) return false;
|
|
130
|
-
return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
|
|
131
|
-
}
|
|
132
197
|
/**
|
|
133
198
|
* Get the retry call options overrides from a retry configuration.
|
|
134
199
|
*/
|
|
@@ -238,9 +303,10 @@ var RetryableEmbeddingModel = class {
|
|
|
238
303
|
}
|
|
239
304
|
async doEmbed(callOptions) {
|
|
240
305
|
/**
|
|
241
|
-
*
|
|
306
|
+
* Resolve the starting model (base or sticky)
|
|
242
307
|
*/
|
|
243
|
-
|
|
308
|
+
const startModel = this.resolveStartModel();
|
|
309
|
+
this.currentModel = startModel;
|
|
244
310
|
/**
|
|
245
311
|
* If retries are disabled, bypass retry machinery entirely
|
|
246
312
|
*/
|
|
@@ -251,17 +317,15 @@ var RetryableEmbeddingModel = class {
|
|
|
251
317
|
},
|
|
252
318
|
callOptions
|
|
253
319
|
});
|
|
320
|
+
this.updateStickyModel(startModel);
|
|
254
321
|
return result;
|
|
255
322
|
}
|
|
256
323
|
};
|
|
257
324
|
|
|
258
325
|
//#endregion
|
|
259
326
|
//#region src/retryable-language-model.ts
|
|
260
|
-
var RetryableLanguageModel = class {
|
|
327
|
+
var RetryableLanguageModel = class extends BaseRetryableModel {
|
|
261
328
|
specificationVersion = "v3";
|
|
262
|
-
baseModel;
|
|
263
|
-
currentModel;
|
|
264
|
-
options;
|
|
265
329
|
get modelId() {
|
|
266
330
|
return this.currentModel.modelId;
|
|
267
331
|
}
|
|
@@ -271,18 +335,6 @@ var RetryableLanguageModel = class {
|
|
|
271
335
|
get supportedUrls() {
|
|
272
336
|
return this.currentModel.supportedUrls;
|
|
273
337
|
}
|
|
274
|
-
constructor(options) {
|
|
275
|
-
this.options = options;
|
|
276
|
-
this.baseModel = options.model;
|
|
277
|
-
this.currentModel = options.model;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Check if retries are disabled
|
|
281
|
-
*/
|
|
282
|
-
isDisabled() {
|
|
283
|
-
if (this.options.disabled === void 0) return false;
|
|
284
|
-
return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
|
|
285
|
-
}
|
|
286
338
|
/**
|
|
287
339
|
* Get the retry call options overrides from a retry configuration.
|
|
288
340
|
*/
|
|
@@ -446,9 +498,10 @@ var RetryableLanguageModel = class {
|
|
|
446
498
|
}
|
|
447
499
|
async doGenerate(callOptions) {
|
|
448
500
|
/**
|
|
449
|
-
*
|
|
501
|
+
* Resolve the starting model (base or sticky)
|
|
450
502
|
*/
|
|
451
|
-
|
|
503
|
+
const startModel = this.resolveStartModel();
|
|
504
|
+
this.currentModel = startModel;
|
|
452
505
|
/**
|
|
453
506
|
* If retries are disabled, bypass retry machinery entirely
|
|
454
507
|
*/
|
|
@@ -459,13 +512,15 @@ var RetryableLanguageModel = class {
|
|
|
459
512
|
},
|
|
460
513
|
callOptions
|
|
461
514
|
});
|
|
515
|
+
this.updateStickyModel(startModel);
|
|
462
516
|
return result;
|
|
463
517
|
}
|
|
464
518
|
async doStream(callOptions) {
|
|
465
519
|
/**
|
|
466
|
-
*
|
|
520
|
+
* Resolve the starting model (base or sticky)
|
|
467
521
|
*/
|
|
468
|
-
|
|
522
|
+
const startModel = this.resolveStartModel();
|
|
523
|
+
this.currentModel = startModel;
|
|
469
524
|
/**
|
|
470
525
|
* If retries are disabled, bypass retry machinery entirely
|
|
471
526
|
*/
|
|
@@ -479,6 +534,7 @@ var RetryableLanguageModel = class {
|
|
|
479
534
|
},
|
|
480
535
|
callOptions
|
|
481
536
|
});
|
|
537
|
+
this.updateStickyModel(startModel);
|
|
482
538
|
/**
|
|
483
539
|
* Track the current retry model for computing call options in the stream handler
|
|
484
540
|
*/
|
|
@@ -554,6 +610,7 @@ var RetryableLanguageModel = class {
|
|
|
554
610
|
await reader?.cancel();
|
|
555
611
|
result = retriedResult.result;
|
|
556
612
|
attempts = retriedResult.attempts;
|
|
613
|
+
this.updateStickyModel(startModel);
|
|
557
614
|
} finally {
|
|
558
615
|
reader?.releaseLock();
|
|
559
616
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as Retryable, T as RetryableOptions, m as ResolvableLanguageModel, t as EmbeddingModel } from "../types-Dk5KMZMd.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/retryables/content-filter-triggered.d.ts
|
|
4
4
|
/**
|
|
@@ -70,6 +70,12 @@ interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
|
|
|
70
70
|
model: MODEL;
|
|
71
71
|
retries: Retries<MODEL>;
|
|
72
72
|
disabled?: boolean | (() => boolean);
|
|
73
|
+
/**
|
|
74
|
+
* Controls when to reset back to the base model after a successful retry.
|
|
75
|
+
*
|
|
76
|
+
* @default 'after-request'
|
|
77
|
+
*/
|
|
78
|
+
reset?: Reset;
|
|
73
79
|
onError?: (context: RetryContext<MODEL>) => void;
|
|
74
80
|
onRetry?: (context: RetryContext<MODEL>) => void;
|
|
75
81
|
}
|
|
@@ -121,9 +127,17 @@ type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
|
|
|
121
127
|
type Retryable<MODEL extends ResolvableLanguageModel | EmbeddingModel> = (context: RetryContext<MODEL>) => Retry<MODEL> | Promise<Retry<MODEL> | undefined> | undefined;
|
|
122
128
|
type Retries<MODEL extends LanguageModel | EmbeddingModel> = Array<Retryable<ResolvableModel<MODEL>> | Retry<ResolvableModel<MODEL>> | ResolvableModel<MODEL>>;
|
|
123
129
|
type RetryableOptions<MODEL extends ResolvableLanguageModel | EmbeddingModel> = Partial<Omit<Retry<MODEL>, 'model'>>;
|
|
130
|
+
/**
|
|
131
|
+
* Controls when to reset the sticky model back to the base model.
|
|
132
|
+
*
|
|
133
|
+
* - `'after-request'` — reset after each request (default, current behavior)
|
|
134
|
+
* - `` `after-${number}-requests` `` — use the retry model for the next N requests
|
|
135
|
+
* - `` `after-${number}-seconds` `` — use the retry model for the next N seconds
|
|
136
|
+
*/
|
|
137
|
+
type Reset = 'after-request' | `after-${number}-requests` | `after-${number}-seconds`;
|
|
124
138
|
type LanguageModelGenerate = Awaited<ReturnType<LanguageModel['doGenerate']>>;
|
|
125
139
|
type LanguageModelStream = Awaited<ReturnType<LanguageModel['doStream']>>;
|
|
126
140
|
type EmbeddingModelCallOptions = Parameters<EmbeddingModel['doEmbed']>[0];
|
|
127
141
|
type EmbeddingModelEmbed = Awaited<ReturnType<EmbeddingModel['doEmbed']>>;
|
|
128
142
|
//#endregion
|
|
129
|
-
export {
|
|
143
|
+
export { Retryable as C, RetryResultAttempt as S, RetryableOptions as T, Retries as _, GatewayLanguageModelId as a, RetryContext as b, LanguageModelGenerate as c, LanguageModelStreamPart as d, ProviderOptions as f, ResolvedModel as g, ResolvableModel as h, EmbeddingModelRetryCallOptions as i, LanguageModelRetryCallOptions as l, ResolvableLanguageModel as m, EmbeddingModelCallOptions as n, LanguageModel as o, Reset as p, EmbeddingModelEmbed as r, LanguageModelCallOptions as s, EmbeddingModel as t, LanguageModelStream as u, Retry as v, RetryableModelOptions as w, RetryErrorAttempt as x, RetryAttempt as y };
|