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.
Files changed (286) hide show
  1. package/.turbo/turbo-build.log +1 -4
  2. package/CHANGELOG.md +68 -1
  3. package/README.md +397 -157
  4. package/dist/ai-promise.d.ts +50 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +410 -51
  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 +54 -837
  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 +272 -0
  56. package/dist/budget.d.ts.map +1 -0
  57. package/dist/budget.js +513 -0
  58. package/dist/budget.js.map +1 -0
  59. package/dist/cache.d.ts +295 -0
  60. package/dist/cache.d.ts.map +1 -0
  61. package/dist/cache.js +433 -0
  62. package/dist/cache.js.map +1 -0
  63. package/dist/context.d.ts +42 -8
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +64 -62
  66. package/dist/context.js.map +1 -1
  67. package/dist/digital-objects-registry.d.ts +229 -0
  68. package/dist/digital-objects-registry.d.ts.map +1 -0
  69. package/dist/digital-objects-registry.js +617 -0
  70. package/dist/digital-objects-registry.js.map +1 -0
  71. package/dist/embeddings.d.ts +2 -2
  72. package/dist/embeddings.d.ts.map +1 -1
  73. package/dist/errors.d.ts +22 -0
  74. package/dist/errors.d.ts.map +1 -0
  75. package/dist/errors.js +35 -0
  76. package/dist/errors.js.map +1 -0
  77. package/dist/eval/runner.d.ts +10 -1
  78. package/dist/eval/runner.d.ts.map +1 -1
  79. package/dist/eval/runner.js +41 -35
  80. package/dist/eval/runner.js.map +1 -1
  81. package/dist/eval-log/in-memory.d.ts +34 -0
  82. package/dist/eval-log/in-memory.d.ts.map +1 -0
  83. package/dist/eval-log/in-memory.js +84 -0
  84. package/dist/eval-log/in-memory.js.map +1 -0
  85. package/dist/eval-log/index.d.ts +29 -0
  86. package/dist/eval-log/index.d.ts.map +1 -0
  87. package/dist/eval-log/index.js +39 -0
  88. package/dist/eval-log/index.js.map +1 -0
  89. package/dist/eval-log/types.d.ts +101 -0
  90. package/dist/eval-log/types.d.ts.map +1 -0
  91. package/dist/eval-log/types.js +16 -0
  92. package/dist/eval-log/types.js.map +1 -0
  93. package/dist/function-registry.d.ts +116 -0
  94. package/dist/function-registry.d.ts.map +1 -0
  95. package/dist/function-registry.js +546 -0
  96. package/dist/function-registry.js.map +1 -0
  97. package/dist/generate.d.ts +9 -3
  98. package/dist/generate.d.ts.map +1 -1
  99. package/dist/generate.js +18 -22
  100. package/dist/generate.js.map +1 -1
  101. package/dist/index.d.ts +35 -20
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +89 -42
  104. package/dist/index.js.map +1 -1
  105. package/dist/logger.d.ts +118 -0
  106. package/dist/logger.d.ts.map +1 -0
  107. package/dist/logger.js +187 -0
  108. package/dist/logger.js.map +1 -0
  109. package/dist/middleware/budget.d.ts +84 -0
  110. package/dist/middleware/budget.d.ts.map +1 -0
  111. package/dist/middleware/budget.js +110 -0
  112. package/dist/middleware/budget.js.map +1 -0
  113. package/dist/middleware/cache.d.ts +103 -0
  114. package/dist/middleware/cache.d.ts.map +1 -0
  115. package/dist/middleware/cache.js +228 -0
  116. package/dist/middleware/cache.js.map +1 -0
  117. package/dist/middleware/embed-cache.d.ts +99 -0
  118. package/dist/middleware/embed-cache.d.ts.map +1 -0
  119. package/dist/middleware/embed-cache.js +128 -0
  120. package/dist/middleware/embed-cache.js.map +1 -0
  121. package/dist/middleware/index.d.ts +11 -0
  122. package/dist/middleware/index.d.ts.map +1 -0
  123. package/dist/middleware/index.js +11 -0
  124. package/dist/middleware/index.js.map +1 -0
  125. package/dist/middleware/trace.d.ts +103 -0
  126. package/dist/middleware/trace.d.ts.map +1 -0
  127. package/dist/middleware/trace.js +176 -0
  128. package/dist/middleware/trace.js.map +1 -0
  129. package/dist/primitives.d.ts +120 -1
  130. package/dist/primitives.d.ts.map +1 -1
  131. package/dist/primitives.js +398 -26
  132. package/dist/primitives.js.map +1 -1
  133. package/dist/retry.d.ts +368 -0
  134. package/dist/retry.d.ts.map +1 -0
  135. package/dist/retry.js +646 -0
  136. package/dist/retry.js.map +1 -0
  137. package/dist/schema.d.ts.map +1 -1
  138. package/dist/schema.js +2 -10
  139. package/dist/schema.js.map +1 -1
  140. package/dist/telemetry.d.ts +128 -0
  141. package/dist/telemetry.d.ts.map +1 -0
  142. package/dist/telemetry.js +285 -0
  143. package/dist/telemetry.js.map +1 -0
  144. package/dist/template.d.ts.map +1 -1
  145. package/dist/template.js +6 -1
  146. package/dist/template.js.map +1 -1
  147. package/dist/tool-orchestration.d.ts +453 -0
  148. package/dist/tool-orchestration.d.ts.map +1 -0
  149. package/dist/tool-orchestration.js +763 -0
  150. package/dist/tool-orchestration.js.map +1 -0
  151. package/dist/type-guards.d.ts +28 -0
  152. package/dist/type-guards.d.ts.map +1 -0
  153. package/dist/type-guards.js +29 -0
  154. package/dist/type-guards.js.map +1 -0
  155. package/dist/types.d.ts +135 -17
  156. package/dist/types.d.ts.map +1 -1
  157. package/dist/types.js +36 -1
  158. package/dist/types.js.map +1 -1
  159. package/dist/wrap-for-v3.d.ts +80 -0
  160. package/dist/wrap-for-v3.d.ts.map +1 -0
  161. package/dist/wrap-for-v3.js +89 -0
  162. package/dist/wrap-for-v3.js.map +1 -0
  163. package/examples/00-quickstart.ts +232 -0
  164. package/examples/01-rag-chatbot.ts +212 -0
  165. package/examples/02-multi-agent-research.ts +290 -0
  166. package/examples/03-email-classification.ts +379 -0
  167. package/examples/04-content-moderation.ts +400 -0
  168. package/examples/05-document-extraction.ts +455 -0
  169. package/examples/06-streaming-chat-nextjs.ts +437 -0
  170. package/examples/07-cloudflare-worker.ts +483 -0
  171. package/examples/08-batch-processing.ts +491 -0
  172. package/examples/09-budget-constrained.ts +527 -0
  173. package/examples/10-tool-orchestration.ts +565 -0
  174. package/examples/11-retry-resilience.ts +403 -0
  175. package/examples/12-caching-strategies.ts +422 -0
  176. package/examples/README.md +145 -0
  177. package/package.json +10 -6
  178. package/src/ai-promise.ts +528 -99
  179. package/src/ai-schemas.ts +122 -0
  180. package/src/ai.ts +69 -1153
  181. package/src/batch/anthropic.ts +96 -161
  182. package/src/batch/bedrock.ts +203 -454
  183. package/src/batch/cloudflare.ts +99 -282
  184. package/src/batch/google.ts +91 -297
  185. package/src/batch/index.ts +4 -1
  186. package/src/batch/memory.ts +15 -10
  187. package/src/batch/openai.ts +65 -193
  188. package/src/batch/provider.ts +336 -0
  189. package/src/batch-map.ts +29 -24
  190. package/src/batch-queue.ts +200 -11
  191. package/src/budget.ts +740 -0
  192. package/src/cache.ts +681 -0
  193. package/src/context.ts +122 -76
  194. package/src/digital-objects-registry.ts +750 -0
  195. package/src/errors.ts +37 -0
  196. package/src/eval/runner.ts +63 -38
  197. package/src/eval-log/in-memory.ts +90 -0
  198. package/src/eval-log/index.ts +46 -0
  199. package/src/eval-log/types.ts +110 -0
  200. package/src/function-registry.ts +671 -0
  201. package/src/generate.ts +33 -33
  202. package/src/index.ts +325 -49
  203. package/src/logger.ts +232 -0
  204. package/src/middleware/budget.ts +171 -0
  205. package/src/middleware/cache.ts +299 -0
  206. package/src/middleware/embed-cache.ts +195 -0
  207. package/src/middleware/index.ts +23 -0
  208. package/src/middleware/trace.ts +248 -0
  209. package/src/primitives.ts +589 -62
  210. package/src/retry.ts +902 -0
  211. package/src/schema.ts +8 -17
  212. package/src/telemetry.ts +403 -0
  213. package/src/template.ts +8 -4
  214. package/src/tool-orchestration.ts +1173 -0
  215. package/src/type-guards.ts +31 -0
  216. package/src/types.ts +164 -25
  217. package/src/wrap-for-v3.ts +105 -0
  218. package/test/ai-promise.test.ts +1080 -0
  219. package/test/ai-proxy.test.ts +1 -1
  220. package/test/backward-compat.test.ts +147 -0
  221. package/test/batch-autosubmit-errors.test.ts +610 -0
  222. package/test/batch-blog-posts.test.ts +87 -129
  223. package/test/budget-tracking.test.ts +800 -0
  224. package/test/cache.test.ts +712 -0
  225. package/test/context-isolation.test.ts +687 -0
  226. package/test/core-functions.test.ts +183 -579
  227. package/test/decide.test.ts +154 -322
  228. package/test/define.test.ts +211 -8
  229. package/test/digital-objects-registry.test.ts +760 -0
  230. package/test/embedding-cache-middleware.test.ts +140 -0
  231. package/test/evals/deterministic.eval.test.ts +376 -0
  232. package/test/generate-core.test.ts +140 -229
  233. package/test/implicit-batch.test.ts +22 -65
  234. package/test/json-parse-error-handling.test.ts +463 -0
  235. package/test/retry-policy-integration.test.ts +117 -0
  236. package/test/retry.test.ts +1016 -0
  237. package/test/schema.test.ts +55 -19
  238. package/test/streaming.test.ts +316 -0
  239. package/test/template.test.ts +1164 -0
  240. package/test/tool-orchestration.test.ts +1040 -0
  241. package/test/wrap-for-v3.test.ts +612 -0
  242. package/vitest.config.js +6 -0
  243. package/vitest.config.ts +20 -0
  244. package/dist/rpc/auth.d.ts +0 -69
  245. package/dist/rpc/auth.d.ts.map +0 -1
  246. package/dist/rpc/auth.js +0 -136
  247. package/dist/rpc/auth.js.map +0 -1
  248. package/dist/rpc/client.d.ts +0 -62
  249. package/dist/rpc/client.d.ts.map +0 -1
  250. package/dist/rpc/client.js +0 -103
  251. package/dist/rpc/client.js.map +0 -1
  252. package/dist/rpc/deferred.d.ts +0 -60
  253. package/dist/rpc/deferred.d.ts.map +0 -1
  254. package/dist/rpc/deferred.js +0 -96
  255. package/dist/rpc/deferred.js.map +0 -1
  256. package/dist/rpc/index.d.ts +0 -22
  257. package/dist/rpc/index.d.ts.map +0 -1
  258. package/dist/rpc/index.js +0 -38
  259. package/dist/rpc/index.js.map +0 -1
  260. package/dist/rpc/local.d.ts +0 -42
  261. package/dist/rpc/local.d.ts.map +0 -1
  262. package/dist/rpc/local.js +0 -50
  263. package/dist/rpc/local.js.map +0 -1
  264. package/dist/rpc/server.d.ts +0 -165
  265. package/dist/rpc/server.d.ts.map +0 -1
  266. package/dist/rpc/server.js +0 -405
  267. package/dist/rpc/server.js.map +0 -1
  268. package/dist/rpc/session.d.ts +0 -32
  269. package/dist/rpc/session.d.ts.map +0 -1
  270. package/dist/rpc/session.js +0 -43
  271. package/dist/rpc/session.js.map +0 -1
  272. package/dist/rpc/transport.d.ts +0 -306
  273. package/dist/rpc/transport.d.ts.map +0 -1
  274. package/dist/rpc/transport.js +0 -731
  275. package/dist/rpc/transport.js.map +0 -1
  276. package/src/batch/anthropic.js +0 -256
  277. package/src/batch/bedrock.js +0 -584
  278. package/src/batch/cloudflare.js +0 -287
  279. package/src/batch/google.js +0 -359
  280. package/src/batch/index.js +0 -30
  281. package/src/batch/memory.js +0 -187
  282. package/src/batch/openai.js +0 -402
  283. package/src/eval/index.js +0 -7
  284. package/src/eval/models.js +0 -119
  285. package/src/eval/runner.js +0 -147
  286. package/test/schema.test.js +0 -96
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Multi-Agent Research Workflow Example
3
+ *
4
+ * This example demonstrates a multi-agent research workflow where different
5
+ * specialized agents collaborate to research a topic. It shows how to:
6
+ * - Define specialized agents with different roles
7
+ * - Coordinate agent interactions
8
+ * - Aggregate and synthesize results
9
+ * - Use tool orchestration for complex workflows
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * ANTHROPIC_API_KEY=sk-... npx tsx examples/02-multi-agent-research.ts
14
+ * ```
15
+ */
16
+
17
+ import {
18
+ ai,
19
+ write,
20
+ list,
21
+ is,
22
+ configure,
23
+ AgenticLoop,
24
+ createTool,
25
+ createToolset,
26
+ type Tool,
27
+ } from '../src/index.js'
28
+ import { z } from 'zod'
29
+
30
+ // ============================================================================
31
+ // Agent Definitions
32
+ // ============================================================================
33
+
34
+ interface AgentResult {
35
+ agent: string
36
+ findings: string[]
37
+ confidence: number
38
+ }
39
+
40
+ /**
41
+ * Research Planner Agent - Creates a research plan
42
+ */
43
+ async function plannerAgent(topic: string): Promise<string[]> {
44
+ console.log('\n[Planner Agent] Creating research plan...')
45
+
46
+ const questions = await list`5 key research questions to thoroughly investigate: "${topic}"
47
+
48
+ Consider:
49
+ - Background and context
50
+ - Current state and trends
51
+ - Key players and stakeholders
52
+ - Challenges and opportunities
53
+ - Future implications`
54
+
55
+ console.log('[Planner Agent] Research questions:')
56
+ questions.forEach((q, i) => console.log(` ${i + 1}. ${q}`))
57
+
58
+ return questions
59
+ }
60
+
61
+ /**
62
+ * Fact Finder Agent - Gathers factual information
63
+ */
64
+ async function factFinderAgent(question: string): Promise<AgentResult> {
65
+ console.log(`\n[Fact Finder] Researching: ${question.substring(0, 50)}...`)
66
+
67
+ const { facts, sources, confidence } =
68
+ await ai`Research this question and provide factual information:
69
+ "${question}"
70
+
71
+ Provide your response with:
72
+ - facts: array of factual findings (3-5 key facts)
73
+ - sources: where this information typically comes from
74
+ - confidence: your confidence level 0-1 in these facts`
75
+
76
+ const result: AgentResult = {
77
+ agent: 'FactFinder',
78
+ findings: facts as string[],
79
+ confidence: (confidence as number) || 0.7,
80
+ }
81
+
82
+ console.log(
83
+ `[Fact Finder] Found ${result.findings.length} facts (confidence: ${result.confidence})`
84
+ )
85
+ return result
86
+ }
87
+
88
+ /**
89
+ * Critical Analyst Agent - Analyzes and critiques
90
+ */
91
+ async function analystAgent(findings: string[]): Promise<AgentResult> {
92
+ console.log('\n[Analyst Agent] Analyzing findings...')
93
+
94
+ const { analysis, gaps, confidence } = await ai`Critically analyze these research findings:
95
+ ${findings.map((f, i) => `${i + 1}. ${f}`).join('\n')}
96
+
97
+ Provide:
98
+ - analysis: array of analytical insights (3-4 insights)
99
+ - gaps: any gaps or areas needing more research
100
+ - confidence: confidence in the analysis 0-1`
101
+
102
+ const result: AgentResult = {
103
+ agent: 'Analyst',
104
+ findings: analysis as string[],
105
+ confidence: (confidence as number) || 0.6,
106
+ }
107
+
108
+ console.log(`[Analyst Agent] Generated ${result.findings.length} insights`)
109
+ return result
110
+ }
111
+
112
+ /**
113
+ * Synthesizer Agent - Creates final summary
114
+ */
115
+ async function synthesizerAgent(allResults: AgentResult[]): Promise<string> {
116
+ console.log('\n[Synthesizer Agent] Creating final synthesis...')
117
+
118
+ const allFindings = allResults.flatMap((r) => r.findings)
119
+
120
+ const synthesis = await write`Create a comprehensive research summary from these findings:
121
+
122
+ ${allFindings.map((f, i) => `- ${f}`).join('\n')}
123
+
124
+ Structure the summary with:
125
+ 1. Executive Summary (2-3 sentences)
126
+ 2. Key Findings (bullet points)
127
+ 3. Analysis & Insights
128
+ 4. Recommendations
129
+ 5. Areas for Further Research`
130
+
131
+ return synthesis
132
+ }
133
+
134
+ // ============================================================================
135
+ // Coordinator - Orchestrates the Multi-Agent Workflow
136
+ // ============================================================================
137
+
138
+ class ResearchCoordinator {
139
+ private topic: string
140
+ private results: AgentResult[] = []
141
+
142
+ constructor(topic: string) {
143
+ this.topic = topic
144
+ }
145
+
146
+ async run(): Promise<string> {
147
+ console.log(`\n${'='.repeat(60)}`)
148
+ console.log(`Research Topic: ${this.topic}`)
149
+ console.log('='.repeat(60))
150
+
151
+ // Phase 1: Planning
152
+ const questions = await plannerAgent(this.topic)
153
+
154
+ // Phase 2: Parallel Research
155
+ console.log('\n[Coordinator] Starting parallel research phase...')
156
+ const researchPromises = questions.slice(0, 3).map((q) => factFinderAgent(q))
157
+ const researchResults = await Promise.all(researchPromises)
158
+ this.results.push(...researchResults)
159
+
160
+ // Phase 3: Analysis
161
+ const allFacts = researchResults.flatMap((r) => r.findings)
162
+ const analysisResult = await analystAgent(allFacts)
163
+ this.results.push(analysisResult)
164
+
165
+ // Phase 4: Synthesis
166
+ const finalReport = await synthesizerAgent(this.results)
167
+
168
+ // Phase 5: Quality Check
169
+ const isQualityOk =
170
+ await is`This research summary is well-structured and comprehensive: "${finalReport.substring(
171
+ 0,
172
+ 200
173
+ )}..."`
174
+
175
+ if (!isQualityOk) {
176
+ console.log('[Coordinator] Quality check failed, requesting revision...')
177
+ // In production, you might iterate on the summary
178
+ }
179
+
180
+ return finalReport
181
+ }
182
+
183
+ getResults(): AgentResult[] {
184
+ return this.results
185
+ }
186
+ }
187
+
188
+ // ============================================================================
189
+ // Tool-Based Research Agent (Alternative Approach)
190
+ // ============================================================================
191
+
192
+ const searchTool = createTool({
193
+ name: 'search',
194
+ description: 'Search for information on a topic',
195
+ parameters: {
196
+ query: z.string().describe('Search query'),
197
+ },
198
+ execute: async ({ query }) => {
199
+ // Simulate search results
200
+ console.log(` [Tool] Searching: ${query}`)
201
+ return {
202
+ results: [
203
+ `Result 1 for "${query}": Found relevant information...`,
204
+ `Result 2 for "${query}": Additional context...`,
205
+ ],
206
+ }
207
+ },
208
+ })
209
+
210
+ const analyzeTool = createTool({
211
+ name: 'analyze',
212
+ description: 'Analyze and synthesize information',
213
+ parameters: {
214
+ data: z.string().describe('Data to analyze'),
215
+ perspective: z.string().describe('Analysis perspective'),
216
+ },
217
+ execute: async ({ data, perspective }) => {
218
+ console.log(` [Tool] Analyzing from ${perspective} perspective...`)
219
+ return {
220
+ analysis: `Analysis of "${data.substring(
221
+ 0,
222
+ 30
223
+ )}..." from ${perspective} perspective: Key insights identified.`,
224
+ }
225
+ },
226
+ })
227
+
228
+ async function toolBasedResearch(topic: string): Promise<void> {
229
+ console.log('\n--- Tool-Based Research Agent ---')
230
+
231
+ const loop = new AgenticLoop({
232
+ tools: createToolset(searchTool, analyzeTool),
233
+ maxSteps: 5,
234
+ onStep: (step) => {
235
+ console.log(`[Step ${step.stepNumber}] Tool calls: ${step.toolCalls.length}`)
236
+ },
237
+ })
238
+
239
+ // This requires a model that supports tool calling
240
+ // For demonstration, we'll skip the actual execution
241
+ console.log('Tool-based agent configured with:', loop.getToolsForSDK())
242
+ }
243
+
244
+ // ============================================================================
245
+ // Main Example
246
+ // ============================================================================
247
+
248
+ async function main() {
249
+ console.log('\n=== Multi-Agent Research Workflow ===\n')
250
+
251
+ // Configure the AI provider
252
+ configure({
253
+ model: 'sonnet',
254
+ provider: 'anthropic',
255
+ })
256
+
257
+ // Run the research workflow
258
+ const coordinator = new ResearchCoordinator(
259
+ 'The impact of Large Language Models on software development practices'
260
+ )
261
+
262
+ const report = await coordinator.run()
263
+
264
+ console.log('\n' + '='.repeat(60))
265
+ console.log('FINAL RESEARCH REPORT')
266
+ console.log('='.repeat(60))
267
+ console.log(report)
268
+
269
+ // Show agent statistics
270
+ console.log('\n--- Agent Statistics ---')
271
+ const results = coordinator.getResults()
272
+ for (const result of results) {
273
+ console.log(
274
+ `${result.agent}: ${result.findings.length} findings (confidence: ${result.confidence})`
275
+ )
276
+ }
277
+
278
+ // Demonstrate tool-based approach
279
+ await toolBasedResearch('AI in healthcare')
280
+ }
281
+
282
+ main()
283
+ .then(() => {
284
+ console.log('\n=== Example Complete ===\n')
285
+ process.exit(0)
286
+ })
287
+ .catch((error) => {
288
+ console.error('\nError:', error.message)
289
+ process.exit(1)
290
+ })
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Email Classification and Routing Example
3
+ *
4
+ * This example demonstrates building an email classification and routing system
5
+ * using ai-functions. It shows how to:
6
+ * - Classify emails into categories
7
+ * - Extract key information from emails
8
+ * - Route to appropriate handlers
9
+ * - Handle priority and urgency
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * ANTHROPIC_API_KEY=sk-... npx tsx examples/03-email-classification.ts
14
+ * ```
15
+ */
16
+
17
+ import { ai, is, list, extract, configure, decide } from '../src/index.js'
18
+
19
+ // ============================================================================
20
+ // Types
21
+ // ============================================================================
22
+
23
+ interface Email {
24
+ id: string
25
+ from: string
26
+ to: string
27
+ subject: string
28
+ body: string
29
+ timestamp: Date
30
+ }
31
+
32
+ interface ClassificationResult {
33
+ category: string
34
+ subcategory: string
35
+ priority: 'low' | 'medium' | 'high' | 'urgent'
36
+ sentiment: 'positive' | 'neutral' | 'negative'
37
+ requiresResponse: boolean
38
+ suggestedTeam: string
39
+ }
40
+
41
+ interface ExtractedInfo {
42
+ senderIntent: string
43
+ keyEntities: string[]
44
+ actionItems: string[]
45
+ deadlines: string[]
46
+ attachmentReferences: string[]
47
+ }
48
+
49
+ interface RoutingDecision {
50
+ team: string
51
+ handler: string
52
+ autoResponse: boolean
53
+ escalate: boolean
54
+ suggestedResponse?: string
55
+ }
56
+
57
+ // ============================================================================
58
+ // Sample Emails
59
+ // ============================================================================
60
+
61
+ const sampleEmails: Email[] = [
62
+ {
63
+ id: 'email-1',
64
+ from: 'john.smith@bigcorp.com',
65
+ to: 'sales@ourcompany.com',
66
+ subject: 'Enterprise License Inquiry - 500 seats',
67
+ body: `Hi,
68
+
69
+ We are interested in your enterprise solution for our organization.
70
+ We have approximately 500 developers who would need access.
71
+
72
+ Could you please provide:
73
+ 1. Enterprise pricing for 500 seats
74
+ 2. Volume discount options
75
+ 3. Security compliance certifications (SOC2, HIPAA)
76
+ 4. On-premise deployment options
77
+
78
+ We're looking to make a decision within the next 2 weeks.
79
+
80
+ Best regards,
81
+ John Smith
82
+ VP of Engineering, BigCorp Inc.`,
83
+ timestamp: new Date(),
84
+ },
85
+ {
86
+ id: 'email-2',
87
+ from: 'frustrated.user@example.com',
88
+ to: 'support@ourcompany.com',
89
+ subject: 'URGENT: System down - Production blocked!',
90
+ body: `Our production system has been down for 3 hours!!!
91
+
92
+ Error: "Connection timeout to API endpoint"
93
+
94
+ We're losing thousands of dollars per hour. We need immediate assistance.
95
+ Our contract includes 24/7 premium support.
96
+
97
+ Account ID: ENT-12345
98
+ Contact: 555-123-4567
99
+
100
+ This needs to be fixed NOW.`,
101
+ timestamp: new Date(),
102
+ },
103
+ {
104
+ id: 'email-3',
105
+ from: 'newsletter@techblog.com',
106
+ to: 'team@ourcompany.com',
107
+ subject: 'Weekly AI Newsletter - Top Stories',
108
+ body: `This week in AI:
109
+
110
+ - GPT-5 rumors continue to swirl
111
+ - New open-source models released
112
+ - Industry adoption trends
113
+
114
+ Click to read more...
115
+
116
+ Unsubscribe: techblog.com/unsubscribe`,
117
+ timestamp: new Date(),
118
+ },
119
+ {
120
+ id: 'email-4',
121
+ from: 'hr@ourcompany.com',
122
+ to: 'team@ourcompany.com',
123
+ subject: 'Company All-Hands Meeting - Friday 3pm',
124
+ body: `Hi everyone,
125
+
126
+ Reminder: Our monthly all-hands meeting is this Friday at 3pm.
127
+
128
+ Agenda:
129
+ - Q4 Results Review
130
+ - 2025 Planning
131
+ - Team Awards
132
+
133
+ Please submit any questions beforehand.
134
+
135
+ Best,
136
+ HR Team`,
137
+ timestamp: new Date(),
138
+ },
139
+ ]
140
+
141
+ // ============================================================================
142
+ // Email Classifier
143
+ // ============================================================================
144
+
145
+ async function classifyEmail(email: Email): Promise<ClassificationResult> {
146
+ console.log(`\nClassifying: "${email.subject}"`)
147
+
148
+ const { category, subcategory, priority, sentiment, requiresResponse, suggestedTeam } =
149
+ await ai`Classify this email:
150
+
151
+ From: ${email.from}
152
+ Subject: ${email.subject}
153
+ Body: ${email.body}
154
+
155
+ Provide:
156
+ - category: one of [sales, support, marketing, internal, spam, other]
157
+ - subcategory: more specific classification
158
+ - priority: one of [low, medium, high, urgent]
159
+ - sentiment: one of [positive, neutral, negative]
160
+ - requiresResponse: boolean - does this need a reply?
161
+ - suggestedTeam: which team should handle this`
162
+
163
+ return {
164
+ category: category as string,
165
+ subcategory: subcategory as string,
166
+ priority: priority as 'low' | 'medium' | 'high' | 'urgent',
167
+ sentiment: sentiment as 'positive' | 'neutral' | 'negative',
168
+ requiresResponse: requiresResponse as boolean,
169
+ suggestedTeam: suggestedTeam as string,
170
+ }
171
+ }
172
+
173
+ // ============================================================================
174
+ // Information Extractor
175
+ // ============================================================================
176
+
177
+ async function extractInfo(email: Email): Promise<ExtractedInfo> {
178
+ console.log(` Extracting key information...`)
179
+
180
+ const { senderIntent, keyEntities, actionItems, deadlines, attachmentReferences } =
181
+ await ai`Extract key information from this email:
182
+
183
+ Subject: ${email.subject}
184
+ Body: ${email.body}
185
+
186
+ Provide:
187
+ - senderIntent: what the sender wants (1 sentence)
188
+ - keyEntities: array of important entities (names, companies, products, account IDs)
189
+ - actionItems: array of action items or requests
190
+ - deadlines: array of mentioned deadlines or timeframes
191
+ - attachmentReferences: any references to attachments or documents`
192
+
193
+ return {
194
+ senderIntent: senderIntent as string,
195
+ keyEntities: keyEntities as string[],
196
+ actionItems: actionItems as string[],
197
+ deadlines: deadlines as string[],
198
+ attachmentReferences: attachmentReferences as string[],
199
+ }
200
+ }
201
+
202
+ // ============================================================================
203
+ // Router
204
+ // ============================================================================
205
+
206
+ async function routeEmail(
207
+ email: Email,
208
+ classification: ClassificationResult,
209
+ info: ExtractedInfo
210
+ ): Promise<RoutingDecision> {
211
+ console.log(` Determining routing...`)
212
+
213
+ // Check if escalation is needed
214
+ const needsEscalation = await is`This email requires immediate escalation to management:
215
+ Category: ${classification.category}
216
+ Priority: ${classification.priority}
217
+ Sentiment: ${classification.sentiment}
218
+ Intent: ${info.senderIntent}`
219
+
220
+ // Determine if auto-response is appropriate
221
+ const canAutoRespond = await is`This email can be handled with an automated response:
222
+ Category: ${classification.category}
223
+ Intent: ${info.senderIntent}
224
+ Action Items: ${info.actionItems.join(', ')}`
225
+
226
+ // Route based on category
227
+ const routingMap: Record<string, { team: string; handler: string }> = {
228
+ sales: { team: 'Sales', handler: 'sales-queue' },
229
+ support: {
230
+ team: 'Support',
231
+ handler: classification.priority === 'urgent' ? 'urgent-queue' : 'support-queue',
232
+ },
233
+ marketing: { team: 'Marketing', handler: 'marketing-inbox' },
234
+ internal: { team: 'Internal', handler: 'internal-comms' },
235
+ spam: { team: 'None', handler: 'spam-filter' },
236
+ other: { team: 'Triage', handler: 'general-queue' },
237
+ }
238
+
239
+ const route = routingMap[classification.category] || routingMap.other
240
+
241
+ let suggestedResponse: string | undefined
242
+ if (canAutoRespond) {
243
+ suggestedResponse = await generateAutoResponse(email, classification, info)
244
+ }
245
+
246
+ return {
247
+ team: route.team,
248
+ handler: route.handler,
249
+ autoResponse: canAutoRespond,
250
+ escalate: needsEscalation,
251
+ suggestedResponse,
252
+ }
253
+ }
254
+
255
+ // ============================================================================
256
+ // Auto-Response Generator
257
+ // ============================================================================
258
+
259
+ async function generateAutoResponse(
260
+ email: Email,
261
+ classification: ClassificationResult,
262
+ info: ExtractedInfo
263
+ ): Promise<string> {
264
+ console.log(` Generating auto-response...`)
265
+
266
+ const response = await ai`Generate a brief, professional auto-response for this email:
267
+
268
+ Original Subject: ${email.subject}
269
+ Category: ${classification.category}
270
+ Sender Intent: ${info.senderIntent}
271
+
272
+ The response should:
273
+ - Acknowledge receipt
274
+ - Set expectations for response time
275
+ - Provide any immediately helpful information
276
+ - Be concise (3-4 sentences max)
277
+
278
+ Return just the response text.`
279
+
280
+ return response as string
281
+ }
282
+
283
+ // ============================================================================
284
+ // Batch Classification
285
+ // ============================================================================
286
+
287
+ async function batchClassifyEmails(emails: Email[]): Promise<void> {
288
+ console.log(`\n${'='.repeat(60)}`)
289
+ console.log('Batch Email Classification Results')
290
+ console.log('='.repeat(60))
291
+
292
+ for (const email of emails) {
293
+ const classification = await classifyEmail(email)
294
+ const info = await extractInfo(email)
295
+ const routing = await routeEmail(email, classification, info)
296
+
297
+ console.log(`
298
+ ID: ${email.id}
299
+ Subject: ${email.subject}
300
+ ---
301
+ Classification:
302
+ Category: ${classification.category} / ${classification.subcategory}
303
+ Priority: ${classification.priority}
304
+ Sentiment: ${classification.sentiment}
305
+ Requires Response: ${classification.requiresResponse}
306
+
307
+ Extracted Info:
308
+ Intent: ${info.senderIntent}
309
+ Entities: ${info.keyEntities.join(', ') || 'none'}
310
+ Action Items: ${info.actionItems.length}
311
+ Deadlines: ${info.deadlines.join(', ') || 'none'}
312
+
313
+ Routing:
314
+ Team: ${routing.team}
315
+ Handler: ${routing.handler}
316
+ Auto-Response: ${routing.autoResponse}
317
+ Escalate: ${routing.escalate}
318
+ ${
319
+ routing.suggestedResponse
320
+ ? `\nSuggested Response:\n "${routing.suggestedResponse.substring(0, 100)}..."`
321
+ : ''
322
+ }
323
+ ${'='.repeat(60)}`)
324
+ }
325
+ }
326
+
327
+ // ============================================================================
328
+ // Priority Comparison
329
+ // ============================================================================
330
+
331
+ async function prioritizeEmails(emails: Email[]): Promise<Email[]> {
332
+ console.log('\n--- Prioritizing Emails ---')
333
+
334
+ // Use decide to compare email urgency
335
+ if (emails.length < 2) return emails
336
+
337
+ // For demo, just compare first two
338
+ const moreUrgent = await decide`which email is more urgent and should be handled first`(
339
+ { subject: emails[0].subject, body: emails[0].body.substring(0, 100) },
340
+ { subject: emails[1].subject, body: emails[1].body.substring(0, 100) }
341
+ )
342
+
343
+ console.log(`Most urgent: "${(moreUrgent as Email).subject}"`)
344
+ return emails
345
+ }
346
+
347
+ // ============================================================================
348
+ // Main Example
349
+ // ============================================================================
350
+
351
+ async function main() {
352
+ console.log('\n=== Email Classification & Routing Example ===\n')
353
+
354
+ // Configure the AI provider
355
+ configure({
356
+ model: 'sonnet',
357
+ provider: 'anthropic',
358
+ })
359
+
360
+ // Process all sample emails
361
+ await batchClassifyEmails(sampleEmails)
362
+
363
+ // Prioritize emails
364
+ await prioritizeEmails(sampleEmails.slice(0, 2))
365
+
366
+ // Show statistics
367
+ console.log('\n--- Processing Statistics ---')
368
+ console.log(`Total emails processed: ${sampleEmails.length}`)
369
+ }
370
+
371
+ main()
372
+ .then(() => {
373
+ console.log('\n=== Example Complete ===\n')
374
+ process.exit(0)
375
+ })
376
+ .catch((error) => {
377
+ console.error('\nError:', error.message)
378
+ process.exit(1)
379
+ })