ai-retry 0.0.1 → 0.0.2

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
@@ -1,3 +1,5 @@
1
+ <a href="https://www.npmjs.com/package/ai-retry" alt="ai-retry"><img src="https://img.shields.io/npm/dt/ai-retry?label=ai-retry"></a> <a href="https://github.com/zirkelc/ai-retry/actions/workflows/ci.yml" alt="CI"><img src="https://img.shields.io/github/actions/workflow/status/zirkelc/ai-retry/ci.yml?branch=main"></a>
2
+
1
3
  # ai-retry: Retry and fallback mechanisms for AI SDK
2
4
 
3
5
  Automatically handle API failures, content filtering and timeouts by switching between different AI models.
@@ -118,6 +120,24 @@ const result = await generateText({
118
120
  });
119
121
  ```
120
122
 
123
+ #### Service Overloaded
124
+
125
+ Handle service overload errors (HTTP code 529) by switching to a provider.
126
+
127
+ > [!NOTE]
128
+ > For Anthropic specifically, use `anthropicServiceOverloaded` instead as Anthropic sometimes returns HTTP 200 OK with an error payload rather than the standard HTTP 529.
129
+
130
+ ```typescript
131
+ import { serviceOverloaded } from 'ai-retry/retryables';
132
+
133
+ const retryableModel = createRetryable({
134
+ model: azure('gpt-4'),
135
+ retries: [
136
+ serviceOverloaded(openai('gpt-4')), // Switch to OpenAI if Azure is overloaded
137
+ ],
138
+ });
139
+ ```
140
+
121
141
  #### Request Not Retryable
122
142
 
123
143
  Handle cases where the base model fails with a non-retryable error.
@@ -240,6 +260,8 @@ There are several built-in retryables:
240
260
  - [`contentFilterTriggered`](./src/retryables/content-filter-triggered.ts): Content filter was triggered based on the prompt or completion.
241
261
  - [`requestTimeout`](./src/retryables/request-timeout.ts): Request timeout occurred.
242
262
  - [`requestNotRetryable`](./src/retryables/request-not-retryable.ts): Request failed with a non-retryable error.
263
+ - [`serviceOverloaded`](./src/retryables/service-overloaded.ts): Response with status code 529 (service overloaded).
264
+ - [`anthropicServiceOverloaded`](./src/retryables/anthropic-service-overloaded.ts): Anthropic-specific overloaded error handling for both HTTP 529 and 200 OK responses with overloaded error payloads.
243
265
 
244
266
  By default, each retryable will only attempt to retry once per model to avoid infinite loops. You can customize this behavior by returning a `maxAttempts` value from your retryable function.
245
267
 
@@ -1,8 +1,31 @@
1
1
  import { RetryModel, Retryable } from "../create-retryable-model-DzDFqgQO.js";
2
2
  import { LanguageModelV2 } from "@ai-sdk/provider";
3
3
 
4
- //#region src/retryables/content-filter-triggered.d.ts
4
+ //#region src/retryables/anthropic-service-overloaded.d.ts
5
5
 
6
+ /**
7
+ * Type for Anthropic error responses.
8
+ *
9
+ * @see https://docs.claude.com/en/api/errors#error-shapes
10
+ */
11
+ type AnthropicErrorResponse = {
12
+ type: 'error';
13
+ error: {
14
+ type: string;
15
+ message: string;
16
+ };
17
+ };
18
+ /**
19
+ * Fallback if Anthropic returns an "overloaded" error with HTTP 200.
20
+ *
21
+ * ```
22
+ * HTTP 200 OK
23
+ * {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}
24
+ * ```
25
+ */
26
+ declare function anthropicServiceOverloaded(model: LanguageModelV2, options?: Omit<RetryModel, 'model'>): Retryable;
27
+ //#endregion
28
+ //#region src/retryables/content-filter-triggered.d.ts
6
29
  /**
7
30
  * Fallback to a different model if the content filter was triggered.
8
31
  */
@@ -24,4 +47,10 @@ declare function requestTimeout(model: LanguageModelV2, options?: Omit<RetryMode
24
47
  //#region src/retryables/response-schema-mismatch.d.ts
25
48
  declare function responseSchemaMismatch(model: LanguageModelV2, options?: Omit<RetryModel, 'model'>): Retryable;
26
49
  //#endregion
27
- export { contentFilterTriggered, requestNotRetryable, requestTimeout, responseSchemaMismatch };
50
+ //#region src/retryables/service-overloaded.d.ts
51
+ /**
52
+ * Fallback to a different model if the provider returns a HTTP 529 error.
53
+ */
54
+ declare function serviceOverloaded(model: LanguageModelV2, options?: Omit<RetryModel, 'model'>): Retryable;
55
+ //#endregion
56
+ export { AnthropicErrorResponse, anthropicServiceOverloaded, contentFilterTriggered, requestNotRetryable, requestTimeout, responseSchemaMismatch, serviceOverloaded };
@@ -6,6 +6,38 @@ import { APICallError, NoObjectGeneratedError, TypeValidationError } from "ai";
6
6
  const isObject = (value) => typeof value === "object" && value !== null;
7
7
  const isString = (value) => typeof value === "string";
8
8
 
9
+ //#endregion
10
+ //#region src/retryables/anthropic-service-overloaded.ts
11
+ /**
12
+ * Fallback if Anthropic returns an "overloaded" error with HTTP 200.
13
+ *
14
+ * ```
15
+ * HTTP 200 OK
16
+ * {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}
17
+ * ```
18
+ */
19
+ function anthropicServiceOverloaded(model, options) {
20
+ return (context) => {
21
+ const { current } = context;
22
+ if (isErrorAttempt(current)) {
23
+ const { error } = current;
24
+ if (APICallError.isInstance(error) && error.statusCode === 529) return {
25
+ model,
26
+ maxAttempts: 1,
27
+ ...options
28
+ };
29
+ if (APICallError.isInstance(error) && error.statusCode === 200) try {
30
+ const responseBody = JSON.parse(error.responseBody ?? "");
31
+ if (responseBody.error && isObject(responseBody.error) && isString(responseBody.error.type) && responseBody.error.type === "overloaded_error") return {
32
+ model,
33
+ maxAttempts: 1,
34
+ ...options
35
+ };
36
+ } catch {}
37
+ }
38
+ };
39
+ }
40
+
9
41
  //#endregion
10
42
  //#region src/retryables/content-filter-triggered.ts
11
43
  /**
@@ -89,4 +121,23 @@ function responseSchemaMismatch(model, options) {
89
121
  }
90
122
 
91
123
  //#endregion
92
- export { contentFilterTriggered, requestNotRetryable, requestTimeout, responseSchemaMismatch };
124
+ //#region src/retryables/service-overloaded.ts
125
+ /**
126
+ * Fallback to a different model if the provider returns a HTTP 529 error.
127
+ */
128
+ function serviceOverloaded(model, options) {
129
+ return (context) => {
130
+ const { current } = context;
131
+ if (isErrorAttempt(current)) {
132
+ const { error } = current;
133
+ if (APICallError.isInstance(error) && error.statusCode === 529) return {
134
+ model,
135
+ maxAttempts: 1,
136
+ ...options
137
+ };
138
+ }
139
+ };
140
+ }
141
+
142
+ //#endregion
143
+ export { anthropicServiceOverloaded, contentFilterTriggered, requestNotRetryable, requestTimeout, responseSchemaMismatch, serviceOverloaded };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-retry",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "AI SDK Retry",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "ai": "5.x"
34
34
  },
35
35
  "devDependencies": {
36
+ "@ai-sdk/anthropic": "^2.0.18",
36
37
  "@ai-sdk/azure": "^2.0.30",
37
38
  "@ai-sdk/openai": "^2.0.30",
38
39
  "@arethetypeswrong/cli": "^0.18.2",