ai-functions 2.1.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +55 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +116 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +546 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +135 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +28 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +671 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +119 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +164 -25
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,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
|
+
})
|