ai-functions 2.1.3 → 2.4.0

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