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.
Files changed (284) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +90 -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 +176 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +685 -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/sandbox.d.ts +36 -0
  137. package/dist/sandbox.d.ts.map +1 -0
  138. package/dist/sandbox.js +44 -0
  139. package/dist/sandbox.js.map +1 -0
  140. package/dist/schema.js +2 -2
  141. package/dist/schema.js.map +1 -1
  142. package/dist/telemetry.d.ts +128 -0
  143. package/dist/telemetry.d.ts.map +1 -0
  144. package/dist/telemetry.js +285 -0
  145. package/dist/telemetry.js.map +1 -0
  146. package/dist/template.d.ts.map +1 -1
  147. package/dist/template.js +6 -1
  148. package/dist/template.js.map +1 -1
  149. package/dist/tool-orchestration.d.ts +66 -4
  150. package/dist/tool-orchestration.d.ts.map +1 -1
  151. package/dist/tool-orchestration.js +123 -23
  152. package/dist/tool-orchestration.js.map +1 -1
  153. package/dist/type-guards.d.ts +28 -0
  154. package/dist/type-guards.d.ts.map +1 -0
  155. package/dist/type-guards.js +29 -0
  156. package/dist/type-guards.js.map +1 -0
  157. package/dist/types.d.ts +155 -19
  158. package/dist/types.d.ts.map +1 -1
  159. package/dist/types.js +36 -1
  160. package/dist/types.js.map +1 -1
  161. package/dist/wrap-for-v3.d.ts +80 -0
  162. package/dist/wrap-for-v3.d.ts.map +1 -0
  163. package/dist/wrap-for-v3.js +89 -0
  164. package/dist/wrap-for-v3.js.map +1 -0
  165. package/examples/00-quickstart.ts +232 -0
  166. package/examples/01-rag-chatbot.ts +212 -0
  167. package/examples/02-multi-agent-research.ts +290 -0
  168. package/examples/03-email-classification.ts +379 -0
  169. package/examples/04-content-moderation.ts +400 -0
  170. package/examples/05-document-extraction.ts +455 -0
  171. package/examples/06-streaming-chat-nextjs.ts +437 -0
  172. package/examples/07-cloudflare-worker.ts +483 -0
  173. package/examples/08-batch-processing.ts +491 -0
  174. package/examples/09-budget-constrained.ts +527 -0
  175. package/examples/10-tool-orchestration.ts +565 -0
  176. package/examples/11-retry-resilience.ts +403 -0
  177. package/examples/12-caching-strategies.ts +422 -0
  178. package/examples/README.md +145 -0
  179. package/package.json +29 -25
  180. package/src/ai-promise.ts +226 -140
  181. package/src/ai-schemas.ts +122 -0
  182. package/src/ai.ts +71 -1176
  183. package/src/batch/anthropic.ts +96 -161
  184. package/src/batch/bedrock.ts +203 -454
  185. package/src/batch/cloudflare.ts +99 -282
  186. package/src/batch/google.ts +91 -297
  187. package/src/batch/index.ts +4 -1
  188. package/src/batch/memory.ts +15 -10
  189. package/src/batch/openai.ts +65 -193
  190. package/src/batch/provider.ts +336 -0
  191. package/src/batch-map.ts +29 -24
  192. package/src/batch-queue.ts +200 -11
  193. package/src/budget.ts +31 -18
  194. package/src/cache.ts +45 -17
  195. package/src/context.ts +106 -77
  196. package/src/digital-objects-registry.ts +750 -0
  197. package/src/errors.ts +37 -0
  198. package/src/eval/runner.ts +60 -36
  199. package/src/eval-log/in-memory.ts +90 -0
  200. package/src/eval-log/index.ts +46 -0
  201. package/src/eval-log/types.ts +110 -0
  202. package/src/function-registry.ts +874 -0
  203. package/src/generate.ts +33 -28
  204. package/src/index.ts +122 -21
  205. package/src/logger.ts +232 -0
  206. package/src/middleware/budget.ts +171 -0
  207. package/src/middleware/cache.ts +299 -0
  208. package/src/middleware/embed-cache.ts +195 -0
  209. package/src/middleware/index.ts +23 -0
  210. package/src/middleware/trace.ts +248 -0
  211. package/src/primitives.ts +589 -62
  212. package/src/retry.ts +144 -18
  213. package/src/sandbox.ts +52 -0
  214. package/src/schema.ts +8 -8
  215. package/src/telemetry.ts +403 -0
  216. package/src/template.ts +8 -4
  217. package/src/tool-orchestration.ts +213 -48
  218. package/src/type-guards.ts +31 -0
  219. package/src/types.ts +186 -27
  220. package/src/wrap-for-v3.ts +105 -0
  221. package/test/ai-promise.test.ts +1080 -0
  222. package/test/ai-proxy.test.ts +1 -1
  223. package/test/batch-autosubmit-errors.test.ts +49 -37
  224. package/test/batch-blog-posts.test.ts +87 -129
  225. package/test/core-functions.test.ts +183 -579
  226. package/test/decide.test.ts +154 -322
  227. package/test/define.test.ts +211 -8
  228. package/test/digital-objects-registry.test.ts +760 -0
  229. package/test/embedding-cache-middleware.test.ts +140 -0
  230. package/test/fill-template.test.ts +89 -0
  231. package/test/generate-core.test.ts +140 -229
  232. package/test/implicit-batch.test.ts +22 -65
  233. package/test/retry-policy-integration.test.ts +117 -0
  234. package/test/sandbox-execution.test.ts +155 -0
  235. package/test/schema.test.ts +55 -19
  236. package/test/template.test.ts +1164 -0
  237. package/test/tool-orchestration.test.ts +270 -0
  238. package/test/wrap-for-v3.test.ts +612 -0
  239. package/vitest.config.js +6 -0
  240. package/vitest.config.ts +20 -0
  241. package/LICENSE +0 -21
  242. package/dist/rpc/auth.d.ts +0 -69
  243. package/dist/rpc/auth.d.ts.map +0 -1
  244. package/dist/rpc/auth.js +0 -136
  245. package/dist/rpc/auth.js.map +0 -1
  246. package/dist/rpc/client.d.ts +0 -62
  247. package/dist/rpc/client.d.ts.map +0 -1
  248. package/dist/rpc/client.js +0 -103
  249. package/dist/rpc/client.js.map +0 -1
  250. package/dist/rpc/deferred.d.ts +0 -60
  251. package/dist/rpc/deferred.d.ts.map +0 -1
  252. package/dist/rpc/deferred.js +0 -96
  253. package/dist/rpc/deferred.js.map +0 -1
  254. package/dist/rpc/index.d.ts +0 -22
  255. package/dist/rpc/index.d.ts.map +0 -1
  256. package/dist/rpc/index.js +0 -38
  257. package/dist/rpc/index.js.map +0 -1
  258. package/dist/rpc/local.d.ts +0 -42
  259. package/dist/rpc/local.d.ts.map +0 -1
  260. package/dist/rpc/local.js +0 -50
  261. package/dist/rpc/local.js.map +0 -1
  262. package/dist/rpc/server.d.ts +0 -165
  263. package/dist/rpc/server.d.ts.map +0 -1
  264. package/dist/rpc/server.js +0 -405
  265. package/dist/rpc/server.js.map +0 -1
  266. package/dist/rpc/session.d.ts +0 -32
  267. package/dist/rpc/session.d.ts.map +0 -1
  268. package/dist/rpc/session.js +0 -43
  269. package/dist/rpc/session.js.map +0 -1
  270. package/dist/rpc/transport.d.ts +0 -306
  271. package/dist/rpc/transport.d.ts.map +0 -1
  272. package/dist/rpc/transport.js +0 -731
  273. package/dist/rpc/transport.js.map +0 -1
  274. package/src/batch/anthropic.js +0 -256
  275. package/src/batch/bedrock.js +0 -584
  276. package/src/batch/cloudflare.js +0 -287
  277. package/src/batch/google.js +0 -359
  278. package/src/batch/index.js +0 -30
  279. package/src/batch/memory.js +0 -187
  280. package/src/batch/openai.js +0 -402
  281. package/src/eval/index.js +0 -7
  282. package/src/eval/models.js +0 -119
  283. package/src/eval/runner.js +0 -147
  284. package/test/schema.test.js +0 -96
