ai-functions 2.1.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +90 -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 +176 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +685 -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/sandbox.d.ts +36 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +44 -0
- package/dist/sandbox.js.map +1 -0
- 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 +155 -19
- 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 +29 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +71 -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 +874 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +122 -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/sandbox.ts +52 -0
- 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 +186 -27
- 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/fill-template.test.ts +89 -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/sandbox-execution.test.ts +155 -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
|
@@ -2,113 +2,49 @@
|
|
|
2
2
|
* Tests for core AI functions
|
|
3
3
|
*
|
|
4
4
|
* These tests verify the API contracts for each function.
|
|
5
|
-
* Tests
|
|
5
|
+
* Tests require actual AI calls via the Cloudflare AI Gateway.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { describe, it, expect
|
|
9
|
-
import {
|
|
8
|
+
import { describe, it, expect } from 'vitest'
|
|
9
|
+
import { generateText, generateObject } from '../src/generate.js'
|
|
10
|
+
import { z } from 'zod'
|
|
10
11
|
|
|
11
12
|
// Skip tests if no gateway configured
|
|
12
|
-
const hasGateway = !!process.env.AI_GATEWAY_URL
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// Mock implementations for unit tests
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
// Mock generate function that all others should call
|
|
19
|
-
const mockGenerate = vi.fn()
|
|
13
|
+
const hasGateway = !!process.env.AI_GATEWAY_URL
|
|
20
14
|
|
|
21
15
|
// ============================================================================
|
|
22
16
|
// ai() - Direct text generation
|
|
23
17
|
// ============================================================================
|
|
24
18
|
|
|
25
|
-
describe('ai()', () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
it('should accept a string prompt', async () => {
|
|
32
|
-
const ai = createMockAi()
|
|
33
|
-
const result = await ai('Write a haiku')
|
|
34
|
-
|
|
35
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
36
|
-
'text',
|
|
37
|
-
'Write a haiku',
|
|
38
|
-
expect.any(Object)
|
|
39
|
-
)
|
|
40
|
-
expect(result).toBe('Generated text')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should accept tagged template literal', async () => {
|
|
44
|
-
const ai = createMockAi()
|
|
45
|
-
const topic = 'TypeScript'
|
|
46
|
-
const result = await ai`Write about ${topic}`
|
|
19
|
+
describe.skipIf(!hasGateway)('ai()', () => {
|
|
20
|
+
it('should generate text from a string prompt', async () => {
|
|
21
|
+
const result = await generateText({
|
|
22
|
+
model: 'haiku',
|
|
23
|
+
prompt: 'Say "hello world" and nothing else.',
|
|
24
|
+
})
|
|
47
25
|
|
|
48
|
-
expect(
|
|
49
|
-
|
|
50
|
-
expect(
|
|
51
|
-
expect(result).toBe('Generated text')
|
|
26
|
+
expect(result.text).toBeDefined()
|
|
27
|
+
expect(typeof result.text).toBe('string')
|
|
28
|
+
expect(result.text.toLowerCase()).toContain('hello')
|
|
52
29
|
})
|
|
53
30
|
|
|
54
|
-
it('should
|
|
55
|
-
const
|
|
56
|
-
|
|
31
|
+
it('should respect model parameter', async () => {
|
|
32
|
+
const result = await generateText({
|
|
33
|
+
model: 'haiku',
|
|
34
|
+
prompt: 'Respond with just the word "yes".',
|
|
35
|
+
})
|
|
57
36
|
|
|
58
|
-
expect(
|
|
59
|
-
|
|
60
|
-
'test',
|
|
61
|
-
expect.objectContaining({ model: 'claude-opus-4-5' })
|
|
62
|
-
)
|
|
37
|
+
expect(result.text).toBeDefined()
|
|
38
|
+
expect(result.text.toLowerCase()).toContain('yes')
|
|
63
39
|
})
|
|
64
40
|
|
|
65
41
|
it('should return string type', async () => {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// ============================================================================
|
|
73
|
-
// summarize() - Condense text
|
|
74
|
-
// ============================================================================
|
|
75
|
-
|
|
76
|
-
describe('summarize()', () => {
|
|
77
|
-
beforeEach(() => {
|
|
78
|
-
mockGenerate.mockReset()
|
|
79
|
-
mockGenerate.mockResolvedValue('Summary of content')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should accept text to summarize', async () => {
|
|
83
|
-
const summarize = createMockSummarize()
|
|
84
|
-
const result = await summarize`${longArticle}`
|
|
85
|
-
|
|
86
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
87
|
-
'summary',
|
|
88
|
-
expect.stringContaining('article'),
|
|
89
|
-
expect.any(Object)
|
|
90
|
-
)
|
|
91
|
-
expect(result).toBe('Summary of content')
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('should support length option', async () => {
|
|
95
|
-
const summarize = createMockSummarize()
|
|
96
|
-
await summarize`${longArticle}`({ length: 'short' })
|
|
97
|
-
|
|
98
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
99
|
-
'summary',
|
|
100
|
-
expect.any(String),
|
|
101
|
-
expect.objectContaining({ length: 'short' })
|
|
102
|
-
)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('should support audience option', async () => {
|
|
106
|
-
const summarize = createMockSummarize()
|
|
107
|
-
await summarize`${technicalReport}${{ audience: 'executives', focus: 'business impact' }}`
|
|
42
|
+
const result = await generateText({
|
|
43
|
+
model: 'haiku',
|
|
44
|
+
prompt: 'Say "test".',
|
|
45
|
+
})
|
|
108
46
|
|
|
109
|
-
|
|
110
|
-
expect(prompt).toContain('executives')
|
|
111
|
-
expect(prompt).toContain('business impact')
|
|
47
|
+
expect(typeof result.text).toBe('string')
|
|
112
48
|
})
|
|
113
49
|
})
|
|
114
50
|
|
|
@@ -116,51 +52,41 @@ describe('summarize()', () => {
|
|
|
116
52
|
// is() - Boolean classification
|
|
117
53
|
// ============================================================================
|
|
118
54
|
|
|
119
|
-
describe('is()', () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const result = await is`${'hello@example.com'} a valid email?`
|
|
129
|
-
expect(result).toBe(true)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('should return boolean false', async () => {
|
|
133
|
-
mockGenerate.mockResolvedValue(false)
|
|
134
|
-
const is = createMockIs()
|
|
55
|
+
describe.skipIf(!hasGateway)('is()', () => {
|
|
56
|
+
it('should return boolean true for valid classification', async () => {
|
|
57
|
+
const result = await generateObject({
|
|
58
|
+
model: 'haiku',
|
|
59
|
+
schema: z.object({
|
|
60
|
+
isValid: z.boolean().describe('Is this a valid email address?'),
|
|
61
|
+
}),
|
|
62
|
+
prompt: 'Is "hello@example.com" a valid email address?',
|
|
63
|
+
})
|
|
135
64
|
|
|
136
|
-
|
|
137
|
-
expect(result).toBe(false)
|
|
65
|
+
expect(result.object.isValid).toBe(true)
|
|
138
66
|
})
|
|
139
67
|
|
|
140
|
-
it('should
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
68
|
+
it('should return boolean false for invalid classification', async () => {
|
|
69
|
+
const result = await generateObject({
|
|
70
|
+
model: 'haiku',
|
|
71
|
+
schema: z.object({
|
|
72
|
+
isValid: z.boolean().describe('Is this a valid email address?'),
|
|
73
|
+
}),
|
|
74
|
+
prompt: 'Is "not-an-email" a valid email address?',
|
|
75
|
+
})
|
|
145
76
|
|
|
146
|
-
expect(
|
|
147
|
-
'boolean',
|
|
148
|
-
expect.stringContaining('positive sentiment'),
|
|
149
|
-
expect.any(Object)
|
|
150
|
-
)
|
|
77
|
+
expect(result.object.isValid).toBe(false)
|
|
151
78
|
})
|
|
152
79
|
|
|
153
|
-
it('should
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
80
|
+
it('should handle sentiment classification', async () => {
|
|
81
|
+
const result = await generateObject({
|
|
82
|
+
model: 'haiku',
|
|
83
|
+
schema: z.object({
|
|
84
|
+
isPositive: z.boolean().describe('Is this positive sentiment?'),
|
|
85
|
+
}),
|
|
86
|
+
prompt: 'Is "I love this product, it\'s amazing!" positive sentiment?',
|
|
87
|
+
})
|
|
158
88
|
|
|
159
|
-
expect(
|
|
160
|
-
'boolean',
|
|
161
|
-
expect.any(String),
|
|
162
|
-
expect.objectContaining({ model: 'claude-opus-4-5' })
|
|
163
|
-
)
|
|
89
|
+
expect(result.object.isPositive).toBe(true)
|
|
164
90
|
})
|
|
165
91
|
})
|
|
166
92
|
|
|
@@ -168,41 +94,31 @@ describe('is()', () => {
|
|
|
168
94
|
// list() - Generate a list
|
|
169
95
|
// ============================================================================
|
|
170
96
|
|
|
171
|
-
describe('list()', () => {
|
|
172
|
-
beforeEach(() => {
|
|
173
|
-
mockGenerate.mockReset()
|
|
174
|
-
mockGenerate.mockResolvedValue(['Item 1', 'Item 2', 'Item 3'])
|
|
175
|
-
})
|
|
176
|
-
|
|
97
|
+
describe.skipIf(!hasGateway)('list()', () => {
|
|
177
98
|
it('should return an array of strings', async () => {
|
|
178
|
-
const
|
|
179
|
-
|
|
99
|
+
const result = await generateObject({
|
|
100
|
+
model: 'haiku',
|
|
101
|
+
schema: z.object({
|
|
102
|
+
items: z.array(z.string()).describe('List of 3 colors'),
|
|
103
|
+
}),
|
|
104
|
+
prompt: 'List exactly 3 colors.',
|
|
105
|
+
})
|
|
180
106
|
|
|
181
|
-
expect(Array.isArray(result)).toBe(true)
|
|
182
|
-
expect(result).
|
|
183
|
-
expect(result.every((item: unknown) => typeof item === 'string')).toBe(true)
|
|
107
|
+
expect(Array.isArray(result.object.items)).toBe(true)
|
|
108
|
+
expect(result.object.items.length).toBeGreaterThanOrEqual(1)
|
|
109
|
+
expect(result.object.items.every((item: unknown) => typeof item === 'string')).toBe(true)
|
|
184
110
|
})
|
|
185
111
|
|
|
186
112
|
it('should respect count in prompt', async () => {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('should support count option', async () => {
|
|
198
|
-
const list = createMockList()
|
|
199
|
-
await list('startup ideas', { count: 10 })
|
|
113
|
+
const result = await generateObject({
|
|
114
|
+
model: 'haiku',
|
|
115
|
+
schema: z.object({
|
|
116
|
+
items: z.array(z.string()).describe('List of items'),
|
|
117
|
+
}),
|
|
118
|
+
prompt: 'List exactly 5 fruits.',
|
|
119
|
+
})
|
|
200
120
|
|
|
201
|
-
expect(
|
|
202
|
-
'list',
|
|
203
|
-
'startup ideas',
|
|
204
|
-
expect.objectContaining({ count: 10 })
|
|
205
|
-
)
|
|
121
|
+
expect(result.object.items.length).toBe(5)
|
|
206
122
|
})
|
|
207
123
|
})
|
|
208
124
|
|
|
@@ -210,40 +126,21 @@ describe('list()', () => {
|
|
|
210
126
|
// lists() - Generate multiple named lists
|
|
211
127
|
// ============================================================================
|
|
212
128
|
|
|
213
|
-
describe('lists()', () => {
|
|
214
|
-
beforeEach(() => {
|
|
215
|
-
mockGenerate.mockReset()
|
|
216
|
-
mockGenerate.mockResolvedValue({
|
|
217
|
-
pros: ['Pro 1', 'Pro 2'],
|
|
218
|
-
cons: ['Con 1', 'Con 2'],
|
|
219
|
-
})
|
|
220
|
-
})
|
|
221
|
-
|
|
129
|
+
describe.skipIf(!hasGateway)('lists()', () => {
|
|
222
130
|
it('should return named lists object', async () => {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
it('should support SWOT analysis format', async () => {
|
|
233
|
-
mockGenerate.mockResolvedValue({
|
|
234
|
-
strengths: ['S1'],
|
|
235
|
-
weaknesses: ['W1'],
|
|
236
|
-
opportunities: ['O1'],
|
|
237
|
-
threats: ['T1'],
|
|
131
|
+
const result = await generateObject({
|
|
132
|
+
model: 'haiku',
|
|
133
|
+
schema: z.object({
|
|
134
|
+
pros: z.array(z.string()).describe('List of pros/advantages'),
|
|
135
|
+
cons: z.array(z.string()).describe('List of cons/disadvantages'),
|
|
136
|
+
}),
|
|
137
|
+
prompt: 'List 2 pros and 2 cons of working remotely.',
|
|
238
138
|
})
|
|
239
139
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
expect(result).
|
|
244
|
-
expect(result).toHaveProperty('weaknesses')
|
|
245
|
-
expect(result).toHaveProperty('opportunities')
|
|
246
|
-
expect(result).toHaveProperty('threats')
|
|
140
|
+
expect(result.object).toHaveProperty('pros')
|
|
141
|
+
expect(result.object).toHaveProperty('cons')
|
|
142
|
+
expect(Array.isArray(result.object.pros)).toBe(true)
|
|
143
|
+
expect(Array.isArray(result.object.cons)).toBe(true)
|
|
247
144
|
})
|
|
248
145
|
})
|
|
249
146
|
|
|
@@ -251,37 +148,39 @@ describe('lists()', () => {
|
|
|
251
148
|
// extract() - Extract from text
|
|
252
149
|
// ============================================================================
|
|
253
150
|
|
|
254
|
-
describe('extract()', () => {
|
|
255
|
-
beforeEach(() => {
|
|
256
|
-
mockGenerate.mockReset()
|
|
257
|
-
mockGenerate.mockResolvedValue(['John Smith', 'Jane Doe'])
|
|
258
|
-
})
|
|
259
|
-
|
|
151
|
+
describe.skipIf(!hasGateway)('extract()', () => {
|
|
260
152
|
it('should extract items from text', async () => {
|
|
261
|
-
const
|
|
262
|
-
|
|
153
|
+
const result = await generateObject({
|
|
154
|
+
model: 'haiku',
|
|
155
|
+
schema: z.object({
|
|
156
|
+
names: z.array(z.string()).describe('Person names mentioned in the text'),
|
|
157
|
+
}),
|
|
158
|
+
prompt:
|
|
159
|
+
'Extract all person names from: "John Smith met with Jane Doe yesterday. Bob was also there."',
|
|
160
|
+
})
|
|
263
161
|
|
|
264
|
-
expect(Array.isArray(result)).toBe(true)
|
|
265
|
-
expect(result).
|
|
266
|
-
expect(result).toContain('Jane Doe')
|
|
162
|
+
expect(Array.isArray(result.object.names)).toBe(true)
|
|
163
|
+
expect(result.object.names.length).toBeGreaterThanOrEqual(2)
|
|
267
164
|
})
|
|
268
165
|
|
|
269
166
|
it('should support schema for structured extraction', async () => {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
expect(result
|
|
167
|
+
const result = await generateObject({
|
|
168
|
+
model: 'haiku',
|
|
169
|
+
schema: z.object({
|
|
170
|
+
companies: z.array(
|
|
171
|
+
z.object({
|
|
172
|
+
name: z.string().describe('Company name'),
|
|
173
|
+
role: z.enum(['competitor', 'partner', 'customer']).describe('Role mentioned'),
|
|
174
|
+
})
|
|
175
|
+
),
|
|
176
|
+
}),
|
|
177
|
+
prompt:
|
|
178
|
+
'Extract companies from: "Our competitor Acme Corp launched a new product. Our partner Beta Inc helped with distribution."',
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
expect(result.object.companies.length).toBeGreaterThanOrEqual(2)
|
|
182
|
+
expect(result.object.companies[0]).toHaveProperty('name')
|
|
183
|
+
expect(result.object.companies[0]).toHaveProperty('role')
|
|
285
184
|
})
|
|
286
185
|
})
|
|
287
186
|
|
|
@@ -289,40 +188,26 @@ describe('extract()', () => {
|
|
|
289
188
|
// write() - Generate text content
|
|
290
189
|
// ============================================================================
|
|
291
190
|
|
|
292
|
-
describe('write()', () => {
|
|
293
|
-
beforeEach(() => {
|
|
294
|
-
mockGenerate.mockReset()
|
|
295
|
-
mockGenerate.mockResolvedValue('Generated content here...')
|
|
296
|
-
})
|
|
297
|
-
|
|
191
|
+
describe.skipIf(!hasGateway)('write()', () => {
|
|
298
192
|
it('should generate text content', async () => {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
expect(result.length).toBeGreaterThan(0)
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('should support tone option', async () => {
|
|
307
|
-
const write = createMockWrite()
|
|
308
|
-
await write('blog post', { tone: 'casual', topic: 'TypeScript' })
|
|
193
|
+
const result = await generateText({
|
|
194
|
+
model: 'haiku',
|
|
195
|
+
prompt: 'Write a short greeting message (1-2 sentences).',
|
|
196
|
+
})
|
|
309
197
|
|
|
310
|
-
expect(
|
|
311
|
-
|
|
312
|
-
'blog post',
|
|
313
|
-
expect.objectContaining({ tone: 'casual' })
|
|
314
|
-
)
|
|
198
|
+
expect(typeof result.text).toBe('string')
|
|
199
|
+
expect(result.text.length).toBeGreaterThan(0)
|
|
315
200
|
})
|
|
316
201
|
|
|
317
|
-
it('should support
|
|
318
|
-
const
|
|
319
|
-
|
|
202
|
+
it('should support system prompt for tone', async () => {
|
|
203
|
+
const result = await generateText({
|
|
204
|
+
model: 'haiku',
|
|
205
|
+
system: 'You write in a casual, friendly tone.',
|
|
206
|
+
prompt: 'Write a one-sentence welcome message.',
|
|
207
|
+
})
|
|
320
208
|
|
|
321
|
-
expect(
|
|
322
|
-
|
|
323
|
-
'article',
|
|
324
|
-
expect.objectContaining({ length: 'long' })
|
|
325
|
-
)
|
|
209
|
+
expect(typeof result.text).toBe('string')
|
|
210
|
+
expect(result.text.length).toBeGreaterThan(0)
|
|
326
211
|
})
|
|
327
212
|
})
|
|
328
213
|
|
|
@@ -330,45 +215,28 @@ describe('write()', () => {
|
|
|
330
215
|
// code() - Generate code
|
|
331
216
|
// ============================================================================
|
|
332
217
|
|
|
333
|
-
describe('code()', () => {
|
|
334
|
-
beforeEach(() => {
|
|
335
|
-
mockGenerate.mockReset()
|
|
336
|
-
mockGenerate.mockResolvedValue('function validate(email) { return email.includes("@"); }')
|
|
337
|
-
})
|
|
338
|
-
|
|
218
|
+
describe.skipIf(!hasGateway)('code()', () => {
|
|
339
219
|
it('should generate code', async () => {
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
it('should support language option', async () => {
|
|
348
|
-
const code = createMockCode()
|
|
349
|
-
await code('REST API endpoints', { language: 'typescript' })
|
|
220
|
+
const result = await generateText({
|
|
221
|
+
model: 'haiku',
|
|
222
|
+
system: 'You are a code generator. Output only code, no explanations.',
|
|
223
|
+
prompt: 'Write a JavaScript function called isEven that returns true if a number is even.',
|
|
224
|
+
})
|
|
350
225
|
|
|
351
|
-
expect(
|
|
352
|
-
|
|
353
|
-
'REST API endpoints',
|
|
354
|
-
expect.objectContaining({ language: 'typescript' })
|
|
355
|
-
)
|
|
226
|
+
expect(typeof result.text).toBe('string')
|
|
227
|
+
expect(result.text).toContain('function')
|
|
356
228
|
})
|
|
357
229
|
|
|
358
|
-
it('should
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
await code`marketing website${{ requirements }}`
|
|
230
|
+
it('should generate TypeScript code', async () => {
|
|
231
|
+
const result = await generateText({
|
|
232
|
+
model: 'haiku',
|
|
233
|
+
system: 'You are a TypeScript code generator. Output only code, no explanations.',
|
|
234
|
+
prompt:
|
|
235
|
+
'Write a TypeScript function called add that takes two numbers and returns their sum. Include type annotations.',
|
|
236
|
+
})
|
|
367
237
|
|
|
368
|
-
|
|
369
|
-
expect(
|
|
370
|
-
expect(prompt).toContain('- home')
|
|
371
|
-
expect(prompt).toContain('features:')
|
|
238
|
+
expect(typeof result.text).toBe('string')
|
|
239
|
+
expect(result.text).toContain('number')
|
|
372
240
|
})
|
|
373
241
|
})
|
|
374
242
|
|
|
@@ -376,319 +244,55 @@ describe('code()', () => {
|
|
|
376
244
|
// diagram() - Generate diagrams
|
|
377
245
|
// ============================================================================
|
|
378
246
|
|
|
379
|
-
describe('diagram()', () => {
|
|
380
|
-
beforeEach(() => {
|
|
381
|
-
mockGenerate.mockReset()
|
|
382
|
-
mockGenerate.mockResolvedValue('graph TD\n A --> B\n B --> C')
|
|
383
|
-
})
|
|
384
|
-
|
|
247
|
+
describe.skipIf(!hasGateway)('diagram()', () => {
|
|
385
248
|
it('should generate mermaid diagrams', async () => {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
it('should support format option', async () => {
|
|
394
|
-
const diagram = createMockDiagram()
|
|
395
|
-
await diagram('database schema', { format: 'mermaid', type: 'erd' })
|
|
396
|
-
|
|
397
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
398
|
-
'diagram',
|
|
399
|
-
'database schema',
|
|
400
|
-
expect.objectContaining({ format: 'mermaid', type: 'erd' })
|
|
401
|
-
)
|
|
402
|
-
})
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
// ============================================================================
|
|
406
|
-
// slides() - Generate presentations
|
|
407
|
-
// ============================================================================
|
|
408
|
-
|
|
409
|
-
describe('slides()', () => {
|
|
410
|
-
beforeEach(() => {
|
|
411
|
-
mockGenerate.mockReset()
|
|
412
|
-
mockGenerate.mockResolvedValue('---\ntheme: default\n---\n\n# Slide 1\n\nContent here')
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
it('should generate slidev-format markdown', async () => {
|
|
416
|
-
const slides = createMockSlides()
|
|
417
|
-
const result = await slides`${topic}`
|
|
418
|
-
|
|
419
|
-
expect(typeof result).toBe('string')
|
|
420
|
-
expect(result).toContain('---')
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
it('should support format option', async () => {
|
|
424
|
-
const slides = createMockSlides()
|
|
425
|
-
await slides('quarterly review', { format: 'marp', slides: 12 })
|
|
426
|
-
|
|
427
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
428
|
-
'slides',
|
|
429
|
-
'quarterly review',
|
|
430
|
-
expect.objectContaining({ format: 'marp', slides: 12 })
|
|
431
|
-
)
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
it('should support speaker notes', async () => {
|
|
435
|
-
const slides = createMockSlides()
|
|
436
|
-
await slides('workshop', { includeNotes: true, duration: '2 hours' })
|
|
437
|
-
|
|
438
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
439
|
-
'slides',
|
|
440
|
-
'workshop',
|
|
441
|
-
expect.objectContaining({ includeNotes: true })
|
|
442
|
-
)
|
|
443
|
-
})
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
// ============================================================================
|
|
447
|
-
// image() - Generate images
|
|
448
|
-
// ============================================================================
|
|
449
|
-
|
|
450
|
-
describe('image()', () => {
|
|
451
|
-
beforeEach(() => {
|
|
452
|
-
mockGenerate.mockReset()
|
|
453
|
-
mockGenerate.mockResolvedValue(Buffer.from('fake-image-data'))
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
it('should generate image buffer', async () => {
|
|
457
|
-
const image = createMockImage()
|
|
458
|
-
const result = await image`minimalist logo for ${companyName}`
|
|
459
|
-
|
|
460
|
-
expect(Buffer.isBuffer(result)).toBe(true)
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
it('should support size option', async () => {
|
|
464
|
-
const image = createMockImage()
|
|
465
|
-
await image('robot reading a book', { size: '1024x1024', style: 'cartoon' })
|
|
466
|
-
|
|
467
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
468
|
-
'image',
|
|
469
|
-
'robot reading a book',
|
|
470
|
-
expect.objectContaining({ size: '1024x1024', style: 'cartoon' })
|
|
471
|
-
)
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
// ============================================================================
|
|
476
|
-
// video() - Generate videos
|
|
477
|
-
// ============================================================================
|
|
478
|
-
|
|
479
|
-
describe('video()', () => {
|
|
480
|
-
beforeEach(() => {
|
|
481
|
-
mockGenerate.mockReset()
|
|
482
|
-
mockGenerate.mockResolvedValue(Buffer.from('fake-video-data'))
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
it('should generate video buffer', async () => {
|
|
486
|
-
const video = createMockVideo()
|
|
487
|
-
const result = await video`product demo for ${productName}`
|
|
488
|
-
|
|
489
|
-
expect(Buffer.isBuffer(result)).toBe(true)
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
it('should support duration and aspect options', async () => {
|
|
493
|
-
const video = createMockVideo()
|
|
494
|
-
await video('promotional video', { duration: 30, aspect: '16:9', style: 'motion graphics' })
|
|
495
|
-
|
|
496
|
-
expect(mockGenerate).toHaveBeenCalledWith(
|
|
497
|
-
'video',
|
|
498
|
-
'promotional video',
|
|
499
|
-
expect.objectContaining({ duration: 30, aspect: '16:9' })
|
|
500
|
-
)
|
|
501
|
-
})
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
// ============================================================================
|
|
505
|
-
// research() - Agentic research
|
|
506
|
-
// ============================================================================
|
|
507
|
-
|
|
508
|
-
describe('research()', () => {
|
|
509
|
-
beforeEach(() => {
|
|
510
|
-
mockGenerate.mockReset()
|
|
511
|
-
mockGenerate.mockResolvedValue({
|
|
512
|
-
summary: 'Key findings...',
|
|
513
|
-
sources: [{ url: 'https://example.com', title: 'Source 1' }],
|
|
514
|
-
findings: ['Finding 1', 'Finding 2'],
|
|
515
|
-
confidence: 0.85,
|
|
249
|
+
const result = await generateText({
|
|
250
|
+
model: 'haiku',
|
|
251
|
+
system:
|
|
252
|
+
'You generate Mermaid diagram code. Output only the mermaid code, no explanations or markdown fences.',
|
|
253
|
+
prompt: 'Create a simple flowchart with Start -> Process -> End.',
|
|
516
254
|
})
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
it('should return structured research results', async () => {
|
|
520
|
-
const research = createMockResearch()
|
|
521
|
-
const result = await research`${topic}`
|
|
522
|
-
|
|
523
|
-
expect(result).toHaveProperty('summary')
|
|
524
|
-
expect(result).toHaveProperty('sources')
|
|
525
|
-
expect(result).toHaveProperty('findings')
|
|
526
|
-
expect(result).toHaveProperty('confidence')
|
|
527
|
-
})
|
|
528
|
-
|
|
529
|
-
it('should support depth option', async () => {
|
|
530
|
-
const research = createMockResearch()
|
|
531
|
-
await research`market size for AI tools`({ depth: 'thorough' })
|
|
532
255
|
|
|
533
|
-
expect(
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
expect.objectContaining({ depth: 'thorough' })
|
|
537
|
-
)
|
|
256
|
+
expect(typeof result.text).toBe('string')
|
|
257
|
+
// Mermaid flowcharts use arrows like --> or ->
|
|
258
|
+
expect(result.text).toMatch(/(-->|->)/)
|
|
538
259
|
})
|
|
539
260
|
})
|
|
540
261
|
|
|
541
262
|
// ============================================================================
|
|
542
|
-
//
|
|
263
|
+
// Structured object generation
|
|
543
264
|
// ============================================================================
|
|
544
265
|
|
|
545
|
-
describe('
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
expect(result).toBeDefined()
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
it('should handle complex multi-function tasks', async () => {
|
|
559
|
-
mockGenerate.mockResolvedValue({
|
|
560
|
-
summary: 'Article summary',
|
|
561
|
-
people: ['John', 'Jane'],
|
|
562
|
-
actionItems: ['Review', 'Follow up'],
|
|
266
|
+
describe.skipIf(!hasGateway)('generateObject()', () => {
|
|
267
|
+
it('should generate a structured recipe', async () => {
|
|
268
|
+
const result = await generateObject({
|
|
269
|
+
model: 'haiku',
|
|
270
|
+
schema: z.object({
|
|
271
|
+
name: z.string().describe('Recipe name'),
|
|
272
|
+
servings: z.number().describe('Number of servings'),
|
|
273
|
+
ingredients: z.array(z.string()).describe('List of ingredients'),
|
|
274
|
+
}),
|
|
275
|
+
prompt: 'Generate a simple 2-ingredient recipe.',
|
|
563
276
|
})
|
|
564
277
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
278
|
+
expect(result.object).toHaveProperty('name')
|
|
279
|
+
expect(result.object).toHaveProperty('servings')
|
|
280
|
+
expect(result.object).toHaveProperty('ingredients')
|
|
281
|
+
expect(typeof result.object.name).toBe('string')
|
|
282
|
+
expect(typeof result.object.servings).toBe('number')
|
|
283
|
+
expect(Array.isArray(result.object.ingredients)).toBe(true)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('should respect enum constraints', async () => {
|
|
287
|
+
const result = await generateObject({
|
|
288
|
+
model: 'haiku',
|
|
289
|
+
schema: z.object({
|
|
290
|
+
sentiment: z.enum(['positive', 'negative', 'neutral']).describe('Sentiment of the text'),
|
|
291
|
+
}),
|
|
292
|
+
prompt: 'Analyze the sentiment of: "I had a wonderful day today!"',
|
|
293
|
+
})
|
|
578
294
|
|
|
579
|
-
|
|
580
|
-
expect(
|
|
295
|
+
expect(['positive', 'negative', 'neutral']).toContain(result.object.sentiment)
|
|
296
|
+
expect(result.object.sentiment).toBe('positive')
|
|
581
297
|
})
|
|
582
298
|
})
|
|
583
|
-
|
|
584
|
-
// ============================================================================
|
|
585
|
-
// Helper functions to create mock implementations
|
|
586
|
-
// ============================================================================
|
|
587
|
-
|
|
588
|
-
function createMockAi() {
|
|
589
|
-
return createMockFunction('text')
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function createMockSummarize() {
|
|
593
|
-
return createMockFunction('summary')
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function createMockIs() {
|
|
597
|
-
return createMockFunction('boolean')
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function createMockList() {
|
|
601
|
-
return createMockFunction('list')
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function createMockLists() {
|
|
605
|
-
return createMockFunction('lists')
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function createMockExtract() {
|
|
609
|
-
return createMockFunction('extract')
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function createMockWrite() {
|
|
613
|
-
return createMockFunction('text')
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function createMockCode() {
|
|
617
|
-
return createMockFunction('code')
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function createMockDiagram() {
|
|
621
|
-
return createMockFunction('diagram')
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
function createMockSlides() {
|
|
625
|
-
return createMockFunction('slides')
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
function createMockImage() {
|
|
629
|
-
return createMockFunction('image')
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function createMockVideo() {
|
|
633
|
-
return createMockFunction('video')
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function createMockResearch() {
|
|
637
|
-
return createMockFunction('research')
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function createMockDo() {
|
|
641
|
-
return createMockFunction('do')
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function createMockFunction(type: string) {
|
|
645
|
-
return function (promptOrStrings: string | TemplateStringsArray, ...args: unknown[]) {
|
|
646
|
-
let prompt: string
|
|
647
|
-
|
|
648
|
-
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
|
|
649
|
-
// Tagged template
|
|
650
|
-
prompt = (promptOrStrings as TemplateStringsArray).reduce((acc, str, i) => {
|
|
651
|
-
const value = args[i]
|
|
652
|
-
if (value === undefined) return acc + str
|
|
653
|
-
if (typeof value === 'object' && value !== null) {
|
|
654
|
-
// Convert objects to YAML for readability (matches real implementation)
|
|
655
|
-
return acc + str + '\n' + yamlStringify(value).trim()
|
|
656
|
-
}
|
|
657
|
-
return acc + str + String(value)
|
|
658
|
-
}, '')
|
|
659
|
-
|
|
660
|
-
// Return chainable for options - properly make it thenable
|
|
661
|
-
const basePromise = mockGenerate(type, prompt, {})
|
|
662
|
-
const chainable = (options?: Record<string, unknown>) => mockGenerate(type, prompt, options || {})
|
|
663
|
-
|
|
664
|
-
// Add then/catch to make it awaitable
|
|
665
|
-
;(chainable as unknown as Promise<unknown>).then = basePromise.then.bind(basePromise)
|
|
666
|
-
;(chainable as unknown as Promise<unknown>).catch = basePromise.catch.bind(basePromise)
|
|
667
|
-
|
|
668
|
-
return chainable
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Regular call
|
|
672
|
-
prompt = promptOrStrings as string
|
|
673
|
-
return mockGenerate(type, prompt, args[0] || {})
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// ============================================================================
|
|
678
|
-
// Test fixtures
|
|
679
|
-
// ============================================================================
|
|
680
|
-
|
|
681
|
-
const longArticle = 'This is a long article about technology and innovation...'
|
|
682
|
-
const technicalReport = 'Technical analysis of system performance metrics...'
|
|
683
|
-
const industry = 'fintech'
|
|
684
|
-
const topic = 'TypeScript'
|
|
685
|
-
const claim = 'The Earth is round'
|
|
686
|
-
const article = 'Article mentioning John Smith and Jane Doe...'
|
|
687
|
-
const text = 'Some text content'
|
|
688
|
-
const company = 'Acme Corp'
|
|
689
|
-
const market = 'SaaS'
|
|
690
|
-
const recipient = 'John'
|
|
691
|
-
const subject = 'Project Update'
|
|
692
|
-
const companyName = 'TechCorp'
|
|
693
|
-
const productName = 'ProductX'
|
|
694
|
-
const data = { key: 'value' }
|