ai-retry 0.10.3 → 0.12.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 CHANGED
@@ -94,6 +94,41 @@ const result = await embed({
94
94
  console.log(result.embedding);
95
95
  ```
96
96
 
97
+ #### Vercel AI Gateway
98
+
99
+ You can use `ai-retry` with Vercel AI Gateway by providing the model as a string. Internally, the model will be resolved with the default `gateway` [provider instance](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway#provider-instance) from AI SDK.
100
+
101
+ ```typescript
102
+ import { gateway } from 'ai';
103
+ import { createRetryable } from 'ai-retry';
104
+
105
+ const retryableModel = createRetryable({
106
+ model: 'openai/gpt-5',
107
+ retries: [
108
+ 'anthropic/claude-sonnet-4'
109
+ ]
110
+ });
111
+
112
+ // Is the same as:
113
+ const retryableModel = createRetryable({
114
+ model: gateway('openai/gpt-5'),
115
+ retries: [
116
+ gateway('anthropic/claude-sonnet-4')
117
+ ]
118
+ });
119
+ ```
120
+
121
+ By default, the `gateway` provider resolves model strings as language models. If you want to use an embedding model, you need to use the `textEmbeddingModel` method.
122
+
123
+ ```typescript
124
+ import { gateway } from 'ai';
125
+ import { createRetryable } from 'ai-retry';
126
+
127
+ const retryableModel = createRetryable({
128
+ model: gateway.textEmbeddingModel('openai/text-embedding-3-large'),
129
+ });
130
+ ```
131
+
97
132
  ### Retryables
98
133
 
99
134
  The objects passed to the `retries` are called retryables and control the retry behavior. We can distinguish between two types of retryables:
@@ -138,6 +173,58 @@ const retryableModel = createRetryable({
138
173
 
139
174
  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
175
 
176
+ #### Errors vs Results
177
+
178
+ Dynamic retryables can be further divided based on what triggers them:
179
+
180
+ - **Error-based retryables** handle API errors where the request throws an error (e.g., timeouts, rate limits, service unavailable, etc.)
181
+ - **Result-based retryables** handle successful responses that still need retrying (e.g., content filtering, guardrails, etc.)
182
+
183
+ 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.
184
+
185
+ ```typescript
186
+ import { generateText } from 'ai';
187
+ import { createRetryable, isErrorAttempt, isResultAttempt } from 'ai-retry';
188
+ import type { Retryable } from 'ai-retry';
189
+
190
+ // Error-based retryable: handles thrown errors (e.g., timeouts, rate limits)
191
+ const errorBasedRetry: Retryable = (context) => {
192
+ if (isErrorAttempt(context.current)) {
193
+ const { error } = context.current;
194
+ // The request threw an error - e.g., network timeout, 429 rate limit
195
+ console.log('Request failed with error:', error);
196
+ return { model: anthropic('claude-3-haiku-20240307') };
197
+ }
198
+ return undefined;
199
+ };
200
+
201
+ // Result-based retryable: handles successful responses that need retrying
202
+ const resultBasedRetry: Retryable = (context) => {
203
+ if (isResultAttempt(context.current)) {
204
+ const { result } = context.current;
205
+ // The request succeeded, but the response indicates a problem
206
+ if (result.finishReason === 'content-filter') {
207
+ console.log('Content was filtered, trying different model');
208
+ return { model: openai('gpt-4') };
209
+ }
210
+ }
211
+ return undefined;
212
+ };
213
+
214
+ const retryableModel = createRetryable({
215
+ model: azure('gpt-4-mini'),
216
+ retries: [
217
+ // Error-based: catches thrown errors like timeouts, rate limits, etc.
218
+ errorBasedRetry,
219
+
220
+ // Result-based: catches successful responses that need retrying
221
+ resultBasedRetry,
222
+ ],
223
+ });
224
+ ```
225
+
226
+ Result-based retryables are only available for generate calls like `generateText` and `generateObject`. They are not available for streaming calls like `streamText` and `streamObject`.
227
+
141
228
  #### Fallbacks
142
229
 
143
230
  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 +411,23 @@ Handle service overload errors (status code 529) by switching to a provider.
324
411
  import { serviceOverloaded } from 'ai-retry/retryables';
325
412
 
326
413
  const retryableModel = createRetryable({
327
- model: azure('gpt-4'),
414
+ model: anthropic('claude-sonnet-4-0'),
328
415
  retries: [
329
- serviceOverloaded(openai('gpt-4')), // Switch to OpenAI if Azure is overloaded
416
+ // Retry with delay and exponential backoff
417
+ serviceOverloaded(anthropic('claude-sonnet-4-0'), {
418
+ delay: 5_000,
419
+ backoffFactor: 2,
420
+ maxAttempts: 5,
421
+ }),
422
+ // Or switch to a different provider
423
+ serviceOverloaded(openai('gpt-4')),
330
424
  ],
331
425
  });
426
+
427
+ const result = streamText({
428
+ model: retryableModel,
429
+ prompt: 'Write a story about a robot...',
430
+ });
332
431
  ```
333
432
 
334
433
  #### Service Unavailable
@@ -537,6 +636,58 @@ const result = await generateText({
537
636
 
538
637
  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
638
 
639
+ #### Call Options
640
+
641
+ 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.
642
+
643
+ ```typescript
644
+ const retryableModel = createRetryable({
645
+ model: openai('gpt-4'),
646
+ retries: [
647
+ {
648
+ model: anthropic('claude-3-haiku'),
649
+ options: {
650
+ // Override generation parameters for more deterministic output
651
+ temperature: 0.3,
652
+ topP: 0.9,
653
+ maxOutputTokens: 500,
654
+ // Set a seed for reproducibility
655
+ seed: 42,
656
+ },
657
+ },
658
+ ],
659
+ });
660
+ ```
661
+
662
+ The following options can be overridden:
663
+
664
+ > [!NOTE]
665
+ > 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.
666
+
667
+ ##### Language Model Options
668
+
669
+ | Option | Description |
670
+ |--------|-------------|
671
+ | [`prompt`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#prompt) | Override the entire prompt for the retry |
672
+ | [`temperature`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#temperature) | Temperature setting for controlling randomness |
673
+ | [`topP`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#topp) | Nucleus sampling parameter |
674
+ | [`topK`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#topk) | Top-K sampling parameter |
675
+ | [`maxOutputTokens`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#max-output-tokens) | Maximum number of tokens to generate |
676
+ | [`seed`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#seed) | Random seed for deterministic generation |
677
+ | [`stopSequences`](https://ai-sdk.dev/docs/reference/ai-sdk-types/generate-text#stopsequences) | Stop sequences to end generation |
678
+ | [`presencePenalty`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#presencepenalty) | Presence penalty for reducing repetition |
679
+ | [`frequencyPenalty`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#frequencypenalty) | Frequency penalty for reducing repetition |
680
+ | [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-text#headers) | Additional HTTP headers |
681
+ | [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-types/generate-text#provideroptions) | Provider-specific options |
682
+
683
+ ##### Embedding Model Options
684
+
685
+ | Option | Description |
686
+ |--------|-------------|
687
+ | [`values`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#values) | Override the values to embed |
688
+ | [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#headers) | Additional HTTP headers |
689
+ | [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#provideroptions) | Provider-specific options |
690
+
540
691
  #### Logging
541
692
 
542
693
  You can use the following callbacks to log retry attempts and errors:
@@ -603,7 +754,7 @@ type Retryable = (
603
754
 
604
755
  #### `Retry`
605
756
 
606
- A `Retry` specifies the model to retry and optional settings like `maxAttempts`, `delay`, `backoffFactor`, `timeout`, and `providerOptions`.
757
+ A `Retry` specifies the model to retry and optional settings. The available options depend on the model type (language model or embedding model).
607
758
 
608
759
  ```typescript
609
760
  interface Retry {
@@ -612,18 +763,11 @@ interface Retry {
612
763
  delay?: number; // Delay in milliseconds before retrying
613
764
  backoffFactor?: number; // Multiplier for exponential backoff
614
765
  timeout?: number; // Timeout in milliseconds for the retry attempt
615
- providerOptions?: ProviderOptions; // Provider-specific options for the retry
766
+ providerOptions?: ProviderOptions; // @deprecated - use options.providerOptions instead
767
+ options?: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions; // Call options to override for this retry
616
768
  }
617
769
  ```
618
770
 
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
771
  #### `RetryContext`
628
772
 
629
773
  The `RetryContext` object contains information about the current attempt and all previous attempts.
@@ -637,13 +781,23 @@ interface RetryContext {
637
781
 
638
782
  #### `RetryAttempt`
639
783
 
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.
784
+ 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
785
 
642
786
  ```typescript
643
787
  // For both language and embedding models
644
788
  type RetryAttempt =
645
- | { type: 'error'; error: unknown; model: LanguageModelV2 | EmbeddingModelV2 }
646
- | { type: 'result'; result: LanguageModelV2Generate; model: LanguageModelV2 };
789
+ | {
790
+ type: 'error';
791
+ error: unknown;
792
+ model: LanguageModelV2 | EmbeddingModelV2;
793
+ options: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions;
794
+ }
795
+ | {
796
+ type: 'result';
797
+ result: LanguageModelV2Generate;
798
+ model: LanguageModelV2;
799
+ options: LanguageModelV2CallOptions;
800
+ };
647
801
 
648
802
  // Note: Result-based retries only apply to language models, not embedding models
649
803
 
package/dist/index.d.mts CHANGED
@@ -1,7 +1,10 @@
1
- import { _ as RetryableOptions, a as LanguageModelCallOptions, c as LanguageModelStreamPart, d as RetryAttempt, f as RetryContext, g as RetryableModelOptions, h as Retryable, i as LanguageModel, l as Retries, m as RetryResultAttempt, n as EmbeddingModelCallOptions, o as LanguageModelGenerate, p as RetryErrorAttempt, r as EmbeddingModelEmbed, s as LanguageModelStream, t as EmbeddingModel, u as Retry } from "./types-TotEvw-5.mjs";
1
+ import { C as RetryableModelOptions, S as Retryable, _ as Retry, a as GatewayLanguageModelId, b as RetryErrorAttempt, c as LanguageModelGenerate, d as LanguageModelStreamPart, f as ProviderOptions, g as Retries, h as ResolvedModel, i as EmbeddingModelRetryCallOptions, l as LanguageModelRetryCallOptions, m as ResolvableModel, n as EmbeddingModelCallOptions, o as LanguageModel, p as ResolvableLanguageModel, r as EmbeddingModelEmbed, s as LanguageModelCallOptions, t as EmbeddingModel, u as LanguageModelStream, v as RetryAttempt, w as RetryableOptions, x as RetryResultAttempt, y as RetryContext } from "./types-Csx79V-W.mjs";
2
2
  import * as _ai_sdk_provider0 from "@ai-sdk/provider";
3
3
 
4
4
  //#region src/create-retryable-model.d.ts
5
+ declare function createRetryable<MODEL extends LanguageModel>(options: Omit<RetryableModelOptions<LanguageModel>, 'model'> & {
6
+ model: GatewayLanguageModelId;
7
+ }): LanguageModel;
5
8
  declare function createRetryable<MODEL extends LanguageModel>(options: RetryableModelOptions<MODEL>): LanguageModel;
6
9
  declare function createRetryable<MODEL extends EmbeddingModel>(options: RetryableModelOptions<MODEL>): EmbeddingModel;
7
10
  //#endregion
@@ -14,9 +17,9 @@ declare const getModelKey: (model: LanguageModel | EmbeddingModel) => string;
14
17
  //#region src/utils.d.ts
15
18
  declare const isObject: (value: unknown) => value is Record<string, unknown>;
16
19
  declare const isString: (value: unknown) => value is string;
17
- declare const isModelV2: (model: unknown) => model is LanguageModel | EmbeddingModel;
18
- declare const isLanguageModelV2: (model: unknown) => model is LanguageModel;
19
- declare const isEmbeddingModelV2: (model: unknown) => model is EmbeddingModel;
20
+ declare const isModel: (model: unknown) => model is LanguageModel | EmbeddingModel;
21
+ declare const isLanguageModel: (model: unknown) => model is LanguageModel;
22
+ declare const isEmbeddingModel: (model: unknown) => model is EmbeddingModel;
20
23
  declare const isStreamResult: (result: LanguageModelGenerate | LanguageModelStream) => result is LanguageModelStream;
21
24
  declare const isGenerateResult: (result: LanguageModelGenerate | LanguageModelStream) => result is LanguageModelGenerate;
22
25
  /**
@@ -65,9 +68,5 @@ declare const isStreamContentPart: (part: LanguageModelStreamPart) => part is _a
65
68
  type: "raw";
66
69
  rawValue: unknown;
67
70
  };
68
- /**
69
- * Type guard to check if a value is a Retry object (has a model property with a MODEL)
70
- */
71
- declare const isRetry: <MODEL extends LanguageModel | EmbeddingModel>(value: unknown) => value is Retry<MODEL>;
72
71
  //#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 };
72
+ 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, isEmbeddingModel, isErrorAttempt, isGenerateResult, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { a as isModelV2, c as isRetry, d as isString, i as isLanguageModelV2, l as isStreamContentPart, n as isErrorAttempt, o as isObject, r as isGenerateResult, s as isResultAttempt, t as isEmbeddingModelV2, u as isStreamResult } from "./utils-C26pNHta.mjs";
1
+ import { a as isModel, c as isStreamContentPart, i as isLanguageModel, l as isStreamResult, n as isErrorAttempt, o as isObject, r as isGenerateResult, s as isResultAttempt, t as isEmbeddingModel, u as isString } from "./utils-C_AwyHeC.mjs";
2
+ import { RetryError, gateway } from "ai";
2
3
  import { delay } from "@ai-sdk/provider-utils";
3
4
  import { getErrorMessage } from "@ai-sdk/provider";
4
- import { RetryError } from "ai";
5
5
 
6
6
  //#region src/calculate-exponential-backoff.ts
7
7
  /**
@@ -27,6 +27,15 @@ function countModelAttempts(model, attempts) {
27
27
  return attempts.filter((a) => getModelKey(a.model) === modelKey).length;
28
28
  }
29
29
 
30
+ //#endregion
31
+ //#region src/resolve-model.ts
32
+ /**
33
+ * Resolve a model string via the AI SDK Gateway to a modelinstance
34
+ */
35
+ function resolveModel(model) {
36
+ return isModel(model) ? model : gateway(model);
37
+ }
38
+
30
39
  //#endregion
31
40
  //#region src/find-retry-model.ts
32
41
  /**
@@ -45,13 +54,19 @@ async function findRetryModel(retries, context) {
45
54
  for (const retry of applicableRetries) {
46
55
  let retryModel;
47
56
  if (typeof retry === "function") retryModel = await retry(context);
48
- else if (isRetry(retry)) retryModel = retry;
57
+ else if (isObject(retry) && "model" in retry) retryModel = retry;
49
58
  else retryModel = { model: retry };
50
59
  if (retryModel) {
60
+ /**
61
+ * The model can be string or an instance.
62
+ * If it is a string, we need to resolve it to an instance.
63
+ */
64
+ const modelValue = retryModel.model;
65
+ const resolvedModel = resolveModel(modelValue);
51
66
  /**
52
67
  * The model key uniquely identifies a model instance (provider + modelId)
53
68
  */
54
- const retryModelKey = getModelKey(retryModel.model);
69
+ const retryModelKey = getModelKey(resolvedModel);
55
70
  /**
56
71
  * Find all attempts with the same model
57
72
  */
@@ -60,7 +75,10 @@ async function findRetryModel(retries, context) {
60
75
  /**
61
76
  * Check if the model can still be retried based on maxAttempts
62
77
  */
63
- if (retryAttempts.length < maxAttempts) return retryModel;
78
+ if (retryAttempts.length < maxAttempts) return {
79
+ ...retryModel,
80
+ model: resolvedModel
81
+ };
64
82
  }
65
83
  }
66
84
  }
@@ -112,6 +130,19 @@ var RetryableEmbeddingModel = class {
112
130
  return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
113
131
  }
114
132
  /**
133
+ * Get the retry call options overrides from a retry configuration.
134
+ */
135
+ getRetryCallOptions(callOptions, currentRetry) {
136
+ const retryOptions = currentRetry?.options ?? {};
137
+ return {
138
+ ...callOptions,
139
+ values: retryOptions.values ?? callOptions.values,
140
+ headers: retryOptions.headers ?? callOptions.headers,
141
+ providerOptions: retryOptions.providerOptions ?? currentRetry?.providerOptions ?? callOptions.providerOptions,
142
+ abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : callOptions.abortSignal
143
+ };
144
+ }
145
+ /**
115
146
  * Execute a function with retry logic for handling errors
116
147
  */
117
148
  async withRetry(input) {
@@ -142,13 +173,17 @@ var RetryableEmbeddingModel = class {
142
173
  };
143
174
  this.options.onRetry?.(context);
144
175
  }
176
+ /**
177
+ * Get the retry call options overrides for this attempt
178
+ */
179
+ const retryCallOptions = this.getRetryCallOptions(input.callOptions, currentRetry);
145
180
  try {
146
181
  return {
147
- result: await input.fn(currentRetry),
182
+ result: await input.fn(retryCallOptions),
148
183
  attempts
149
184
  };
150
185
  } catch (error) {
151
- const { retryModel, attempt } = await this.handleError(error, attempts);
186
+ const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
152
187
  attempts.push(attempt);
153
188
  if (retryModel.delay) {
154
189
  /**
@@ -160,7 +195,7 @@ var RetryableEmbeddingModel = class {
160
195
  * - Attempt 3: 4000ms
161
196
  */
162
197
  const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
163
- await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: input.abortSignal });
198
+ await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
164
199
  }
165
200
  this.currentModel = retryModel.model;
166
201
  currentRetry = retryModel;
@@ -170,11 +205,12 @@ var RetryableEmbeddingModel = class {
170
205
  /**
171
206
  * Handle an error and determine if a retry is needed
172
207
  */
173
- async handleError(error, attempts) {
208
+ async handleError(error, attempts, callOptions) {
174
209
  const errorAttempt = {
175
210
  type: "error",
176
211
  error,
177
- model: this.currentModel
212
+ model: this.currentModel,
213
+ options: callOptions
178
214
  };
179
215
  /**
180
216
  * Save the current attempt
@@ -199,7 +235,7 @@ var RetryableEmbeddingModel = class {
199
235
  attempt: errorAttempt
200
236
  };
201
237
  }
202
- async doEmbed(options) {
238
+ async doEmbed(callOptions) {
203
239
  /**
204
240
  * Always start with the original model
205
241
  */
@@ -207,17 +243,12 @@ var RetryableEmbeddingModel = class {
207
243
  /**
208
244
  * If retries are disabled, bypass retry machinery entirely
209
245
  */
210
- if (this.isDisabled()) return this.currentModel.doEmbed(options);
246
+ if (this.isDisabled()) return this.currentModel.doEmbed(callOptions);
211
247
  const { result } = await this.withRetry({
212
- fn: async (currentRetry) => {
213
- const callOptions = {
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);
248
+ fn: async (retryCallOptions) => {
249
+ return this.currentModel.doEmbed(retryCallOptions);
219
250
  },
220
- abortSignal: options.abortSignal
251
+ callOptions
221
252
  });
222
253
  return result;
223
254
  }
@@ -252,6 +283,27 @@ var RetryableLanguageModel = class {
252
283
  return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
253
284
  }
254
285
  /**
286
+ * Get the retry call options overrides from a retry configuration.
287
+ */
288
+ getRetryCallOptions(callOptions, currentRetry) {
289
+ const retryOptions = currentRetry?.options ?? {};
290
+ return {
291
+ ...callOptions,
292
+ prompt: retryOptions.prompt ?? callOptions.prompt,
293
+ maxOutputTokens: retryOptions.maxOutputTokens ?? callOptions.maxOutputTokens,
294
+ temperature: retryOptions.temperature ?? callOptions.temperature,
295
+ stopSequences: retryOptions.stopSequences ?? callOptions.stopSequences,
296
+ topP: retryOptions.topP ?? callOptions.topP,
297
+ topK: retryOptions.topK ?? callOptions.topK,
298
+ presencePenalty: retryOptions.presencePenalty ?? callOptions.presencePenalty,
299
+ frequencyPenalty: retryOptions.frequencyPenalty ?? callOptions.frequencyPenalty,
300
+ seed: retryOptions.seed ?? callOptions.seed,
301
+ headers: retryOptions.headers ?? callOptions.headers,
302
+ providerOptions: retryOptions.providerOptions ?? currentRetry?.providerOptions ?? callOptions.providerOptions,
303
+ abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : callOptions.abortSignal
304
+ };
305
+ }
306
+ /**
255
307
  * Execute a function with retry logic for handling errors
256
308
  */
257
309
  async withRetry(input) {
@@ -262,7 +314,7 @@ var RetryableLanguageModel = class {
262
314
  /**
263
315
  * Track current retry configuration.
264
316
  */
265
- let currentRetry;
317
+ let currentRetry = input.currentRetry;
266
318
  while (true) {
267
319
  /**
268
320
  * The previous attempt that triggered a retry, or undefined if this is the first attempt
@@ -282,16 +334,20 @@ var RetryableLanguageModel = class {
282
334
  };
283
335
  this.options.onRetry?.(context);
284
336
  }
337
+ /**
338
+ * Get the retry call options overrides for this attempt
339
+ */
340
+ const retryCallOptions = this.getRetryCallOptions(input.callOptions, currentRetry);
285
341
  try {
286
342
  /**
287
343
  * Call the function that may need to be retried
288
344
  */
289
- const result = await input.fn(currentRetry);
345
+ const result = await input.fn(retryCallOptions);
290
346
  /**
291
347
  * Check if the result should trigger a retry (only for generate results, not streams)
292
348
  */
293
349
  if (isGenerateResult(result)) {
294
- const { retryModel, attempt } = await this.handleResult(result, attempts);
350
+ const { retryModel, attempt } = await this.handleResult(result, attempts, retryCallOptions);
295
351
  attempts.push(attempt);
296
352
  if (retryModel) {
297
353
  if (retryModel.delay) {
@@ -304,7 +360,7 @@ var RetryableLanguageModel = class {
304
360
  * - Attempt 3: 4000ms
305
361
  */
306
362
  const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
307
- await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: input.abortSignal });
363
+ await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
308
364
  }
309
365
  this.currentModel = retryModel.model;
310
366
  currentRetry = retryModel;
@@ -319,7 +375,7 @@ var RetryableLanguageModel = class {
319
375
  attempts
320
376
  };
321
377
  } catch (error) {
322
- const { retryModel, attempt } = await this.handleError(error, attempts);
378
+ const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
323
379
  attempts.push(attempt);
324
380
  if (retryModel.delay) {
325
381
  /**
@@ -327,7 +383,7 @@ var RetryableLanguageModel = class {
327
383
  * The delay grows exponentially: baseDelay * backoffFactor^attempts
328
384
  */
329
385
  const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
330
- await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: input.abortSignal });
386
+ await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
331
387
  }
332
388
  this.currentModel = retryModel.model;
333
389
  currentRetry = retryModel;
@@ -337,11 +393,12 @@ var RetryableLanguageModel = class {
337
393
  /**
338
394
  * Handle a successful result and determine if a retry is needed
339
395
  */
340
- async handleResult(result, attempts) {
396
+ async handleResult(result, attempts, callOptions) {
341
397
  const resultAttempt = {
342
398
  type: "result",
343
399
  result,
344
- model: this.currentModel
400
+ model: this.currentModel,
401
+ options: callOptions
345
402
  };
346
403
  const context = {
347
404
  current: resultAttempt,
@@ -355,11 +412,12 @@ var RetryableLanguageModel = class {
355
412
  /**
356
413
  * Handle an error and determine if a retry is needed
357
414
  */
358
- async handleError(error, attempts) {
415
+ async handleError(error, attempts, callOptions) {
359
416
  const errorAttempt = {
360
417
  type: "error",
361
418
  error,
362
- model: this.currentModel
419
+ model: this.currentModel,
420
+ options: callOptions
363
421
  };
364
422
  /**
365
423
  * Save the current attempt
@@ -384,7 +442,7 @@ var RetryableLanguageModel = class {
384
442
  attempt: errorAttempt
385
443
  };
386
444
  }
387
- async doGenerate(options) {
445
+ async doGenerate(callOptions) {
388
446
  /**
389
447
  * Always start with the original model
390
448
  */
@@ -392,21 +450,16 @@ var RetryableLanguageModel = class {
392
450
  /**
393
451
  * If retries are disabled, bypass retry machinery entirely
394
452
  */
395
- if (this.isDisabled()) return this.currentModel.doGenerate(options);
453
+ if (this.isDisabled()) return this.currentModel.doGenerate(callOptions);
396
454
  const { result } = await this.withRetry({
397
- fn: async (currentRetry) => {
398
- const callOptions = {
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);
455
+ fn: async (retryCallOptions) => {
456
+ return this.currentModel.doGenerate(retryCallOptions);
404
457
  },
405
- abortSignal: options.abortSignal
458
+ callOptions
406
459
  });
407
460
  return result;
408
461
  }
409
- async doStream(options) {
462
+ async doStream(callOptions) {
410
463
  /**
411
464
  * Always start with the original model
412
465
  */
@@ -414,22 +467,21 @@ var RetryableLanguageModel = class {
414
467
  /**
415
468
  * If retries are disabled, bypass retry machinery entirely
416
469
  */
417
- if (this.isDisabled()) return this.currentModel.doStream(options);
470
+ if (this.isDisabled()) return this.currentModel.doStream(callOptions);
418
471
  /**
419
472
  * Perform the initial call to doStream with retry logic to handle errors before any data is streamed.
420
473
  */
421
474
  let { result, attempts } = await this.withRetry({
422
- fn: async (currentRetry) => {
423
- const callOptions = {
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);
475
+ fn: async (retryCallOptions) => {
476
+ return this.currentModel.doStream(retryCallOptions);
429
477
  },
430
- abortSignal: options.abortSignal
478
+ callOptions
431
479
  });
432
480
  /**
481
+ * Track the current retry model for computing call options in the stream handler
482
+ */
483
+ let currentRetry;
484
+ /**
433
485
  * Wrap the original stream to handle retries if an error occurs during streaming.
434
486
  */
435
487
  const retryableStream = new ReadableStream({ start: async (controller) => {
@@ -459,11 +511,15 @@ var RetryableLanguageModel = class {
459
511
  controller.close();
460
512
  break;
461
513
  } catch (error) {
514
+ /**
515
+ * Get the retry call options for the failed attempt
516
+ */
517
+ const retryCallOptions = this.getRetryCallOptions(callOptions, currentRetry);
462
518
  /**
463
519
  * Check if the error from the stream can be retried.
464
520
  * Otherwise it will rethrow the error.
465
521
  */
466
- const { retryModel, attempt } = await this.handleError(error, attempts);
522
+ const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
467
523
  /**
468
524
  * Save the attempt
469
525
  */
@@ -474,24 +530,21 @@ var RetryableLanguageModel = class {
474
530
  * The delay grows exponentially: baseDelay * backoffFactor^attempts
475
531
  */
476
532
  const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
477
- await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: options.abortSignal });
533
+ await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
478
534
  }
479
535
  this.currentModel = retryModel.model;
536
+ currentRetry = retryModel;
480
537
  /**
481
538
  * Retry the request by calling doStream again.
482
539
  * This will create a new stream.
483
540
  */
484
541
  const retriedResult = await this.withRetry({
485
- fn: async () => {
486
- const callOptions = {
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);
542
+ fn: async (retryCallOptions$1) => {
543
+ return this.currentModel.doStream(retryCallOptions$1);
492
544
  },
545
+ callOptions,
493
546
  attempts,
494
- abortSignal: options.abortSignal
547
+ currentRetry
495
548
  });
496
549
  /**
497
550
  * Cancel the previous reader and stream if we are retrying
@@ -513,9 +566,16 @@ var RetryableLanguageModel = class {
513
566
  //#endregion
514
567
  //#region src/create-retryable-model.ts
515
568
  function createRetryable(options) {
516
- if ("doEmbed" in options.model) return new RetryableEmbeddingModel(options);
517
- return new RetryableLanguageModel(options);
569
+ const model = isModel(options.model) ? options.model : gateway(options.model);
570
+ if (isEmbeddingModel(model)) return new RetryableEmbeddingModel({
571
+ ...options,
572
+ model
573
+ });
574
+ return new RetryableLanguageModel({
575
+ ...options,
576
+ model
577
+ });
518
578
  }
519
579
 
520
580
  //#endregion
521
- export { createRetryable, getModelKey, isEmbeddingModelV2, isErrorAttempt, isGenerateResult, isLanguageModelV2, isModelV2, isObject, isResultAttempt, isRetry, isStreamContentPart, isStreamResult, isString };
581
+ export { createRetryable, getModelKey, isEmbeddingModel, isErrorAttempt, isGenerateResult, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString };
@@ -1,17 +1,17 @@
1
- import { _ as RetryableOptions, h as Retryable, i as LanguageModel, t as EmbeddingModel } from "../types-TotEvw-5.mjs";
1
+ import { S as Retryable, p as ResolvableLanguageModel, t as EmbeddingModel, w as RetryableOptions } from "../types-Csx79V-W.mjs";
2
2
 
3
3
  //#region src/retryables/content-filter-triggered.d.ts
4
4
 
5
5
  /**
6
6
  * Fallback to a different model if the content filter was triggered.
7
7
  */
8
- declare function contentFilterTriggered<MODEL extends LanguageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
8
+ declare function contentFilterTriggered<MODEL extends ResolvableLanguageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
9
9
  //#endregion
10
10
  //#region src/retryables/request-not-retryable.d.ts
11
11
  /**
12
12
  * Fallback to a different model if the error is non-retryable.
13
13
  */
14
- declare function requestNotRetryable<MODEL extends LanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
14
+ declare function requestNotRetryable<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
15
15
  //#endregion
16
16
  //#region src/retryables/request-timeout.d.ts
17
17
  /**
@@ -19,7 +19,7 @@ declare function requestNotRetryable<MODEL extends LanguageModel | EmbeddingMode
19
19
  * Use in combination with the `abortSignal` option.
20
20
  * If no timeout is specified, a default of 60 seconds is used.
21
21
  */
22
- declare function requestTimeout<MODEL extends LanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
22
+ declare function requestTimeout<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
23
23
  //#endregion
24
24
  //#region src/retryables/retry-after-delay.d.ts
25
25
  /**
@@ -27,7 +27,7 @@ declare function requestTimeout<MODEL extends LanguageModel | EmbeddingModel>(mo
27
27
  * Uses the `Retry-After` or `Retry-After-Ms` headers if present.
28
28
  * Otherwise uses the specified `delay` and `backoffFactor` if provided.
29
29
  */
30
- declare function retryAfterDelay<MODEL extends LanguageModel | EmbeddingModel>(options: RetryableOptions<MODEL>): Retryable<MODEL>;
30
+ declare function retryAfterDelay<MODEL extends ResolvableLanguageModel | EmbeddingModel>(options: RetryableOptions<MODEL>): Retryable<MODEL>;
31
31
  //#endregion
32
32
  //#region src/retryables/service-overloaded.d.ts
33
33
  /**
@@ -37,13 +37,13 @@ declare function retryAfterDelay<MODEL extends LanguageModel | EmbeddingModel>(o
37
37
  * - Response with `type: "overloaded_error"`
38
38
  * - Response with a `message` containing "overloaded"
39
39
  */
40
- declare function serviceOverloaded<MODEL extends LanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
40
+ declare function serviceOverloaded<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
41
41
  //#endregion
42
42
  //#region src/retryables/service-unavailable.d.ts
43
43
  /**
44
44
  * Fallback to a different model if the provider returns a service unavailable error.
45
45
  * This retryable handles HTTP status code 503 (Service Unavailable).
46
46
  */
47
- declare function serviceUnavailable<MODEL extends LanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
47
+ declare function serviceUnavailable<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
48
48
  //#endregion
49
49
  export { contentFilterTriggered, requestNotRetryable, requestTimeout, retryAfterDelay, serviceOverloaded, serviceUnavailable };
@@ -1,6 +1,6 @@
1
- import { d as isString, n as isErrorAttempt, o as isObject, s as isResultAttempt } from "../utils-C26pNHta.mjs";
2
- import { isAbortError } from "@ai-sdk/provider-utils";
1
+ import { n as isErrorAttempt, o as isObject, s as isResultAttempt, u as isString } from "../utils-C_AwyHeC.mjs";
3
2
  import { APICallError } from "ai";
3
+ import { isAbortError } from "@ai-sdk/provider-utils";
4
4
 
5
5
  //#region src/retryables/content-filter-triggered.ts
6
6
  /**
@@ -0,0 +1,129 @@
1
+ import { gateway } from "ai";
2
+ import { EmbeddingModelV2, LanguageModelV2, LanguageModelV2CallOptions, LanguageModelV2StreamPart, SharedV2ProviderOptions } from "@ai-sdk/provider";
3
+
4
+ //#region src/types.d.ts
5
+ type Literals<T> = T extends string ? string extends T ? never : T : never;
6
+ type LanguageModel = LanguageModelV2;
7
+ type EmbeddingModel<VALUE = any> = EmbeddingModelV2<VALUE>;
8
+ type LanguageModelCallOptions = LanguageModelV2CallOptions;
9
+ type LanguageModelStreamPart = LanguageModelV2StreamPart;
10
+ type ProviderOptions = SharedV2ProviderOptions;
11
+ type GatewayLanguageModelId = Parameters<(typeof gateway)['languageModel']>[0];
12
+ type ResolvableLanguageModel = LanguageModel | Literals<GatewayLanguageModelId>;
13
+ type ResolvableModel<MODEL extends LanguageModel | EmbeddingModel> = MODEL extends LanguageModel ? ResolvableLanguageModel : EmbeddingModel;
14
+ type ResolvedModel<MODEL extends ResolvableLanguageModel | EmbeddingModel> = MODEL extends ResolvableLanguageModel ? LanguageModel : EmbeddingModel;
15
+ /**
16
+ * Call options that can be overridden during retry for language models.
17
+ */
18
+ type LanguageModelRetryCallOptions = Partial<Pick<LanguageModelCallOptions, 'prompt' | 'maxOutputTokens' | 'temperature' | 'stopSequences' | 'topP' | 'topK' | 'presencePenalty' | 'frequencyPenalty' | 'seed' | 'headers' | 'providerOptions'>>;
19
+ /**
20
+ * Call options that can be overridden during retry for embedding models.
21
+ */
22
+ type EmbeddingModelRetryCallOptions<VALUE = any> = Partial<Pick<EmbeddingModelCallOptions<VALUE>, 'values' | 'headers' | 'providerOptions'>>;
23
+ /**
24
+ * A retry attempt with an error
25
+ */
26
+ type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel> = {
27
+ type: 'error';
28
+ error: unknown;
29
+ result?: undefined;
30
+ model: MODEL;
31
+ /**
32
+ * The call options used for this attempt.
33
+ */
34
+ options: MODEL extends LanguageModel ? LanguageModelCallOptions : EmbeddingModelCallOptions<any>;
35
+ };
36
+ /**
37
+ * A retry attempt with a successful result
38
+ */
39
+ type RetryResultAttempt = {
40
+ type: 'result';
41
+ result: LanguageModelGenerate;
42
+ error?: undefined;
43
+ model: LanguageModel;
44
+ /**
45
+ * The call options used for this attempt.
46
+ */
47
+ options: LanguageModelCallOptions;
48
+ };
49
+ /**
50
+ * A retry attempt with either an error or a result and the model used
51
+ */
52
+ type RetryAttempt<MODEL extends LanguageModel | EmbeddingModel> = RetryErrorAttempt<MODEL> | RetryResultAttempt;
53
+ /**
54
+ * The context provided to Retryables with the current attempt and all previous attempts.
55
+ */
56
+ type RetryContext<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
57
+ /**
58
+ * Current attempt that caused the retry
59
+ */
60
+ current: RetryAttempt<ResolvedModel<MODEL>>;
61
+ /**
62
+ * All attempts made so far, including the current one
63
+ */
64
+ attempts: Array<RetryAttempt<ResolvedModel<MODEL>>>;
65
+ };
66
+ /**
67
+ * Options for creating a retryable model.
68
+ */
69
+ interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
70
+ model: MODEL;
71
+ retries: Retries<MODEL>;
72
+ disabled?: boolean | (() => boolean);
73
+ onError?: (context: RetryContext<MODEL>) => void;
74
+ onRetry?: (context: RetryContext<MODEL>) => void;
75
+ }
76
+ /**
77
+ * A model to retry with and the maximum number of attempts for that model.
78
+ *
79
+ * The model can be:
80
+ * - The exact MODEL type (instance)
81
+ * - A gateway string literal (for LanguageModel only)
82
+ * - A ResolvableModel<MODEL> (for compatibility with plain model arrays)
83
+ *
84
+ * This flexible approach allows retryable functions to return the exact model type
85
+ * they received without type assertions, while still supporting string-based gateway models.
86
+ */
87
+ type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
88
+ model: MODEL;
89
+ /**
90
+ * Maximum number of attempts for this model.
91
+ */
92
+ maxAttempts?: number;
93
+ /**
94
+ * Delay in milliseconds before retrying.
95
+ */
96
+ delay?: number;
97
+ /**
98
+ * Factor to multiply the delay by for exponential backoff.
99
+ */
100
+ backoffFactor?: number;
101
+ /**
102
+ * Timeout in milliseconds for the retry request.
103
+ * Creates a new AbortSignal with this timeout.
104
+ */
105
+ timeout?: number;
106
+ /**
107
+ * Call options to override for this retry.
108
+ */
109
+ options?: MODEL extends LanguageModel ? Partial<LanguageModelRetryCallOptions> : Partial<EmbeddingModelRetryCallOptions>;
110
+ /**
111
+ * @deprecated Use `options.providerOptions` instead.
112
+ * Provider options to override for this retry.
113
+ * If both `providerOptions` and `options.providerOptions` are set,
114
+ * `options.providerOptions` takes precedence.
115
+ */
116
+ providerOptions?: SharedV2ProviderOptions;
117
+ };
118
+ /**
119
+ * A function that determines whether to retry with a different model based on the current attempt and all previous attempts.
120
+ */
121
+ type Retryable<MODEL extends ResolvableLanguageModel | EmbeddingModel> = (context: RetryContext<MODEL>) => Retry<MODEL> | Promise<Retry<MODEL> | undefined> | undefined;
122
+ type Retries<MODEL extends LanguageModel | EmbeddingModel> = Array<Retryable<ResolvableModel<MODEL>> | Retry<ResolvableModel<MODEL>> | ResolvableModel<MODEL>>;
123
+ type RetryableOptions<MODEL extends ResolvableLanguageModel | EmbeddingModel> = Partial<Omit<Retry<MODEL>, 'model'>>;
124
+ type LanguageModelGenerate = Awaited<ReturnType<LanguageModel['doGenerate']>>;
125
+ type LanguageModelStream = Awaited<ReturnType<LanguageModel['doStream']>>;
126
+ type EmbeddingModelCallOptions<VALUE> = Parameters<EmbeddingModel<VALUE>['doEmbed']>[0];
127
+ type EmbeddingModelEmbed<VALUE = any> = Awaited<ReturnType<EmbeddingModel<VALUE>['doEmbed']>>;
128
+ //#endregion
129
+ export { RetryableModelOptions as C, Retryable as S, Retry as _, GatewayLanguageModelId as a, RetryErrorAttempt as b, LanguageModelGenerate as c, LanguageModelStreamPart as d, ProviderOptions as f, Retries as g, ResolvedModel as h, EmbeddingModelRetryCallOptions as i, LanguageModelRetryCallOptions as l, ResolvableModel as m, EmbeddingModelCallOptions as n, LanguageModel as o, ResolvableLanguageModel as p, EmbeddingModelEmbed as r, LanguageModelCallOptions as s, EmbeddingModel as t, LanguageModelStream as u, RetryAttempt as v, RetryableOptions as w, RetryResultAttempt as x, RetryContext as y };
@@ -1,9 +1,9 @@
1
1
  //#region src/utils.ts
2
2
  const isObject = (value) => typeof value === "object" && value !== null;
3
3
  const isString = (value) => typeof value === "string";
4
- const isModelV2 = (model) => isLanguageModelV2(model) || isEmbeddingModelV2(model);
5
- const isLanguageModelV2 = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && model.specificationVersion === "v2";
6
- const isEmbeddingModelV2 = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && model.specificationVersion === "v2";
4
+ const isModel = (model) => isLanguageModel(model) || isEmbeddingModel(model);
5
+ const isLanguageModel = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && "doGenerate" in model && model.specificationVersion === "v2";
6
+ const isEmbeddingModel = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && "doEmbed" in model && model.specificationVersion === "v2";
7
7
  const isStreamResult = (result) => "stream" in result;
8
8
  const isGenerateResult = (result) => "content" in result;
9
9
  /**
@@ -26,10 +26,6 @@ function isResultAttempt(attempt) {
26
26
  const isStreamContentPart = (part) => {
27
27
  return part.type === "text-delta" || part.type === "reasoning-delta" || part.type === "source" || part.type === "tool-call" || part.type === "tool-result" || part.type === "tool-input-start" || part.type === "tool-input-delta" || part.type === "raw";
28
28
  };
29
- /**
30
- * Type guard to check if a value is a Retry object (has a model property with a MODEL)
31
- */
32
- const isRetry = (value) => isObject(value) && "model" in value && isModelV2(value.model);
33
29
 
34
30
  //#endregion
35
- export { isModelV2 as a, isRetry as c, isString as d, isLanguageModelV2 as i, isStreamContentPart as l, isErrorAttempt as n, isObject as o, isGenerateResult as r, isResultAttempt as s, isEmbeddingModelV2 as t, isStreamResult as u };
31
+ export { isModel as a, isStreamContentPart as c, isLanguageModel as i, isStreamResult as l, isErrorAttempt as n, isObject as o, isGenerateResult as r, isResultAttempt as s, isEmbeddingModel as t, isString as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-retry",
3
- "version": "0.10.3",
3
+ "version": "0.12.0",
4
4
  "description": "AI SDK Retry",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -35,13 +35,14 @@
35
35
  "devDependencies": {
36
36
  "@ai-sdk/anthropic": "^2.0.18",
37
37
  "@ai-sdk/azure": "^2.0.30",
38
+ "@ai-sdk/gateway": "^2.0.15",
38
39
  "@ai-sdk/groq": "^2.0.24",
39
40
  "@ai-sdk/openai": "^2.0.53",
40
41
  "@arethetypeswrong/cli": "^0.18.2",
41
42
  "@biomejs/biome": "^2.2.4",
42
43
  "@total-typescript/tsconfig": "^1.0.4",
43
44
  "@types/node": "^24.3.3",
44
- "ai": "^5.0.44",
45
+ "ai": "^5.0.102",
45
46
  "husky": "^9.1.7",
46
47
  "msw": "^2.11.2",
47
48
  "pkg-pr-new": "^0.0.60",
@@ -1,76 +0,0 @@
1
- import { ProviderOptions } from "@ai-sdk/provider-utils";
2
- import { EmbeddingModelV2, LanguageModelV2, LanguageModelV2CallOptions, LanguageModelV2StreamPart } from "@ai-sdk/provider";
3
-
4
- //#region src/types.d.ts
5
- type LanguageModel = LanguageModelV2;
6
- type EmbeddingModel<VALUE = unknown> = EmbeddingModelV2<VALUE>;
7
- type LanguageModelCallOptions = LanguageModelV2CallOptions;
8
- type LanguageModelStreamPart = LanguageModelV2StreamPart;
9
- /**
10
- * Options for creating a retryable model.
11
- */
12
- interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
13
- model: MODEL;
14
- retries: Retries<MODEL>;
15
- disabled?: boolean | (() => boolean);
16
- onError?: (context: RetryContext<MODEL>) => void;
17
- onRetry?: (context: RetryContext<MODEL>) => void;
18
- }
19
- /**
20
- * The context provided to Retryables with the current attempt and all previous attempts.
21
- */
22
- type RetryContext<MODEL extends LanguageModel | EmbeddingModel> = {
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
- };
32
- /**
33
- * A retry attempt with an error
34
- */
35
- type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel> = {
36
- type: 'error';
37
- error: unknown;
38
- result?: undefined;
39
- model: MODEL;
40
- };
41
- /**
42
- * A retry attempt with a successful result
43
- */
44
- type RetryResultAttempt = {
45
- type: 'result';
46
- result: LanguageModelGenerate;
47
- error?: undefined;
48
- model: LanguageModel;
49
- };
50
- /**
51
- * A retry attempt with either an error or a result and the model used
52
- */
53
- type RetryAttempt<MODEL extends LanguageModel | EmbeddingModel> = RetryErrorAttempt<MODEL> | RetryResultAttempt;
54
- /**
55
- * A model to retry with and the maximum number of attempts for that model.
56
- */
57
- type Retry<MODEL extends LanguageModel | EmbeddingModel> = {
58
- model: MODEL;
59
- maxAttempts?: number;
60
- delay?: number;
61
- backoffFactor?: number;
62
- providerOptions?: ProviderOptions;
63
- timeout?: number;
64
- };
65
- /**
66
- * A function that determines whether to retry with a different model based on the current attempt and all previous attempts.
67
- */
68
- type Retryable<MODEL extends LanguageModel | EmbeddingModel> = (context: RetryContext<MODEL>) => Retry<MODEL> | Promise<Retry<MODEL> | undefined> | undefined;
69
- type Retries<MODEL extends LanguageModel | EmbeddingModel> = Array<Retryable<MODEL> | Retry<MODEL> | MODEL>;
70
- 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
- //#endregion
76
- export { RetryableOptions as _, LanguageModelCallOptions as a, LanguageModelStreamPart as c, RetryAttempt as d, RetryContext as f, RetryableModelOptions as g, Retryable as h, LanguageModel as i, Retries as l, RetryResultAttempt as m, EmbeddingModelCallOptions as n, LanguageModelGenerate as o, RetryErrorAttempt as p, EmbeddingModelEmbed as r, LanguageModelStream as s, EmbeddingModel as t, Retry as u };