ai-retry 1.1.0 → 1.3.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
@@ -41,7 +41,7 @@ Create a retryable model by providing a base model and a list of retryables or f
41
41
  When an error occurs, it will evaluate each retryable in order and use the first one that indicates a retry should be attempted with a different model.
42
42
 
43
43
  > [!NOTE]
44
- > `ai-retry` supports both language models and embedding models.
44
+ > `ai-retry` supports language models, embedding models, and image models.
45
45
 
46
46
  ```typescript
47
47
  import { openai } from '@ai-sdk/openai';
@@ -101,6 +101,28 @@ const result = await embed({
101
101
  console.log(result.embedding);
102
102
  ```
103
103
 
104
+ This also works with image models:
105
+
106
+ ```typescript
107
+ import { openai } from '@ai-sdk/openai';
108
+ import { generateImage } from 'ai';
109
+ import { createRetryable } from 'ai-retry';
110
+
111
+ const retryableModel = createRetryable({
112
+ model: openai.image('dall-e-3'),
113
+ retries: [
114
+ // Retry strategies and fallbacks...
115
+ ],
116
+ });
117
+
118
+ const result = await generateImage({
119
+ model: retryableModel,
120
+ prompt: 'A sunset over mountains',
121
+ });
122
+
123
+ console.log(result.images);
124
+ ```
125
+
104
126
  #### Vercel AI Gateway
105
127
 
106
128
  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.
@@ -356,9 +378,9 @@ There are several built-in dynamic retryables available for common use cases:
356
378
  - [`requestNotRetryable`](./src/retryables/request-not-retryable.ts): Request failed with a non-retryable error.
357
379
  - [`retryAfterDelay`](./src/retryables/retry-after-delay.ts): Retry with delay and exponential backoff and respect `retry-after` headers.
358
380
  - [`serviceOverloaded`](./src/retryables/service-overloaded.ts): Response with status code 529 (service overloaded).
359
- - Use this retryable to handle Anthropic's overloaded errors.
360
381
  - [`serviceUnavailable`](./src/retryables/service-unavailable.ts): Response with status code 503 (service unavailable).
361
382
  - [`schemaMismatch`](./src/retryables/schema-mismatch.ts): Response JSON doesn't match the expected schema from structured output modes (`Output.object()`, `Output.array()`, `Output.choice()`).
383
+ - [`noImageGenerated`](./src/retryables/no-image-generated.ts): Image generation failed with `NoImageGeneratedError`.
362
384
 
363
385
  #### Content Filter
364
386
 
