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,491 @@
1
+ /**
2
+ * Batch Processing Workflow Example (1000+ items)
3
+ *
4
+ * This example demonstrates processing large batches efficiently using ai-functions.
5
+ * It shows how to:
6
+ * - Process 1000+ items with automatic batching
7
+ * - Use provider batch APIs for 50% cost savings
8
+ * - Handle progress tracking and error recovery
9
+ * - Implement parallel processing with concurrency limits
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * ANTHROPIC_API_KEY=sk-... npx tsx examples/08-batch-processing.ts
14
+ * ```
15
+ */
16
+
17
+ import {
18
+ write,
19
+ list,
20
+ ai,
21
+ is,
22
+ configure,
23
+ createBatch,
24
+ withBatch,
25
+ BatchQueue,
26
+ withRetry,
27
+ BudgetTracker,
28
+ type BatchItem,
29
+ } from '../src/index.js'
30
+
31
+ // For demo, use memory adapter
32
+ import '../src/batch/memory.js'
33
+
34
+ // ============================================================================
35
+ // Types
36
+ // ============================================================================
37
+
38
+ interface Product {
39
+ id: string
40
+ name: string
41
+ description: string
42
+ category: string
43
+ }
44
+
45
+ interface ProcessedProduct {
46
+ id: string
47
+ originalName: string
48
+ enhancedDescription: string
49
+ seoTitle: string
50
+ seoKeywords: string[]
51
+ sentiment: string
52
+ qualityScore: number
53
+ }
54
+
55
+ interface BatchProgress {
56
+ total: number
57
+ processed: number
58
+ successful: number
59
+ failed: number
60
+ startTime: number
61
+ estimatedRemaining: number
62
+ }
63
+
64
+ // ============================================================================
65
+ // Sample Data Generator
66
+ // ============================================================================
67
+
68
+ function generateSampleProducts(count: number): Product[] {
69
+ const categories = ['Electronics', 'Clothing', 'Home & Garden', 'Sports', 'Books', 'Toys']
70
+ const adjectives = ['Premium', 'Deluxe', 'Basic', 'Pro', 'Ultra', 'Eco', 'Smart', 'Classic']
71
+ const nouns = ['Widget', 'Gadget', 'Tool', 'Device', 'Item', 'Product', 'Solution', 'System']
72
+
73
+ const products: Product[] = []
74
+
75
+ for (let i = 0; i < count; i++) {
76
+ const adj = adjectives[Math.floor(Math.random() * adjectives.length)]
77
+ const noun = nouns[Math.floor(Math.random() * nouns.length)]
78
+ const category = categories[Math.floor(Math.random() * categories.length)]
79
+
80
+ products.push({
81
+ id: `PROD-${String(i + 1).padStart(5, '0')}`,
82
+ name: `${adj} ${noun} ${i + 1}`,
83
+ description: `A high-quality ${noun.toLowerCase()} designed for ${category.toLowerCase()} enthusiasts. Features include advanced technology and durable construction.`,
84
+ category,
85
+ })
86
+ }
87
+
88
+ return products
89
+ }
90
+
91
+ // ============================================================================
92
+ // Progress Tracking
93
+ // ============================================================================
94
+
95
+ class ProgressTracker {
96
+ private progress: BatchProgress
97
+
98
+ constructor(total: number) {
99
+ this.progress = {
100
+ total,
101
+ processed: 0,
102
+ successful: 0,
103
+ failed: 0,
104
+ startTime: Date.now(),
105
+ estimatedRemaining: 0,
106
+ }
107
+ }
108
+
109
+ update(success: boolean): void {
110
+ this.progress.processed++
111
+ if (success) {
112
+ this.progress.successful++
113
+ } else {
114
+ this.progress.failed++
115
+ }
116
+
117
+ // Calculate estimated remaining time
118
+ const elapsed = Date.now() - this.progress.startTime
119
+ const rate = this.progress.processed / elapsed
120
+ const remaining = this.progress.total - this.progress.processed
121
+ this.progress.estimatedRemaining = remaining / rate
122
+ }
123
+
124
+ display(): void {
125
+ const percent = ((this.progress.processed / this.progress.total) * 100).toFixed(1)
126
+ const elapsed = ((Date.now() - this.progress.startTime) / 1000).toFixed(0)
127
+ const remaining = (this.progress.estimatedRemaining / 1000).toFixed(0)
128
+
129
+ // Clear line and update progress
130
+ process.stdout.write(
131
+ `\r[${percent}%] ${this.progress.processed}/${this.progress.total} | Success: ${this.progress.successful} | Failed: ${this.progress.failed} | Elapsed: ${elapsed}s | ETA: ${remaining}s `
132
+ )
133
+ }
134
+
135
+ getStats(): BatchProgress {
136
+ return { ...this.progress }
137
+ }
138
+ }
139
+
140
+ // ============================================================================
141
+ // Batch Processor
142
+ // ============================================================================
143
+
144
+ class ProductEnhancer {
145
+ private budgetTracker: BudgetTracker
146
+ private concurrency: number
147
+ private results: ProcessedProduct[] = []
148
+ private errors: { id: string; error: string }[] = []
149
+
150
+ constructor(options: { maxCost?: number; concurrency?: number } = {}) {
151
+ this.budgetTracker = new BudgetTracker({
152
+ maxCost: options.maxCost || 100,
153
+ maxTokens: 1000000,
154
+ alertThresholds: [0.5, 0.8, 0.95],
155
+ onAlert: (alert) => {
156
+ console.log(`\n[Budget Alert] ${(alert.threshold * 100).toFixed(0)}% of budget used`)
157
+ },
158
+ })
159
+ this.concurrency = options.concurrency || 10
160
+ }
161
+
162
+ /**
163
+ * Process a single product
164
+ */
165
+ private async processProduct(product: Product): Promise<ProcessedProduct> {
166
+ // Generate enhanced description
167
+ const enhanced = await ai`Enhance this product description for better marketing appeal:
168
+
169
+ Product: ${product.name}
170
+ Category: ${product.category}
171
+ Original Description: ${product.description}
172
+
173
+ Provide:
174
+ - enhancedDescription: improved, engaging description (2-3 sentences)
175
+ - seoTitle: SEO-optimized title (under 60 chars)
176
+ - seoKeywords: array of 5 relevant keywords
177
+ - sentiment: the tone (professional/casual/luxury/budget)
178
+ - qualityScore: quality score 1-10 based on the original`
179
+
180
+ // Track token usage (estimated)
181
+ this.budgetTracker.recordUsage({
182
+ inputTokens: 150,
183
+ outputTokens: 100,
184
+ model: 'sonnet',
185
+ })
186
+
187
+ return {
188
+ id: product.id,
189
+ originalName: product.name,
190
+ enhancedDescription: (enhanced as any).enhancedDescription || '',
191
+ seoTitle: (enhanced as any).seoTitle || '',
192
+ seoKeywords: (enhanced as any).seoKeywords || [],
193
+ sentiment: (enhanced as any).sentiment || 'professional',
194
+ qualityScore: (enhanced as any).qualityScore || 5,
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Process products in chunks with concurrency control
200
+ */
201
+ async processInChunks(products: Product[]): Promise<void> {
202
+ console.log(
203
+ `\nProcessing ${products.length} products with concurrency ${this.concurrency}...\n`
204
+ )
205
+
206
+ const tracker = new ProgressTracker(products.length)
207
+
208
+ // Process in chunks for better memory management
209
+ const chunkSize = this.concurrency * 5
210
+ const chunks: Product[][] = []
211
+
212
+ for (let i = 0; i < products.length; i += chunkSize) {
213
+ chunks.push(products.slice(i, i + chunkSize))
214
+ }
215
+
216
+ for (const chunk of chunks) {
217
+ // Process chunk items with concurrency limit
218
+ const promises = chunk.map(async (product) => {
219
+ try {
220
+ const result = await withRetry(() => this.processProduct(product), {
221
+ maxRetries: 2,
222
+ baseDelay: 1000,
223
+ })
224
+ this.results.push(result)
225
+ tracker.update(true)
226
+ } catch (error) {
227
+ this.errors.push({ id: product.id, error: (error as Error).message })
228
+ tracker.update(false)
229
+ }
230
+ tracker.display()
231
+ })
232
+
233
+ // Process with concurrency limit
234
+ const executing: Promise<void>[] = []
235
+ for (const promise of promises) {
236
+ const p = promise.then(() => {
237
+ executing.splice(executing.indexOf(p), 1)
238
+ })
239
+ executing.push(p)
240
+
241
+ if (executing.length >= this.concurrency) {
242
+ await Promise.race(executing)
243
+ }
244
+ }
245
+ await Promise.all(executing)
246
+ }
247
+
248
+ console.log('\n') // New line after progress
249
+ }
250
+
251
+ /**
252
+ * Use batch API for processing (50% cost savings)
253
+ */
254
+ async processWithBatchAPI(products: Product[]): Promise<void> {
255
+ console.log(`\nUsing Batch API for ${products.length} products (50% cost savings)...\n`)
256
+
257
+ // Create batch queue
258
+ const batch = createBatch({
259
+ provider: 'openai',
260
+ autoSubmit: {
261
+ threshold: 100,
262
+ maxWait: 5000,
263
+ },
264
+ })
265
+
266
+ // Add all items to batch
267
+ const promises = products.map((product) =>
268
+ batch.add(
269
+ `Enhance this product description:
270
+ Name: ${product.name}
271
+ Category: ${product.category}
272
+ Description: ${product.description}
273
+
274
+ Return: enhanced description, SEO title, and 5 keywords as JSON`
275
+ )
276
+ )
277
+
278
+ console.log(`Added ${products.length} items to batch queue`)
279
+
280
+ // Submit batch
281
+ console.log('Submitting batch...')
282
+ const submission = await batch.submit()
283
+
284
+ if (submission.job) {
285
+ console.log(`Batch submitted: ${submission.job.id}`)
286
+ console.log('Status: Processing (this would take up to 24 hours in production)')
287
+ }
288
+
289
+ // For demo, we simulate the results
290
+ console.log('\n[Demo mode: Simulating batch results]')
291
+ }
292
+
293
+ /**
294
+ * Get processing results
295
+ */
296
+ getResults(): {
297
+ results: ProcessedProduct[]
298
+ errors: { id: string; error: string }[]
299
+ stats: {
300
+ total: number
301
+ successful: number
302
+ failed: number
303
+ cost: number
304
+ tokens: number
305
+ }
306
+ } {
307
+ return {
308
+ results: this.results,
309
+ errors: this.errors,
310
+ stats: {
311
+ total: this.results.length + this.errors.length,
312
+ successful: this.results.length,
313
+ failed: this.errors.length,
314
+ cost: this.budgetTracker.getTotalCost(),
315
+ tokens: this.budgetTracker.getTotalTokens(),
316
+ },
317
+ }
318
+ }
319
+ }
320
+
321
+ // ============================================================================
322
+ // Using list.map() for Batch Processing
323
+ // ============================================================================
324
+
325
+ async function processWithListMap(): Promise<void> {
326
+ console.log('\n--- Using list.map() for automatic batching ---\n')
327
+
328
+ // Generate ideas and process each in batch
329
+ const ideas = await list`10 product improvement ideas for a smart home device`
330
+
331
+ console.log(`Generated ${ideas.length} ideas`)
332
+
333
+ // Map processes each item - batched automatically when batchMode is enabled
334
+ const evaluated = await Promise.all(
335
+ ideas.map(async (idea) => {
336
+ const { feasibility, cost, impact } = await ai`Evaluate this product improvement idea:
337
+ "${idea}"
338
+
339
+ Provide:
340
+ - feasibility: score 1-10
341
+ - cost: estimated cost (low/medium/high)
342
+ - impact: customer impact score 1-10`
343
+
344
+ return {
345
+ idea,
346
+ feasibility,
347
+ cost,
348
+ impact,
349
+ }
350
+ })
351
+ )
352
+
353
+ console.log('\nEvaluated ideas:')
354
+ evaluated.forEach((e, i) => {
355
+ console.log(
356
+ ` ${i + 1}. ${(e.idea as string).substring(0, 40)}... (feasibility: ${
357
+ e.feasibility
358
+ }, impact: ${e.impact})`
359
+ )
360
+ })
361
+ }
362
+
363
+ // ============================================================================
364
+ // Parallel Processing with Streams
365
+ // ============================================================================
366
+
367
+ async function processWithStreams(products: Product[]): Promise<void> {
368
+ console.log('\n--- Stream-based Processing ---\n')
369
+
370
+ let processed = 0
371
+ const total = products.length
372
+
373
+ // Process as async generator
374
+ async function* processGenerator(): AsyncGenerator<ProcessedProduct> {
375
+ for (const product of products) {
376
+ const result = await ai`Quick enhancement for: ${product.name}`
377
+ processed++
378
+
379
+ if (processed % 10 === 0) {
380
+ console.log(` Processed ${processed}/${total}`)
381
+ }
382
+
383
+ yield {
384
+ id: product.id,
385
+ originalName: product.name,
386
+ enhancedDescription: result as string,
387
+ seoTitle: product.name,
388
+ seoKeywords: [],
389
+ sentiment: 'professional',
390
+ qualityScore: 7,
391
+ }
392
+ }
393
+ }
394
+
395
+ // Consume stream
396
+ const results: ProcessedProduct[] = []
397
+ for await (const result of processGenerator()) {
398
+ results.push(result)
399
+ if (results.length >= 5) break // Demo limit
400
+ }
401
+
402
+ console.log(`\nProcessed ${results.length} products via stream`)
403
+ }
404
+
405
+ // ============================================================================
406
+ // Main Example
407
+ // ============================================================================
408
+
409
+ async function main() {
410
+ console.log('\n=== Batch Processing Workflow Example (1000+ items) ===\n')
411
+
412
+ // Configure the AI provider
413
+ configure({
414
+ model: 'sonnet',
415
+ provider: 'anthropic',
416
+ batchMode: 'auto',
417
+ batchThreshold: 10,
418
+ })
419
+
420
+ // Generate sample products
421
+ const smallBatch = generateSampleProducts(25)
422
+ const largeBatch = generateSampleProducts(100)
423
+
424
+ console.log(`Generated ${smallBatch.length} products for demo`)
425
+ console.log(`Would generate ${largeBatch.length}+ products for production\n`)
426
+
427
+ // Method 1: Process with concurrency control
428
+ console.log('=== Method 1: Concurrent Processing ===')
429
+ const enhancer = new ProductEnhancer({
430
+ maxCost: 10,
431
+ concurrency: 5,
432
+ })
433
+
434
+ await enhancer.processInChunks(smallBatch.slice(0, 10)) // Demo with 10 items
435
+
436
+ const { stats } = enhancer.getResults()
437
+ console.log('\nProcessing Statistics:')
438
+ console.log(` Total: ${stats.total}`)
439
+ console.log(` Successful: ${stats.successful}`)
440
+ console.log(` Failed: ${stats.failed}`)
441
+ console.log(` Estimated Cost: $${stats.cost.toFixed(4)}`)
442
+ console.log(` Total Tokens: ${stats.tokens}`)
443
+
444
+ // Method 2: Batch API (50% savings)
445
+ console.log('\n=== Method 2: Batch API (50% Cost Savings) ===')
446
+ await enhancer.processWithBatchAPI(smallBatch.slice(0, 5))
447
+
448
+ // Method 3: list.map() automatic batching
449
+ console.log('\n=== Method 3: list.map() Automatic Batching ===')
450
+ await processWithListMap()
451
+
452
+ // Method 4: Stream-based processing
453
+ console.log('\n=== Method 4: Stream-based Processing ===')
454
+ await processWithStreams(smallBatch.slice(0, 5))
455
+
456
+ // Summary
457
+ console.log('\n=== Batch Processing Summary ===')
458
+ console.log(`
459
+ For processing 1000+ items, consider:
460
+
461
+ 1. Concurrent Processing
462
+ - Best for: Real-time results needed
463
+ - Cost: Standard pricing
464
+ - Latency: Low (parallel execution)
465
+
466
+ 2. Provider Batch API
467
+ - Best for: Large volumes, non-urgent
468
+ - Cost: 50% discount
469
+ - Latency: Up to 24 hours
470
+
471
+ 3. list.map() with batchMode
472
+ - Best for: Simple transformations
473
+ - Cost: Automatic optimization
474
+ - Latency: Variable
475
+
476
+ 4. Stream-based Processing
477
+ - Best for: Memory efficiency
478
+ - Cost: Standard pricing
479
+ - Latency: Progressive results
480
+ `)
481
+ }
482
+
483
+ main()
484
+ .then(() => {
485
+ console.log('\n=== Example Complete ===\n')
486
+ process.exit(0)
487
+ })
488
+ .catch((error) => {
489
+ console.error('\nError:', error.message)
490
+ process.exit(1)
491
+ })