ai-functions 2.1.3 → 2.4.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +90 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +176 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +685 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/sandbox.d.ts +36 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +44 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +155 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +29 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +71 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +874 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +122 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/sandbox.ts +52 -0
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +186 -27
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/fill-template.test.ts +89 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/sandbox-execution.test.ts +155 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and Resilience Patterns Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates building resilient AI applications using ai-functions.
|
|
5
|
+
* It shows how to:
|
|
6
|
+
* - Implement retry logic with exponential backoff
|
|
7
|
+
* - Use circuit breakers for fail-fast behavior
|
|
8
|
+
* - Create fallback chains across models
|
|
9
|
+
* - Handle rate limits and transient errors
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```bash
|
|
13
|
+
* ANTHROPIC_API_KEY=sk-... npx tsx examples/11-retry-resilience.ts
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
write,
|
|
19
|
+
ai,
|
|
20
|
+
configure,
|
|
21
|
+
withRetry,
|
|
22
|
+
RetryPolicy,
|
|
23
|
+
CircuitBreaker,
|
|
24
|
+
FallbackChain,
|
|
25
|
+
RetryableError,
|
|
26
|
+
RateLimitError,
|
|
27
|
+
classifyError,
|
|
28
|
+
calculateBackoff,
|
|
29
|
+
type RetryOptions,
|
|
30
|
+
type JitterStrategy,
|
|
31
|
+
} from '../src/index.js'
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Basic Retry with withRetry
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
async function basicRetryExample(): Promise<void> {
|
|
38
|
+
console.log('\n=== Basic Retry with withRetry() ===\n')
|
|
39
|
+
|
|
40
|
+
// Simple retry wrapper
|
|
41
|
+
const reliableGenerate = async (prompt: string): Promise<string> => {
|
|
42
|
+
return withRetry(async () => write`${prompt}`, {
|
|
43
|
+
maxRetries: 3,
|
|
44
|
+
baseDelay: 1000,
|
|
45
|
+
jitter: 0.2, // Add 20% random jitter
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('Generating with retry protection...')
|
|
50
|
+
const result = await reliableGenerate('Say hello in one word')
|
|
51
|
+
console.log(`Result: ${result}`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// RetryPolicy - Advanced Configuration
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
async function retryPolicyExample(): Promise<void> {
|
|
59
|
+
console.log('\n=== RetryPolicy - Advanced Configuration ===\n')
|
|
60
|
+
|
|
61
|
+
// Create a reusable retry policy
|
|
62
|
+
const policy = new RetryPolicy({
|
|
63
|
+
maxRetries: 5,
|
|
64
|
+
baseDelay: 1000,
|
|
65
|
+
maxDelay: 30000,
|
|
66
|
+
jitterStrategy: 'decorrelated', // Better than simple jitter for distributed systems
|
|
67
|
+
shouldRetry: (error, attempt) => {
|
|
68
|
+
// Custom retry decision logic
|
|
69
|
+
console.log(` Attempt ${attempt}: ${error.message}`)
|
|
70
|
+
|
|
71
|
+
// Don't retry authentication errors
|
|
72
|
+
if (error.message.includes('auth')) {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Retry rate limits with longer delay
|
|
77
|
+
if (error.message.includes('rate limit')) {
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return attempt < 5
|
|
82
|
+
},
|
|
83
|
+
onRetry: (error, attempt, delay) => {
|
|
84
|
+
console.log(` Retrying in ${delay}ms (attempt ${attempt})...`)
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Simulate a function that might fail
|
|
89
|
+
let attempts = 0
|
|
90
|
+
const flakyFunction = async (): Promise<string> => {
|
|
91
|
+
attempts++
|
|
92
|
+
if (attempts < 3) {
|
|
93
|
+
throw new RetryableError('Temporary failure')
|
|
94
|
+
}
|
|
95
|
+
return 'Success!'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log('Executing with RetryPolicy...')
|
|
99
|
+
try {
|
|
100
|
+
const result = await policy.execute(flakyFunction)
|
|
101
|
+
console.log(`Result: ${result} (after ${attempts} attempts)`)
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.log(`Failed: ${(error as Error).message}`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Jitter Strategies
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
async function jitterStrategiesExample(): Promise<void> {
|
|
112
|
+
console.log('\n=== Jitter Strategies ===\n')
|
|
113
|
+
|
|
114
|
+
const strategies: JitterStrategy[] = ['none', 'full', 'equal', 'decorrelated']
|
|
115
|
+
|
|
116
|
+
console.log('Backoff delays with different jitter strategies:')
|
|
117
|
+
console.log('(Base delay: 1000ms, Max delay: 30000ms)\n')
|
|
118
|
+
|
|
119
|
+
for (const strategy of strategies) {
|
|
120
|
+
console.log(`${strategy}:`)
|
|
121
|
+
let prevDelay = 1000
|
|
122
|
+
|
|
123
|
+
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
124
|
+
const delay = calculateBackoff(attempt, {
|
|
125
|
+
baseDelay: 1000,
|
|
126
|
+
maxDelay: 30000,
|
|
127
|
+
jitterStrategy: strategy,
|
|
128
|
+
previousDelay: prevDelay,
|
|
129
|
+
})
|
|
130
|
+
console.log(` Attempt ${attempt}: ${Math.round(delay)}ms`)
|
|
131
|
+
prevDelay = delay
|
|
132
|
+
}
|
|
133
|
+
console.log('')
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Circuit Breaker Pattern
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
async function circuitBreakerExample(): Promise<void> {
|
|
142
|
+
console.log('\n=== Circuit Breaker Pattern ===\n')
|
|
143
|
+
|
|
144
|
+
const breaker = new CircuitBreaker({
|
|
145
|
+
failureThreshold: 3, // Open after 3 failures
|
|
146
|
+
resetTimeout: 5000, // Try again after 5 seconds
|
|
147
|
+
halfOpenMaxAttempts: 2, // Allow 2 attempts in half-open
|
|
148
|
+
onStateChange: (from, to) => {
|
|
149
|
+
console.log(` Circuit state: ${from} -> ${to}`)
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// Simulate failures
|
|
154
|
+
let callCount = 0
|
|
155
|
+
const unreliableService = async (): Promise<string> => {
|
|
156
|
+
callCount++
|
|
157
|
+
if (callCount <= 4) {
|
|
158
|
+
throw new Error('Service unavailable')
|
|
159
|
+
}
|
|
160
|
+
return 'Success!'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log('Making calls through circuit breaker...\n')
|
|
164
|
+
|
|
165
|
+
for (let i = 1; i <= 8; i++) {
|
|
166
|
+
try {
|
|
167
|
+
const result = await breaker.execute(unreliableService)
|
|
168
|
+
console.log(` Call ${i}: ${result}`)
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.log(` Call ${i}: ${(error as Error).message}`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Small delay between calls
|
|
174
|
+
await new Promise((r) => setTimeout(r, 500))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Show circuit metrics
|
|
178
|
+
const metrics = breaker.getMetrics()
|
|
179
|
+
console.log('\nCircuit Breaker Metrics:')
|
|
180
|
+
console.log(` State: ${metrics.state}`)
|
|
181
|
+
console.log(` Failures: ${metrics.failures}`)
|
|
182
|
+
console.log(` Successes: ${metrics.successes}`)
|
|
183
|
+
console.log(` Consecutive Failures: ${metrics.consecutiveFailures}`)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Fallback Chain
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
async function fallbackChainExample(): Promise<void> {
|
|
191
|
+
console.log('\n=== Fallback Chain ===\n')
|
|
192
|
+
|
|
193
|
+
// Simulate different model behaviors
|
|
194
|
+
const claudeCall = async () => {
|
|
195
|
+
console.log(' Trying Claude Sonnet...')
|
|
196
|
+
// Simulate occasional failure
|
|
197
|
+
if (Math.random() < 0.7) {
|
|
198
|
+
throw new Error('Claude temporarily unavailable')
|
|
199
|
+
}
|
|
200
|
+
return 'Response from Claude Sonnet'
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const gptCall = async () => {
|
|
204
|
+
console.log(' Trying GPT-4o...')
|
|
205
|
+
// Simulate occasional failure
|
|
206
|
+
if (Math.random() < 0.5) {
|
|
207
|
+
throw new Error('GPT-4o rate limited')
|
|
208
|
+
}
|
|
209
|
+
return 'Response from GPT-4o'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const haikuCall = async () => {
|
|
213
|
+
console.log(' Trying Claude Haiku...')
|
|
214
|
+
return 'Response from Claude Haiku (fallback)'
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Create fallback chain
|
|
218
|
+
const chain = new FallbackChain([
|
|
219
|
+
{ name: 'claude-sonnet', execute: claudeCall },
|
|
220
|
+
{ name: 'gpt-4o', execute: gptCall },
|
|
221
|
+
{ name: 'claude-haiku', execute: haikuCall }, // Always succeeds
|
|
222
|
+
])
|
|
223
|
+
|
|
224
|
+
console.log('Executing with fallback chain:\n')
|
|
225
|
+
|
|
226
|
+
// Try a few times to see different paths
|
|
227
|
+
for (let i = 1; i <= 3; i++) {
|
|
228
|
+
console.log(`Attempt ${i}:`)
|
|
229
|
+
const result = await chain.execute()
|
|
230
|
+
console.log(` Result: ${result}\n`)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Show metrics
|
|
234
|
+
const metrics = chain.getMetrics()
|
|
235
|
+
console.log('Fallback Chain Metrics:')
|
|
236
|
+
for (const [name, m] of Object.entries(metrics)) {
|
|
237
|
+
console.log(` ${name}: ${m.successes} successes, ${m.failures} failures`)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Error Classification
|
|
243
|
+
// ============================================================================
|
|
244
|
+
|
|
245
|
+
async function errorClassificationExample(): Promise<void> {
|
|
246
|
+
console.log('\n=== Error Classification ===\n')
|
|
247
|
+
|
|
248
|
+
const errors = [
|
|
249
|
+
new Error('Request timeout'),
|
|
250
|
+
new Error('Rate limit exceeded'),
|
|
251
|
+
new Error('Invalid API key'),
|
|
252
|
+
new Error('Connection refused'),
|
|
253
|
+
new Error('Internal server error'),
|
|
254
|
+
new RateLimitError('Too many requests', 60),
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
console.log('Classifying errors:\n')
|
|
258
|
+
|
|
259
|
+
for (const error of errors) {
|
|
260
|
+
const category = classifyError(error)
|
|
261
|
+
const shouldRetry = category === 'transient' || category === 'rate_limit'
|
|
262
|
+
|
|
263
|
+
console.log(` "${error.message}"`)
|
|
264
|
+
console.log(` Category: ${category}`)
|
|
265
|
+
console.log(` Should retry: ${shouldRetry}`)
|
|
266
|
+
|
|
267
|
+
if (error instanceof RateLimitError) {
|
|
268
|
+
console.log(` Retry after: ${error.retryAfter}s`)
|
|
269
|
+
}
|
|
270
|
+
console.log('')
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// Combined Resilience Pattern
|
|
276
|
+
// ============================================================================
|
|
277
|
+
|
|
278
|
+
async function combinedResilienceExample(): Promise<void> {
|
|
279
|
+
console.log('\n=== Combined Resilience Pattern ===\n')
|
|
280
|
+
|
|
281
|
+
// Create circuit breaker
|
|
282
|
+
const breaker = new CircuitBreaker({
|
|
283
|
+
failureThreshold: 5,
|
|
284
|
+
resetTimeout: 10000,
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// Create retry policy
|
|
288
|
+
const retryPolicy = new RetryPolicy({
|
|
289
|
+
maxRetries: 3,
|
|
290
|
+
baseDelay: 1000,
|
|
291
|
+
maxDelay: 5000,
|
|
292
|
+
jitterStrategy: 'decorrelated',
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
// Create fallback chain
|
|
296
|
+
const fallbackChain = new FallbackChain([
|
|
297
|
+
{
|
|
298
|
+
name: 'primary',
|
|
299
|
+
execute: async () => {
|
|
300
|
+
// Wrap in circuit breaker and retry
|
|
301
|
+
return breaker.execute(() =>
|
|
302
|
+
retryPolicy.execute(async () => {
|
|
303
|
+
// Simulate primary service
|
|
304
|
+
return write`Hello world`
|
|
305
|
+
})
|
|
306
|
+
)
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: 'fallback',
|
|
311
|
+
execute: async () => {
|
|
312
|
+
// Simpler fallback with just retry
|
|
313
|
+
return retryPolicy.execute(async () => {
|
|
314
|
+
return 'Fallback response'
|
|
315
|
+
})
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
])
|
|
319
|
+
|
|
320
|
+
console.log('Combined resilience: Fallback -> Circuit Breaker -> Retry\n')
|
|
321
|
+
|
|
322
|
+
const result = await fallbackChain.execute()
|
|
323
|
+
console.log(`Result: ${result}`)
|
|
324
|
+
|
|
325
|
+
console.log(`
|
|
326
|
+
This pattern provides:
|
|
327
|
+
1. Automatic retries with exponential backoff
|
|
328
|
+
2. Circuit breaker to prevent cascading failures
|
|
329
|
+
3. Fallback to alternative services when primary fails
|
|
330
|
+
`)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ============================================================================
|
|
334
|
+
// Production Recommendations
|
|
335
|
+
// ============================================================================
|
|
336
|
+
|
|
337
|
+
function showProductionRecommendations(): void {
|
|
338
|
+
console.log('\n=== Production Recommendations ===\n')
|
|
339
|
+
|
|
340
|
+
console.log(`
|
|
341
|
+
1. RETRY CONFIGURATION
|
|
342
|
+
- Start with 3 retries, adjust based on SLAs
|
|
343
|
+
- Use decorrelated jitter for distributed systems
|
|
344
|
+
- Set maxDelay to prevent infinite waits
|
|
345
|
+
- Respect Retry-After headers from providers
|
|
346
|
+
|
|
347
|
+
2. CIRCUIT BREAKER
|
|
348
|
+
- Set failureThreshold based on error budget
|
|
349
|
+
- resetTimeout should match service recovery time
|
|
350
|
+
- Monitor state changes for alerting
|
|
351
|
+
- Use separate breakers for different services
|
|
352
|
+
|
|
353
|
+
3. FALLBACK CHAINS
|
|
354
|
+
- Order by quality: best -> acceptable -> minimal
|
|
355
|
+
- Consider cost differences between models
|
|
356
|
+
- Log which model was used for debugging
|
|
357
|
+
- Set timeouts per fallback option
|
|
358
|
+
|
|
359
|
+
4. ERROR HANDLING
|
|
360
|
+
- Classify errors to determine retry strategy
|
|
361
|
+
- Don't retry authentication errors
|
|
362
|
+
- Rate limit errors: use Retry-After header
|
|
363
|
+
- Log all errors with request IDs
|
|
364
|
+
|
|
365
|
+
5. MONITORING
|
|
366
|
+
- Track retry rates and success/failure ratios
|
|
367
|
+
- Alert on circuit breaker state changes
|
|
368
|
+
- Monitor fallback usage patterns
|
|
369
|
+
- Set up dashboards for resilience metrics
|
|
370
|
+
`)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// Main
|
|
375
|
+
// ============================================================================
|
|
376
|
+
|
|
377
|
+
async function main() {
|
|
378
|
+
console.log('\n=== Retry and Resilience Patterns Example ===')
|
|
379
|
+
|
|
380
|
+
configure({
|
|
381
|
+
model: 'sonnet',
|
|
382
|
+
provider: 'anthropic',
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
await basicRetryExample()
|
|
386
|
+
await retryPolicyExample()
|
|
387
|
+
await jitterStrategiesExample()
|
|
388
|
+
await circuitBreakerExample()
|
|
389
|
+
await fallbackChainExample()
|
|
390
|
+
await errorClassificationExample()
|
|
391
|
+
await combinedResilienceExample()
|
|
392
|
+
showProductionRecommendations()
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
main()
|
|
396
|
+
.then(() => {
|
|
397
|
+
console.log('\n=== Example Complete ===\n')
|
|
398
|
+
process.exit(0)
|
|
399
|
+
})
|
|
400
|
+
.catch((error) => {
|
|
401
|
+
console.error('\nError:', error.message)
|
|
402
|
+
process.exit(1)
|
|
403
|
+
})
|