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.
Files changed (277) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +55 -1
  3. package/README.md +38 -0
  4. package/dist/ai-promise.d.ts +3 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +135 -64
  7. package/dist/ai-promise.js.map +1 -1
  8. package/dist/ai-schemas.d.ts +56 -0
  9. package/dist/ai-schemas.d.ts.map +1 -0
  10. package/dist/ai-schemas.js +53 -0
  11. package/dist/ai-schemas.js.map +1 -0
  12. package/dist/ai.d.ts +16 -242
  13. package/dist/ai.d.ts.map +1 -1
  14. package/dist/ai.js +51 -858
  15. package/dist/ai.js.map +1 -1
  16. package/dist/batch/anthropic.d.ts +6 -4
  17. package/dist/batch/anthropic.d.ts.map +1 -1
  18. package/dist/batch/anthropic.js +83 -145
  19. package/dist/batch/anthropic.js.map +1 -1
  20. package/dist/batch/bedrock.d.ts +8 -30
  21. package/dist/batch/bedrock.d.ts.map +1 -1
  22. package/dist/batch/bedrock.js +155 -338
  23. package/dist/batch/bedrock.js.map +1 -1
  24. package/dist/batch/cloudflare.d.ts +8 -20
  25. package/dist/batch/cloudflare.d.ts.map +1 -1
  26. package/dist/batch/cloudflare.js +68 -189
  27. package/dist/batch/cloudflare.js.map +1 -1
  28. package/dist/batch/google.d.ts +6 -20
  29. package/dist/batch/google.d.ts.map +1 -1
  30. package/dist/batch/google.js +70 -238
  31. package/dist/batch/google.js.map +1 -1
  32. package/dist/batch/index.d.ts +4 -1
  33. package/dist/batch/index.d.ts.map +1 -1
  34. package/dist/batch/index.js +4 -1
  35. package/dist/batch/index.js.map +1 -1
  36. package/dist/batch/memory.d.ts +1 -1
  37. package/dist/batch/memory.d.ts.map +1 -1
  38. package/dist/batch/memory.js +14 -10
  39. package/dist/batch/memory.js.map +1 -1
  40. package/dist/batch/openai.d.ts +11 -14
  41. package/dist/batch/openai.d.ts.map +1 -1
  42. package/dist/batch/openai.js +52 -156
  43. package/dist/batch/openai.js.map +1 -1
  44. package/dist/batch/provider.d.ts +111 -0
  45. package/dist/batch/provider.d.ts.map +1 -0
  46. package/dist/batch/provider.js +233 -0
  47. package/dist/batch/provider.js.map +1 -0
  48. package/dist/batch-map.d.ts.map +1 -1
  49. package/dist/batch-map.js +23 -17
  50. package/dist/batch-map.js.map +1 -1
  51. package/dist/batch-queue.d.ts +65 -0
  52. package/dist/batch-queue.d.ts.map +1 -1
  53. package/dist/batch-queue.js +169 -14
  54. package/dist/batch-queue.js.map +1 -1
  55. package/dist/budget.d.ts.map +1 -1
  56. package/dist/budget.js +27 -14
  57. package/dist/budget.js.map +1 -1
  58. package/dist/cache.d.ts +23 -0
  59. package/dist/cache.d.ts.map +1 -1
  60. package/dist/cache.js +36 -15
  61. package/dist/cache.js.map +1 -1
  62. package/dist/context.d.ts +26 -8
  63. package/dist/context.d.ts.map +1 -1
  64. package/dist/context.js +64 -62
  65. package/dist/context.js.map +1 -1
  66. package/dist/digital-objects-registry.d.ts +229 -0
  67. package/dist/digital-objects-registry.d.ts.map +1 -0
  68. package/dist/digital-objects-registry.js +617 -0
  69. package/dist/digital-objects-registry.js.map +1 -0
  70. package/dist/embeddings.d.ts +2 -2
  71. package/dist/embeddings.d.ts.map +1 -1
  72. package/dist/errors.d.ts +22 -0
  73. package/dist/errors.d.ts.map +1 -0
  74. package/dist/errors.js +35 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/eval/runner.d.ts +8 -0
  77. package/dist/eval/runner.d.ts.map +1 -1
  78. package/dist/eval/runner.js +41 -35
  79. package/dist/eval/runner.js.map +1 -1
  80. package/dist/eval-log/in-memory.d.ts +34 -0
  81. package/dist/eval-log/in-memory.d.ts.map +1 -0
  82. package/dist/eval-log/in-memory.js +84 -0
  83. package/dist/eval-log/in-memory.js.map +1 -0
  84. package/dist/eval-log/index.d.ts +29 -0
  85. package/dist/eval-log/index.d.ts.map +1 -0
  86. package/dist/eval-log/index.js +39 -0
  87. package/dist/eval-log/index.js.map +1 -0
  88. package/dist/eval-log/types.d.ts +101 -0
  89. package/dist/eval-log/types.d.ts.map +1 -0
  90. package/dist/eval-log/types.js +16 -0
  91. package/dist/eval-log/types.js.map +1 -0
  92. package/dist/function-registry.d.ts +116 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +546 -0
  95. package/dist/function-registry.js.map +1 -0
  96. package/dist/generate.d.ts +9 -3
  97. package/dist/generate.d.ts.map +1 -1
  98. package/dist/generate.js +18 -18
  99. package/dist/generate.js.map +1 -1
  100. package/dist/index.d.ts +18 -11
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +35 -18
  103. package/dist/index.js.map +1 -1
  104. package/dist/logger.d.ts +118 -0
  105. package/dist/logger.d.ts.map +1 -0
  106. package/dist/logger.js +187 -0
  107. package/dist/logger.js.map +1 -0
  108. package/dist/middleware/budget.d.ts +84 -0
  109. package/dist/middleware/budget.d.ts.map +1 -0
  110. package/dist/middleware/budget.js +110 -0
  111. package/dist/middleware/budget.js.map +1 -0
  112. package/dist/middleware/cache.d.ts +103 -0
  113. package/dist/middleware/cache.d.ts.map +1 -0
  114. package/dist/middleware/cache.js +228 -0
  115. package/dist/middleware/cache.js.map +1 -0
  116. package/dist/middleware/embed-cache.d.ts +99 -0
  117. package/dist/middleware/embed-cache.d.ts.map +1 -0
  118. package/dist/middleware/embed-cache.js +128 -0
  119. package/dist/middleware/embed-cache.js.map +1 -0
  120. package/dist/middleware/index.d.ts +11 -0
  121. package/dist/middleware/index.d.ts.map +1 -0
  122. package/dist/middleware/index.js +11 -0
  123. package/dist/middleware/index.js.map +1 -0
  124. package/dist/middleware/trace.d.ts +103 -0
  125. package/dist/middleware/trace.d.ts.map +1 -0
  126. package/dist/middleware/trace.js +176 -0
  127. package/dist/middleware/trace.js.map +1 -0
  128. package/dist/primitives.d.ts +120 -1
  129. package/dist/primitives.d.ts.map +1 -1
  130. package/dist/primitives.js +398 -26
  131. package/dist/primitives.js.map +1 -1
  132. package/dist/retry.d.ts +66 -1
  133. package/dist/retry.d.ts.map +1 -1
  134. package/dist/retry.js +115 -8
  135. package/dist/retry.js.map +1 -1
  136. package/dist/schema.js +2 -2
  137. package/dist/schema.js.map +1 -1
  138. package/dist/telemetry.d.ts +128 -0
  139. package/dist/telemetry.d.ts.map +1 -0
  140. package/dist/telemetry.js +285 -0
  141. package/dist/telemetry.js.map +1 -0
  142. package/dist/template.d.ts.map +1 -1
  143. package/dist/template.js +6 -1
  144. package/dist/template.js.map +1 -1
  145. package/dist/tool-orchestration.d.ts +66 -4
  146. package/dist/tool-orchestration.d.ts.map +1 -1
  147. package/dist/tool-orchestration.js +123 -23
  148. package/dist/tool-orchestration.js.map +1 -1
  149. package/dist/type-guards.d.ts +28 -0
  150. package/dist/type-guards.d.ts.map +1 -0
  151. package/dist/type-guards.js +29 -0
  152. package/dist/type-guards.js.map +1 -0
  153. package/dist/types.d.ts +135 -17
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +36 -1
  156. package/dist/types.js.map +1 -1
  157. package/dist/wrap-for-v3.d.ts +80 -0
  158. package/dist/wrap-for-v3.d.ts.map +1 -0
  159. package/dist/wrap-for-v3.js +89 -0
  160. package/dist/wrap-for-v3.js.map +1 -0
  161. package/examples/00-quickstart.ts +232 -0
  162. package/examples/01-rag-chatbot.ts +212 -0
  163. package/examples/02-multi-agent-research.ts +290 -0
  164. package/examples/03-email-classification.ts +379 -0
  165. package/examples/04-content-moderation.ts +400 -0
  166. package/examples/05-document-extraction.ts +455 -0
  167. package/examples/06-streaming-chat-nextjs.ts +437 -0
  168. package/examples/07-cloudflare-worker.ts +483 -0
  169. package/examples/08-batch-processing.ts +491 -0
  170. package/examples/09-budget-constrained.ts +527 -0
  171. package/examples/10-tool-orchestration.ts +565 -0
  172. package/examples/11-retry-resilience.ts +403 -0
  173. package/examples/12-caching-strategies.ts +422 -0
  174. package/examples/README.md +145 -0
  175. package/package.json +28 -25
  176. package/src/ai-promise.ts +226 -140
  177. package/src/ai-schemas.ts +122 -0
  178. package/src/ai.ts +69 -1176
  179. package/src/batch/anthropic.ts +96 -161
  180. package/src/batch/bedrock.ts +203 -454
  181. package/src/batch/cloudflare.ts +99 -282
  182. package/src/batch/google.ts +91 -297
  183. package/src/batch/index.ts +4 -1
  184. package/src/batch/memory.ts +15 -10
  185. package/src/batch/openai.ts +65 -193
  186. package/src/batch/provider.ts +336 -0
  187. package/src/batch-map.ts +29 -24
  188. package/src/batch-queue.ts +200 -11
  189. package/src/budget.ts +31 -18
  190. package/src/cache.ts +45 -17
  191. package/src/context.ts +106 -77
  192. package/src/digital-objects-registry.ts +750 -0
  193. package/src/errors.ts +37 -0
  194. package/src/eval/runner.ts +60 -36
  195. package/src/eval-log/in-memory.ts +90 -0
  196. package/src/eval-log/index.ts +46 -0
  197. package/src/eval-log/types.ts +110 -0
  198. package/src/function-registry.ts +671 -0
  199. package/src/generate.ts +33 -28
  200. package/src/index.ts +119 -21
  201. package/src/logger.ts +232 -0
  202. package/src/middleware/budget.ts +171 -0
  203. package/src/middleware/cache.ts +299 -0
  204. package/src/middleware/embed-cache.ts +195 -0
  205. package/src/middleware/index.ts +23 -0
  206. package/src/middleware/trace.ts +248 -0
  207. package/src/primitives.ts +589 -62
  208. package/src/retry.ts +144 -18
  209. package/src/schema.ts +8 -8
  210. package/src/telemetry.ts +403 -0
  211. package/src/template.ts +8 -4
  212. package/src/tool-orchestration.ts +213 -48
  213. package/src/type-guards.ts +31 -0
  214. package/src/types.ts +164 -25
  215. package/src/wrap-for-v3.ts +105 -0
  216. package/test/ai-promise.test.ts +1080 -0
  217. package/test/ai-proxy.test.ts +1 -1
  218. package/test/batch-autosubmit-errors.test.ts +49 -37
  219. package/test/batch-blog-posts.test.ts +87 -129
  220. package/test/core-functions.test.ts +183 -579
  221. package/test/decide.test.ts +154 -322
  222. package/test/define.test.ts +211 -8
  223. package/test/digital-objects-registry.test.ts +760 -0
  224. package/test/embedding-cache-middleware.test.ts +140 -0
  225. package/test/generate-core.test.ts +140 -229
  226. package/test/implicit-batch.test.ts +22 -65
  227. package/test/retry-policy-integration.test.ts +117 -0
  228. package/test/schema.test.ts +55 -19
  229. package/test/template.test.ts +1164 -0
  230. package/test/tool-orchestration.test.ts +270 -0
  231. package/test/wrap-for-v3.test.ts +612 -0
  232. package/vitest.config.js +6 -0
  233. package/vitest.config.ts +20 -0
  234. package/LICENSE +0 -21
  235. package/dist/rpc/auth.d.ts +0 -69
  236. package/dist/rpc/auth.d.ts.map +0 -1
  237. package/dist/rpc/auth.js +0 -136
  238. package/dist/rpc/auth.js.map +0 -1
  239. package/dist/rpc/client.d.ts +0 -62
  240. package/dist/rpc/client.d.ts.map +0 -1
  241. package/dist/rpc/client.js +0 -103
  242. package/dist/rpc/client.js.map +0 -1
  243. package/dist/rpc/deferred.d.ts +0 -60
  244. package/dist/rpc/deferred.d.ts.map +0 -1
  245. package/dist/rpc/deferred.js +0 -96
  246. package/dist/rpc/deferred.js.map +0 -1
  247. package/dist/rpc/index.d.ts +0 -22
  248. package/dist/rpc/index.d.ts.map +0 -1
  249. package/dist/rpc/index.js +0 -38
  250. package/dist/rpc/index.js.map +0 -1
  251. package/dist/rpc/local.d.ts +0 -42
  252. package/dist/rpc/local.d.ts.map +0 -1
  253. package/dist/rpc/local.js +0 -50
  254. package/dist/rpc/local.js.map +0 -1
  255. package/dist/rpc/server.d.ts +0 -165
  256. package/dist/rpc/server.d.ts.map +0 -1
  257. package/dist/rpc/server.js +0 -405
  258. package/dist/rpc/server.js.map +0 -1
  259. package/dist/rpc/session.d.ts +0 -32
  260. package/dist/rpc/session.d.ts.map +0 -1
  261. package/dist/rpc/session.js +0 -43
  262. package/dist/rpc/session.js.map +0 -1
  263. package/dist/rpc/transport.d.ts +0 -306
  264. package/dist/rpc/transport.d.ts.map +0 -1
  265. package/dist/rpc/transport.js +0 -731
  266. package/dist/rpc/transport.js.map +0 -1
  267. package/src/batch/anthropic.js +0 -256
  268. package/src/batch/bedrock.js +0 -584
  269. package/src/batch/cloudflare.js +0 -287
  270. package/src/batch/google.js +0 -359
  271. package/src/batch/index.js +0 -30
  272. package/src/batch/memory.js +0 -187
  273. package/src/batch/openai.js +0 -402
  274. package/src/eval/index.js +0 -7
  275. package/src/eval/models.js +0 -119
  276. package/src/eval/runner.js +0 -147
  277. 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
- this.retryAfter = options?.retryAfter
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: { status: number; headers?: Record<string, string> }): RateLimitError {
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(`Rate limited (${response.status})`, { retryAfter })
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 any).status as number | undefined
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'>> & { shouldRetry?: (error: unknown) => boolean }
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 any).retryable === true
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<T extends (...args: any[]) => Promise<any>>(
762
- fn: T,
887
+ export function withRetry<TArgs extends unknown[], TResult>(
888
+ fn: (...args: TArgs) => Promise<TResult>,
763
889
  options: RetryOptions = {}
764
- ): T {
890
+ ): (...args: TArgs) => Promise<TResult> {
765
891
  const policy = new RetryPolicy(options)
766
892
 
767
- return (async (...args: Parameters<T>) => {
893
+ return async (...args: TArgs): Promise<TResult> => {
768
894
  return policy.execute(() => fn(...args))
769
- }) as T
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 '@org.ai/core'
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 // 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
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