ai-functions 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -4
- package/CHANGELOG.md +68 -1
- package/README.md +397 -157
- package/dist/ai-promise.d.ts +50 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +410 -51
- 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 +54 -837
- 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 +272 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +513 -0
- package/dist/budget.js.map +1 -0
- package/dist/cache.d.ts +295 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +433 -0
- package/dist/cache.js.map +1 -0
- package/dist/context.d.ts +42 -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 +10 -1
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +116 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +546 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -22
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +35 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +89 -42
- 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 +368 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +646 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -10
- 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 +453 -0
- package/dist/tool-orchestration.d.ts.map +1 -0
- package/dist/tool-orchestration.js +763 -0
- package/dist/tool-orchestration.js.map +1 -0
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +135 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +10 -6
- package/src/ai-promise.ts +528 -99
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -1153
- 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 +740 -0
- package/src/cache.ts +681 -0
- package/src/context.ts +122 -76
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +63 -38
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +671 -0
- package/src/generate.ts +33 -33
- package/src/index.ts +325 -49
- 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 +902 -0
- package/src/schema.ts +8 -17
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +1173 -0
- package/src/type-guards.ts +31 -0
- package/src/types.ts +164 -25
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/backward-compat.test.ts +147 -0
- package/test/batch-autosubmit-errors.test.ts +610 -0
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/budget-tracking.test.ts +800 -0
- package/test/cache.test.ts +712 -0
- package/test/context-isolation.test.ts +687 -0
- 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/evals/deterministic.eval.test.ts +376 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/json-parse-error-handling.test.ts +463 -0
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/retry.test.ts +1016 -0
- package/test/schema.test.ts +55 -19
- package/test/streaming.test.ts +316 -0
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +1040 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- 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
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Orchestration Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates building agentic loops with tool calling using ai-functions.
|
|
5
|
+
* It shows how to:
|
|
6
|
+
* - Define and register tools
|
|
7
|
+
* - Create an agentic loop
|
|
8
|
+
* - Handle tool results and multi-turn conversations
|
|
9
|
+
* - Implement tool composition patterns
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```bash
|
|
13
|
+
* ANTHROPIC_API_KEY=sk-... npx tsx examples/10-tool-orchestration.ts
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
configure,
|
|
19
|
+
AgenticLoop,
|
|
20
|
+
ToolRouter,
|
|
21
|
+
ToolValidator,
|
|
22
|
+
createTool,
|
|
23
|
+
createToolset,
|
|
24
|
+
wrapTool,
|
|
25
|
+
cachedTool,
|
|
26
|
+
rateLimitedTool,
|
|
27
|
+
timeoutTool,
|
|
28
|
+
createAgenticLoop,
|
|
29
|
+
type Tool,
|
|
30
|
+
type ToolCall,
|
|
31
|
+
type LoopResult,
|
|
32
|
+
} from '../src/index.js'
|
|
33
|
+
import { z } from 'zod'
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Define Tools
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Calculator tool - performs basic math
|
|
41
|
+
*/
|
|
42
|
+
const calculatorTool = createTool({
|
|
43
|
+
name: 'calculator',
|
|
44
|
+
description: 'Performs basic math operations (add, subtract, multiply, divide)',
|
|
45
|
+
parameters: {
|
|
46
|
+
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
|
|
47
|
+
a: z.number().describe('First operand'),
|
|
48
|
+
b: z.number().describe('Second operand'),
|
|
49
|
+
},
|
|
50
|
+
execute: async ({ operation, a, b }) => {
|
|
51
|
+
console.log(` [Calculator] ${a} ${operation} ${b}`)
|
|
52
|
+
switch (operation) {
|
|
53
|
+
case 'add':
|
|
54
|
+
return a + b
|
|
55
|
+
case 'subtract':
|
|
56
|
+
return a - b
|
|
57
|
+
case 'multiply':
|
|
58
|
+
return a * b
|
|
59
|
+
case 'divide':
|
|
60
|
+
return b !== 0 ? a / b : 'Error: Division by zero'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Weather tool - simulates weather lookup
|
|
67
|
+
*/
|
|
68
|
+
const weatherTool = createTool({
|
|
69
|
+
name: 'get_weather',
|
|
70
|
+
description: 'Gets the current weather for a location',
|
|
71
|
+
parameters: {
|
|
72
|
+
location: z.string().describe('City name or location'),
|
|
73
|
+
units: z.enum(['celsius', 'fahrenheit']).optional().default('celsius'),
|
|
74
|
+
},
|
|
75
|
+
execute: async ({ location, units }) => {
|
|
76
|
+
console.log(` [Weather] Looking up: ${location}`)
|
|
77
|
+
// Simulated weather data
|
|
78
|
+
const temp = Math.floor(Math.random() * 30) + 5
|
|
79
|
+
const conditions = ['sunny', 'cloudy', 'rainy', 'partly cloudy'][Math.floor(Math.random() * 4)]
|
|
80
|
+
const displayTemp = units === 'fahrenheit' ? Math.round(temp * 1.8 + 32) : temp
|
|
81
|
+
const unit = units === 'fahrenheit' ? 'F' : 'C'
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
location,
|
|
85
|
+
temperature: `${displayTemp}${unit}`,
|
|
86
|
+
conditions,
|
|
87
|
+
humidity: `${Math.floor(Math.random() * 50) + 30}%`,
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Search tool - simulates web search
|
|
94
|
+
*/
|
|
95
|
+
const searchTool = createTool({
|
|
96
|
+
name: 'search',
|
|
97
|
+
description: 'Searches the web for information',
|
|
98
|
+
parameters: {
|
|
99
|
+
query: z.string().describe('Search query'),
|
|
100
|
+
maxResults: z.number().optional().default(3),
|
|
101
|
+
},
|
|
102
|
+
execute: async ({ query, maxResults }) => {
|
|
103
|
+
console.log(` [Search] Query: "${query}"`)
|
|
104
|
+
// Simulated search results
|
|
105
|
+
return {
|
|
106
|
+
query,
|
|
107
|
+
results: [
|
|
108
|
+
{
|
|
109
|
+
title: `Result 1 for "${query}"`,
|
|
110
|
+
snippet: 'Relevant information found...',
|
|
111
|
+
url: 'https://example.com/1',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
title: `Result 2 for "${query}"`,
|
|
115
|
+
snippet: 'More details about the topic...',
|
|
116
|
+
url: 'https://example.com/2',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
title: `Result 3 for "${query}"`,
|
|
120
|
+
snippet: 'Additional context and data...',
|
|
121
|
+
url: 'https://example.com/3',
|
|
122
|
+
},
|
|
123
|
+
].slice(0, maxResults),
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Database tool - simulates data lookup
|
|
130
|
+
*/
|
|
131
|
+
const databaseTool = createTool({
|
|
132
|
+
name: 'query_database',
|
|
133
|
+
description: 'Queries a database for user or product information',
|
|
134
|
+
parameters: {
|
|
135
|
+
table: z.enum(['users', 'products', 'orders']),
|
|
136
|
+
filter: z.object({
|
|
137
|
+
field: z.string(),
|
|
138
|
+
value: z.string(),
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
execute: async ({ table, filter }) => {
|
|
142
|
+
console.log(` [Database] Querying ${table} where ${filter.field} = ${filter.value}`)
|
|
143
|
+
|
|
144
|
+
// Simulated database results
|
|
145
|
+
const mockData: Record<string, unknown[]> = {
|
|
146
|
+
users: [
|
|
147
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com' },
|
|
148
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com' },
|
|
149
|
+
],
|
|
150
|
+
products: [
|
|
151
|
+
{ id: 101, name: 'Widget Pro', price: 99.99 },
|
|
152
|
+
{ id: 102, name: 'Widget Basic', price: 49.99 },
|
|
153
|
+
],
|
|
154
|
+
orders: [
|
|
155
|
+
{ id: 1001, userId: 1, productId: 101, status: 'shipped' },
|
|
156
|
+
{ id: 1002, userId: 2, productId: 102, status: 'pending' },
|
|
157
|
+
],
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
table,
|
|
162
|
+
results: mockData[table] || [],
|
|
163
|
+
count: mockData[table]?.length || 0,
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Tool Composition Patterns
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Wrap a tool with logging middleware
|
|
174
|
+
*/
|
|
175
|
+
function withLogging<T extends Tool>(tool: T): Tool {
|
|
176
|
+
return wrapTool(tool, {
|
|
177
|
+
before: (params) => {
|
|
178
|
+
console.log(` [LOG] Calling ${tool.name} with:`, JSON.stringify(params).substring(0, 100))
|
|
179
|
+
return params
|
|
180
|
+
},
|
|
181
|
+
after: (result) => {
|
|
182
|
+
console.log(` [LOG] ${tool.name} returned:`, JSON.stringify(result).substring(0, 100))
|
|
183
|
+
return result
|
|
184
|
+
},
|
|
185
|
+
onError: (error) => {
|
|
186
|
+
console.log(` [LOG] ${tool.name} error:`, error.message)
|
|
187
|
+
throw error
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Create a toolset with common wrappers
|
|
194
|
+
*/
|
|
195
|
+
function createProductionToolset(...tools: Tool[]): Tool[] {
|
|
196
|
+
return tools.map((tool) => {
|
|
197
|
+
// Apply wrappers in order
|
|
198
|
+
let wrapped: Tool = tool
|
|
199
|
+
|
|
200
|
+
// Add rate limiting (5 calls per 10 seconds)
|
|
201
|
+
wrapped = rateLimitedTool(wrapped, { maxCalls: 5, windowMs: 10000 })
|
|
202
|
+
|
|
203
|
+
// Add timeout (30 seconds)
|
|
204
|
+
wrapped = timeoutTool(wrapped, 30000)
|
|
205
|
+
|
|
206
|
+
return wrapped
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Agentic Loop Examples
|
|
212
|
+
// ============================================================================
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Simple calculation agent
|
|
216
|
+
*/
|
|
217
|
+
async function runCalculationAgent(): Promise<void> {
|
|
218
|
+
console.log('\n--- Calculation Agent ---\n')
|
|
219
|
+
|
|
220
|
+
const loop = new AgenticLoop({
|
|
221
|
+
tools: [calculatorTool],
|
|
222
|
+
maxSteps: 5,
|
|
223
|
+
onStep: (step) => {
|
|
224
|
+
console.log(` Step ${step.stepNumber}: ${step.toolCalls.length} tool calls`)
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Mock model that performs a calculation
|
|
229
|
+
const mockModel = {
|
|
230
|
+
generate: async ({ messages }: { messages: Array<{ role: string; content: string }> }) => {
|
|
231
|
+
const lastMessage = messages[messages.length - 1]?.content || ''
|
|
232
|
+
|
|
233
|
+
// First call: request calculation
|
|
234
|
+
if (!lastMessage.includes('100')) {
|
|
235
|
+
return {
|
|
236
|
+
toolCalls: [{ name: 'calculator', arguments: { operation: 'multiply', a: 25, b: 4 } }],
|
|
237
|
+
finishReason: 'tool_call' as const,
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// After getting result
|
|
242
|
+
return {
|
|
243
|
+
text: 'The result of 25 multiplied by 4 is 100.',
|
|
244
|
+
finishReason: 'stop' as const,
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const result = await loop.run({
|
|
250
|
+
model: mockModel,
|
|
251
|
+
prompt: 'What is 25 multiplied by 4?',
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
console.log(`\n Final answer: ${result.text}`)
|
|
255
|
+
console.log(` Total steps: ${result.steps}`)
|
|
256
|
+
console.log(` Tool calls: ${result.toolCalls.length}`)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Research agent with multiple tools
|
|
261
|
+
*/
|
|
262
|
+
async function runResearchAgent(): Promise<void> {
|
|
263
|
+
console.log('\n--- Research Agent ---\n')
|
|
264
|
+
|
|
265
|
+
const toolset = createToolset(searchTool, weatherTool, calculatorTool)
|
|
266
|
+
|
|
267
|
+
const loop = createAgenticLoop({
|
|
268
|
+
tools: toolset,
|
|
269
|
+
maxSteps: 10,
|
|
270
|
+
parallelExecution: true,
|
|
271
|
+
trackUsage: true,
|
|
272
|
+
onStep: (step) => {
|
|
273
|
+
console.log(` Step ${step.stepNumber}:`)
|
|
274
|
+
for (const call of step.toolCalls) {
|
|
275
|
+
console.log(
|
|
276
|
+
` - ${call.name}${
|
|
277
|
+
call.result !== undefined
|
|
278
|
+
? ` -> ${JSON.stringify(call.result).substring(0, 50)}...`
|
|
279
|
+
: ''
|
|
280
|
+
}`
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
// Mock model that does research
|
|
287
|
+
let stepCount = 0
|
|
288
|
+
const mockModel = {
|
|
289
|
+
generate: async () => {
|
|
290
|
+
stepCount++
|
|
291
|
+
|
|
292
|
+
if (stepCount === 1) {
|
|
293
|
+
// First: search and get weather in parallel
|
|
294
|
+
return {
|
|
295
|
+
toolCalls: [
|
|
296
|
+
{ name: 'search', arguments: { query: 'best coffee shops' } },
|
|
297
|
+
{ name: 'get_weather', arguments: { location: 'San Francisco' } },
|
|
298
|
+
],
|
|
299
|
+
finishReason: 'tool_call' as const,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (stepCount === 2) {
|
|
304
|
+
// Second: do a calculation
|
|
305
|
+
return {
|
|
306
|
+
toolCalls: [{ name: 'calculator', arguments: { operation: 'add', a: 3, b: 2 } }],
|
|
307
|
+
finishReason: 'tool_call' as const,
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Final response
|
|
312
|
+
return {
|
|
313
|
+
text: 'Based on my research, I found several coffee shops. The weather is pleasant. And 3 + 2 = 5.',
|
|
314
|
+
finishReason: 'stop' as const,
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const result = await loop.run({
|
|
320
|
+
model: mockModel,
|
|
321
|
+
prompt: 'Find coffee shops, check the weather in SF, and calculate 3+2',
|
|
322
|
+
system: 'You are a helpful research assistant.',
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
console.log(`\n Final answer: ${result.text}`)
|
|
326
|
+
console.log(` Total steps: ${result.steps}`)
|
|
327
|
+
console.log(` Tool calls made: ${result.toolCalls.length}`)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Data lookup agent
|
|
332
|
+
*/
|
|
333
|
+
async function runDatabaseAgent(): Promise<void> {
|
|
334
|
+
console.log('\n--- Database Agent ---\n')
|
|
335
|
+
|
|
336
|
+
// Use cached version of database tool
|
|
337
|
+
const cachedDb = cachedTool(databaseTool, { ttl: 60000, maxSize: 100 })
|
|
338
|
+
|
|
339
|
+
const loop = new AgenticLoop({
|
|
340
|
+
tools: [cachedDb],
|
|
341
|
+
maxSteps: 5,
|
|
342
|
+
retryFailedTools: true,
|
|
343
|
+
maxToolRetries: 2,
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Mock model
|
|
347
|
+
let called = false
|
|
348
|
+
const mockModel = {
|
|
349
|
+
generate: async () => {
|
|
350
|
+
if (!called) {
|
|
351
|
+
called = true
|
|
352
|
+
return {
|
|
353
|
+
toolCalls: [
|
|
354
|
+
{
|
|
355
|
+
name: 'query_database',
|
|
356
|
+
arguments: { table: 'users', filter: { field: 'name', value: 'Alice' } },
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
finishReason: 'tool_call' as const,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
text: 'Found user Alice in the database.',
|
|
365
|
+
finishReason: 'stop' as const,
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const result = await loop.run({
|
|
371
|
+
model: mockModel,
|
|
372
|
+
prompt: 'Find information about user Alice',
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
console.log(`\n Result: ${result.text}`)
|
|
376
|
+
|
|
377
|
+
// Clean up cached tool
|
|
378
|
+
;(cachedDb as any).destroy?.()
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ============================================================================
|
|
382
|
+
// Tool Validation Demo
|
|
383
|
+
// ============================================================================
|
|
384
|
+
|
|
385
|
+
async function demonstrateValidation(): Promise<void> {
|
|
386
|
+
console.log('\n--- Tool Validation ---\n')
|
|
387
|
+
|
|
388
|
+
const validator = new ToolValidator()
|
|
389
|
+
validator.register(calculatorTool)
|
|
390
|
+
validator.register(weatherTool)
|
|
391
|
+
|
|
392
|
+
const testCalls: ToolCall[] = [
|
|
393
|
+
{ name: 'calculator', arguments: { operation: 'add', a: 5, b: 3 } },
|
|
394
|
+
{ name: 'calculator', arguments: { operation: 'invalid', a: 5, b: 3 } },
|
|
395
|
+
{ name: 'get_weather', arguments: { location: 'NYC' } },
|
|
396
|
+
{ name: 'unknown_tool', arguments: {} },
|
|
397
|
+
]
|
|
398
|
+
|
|
399
|
+
for (const call of testCalls) {
|
|
400
|
+
const result = validator.validate(call.name, call.arguments)
|
|
401
|
+
console.log(` ${call.name}(${JSON.stringify(call.arguments)})`)
|
|
402
|
+
console.log(
|
|
403
|
+
` Valid: ${result.valid}${result.errors ? ` | Errors: ${result.errors.join(', ')}` : ''}\n`
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ============================================================================
|
|
409
|
+
// Tool Router Demo
|
|
410
|
+
// ============================================================================
|
|
411
|
+
|
|
412
|
+
async function demonstrateRouter(): Promise<void> {
|
|
413
|
+
console.log('\n--- Tool Router ---\n')
|
|
414
|
+
|
|
415
|
+
const router = new ToolRouter()
|
|
416
|
+
router.register(calculatorTool)
|
|
417
|
+
router.register(weatherTool)
|
|
418
|
+
router.register(searchTool)
|
|
419
|
+
|
|
420
|
+
// Route single call
|
|
421
|
+
const result = await router.route({
|
|
422
|
+
name: 'calculator',
|
|
423
|
+
arguments: { operation: 'multiply', a: 7, b: 8 },
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
console.log(` Single route result:`, result.success ? result.result : result.error)
|
|
427
|
+
|
|
428
|
+
// Route multiple calls in parallel
|
|
429
|
+
const results = await router.routeAllParallel([
|
|
430
|
+
{ name: 'get_weather', arguments: { location: 'Tokyo' } },
|
|
431
|
+
{ name: 'search', arguments: { query: 'TypeScript tutorials' } },
|
|
432
|
+
])
|
|
433
|
+
|
|
434
|
+
console.log(`\n Parallel route results:`)
|
|
435
|
+
for (const r of results) {
|
|
436
|
+
const formatted = router.formatResult(r)
|
|
437
|
+
console.log(` ${r.toolCall?.name}: ${formatted.content.substring(0, 60)}...`)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ============================================================================
|
|
442
|
+
// Streaming Agent Demo
|
|
443
|
+
// ============================================================================
|
|
444
|
+
|
|
445
|
+
async function demonstrateStreaming(): Promise<void> {
|
|
446
|
+
console.log('\n--- Streaming Agent Loop ---\n')
|
|
447
|
+
|
|
448
|
+
const loop = new AgenticLoop({
|
|
449
|
+
tools: [calculatorTool],
|
|
450
|
+
maxSteps: 3,
|
|
451
|
+
trackUsage: true,
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
// Mock streaming model
|
|
455
|
+
let step = 0
|
|
456
|
+
const mockModel = {
|
|
457
|
+
generate: async () => {
|
|
458
|
+
step++
|
|
459
|
+
if (step === 1) {
|
|
460
|
+
return {
|
|
461
|
+
toolCalls: [{ name: 'calculator', arguments: { operation: 'add', a: 10, b: 20 } }],
|
|
462
|
+
finishReason: 'tool_call' as const,
|
|
463
|
+
usage: { promptTokens: 50, completionTokens: 20, totalTokens: 70 },
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
text: '10 + 20 = 30',
|
|
468
|
+
finishReason: 'stop' as const,
|
|
469
|
+
usage: { promptTokens: 80, completionTokens: 10, totalTokens: 90 },
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.log(' Streaming events:')
|
|
475
|
+
|
|
476
|
+
for await (const event of loop.stream({
|
|
477
|
+
model: mockModel,
|
|
478
|
+
prompt: 'Calculate 10 + 20',
|
|
479
|
+
})) {
|
|
480
|
+
switch (event.type) {
|
|
481
|
+
case 'start':
|
|
482
|
+
console.log(` [start] Prompt: "${event.prompt.substring(0, 30)}..."`)
|
|
483
|
+
break
|
|
484
|
+
case 'step_start':
|
|
485
|
+
console.log(` [step_start] Step ${event.stepNumber}`)
|
|
486
|
+
break
|
|
487
|
+
case 'tool_calls':
|
|
488
|
+
console.log(` [tool_calls] ${event.toolCalls.map((t) => t.name).join(', ')}`)
|
|
489
|
+
break
|
|
490
|
+
case 'tool_result':
|
|
491
|
+
console.log(` [tool_result] ${event.toolName}: ${JSON.stringify(event.result)}`)
|
|
492
|
+
break
|
|
493
|
+
case 'text':
|
|
494
|
+
console.log(` [text] "${event.text}"`)
|
|
495
|
+
break
|
|
496
|
+
case 'end':
|
|
497
|
+
console.log(` [end] Steps: ${event.steps}, Reason: ${event.stopReason}`)
|
|
498
|
+
break
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ============================================================================
|
|
504
|
+
// Main Example
|
|
505
|
+
// ============================================================================
|
|
506
|
+
|
|
507
|
+
async function main() {
|
|
508
|
+
console.log('\n=== Tool Orchestration Example ===\n')
|
|
509
|
+
|
|
510
|
+
// Configure (not used for mocked examples, but shown for reference)
|
|
511
|
+
configure({
|
|
512
|
+
model: 'sonnet',
|
|
513
|
+
provider: 'anthropic',
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
// Run demos
|
|
517
|
+
await runCalculationAgent()
|
|
518
|
+
await runResearchAgent()
|
|
519
|
+
await runDatabaseAgent()
|
|
520
|
+
await demonstrateValidation()
|
|
521
|
+
await demonstrateRouter()
|
|
522
|
+
await demonstrateStreaming()
|
|
523
|
+
|
|
524
|
+
// Summary
|
|
525
|
+
console.log('\n=== Tool Orchestration Summary ===')
|
|
526
|
+
console.log(`
|
|
527
|
+
Key concepts demonstrated:
|
|
528
|
+
|
|
529
|
+
1. Tool Definition
|
|
530
|
+
- Use createTool() with Zod schemas
|
|
531
|
+
- Provide clear descriptions for the model
|
|
532
|
+
- Return structured data from execute()
|
|
533
|
+
|
|
534
|
+
2. AgenticLoop
|
|
535
|
+
- Multi-turn model -> tools -> model loop
|
|
536
|
+
- Configurable max steps and parallel execution
|
|
537
|
+
- Built-in retry and error handling
|
|
538
|
+
|
|
539
|
+
3. Tool Composition
|
|
540
|
+
- wrapTool() for middleware (logging, auth)
|
|
541
|
+
- cachedTool() for result caching
|
|
542
|
+
- rateLimitedTool() for rate limiting
|
|
543
|
+
- timeoutTool() for execution limits
|
|
544
|
+
|
|
545
|
+
4. Validation & Routing
|
|
546
|
+
- ToolValidator for pre-execution checks
|
|
547
|
+
- ToolRouter for dispatching calls
|
|
548
|
+
- Support for parallel routing
|
|
549
|
+
|
|
550
|
+
5. Streaming
|
|
551
|
+
- AsyncGenerator for step-by-step events
|
|
552
|
+
- Real-time progress monitoring
|
|
553
|
+
- Usage tracking
|
|
554
|
+
`)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
main()
|
|
558
|
+
.then(() => {
|
|
559
|
+
console.log('\n=== Example Complete ===\n')
|
|
560
|
+
process.exit(0)
|
|
561
|
+
})
|
|
562
|
+
.catch((error) => {
|
|
563
|
+
console.error('\nError:', error.message)
|
|
564
|
+
process.exit(1)
|
|
565
|
+
})
|