ai-resilience 0.1.3 → 0.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 +166 -2
- package/dist/index.cjs +907 -11
- package/dist/index.d.cts +371 -1
- package/dist/index.d.ts +371 -1
- package/dist/index.js +877 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ai-resilience
|
|
2
2
|
|
|
3
|
-
`ai-resilience` is an axios-retry++
|
|
3
|
+
`ai-resilience` is an axios-retry++ toolkit for modern AI and backend systems. It keeps axios compatibility while adding configurable retry strategies, semantic recovery, provider fallback, circuit breakers, distributed coordination, streaming recovery, telemetry hooks, and strong TypeScript types.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -12,7 +12,8 @@ npm install ai-resilience axios axios-retry
|
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
14
|
import axios from "axios";
|
|
15
|
-
import { applyAiResilience, ConsoleRetryLogger } from "ai-resilience";
|
|
15
|
+
import { applyAiResilience, ConsoleRetryLogger, requestWithSemanticRetry } from "ai-resilience";
|
|
16
|
+
import { z } from "zod";
|
|
16
17
|
|
|
17
18
|
const client = axios.create({ baseURL: "https://api.example.com" });
|
|
18
19
|
|
|
@@ -29,6 +30,13 @@ applyAiResilience(client, {
|
|
|
29
30
|
},
|
|
30
31
|
},
|
|
31
32
|
});
|
|
33
|
+
|
|
34
|
+
const result = await requestWithSemanticRetry(client, {
|
|
35
|
+
request: { method: "post", url: "/chat/completions", data: { prompt: "Return JSON" } },
|
|
36
|
+
schema: z.object({ answer: z.string() }),
|
|
37
|
+
repairJson: true,
|
|
38
|
+
requireJson: true,
|
|
39
|
+
});
|
|
32
40
|
```
|
|
33
41
|
|
|
34
42
|
## Features
|
|
@@ -40,6 +48,14 @@ applyAiResilience(client, {
|
|
|
40
48
|
- Async custom retry conditions
|
|
41
49
|
- EventEmitter-based hooks plus direct hook callbacks
|
|
42
50
|
- Structured logger interface and console logger
|
|
51
|
+
- Semantic retry for valid HTTP responses with invalid AI payloads
|
|
52
|
+
- JSON validation, Zod schema validation, and JSON repair
|
|
53
|
+
- AI-aware failure detection for empty responses, refusals, truncation, and schema mismatches
|
|
54
|
+
- AI retry policies and semantic lifecycle hooks
|
|
55
|
+
- Provider fallback for OpenAI, Anthropic, Gemini, or custom providers
|
|
56
|
+
- Circuit breaker, provider health tracking, adaptive fallback delay, and provider metrics
|
|
57
|
+
- Redis-compatible retry coordination and distributed rate limiting through lightweight adapters
|
|
58
|
+
- Adaptive routing, streaming recovery, OpenTelemetry-compatible spans, and advanced metrics
|
|
43
59
|
- TypeScript-first public API
|
|
44
60
|
|
|
45
61
|
## API
|
|
@@ -52,6 +68,134 @@ Installs retry behavior on an existing axios instance and returns `{ axios, hook
|
|
|
52
68
|
|
|
53
69
|
Creates a new axios instance with retry behavior already applied.
|
|
54
70
|
|
|
71
|
+
### `requestWithSemanticRetry(instance, config)`
|
|
72
|
+
|
|
73
|
+
Runs an axios request and retries when the response is semantically invalid, such as malformed JSON, a schema mismatch, an empty body, a refusal, or a truncated answer.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { requestWithSemanticRetry } from "ai-resilience";
|
|
77
|
+
import { z } from "zod";
|
|
78
|
+
|
|
79
|
+
const data = await requestWithSemanticRetry(client, {
|
|
80
|
+
request: { url: "/generate", method: "post", data: { prompt } },
|
|
81
|
+
schema: z.object({
|
|
82
|
+
title: z.string(),
|
|
83
|
+
tags: z.array(z.string()),
|
|
84
|
+
}),
|
|
85
|
+
repairJson: true,
|
|
86
|
+
requireJson: true,
|
|
87
|
+
hooks: {
|
|
88
|
+
onSemanticRetry: ({ issue, attempt }) => {
|
|
89
|
+
console.log(`semantic retry ${attempt}: ${issue.kind}`);
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `semanticRetry(operation, policy)`
|
|
96
|
+
|
|
97
|
+
Wraps any async operation that returns either an axios response or raw data and retries until the payload passes semantic validation.
|
|
98
|
+
|
|
99
|
+
### `parseJsonResponse(data, options)`
|
|
100
|
+
|
|
101
|
+
Parses JSON strings, optionally repairs common model-output issues, and validates against a Zod schema.
|
|
102
|
+
|
|
103
|
+
### `applySemanticRecovery(instance, policy)`
|
|
104
|
+
|
|
105
|
+
Installs a response interceptor that validates and repairs successful axios responses. Use `requestWithSemanticRetry` when you also want semantic retries.
|
|
106
|
+
|
|
107
|
+
### `createProviderFallback(providers, options)`
|
|
108
|
+
|
|
109
|
+
Creates a provider fallback engine with provider routing, circuit breakers, health tracking, adaptive delay, fallback hooks, and metrics.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { createProviderFallback } from "ai-resilience";
|
|
113
|
+
|
|
114
|
+
const fallback = createProviderFallback(
|
|
115
|
+
[
|
|
116
|
+
{
|
|
117
|
+
id: "openai-primary",
|
|
118
|
+
type: "openai",
|
|
119
|
+
priority: 1,
|
|
120
|
+
request: (input) => openaiClient.responses.create(input),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "anthropic-backup",
|
|
124
|
+
type: "anthropic",
|
|
125
|
+
priority: 2,
|
|
126
|
+
request: (input) => anthropicClient.messages.create(input),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
{
|
|
130
|
+
strategy: "priority",
|
|
131
|
+
circuitBreaker: { failureThreshold: 3, cooldownMs: 30_000 },
|
|
132
|
+
hooks: {
|
|
133
|
+
onProviderFallback: ({ fromProviderId, toProviderId }) => {
|
|
134
|
+
console.log(`fallback ${fromProviderId} -> ${toProviderId}`);
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const response = await fallback.request({ prompt: "Summarize this" });
|
|
141
|
+
const metrics = fallback.snapshot();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `RedisRetryCoordinator`
|
|
145
|
+
|
|
146
|
+
Coordinates retries and locks across processes. Pass an `ioredis`-compatible client through the `redis` option, or omit it for local in-memory coordination during tests and development.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { RedisRetryCoordinator } from "ai-resilience";
|
|
150
|
+
|
|
151
|
+
const coordinator = new RedisRetryCoordinator({
|
|
152
|
+
namespace: "my-api",
|
|
153
|
+
redis,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await coordinator.incrementRetry("tenant-a:request-123");
|
|
157
|
+
const locked = await coordinator.acquireLock("provider-routing");
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `DistributedRateLimiter`
|
|
161
|
+
|
|
162
|
+
Provides Redis-compatible fixed-window rate limiting.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { DistributedRateLimiter } from "ai-resilience";
|
|
166
|
+
|
|
167
|
+
const limiter = new DistributedRateLimiter(redis);
|
|
168
|
+
const result = await limiter.consume({
|
|
169
|
+
key: "tenant-a:openai",
|
|
170
|
+
limit: 100,
|
|
171
|
+
windowSeconds: 60,
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `AdaptiveRouter`
|
|
176
|
+
|
|
177
|
+
Ranks providers using latency, failure, and cost signals.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { AdaptiveRouter } from "ai-resilience";
|
|
181
|
+
|
|
182
|
+
const router = new AdaptiveRouter(providers, {
|
|
183
|
+
latencyWeight: 1,
|
|
184
|
+
failureWeight: 2,
|
|
185
|
+
costWeight: 0.5,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const provider = router.select(metricsByProvider);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `recoverStream(stream, options)`
|
|
192
|
+
|
|
193
|
+
Collects streaming chunks and calls recovery hooks when chunk gaps exceed a configured threshold.
|
|
194
|
+
|
|
195
|
+
### `withTelemetry(tracer, name, operation, attributes)`
|
|
196
|
+
|
|
197
|
+
Wraps an async operation with an OpenTelemetry-style tracer adapter. This package does not force `@opentelemetry/api` into runtime dependencies; pass a tracer with `startSpan`.
|
|
198
|
+
|
|
55
199
|
### Retry config
|
|
56
200
|
|
|
57
201
|
```ts
|
|
@@ -70,6 +214,26 @@ type AiResilienceRetryConfig = {
|
|
|
70
214
|
};
|
|
71
215
|
```
|
|
72
216
|
|
|
217
|
+
### Semantic policy
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
type AiRetryPolicy = {
|
|
221
|
+
maxSemanticRetries?: number;
|
|
222
|
+
retryOnFailureKinds?: Array<
|
|
223
|
+
"invalid_json" | "schema_mismatch" | "empty_response" | "refusal" | "truncated"
|
|
224
|
+
>;
|
|
225
|
+
repairJson?: boolean;
|
|
226
|
+
requireJson?: boolean;
|
|
227
|
+
detectRefusals?: boolean;
|
|
228
|
+
detectTruncation?: boolean;
|
|
229
|
+
schema?: z.ZodType;
|
|
230
|
+
validate?: (data, response) => SemanticValidationResult | Promise<SemanticValidationResult>;
|
|
231
|
+
hooks?: SemanticRetryHooks;
|
|
232
|
+
logger?: RetryLogger;
|
|
233
|
+
metadata?: Record<string, unknown>;
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
73
237
|
## Scripts
|
|
74
238
|
|
|
75
239
|
```sh
|