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
|
@@ -156,13 +156,26 @@ export interface LoopOptions {
|
|
|
156
156
|
onStep?: (step: StepInfo) => void
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Model generation options passed to the model
|
|
161
|
+
*/
|
|
162
|
+
export interface ModelGenerationOptions {
|
|
163
|
+
/** Messages for the conversation */
|
|
164
|
+
messages: Message[]
|
|
165
|
+
/** Tools available for use */
|
|
166
|
+
tools: Record<
|
|
167
|
+
string,
|
|
168
|
+
{ description: string; parameters: unknown; execute: (args: unknown) => Promise<unknown> }
|
|
169
|
+
>
|
|
170
|
+
}
|
|
171
|
+
|
|
159
172
|
/**
|
|
160
173
|
* Options for running the loop
|
|
161
174
|
*/
|
|
162
175
|
export interface RunOptions {
|
|
163
176
|
/** Model to use for generation */
|
|
164
177
|
model: {
|
|
165
|
-
generate: (options:
|
|
178
|
+
generate: (options: ModelGenerationOptions) => Promise<ModelResponse>
|
|
166
179
|
}
|
|
167
180
|
/** Initial prompt */
|
|
168
181
|
prompt: string
|
|
@@ -263,7 +276,7 @@ export class ToolValidator {
|
|
|
263
276
|
if (error instanceof z.ZodError) {
|
|
264
277
|
return {
|
|
265
278
|
valid: false,
|
|
266
|
-
errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`),
|
|
279
|
+
errors: error.errors.map((e) => `${e.path.join('.')}: ${e.message}`),
|
|
267
280
|
}
|
|
268
281
|
}
|
|
269
282
|
return {
|
|
@@ -277,7 +290,7 @@ export class ToolValidator {
|
|
|
277
290
|
* Validate multiple tool calls at once
|
|
278
291
|
*/
|
|
279
292
|
validateAll(calls: ToolCall[]): ValidationResult[] {
|
|
280
|
-
return calls.map(call => this.validate(call.name, call.arguments))
|
|
293
|
+
return calls.map((call) => this.validate(call.name, call.arguments))
|
|
281
294
|
}
|
|
282
295
|
}
|
|
283
296
|
|
|
@@ -287,6 +300,13 @@ export class ToolValidator {
|
|
|
287
300
|
|
|
288
301
|
/**
|
|
289
302
|
* Routes tool calls to registered handlers
|
|
303
|
+
*
|
|
304
|
+
* @deprecated Phase C Week 2 — `ToolRouter` has zero production callers in
|
|
305
|
+
* primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). Only the
|
|
306
|
+
* `ai-primitives` umbrella re-export tests reference it. AI SDK 6's native
|
|
307
|
+
* tool-routing under `generateText({ tools })` and `Agent` / `ToolLoopAgent`
|
|
308
|
+
* cover the same surface. Will be removed in the Phase C semver bump
|
|
309
|
+
* alongside `AgenticLoop` and `createAgenticLoop`.
|
|
290
310
|
*/
|
|
291
311
|
export class ToolRouter {
|
|
292
312
|
private tools = new Map<string, Tool>()
|
|
@@ -353,7 +373,7 @@ export class ToolRouter {
|
|
|
353
373
|
* Route multiple tool calls in parallel
|
|
354
374
|
*/
|
|
355
375
|
async routeAllParallel(calls: ToolCall[]): Promise<ToolResult[]> {
|
|
356
|
-
return Promise.all(calls.map(call => this.route(call)))
|
|
376
|
+
return Promise.all(calls.map((call) => this.route(call)))
|
|
357
377
|
}
|
|
358
378
|
|
|
359
379
|
/**
|
|
@@ -364,13 +384,13 @@ export class ToolRouter {
|
|
|
364
384
|
return {
|
|
365
385
|
role: 'tool',
|
|
366
386
|
content: JSON.stringify(result.result),
|
|
367
|
-
tool_call_id: result.toolCall
|
|
387
|
+
...(result.toolCall?.id !== undefined && { tool_call_id: result.toolCall.id }),
|
|
368
388
|
}
|
|
369
389
|
}
|
|
370
390
|
return {
|
|
371
391
|
role: 'tool',
|
|
372
392
|
content: JSON.stringify({ error: result.error }),
|
|
373
|
-
tool_call_id: result.toolCall
|
|
393
|
+
...(result.toolCall?.id !== undefined && { tool_call_id: result.toolCall.id }),
|
|
374
394
|
isError: true,
|
|
375
395
|
}
|
|
376
396
|
}
|
|
@@ -382,6 +402,15 @@ export class ToolRouter {
|
|
|
382
402
|
|
|
383
403
|
/**
|
|
384
404
|
* Orchestrates multi-turn model→tools→model loops
|
|
405
|
+
*
|
|
406
|
+
* @deprecated Phase C Week 2 — `AgenticLoop` has zero production callers in
|
|
407
|
+
* primitives.org.ai (audited 2026-05-06; see `bd show aip-ibid`). Only the
|
|
408
|
+
* `ai-primitives` umbrella re-export tests reference it. The production
|
|
409
|
+
* cascade walker (`services-as-software/v3/invoke/cascade-walker.ts:178`)
|
|
410
|
+
* already uses AI SDK 6's `generateText({ tools, maxSteps: 10 })` directly
|
|
411
|
+
* for agentic steps — no consumer code paths through this class. AI SDK 6's
|
|
412
|
+
* `Agent` / `ToolLoopAgent` (`stopWhen: stepCountIs(N)`) are the going-
|
|
413
|
+
* forward primitives. Will be removed in the Phase C semver bump.
|
|
385
414
|
*/
|
|
386
415
|
export class AgenticLoop {
|
|
387
416
|
private options: LoopOptions
|
|
@@ -412,8 +441,14 @@ export class AgenticLoop {
|
|
|
412
441
|
/**
|
|
413
442
|
* Get tools in AI SDK format
|
|
414
443
|
*/
|
|
415
|
-
getToolsForSDK(): Record<
|
|
416
|
-
|
|
444
|
+
getToolsForSDK(): Record<
|
|
445
|
+
string,
|
|
446
|
+
{ description: string; parameters: unknown; execute: (args: unknown) => Promise<unknown> }
|
|
447
|
+
> {
|
|
448
|
+
const tools: Record<
|
|
449
|
+
string,
|
|
450
|
+
{ description: string; parameters: unknown; execute: (args: unknown) => Promise<unknown> }
|
|
451
|
+
> = {}
|
|
417
452
|
for (const tool of this.options.tools) {
|
|
418
453
|
tools[tool.name] = {
|
|
419
454
|
description: tool.description,
|
|
@@ -484,7 +519,7 @@ export class AgenticLoop {
|
|
|
484
519
|
return {
|
|
485
520
|
name: call.name,
|
|
486
521
|
arguments: call.arguments,
|
|
487
|
-
error: lastError,
|
|
522
|
+
...(lastError !== undefined && { error: lastError }),
|
|
488
523
|
retryCount: retryCount > 0 ? retryCount - 1 : 0,
|
|
489
524
|
}
|
|
490
525
|
}
|
|
@@ -517,7 +552,7 @@ export class AgenticLoop {
|
|
|
517
552
|
|
|
518
553
|
for (const chunk of chunks) {
|
|
519
554
|
const chunkResults = await Promise.all(
|
|
520
|
-
chunk.map(call => this.executeToolCall(call, abortSignal))
|
|
555
|
+
chunk.map((call) => this.executeToolCall(call, abortSignal))
|
|
521
556
|
)
|
|
522
557
|
results.push(...chunkResults)
|
|
523
558
|
}
|
|
@@ -576,7 +611,9 @@ export class AgenticLoop {
|
|
|
576
611
|
let steps = 0
|
|
577
612
|
let stopReason: LoopResult['stopReason'] = 'stop'
|
|
578
613
|
let finalText = ''
|
|
579
|
-
let totalUsage = trackUsage
|
|
614
|
+
let totalUsage = trackUsage
|
|
615
|
+
? { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
|
|
616
|
+
: undefined
|
|
580
617
|
|
|
581
618
|
try {
|
|
582
619
|
while (steps < maxSteps) {
|
|
@@ -622,7 +659,7 @@ export class AgenticLoop {
|
|
|
622
659
|
const toolResults = await this.executeToolCalls(response.toolCalls, abortSignal)
|
|
623
660
|
|
|
624
661
|
// Check for errors
|
|
625
|
-
const hasErrors = toolResults.some(r => r.error)
|
|
662
|
+
const hasErrors = toolResults.some((r) => r.error)
|
|
626
663
|
if (hasErrors && !continueOnError) {
|
|
627
664
|
// Still record the results but note the errors
|
|
628
665
|
}
|
|
@@ -656,8 +693,8 @@ export class AgenticLoop {
|
|
|
656
693
|
stepNumber: steps,
|
|
657
694
|
toolCalls: response.toolCalls.map((tc, i) => ({
|
|
658
695
|
...tc,
|
|
659
|
-
result: toolResults[i]?.result,
|
|
660
|
-
error: toolResults[i]?.error,
|
|
696
|
+
...(toolResults[i]?.result !== undefined && { result: toolResults[i]?.result }),
|
|
697
|
+
...(toolResults[i]?.error !== undefined && { error: toolResults[i]?.error }),
|
|
661
698
|
})),
|
|
662
699
|
response,
|
|
663
700
|
messages: [...messages],
|
|
@@ -697,7 +734,7 @@ export class AgenticLoop {
|
|
|
697
734
|
toolCalls: allToolCalls,
|
|
698
735
|
toolResults: allToolResults,
|
|
699
736
|
stopReason,
|
|
700
|
-
usage: totalUsage,
|
|
737
|
+
...(totalUsage !== undefined && { usage: totalUsage }),
|
|
701
738
|
messages,
|
|
702
739
|
}
|
|
703
740
|
}
|
|
@@ -717,7 +754,9 @@ export class AgenticLoop {
|
|
|
717
754
|
let steps = 0
|
|
718
755
|
let stopReason: LoopResult['stopReason'] = 'stop'
|
|
719
756
|
let finalText = ''
|
|
720
|
-
let totalUsage = trackUsage
|
|
757
|
+
let totalUsage = trackUsage
|
|
758
|
+
? { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
|
|
759
|
+
: undefined
|
|
721
760
|
|
|
722
761
|
yield { type: 'start', prompt, timestamp: Date.now() }
|
|
723
762
|
|
|
@@ -766,8 +805,8 @@ export class AgenticLoop {
|
|
|
766
805
|
yield {
|
|
767
806
|
type: 'tool_result',
|
|
768
807
|
toolName: result.name,
|
|
769
|
-
result: result.result,
|
|
770
|
-
error: result.error,
|
|
808
|
+
...(result.result !== undefined && { result: result.result }),
|
|
809
|
+
...(result.error !== undefined && { error: result.error }),
|
|
771
810
|
stepNumber: steps,
|
|
772
811
|
timestamp: Date.now(),
|
|
773
812
|
}
|
|
@@ -821,7 +860,7 @@ export class AgenticLoop {
|
|
|
821
860
|
toolCalls: allToolCalls,
|
|
822
861
|
toolResults: allToolResults,
|
|
823
862
|
stopReason,
|
|
824
|
-
usage: totalUsage,
|
|
863
|
+
...(totalUsage !== undefined && { usage: totalUsage }),
|
|
825
864
|
messages,
|
|
826
865
|
}
|
|
827
866
|
}
|
|
@@ -840,7 +879,14 @@ export type LoopStreamEvent =
|
|
|
840
879
|
| { type: 'step_end'; stepNumber: number; hasToolCalls: boolean; timestamp: number }
|
|
841
880
|
| { type: 'text'; text: string; stepNumber: number; timestamp: number }
|
|
842
881
|
| { type: 'tool_calls'; toolCalls: ToolCall[]; stepNumber: number; timestamp: number }
|
|
843
|
-
| {
|
|
882
|
+
| {
|
|
883
|
+
type: 'tool_result'
|
|
884
|
+
toolName: string
|
|
885
|
+
result?: unknown
|
|
886
|
+
error?: string
|
|
887
|
+
stepNumber: number
|
|
888
|
+
timestamp: number
|
|
889
|
+
}
|
|
844
890
|
| { type: 'max_steps'; steps: number; timestamp: number }
|
|
845
891
|
| { type: 'aborted'; steps: number; timestamp: number }
|
|
846
892
|
| { type: 'error'; error: string; timestamp: number }
|
|
@@ -853,14 +899,12 @@ export type LoopStreamEvent =
|
|
|
853
899
|
/**
|
|
854
900
|
* Create a tool from a simple function
|
|
855
901
|
*/
|
|
856
|
-
export function createTool<TParams extends z.ZodRawShape, TResult>(
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
863
|
-
): Tool<z.ZodObject<TParams>, TResult> {
|
|
902
|
+
export function createTool<TParams extends z.ZodRawShape, TResult>(config: {
|
|
903
|
+
name: string
|
|
904
|
+
description: string
|
|
905
|
+
parameters: TParams
|
|
906
|
+
execute: (params: z.infer<z.ZodObject<TParams>>) => Promise<TResult>
|
|
907
|
+
}): Tool<z.ZodObject<TParams>, TResult> {
|
|
864
908
|
return {
|
|
865
909
|
name: config.name,
|
|
866
910
|
description: config.description,
|
|
@@ -891,9 +935,7 @@ export function wrapTool<T extends Tool>(
|
|
|
891
935
|
...tool,
|
|
892
936
|
execute: async (params: unknown) => {
|
|
893
937
|
try {
|
|
894
|
-
const modifiedParams = middleware.before
|
|
895
|
-
? await middleware.before(params)
|
|
896
|
-
: params
|
|
938
|
+
const modifiedParams = middleware.before ? await middleware.before(params) : params
|
|
897
939
|
const result = await tool.execute(modifiedParams)
|
|
898
940
|
return middleware.after ? await middleware.after(result) : result
|
|
899
941
|
} catch (error) {
|
|
@@ -906,34 +948,151 @@ export function wrapTool<T extends Tool>(
|
|
|
906
948
|
}
|
|
907
949
|
}
|
|
908
950
|
|
|
951
|
+
/**
|
|
952
|
+
* Options for cachedTool
|
|
953
|
+
*/
|
|
954
|
+
export interface CachedToolOptions {
|
|
955
|
+
/** Time-to-live in milliseconds (default: 60000) */
|
|
956
|
+
ttl?: number
|
|
957
|
+
/** Function to generate cache key from params (default: JSON.stringify) */
|
|
958
|
+
keyFn?: (params: unknown) => string
|
|
959
|
+
/** Interval in ms for automatic cleanup of expired entries (default: 0 = disabled) */
|
|
960
|
+
cleanupIntervalMs?: number
|
|
961
|
+
/** Maximum cache size before LRU eviction kicks in (default: 0 = unlimited) */
|
|
962
|
+
maxSize?: number
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Extended tool interface with cache management methods
|
|
967
|
+
*/
|
|
968
|
+
export interface CachedTool extends Tool {
|
|
969
|
+
/** Get the current number of entries in the cache */
|
|
970
|
+
cacheSize(): number
|
|
971
|
+
/** Clear all cache entries */
|
|
972
|
+
clearCache(): void
|
|
973
|
+
/** Stop cleanup timer and clear cache */
|
|
974
|
+
destroy(): void
|
|
975
|
+
}
|
|
976
|
+
|
|
909
977
|
/**
|
|
910
978
|
* Create a tool with caching support
|
|
979
|
+
*
|
|
980
|
+
* Features:
|
|
981
|
+
* - TTL-based expiration
|
|
982
|
+
* - Optional periodic cleanup of expired entries (prevents memory leaks)
|
|
983
|
+
* - Optional max size with LRU eviction
|
|
984
|
+
* - Manual cache control (clear, destroy)
|
|
911
985
|
*/
|
|
912
|
-
export function cachedTool<T extends Tool>(
|
|
913
|
-
|
|
914
|
-
options: {
|
|
915
|
-
ttl?: number
|
|
916
|
-
keyFn?: (params: unknown) => string
|
|
917
|
-
} = {}
|
|
918
|
-
): Tool {
|
|
919
|
-
const cache = new Map<string, { value: unknown; expires: number }>()
|
|
920
|
-
const { ttl = 60000, keyFn = JSON.stringify } = options
|
|
986
|
+
export function cachedTool<T extends Tool>(tool: T, options: CachedToolOptions = {}): CachedTool {
|
|
987
|
+
const { ttl = 60000, keyFn = JSON.stringify, cleanupIntervalMs = 0, maxSize = 0 } = options
|
|
921
988
|
|
|
922
|
-
|
|
989
|
+
interface CacheEntry {
|
|
990
|
+
value: unknown
|
|
991
|
+
expires: number
|
|
992
|
+
lastAccessed: number
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const cache = new Map<string, CacheEntry>()
|
|
996
|
+
let cleanupTimer: ReturnType<typeof setInterval> | null = null
|
|
997
|
+
let destroyed = false
|
|
998
|
+
|
|
999
|
+
// Cleanup function to remove expired entries
|
|
1000
|
+
const cleanupExpired = () => {
|
|
1001
|
+
const now = Date.now()
|
|
1002
|
+
for (const [key, entry] of cache) {
|
|
1003
|
+
if (entry.expires <= now) {
|
|
1004
|
+
cache.delete(key)
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// Start periodic cleanup if configured
|
|
1010
|
+
if (cleanupIntervalMs > 0) {
|
|
1011
|
+
cleanupTimer = setInterval(cleanupExpired, cleanupIntervalMs)
|
|
1012
|
+
// Unref the timer so it doesn't keep the process alive (Node.js)
|
|
1013
|
+
if (typeof cleanupTimer === 'object' && 'unref' in cleanupTimer) {
|
|
1014
|
+
cleanupTimer.unref()
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Evict oldest entries based on lastAccessed (LRU)
|
|
1019
|
+
const evictOldest = () => {
|
|
1020
|
+
if (maxSize <= 0 || cache.size < maxSize) return
|
|
1021
|
+
|
|
1022
|
+
// Find the entry with oldest lastAccessed
|
|
1023
|
+
let oldestKey: string | null = null
|
|
1024
|
+
let oldestTime = Infinity
|
|
1025
|
+
|
|
1026
|
+
for (const [key, entry] of cache) {
|
|
1027
|
+
if (entry.lastAccessed < oldestTime) {
|
|
1028
|
+
oldestTime = entry.lastAccessed
|
|
1029
|
+
oldestKey = key
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (oldestKey) {
|
|
1034
|
+
cache.delete(oldestKey)
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const cachedToolInstance: CachedTool = {
|
|
923
1039
|
...tool,
|
|
924
1040
|
execute: async (params: unknown) => {
|
|
1041
|
+
if (destroyed) {
|
|
1042
|
+
// If destroyed, just execute without caching
|
|
1043
|
+
return tool.execute(params)
|
|
1044
|
+
}
|
|
1045
|
+
|
|
925
1046
|
const key = keyFn(params)
|
|
926
1047
|
const cached = cache.get(key)
|
|
1048
|
+
const now = Date.now()
|
|
927
1049
|
|
|
928
|
-
if (cached && cached.expires >
|
|
1050
|
+
if (cached && cached.expires > now) {
|
|
1051
|
+
// Cache hit - update last accessed time for LRU
|
|
1052
|
+
cached.lastAccessed = now
|
|
929
1053
|
return cached.value
|
|
930
1054
|
}
|
|
931
1055
|
|
|
1056
|
+
// Cache miss or expired - remove expired entry if present
|
|
1057
|
+
if (cached) {
|
|
1058
|
+
cache.delete(key)
|
|
1059
|
+
}
|
|
1060
|
+
|
|
932
1061
|
const result = await tool.execute(params)
|
|
933
|
-
|
|
1062
|
+
|
|
1063
|
+
// Evict oldest if we're at max size
|
|
1064
|
+
if (maxSize > 0 && cache.size >= maxSize) {
|
|
1065
|
+
evictOldest()
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
cache.set(key, {
|
|
1069
|
+
value: result,
|
|
1070
|
+
expires: now + ttl,
|
|
1071
|
+
lastAccessed: now,
|
|
1072
|
+
})
|
|
1073
|
+
|
|
934
1074
|
return result
|
|
935
1075
|
},
|
|
1076
|
+
|
|
1077
|
+
cacheSize(): number {
|
|
1078
|
+
return cache.size
|
|
1079
|
+
},
|
|
1080
|
+
|
|
1081
|
+
clearCache(): void {
|
|
1082
|
+
cache.clear()
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
destroy(): void {
|
|
1086
|
+
destroyed = true
|
|
1087
|
+
if (cleanupTimer !== null) {
|
|
1088
|
+
clearInterval(cleanupTimer)
|
|
1089
|
+
cleanupTimer = null
|
|
1090
|
+
}
|
|
1091
|
+
cache.clear()
|
|
1092
|
+
},
|
|
936
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
return cachedToolInstance
|
|
937
1096
|
}
|
|
938
1097
|
|
|
939
1098
|
/**
|
|
@@ -971,16 +1130,16 @@ export function rateLimitedTool<T extends Tool>(
|
|
|
971
1130
|
/**
|
|
972
1131
|
* Create a tool that times out after a specified duration
|
|
973
1132
|
*/
|
|
974
|
-
export function timeoutTool<T extends Tool>(
|
|
975
|
-
tool: T,
|
|
976
|
-
timeoutMs: number
|
|
977
|
-
): Tool {
|
|
1133
|
+
export function timeoutTool<T extends Tool>(tool: T, timeoutMs: number): Tool {
|
|
978
1134
|
return {
|
|
979
1135
|
...tool,
|
|
980
1136
|
execute: async (params: unknown) => {
|
|
981
1137
|
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
982
1138
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
983
|
-
timeoutId = setTimeout(
|
|
1139
|
+
timeoutId = setTimeout(
|
|
1140
|
+
() => reject(new Error(`Tool '${tool.name}' timed out after ${timeoutMs}ms`)),
|
|
1141
|
+
timeoutMs
|
|
1142
|
+
)
|
|
984
1143
|
})
|
|
985
1144
|
try {
|
|
986
1145
|
return await Promise.race([tool.execute(params), timeoutPromise])
|
|
@@ -995,6 +1154,12 @@ export function timeoutTool<T extends Tool>(
|
|
|
995
1154
|
|
|
996
1155
|
/**
|
|
997
1156
|
* Create an agentic loop with sensible defaults
|
|
1157
|
+
*
|
|
1158
|
+
* @deprecated Phase C Week 2 — `createAgenticLoop` has zero production
|
|
1159
|
+
* callers in primitives.org.ai (only `ai-primitives` umbrella re-export
|
|
1160
|
+
* tests). Use AI SDK 6's `Agent` / `ToolLoopAgent` with
|
|
1161
|
+
* `stopWhen: stepCountIs(N)` instead. Will be removed alongside
|
|
1162
|
+
* `AgenticLoop` in the Phase C semver bump. See `bd show aip-ibid`.
|
|
998
1163
|
*/
|
|
999
1164
|
export function createAgenticLoop(options: Partial<LoopOptions> & { tools: Tool[] }): AgenticLoop {
|
|
1000
1165
|
return new AgenticLoop({
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type Guards for AI Functions
|
|
3
|
+
*
|
|
4
|
+
* Shared type guard utilities used across AI packages.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ZodTypeAny } from 'zod'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if value is a Zod schema
|
|
13
|
+
*
|
|
14
|
+
* Uses duck-typing to detect Zod schemas by checking for the
|
|
15
|
+
* `_def` property (internal Zod schema definition) and `parse` method.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { isZodSchema } from 'ai-functions'
|
|
20
|
+
* import { z } from 'zod'
|
|
21
|
+
*
|
|
22
|
+
* const schema = z.object({ name: z.string() })
|
|
23
|
+
* if (isZodSchema(schema)) {
|
|
24
|
+
* // TypeScript knows schema is ZodTypeAny
|
|
25
|
+
* schema.parse({ name: 'test' })
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function isZodSchema(value: unknown): value is ZodTypeAny {
|
|
30
|
+
return value !== null && typeof value === 'object' && '_def' in value && 'parse' in value
|
|
31
|
+
}
|