ai-functions 2.1.3 → 2.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +55 -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 +116 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +546 -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/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 +135 -17
- 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 +28 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -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 +671 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +119 -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/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 +164 -25
- 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/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/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
package/src/retry.ts
CHANGED
|
@@ -9,9 +9,17 @@
|
|
|
9
9
|
* - Error classification for intelligent retry decisions
|
|
10
10
|
* - Partial retry for batch operations
|
|
11
11
|
*
|
|
12
|
+
* Per-model policy data (which models retry how, who falls back to whom,
|
|
13
|
+
* which batch tiers each model supports) lives in `language-models`'s
|
|
14
|
+
* `policyFor()`. The classes in this file are the *machinery* that reads
|
|
15
|
+
* that policy. See `RetryPolicy.forModel`, `CircuitBreaker.forModel`,
|
|
16
|
+
* `FallbackChain.forModel`.
|
|
17
|
+
*
|
|
12
18
|
* @packageDocumentation
|
|
13
19
|
*/
|
|
14
20
|
|
|
21
|
+
import { policyFor, type ModelPolicy, type ErrorCategoryName } from 'language-models'
|
|
22
|
+
|
|
15
23
|
// ============================================================================
|
|
16
24
|
// ERROR TYPES AND CLASSIFICATION
|
|
17
25
|
// ============================================================================
|
|
@@ -83,13 +91,18 @@ export class RateLimitError extends RetryableError {
|
|
|
83
91
|
constructor(message: string, options?: { retryAfter?: number }) {
|
|
84
92
|
super(message, ErrorCategory.RateLimit)
|
|
85
93
|
this.name = 'RateLimitError'
|
|
86
|
-
|
|
94
|
+
if (options?.retryAfter !== undefined) {
|
|
95
|
+
this.retryAfter = options.retryAfter
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
/**
|
|
90
100
|
* Create RateLimitError from HTTP response
|
|
91
101
|
*/
|
|
92
|
-
static fromResponse(response: {
|
|
102
|
+
static fromResponse(response: {
|
|
103
|
+
status: number
|
|
104
|
+
headers?: Record<string, string>
|
|
105
|
+
}): RateLimitError {
|
|
93
106
|
const retryAfterHeader = response.headers?.['retry-after']
|
|
94
107
|
let retryAfter: number | undefined
|
|
95
108
|
|
|
@@ -100,7 +113,10 @@ export class RateLimitError extends RetryableError {
|
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
|
|
103
|
-
return new RateLimitError(
|
|
116
|
+
return new RateLimitError(
|
|
117
|
+
`Rate limited (${response.status})`,
|
|
118
|
+
retryAfter !== undefined ? { retryAfter } : undefined
|
|
119
|
+
)
|
|
104
120
|
}
|
|
105
121
|
}
|
|
106
122
|
|
|
@@ -116,6 +132,11 @@ export class CircuitOpenError extends Error {
|
|
|
116
132
|
}
|
|
117
133
|
}
|
|
118
134
|
|
|
135
|
+
/** Error with status property (e.g., HTTP errors) */
|
|
136
|
+
interface ErrorWithStatus extends Error {
|
|
137
|
+
status?: number
|
|
138
|
+
}
|
|
139
|
+
|
|
119
140
|
/**
|
|
120
141
|
* Classify an error into a category for retry decisions
|
|
121
142
|
*/
|
|
@@ -125,7 +146,7 @@ export function classifyError(error: unknown): ErrorCategory {
|
|
|
125
146
|
}
|
|
126
147
|
|
|
127
148
|
const message = error.message.toLowerCase()
|
|
128
|
-
const status = (error as
|
|
149
|
+
const status = (error as ErrorWithStatus).status
|
|
129
150
|
|
|
130
151
|
// Network errors
|
|
131
152
|
if (
|
|
@@ -309,9 +330,20 @@ export interface BatchItemResult<T, R> {
|
|
|
309
330
|
|
|
310
331
|
/**
|
|
311
332
|
* Retry policy for executing operations with exponential backoff
|
|
333
|
+
*
|
|
334
|
+
* @deprecated Phase C Week 3 — `RetryPolicy` has 1 real production caller
|
|
335
|
+
* (audited 2026-05-06; see `bd show aip-ibid`):
|
|
336
|
+
* `ai-database/src/cascade-orchestrator.ts:1235` (loose coupling — dynamic
|
|
337
|
+
* import + graceful try/catch fallback when ai-functions not available).
|
|
338
|
+
* AI SDK 6's `customProvider({ retryPolicy })` and `wrapLanguageModel(model,
|
|
339
|
+
* retryMiddleware)` cover the same surface. Migration tracked in aip-ibid;
|
|
340
|
+
* the one callsite can move on a separate commit. Will be removed in the
|
|
341
|
+
* Phase C semver bump.
|
|
312
342
|
*/
|
|
313
343
|
export class RetryPolicy {
|
|
314
|
-
private readonly options: Required<Omit<RetryOptions, 'shouldRetry'>> & {
|
|
344
|
+
private readonly options: Required<Omit<RetryOptions, 'shouldRetry'>> & {
|
|
345
|
+
shouldRetry?: (error: unknown) => boolean
|
|
346
|
+
}
|
|
315
347
|
|
|
316
348
|
constructor(options: RetryOptions = {}) {
|
|
317
349
|
this.options = {
|
|
@@ -322,16 +354,55 @@ export class RetryPolicy {
|
|
|
322
354
|
jitter: options.jitter ?? 0,
|
|
323
355
|
jitterStrategy: options.jitterStrategy ?? 'equal',
|
|
324
356
|
respectRetryAfter: options.respectRetryAfter ?? true,
|
|
325
|
-
shouldRetry: options.shouldRetry,
|
|
357
|
+
...(options.shouldRetry !== undefined && { shouldRetry: options.shouldRetry }),
|
|
326
358
|
}
|
|
327
359
|
}
|
|
328
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Build a RetryPolicy from a model's `ModelPolicy` (loaded via
|
|
363
|
+
* `language-models`). Per-call `overrides` win over policy data.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* const policy = RetryPolicy.forModel('sonnet')
|
|
368
|
+
* // Uses retry settings derived for anthropic/claude-sonnet-4.5
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
static forModel(alias: string, overrides: RetryOptions = {}): RetryPolicy {
|
|
372
|
+
const policy = policyFor(alias)
|
|
373
|
+
return RetryPolicy.fromPolicy(policy, overrides)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Build a RetryPolicy directly from a `ModelPolicy`. Useful when the policy
|
|
378
|
+
* is already in hand (e.g. from a request context).
|
|
379
|
+
*/
|
|
380
|
+
static fromPolicy(policy: ModelPolicy, overrides: RetryOptions = {}): RetryPolicy {
|
|
381
|
+
const retryable = new Set<ErrorCategoryName>(policy.retry.retryableCategories)
|
|
382
|
+
const shouldRetry = (error: unknown): boolean => {
|
|
383
|
+
// Honour error's own retryable property when present.
|
|
384
|
+
if (error && typeof error === 'object' && 'retryable' in error) {
|
|
385
|
+
const flag = (error as { retryable: boolean }).retryable
|
|
386
|
+
if (flag === false) return false
|
|
387
|
+
}
|
|
388
|
+
const category = classifyError(error)
|
|
389
|
+
return retryable.has(category as ErrorCategoryName)
|
|
390
|
+
}
|
|
391
|
+
return new RetryPolicy({
|
|
392
|
+
maxRetries: policy.retry.maxRetries,
|
|
393
|
+
baseDelay: policy.retry.baseDelay,
|
|
394
|
+
maxDelay: policy.retry.maxDelay,
|
|
395
|
+
multiplier: policy.retry.multiplier,
|
|
396
|
+
jitter: policy.retry.jitter,
|
|
397
|
+
shouldRetry,
|
|
398
|
+
...overrides,
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
|
|
329
402
|
/**
|
|
330
403
|
* Execute an operation with retry logic
|
|
331
404
|
*/
|
|
332
|
-
async execute<T>(
|
|
333
|
-
operation: (info: RetryInfo) => Promise<T>
|
|
334
|
-
): Promise<T> {
|
|
405
|
+
async execute<T>(operation: (info: RetryInfo) => Promise<T>): Promise<T> {
|
|
335
406
|
let lastError: unknown
|
|
336
407
|
let previousDelay = this.options.baseDelay
|
|
337
408
|
|
|
@@ -386,7 +457,7 @@ export class RetryPolicy {
|
|
|
386
457
|
const attemptCounts = new Map<T, number>()
|
|
387
458
|
|
|
388
459
|
// Initialize attempt counts
|
|
389
|
-
items.forEach(item => attemptCounts.set(item, 0))
|
|
460
|
+
items.forEach((item) => attemptCounts.set(item, 0))
|
|
390
461
|
|
|
391
462
|
for (let round = 0; round <= this.options.maxRetries && pendingItems.length > 0; round++) {
|
|
392
463
|
// Wait before retry (not on first attempt)
|
|
@@ -427,7 +498,7 @@ export class RetryPolicy {
|
|
|
427
498
|
}
|
|
428
499
|
|
|
429
500
|
// Return results in original order
|
|
430
|
-
return items.map(item => finalResults.get(item)!)
|
|
501
|
+
return items.map((item) => finalResults.get(item)!)
|
|
431
502
|
}
|
|
432
503
|
|
|
433
504
|
private isRetryable(error: unknown): boolean {
|
|
@@ -438,7 +509,7 @@ export class RetryPolicy {
|
|
|
438
509
|
|
|
439
510
|
// Check error's own retryable property
|
|
440
511
|
if (error && typeof error === 'object' && 'retryable' in error) {
|
|
441
|
-
return (error as
|
|
512
|
+
return (error as { retryable: boolean }).retryable === true
|
|
442
513
|
}
|
|
443
514
|
|
|
444
515
|
// Classify error and determine retryability
|
|
@@ -452,7 +523,7 @@ export class RetryPolicy {
|
|
|
452
523
|
}
|
|
453
524
|
|
|
454
525
|
private sleep(ms: number): Promise<void> {
|
|
455
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
526
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
456
527
|
}
|
|
457
528
|
}
|
|
458
529
|
|
|
@@ -497,6 +568,12 @@ export interface CircuitBreakerMetrics {
|
|
|
497
568
|
* - CLOSED: Normal operation, failures tracked
|
|
498
569
|
* - OPEN: Fail fast, reject all requests
|
|
499
570
|
* - HALF-OPEN: Allow single test request
|
|
571
|
+
*
|
|
572
|
+
* @deprecated Phase C Week 3 — `CircuitBreaker` has zero real callers in
|
|
573
|
+
* primitives.org.ai (audited 2026-05-06; only comment-only references in
|
|
574
|
+
* `language-models/src/index.ts`; see `bd show aip-ibid`). AI SDK 6's
|
|
575
|
+
* `wrapLanguageModel(model, circuitMiddleware)` replacement is the going-
|
|
576
|
+
* forward primitive. Will be removed in the Phase C semver bump.
|
|
500
577
|
*/
|
|
501
578
|
export class CircuitBreaker {
|
|
502
579
|
private _state: CircuitState = 'closed'
|
|
@@ -517,6 +594,20 @@ export class CircuitBreaker {
|
|
|
517
594
|
}
|
|
518
595
|
}
|
|
519
596
|
|
|
597
|
+
/**
|
|
598
|
+
* Build a CircuitBreaker for a specific model, using its `ModelPolicy`.
|
|
599
|
+
* Per-call overrides win over policy data.
|
|
600
|
+
*/
|
|
601
|
+
static forModel(alias: string, overrides: CircuitBreakerOptions = {}): CircuitBreaker {
|
|
602
|
+
const policy = policyFor(alias)
|
|
603
|
+
return new CircuitBreaker({
|
|
604
|
+
failureThreshold: policy.circuitBreaker.failureThreshold,
|
|
605
|
+
resetTimeout: policy.circuitBreaker.resetTimeout,
|
|
606
|
+
successThreshold: policy.circuitBreaker.successThreshold,
|
|
607
|
+
...overrides,
|
|
608
|
+
})
|
|
609
|
+
}
|
|
610
|
+
|
|
520
611
|
/**
|
|
521
612
|
* Current circuit state
|
|
522
613
|
*/
|
|
@@ -658,6 +749,14 @@ export interface FallbackMetrics {
|
|
|
658
749
|
*
|
|
659
750
|
* Tries models in order until one succeeds:
|
|
660
751
|
* sonnet -> opus -> gpt-4o -> gemini
|
|
752
|
+
*
|
|
753
|
+
* @deprecated Phase C Week 3 — `FallbackChain` (LLM model failover) has
|
|
754
|
+
* zero real callers in primitives.org.ai (audited 2026-05-06; the
|
|
755
|
+
* `human-in-the-loop` package's `FallbackChain` is a different class for
|
|
756
|
+
* HITL fallback resolution, not LLM failover). AI SDK 4.3+ ships native
|
|
757
|
+
* `customProvider({ fallbackProvider })` which is the going-forward
|
|
758
|
+
* primitive. See `bd show aip-ibid`. Will be removed in the Phase C
|
|
759
|
+
* semver bump.
|
|
661
760
|
*/
|
|
662
761
|
export class FallbackChain<T = unknown, P = unknown> {
|
|
663
762
|
private readonly models: FallbackModel<T, P>[]
|
|
@@ -672,6 +771,33 @@ export class FallbackChain<T = unknown, P = unknown> {
|
|
|
672
771
|
this.options = options
|
|
673
772
|
}
|
|
674
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Build a FallbackChain from a model's `ModelPolicy`. The caller supplies
|
|
776
|
+
* an `executor` that takes a model id and returns a promise — the chain
|
|
777
|
+
* applies it to the primary model first, then to each fallback in order.
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* ```ts
|
|
781
|
+
* const chain = FallbackChain.forModel('sonnet', (modelId, params) =>
|
|
782
|
+
* ai({ model: modelId, prompt: params!.prompt })
|
|
783
|
+
* )
|
|
784
|
+
* await chain.execute({ prompt: 'Hello' })
|
|
785
|
+
* ```
|
|
786
|
+
*/
|
|
787
|
+
static forModel<T = unknown, P = unknown>(
|
|
788
|
+
alias: string,
|
|
789
|
+
executor: (modelId: string, params?: P) => Promise<T>,
|
|
790
|
+
options: FallbackOptions = {}
|
|
791
|
+
): FallbackChain<T, P> {
|
|
792
|
+
const policy = policyFor(alias)
|
|
793
|
+
const ids = [policy.$id, ...policy.fallbackChain]
|
|
794
|
+
const models: FallbackModel<T, P>[] = ids.map((id) => ({
|
|
795
|
+
name: id,
|
|
796
|
+
execute: (params?: P) => executor(id, params),
|
|
797
|
+
}))
|
|
798
|
+
return new FallbackChain<T, P>(models, options)
|
|
799
|
+
}
|
|
800
|
+
|
|
675
801
|
/**
|
|
676
802
|
* Execute the fallback chain
|
|
677
803
|
*/
|
|
@@ -758,15 +884,15 @@ export class FallbackChain<T = unknown, P = unknown> {
|
|
|
758
884
|
* const response = await reliableFetch('https://api.example.com')
|
|
759
885
|
* ```
|
|
760
886
|
*/
|
|
761
|
-
export function withRetry<
|
|
762
|
-
fn:
|
|
887
|
+
export function withRetry<TArgs extends unknown[], TResult>(
|
|
888
|
+
fn: (...args: TArgs) => Promise<TResult>,
|
|
763
889
|
options: RetryOptions = {}
|
|
764
|
-
):
|
|
890
|
+
): (...args: TArgs) => Promise<TResult> {
|
|
765
891
|
const policy = new RetryPolicy(options)
|
|
766
892
|
|
|
767
|
-
return
|
|
893
|
+
return async (...args: TArgs): Promise<TResult> => {
|
|
768
894
|
return policy.execute(() => fn(...args))
|
|
769
|
-
}
|
|
895
|
+
}
|
|
770
896
|
}
|
|
771
897
|
|
|
772
898
|
// ============================================================================
|
package/src/schema.ts
CHANGED
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { z, type ZodTypeAny } from 'zod'
|
|
18
|
-
import { isZodSchema } from '
|
|
18
|
+
import { isZodSchema } from './type-guards.js'
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Simplified schema types
|
|
22
22
|
*/
|
|
23
23
|
export type SimpleSchema =
|
|
24
|
-
| string
|
|
25
|
-
| [string]
|
|
26
|
-
| [number]
|
|
27
|
-
| [SimpleSchema]
|
|
28
|
-
| { [key: string]: SimpleSchema }
|
|
29
|
-
| ZodTypeAny
|
|
24
|
+
| string // z.string().describe(value)
|
|
25
|
+
| [string] // z.array(z.string()).describe(value)
|
|
26
|
+
| [number] // z.array(z.number()).describe(value)
|
|
27
|
+
| [SimpleSchema] // z.array(converted).describe(value)
|
|
28
|
+
| { [key: string]: SimpleSchema } // z.object() recursively
|
|
29
|
+
| ZodTypeAny // Pass-through for actual Zod schemas
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Convert a simplified schema to a Zod schema
|
|
@@ -64,7 +64,7 @@ function convertToZod(input: SimpleSchema): ZodTypeAny {
|
|
|
64
64
|
if (typeof input === 'string') {
|
|
65
65
|
// Enum syntax: 'option1 | option2 | option3'
|
|
66
66
|
if (input.includes(' | ')) {
|
|
67
|
-
const options = input.split(' | ').map(s => s.trim())
|
|
67
|
+
const options = input.split(' | ').map((s) => s.trim())
|
|
68
68
|
return z.enum(options as [string, ...string[]])
|
|
69
69
|
}
|
|
70
70
|
|