ai-functions 2.1.3 → 2.3.0

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