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.
- package/.turbo/turbo-build.log +1 -4
- package/CHANGELOG.md +68 -1
- package/README.md +397 -157
- package/dist/ai-promise.d.ts +50 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +410 -51
- 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 +54 -837
- 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 +272 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +513 -0
- package/dist/budget.js.map +1 -0
- package/dist/cache.d.ts +295 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +433 -0
- package/dist/cache.js.map +1 -0
- package/dist/context.d.ts +42 -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 +10 -1
- 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 -22
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +35 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +89 -42
- 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 +368 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +646 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -10
- 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 +453 -0
- package/dist/tool-orchestration.d.ts.map +1 -0
- package/dist/tool-orchestration.js +763 -0
- package/dist/tool-orchestration.js.map +1 -0
- 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 +10 -6
- package/src/ai-promise.ts +528 -99
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -1153
- 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 +740 -0
- package/src/cache.ts +681 -0
- package/src/context.ts +122 -76
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +63 -38
- 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 -33
- package/src/index.ts +325 -49
- 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 +902 -0
- package/src/schema.ts +8 -17
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +1173 -0
- 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/backward-compat.test.ts +147 -0
- package/test/batch-autosubmit-errors.test.ts +610 -0
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/budget-tracking.test.ts +800 -0
- package/test/cache.test.ts +712 -0
- package/test/context-isolation.test.ts +687 -0
- 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/evals/deterministic.eval.test.ts +376 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/json-parse-error-handling.test.ts +463 -0
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/retry.test.ts +1016 -0
- package/test/schema.test.ts +55 -19
- package/test/streaming.test.ts +316 -0
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +1040 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- 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
|
@@ -15,117 +15,71 @@
|
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { describe, it, expect,
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
withBatch,
|
|
22
|
-
type BatchQueue,
|
|
23
|
-
type BatchResult,
|
|
24
|
-
} from '../src/batch-queue.js'
|
|
25
|
-
|
|
26
|
-
// Import memory adapter to register it
|
|
27
|
-
import '../src/batch/memory.js'
|
|
28
|
-
import { configureMemoryAdapter, clearBatches } from '../src/batch/memory.js'
|
|
18
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
19
|
+
import { createBatch, withBatchQueue, generateObject, generateText } from '../src/index.js'
|
|
20
|
+
import { z } from 'zod'
|
|
29
21
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
22
|
+
// Memory adapter for testing - simulates batch processing locally
|
|
23
|
+
// Import from .ts file for proper vite resolution
|
|
24
|
+
import { configureMemoryAdapter, clearBatches } from '../src/batch/memory.ts'
|
|
33
25
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
generateObject: vi.fn().mockImplementation(async ({ prompt, schema }) => {
|
|
37
|
-
// Simulate list generation
|
|
38
|
-
if (schema?.items) {
|
|
39
|
-
return {
|
|
40
|
-
object: {
|
|
41
|
-
items: [
|
|
42
|
-
'How AI is Revolutionizing Startup Fundraising in 2026',
|
|
43
|
-
'The Rise of Solo Founders: Building $10M ARR Companies Alone',
|
|
44
|
-
'Why Remote-First is Non-Negotiable for 2026 Startups',
|
|
45
|
-
'Sustainable Growth vs Hypergrowth: The 2026 Paradigm Shift',
|
|
46
|
-
'Building in Public: How Transparency Became a Competitive Advantage',
|
|
47
|
-
'The API-First Startup: Lessons from 2026 Unicorns',
|
|
48
|
-
'From Side Project to Series A: The 2026 Playbook',
|
|
49
|
-
'Climate Tech Startups: The Hottest Sector of 2026',
|
|
50
|
-
'The Death of Traditional MVPs: Ship Faster, Learn Faster',
|
|
51
|
-
'Community-Led Growth: The New GTM Strategy for 2026',
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// Simulate blog post generation
|
|
57
|
-
if (prompt.includes('blog post about')) {
|
|
58
|
-
const titleMatch = prompt.match(/blog post about (.+)/)
|
|
59
|
-
const title = titleMatch?.[1] || 'Unknown Topic'
|
|
60
|
-
return {
|
|
61
|
-
object: {
|
|
62
|
-
text: `# ${title}\n\nThis is a comprehensive blog post about ${title}.\n\n## Introduction\n\nIn 2026, the startup landscape continues to evolve...\n\n## Key Takeaways\n\n1. Innovation is key\n2. Focus on customer value\n3. Build sustainable businesses\n\n## Conclusion\n\nThe future of startups is bright for those who adapt.`,
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return { object: { result: 'Generated content' } }
|
|
67
|
-
}),
|
|
68
|
-
generateText: vi.fn().mockImplementation(async ({ prompt }) => {
|
|
69
|
-
// Simulate blog post text generation
|
|
70
|
-
if (prompt.includes('blog post about')) {
|
|
71
|
-
const titleMatch = prompt.match(/blog post about (.+)/)
|
|
72
|
-
const title = titleMatch?.[1] || 'Unknown Topic'
|
|
73
|
-
return {
|
|
74
|
-
text: `# ${title}\n\nThis is a comprehensive blog post about ${title}.\n\n## Introduction\n\nIn 2026, the startup landscape continues to evolve rapidly. Entrepreneurs are finding new ways to build, scale, and succeed.\n\n## The State of Startups in 2026\n\nThe ecosystem has matured significantly. AI tools have become indispensable, funding patterns have shifted, and remote work is now the default.\n\n## Key Strategies for Success\n\n1. **Leverage AI Wisely** - Use AI as a multiplier, not a replacement\n2. **Build Community First** - Your early adopters are your growth engine\n3. **Focus on Unit Economics** - Hypergrowth without sustainability is dead\n4. **Embrace Transparency** - Building in public creates trust and accountability\n\n## Practical Steps\n\n- Start with a problem you deeply understand\n- Validate with paying customers, not surveys\n- Build the smallest thing that delivers value\n- Iterate based on real usage data\n\n## Conclusion\n\nBuilding a startup in 2026 requires a blend of traditional business fundamentals and modern tools. The founders who succeed will be those who can navigate this balance effectively.`,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return { text: 'Generated text content' }
|
|
78
|
-
}),
|
|
79
|
-
}))
|
|
26
|
+
// Skip AI-dependent tests if no gateway configured
|
|
27
|
+
const hasGateway = !!process.env.AI_GATEWAY_URL
|
|
80
28
|
|
|
81
29
|
// ============================================================================
|
|
82
|
-
//
|
|
30
|
+
// Real AI Tests (require gateway)
|
|
83
31
|
// ============================================================================
|
|
84
32
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
33
|
+
describe.skipIf(!hasGateway)('Batch Blog Post Generation with Real AI', () => {
|
|
34
|
+
it('generates blog post titles using real AI', async () => {
|
|
35
|
+
const result = await generateObject({
|
|
36
|
+
model: 'haiku',
|
|
37
|
+
schema: z.object({
|
|
38
|
+
titles: z.array(z.string()).describe('List of blog post titles'),
|
|
39
|
+
}),
|
|
40
|
+
prompt: 'Generate exactly 3 blog post titles about building startups.',
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(result.object.titles).toHaveLength(3)
|
|
44
|
+
expect(result.object.titles.every((t: string) => typeof t === 'string')).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('generates a single blog post using real AI', async () => {
|
|
48
|
+
const result = await generateText({
|
|
49
|
+
model: 'haiku',
|
|
50
|
+
prompt: 'Write a very short blog post intro (2-3 sentences) about TypeScript.',
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
expect(result.text).toBeDefined()
|
|
54
|
+
expect(result.text.length).toBeGreaterThan(50)
|
|
94
55
|
})
|
|
95
|
-
|
|
96
|
-
}
|
|
56
|
+
})
|
|
97
57
|
|
|
98
58
|
// ============================================================================
|
|
99
|
-
// Tests
|
|
59
|
+
// Batch Queue Mechanics Tests (use memory adapter - no AI needed)
|
|
100
60
|
// ============================================================================
|
|
101
61
|
|
|
102
|
-
describe('Batch
|
|
62
|
+
describe('Batch Queue Mechanics', () => {
|
|
63
|
+
// Use a simple handler that doesn't call real AI
|
|
64
|
+
const mockHandler = async (item: { prompt: string }) => {
|
|
65
|
+
return `Generated content for: ${item.prompt.substring(0, 50)}...`
|
|
66
|
+
}
|
|
67
|
+
|
|
103
68
|
beforeEach(() => {
|
|
104
|
-
vi.clearAllMocks()
|
|
105
69
|
clearBatches()
|
|
106
|
-
//
|
|
107
|
-
configureMemoryAdapter({})
|
|
70
|
+
// Configure with mock handler to avoid real AI calls in batch tests
|
|
71
|
+
configureMemoryAdapter({ handler: mockHandler })
|
|
108
72
|
})
|
|
109
73
|
|
|
110
74
|
afterEach(() => {
|
|
111
75
|
clearBatches()
|
|
112
76
|
})
|
|
113
77
|
|
|
114
|
-
describe('list` immediate execution', () => {
|
|
115
|
-
it('list` executes immediately and returns titles', async () => {
|
|
116
|
-
const titles = await mockList('10 blog post titles about building startups in 2026')
|
|
117
|
-
|
|
118
|
-
expect(titles).toHaveLength(10)
|
|
119
|
-
expect(titles[0]).toBe('How AI is Revolutionizing Startup Fundraising in 2026')
|
|
120
|
-
expect(titles[9]).toBe('Community-Led Growth: The New GTM Strategy for 2026')
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
78
|
describe('batch processing workflow', () => {
|
|
125
79
|
it('creates batch queue and adds items', async () => {
|
|
126
80
|
const batch = createBatch({ provider: 'openai', model: 'gpt-4o' })
|
|
127
81
|
|
|
128
|
-
const titles =
|
|
82
|
+
const titles = ['Title 1', 'Title 2', 'Title 3']
|
|
129
83
|
|
|
130
84
|
// Add each title to the batch
|
|
131
85
|
const items = titles.map((title) =>
|
|
@@ -134,45 +88,41 @@ describe('Batch Blog Post Generation', () => {
|
|
|
134
88
|
})
|
|
135
89
|
)
|
|
136
90
|
|
|
137
|
-
expect(batch.size).toBe(
|
|
138
|
-
expect(items).toHaveLength(
|
|
91
|
+
expect(batch.size).toBe(3)
|
|
92
|
+
expect(items).toHaveLength(3)
|
|
139
93
|
expect(items[0].status).toBe('pending')
|
|
140
94
|
})
|
|
141
95
|
|
|
142
96
|
it('submits batch and returns job info', async () => {
|
|
143
97
|
const batch = createBatch({ provider: 'openai', model: 'gpt-4o' })
|
|
144
98
|
|
|
145
|
-
const titles =
|
|
99
|
+
const titles = ['Title 1', 'Title 2', 'Title 3']
|
|
146
100
|
|
|
147
|
-
titles.forEach((title) =>
|
|
148
|
-
batch.add(`Write a comprehensive blog post about: ${title}`)
|
|
149
|
-
)
|
|
101
|
+
titles.forEach((title) => batch.add(`Write a comprehensive blog post about: ${title}`))
|
|
150
102
|
|
|
151
103
|
const { job, completion } = await batch.submit()
|
|
152
104
|
|
|
153
105
|
expect(job.id).toMatch(/^batch_memory_/)
|
|
154
106
|
expect(job.provider).toBe('openai')
|
|
155
|
-
expect(job.totalItems).toBe(
|
|
107
|
+
expect(job.totalItems).toBe(3)
|
|
156
108
|
expect(job.status).toBe('pending')
|
|
157
109
|
|
|
158
110
|
// Wait for completion
|
|
159
111
|
const results = await completion
|
|
160
|
-
expect(results).toHaveLength(
|
|
112
|
+
expect(results).toHaveLength(3)
|
|
161
113
|
})
|
|
162
114
|
|
|
163
115
|
it('waits for batch completion and returns results', async () => {
|
|
164
116
|
const batch = createBatch({ provider: 'openai', model: 'gpt-4o' })
|
|
165
117
|
|
|
166
|
-
const titles =
|
|
118
|
+
const titles = ['Title 1', 'Title 2', 'Title 3']
|
|
167
119
|
|
|
168
|
-
titles.forEach((title) =>
|
|
169
|
-
batch.add(`Write a comprehensive blog post about: ${title}`)
|
|
170
|
-
)
|
|
120
|
+
titles.forEach((title) => batch.add(`Write a comprehensive blog post about: ${title}`))
|
|
171
121
|
|
|
172
122
|
await batch.submit()
|
|
173
123
|
const results = await batch.wait()
|
|
174
124
|
|
|
175
|
-
expect(results).toHaveLength(
|
|
125
|
+
expect(results).toHaveLength(3)
|
|
176
126
|
expect(results.every((r) => r.status === 'completed')).toBe(true)
|
|
177
127
|
expect(results[0].result).toBeDefined()
|
|
178
128
|
})
|
|
@@ -181,9 +131,7 @@ describe('Batch Blog Post Generation', () => {
|
|
|
181
131
|
const batch = createBatch({ provider: 'openai' })
|
|
182
132
|
|
|
183
133
|
const titles = ['First', 'Second', 'Third']
|
|
184
|
-
|
|
185
|
-
batch.add(`Write about: ${title}`, { customId: `item_${i}` })
|
|
186
|
-
)
|
|
134
|
+
titles.map((title, i) => batch.add(`Write about: ${title}`, { customId: `item_${i}` }))
|
|
187
135
|
|
|
188
136
|
await batch.submit()
|
|
189
137
|
const results = await batch.wait()
|
|
@@ -196,17 +144,14 @@ describe('Batch Blog Post Generation', () => {
|
|
|
196
144
|
|
|
197
145
|
describe('withBatchQueue helper', () => {
|
|
198
146
|
it('provides convenient batch execution', async () => {
|
|
199
|
-
const titles =
|
|
147
|
+
const titles = ['Title A', 'Title B', 'Title C']
|
|
200
148
|
|
|
201
|
-
const results = await
|
|
202
|
-
(batch) =>
|
|
203
|
-
titles.map((title) =>
|
|
204
|
-
batch.add(`Write a blog post about: ${title}`)
|
|
205
|
-
),
|
|
149
|
+
const results = await withBatchQueue(
|
|
150
|
+
(batch) => titles.map((title) => batch.add(`Write a blog post about: ${title}`)),
|
|
206
151
|
{ provider: 'openai', model: 'gpt-4o' }
|
|
207
152
|
)
|
|
208
153
|
|
|
209
|
-
expect(results).toHaveLength(
|
|
154
|
+
expect(results).toHaveLength(3)
|
|
210
155
|
expect(results.every((r) => r.status === 'completed')).toBe(true)
|
|
211
156
|
})
|
|
212
157
|
})
|
|
@@ -233,8 +178,11 @@ describe('Batch Blog Post Generation', () => {
|
|
|
233
178
|
|
|
234
179
|
describe('error handling', () => {
|
|
235
180
|
it('handles partial failures', async () => {
|
|
236
|
-
// Configure adapter to fail 30% of requests
|
|
237
|
-
configureMemoryAdapter({
|
|
181
|
+
// Configure adapter to fail 30% of requests with a mock handler
|
|
182
|
+
configureMemoryAdapter({
|
|
183
|
+
failureRate: 0.3,
|
|
184
|
+
handler: async (item: { prompt: string }) => `Result for: ${item.prompt}`,
|
|
185
|
+
})
|
|
238
186
|
|
|
239
187
|
const batch = createBatch({ provider: 'openai' })
|
|
240
188
|
|
|
@@ -280,9 +228,11 @@ describe('Batch Blog Post Generation', () => {
|
|
|
280
228
|
|
|
281
229
|
describe('batch with custom handler', () => {
|
|
282
230
|
it('uses custom handler for processing', async () => {
|
|
283
|
-
|
|
231
|
+
let callCount = 0
|
|
232
|
+
const customHandler = async (item: { prompt: string }) => {
|
|
233
|
+
callCount++
|
|
284
234
|
return `Custom result for: ${item.prompt}`
|
|
285
|
-
}
|
|
235
|
+
}
|
|
286
236
|
|
|
287
237
|
configureMemoryAdapter({ handler: customHandler })
|
|
288
238
|
|
|
@@ -293,17 +243,20 @@ describe('Batch Blog Post Generation', () => {
|
|
|
293
243
|
await batch.submit()
|
|
294
244
|
const results = await batch.wait()
|
|
295
245
|
|
|
296
|
-
expect(
|
|
246
|
+
expect(callCount).toBe(2)
|
|
297
247
|
expect(results[0].result).toBe('Custom result for: Topic 1')
|
|
298
248
|
expect(results[1].result).toBe('Custom result for: Topic 2')
|
|
299
249
|
})
|
|
300
250
|
})
|
|
301
251
|
|
|
302
|
-
describe('full workflow: list
|
|
252
|
+
describe('full workflow: list -> map -> batch', () => {
|
|
303
253
|
it('executes the complete blog post generation workflow', async () => {
|
|
304
|
-
// Step 1:
|
|
305
|
-
const titles =
|
|
306
|
-
|
|
254
|
+
// Step 1: Simulate getting titles (in real usage, this would be AI-generated)
|
|
255
|
+
const titles = [
|
|
256
|
+
'How AI is Revolutionizing Startup Fundraising',
|
|
257
|
+
'The Rise of Solo Founders',
|
|
258
|
+
'Remote-First is Non-Negotiable',
|
|
259
|
+
]
|
|
307
260
|
|
|
308
261
|
// Step 2: Create batch for blog posts (deferred)
|
|
309
262
|
const batch = createBatch({
|
|
@@ -320,28 +273,26 @@ describe('Batch Blog Post Generation', () => {
|
|
|
320
273
|
})
|
|
321
274
|
)
|
|
322
275
|
|
|
323
|
-
expect(batch.size).toBe(
|
|
276
|
+
expect(batch.size).toBe(3)
|
|
324
277
|
expect(blogItems.every((item) => item.status === 'pending')).toBe(true)
|
|
325
278
|
|
|
326
279
|
// Step 4: Submit the batch
|
|
327
280
|
const { job, completion } = await batch.submit()
|
|
328
281
|
|
|
329
282
|
expect(job.id).toBeDefined()
|
|
330
|
-
expect(job.totalItems).toBe(
|
|
283
|
+
expect(job.totalItems).toBe(3)
|
|
331
284
|
expect(batch.isSubmitted).toBe(true)
|
|
332
285
|
|
|
333
286
|
// Step 5: Wait for results
|
|
334
287
|
const results = await completion
|
|
335
288
|
|
|
336
|
-
expect(results).toHaveLength(
|
|
289
|
+
expect(results).toHaveLength(3)
|
|
337
290
|
expect(results.every((r) => r.status === 'completed')).toBe(true)
|
|
338
291
|
|
|
339
|
-
// Verify results have
|
|
292
|
+
// Verify results have content
|
|
340
293
|
for (const result of results) {
|
|
341
294
|
expect(result.result).toBeDefined()
|
|
342
295
|
expect(typeof result.result).toBe('string')
|
|
343
|
-
// Blog posts should have some content
|
|
344
|
-
expect((result.result as string).length).toBeGreaterThan(100)
|
|
345
296
|
}
|
|
346
297
|
|
|
347
298
|
// Verify items are updated after completion
|
|
@@ -351,9 +302,12 @@ describe('Batch Blog Post Generation', () => {
|
|
|
351
302
|
})
|
|
352
303
|
|
|
353
304
|
describe('Provider-specific batch behavior', () => {
|
|
305
|
+
// Use a simple handler that doesn't call real AI
|
|
306
|
+
const mockHandler = async () => 'Test result'
|
|
307
|
+
|
|
354
308
|
beforeEach(() => {
|
|
355
309
|
clearBatches()
|
|
356
|
-
configureMemoryAdapter({})
|
|
310
|
+
configureMemoryAdapter({ handler: mockHandler })
|
|
357
311
|
})
|
|
358
312
|
|
|
359
313
|
it('uses specified provider', async () => {
|
|
@@ -372,7 +326,11 @@ describe('Provider-specific batch behavior', () => {
|
|
|
372
326
|
})
|
|
373
327
|
|
|
374
328
|
it('respects model configuration', async () => {
|
|
375
|
-
|
|
329
|
+
let handlerCalled = false
|
|
330
|
+
const customHandler = async () => {
|
|
331
|
+
handlerCalled = true
|
|
332
|
+
return 'Result'
|
|
333
|
+
}
|
|
376
334
|
configureMemoryAdapter({ handler: customHandler })
|
|
377
335
|
|
|
378
336
|
const batch = createBatch({ provider: 'openai', model: 'gpt-4o-mini' })
|
|
@@ -382,6 +340,6 @@ describe('Provider-specific batch behavior', () => {
|
|
|
382
340
|
|
|
383
341
|
// The model should be passed to the handler via batch options
|
|
384
342
|
// (memory adapter doesn't use it, but real adapters would)
|
|
385
|
-
expect(
|
|
343
|
+
expect(handlerCalled).toBe(true)
|
|
386
344
|
})
|
|
387
345
|
})
|