@@ -412,9 +434,6 @@ const result = await generateText({
412
434
 
413
435
  Handle service overload errors (status code 529) by switching to a provider.
414
436
 
415
- > [!NOTE]
416
- > You can use this retryable to handle Anthropic's overloaded errors.
417
-
418
437
  ```typescript
419
438
  import { serviceOverloaded } from 'ai-retry/retryables';
420
439
 
@@ -453,6 +472,29 @@ const retryableModel = createRetryable({
453
472
  });
454
473
  ```
455
474
 
475
+ #### No Image Generated
476
+
477
+ Handle image generation failures by switching to a different model.
478
+
479
+ ```typescript
480
+ import { openai } from '@ai-sdk/openai';
481
+ import { generateImage } from 'ai';
482
+ import { createRetryable } from 'ai-retry';
483
+ import { noImageGenerated } from 'ai-retry/retryables';
484
+
485
+ const retryableModel = createRetryable({
486
+ model: openai.image('dall-e-3'),
487
+ retries: [
488
+ noImageGenerated(openai.image('dall-e-2')), // Fallback to DALL-E 2
489
+ ],
490
+ });
491
+
492
+ const result = await generateImage({
493
+ model: retryableModel,
494
+ prompt: 'A sunset over mountains',
495
+ });
496
+ ```
497
+
456
498
  #### Request Not Retryable
457
499
 
458
500
  Handle cases where the base model fails with a non-retryable error.
@@ -736,6 +778,17 @@ The following options can be overridden:
736
778
  | [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#headers) | Additional HTTP headers |
737
779
  | [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-core/embed#provideroptions) | Provider-specific options |
738
780
 
781
+ ##### Image Model Options
782
+
783
+ | Option | Description |
784
+ |--------|-------------|
785
+ | [`n`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#n) | Number of images to generate |
786
+ | [`size`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#size) | Size of generated images |
787
+ | [`aspectRatio`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#aspectratio) | Aspect ratio of generated images |
788
+ | [`seed`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#seed) | Random seed for reproducibility |
789
+ | [`headers`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#headers) | Additional HTTP headers |
790
+ | [`providerOptions`](https://ai-sdk.dev/docs/reference/ai-sdk-core/generate-image#provideroptions) | Provider-specific options |
791
+
739
792
  #### Logging
740
793
 
741
794
  You can use the following callbacks to log retry attempts and errors:
@@ -757,6 +810,46 @@ const retryableModel = createRetryable({
757
810
  });
758
811
  ```
759
812
 
813
+ #### Reset
814
+
815
+ 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.
816
+
817
+ | Value | Description |
818
+ |-------|-------------|
819
+ | `after-request` | Reset immediately after the next request (default) |
820
+ | `after-N-requests` | Keep the retry model for the next **N** requests, then reset |
821
+ | `after-N-seconds` | Keep the retry model for **N** seconds, then reset |
822
+
823
+ ##### Reset after each request (default)
824
+
825
+ ```typescript
826
+ const retryableModel = createRetryable({
827
+ model: openai('gpt-4o-mini'),
828
+ retries: [anthropic('claude-sonnet-4-20250514')],
829
+ reset: 'after-request', // default: always start with the base model
830
+ });
831
+ ```
832
+
833
+ ##### Keep the retry model for N requests
834
+
835
+ ```typescript
836
+ const retryableModel = createRetryable({
837
+ model: openai('gpt-4o-mini'),
838
+ retries: [anthropic('claude-sonnet-4-20250514')],
839
+ reset: 'after-5-requests', // use the retry model for 5 more requests before resetting
840
+ });
841
+ ```
842
+
843
+ ##### Keep the retry model for N seconds
844
+
845
+ ```typescript
846
+ const retryableModel = createRetryable({
847
+ model: openai('gpt-4o-mini'),
848
+ retries: [anthropic('claude-sonnet-4-20250514')],
849
+ reset: 'after-30-seconds', // use the retry model for 30 seconds before resetting
850
+ });
851
+ ```
852
+
760
853
  ### Streaming
761
854
 
762
855
  Errors during streaming requests can occur in two ways:
@@ -768,15 +861,16 @@ In the second case, errors during stream processing will not always be retried,
768
861
 
769
862
  ### API Reference
770
863
 
771
- #### `createRetryable(options: RetryableModelOptions): LanguageModelV2 | EmbeddingModelV2`
864
+ #### `createRetryable(options: RetryableModelOptions): LanguageModelV3 | EmbeddingModelV3 | ImageModelV3`
772
865
 
773
- Creates a retryable model that works with both language models and embedding models.
866
+ Creates a retryable model that works with language models, embedding models, and image models.
774
867
 
775
868
  ```ts
776
- interface RetryableModelOptions<MODEL extends LanguageModelV2 | EmbeddingModelV2> {
869
+ interface RetryableModelOptions<MODEL extends LanguageModelV3 | EmbeddingModelV3 | ImageModelV3> {
777
870
  model: MODEL;
778
871
  retries: Array<Retryable<MODEL> | MODEL>;
779
872
  disabled?: boolean | (() => boolean);
873
+ reset?: Reset;
780
874
  onError?: (context: RetryContext<MODEL>) => void;
781
875
  onRetry?: (context: RetryContext<MODEL>) => void;
782
876
  }
@@ -786,9 +880,25 @@ interface RetryableModelOptions<MODEL extends LanguageModelV2 | EmbeddingModelV2
786
880
  - `model`: The base model to use for the initial request.
787
881
  - `retries`: Array of retryables (functions, models, or retry objects) to attempt on failure.
788
882
  - `disabled`: Disable all retry logic. Can be a boolean or function returning boolean. Default: `false` (retries enabled).
883
+ - `reset`: Controls when to reset back to the base model after a successful retry. Default: `after-request`.
789
884
  - `onError`: Callback invoked when an error occurs.
790
885
  - `onRetry`: Callback invoked before attempting a retry.
791
886
 
887
+ #### `Reset`
888
+
889
+ Controls when the sticky model resets back to the base model after a successful retry.
890
+
891
+ ```ts
892
+ type Reset =
893
+ | 'after-request'
894
+ | `after-${number}-requests`
895
+ | `after-${number}-seconds`;
896
+ ```
897
+
898
+ - `after-request` — reset immediately after the next request (default).
899
+ - `after-N-requests` — keep the retry model for the next N requests, then reset.
900
+ - `after-N-seconds` — keep the retry model for N seconds, then reset.
901
+
792
902
  #### `Retryable`
793
903
 
794
904
  A `Retryable` is a function that receives a `RetryContext` with the current error or result and model and all previous attempts.
@@ -802,17 +912,17 @@ type Retryable = (
802
912
 
803
913
  #### `Retry`
804
914
 
805
- A `Retry` specifies the model to retry and optional settings. The available options depend on the model type (language model or embedding model).
915
+ A `Retry` specifies the model to retry and optional settings. The available options depend on the model type (language model, embedding model, or image model).
806
916
 
807
917
  ```typescript
808
918
  interface Retry {
809
- model: LanguageModelV2 | EmbeddingModelV2;
919
+ model: LanguageModelV3 | EmbeddingModelV3 | ImageModelV3;
810
920
  maxAttempts?: number; // Maximum retry attempts per model (default: 1)
811
921
  delay?: number; // Delay in milliseconds before retrying
812
922
  backoffFactor?: number; // Multiplier for exponential backoff
813
923
  timeout?: number; // Timeout in milliseconds for the retry attempt
814
924
  providerOptions?: ProviderOptions; // @deprecated - use options.providerOptions instead
815
- options?: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions; // Call options to override for this retry
925
+ options?: LanguageModelV3CallOptions | EmbeddingModelV3CallOptions | ImageModelV3CallOptions; // Call options to override for this retry
816
926
  }
817
927
  ```
818
928
 
@@ -832,22 +942,22 @@ interface RetryContext {
832
942
  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.
833
943
 
834
944
  ```typescript
835
- // For both language and embedding models
945
+ // For language, embedding, and image models
836
946
  type RetryAttempt =
837
- | {
838
- type: 'error';
839
- error: unknown;
840
- model: LanguageModelV2 | EmbeddingModelV2;
841
- options: LanguageModelV2CallOptions | EmbeddingModelV2CallOptions;
947
+ | {
948
+ type: 'error';
949
+ error: unknown;
950
+ model: LanguageModelV3 | EmbeddingModelV3 | ImageModelV3;
951
+ options: LanguageModelV3CallOptions | EmbeddingModelV3CallOptions | ImageModelV3CallOptions;
842
952
  }
843
- | {
844
- type: 'result';
845
- result: LanguageModelV2Generate;
846
- model: LanguageModelV2;
847
- options: LanguageModelV2CallOptions;
953
+ | {
954
+ type: 'result';
955
+ result: LanguageModelV3Generate;
956
+ model: LanguageModelV3;
957
+ options: LanguageModelV3CallOptions;
848
958
  };
849
959
 
850
- // Note: Result-based retries only apply to language models, not embedding models
960
+ // Note: Result-based retries only apply to language models, not embedding or image models
851
961
 
852
962
  // Type guards for discriminating attempts
853
963
  function isErrorAttempt(attempt: RetryAttempt): attempt is RetryErrorAttempt;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
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-Bty5BU37.mjs";
1
+ import { C as RetryAttempt, D as Retryable, E as RetryResultAttempt, O as RetryableModelOptions, S as Retry, T as RetryErrorAttempt, _ as Reset, a as GatewayLanguageModelId, b as ResolvedModel, c as ImageModelGenerate, d as LanguageModelCallOptions, f as LanguageModelGenerate, g as ProviderOptions, h as LanguageModelStreamPart, i as EmbeddingModelRetryCallOptions, k as RetryableOptions, l as ImageModelRetryCallOptions, m as LanguageModelStream, n as EmbeddingModelCallOptions, o as ImageModel, p as LanguageModelRetryCallOptions, r as EmbeddingModelEmbed, s as ImageModelCallOptions, t as EmbeddingModel, u as LanguageModel, v as ResolvableLanguageModel, w as RetryContext, x as Retries, y as ResolvableModel } from "./types-wEZKtEcH.mjs";
2
2
  import * as _ai_sdk_provider0 from "@ai-sdk/provider";
3
3
 
4
4
  //#region src/create-retryable-model.d.ts
@@ -7,19 +7,21 @@ declare function createRetryable<MODEL extends LanguageModel>(options: Omit<Retr
7
7
  }): LanguageModel;
8
8
  declare function createRetryable<MODEL extends LanguageModel>(options: RetryableModelOptions<MODEL>): LanguageModel;
9
9
  declare function createRetryable<MODEL extends EmbeddingModel>(options: RetryableModelOptions<MODEL>): EmbeddingModel;
10
+ declare function createRetryable<MODEL extends ImageModel>(options: RetryableModelOptions<MODEL>): ImageModel;
10
11
  //#endregion
11
12
  //#region src/get-model-key.d.ts
12
13
  /**
13
- * Generate a unique key for a LanguageModel instance.
14
+ * Generate a unique key for a model instance.
14
15
  */
15
- declare const getModelKey: (model: LanguageModel | EmbeddingModel) => string;
16
+ declare const getModelKey: (model: LanguageModel | EmbeddingModel | ImageModel) => string;
16
17
  //#endregion
17
18
  //#region src/utils.d.ts
18
19
  declare const isObject: (value: unknown) => value is Record<string, unknown>;
19
20
  declare const isString: (value: unknown) => value is string;
20
- declare const isModel: (model: unknown) => model is LanguageModel | EmbeddingModel;
21
+ declare const isModel: (model: unknown) => model is LanguageModel | EmbeddingModel | ImageModel;
21
22
  declare const isLanguageModel: (model: unknown) => model is LanguageModel;
22
23
  declare const isEmbeddingModel: (model: unknown) => model is EmbeddingModel;
24
+ declare const isImageModel: (model: unknown) => model is ImageModel;
23
25
  declare const isStreamResult: (result: LanguageModelGenerate | LanguageModelStream) => result is LanguageModelStream;
24
26
  declare const isGenerateResult: (result: LanguageModelGenerate | LanguageModelStream) => result is LanguageModelGenerate;
25
27
  /**
@@ -73,4 +75,4 @@ declare const isAbortError: (error: unknown) => boolean;
73
75
  */
74
76
  declare const isTimeoutError: (error: unknown) => boolean;
75
77
  //#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 };
78
+ export { EmbeddingModel, EmbeddingModelCallOptions, EmbeddingModelEmbed, EmbeddingModelRetryCallOptions, GatewayLanguageModelId, ImageModel, ImageModelCallOptions, ImageModelGenerate, ImageModelRetryCallOptions, 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, isImageModel, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString, isTimeoutError };
package/dist/index.mjs CHANGED
@@ -1,8 +1,88 @@
1
- import { a as isLanguageModel, c as isResultAttempt, d as isString, f as isTimeoutError, i as isGenerateResult, l as isStreamContentPart, n as isEmbeddingModel, o as isModel, r as isErrorAttempt, s as isObject, t as isAbortError, u as isStreamResult } from "./utils-fYGELi1C.mjs";
1
+ import { a as isImageModel, c as isObject, d as isStreamResult, f as isString, i as isGenerateResult, l as isResultAttempt, n as isEmbeddingModel, o as isLanguageModel, p as isTimeoutError, r as isErrorAttempt, s as isModel, t as isAbortError, u as isStreamContentPart } from "./utils-CfnsSGrw.mjs";
2
2
  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.
@@ -14,7 +94,7 @@ function calculateExponentialBackoff(baseDelay, backoffFactor = 1, attempts) {
14
94
  //#endregion
15
95
  //#region src/get-model-key.ts
16
96
  /**
17
- * Generate a unique key for a LanguageModel instance.
97
+ * Generate a unique key for a model instance.
18
98
  */
19
99
  const getModelKey = (model) => {
20
100
  return `${model.provider}/${model.modelId}`;
@@ -30,7 +110,7 @@ function countModelAttempts(model, attempts) {
30
110
  //#endregion
31
111
  //#region src/resolve-model.ts
32
112
  /**
33
- * Resolve a model string via the AI SDK Gateway to a modelinstance
113
+ * Resolve a model string via the AI SDK Gateway to a model instance
34
114
  */
35
115
  function resolveModel(model) {
36
116
  return isModel(model) ? model : gateway(model);
@@ -53,9 +133,20 @@ async function findRetryModel(retries, context) {
53
133
  */
54
134
  for (const retry of applicableRetries) {
55
135
  let retryModel;
56
- if (typeof retry === "function") retryModel = await retry(context);
57
- else if (isObject(retry) && "model" in retry) retryModel = retry;
58
- else retryModel = { model: retry };
136
+ if (typeof retry === `function`)
137
+ /**
138
+ * Function retryable - call it with context
139
+ * The function can be either Retryable<MODEL> or Retryable<ResolvableLanguageModel>
140
+ * At runtime, both work because the context is structurally compatible
141
+ * We use type assertion here because TypeScript can't prove the union type compatibility
142
+ */
143
+ retryModel = await retry(context);
144
+ else if (isObject(retry) && `model` in retry)
145
+ /** Static Retry object */
146
+ retryModel = retry;
147
+ else
148
+ /** Plain model */
149
+ retryModel = { model: retry };
59
150
  if (retryModel) {
60
151
  /**
61
152
  * The model can be string or an instance.
@@ -100,11 +191,8 @@ function prepareRetryError(error, attempts) {
100
191
 
101
192
  //#endregion
102
193
  //#region src/retryable-embedding-model.ts
103
- var RetryableEmbeddingModel = class {
194
+ var RetryableEmbeddingModel = class extends BaseRetryableModel {
104
195
  specificationVersion = "v3";
105
- baseModel;
106
- currentModel;
107
- options;
108
196
  get modelId() {
109
197
  return this.currentModel.modelId;
110
198
  }
@@ -117,18 +205,6 @@ var RetryableEmbeddingModel = class {
117
205
  get supportsParallelCalls() {
118
206
  return this.currentModel.supportsParallelCalls;
119
207
  }
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
208
  /**
133
209
  * Get the retry call options overrides from a retry configuration.
134
210
  */
@@ -238,9 +314,10 @@ var RetryableEmbeddingModel = class {
238
314
  }
239
315
  async doEmbed(callOptions) {
240
316
  /**
241
- * Always start with the original model
317
+ * Resolve the starting model (base or sticky)
242
318
  */
243
- this.currentModel = this.baseModel;
319
+ const startModel = this.resolveStartModel();
320
+ this.currentModel = startModel;
244
321
  /**
245
322
  * If retries are disabled, bypass retry machinery entirely
246
323
  */
@@ -251,37 +328,169 @@ var RetryableEmbeddingModel = class {
251
328
  },
252
329
  callOptions
253
330
  });
331
+ this.updateStickyModel(startModel);
254
332
  return result;
255
333
  }
256
334
  };
257
335
 
258
336
  //#endregion
259
- //#region src/retryable-language-model.ts
260
- var RetryableLanguageModel = class {
337
+ //#region src/retryable-image-model.ts
338
+ var RetryableImageModel = class extends BaseRetryableModel {
261
339
  specificationVersion = "v3";
262
- baseModel;
263
- currentModel;
264
- options;
265
340
  get modelId() {
266
341
  return this.currentModel.modelId;
267
342
  }
268
343
  get provider() {
269
344
  return this.currentModel.provider;
270
345
  }
271
- get supportedUrls() {
272
- return this.currentModel.supportedUrls;
346
+ get maxImagesPerCall() {
347
+ return this.currentModel.maxImagesPerCall;
273
348
  }
274
- constructor(options) {
275
- this.options = options;
276
- this.baseModel = options.model;
277
- this.currentModel = options.model;
349
+ /**
350
+ * Get the retry call options overrides from a retry configuration.
351
+ */
352
+ getRetryCallOptions(callOptions, currentRetry) {
353
+ const retryOptions = currentRetry?.options ?? {};
354
+ return {
355
+ ...callOptions,
356
+ n: retryOptions.n ?? callOptions.n,
357
+ size: retryOptions.size ?? callOptions.size,
358
+ aspectRatio: retryOptions.aspectRatio ?? callOptions.aspectRatio,
359
+ seed: retryOptions.seed ?? callOptions.seed,
360
+ headers: retryOptions.headers ?? callOptions.headers,
361
+ providerOptions: retryOptions.providerOptions ?? currentRetry?.providerOptions ?? callOptions.providerOptions,
362
+ abortSignal: currentRetry?.timeout ? AbortSignal.timeout(currentRetry.timeout) : callOptions.abortSignal
363
+ };
278
364
  }
279
365
  /**
280
- * Check if retries are disabled
366
+ * Execute a function with retry logic for handling errors
281
367
  */
282
- isDisabled() {
283
- if (this.options.disabled === void 0) return false;
284
- return typeof this.options.disabled === "function" ? this.options.disabled() : this.options.disabled;
368
+ async withRetry(input) {
369
+ /**
370
+ * Track all attempts.
371
+ */
372
+ const attempts = input.attempts ?? [];
373
+ /**
374
+ * Track current retry configuration.
375
+ */
376
+ let currentRetry;
377
+ while (true) {
378
+ /**
379
+ * The previous attempt that triggered a retry, or undefined if this is the first attempt
380
+ */
381
+ const previousAttempt = attempts.at(-1);
382
+ /**
383
+ * Call the onRetry handler if provided.
384
+ * Skip on the first attempt since no previous attempt exists yet.
385
+ */
386
+ if (previousAttempt) {
387
+ const context = {
388
+ current: {
389
+ ...previousAttempt,
390
+ model: this.currentModel
391
+ },
392
+ attempts: [...attempts]
393
+ };
394
+ this.options.onRetry?.(context);
395
+ }
396
+ /**
397
+ * Get the retry call options overrides for this attempt
398
+ */
399
+ const retryCallOptions = this.getRetryCallOptions(input.callOptions, currentRetry);
400
+ try {
401
+ return {
402
+ result: await input.fn(retryCallOptions),
403
+ attempts
404
+ };
405
+ } catch (error) {
406
+ /** Don't retry if user manually aborted the request. */
407
+ /** TimeoutError from AbortSignal.timeout() will still be handled by retry handlers. */
408
+ if (isAbortError(error)) throw error;
409
+ const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
410
+ attempts.push(attempt);
411
+ if (retryModel.delay) {
412
+ /**
413
+ * Calculate exponential backoff delay based on the number of attempts for this specific model.
414
+ * The delay grows exponentially: baseDelay * backoffFactor^attempts
415
+ * Example: With delay=1000ms and backoffFactor=2:
416
+ * - Attempt 1: 1000ms
417
+ * - Attempt 2: 2000ms
418
+ * - Attempt 3: 4000ms
419
+ */
420
+ const modelAttemptsCount = countModelAttempts(retryModel.model, attempts);
421
+ await delay(calculateExponentialBackoff(retryModel.delay, retryModel.backoffFactor, modelAttemptsCount), { abortSignal: retryCallOptions.abortSignal });
422
+ }
423
+ this.currentModel = retryModel.model;
424
+ currentRetry = retryModel;
425
+ }
426
+ }
427
+ }
428
+ /**
429
+ * Handle an error and determine if a retry is needed
430
+ */
431
+ async handleError(error, attempts, callOptions) {
432
+ const errorAttempt = {
433
+ type: "error",
434
+ error,
435
+ model: this.currentModel,
436
+ options: callOptions
437
+ };
438
+ /**
439
+ * Save the current attempt
440
+ */
441
+ const updatedAttempts = [...attempts, errorAttempt];
442
+ const context = {
443
+ current: errorAttempt,
444
+ attempts: updatedAttempts
445
+ };
446
+ this.options.onError?.(context);
447
+ const retryModel = await findRetryModel(this.options.retries, context);
448
+ /**
449
+ * Handler didn't return any models to try next, rethrow the error.
450
+ * If we retried the request, wrap the error into a `RetryError` for better visibility.
451
+ */
452
+ if (!retryModel) {
453
+ if (updatedAttempts.length > 1) throw prepareRetryError(error, updatedAttempts);
454
+ throw error;
455
+ }
456
+ return {
457
+ retryModel,
458
+ attempt: errorAttempt
459
+ };
460
+ }
461
+ async doGenerate(callOptions) {
462
+ /**
463
+ * Resolve the starting model (base or sticky)
464
+ */
465
+ const startModel = this.resolveStartModel();
466
+ this.currentModel = startModel;
467
+ /**
468
+ * If retries are disabled, bypass retry machinery entirely
469
+ */
470
+ if (this.isDisabled()) return this.currentModel.doGenerate(callOptions);
471
+ const { result } = await this.withRetry({
472
+ fn: async (retryCallOptions) => {
473
+ return this.currentModel.doGenerate(retryCallOptions);
474
+ },
475
+ callOptions
476
+ });
477
+ this.updateStickyModel(startModel);
478
+ return result;
479
+ }
480
+ };
481
+
482
+ //#endregion
483
+ //#region src/retryable-language-model.ts
484
+ var RetryableLanguageModel = class extends BaseRetryableModel {
485
+ specificationVersion = "v3";
486
+ get modelId() {
487
+ return this.currentModel.modelId;
488
+ }
489
+ get provider() {
490
+ return this.currentModel.provider;
491
+ }
492
+ get supportedUrls() {
493
+ return this.currentModel.supportedUrls;
285
494
  }
286
495
  /**
287
496
  * Get the retry call options overrides from a retry configuration.
@@ -446,9 +655,10 @@ var RetryableLanguageModel = class {
446
655
  }
447
656
  async doGenerate(callOptions) {
448
657
  /**
449
- * Always start with the original model
658
+ * Resolve the starting model (base or sticky)
450
659
  */
451
- this.currentModel = this.baseModel;
660
+ const startModel = this.resolveStartModel();
661
+ this.currentModel = startModel;
452
662
  /**
453
663
  * If retries are disabled, bypass retry machinery entirely
454
664
  */
@@ -459,13 +669,15 @@ var RetryableLanguageModel = class {
459
669
  },
460
670
  callOptions
461
671
  });
672
+ this.updateStickyModel(startModel);
462
673
  return result;
463
674
  }
464
675
  async doStream(callOptions) {
465
676
  /**
466
- * Always start with the original model
677
+ * Resolve the starting model (base or sticky)
467
678
  */
468
- this.currentModel = this.baseModel;
679
+ const startModel = this.resolveStartModel();
680
+ this.currentModel = startModel;
469
681
  /**
470
682
  * If retries are disabled, bypass retry machinery entirely
471
683
  */
@@ -479,6 +691,7 @@ var RetryableLanguageModel = class {
479
691
  },
480
692
  callOptions
481
693
  });
694
+ this.updateStickyModel(startModel);
482
695
  /**
483
696
  * Track the current retry model for computing call options in the stream handler
484
697
  */
@@ -554,6 +767,7 @@ var RetryableLanguageModel = class {
554
767
  await reader?.cancel();
555
768
  result = retriedResult.result;
556
769
  attempts = retriedResult.attempts;
770
+ this.updateStickyModel(startModel);
557
771
  } finally {
558
772
  reader?.releaseLock();
559
773
  }
@@ -573,6 +787,10 @@ function createRetryable(options) {
573
787
  ...options,
574
788
  model
575
789
  });
790
+ if (isImageModel(model)) return new RetryableImageModel({
791
+ ...options,
792
+ model
793
+ });
576
794
  return new RetryableLanguageModel({
577
795
  ...options,
578
796
  model
@@ -580,4 +798,4 @@ function createRetryable(options) {
580
798
  }
581
799
 
582
800
  //#endregion
583
- export { createRetryable, getModelKey, isAbortError, isEmbeddingModel, isErrorAttempt, isGenerateResult, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString, isTimeoutError };
801
+ export { createRetryable, getModelKey, isAbortError, isEmbeddingModel, isErrorAttempt, isGenerateResult, isImageModel, isLanguageModel, isModel, isObject, isResultAttempt, isStreamContentPart, isStreamResult, isString, isTimeoutError };
@@ -1,4 +1,4 @@
1
- import { S as Retryable, p as ResolvableLanguageModel, t as EmbeddingModel, w as RetryableOptions } from "../types-Bty5BU37.mjs";
1
+ import { D as Retryable, k as RetryableOptions, o as ImageModel, t as EmbeddingModel, v as ResolvableLanguageModel } from "../types-wEZKtEcH.mjs";
2
2
 
3
3
  //#region src/retryables/content-filter-triggered.d.ts
4
4
  /**
@@ -6,11 +6,17 @@ import { S as Retryable, p as ResolvableLanguageModel, t as EmbeddingModel, w as
6
6
  */
7
7
  declare function contentFilterTriggered<MODEL extends ResolvableLanguageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
8
8
  //#endregion
9
+ //#region src/retryables/no-image-generated.d.ts
10
+ /**
11
+ * Fallback to a different model if image generation fails with NoImageGeneratedError.
12
+ */
13
+ declare function noImageGenerated<MODEL extends ImageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
14
+ //#endregion
9
15
  //#region src/retryables/request-not-retryable.d.ts
10
16
  /**
11
17
  * Fallback to a different model if the error is non-retryable.
12
18
  */
13
- declare function requestNotRetryable<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
19
+ declare function requestNotRetryable<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
14
20
  //#endregion
15
21
  //#region src/retryables/request-timeout.d.ts
16
22
  /**
@@ -18,7 +24,7 @@ declare function requestNotRetryable<MODEL extends ResolvableLanguageModel | Emb
18
24
  * Use in combination with the `abortSignal` option.
19
25
  * If no timeout is specified, a default of 60 seconds is used.
20
26
  */
21
- declare function requestTimeout<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
27
+ declare function requestTimeout<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
22
28
  //#endregion
23
29
  //#region src/retryables/retry-after-delay.d.ts
24
30
  /**
@@ -26,7 +32,7 @@ declare function requestTimeout<MODEL extends ResolvableLanguageModel | Embeddin
26
32
  * Uses the `Retry-After` or `Retry-After-Ms` headers if present.
27
33
  * Otherwise uses the specified `delay` and `backoffFactor` if provided.
28
34
  */
29
- declare function retryAfterDelay<MODEL extends ResolvableLanguageModel | EmbeddingModel>(options: RetryableOptions<MODEL>): Retryable<MODEL>;
35
+ declare function retryAfterDelay<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel>(options: RetryableOptions<MODEL>): Retryable<MODEL>;
30
36
  //#endregion
31
37
  //#region src/retryables/schema-mismatch.d.ts
32
38
  /**
@@ -58,13 +64,13 @@ declare function schemaMismatch<MODEL extends ResolvableLanguageModel>(model: MO
58
64
  * - Response with `type: "overloaded_error"`
59
65
  * - Response with a `message` containing "overloaded"
60
66
  */
61
- declare function serviceOverloaded<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
67
+ declare function serviceOverloaded<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
62
68
  //#endregion
63
69
  //#region src/retryables/service-unavailable.d.ts
64
70
  /**
65
71
  * Fallback to a different model if the provider returns a service unavailable error.
66
72
  * This retryable handles HTTP status code 503 (Service Unavailable).
67
73
  */
68
- declare function serviceUnavailable<MODEL extends ResolvableLanguageModel | EmbeddingModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
74
+ declare function serviceUnavailable<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel>(model: MODEL, options?: RetryableOptions<MODEL>): Retryable<MODEL>;
69
75
  //#endregion
70
- export { contentFilterTriggered, requestNotRetryable, requestTimeout, retryAfterDelay, schemaMismatch, serviceOverloaded, serviceUnavailable };
76
+ export { contentFilterTriggered, noImageGenerated, requestNotRetryable, requestTimeout, retryAfterDelay, schemaMismatch, serviceOverloaded, serviceUnavailable };
@@ -1,5 +1,5 @@
1
- import { c as isResultAttempt, d as isString, f as isTimeoutError, r as isErrorAttempt, s as isObject } from "../utils-fYGELi1C.mjs";
2
- import { APICallError } from "ai";
1
+ import { c as isObject, f as isString, l as isResultAttempt, p as isTimeoutError, r as isErrorAttempt } from "../utils-CfnsSGrw.mjs";
2
+ import { APICallError, NoImageGeneratedError } from "ai";
3
3
  import { safeParseJSON } from "@ai-sdk/provider-utils";
4
4
  import { fromJSONSchema } from "zod";
5
5
 
@@ -29,6 +29,25 @@ function contentFilterTriggered(model, options) {
29
29
  };
30
30
  }
31
31
 
32
+ //#endregion
33
+ //#region src/retryables/no-image-generated.ts
34
+ /**
35
+ * Fallback to a different model if image generation fails with NoImageGeneratedError.
36
+ */
37
+ function noImageGenerated(model, options) {
38
+ return (context) => {
39
+ const { current } = context;
40
+ if (isErrorAttempt(current)) {
41
+ const { error } = current;
42
+ if (NoImageGeneratedError.isInstance(error)) return {
43
+ model,
44
+ maxAttempts: 1,
45
+ ...options
46
+ };
47
+ }
48
+ };
49
+ }
50
+
32
51
  //#endregion
33
52
  //#region src/retryables/request-not-retryable.ts
34
53
  /**
@@ -219,4 +238,4 @@ function serviceUnavailable(model, options) {
219
238
  }
220
239
 
221
240
  //#endregion
222
- export { contentFilterTriggered, requestNotRetryable, requestTimeout, retryAfterDelay, schemaMismatch, serviceOverloaded, serviceUnavailable };
241
+ export { contentFilterTriggered, noImageGenerated, requestNotRetryable, requestTimeout, retryAfterDelay, schemaMismatch, serviceOverloaded, serviceUnavailable };
@@ -1,17 +1,19 @@
1
1
  import { gateway } from "ai";
2
- import { EmbeddingModelV3, LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3StreamPart, SharedV3ProviderOptions } from "@ai-sdk/provider";
2
+ import { EmbeddingModelV3, ImageModelV3, ImageModelV3CallOptions, LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3StreamPart, SharedV3ProviderOptions } from "@ai-sdk/provider";
3
3
 
4
4
  //#region src/types.d.ts
5
5
  type Literals<T> = T extends string ? string extends T ? never : T : never;
6
6
  type LanguageModel = LanguageModelV3;
7
7
  type EmbeddingModel = EmbeddingModelV3;
8
+ type ImageModel = ImageModelV3;
8
9
  type LanguageModelCallOptions = LanguageModelV3CallOptions;
9
10
  type LanguageModelStreamPart = LanguageModelV3StreamPart;
11
+ type ImageModelCallOptions = ImageModelV3CallOptions;
10
12
  type ProviderOptions = SharedV3ProviderOptions;
11
13
  type GatewayLanguageModelId = Parameters<(typeof gateway)['languageModel']>[0];
12
14
  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
+ type ResolvableModel<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = MODEL extends LanguageModel ? ResolvableLanguageModel : MODEL extends EmbeddingModel ? EmbeddingModel : ImageModel;
16
+ type ResolvedModel<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel> = MODEL extends ResolvableLanguageModel ? LanguageModel : MODEL extends EmbeddingModel ? EmbeddingModel : ImageModel;
15
17
  /**
16
18
  * Call options that can be overridden during retry for language models.
17
19
  */
@@ -20,10 +22,14 @@ type LanguageModelRetryCallOptions = Partial<Pick<LanguageModelCallOptions, 'pro
20
22
  * Call options that can be overridden during retry for embedding models.
21
23
  */
22
24
  type EmbeddingModelRetryCallOptions = Partial<Pick<EmbeddingModelCallOptions, 'values' | 'headers' | 'providerOptions'>>;
25
+ /**
26
+ * Call options that can be overridden during retry for image models.
27
+ */
28
+ type ImageModelRetryCallOptions = Partial<Pick<ImageModelCallOptions, 'n' | 'size' | 'aspectRatio' | 'seed' | 'headers' | 'providerOptions'>>;
23
29
  /**
24
30
  * A retry attempt with an error
25
31
  */
26
- type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel> = {
32
+ type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = {
27
33
  type: 'error';
28
34
  error: unknown;
29
35
  result?: undefined;
@@ -31,7 +37,7 @@ type RetryErrorAttempt<MODEL extends LanguageModel | EmbeddingModel> = {
31
37
  /**
32
38
  * The call options used for this attempt.
33
39
  */
34
- options: MODEL extends LanguageModel ? LanguageModelCallOptions : EmbeddingModelCallOptions;
40
+ options: MODEL extends LanguageModel ? LanguageModelCallOptions : MODEL extends EmbeddingModel ? EmbeddingModelCallOptions : ImageModelCallOptions;
35
41
  };
36
42
  /**
37
43
  * A retry attempt with a successful result
@@ -49,11 +55,11 @@ type RetryResultAttempt = {
49
55
  /**
50
56
  * A retry attempt with either an error or a result and the model used
51
57
  */
52
- type RetryAttempt<MODEL extends LanguageModel | EmbeddingModel> = RetryErrorAttempt<MODEL> | RetryResultAttempt;
58
+ type RetryAttempt<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = MODEL extends LanguageModel ? RetryErrorAttempt<MODEL> | RetryResultAttempt : RetryErrorAttempt<MODEL>;
53
59
  /**
54
60
  * The context provided to Retryables with the current attempt and all previous attempts.
55
61
  */
56
- type RetryContext<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
62
+ type RetryContext<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel> = {
57
63
  /**
58
64
  * Current attempt that caused the retry
59
65
  */
@@ -66,10 +72,16 @@ type RetryContext<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
66
72
  /**
67
73
  * Options for creating a retryable model.
68
74
  */
69
- interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
75
+ interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel | ImageModel> {
70
76
  model: MODEL;
71
77
  retries: Retries<MODEL>;
72
78
  disabled?: boolean | (() => boolean);
79
+ /**
80
+ * Controls when to reset back to the base model after a successful retry.
81
+ *
82
+ * @default 'after-request'
83
+ */
84
+ reset?: Reset;
73
85
  onError?: (context: RetryContext<MODEL>) => void;
74
86
  onRetry?: (context: RetryContext<MODEL>) => void;
75
87
  }
@@ -84,7 +96,7 @@ interface RetryableModelOptions<MODEL extends LanguageModel | EmbeddingModel> {
84
96
  * This flexible approach allows retryable functions to return the exact model type
85
97
  * they received without type assertions, while still supporting string-based gateway models.
86
98
  */
87
- type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
99
+ type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel> = {
88
100
  model: MODEL;
89
101
  /**
90
102
  * Maximum number of attempts for this model.
@@ -106,7 +118,7 @@ type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
106
118
  /**
107
119
  * Call options to override for this retry.
108
120
  */
109
- options?: MODEL extends LanguageModel ? Partial<LanguageModelRetryCallOptions> : Partial<EmbeddingModelRetryCallOptions>;
121
+ options?: MODEL extends LanguageModel ? Partial<LanguageModelRetryCallOptions> : MODEL extends EmbeddingModel ? Partial<EmbeddingModelRetryCallOptions> : Partial<ImageModelRetryCallOptions>;
110
122
  /**
111
123
  * @deprecated Use `options.providerOptions` instead.
112
124
  * Provider options to override for this retry.
@@ -118,12 +130,21 @@ type Retry<MODEL extends ResolvableLanguageModel | EmbeddingModel> = {
118
130
  /**
119
131
  * A function that determines whether to retry with a different model based on the current attempt and all previous attempts.
120
132
  */
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'>>;
133
+ type Retryable<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel> = (context: RetryContext<MODEL>) => Retry<MODEL> | Promise<Retry<MODEL> | undefined> | undefined;
134
+ type Retries<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = Array<Retryable<ResolvableModel<MODEL>> | Retry<ResolvableModel<MODEL>> | ResolvableModel<MODEL>>;
135
+ type RetryableOptions<MODEL extends ResolvableLanguageModel | EmbeddingModel | ImageModel> = Partial<Omit<Retry<MODEL>, 'model'>>;
136
+ /**
137
+ * Controls when to reset the sticky model back to the base model.
138
+ *
139
+ * - `'after-request'` — reset after each request (default, current behavior)
140
+ * - `` `after-${number}-requests` `` — use the retry model for the next N requests
141
+ * - `` `after-${number}-seconds` `` — use the retry model for the next N seconds
142
+ */
143
+ type Reset = 'after-request' | `after-${number}-requests` | `after-${number}-seconds`;
124
144
  type LanguageModelGenerate = Awaited<ReturnType<LanguageModel['doGenerate']>>;
125
145
  type LanguageModelStream = Awaited<ReturnType<LanguageModel['doStream']>>;
126
146
  type EmbeddingModelCallOptions = Parameters<EmbeddingModel['doEmbed']>[0];
127
147
  type EmbeddingModelEmbed = Awaited<ReturnType<EmbeddingModel['doEmbed']>>;
148
+ type ImageModelGenerate = Awaited<ReturnType<ImageModel['doGenerate']>>;
128
149
  //#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 };
150
+ export { RetryAttempt as C, Retryable as D, RetryResultAttempt as E, RetryableModelOptions as O, Retry as S, RetryErrorAttempt as T, Reset as _, GatewayLanguageModelId as a, ResolvedModel as b, ImageModelGenerate as c, LanguageModelCallOptions as d, LanguageModelGenerate as f, ProviderOptions as g, LanguageModelStreamPart as h, EmbeddingModelRetryCallOptions as i, RetryableOptions as k, ImageModelRetryCallOptions as l, LanguageModelStream as m, EmbeddingModelCallOptions as n, ImageModel as o, LanguageModelRetryCallOptions as p, EmbeddingModelEmbed as r, ImageModelCallOptions as s, EmbeddingModel as t, LanguageModel as u, ResolvableLanguageModel as v, RetryContext as w, Retries as x, ResolvableModel as y };
@@ -1,9 +1,10 @@
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 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 === "v3";
4
+ const isModel = (model) => isLanguageModel(model) || isEmbeddingModel(model) || isImageModel(model);
5
+ const isLanguageModel = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && "doGenerate" in model && "doStream" in model && model.specificationVersion === "v3";
6
6
  const isEmbeddingModel = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && "doEmbed" in model && model.specificationVersion === "v3";
7
+ const isImageModel = (model) => isObject(model) && "provider" in model && "modelId" in model && "specificationVersion" in model && "doGenerate" in model && model.specificationVersion === "v3" && !("doStream" in model) && !("doEmbed" in model);
7
8
  const isStreamResult = (result) => "stream" in result;
8
9
  const isGenerateResult = (result) => "content" in result;
9
10
  /**
@@ -38,4 +39,4 @@ const isAbortError = (error) => error instanceof Error && error.name === "AbortE
38
39
  const isTimeoutError = (error) => error instanceof Error && error.name === "TimeoutError";
39
40
 
40
41
  //#endregion
41
- export { isLanguageModel as a, isResultAttempt as c, isString as d, isTimeoutError as f, isGenerateResult as i, isStreamContentPart as l, isEmbeddingModel as n, isModel as o, isErrorAttempt as r, isObject as s, isAbortError as t, isStreamResult as u };
42
+ export { isImageModel as a, isObject as c, isStreamResult as d, isString as f, isGenerateResult as i, isResultAttempt as l, isEmbeddingModel as n, isLanguageModel as o, isTimeoutError as p, isErrorAttempt as r, isModel as s, isAbortError as t, isStreamContentPart as u };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-retry",
3
- "version": "1.1.0",
4
- "description": "AI SDK Retry",
3
+ "version": "1.3.0",
4
+ "description": "Retry and fallback mechanisms for AI SDK",
5
5
  "types": "./dist/index.d.mts",
6
6
  "type": "module",
7
7
  "files": [
@@ -34,6 +34,7 @@
34
34
  "@ai-sdk/anthropic": "3.0.23",
35
35
  "@ai-sdk/azure": "3.0.19",
36
36
  "@ai-sdk/gateway": "3.0.23",
37
+ "@ai-sdk/google": "^3.0.33",
37
38
  "@ai-sdk/groq": "3.0.15",
38
39
  "@ai-sdk/openai": "3.0.19",
39
40
  "@ai-sdk/test-server": "1.0.3",