ai-retry 1.6.0 → 1.6.1

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
@@ -782,7 +782,9 @@ const retryableModel = createRetryable({
782
782
 
783
783
  #### Timeouts
784
784
 
785
- When a retry specifies a `timeout` value, a fresh `AbortSignal.timeout()` is created for that retry attempt, replacing any existing abort signal. This is essential when retrying after timeout errors, as the original abort signal would already be in an aborted state.
785
+ When a retry specifies a `timeout` value, a fresh `AbortSignal.timeout()` is created for that retry attempt. If the original `abortSignal` is still alive, the fresh deadline is composed with it via `AbortSignal.any()` so user cancellation still works mid-retry. If the original signal is already aborted (for example it carried a request-level deadline that already fired), it is dropped so the retry runs against the fresh deadline alone.
786
+
787
+ If the original `abortSignal` is already aborted at the time of retry and the chosen retry does **not** supply a `timeout`, ai-retry rethrows the original error rather than firing a misleading retry against the dead signal. `onError` still fires for observability, but `onRetry` is skipped. Setting `retry.timeout` is the explicit opt-in for retrying past an aborted signal.
786
788
 
787
789
  ```typescript
788
790
  const retryableModel = createRetryable({
@@ -925,7 +927,7 @@ The following options can be overridden:
925
927
 
926
928
  #### Dynamic Call Options
927
929
 
928
- You can also override call options dynamically from inside the `onRetry` callback, instead of declaring them statically on the retry object. This is useful when the override depends on something only known at runtime, like the prompt that just failed, the model that's about to be tried next, or the error that triggered the retry. The overrides apply to the upcoming retry attempt only, and can change the same fields as the static `options` on a retry plus the request `timeout`. The callback may also be `async` if computing the override needs to do work (e.g. fetching a fresh credential).
930
+ You can also override call options dynamically from inside the `onRetry` callback, instead of declaring them statically on the retry object. This is useful when the override depends on something only known at runtime, like the prompt that just failed, the model that's about to be tried next, or the error that triggered the retry. The overrides apply to the upcoming retry attempt only, and can change the same fields as the static `options` on a retry. The callback may also be `async` if computing the override needs to do work (e.g. fetching a fresh credential).
929
931
 
930
932
  A common use case is sanitizing provider-scoped metadata when falling back to a different provider, for example stripping `providerOptions.azure.itemId` references from the previous prompt before retrying on OpenAI:
931
933
 
@@ -1090,7 +1092,7 @@ interface RetryableModelOptions<
1090
1092
  - `disabled`: Disable all retry logic. Can be a boolean or function returning boolean. Default: `false` (retries enabled).
1091
1093
  - `reset`: Controls when to reset back to the base model after a successful retry. Default: `after-request`.
1092
1094
  - `onError`: Callback invoked when an error occurs.
1093
- - `onRetry`: Callback invoked before attempting a retry. May optionally return an `OnRetryOverrides` object (or a `Promise` of one) to override `options.*` and `timeout` for the upcoming attempt only. See [Dynamic Call Options via `onRetry`](#dynamic-call-options-via-onretry).
1095
+ - `onRetry`: Callback invoked before attempting a retry. May optionally return an `OnRetryOverrides` object (or a `Promise` of one) to override `options.*` for the upcoming attempt only. See [Dynamic Call Options via `onRetry`](#dynamic-call-options-via-onretry).
1094
1096
  - `onSuccess`: Callback invoked after a successful request. Receives the model that handled the request and all previous attempts.
1095
1097
 
1096
1098
  #### `Reset`
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as RetryResultAttempt, C as Result, D as RetryCallOptions, E as RetryAttempt, F as SuccessContext, M as RetryableModelOptions, N as RetryableOptions, O as RetryContext, P as SuccessAttempt, S as ResolvedModel, T as Retry, _ as OnRetryOverrides, a as EmbeddingModelRetryCallOptions, b as ResolvableLanguageModel, c as ImageModelCallOptions, d as LanguageModel, f as LanguageModelCallOptions, g as LanguageModelStreamPart, h as LanguageModelStream, i as EmbeddingModelEmbed, j as Retryable, k as RetryErrorAttempt, l as ImageModelGenerate, m as LanguageModelRetryCallOptions, n as EmbeddingModel, o as GatewayLanguageModelId, p as LanguageModelGenerate, r as EmbeddingModelCallOptions, s as ImageModel, t as CallOptions, u as ImageModelRetryCallOptions, v as ProviderOptions, w as Retries, x as ResolvableModel, y as Reset } from "./types-pGdkwtOE.mjs";
1
+ import { A as RetryResultAttempt, C as Result, D as RetryCallOptions, E as RetryAttempt, F as SuccessContext, M as RetryableModelOptions, N as RetryableOptions, O as RetryContext, P as SuccessAttempt, S as ResolvedModel, T as Retry, _ as OnRetryOverrides, a as EmbeddingModelRetryCallOptions, b as ResolvableLanguageModel, c as ImageModelCallOptions, d as LanguageModel, f as LanguageModelCallOptions, g as LanguageModelStreamPart, h as LanguageModelStream, i as EmbeddingModelEmbed, j as Retryable, k as RetryErrorAttempt, l as ImageModelGenerate, m as LanguageModelRetryCallOptions, n as EmbeddingModel, o as GatewayLanguageModelId, p as LanguageModelGenerate, r as EmbeddingModelCallOptions, s as ImageModel, t as CallOptions, u as ImageModelRetryCallOptions, v as ProviderOptions, w as Retries, x as ResolvableModel, y as Reset } from "./types-CRKV-hdW.mjs";
2
2
  import * as _ai_sdk_provider0 from "@ai-sdk/provider";
3
3
 
4
4
  //#region src/create-retryable-model.d.ts
package/dist/index.mjs CHANGED
@@ -197,14 +197,18 @@ function resolveProviderOptions(base, currentRetry, onRetryOverrides) {
197
197
  /**
198
198
  * Resolve `abortSignal` for the upcoming attempt.
199
199
  *
200
- * If either `onRetryOverrides.timeout` or `currentRetry.timeout` is set, a
201
- * fresh `AbortSignal.timeout(...)` is created (override wins). Otherwise
202
- * the base `abortSignal` is preserved unchanged.
200
+ * If `currentRetry.timeout` is set, a fresh `AbortSignal.timeout(...)` is
201
+ * created. When the base signal is still alive, the fresh deadline is
202
+ * composed with it via `AbortSignal.any` so the user can still cancel
203
+ * mid-retry. When the base is already aborted, it is dropped so the retry
204
+ * runs against the fresh deadline alone. Without a retry timeout, the base
205
+ * is preserved unchanged.
203
206
  */
204
- function resolveAbortSignal(base, currentRetry, onRetryOverrides) {
205
- if (onRetryOverrides?.timeout !== void 0) return AbortSignal.timeout(onRetryOverrides.timeout);
206
- if (currentRetry?.timeout !== void 0) return AbortSignal.timeout(currentRetry.timeout);
207
- return base;
207
+ function resolveAbortSignal(base, currentRetry) {
208
+ if (currentRetry?.timeout === void 0) return base;
209
+ const fresh = AbortSignal.timeout(currentRetry.timeout);
210
+ if (base !== void 0 && !base.aborted) return AbortSignal.any([base, fresh]);
211
+ return fresh;
208
212
  }
209
213
  /**
210
214
  * Merge call options for the upcoming language model retry attempt.
@@ -231,7 +235,7 @@ function mergeLanguageModelCallOptions(input) {
231
235
  seed: overrideOptions.seed ?? retryOptions.seed ?? callOptions.seed,
232
236
  headers: overrideOptions.headers ?? retryOptions.headers ?? callOptions.headers,
233
237
  providerOptions: resolveProviderOptions(callOptions.providerOptions, currentRetry, onRetryOverrides),
234
- abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry, onRetryOverrides)
238
+ abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry)
235
239
  };
236
240
  }
237
241
  /**
@@ -246,7 +250,7 @@ function mergeEmbeddingModelCallOptions(input) {
246
250
  values: overrideOptions.values ?? retryOptions.values ?? callOptions.values,
247
251
  headers: overrideOptions.headers ?? retryOptions.headers ?? callOptions.headers,
248
252
  providerOptions: resolveProviderOptions(callOptions.providerOptions, currentRetry, onRetryOverrides),
249
- abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry, onRetryOverrides)
253
+ abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry)
250
254
  };
251
255
  }
252
256
  /**
@@ -264,7 +268,7 @@ function mergeImageModelCallOptions(input) {
264
268
  seed: overrideOptions.seed ?? retryOptions.seed ?? callOptions.seed,
265
269
  headers: overrideOptions.headers ?? retryOptions.headers ?? callOptions.headers,
266
270
  providerOptions: resolveProviderOptions(callOptions.providerOptions, currentRetry, onRetryOverrides),
267
- abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry, onRetryOverrides)
271
+ abortSignal: resolveAbortSignal(callOptions.abortSignal, currentRetry)
268
272
  };
269
273
  }
270
274
 
@@ -331,9 +335,15 @@ var RetryableEmbeddingModel = class extends BaseRetryableModel {
331
335
  callOptions: retryCallOptions
332
336
  };
333
337
  } catch (error) {
334
- if (isAbortError(error)) throw error;
335
338
  const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
336
339
  attempts.push(attempt);
340
+ /**
341
+ * If the inbound abort signal is already aborted and the chosen
342
+ * retry does not supply a fresh deadline, the retry would die
343
+ * instantly with the same abort. Rethrow rather than fire a
344
+ * misleading retry against a dead signal.
345
+ */
346
+ if (input.callOptions.abortSignal?.aborted && retryModel.timeout === void 0) throw error;
337
347
  if (retryModel.delay) {
338
348
  /**
339
349
  * Calculate exponential backoff delay based on the number of attempts for this specific model.
@@ -474,11 +484,15 @@ var RetryableImageModel = class extends BaseRetryableModel {
474
484
  callOptions: retryCallOptions
475
485
  };
476
486
  } catch (error) {
477
- /** Don't retry if user manually aborted the request. */
478
- /** TimeoutError from AbortSignal.timeout() will still be handled by retry handlers. */
479
- if (isAbortError(error)) throw error;
480
487
  const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
481
488
  attempts.push(attempt);
489
+ /**
490
+ * If the inbound abort signal is already aborted and the chosen
491
+ * retry does not supply a fresh deadline, the retry would die
492
+ * instantly with the same abort. Rethrow rather than fire a
493
+ * misleading retry against a dead signal.
494
+ */
495
+ if (input.callOptions.abortSignal?.aborted && retryModel.timeout === void 0) throw error;
482
496
  if (retryModel.delay) {
483
497
  /**
484
498
  * Calculate exponential backoff delay based on the number of attempts for this specific model.
@@ -650,9 +664,15 @@ var RetryableLanguageModel = class extends BaseRetryableModel {
650
664
  callOptions: retryCallOptions
651
665
  };
652
666
  } catch (error) {
653
- if (isAbortError(error)) throw error;
654
667
  const { retryModel, attempt } = await this.handleError(error, attempts, retryCallOptions);
655
668
  attempts.push(attempt);
669
+ /**
670
+ * If the inbound abort signal is already aborted and the chosen
671
+ * retry does not supply a fresh deadline, the retry would die
672
+ * instantly with the same abort. Rethrow rather than fire a
673
+ * misleading retry against a dead signal.
674
+ */
675
+ if (input.callOptions.abortSignal?.aborted && retryModel.timeout === void 0) throw error;
656
676
  if (retryModel.delay) {
657
677
  /**
658
678
  * Calculate exponential backoff delay based on the number of attempts for this specific model.
@@ -825,6 +845,13 @@ var RetryableLanguageModel = class extends BaseRetryableModel {
825
845
  * Save the attempt
826
846
  */
827
847
  attempts.push(attempt);
848
+ /**
849
+ * If the inbound abort signal is already aborted and the chosen
850
+ * retry does not supply a fresh deadline, the retry would die
851
+ * instantly with the same abort. Rethrow rather than fire a
852
+ * misleading retry against a dead signal.
853
+ */
854
+ if (callOptions.abortSignal?.aborted && retryModel.timeout === void 0) throw error;
828
855
  if (retryModel.delay) {
829
856
  /**
830
857
  * Calculate exponential backoff delay based on the number of attempts for this specific model.
@@ -1,4 +1,4 @@
1
- import { O as RetryContext, T as Retry, b as ResolvableLanguageModel, j as Retryable, n as EmbeddingModel, p as LanguageModelGenerate, s as ImageModel } from "../../types-pGdkwtOE.mjs";
1
+ import { O as RetryContext, T as Retry, b as ResolvableLanguageModel, j as Retryable, n as EmbeddingModel, p as LanguageModelGenerate, s as ImageModel } from "../../types-CRKV-hdW.mjs";
2
2
 
3
3
  //#region src/retryables/experimental/condition.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { N as RetryableOptions, b as ResolvableLanguageModel, j as Retryable, n as EmbeddingModel, s as ImageModel } from "../types-pGdkwtOE.mjs";
1
+ import { N as RetryableOptions, b as ResolvableLanguageModel, j as Retryable, n as EmbeddingModel, s as ImageModel } from "../types-CRKV-hdW.mjs";
2
2
 
3
3
  //#region src/retryables/content-filter-triggered.d.ts
4
4
  /**
@@ -34,7 +34,7 @@ type RetryCallOptions<MODEL extends LanguageModel | EmbeddingModel | ImageModel>
34
34
  /**
35
35
  * Override returned by `onRetry` to influence the upcoming retry attempt.
36
36
  */
37
- type OnRetryOverrides<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = Pick<Retry<MODEL>, 'options' | 'timeout'>;
37
+ type OnRetryOverrides<MODEL extends LanguageModel | EmbeddingModel | ImageModel> = Pick<Retry<MODEL>, 'options'>;
38
38
  /**
39
39
  * Maps a model type to its call options type.
40
40
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-retry",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Retry and fallback mechanisms for AI SDK",
5
5
  "keywords": [
6
6
  "ai",