@@ -26,6 +26,8 @@
26
26
 
27
27
  import type { FunctionOptions } from './template.js'
28
28
  import type { SimpleSchema } from './schema.js'
29
+ import { getLogger } from './logger.js'
30
+ import { policyFor, type BatchTier } from 'language-models'
29
31
 
30
32
  // ============================================================================
31
33
  // Types
@@ -197,6 +199,11 @@ export interface BatchQueueOptions {
197
199
  // BatchQueue Implementation
198
200
  // ============================================================================
199
201
 
202
+ /**
203
+ * Event handler type for BatchQueue events
204
+ */
205
+ export type BatchEventHandler<T = unknown> = (data: T) => void
206
+
200
207
  /**
201
208
  * BatchQueue collects AI operations for deferred batch execution
202
209
  */
@@ -207,6 +214,11 @@ export class BatchQueue {
207
214
  private submitted = false
208
215
  private job: BatchJob | null = null
209
216
 
217
+ // Error handling properties
218
+ private _autoSubmitPromise: Promise<void> | null = null
219
+ private _submissionError: Error | null = null
220
+ private _eventHandlers: Map<string, Set<BatchEventHandler>> = new Map()
221
+
210
222
  constructor(options: BatchQueueOptions = {}) {
211
223
  this.options = {
212
224
  provider: 'openai',
@@ -216,6 +228,95 @@ export class BatchQueue {
216
228
  }
217
229
  }
218
230
 
231
+ /**
232
+ * Subscribe to batch events
233
+ * @param event - Event name ('error', 'partial-failure', 'complete')
234
+ * @param handler - Event handler function
235
+ */
236
+ on<T = unknown>(event: string, handler: BatchEventHandler<T>): void {
237
+ if (!this._eventHandlers.has(event)) {
238
+ this._eventHandlers.set(event, new Set())
239
+ }
240
+ this._eventHandlers.get(event)!.add(handler as BatchEventHandler)
241
+ }
242
+
243
+ /**
244
+ * Unsubscribe from batch events
245
+ */
246
+ off(event: string, handler: BatchEventHandler): void {
247
+ this._eventHandlers.get(event)?.delete(handler)
248
+ }
249
+
250
+ /**
251
+ * Emit an event to all subscribed handlers
252
+ */
253
+ private emit<T>(event: string, data: T): void {
254
+ this._eventHandlers.get(event)?.forEach((handler) => handler(data))
255
+ }
256
+
257
+ /**
258
+ * Get the auto-submit promise (if auto-submit was triggered)
259
+ */
260
+ get autoSubmitPromise(): Promise<void> | undefined {
261
+ return this._autoSubmitPromise ?? undefined
262
+ }
263
+
264
+ /**
265
+ * Get the last submission error (if any)
266
+ */
267
+ get submissionError(): Error | undefined {
268
+ return this._submissionError ?? undefined
269
+ }
270
+
271
+ /**
272
+ * Check if there was a submission error
273
+ */
274
+ get hasSubmissionError(): boolean {
275
+ return this._submissionError !== null
276
+ }
277
+
278
+ /**
279
+ * Await auto-submit completion or failure
280
+ * Returns a promise that resolves when auto-submit completes or rejects on error
281
+ */
282
+ awaitAutoSubmit(): Promise<void> {
283
+ if (!this._autoSubmitPromise) {
284
+ return Promise.resolve()
285
+ }
286
+ return this._autoSubmitPromise
287
+ }
288
+
289
+ /**
290
+ * Get items that failed during batch processing
291
+ */
292
+ getFailedItems(): BatchItem[] {
293
+ return this.items.filter((item) => item.status === 'failed')
294
+ }
295
+
296
+ /**
297
+ * Retry a failed submission
298
+ * Only available after a failed auto-submit
299
+ */
300
+ async retry(): Promise<BatchSubmitResult> {
301
+ if (!this._submissionError) {
302
+ throw new Error('No failed submission to retry')
303
+ }
304
+
305
+ // Reset error state and submitted flag
306
+ this._submissionError = null
307
+ this.submitted = false
308
+
309
+ // Reset item statuses
310
+ for (const item of this.items) {
311
+ if (item.status === 'failed') {
312
+ item.status = 'pending'
313
+ delete item.error
314
+ }
315
+ }
316
+
317
+ return this.submit()
318
+ }
319
+
219
320
  /**
220
321
  * Add an item to the batch queue
221
322
  * Returns a placeholder that will be resolved after batch completion
@@ -236,9 +337,9 @@ export class BatchQueue {
236
337
  const item: BatchItem<T> = {
237
338
  id: options?.customId || `item_${++this.idCounter}`,
238
339
  prompt,
239
- schema: options?.schema,
240
- options: options?.options,
241
- metadata: options?.metadata,
340
+ ...(options?.schema !== undefined && { schema: options.schema }),
341
+ ...(options?.options !== undefined && { options: options.options }),
342
+ ...(options?.metadata !== undefined && { metadata: options.metadata }),
242
343
  status: 'pending',
243
344
  }
244
345
 
@@ -246,8 +347,50 @@ export class BatchQueue {
246
347
 
247
348
  // Auto-submit if we hit the limit
248
349
  if (this.options.autoSubmit && this.items.length >= (this.options.maxItems || 50000)) {
249
- // Fire and forget - user can await the completion promise
250
- this.submit().catch(console.error)
350
+ // Create a trackable promise for auto-submit
351
+ this._autoSubmitPromise = this.submit()
352
+ .then(() => {
353
+ // Success - promise is resolved
354
+ })
355
+ .catch((error: Error) => {
356
+ // Store error and update item statuses
357
+ this._submissionError = error
358
+ this.submitted = false // Reset to allow retry
359
+
360
+ // Update all items to failed status
361
+ for (const item of this.items) {
362
+ if (item.status === 'pending') {
363
+ item.status = 'failed'
364
+ item.error = error.message
365
+ }
366
+ }
367
+
368
+ // Create a synthetic failed job to store error info
369
+ const errorWithMeta = error as Error & {
370
+ status?: number
371
+ headers?: Record<string, string>
372
+ }
373
+ this.job = {
374
+ id: `failed_${Date.now()}`,
375
+ provider: this.options.provider || 'openai',
376
+ status: 'failed',
377
+ totalItems: this.items.length,
378
+ completedItems: 0,
379
+ failedItems: this.items.length,
380
+ createdAt: new Date(),
381
+ // Add rate limit retry info if available
382
+ ...(errorWithMeta.headers?.['retry-after'] && {
383
+ retryAfter: parseInt(errorWithMeta.headers['retry-after'], 10),
384
+ }),
385
+ } as BatchJob & { retryAfter?: number }
386
+
387
+ // Emit error event
388
+ this.emit('error', error)
389
+
390
+ // Log error and re-throw
391
+ getLogger().error('Batch auto-submit failed:', error)
392
+ throw error
393
+ })
251
394
  }
252
395
 
253
396
  return item
@@ -302,16 +445,35 @@ export class BatchQueue {
302
445
  const result = await adapter.submit(this.items, this.options)
303
446
  this.job = result.job
304
447
 
305
- // When completion resolves, update item statuses
448
+ // When completion resolves, update item statuses and check for partial failures
306
449
  result.completion.then((results) => {
450
+ const failedResults: BatchResult[] = []
451
+
307
452
  for (const r of results) {
308
453
  const item = this.items.find((i) => i.id === r.id)
309
454
  if (item) {
310
455
  item.status = r.status
311
- item.result = r.result
312
- item.error = r.error
456
+ if (r.result !== undefined) item.result = r.result
457
+ if (r.error !== undefined) item.error = r.error
458
+
459
+ if (r.status === 'failed') {
460
+ failedResults.push(r)
461
+ }
313
462
  }
314
463
  }
464
+
465
+ // Emit partial-failure event if some items failed
466
+ if (failedResults.length > 0 && failedResults.length < results.length) {
467
+ this.emit('partial-failure', failedResults)
468
+ }
469
+
470
+ // Emit error event if there were any failures
471
+ if (failedResults.length > 0) {
472
+ this.emit('error', new Error(`${failedResults.length} items failed in batch`))
473
+ }
474
+
475
+ // Emit complete event
476
+ this.emit('complete', results)
315
477
  })
316
478
 
317
479
  return result
@@ -496,6 +658,33 @@ export function hasFlexAdapter(provider: BatchProvider): boolean {
496
658
  return flexAdapters[provider] !== null
497
659
  }
498
660
 
661
+ // ============================================================================
662
+ // Tier eligibility (per-model policy)
663
+ // ============================================================================
664
+
665
+ /**
666
+ * List the batch tiers a model is eligible for.
667
+ *
668
+ * Reads `ModelPolicy.batchTier` from `language-models` — this is the per-model
669
+ * policy data, distinct from the runtime adapter registration above.
670
+ *
671
+ * @example
672
+ * ```ts
673
+ * tiersForModel('sonnet') // ['immediate', 'batch']
674
+ * tiersForModel('gpt-4o') // ['immediate', 'flex', 'batch']
675
+ * ```
676
+ */
677
+ export function tiersForModel(alias: string): BatchTier[] {
678
+ return policyFor(alias).batchTier
679
+ }
680
+
681
+ /**
682
+ * Check whether a model is eligible for a given tier.
683
+ */
684
+ export function modelSupportsTier(alias: string, tier: BatchTier): boolean {
685
+ return tiersForModel(alias).includes(tier)
686
+ }
687
+
499
688
  // ============================================================================
500
689
  // Factory Functions
501
690
  // ============================================================================
@@ -590,8 +779,8 @@ export function deferToBatch<T>(
590
779
  options?: FunctionOptions & { customId?: string }
591
780
  ): BatchItem<T> {
592
781
  return batch.add<T>(prompt, {
593
- schema,
594
- options,
595
- customId: options?.customId,
782
+ ...(schema !== undefined && { schema }),
783
+ ...(options !== undefined && { options }),
784
+ ...(options?.customId !== undefined && { customId: options.customId }),
596
785
  })
597
786
  }
package/src/budget.ts CHANGED
@@ -124,7 +124,7 @@ const DEFAULT_MODEL_PRICING: Record<string, ModelPricing> = {
124
124
  'gpt-4-turbo': { inputPricePerMillion: 10, outputPricePerMillion: 30 },
125
125
  'gpt-4': { inputPricePerMillion: 30, outputPricePerMillion: 60 },
126
126
  'gpt-3.5-turbo': { inputPricePerMillion: 0.5, outputPricePerMillion: 1.5 },
127
- 'o1': { inputPricePerMillion: 15, outputPricePerMillion: 60 },
127
+ o1: { inputPricePerMillion: 15, outputPricePerMillion: 60 },
128
128
  'o1-mini': { inputPricePerMillion: 3, outputPricePerMillion: 12 },
129
129
  'o1-preview': { inputPricePerMillion: 15, outputPricePerMillion: 60 },
130
130
  'o3-mini': { inputPricePerMillion: 1.1, outputPricePerMillion: 4.4 },
@@ -144,7 +144,7 @@ const DEFAULT_MODEL_PRICING: Record<string, ModelPricing> = {
144
144
  'gemini-1.5-flash': { inputPricePerMillion: 0.075, outputPricePerMillion: 0.3 },
145
145
 
146
146
  // Default fallback
147
- 'default': { inputPricePerMillion: 1, outputPricePerMillion: 3 },
147
+ default: { inputPricePerMillion: 1, outputPricePerMillion: 3 },
148
148
  }
149
149
 
150
150
  // ============================================================================
@@ -181,7 +181,7 @@ export class TokenCounter {
181
181
 
182
182
  // Rough estimate: ~4 chars per token for English
183
183
  // Unicode characters may use more tokens
184
- const unicodeChars = Array.from(text).filter(char => char.charCodeAt(0) > 127).length
184
+ const unicodeChars = Array.from(text).filter((char) => char.charCodeAt(0) > 127).length
185
185
  const asciiChars = charCount - unicodeChars
186
186
 
187
187
  // ASCII chars: ~4 per token, Unicode: ~2 per token (rough)
@@ -240,7 +240,10 @@ export class BudgetExceededError extends Error {
240
240
  export class BudgetTracker {
241
241
  private totalInputTokens = 0
242
242
  private totalOutputTokens = 0
243
- private usageByModel: Record<string, { inputTokens: number; outputTokens: number; cost: number }> = {}
243
+ private usageByModel: Record<
244
+ string,
245
+ { inputTokens: number; outputTokens: number; cost: number }
246
+ > = {}
244
247
  private triggeredThresholds: Set<number> = new Set()
245
248
  private requests: StoredRequest[] = []
246
249
 
@@ -347,7 +350,9 @@ export class BudgetTracker {
347
350
 
348
351
  if (projectedCost > this.config.maxCost) {
349
352
  throw new BudgetExceededError(
350
- `Cost budget exceeded: $${projectedCost.toFixed(4)} would exceed limit of $${this.config.maxCost}`,
353
+ `Cost budget exceeded: $${projectedCost.toFixed(4)} would exceed limit of $${
354
+ this.config.maxCost
355
+ }`,
351
356
  'cost',
352
357
  this.config.maxCost,
353
358
  this.getTotalCost(),
@@ -365,7 +370,9 @@ export class BudgetTracker {
365
370
  if (this.config.maxTokens !== undefined) {
366
371
  if (this.getTotalTokens() > this.config.maxTokens) {
367
372
  throw new BudgetExceededError(
368
- `Token budget exceeded: ${this.getTotalTokens()} tokens exceeds limit of ${this.config.maxTokens}`,
373
+ `Token budget exceeded: ${this.getTotalTokens()} tokens exceeds limit of ${
374
+ this.config.maxTokens
375
+ }`,
369
376
  'tokens',
370
377
  this.config.maxTokens,
371
378
  this.getTotalTokens()
@@ -378,7 +385,9 @@ export class BudgetTracker {
378
385
  const currentCost = this.getTotalCost()
379
386
  if (currentCost > this.config.maxCost) {
380
387
  throw new BudgetExceededError(
381
- `Cost budget exceeded: $${currentCost.toFixed(4)} exceeds limit of $${this.config.maxCost}`,
388
+ `Cost budget exceeded: $${currentCost.toFixed(4)} exceeds limit of $${
389
+ this.config.maxCost
390
+ }`,
382
391
  'cost',
383
392
  this.config.maxCost,
384
393
  currentCost
@@ -580,11 +589,11 @@ export class RequestContext implements IRequestContext {
580
589
 
581
590
  constructor(options: RequestContextOptions & { depth?: number } = {}) {
582
591
  this.requestId = options.requestId ?? randomUUID()
583
- this.userId = options.userId
584
- this.tenantId = options.tenantId
585
- this.parentRequestId = options.parentRequestId
592
+ if (options.userId !== undefined) this.userId = options.userId
593
+ if (options.tenantId !== undefined) this.tenantId = options.tenantId
594
+ if (options.parentRequestId !== undefined) this.parentRequestId = options.parentRequestId
586
595
  this.depth = (options as { depth?: number }).depth ?? 0
587
- this.metadata = options.metadata
596
+ if (options.metadata !== undefined) this.metadata = options.metadata
588
597
 
589
598
  // Generate trace/span IDs for W3C traceparent
590
599
  this.traceId = randomUUID().replace(/-/g, '')
@@ -648,12 +657,13 @@ export class RequestContext implements IRequestContext {
648
657
  * Create a RequestContext from trace headers
649
658
  */
650
659
  static fromHeaders(headers: Record<string, string>): RequestContext {
651
- return new RequestContext({
652
- requestId: headers['x-request-id'],
653
- userId: headers['x-user-id'],
654
- tenantId: headers['x-tenant-id'],
655
- parentRequestId: headers['x-parent-request-id'],
656
- })
660
+ const opts: RequestContextOptions = {}
661
+ if (headers['x-request-id'] !== undefined) opts.requestId = headers['x-request-id']
662
+ if (headers['x-user-id'] !== undefined) opts.userId = headers['x-user-id']
663
+ if (headers['x-tenant-id'] !== undefined) opts.tenantId = headers['x-tenant-id']
664
+ if (headers['x-parent-request-id'] !== undefined)
665
+ opts.parentRequestId = headers['x-parent-request-id']
666
+ return new RequestContext(opts)
657
667
  }
658
668
  }
659
669
 
@@ -695,7 +705,10 @@ export async function withBudget<T>(
695
705
  const { userId, tenantId, ...budgetConfig } = options
696
706
 
697
707
  const tracker = new BudgetTracker(budgetConfig)
698
- const ctx = userId || tenantId ? createRequestContext({ userId, tenantId }) : undefined
708
+ const ctxOptions: RequestContextOptions = {}
709
+ if (userId !== undefined) ctxOptions.userId = userId
710
+ if (tenantId !== undefined) ctxOptions.tenantId = tenantId
711
+ const ctx = userId || tenantId ? createRequestContext(ctxOptions) : undefined
699
712
 
700
713
  // Track parent tracker for nested contexts
701
714
  const parentTracker = currentBudgetTracker
package/src/cache.ts CHANGED
@@ -95,6 +95,15 @@ export interface CacheStorage<T> {
95
95
 
96
96
  /**
97
97
  * In-memory cache implementation with TTL and LRU eviction support
98
+ *
99
+ * @deprecated Phase C Week 1 — `MemoryCache` has zero production callers in
100
+ * primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). New code
101
+ * should use `cacheMiddleware` (for `wrapLanguageModel`) or
102
+ * `embeddingCacheMiddleware` (for `wrapEmbeddingModel`) instead — both
103
+ * compose with AI SDK 6's `wrapLanguageModel` / `wrapEmbeddingModel` and
104
+ * carry per-call telemetry (TraceEvent emission) and budget tracking when
105
+ * paired via `wrapForV3`. The `MemoryCache` class will be removed in the
106
+ * Phase C semver bump alongside `EmbeddingCache` and `GenerationCache`.
98
107
  */
99
108
  export class MemoryCache<T> implements CacheStorage<T> {
100
109
  private cache: Map<string, CacheEntry<T>> = new Map()
@@ -157,7 +166,7 @@ export class MemoryCache<T> implements CacheStorage<T> {
157
166
  createdAt: now,
158
167
  lastAccessedAt: now,
159
168
  accessCount: 0,
160
- expiresAt: ttl ? now + ttl : undefined
169
+ ...(ttl ? { expiresAt: now + ttl } : {}),
161
170
  }
162
171
 
163
172
  // Check if we need to evict (LRU)
@@ -247,7 +256,7 @@ export class MemoryCache<T> implements CacheStorage<T> {
247
256
  dispose(): void {
248
257
  if (this.cleanupTimer) {
249
258
  clearInterval(this.cleanupTimer)
250
- this.cleanupTimer = undefined
259
+ delete this.cleanupTimer
251
260
  }
252
261
  }
253
262
 
@@ -311,14 +320,12 @@ export class MemoryCache<T> implements CacheStorage<T> {
311
320
  * Uses a fast, non-cryptographic hash suitable for cache keys
312
321
  */
313
322
  export function hashKey(input: unknown): string {
314
- const str = typeof input === 'string'
315
- ? input
316
- : stableStringify(input)
323
+ const str = typeof input === 'string' ? input : stableStringify(input)
317
324
 
318
325
  // Simple djb2 hash
319
326
  let hash = 5381
320
327
  for (let i = 0; i < str.length; i++) {
321
- hash = ((hash << 5) + hash) + str.charCodeAt(i)
328
+ hash = (hash << 5) + hash + str.charCodeAt(i)
322
329
  hash = hash & hash // Convert to 32bit integer
323
330
  }
324
331
 
@@ -338,7 +345,7 @@ function stableStringify(obj: unknown): string {
338
345
  }
339
346
 
340
347
  const keys = Object.keys(obj).sort()
341
- const pairs = keys.map(key => {
348
+ const pairs = keys.map((key) => {
342
349
  return JSON.stringify(key) + ':' + stableStringify((obj as Record<string, unknown>)[key])
343
350
  })
344
351
 
@@ -382,6 +389,14 @@ export interface BatchEmbeddingResult {
382
389
 
383
390
  /**
384
391
  * Specialized cache for embedding vectors
392
+ *
393
+ * @deprecated Phase C Week 1 — `EmbeddingCache` has zero production callers
394
+ * in primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). New
395
+ * code should use `embeddingCacheMiddleware` (for `wrapEmbeddingModel`) —
396
+ * it composes with AI SDK 6 directly and carries trace + budget telemetry
397
+ * when paired via `wrapForV3`. Note: `embeddingCacheMiddleware` keys on the
398
+ * whole batch, not per-text — callers wanting per-text caching should pass
399
+ * stable per-text batches. Will be removed in the Phase C semver bump.
385
400
  */
386
401
  export class EmbeddingCache {
387
402
  private storage: MemoryCache<number[]>
@@ -418,7 +433,11 @@ export class EmbeddingCache {
418
433
  /**
419
434
  * Set multiple embeddings at once
420
435
  */
421
- async setMany(texts: string[], embeddings: number[][], options: EmbeddingCacheOptions): Promise<void> {
436
+ async setMany(
437
+ texts: string[],
438
+ embeddings: number[][],
439
+ options: EmbeddingCacheOptions
440
+ ): Promise<void> {
422
441
  for (let i = 0; i < texts.length; i++) {
423
442
  await this.set(texts[i]!, embeddings[i]!, options)
424
443
  }
@@ -459,7 +478,7 @@ export class EmbeddingCache {
459
478
  hits: this.stats.hits,
460
479
  misses: this.stats.misses,
461
480
  hitRate: total > 0 ? this.stats.hits / total : 0,
462
- size: this.storage['cache'].size
481
+ size: this.storage['cache'].size,
463
482
  }
464
483
  }
465
484
 
@@ -502,6 +521,12 @@ export interface GenerationCacheGetOptions {
502
521
 
503
522
  /**
504
523
  * Specialized cache for generation results
524
+ *
525
+ * @deprecated Phase C Week 1 — `GenerationCache` has zero production callers
526
+ * in primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). New
527
+ * code should use `cacheMiddleware` (for `wrapLanguageModel`) — it composes
528
+ * with AI SDK 6 directly and carries trace + budget telemetry when paired
529
+ * via `wrapForV3`. Will be removed in the Phase C semver bump.
505
530
  */
506
531
  export class GenerationCache {
507
532
  private storage: MemoryCache<unknown>
@@ -514,7 +539,10 @@ export class GenerationCache {
514
539
  /**
515
540
  * Get a cached generation result
516
541
  */
517
- async get<T = unknown>(params: GenerationParams, options?: GenerationCacheGetOptions): Promise<T | undefined> {
542
+ async get<T = unknown>(
543
+ params: GenerationParams,
544
+ options?: GenerationCacheGetOptions
545
+ ): Promise<T | undefined> {
518
546
  if (options?.bypass) {
519
547
  return undefined
520
548
  }
@@ -548,7 +576,7 @@ export class GenerationCache {
548
576
  hits: this.stats.hits,
549
577
  misses: this.stats.misses,
550
578
  hitRate: total > 0 ? this.stats.hits / total : 0,
551
- size: this.storage['cache'].size
579
+ size: this.storage['cache'].size,
552
580
  }
553
581
  }
554
582
 
@@ -566,19 +594,19 @@ export class GenerationCache {
566
594
  private createKey(params: GenerationParams): string {
567
595
  const keyParams: Record<string, unknown> = {
568
596
  prompt: params.prompt,
569
- model: params.model
597
+ model: params.model,
570
598
  }
571
599
 
572
600
  if (params.system !== undefined) {
573
- keyParams.system = params.system
601
+ keyParams['system'] = params.system
574
602
  }
575
603
 
576
604
  if (params.temperature !== undefined) {
577
- keyParams.temperature = params.temperature
605
+ keyParams['temperature'] = params.temperature
578
606
  }
579
607
 
580
608
  if (params.schemaVersion !== undefined) {
581
- keyParams.schemaVersion = params.schemaVersion
609
+ keyParams['schemaVersion'] = params.schemaVersion
582
610
  }
583
611
 
584
612
  return createCacheKey('generation', keyParams)
@@ -631,7 +659,7 @@ export function withCache<TArgs extends unknown[], TResult>(
631
659
  const result = await fn(...args)
632
660
 
633
661
  // Cache result (don't cache errors - they throw before reaching here)
634
- await cache.set(key, result, { ttl })
662
+ await cache.set(key, result, ttl !== undefined ? { ttl } : undefined)
635
663
 
636
664
  return result
637
665
  }
@@ -644,7 +672,7 @@ export function withCache<TArgs extends unknown[], TResult>(
644
672
  const result = await fn(...args)
645
673
 
646
674
  // Update cache with new result
647
- await cache.set(key, result, { ttl })
675
+ await cache.set(key, result, ttl !== undefined ? { ttl } : undefined)
648
676
 
649
677
  return result
650
678
  }