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
package/src/batch-queue.ts
CHANGED
|
@@ -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
|
|
240
|
-
options: options
|
|
241
|
-
metadata: options
|
|
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
|
-
//
|
|
250
|
-
this.submit()
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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 $${
|
|
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 ${
|
|
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 $${
|
|
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
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 = (
|
|
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(
|
|
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>(
|
|
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
|
|
601
|
+
keyParams['system'] = params.system
|
|
574
602
|
}
|
|
575
603
|
|
|
576
604
|
if (params.temperature !== undefined) {
|
|
577
|
-
keyParams
|
|
605
|
+
keyParams['temperature'] = params.temperature
|
|
578
606
|
}
|
|
579
607
|
|
|
580
608
|
if (params.schemaVersion !== undefined) {
|
|
581
|
-
keyParams
|
|
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
|
}
|