ai-functions 0.3.0 → 0.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 +5 -0
- package/.turbo/turbo-test.log +105 -0
- package/README.md +190 -86
- package/TODO.md +138 -0
- package/dist/ai-promise.d.ts +219 -0
- package/dist/ai-promise.d.ts.map +1 -0
- package/dist/ai-promise.js +610 -0
- package/dist/ai-promise.js.map +1 -0
- package/dist/ai.d.ts +285 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +842 -0
- package/dist/ai.js.map +1 -0
- package/dist/batch/anthropic.d.ts +23 -0
- package/dist/batch/anthropic.d.ts.map +1 -0
- package/dist/batch/anthropic.js +257 -0
- package/dist/batch/anthropic.js.map +1 -0
- package/dist/batch/bedrock.d.ts +64 -0
- package/dist/batch/bedrock.d.ts.map +1 -0
- package/dist/batch/bedrock.js +586 -0
- package/dist/batch/bedrock.js.map +1 -0
- package/dist/batch/cloudflare.d.ts +37 -0
- package/dist/batch/cloudflare.d.ts.map +1 -0
- package/dist/batch/cloudflare.js +289 -0
- package/dist/batch/cloudflare.js.map +1 -0
- package/dist/batch/google.d.ts +41 -0
- package/dist/batch/google.d.ts.map +1 -0
- package/dist/batch/google.js +360 -0
- package/dist/batch/google.js.map +1 -0
- package/dist/batch/index.d.ts +31 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +31 -0
- package/dist/batch/index.js.map +1 -0
- package/dist/batch/memory.d.ts +44 -0
- package/dist/batch/memory.d.ts.map +1 -0
- package/dist/batch/memory.js +188 -0
- package/dist/batch/memory.js.map +1 -0
- package/dist/batch/openai.d.ts +37 -0
- package/dist/batch/openai.d.ts.map +1 -0
- package/dist/batch/openai.js +403 -0
- package/dist/batch/openai.js.map +1 -0
- package/dist/batch-map.d.ts +125 -0
- package/dist/batch-map.d.ts.map +1 -0
- package/dist/batch-map.js +406 -0
- package/dist/batch-map.js.map +1 -0
- package/dist/batch-queue.d.ts +273 -0
- package/dist/batch-queue.d.ts.map +1 -0
- package/dist/batch-queue.js +271 -0
- package/dist/batch-queue.js.map +1 -0
- package/dist/context.d.ts +133 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +267 -0
- package/dist/context.js.map +1 -0
- package/dist/embeddings.d.ts +123 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +170 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/eval/index.d.ts +8 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +8 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/models.d.ts +66 -0
- package/dist/eval/models.d.ts.map +1 -0
- package/dist/eval/models.js +120 -0
- package/dist/eval/models.js.map +1 -0
- package/dist/eval/runner.d.ts +64 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +148 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/generate.d.ts +168 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +174 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +29 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -52
- package/dist/index.js.map +1 -1
- package/dist/primitives.d.ts +292 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +471 -0
- package/dist/primitives.js.map +1 -0
- package/dist/providers/cloudflare.d.ts +9 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +9 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/schema.d.ts +54 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +109 -0
- package/dist/schema.js.map +1 -0
- package/dist/template.d.ts +73 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +129 -0
- package/dist/template.js.map +1 -0
- package/dist/types.d.ts +474 -106
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -8
- package/dist/types.js.map +1 -1
- package/evalite.config.ts +19 -0
- package/evals/README.md +212 -0
- package/evals/classification.eval.ts +108 -0
- package/evals/marketing.eval.ts +370 -0
- package/evals/math.eval.ts +94 -0
- package/evals/run-evals.ts +166 -0
- package/evals/structured-output.eval.ts +143 -0
- package/evals/writing.eval.ts +117 -0
- package/examples/batch-blog-posts.ts +160 -0
- package/package.json +57 -57
- package/src/ai-promise.ts +784 -0
- package/src/ai.ts +1183 -0
- package/src/batch/anthropic.ts +375 -0
- package/src/batch/bedrock.ts +801 -0
- package/src/batch/cloudflare.ts +421 -0
- package/src/batch/google.ts +491 -0
- package/src/batch/index.ts +31 -0
- package/src/batch/memory.ts +253 -0
- package/src/batch/openai.ts +557 -0
- package/src/batch-map.ts +534 -0
- package/src/batch-queue.ts +493 -0
- package/src/context.ts +332 -0
- package/src/embeddings.ts +244 -0
- package/src/eval/index.ts +8 -0
- package/src/eval/models.ts +158 -0
- package/src/eval/runner.ts +217 -0
- package/src/generate.ts +245 -0
- package/src/index.ts +154 -0
- package/src/primitives.ts +612 -0
- package/src/providers/cloudflare.ts +15 -0
- package/src/providers/index.ts +14 -0
- package/src/schema.ts +147 -0
- package/src/template.ts +209 -0
- package/src/types.ts +540 -0
- package/test/README.md +105 -0
- package/test/ai-proxy.test.ts +192 -0
- package/test/async-iterators.test.ts +327 -0
- package/test/batch-background.test.ts +482 -0
- package/test/batch-blog-posts.test.ts +387 -0
- package/test/blog-generation.test.ts +510 -0
- package/test/browse-read.test.ts +611 -0
- package/test/core-functions.test.ts +694 -0
- package/test/decide.test.ts +393 -0
- package/test/define.test.ts +274 -0
- package/test/e2e-bedrock-manual.ts +163 -0
- package/test/e2e-bedrock.test.ts +191 -0
- package/test/e2e-flex-gateway.ts +157 -0
- package/test/e2e-flex-manual.ts +183 -0
- package/test/e2e-flex.test.ts +209 -0
- package/test/e2e-google-manual.ts +178 -0
- package/test/e2e-google.test.ts +216 -0
- package/test/embeddings.test.ts +284 -0
- package/test/evals/define-function.eval.test.ts +379 -0
- package/test/evals/primitives.eval.test.ts +384 -0
- package/test/function-types.test.ts +492 -0
- package/test/generate-core.test.ts +319 -0
- package/test/generate.test.ts +163 -0
- package/test/implicit-batch.test.ts +422 -0
- package/test/schema.test.ts +109 -0
- package/test/tagged-templates.test.ts +302 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +42 -0
- package/LICENSE +0 -21
- package/bin/cli.js +0 -5
- package/dist/cli/index.d.ts +0 -10
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -38
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.test.d.ts +0 -2
- package/dist/cli/index.test.d.ts.map +0 -1
- package/dist/cli/index.test.js +0 -35
- package/dist/cli/index.test.js.map +0 -1
- package/dist/constants/models.d.ts +0 -10
- package/dist/constants/models.d.ts.map +0 -1
- package/dist/constants/models.js +0 -12
- package/dist/constants/models.js.map +0 -1
- package/dist/converters/index.d.ts +0 -3
- package/dist/converters/index.d.ts.map +0 -1
- package/dist/converters/index.js +0 -3
- package/dist/converters/index.js.map +0 -1
- package/dist/converters/model.d.ts +0 -4
- package/dist/converters/model.d.ts.map +0 -1
- package/dist/converters/model.js +0 -19
- package/dist/converters/model.js.map +0 -1
- package/dist/converters/schema.d.ts +0 -4
- package/dist/converters/schema.d.ts.map +0 -1
- package/dist/converters/schema.js +0 -25
- package/dist/converters/schema.js.map +0 -1
- package/dist/core/responses.d.ts +0 -5
- package/dist/core/responses.d.ts.map +0 -1
- package/dist/core/responses.js +0 -16
- package/dist/core/responses.js.map +0 -1
- package/dist/core/responses.test.d.ts +0 -2
- package/dist/core/responses.test.d.ts.map +0 -1
- package/dist/core/responses.test.js +0 -31
- package/dist/core/responses.test.js.map +0 -1
- package/dist/errors.d.ts +0 -6
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -9
- package/dist/errors.js.map +0 -1
- package/dist/examples/streaming.test.d.ts +0 -2
- package/dist/examples/streaming.test.d.ts.map +0 -1
- package/dist/examples/streaming.test.js +0 -176
- package/dist/examples/streaming.test.js.map +0 -1
- package/dist/factory/__tests__/index.test.d.ts +0 -2
- package/dist/factory/__tests__/index.test.d.ts.map +0 -1
- package/dist/factory/__tests__/index.test.js +0 -430
- package/dist/factory/__tests__/index.test.js.map +0 -1
- package/dist/factory/__tests__/list.test.d.ts +0 -2
- package/dist/factory/__tests__/list.test.d.ts.map +0 -1
- package/dist/factory/__tests__/list.test.js +0 -92
- package/dist/factory/__tests__/list.test.js.map +0 -1
- package/dist/factory/index.d.ts +0 -20
- package/dist/factory/index.d.ts.map +0 -1
- package/dist/factory/index.js +0 -287
- package/dist/factory/index.js.map +0 -1
- package/dist/factory/index.test.d.ts +0 -2
- package/dist/factory/index.test.d.ts.map +0 -1
- package/dist/factory/index.test.js +0 -287
- package/dist/factory/index.test.js.map +0 -1
- package/dist/factory/list.d.ts +0 -3
- package/dist/factory/list.d.ts.map +0 -1
- package/dist/factory/list.js +0 -221
- package/dist/factory/list.js.map +0 -1
- package/dist/factory/list.test.d.ts +0 -2
- package/dist/factory/list.test.d.ts.map +0 -1
- package/dist/factory/list.test.js +0 -84
- package/dist/factory/list.test.js.map +0 -1
- package/dist/generate/index.d.ts +0 -5
- package/dist/generate/index.d.ts.map +0 -1
- package/dist/generate/index.js +0 -17
- package/dist/generate/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -59
- package/dist/index.test.js.map +0 -1
- package/dist/list/await.d.ts +0 -3
- package/dist/list/await.d.ts.map +0 -1
- package/dist/list/await.js +0 -28
- package/dist/list/await.js.map +0 -1
- package/dist/list/constants.d.ts +0 -4
- package/dist/list/constants.d.ts.map +0 -1
- package/dist/list/constants.js +0 -5
- package/dist/list/constants.js.map +0 -1
- package/dist/list/create-function.d.ts +0 -3
- package/dist/list/create-function.d.ts.map +0 -1
- package/dist/list/create-function.js +0 -11
- package/dist/list/create-function.js.map +0 -1
- package/dist/list/index.d.ts +0 -4
- package/dist/list/index.d.ts.map +0 -1
- package/dist/list/index.js +0 -5
- package/dist/list/index.js.map +0 -1
- package/dist/list/prompt.d.ts +0 -3
- package/dist/list/prompt.d.ts.map +0 -1
- package/dist/list/prompt.js +0 -6
- package/dist/list/prompt.js.map +0 -1
- package/dist/list/schemas.d.ts +0 -4
- package/dist/list/schemas.d.ts.map +0 -1
- package/dist/list/schemas.js +0 -8
- package/dist/list/schemas.js.map +0 -1
- package/dist/list/stream.d.ts +0 -3
- package/dist/list/stream.d.ts.map +0 -1
- package/dist/list/stream.js +0 -33
- package/dist/list/stream.js.map +0 -1
- package/dist/list/types.d.ts +0 -11
- package/dist/list/types.d.ts.map +0 -1
- package/dist/list/types.js +0 -2
- package/dist/list/types.js.map +0 -1
- package/dist/list/validation.d.ts +0 -3
- package/dist/list/validation.d.ts.map +0 -1
- package/dist/list/validation.js +0 -12
- package/dist/list/validation.js.map +0 -1
- package/dist/providers/config.d.ts +0 -4
- package/dist/providers/config.d.ts.map +0 -1
- package/dist/providers/config.js +0 -21
- package/dist/providers/config.js.map +0 -1
- package/dist/providers/config.test.d.ts +0 -2
- package/dist/providers/config.test.d.ts.map +0 -1
- package/dist/providers/config.test.js +0 -37
- package/dist/providers/config.test.js.map +0 -1
- package/dist/proxy/constants.d.ts +0 -4
- package/dist/proxy/constants.d.ts.map +0 -1
- package/dist/proxy/constants.js +0 -5
- package/dist/proxy/constants.js.map +0 -1
- package/dist/proxy/create-function.d.ts +0 -4
- package/dist/proxy/create-function.d.ts.map +0 -1
- package/dist/proxy/create-function.js +0 -24
- package/dist/proxy/create-function.js.map +0 -1
- package/dist/proxy/create-proxy.d.ts +0 -2
- package/dist/proxy/create-proxy.d.ts.map +0 -1
- package/dist/proxy/create-proxy.js +0 -11
- package/dist/proxy/create-proxy.js.map +0 -1
- package/dist/proxy/function-generator.d.ts +0 -9
- package/dist/proxy/function-generator.d.ts.map +0 -1
- package/dist/proxy/function-generator.js +0 -29
- package/dist/proxy/function-generator.js.map +0 -1
- package/dist/proxy/index.d.ts +0 -4
- package/dist/proxy/index.d.ts.map +0 -1
- package/dist/proxy/index.js +0 -4
- package/dist/proxy/index.js.map +0 -1
- package/dist/proxy/prompt.d.ts +0 -2
- package/dist/proxy/prompt.d.ts.map +0 -1
- package/dist/proxy/prompt.js +0 -6
- package/dist/proxy/prompt.js.map +0 -1
- package/dist/proxy/types.d.ts +0 -7
- package/dist/proxy/types.d.ts.map +0 -1
- package/dist/proxy/types.js +0 -2
- package/dist/proxy/types.js.map +0 -1
- package/dist/queue/manager.d.ts +0 -5
- package/dist/queue/manager.d.ts.map +0 -1
- package/dist/queue/manager.js +0 -37
- package/dist/queue/manager.js.map +0 -1
- package/dist/queue/manager.test.d.ts +0 -2
- package/dist/queue/manager.test.d.ts.map +0 -1
- package/dist/queue/manager.test.js +0 -52
- package/dist/queue/manager.test.js.map +0 -1
- package/dist/schema-converter.d.ts +0 -4
- package/dist/schema-converter.d.ts.map +0 -1
- package/dist/schema-converter.js +0 -30
- package/dist/schema-converter.js.map +0 -1
- package/dist/stream/index.d.ts +0 -7
- package/dist/stream/index.d.ts.map +0 -1
- package/dist/stream/index.js +0 -23
- package/dist/stream/index.js.map +0 -1
- package/dist/streaming/utils.d.ts +0 -4
- package/dist/streaming/utils.d.ts.map +0 -1
- package/dist/streaming/utils.js +0 -131
- package/dist/streaming/utils.js.map +0 -1
- package/dist/streaming/utils.test.d.ts +0 -2
- package/dist/streaming/utils.test.d.ts.map +0 -1
- package/dist/streaming/utils.test.js +0 -84
- package/dist/streaming/utils.test.js.map +0 -1
- package/dist/templates/result.d.ts +0 -7
- package/dist/templates/result.d.ts.map +0 -1
- package/dist/templates/result.js +0 -40
- package/dist/templates/result.js.map +0 -1
- package/dist/templates/result.test.d.ts +0 -2
- package/dist/templates/result.test.d.ts.map +0 -1
- package/dist/templates/result.test.js +0 -75
- package/dist/templates/result.test.js.map +0 -1
- package/dist/test/setup.d.ts +0 -2
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js +0 -21
- package/dist/test/setup.js.map +0 -1
- package/dist/test-types.d.ts +0 -13
- package/dist/test-types.d.ts.map +0 -1
- package/dist/test-types.js +0 -55
- package/dist/test-types.js.map +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +0 -1
- package/dist/types/list.d.ts +0 -10
- package/dist/types/list.d.ts.map +0 -1
- package/dist/types/list.js +0 -2
- package/dist/types/list.js.map +0 -1
- package/dist/types/model.d.ts +0 -7
- package/dist/types/model.d.ts.map +0 -1
- package/dist/types/model.js +0 -2
- package/dist/types/model.js.map +0 -1
- package/dist/types/options.d.ts +0 -25
- package/dist/types/options.d.ts.map +0 -1
- package/dist/types/options.js +0 -2
- package/dist/types/options.js.map +0 -1
- package/dist/types/schema.d.ts +0 -5
- package/dist/types/schema.d.ts.map +0 -1
- package/dist/types/schema.js +0 -2
- package/dist/types/schema.js.map +0 -1
- package/dist/utils/__tests__/request-handler.test.d.ts +0 -2
- package/dist/utils/__tests__/request-handler.test.d.ts.map +0 -1
- package/dist/utils/__tests__/request-handler.test.js +0 -134
- package/dist/utils/__tests__/request-handler.test.js.map +0 -1
- package/dist/utils/__tests__/schema.test.d.ts +0 -2
- package/dist/utils/__tests__/schema.test.d.ts.map +0 -1
- package/dist/utils/__tests__/schema.test.js +0 -49
- package/dist/utils/__tests__/schema.test.js.map +0 -1
- package/dist/utils/__tests__/stream-progress.test.d.ts +0 -2
- package/dist/utils/__tests__/stream-progress.test.d.ts.map +0 -1
- package/dist/utils/__tests__/stream-progress.test.js +0 -85
- package/dist/utils/__tests__/stream-progress.test.js.map +0 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/request-handler.d.ts +0 -17
- package/dist/utils/request-handler.d.ts.map +0 -1
- package/dist/utils/request-handler.js +0 -105
- package/dist/utils/request-handler.js.map +0 -1
- package/dist/utils/schema.d.ts +0 -11
- package/dist/utils/schema.d.ts.map +0 -1
- package/dist/utils/schema.js +0 -51
- package/dist/utils/schema.js.map +0 -1
- package/dist/utils/stream-progress.d.ts +0 -17
- package/dist/utils/stream-progress.d.ts.map +0 -1
- package/dist/utils/stream-progress.js +0 -86
- package/dist/utils/stream-progress.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -30
- package/dist/utils/validation.js.map +0 -1
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the decide() function - LLM as Judge
|
|
3
|
+
*
|
|
4
|
+
* decide`criteria`(optionA, optionB) - Compare options and pick the best
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Mock for underlying AI calls
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
const mockDecide = vi.fn()
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Mock decide implementation
|
|
17
|
+
* Returns a function that compares options against criteria
|
|
18
|
+
*/
|
|
19
|
+
function createMockDecide() {
|
|
20
|
+
return function decide(
|
|
21
|
+
promptOrStrings: string | TemplateStringsArray,
|
|
22
|
+
...args: unknown[]
|
|
23
|
+
) {
|
|
24
|
+
let criteria: string
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
|
|
27
|
+
criteria = (promptOrStrings as TemplateStringsArray).reduce((acc, str, i) => {
|
|
28
|
+
return acc + str + (args[i] ?? '')
|
|
29
|
+
}, '')
|
|
30
|
+
} else {
|
|
31
|
+
criteria = promptOrStrings as string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Return a function that accepts options to compare
|
|
35
|
+
return function compareOptions<T>(...options: T[]): Promise<T> {
|
|
36
|
+
return mockDecide(criteria, options)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Basic decide() tests
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
describe('decide() - LLM as Judge', () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
mockDecide.mockReset()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('basic comparison', () => {
|
|
51
|
+
it('compares two options and returns the better one', async () => {
|
|
52
|
+
const decide = createMockDecide()
|
|
53
|
+
|
|
54
|
+
const optionA = { name: 'Simple Solution', complexity: 'low', time: '1 day' }
|
|
55
|
+
const optionB = { name: 'Complex Solution', complexity: 'high', time: '1 week' }
|
|
56
|
+
|
|
57
|
+
mockDecide.mockResolvedValue(optionA)
|
|
58
|
+
|
|
59
|
+
const result = await decide`fastest to implement`(optionA, optionB)
|
|
60
|
+
|
|
61
|
+
expect(mockDecide).toHaveBeenCalledWith(
|
|
62
|
+
'fastest to implement',
|
|
63
|
+
[optionA, optionB]
|
|
64
|
+
)
|
|
65
|
+
expect(result).toBe(optionA)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('compares multiple options', async () => {
|
|
69
|
+
const decide = createMockDecide()
|
|
70
|
+
|
|
71
|
+
const options = [
|
|
72
|
+
{ framework: 'React', popularity: 'high' },
|
|
73
|
+
{ framework: 'Vue', popularity: 'medium' },
|
|
74
|
+
{ framework: 'Angular', popularity: 'medium' },
|
|
75
|
+
{ framework: 'Svelte', popularity: 'growing' },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
mockDecide.mockResolvedValue(options[0])
|
|
79
|
+
|
|
80
|
+
const result = await decide`best for large enterprise applications`(
|
|
81
|
+
options[0],
|
|
82
|
+
options[1],
|
|
83
|
+
options[2],
|
|
84
|
+
options[3]
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
expect(mockDecide).toHaveBeenCalledWith(
|
|
88
|
+
'best for large enterprise applications',
|
|
89
|
+
options
|
|
90
|
+
)
|
|
91
|
+
expect(result).toEqual({ framework: 'React', popularity: 'high' })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('works with string options', async () => {
|
|
95
|
+
const decide = createMockDecide()
|
|
96
|
+
|
|
97
|
+
mockDecide.mockResolvedValue('Option B: More engaging title')
|
|
98
|
+
|
|
99
|
+
const result = await decide`better headline for click-through rate`(
|
|
100
|
+
'Option A: Simple Guide to TypeScript',
|
|
101
|
+
'Option B: More engaging title'
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
expect(result).toBe('Option B: More engaging title')
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('criteria as tagged template', () => {
|
|
109
|
+
it('supports variable interpolation in criteria', async () => {
|
|
110
|
+
const decide = createMockDecide()
|
|
111
|
+
const audience = 'enterprise developers'
|
|
112
|
+
|
|
113
|
+
mockDecide.mockResolvedValue({ headline: 'A' })
|
|
114
|
+
|
|
115
|
+
await decide`best for ${audience}`(
|
|
116
|
+
{ headline: 'A' },
|
|
117
|
+
{ headline: 'B' }
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
expect(mockDecide).toHaveBeenCalledWith(
|
|
121
|
+
`best for ${audience}`,
|
|
122
|
+
expect.any(Array)
|
|
123
|
+
)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('supports complex criteria descriptions', async () => {
|
|
127
|
+
const decide = createMockDecide()
|
|
128
|
+
|
|
129
|
+
const requirements = {
|
|
130
|
+
performance: 'high',
|
|
131
|
+
maintainability: 'critical',
|
|
132
|
+
teamExperience: 'junior',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
mockDecide.mockResolvedValue({ approach: 'A' })
|
|
136
|
+
|
|
137
|
+
// Criteria would have requirements as YAML
|
|
138
|
+
await decide`best architecture considering team and requirements`(
|
|
139
|
+
{ approach: 'A' },
|
|
140
|
+
{ approach: 'B' }
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
expect(mockDecide).toHaveBeenCalled()
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('use cases from README', () => {
|
|
148
|
+
it('A/B testing headlines', async () => {
|
|
149
|
+
const decide = createMockDecide()
|
|
150
|
+
|
|
151
|
+
const headlineA = 'Get Started with TypeScript Today'
|
|
152
|
+
const headlineB = 'TypeScript: The Complete Guide for 2025'
|
|
153
|
+
|
|
154
|
+
mockDecide.mockResolvedValue(headlineB)
|
|
155
|
+
|
|
156
|
+
const winner = await decide`higher click-through rate for developer audience`(
|
|
157
|
+
headlineA,
|
|
158
|
+
headlineB
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
expect(winner).toBe(headlineB)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('choosing best code implementation', async () => {
|
|
165
|
+
const decide = createMockDecide()
|
|
166
|
+
|
|
167
|
+
const implementations = [
|
|
168
|
+
`function isPrime(n) {
|
|
169
|
+
if (n <= 1) return false;
|
|
170
|
+
for (let i = 2; i <= Math.sqrt(n); i++) {
|
|
171
|
+
if (n % i === 0) return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}`,
|
|
175
|
+
`function isPrime(n) {
|
|
176
|
+
if (n <= 1) return false;
|
|
177
|
+
if (n <= 3) return true;
|
|
178
|
+
if (n % 2 === 0 || n % 3 === 0) return false;
|
|
179
|
+
for (let i = 5; i * i <= n; i += 6) {
|
|
180
|
+
if (n % i === 0 || n % (i + 2) === 0) return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}`,
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
mockDecide.mockResolvedValue(implementations[1])
|
|
187
|
+
|
|
188
|
+
const best = await decide`most performant and readable`(
|
|
189
|
+
implementations[0],
|
|
190
|
+
implementations[1]
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
expect(best).toBe(implementations[1])
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('selecting marketing copy', async () => {
|
|
197
|
+
const decide = createMockDecide()
|
|
198
|
+
|
|
199
|
+
const copies = [
|
|
200
|
+
{ text: 'Build faster with AI', tone: 'direct' },
|
|
201
|
+
{ text: 'Empower your workflow', tone: 'inspirational' },
|
|
202
|
+
{ text: '10x your productivity', tone: 'bold' },
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
mockDecide.mockResolvedValue(copies[2])
|
|
206
|
+
|
|
207
|
+
const winner = await decide`most compelling for startup founders`(
|
|
208
|
+
copies[0],
|
|
209
|
+
copies[1],
|
|
210
|
+
copies[2]
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
expect(winner).toEqual({ text: '10x your productivity', tone: 'bold' })
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// decide() with options parameter
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
describe('decide() with options', () => {
|
|
223
|
+
beforeEach(() => {
|
|
224
|
+
mockDecide.mockReset()
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('accepts model option', async () => {
|
|
228
|
+
// This tests the concept - actual implementation would pass options
|
|
229
|
+
const mockDecideWithOptions = vi.fn()
|
|
230
|
+
|
|
231
|
+
function createMockDecideWithOptions() {
|
|
232
|
+
return function decide(
|
|
233
|
+
promptOrStrings: string | TemplateStringsArray,
|
|
234
|
+
...args: unknown[]
|
|
235
|
+
) {
|
|
236
|
+
let criteria: string
|
|
237
|
+
|
|
238
|
+
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
|
|
239
|
+
criteria = (promptOrStrings as TemplateStringsArray).reduce((acc, str, i) => {
|
|
240
|
+
return acc + str + (args[i] ?? '')
|
|
241
|
+
}, '')
|
|
242
|
+
} else {
|
|
243
|
+
criteria = promptOrStrings as string
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return function compareOptions<T>(...options: T[]) {
|
|
247
|
+
// Support options as last parameter if it's an object with known keys
|
|
248
|
+
const lastArg = options[options.length - 1] as Record<string, unknown>
|
|
249
|
+
if (lastArg && typeof lastArg === 'object' && 'model' in lastArg) {
|
|
250
|
+
const realOptions = options.slice(0, -1)
|
|
251
|
+
return mockDecideWithOptions(criteria, realOptions, lastArg)
|
|
252
|
+
}
|
|
253
|
+
return mockDecideWithOptions(criteria, options, undefined)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const decide = createMockDecideWithOptions()
|
|
259
|
+
mockDecideWithOptions.mockResolvedValue('A')
|
|
260
|
+
|
|
261
|
+
// Two ways to pass options:
|
|
262
|
+
// 1. Separate options object
|
|
263
|
+
await decide`better option`('A', 'B')
|
|
264
|
+
|
|
265
|
+
expect(mockDecideWithOptions).toHaveBeenCalledWith(
|
|
266
|
+
'better option',
|
|
267
|
+
['A', 'B'],
|
|
268
|
+
undefined
|
|
269
|
+
)
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('accepts thinking option for complex decisions', async () => {
|
|
273
|
+
// Complex decisions might benefit from extended thinking
|
|
274
|
+
const mockDecideWithThinking = vi.fn().mockResolvedValue('Option A')
|
|
275
|
+
|
|
276
|
+
// Simulating how decide might use thinking
|
|
277
|
+
const decision = mockDecideWithThinking('complex criteria', ['A', 'B'], {
|
|
278
|
+
thinking: 'high',
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
expect(mockDecideWithThinking).toHaveBeenCalledWith(
|
|
282
|
+
'complex criteria',
|
|
283
|
+
['A', 'B'],
|
|
284
|
+
{ thinking: 'high' }
|
|
285
|
+
)
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// ============================================================================
|
|
290
|
+
// decide() return value
|
|
291
|
+
// ============================================================================
|
|
292
|
+
|
|
293
|
+
describe('decide() return value', () => {
|
|
294
|
+
beforeEach(() => {
|
|
295
|
+
mockDecide.mockReset()
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('returns the exact option object passed in', async () => {
|
|
299
|
+
const decide = createMockDecide()
|
|
300
|
+
|
|
301
|
+
const optionA = { id: 1, name: 'Option A', metadata: { score: 10 } }
|
|
302
|
+
const optionB = { id: 2, name: 'Option B', metadata: { score: 20 } }
|
|
303
|
+
|
|
304
|
+
// Return the exact reference, not a copy
|
|
305
|
+
mockDecide.mockResolvedValue(optionA)
|
|
306
|
+
|
|
307
|
+
const result = await decide`best option`(optionA, optionB)
|
|
308
|
+
|
|
309
|
+
// Should be the exact same reference
|
|
310
|
+
expect(result).toBe(optionA)
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('can return with reasoning (extended mode)', async () => {
|
|
314
|
+
// Extended mode returns both the decision and reasoning
|
|
315
|
+
const mockDecideWithReasoning = vi.fn()
|
|
316
|
+
|
|
317
|
+
mockDecideWithReasoning.mockResolvedValue({
|
|
318
|
+
choice: 'Option A',
|
|
319
|
+
reasoning: 'Option A is better because it has lower complexity',
|
|
320
|
+
confidence: 0.85,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
const result = await mockDecideWithReasoning('criteria', ['A', 'B'], {
|
|
324
|
+
explain: true,
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
expect(result).toHaveProperty('choice')
|
|
328
|
+
expect(result).toHaveProperty('reasoning')
|
|
329
|
+
expect(result).toHaveProperty('confidence')
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// ============================================================================
|
|
334
|
+
// Edge cases
|
|
335
|
+
// ============================================================================
|
|
336
|
+
|
|
337
|
+
describe('decide() edge cases', () => {
|
|
338
|
+
beforeEach(() => {
|
|
339
|
+
mockDecide.mockReset()
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it('handles identical options', async () => {
|
|
343
|
+
const decide = createMockDecide()
|
|
344
|
+
|
|
345
|
+
mockDecide.mockResolvedValue('Same')
|
|
346
|
+
|
|
347
|
+
const result = await decide`better one`('Same', 'Same')
|
|
348
|
+
|
|
349
|
+
expect(result).toBe('Same')
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('handles single option (validation use case)', async () => {
|
|
353
|
+
const decide = createMockDecide()
|
|
354
|
+
|
|
355
|
+
const option = { content: 'Some text' }
|
|
356
|
+
mockDecide.mockResolvedValue(option)
|
|
357
|
+
|
|
358
|
+
// Single option - essentially asking "is this good enough?"
|
|
359
|
+
const result = await decide`meets quality standards`(option)
|
|
360
|
+
|
|
361
|
+
expect(mockDecide).toHaveBeenCalledWith('meets quality standards', [option])
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('handles complex nested objects', async () => {
|
|
365
|
+
const decide = createMockDecide()
|
|
366
|
+
|
|
367
|
+
const architectureA = {
|
|
368
|
+
name: 'Microservices',
|
|
369
|
+
components: {
|
|
370
|
+
gateway: { type: 'API Gateway', tech: 'Kong' },
|
|
371
|
+
services: ['auth', 'users', 'products'],
|
|
372
|
+
database: { type: 'distributed', engine: 'CockroachDB' },
|
|
373
|
+
},
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const architectureB = {
|
|
377
|
+
name: 'Monolith',
|
|
378
|
+
components: {
|
|
379
|
+
app: { type: 'Monolithic', tech: 'NestJS' },
|
|
380
|
+
database: { type: 'single', engine: 'PostgreSQL' },
|
|
381
|
+
},
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
mockDecide.mockResolvedValue(architectureA)
|
|
385
|
+
|
|
386
|
+
const result = await decide`better for a startup with 3 developers`(
|
|
387
|
+
architectureA,
|
|
388
|
+
architectureB
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
expect(result.name).toBe('Microservices')
|
|
392
|
+
})
|
|
393
|
+
})
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for define and function registry
|
|
3
|
+
*
|
|
4
|
+
* These tests use real AI calls via the Cloudflare AI Gateway.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
8
|
+
import { define, defineFunction, functions } from '../src/index.js'
|
|
9
|
+
|
|
10
|
+
// Skip tests if no gateway configured
|
|
11
|
+
const hasGateway = !!process.env.AI_GATEWAY_URL || !!process.env.ANTHROPIC_API_KEY
|
|
12
|
+
|
|
13
|
+
describe('functions registry', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
functions.clear()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('starts empty', () => {
|
|
19
|
+
expect(functions.list()).toEqual([])
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('tracks defined functions', () => {
|
|
23
|
+
const fn = defineFunction({
|
|
24
|
+
type: 'generative',
|
|
25
|
+
name: 'testFunc',
|
|
26
|
+
args: { input: 'Input text' },
|
|
27
|
+
output: 'string',
|
|
28
|
+
})
|
|
29
|
+
functions.set('testFunc', fn)
|
|
30
|
+
|
|
31
|
+
expect(functions.has('testFunc')).toBe(true)
|
|
32
|
+
expect(functions.list()).toContain('testFunc')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('retrieves defined functions', () => {
|
|
36
|
+
const fn = defineFunction({
|
|
37
|
+
type: 'generative',
|
|
38
|
+
name: 'testFunc',
|
|
39
|
+
args: { input: 'Input text' },
|
|
40
|
+
output: 'string',
|
|
41
|
+
})
|
|
42
|
+
functions.set('testFunc', fn)
|
|
43
|
+
|
|
44
|
+
const retrieved = functions.get('testFunc')
|
|
45
|
+
expect(retrieved).toBeDefined()
|
|
46
|
+
expect(retrieved?.definition.name).toBe('testFunc')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('deletes functions', () => {
|
|
50
|
+
const fn = defineFunction({
|
|
51
|
+
type: 'generative',
|
|
52
|
+
name: 'testFunc',
|
|
53
|
+
args: { input: 'Input text' },
|
|
54
|
+
output: 'string',
|
|
55
|
+
})
|
|
56
|
+
functions.set('testFunc', fn)
|
|
57
|
+
|
|
58
|
+
expect(functions.delete('testFunc')).toBe(true)
|
|
59
|
+
expect(functions.has('testFunc')).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('clears all functions', () => {
|
|
63
|
+
const fn1 = defineFunction({
|
|
64
|
+
type: 'generative',
|
|
65
|
+
name: 'func1',
|
|
66
|
+
args: {},
|
|
67
|
+
output: 'string',
|
|
68
|
+
})
|
|
69
|
+
const fn2 = defineFunction({
|
|
70
|
+
type: 'generative',
|
|
71
|
+
name: 'func2',
|
|
72
|
+
args: {},
|
|
73
|
+
output: 'string',
|
|
74
|
+
})
|
|
75
|
+
functions.set('func1', fn1)
|
|
76
|
+
functions.set('func2', fn2)
|
|
77
|
+
|
|
78
|
+
functions.clear()
|
|
79
|
+
expect(functions.list()).toEqual([])
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('defineFunction', () => {
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
functions.clear()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('creates a generative function definition', () => {
|
|
89
|
+
const fn = defineFunction({
|
|
90
|
+
type: 'generative',
|
|
91
|
+
name: 'summarize',
|
|
92
|
+
args: { text: 'Text to summarize' },
|
|
93
|
+
output: 'string',
|
|
94
|
+
system: 'You are a summarizer.',
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
expect(fn.definition.type).toBe('generative')
|
|
98
|
+
expect(fn.definition.name).toBe('summarize')
|
|
99
|
+
expect(typeof fn.call).toBe('function')
|
|
100
|
+
expect(typeof fn.asTool).toBe('function')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('creates an agentic function definition', () => {
|
|
104
|
+
const fn = defineFunction({
|
|
105
|
+
type: 'agentic',
|
|
106
|
+
name: 'research',
|
|
107
|
+
args: { topic: 'Research topic' },
|
|
108
|
+
instructions: 'Research the topic thoroughly.',
|
|
109
|
+
maxIterations: 5,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
expect(fn.definition.type).toBe('agentic')
|
|
113
|
+
expect(fn.definition.name).toBe('research')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('creates a human function definition', () => {
|
|
117
|
+
const fn = defineFunction({
|
|
118
|
+
type: 'human',
|
|
119
|
+
name: 'approve',
|
|
120
|
+
args: { amount: 'Amount (number)' },
|
|
121
|
+
channel: 'workspace',
|
|
122
|
+
instructions: 'Review and approve.',
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(fn.definition.type).toBe('human')
|
|
126
|
+
expect((fn.definition as { channel: string }).channel).toBe('workspace')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('creates a code function definition', () => {
|
|
130
|
+
const fn = defineFunction({
|
|
131
|
+
type: 'code',
|
|
132
|
+
name: 'implement',
|
|
133
|
+
args: { spec: 'Function specification' },
|
|
134
|
+
language: 'typescript',
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
expect(fn.definition.type).toBe('code')
|
|
138
|
+
expect((fn.definition as { language: string }).language).toBe('typescript')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('generates asTool with correct parameters', () => {
|
|
142
|
+
const fn = defineFunction({
|
|
143
|
+
type: 'generative',
|
|
144
|
+
name: 'translate',
|
|
145
|
+
description: 'Translate text to another language',
|
|
146
|
+
args: {
|
|
147
|
+
text: 'Text to translate',
|
|
148
|
+
targetLang: 'Target language',
|
|
149
|
+
},
|
|
150
|
+
output: 'string',
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const tool = fn.asTool()
|
|
154
|
+
expect(tool.name).toBe('translate')
|
|
155
|
+
expect(tool.description).toBe('Translate text to another language')
|
|
156
|
+
expect(tool.parameters.type).toBe('object')
|
|
157
|
+
expect(tool.parameters.properties).toHaveProperty('text')
|
|
158
|
+
expect(tool.parameters.properties).toHaveProperty('targetLang')
|
|
159
|
+
expect(tool.parameters.required).toContain('text')
|
|
160
|
+
expect(tool.parameters.required).toContain('targetLang')
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('define helpers', () => {
|
|
165
|
+
beforeEach(() => {
|
|
166
|
+
functions.clear()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('define.generative registers function', () => {
|
|
170
|
+
const fn = define.generative({
|
|
171
|
+
name: 'greet',
|
|
172
|
+
args: { name: 'Name to greet' },
|
|
173
|
+
output: 'string',
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
expect(functions.has('greet')).toBe(true)
|
|
177
|
+
expect(fn.definition.type).toBe('generative')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('define.agentic registers function', () => {
|
|
181
|
+
const fn = define.agentic({
|
|
182
|
+
name: 'analyze',
|
|
183
|
+
args: { data: 'Data to analyze' },
|
|
184
|
+
instructions: 'Analyze the data.',
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
expect(functions.has('analyze')).toBe(true)
|
|
188
|
+
expect(fn.definition.type).toBe('agentic')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('define.human registers function', () => {
|
|
192
|
+
const fn = define.human({
|
|
193
|
+
name: 'review',
|
|
194
|
+
args: { content: 'Content to review' },
|
|
195
|
+
channel: 'web',
|
|
196
|
+
instructions: 'Review the content.',
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
expect(functions.has('review')).toBe(true)
|
|
200
|
+
expect(fn.definition.type).toBe('human')
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('define.code registers function', () => {
|
|
204
|
+
const fn = define.code({
|
|
205
|
+
name: 'generate',
|
|
206
|
+
args: { prompt: 'Code generation prompt' },
|
|
207
|
+
language: 'python',
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
expect(functions.has('generate')).toBe(true)
|
|
211
|
+
expect(fn.definition.type).toBe('code')
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
describe.skipIf(!hasGateway)('generative function execution', () => {
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
functions.clear()
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('executes a generative string function', async () => {
|
|
221
|
+
const greet = define.generative({
|
|
222
|
+
name: 'greet',
|
|
223
|
+
args: { name: 'Name to greet' },
|
|
224
|
+
output: 'string',
|
|
225
|
+
promptTemplate: 'Say hello to {{name}}',
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
const result = await greet.call({ name: 'World' })
|
|
229
|
+
expect(typeof result).toBe('string')
|
|
230
|
+
expect((result as string).toLowerCase()).toContain('hello')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('executes a generative object function', async () => {
|
|
234
|
+
const analyze = define.generative({
|
|
235
|
+
name: 'analyze',
|
|
236
|
+
args: { text: 'Text to analyze' },
|
|
237
|
+
output: 'object',
|
|
238
|
+
returnType: {
|
|
239
|
+
sentiment: 'positive | negative | neutral',
|
|
240
|
+
confidence: 'Confidence 0-1 (number)',
|
|
241
|
+
},
|
|
242
|
+
promptTemplate: 'Analyze the sentiment of: {{text}}',
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const result = await analyze.call({ text: 'I love this!' }) as { sentiment: string; confidence: number }
|
|
246
|
+
expect(result).toBeDefined()
|
|
247
|
+
expect(['positive', 'negative', 'neutral']).toContain(result.sentiment)
|
|
248
|
+
expect(typeof result.confidence).toBe('number')
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
describe.skipIf(!hasGateway)('auto-define', () => {
|
|
253
|
+
beforeEach(() => {
|
|
254
|
+
functions.clear()
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('auto-defines a function from name and args', async () => {
|
|
258
|
+
const fn = await define('translateText', {
|
|
259
|
+
text: 'Hello',
|
|
260
|
+
targetLanguage: 'French',
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
expect(fn).toBeDefined()
|
|
264
|
+
expect(fn.definition.name).toBe('translateText')
|
|
265
|
+
expect(functions.has('translateText')).toBe(true)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('returns cached function on second call', async () => {
|
|
269
|
+
const fn1 = await define('greetUser', { name: 'Alice' })
|
|
270
|
+
const fn2 = await define('greetUser', { name: 'Bob' })
|
|
271
|
+
|
|
272
|
+
expect(fn1).toBe(fn2)
|
|
273
|
+
})
|
|
274
|
+
})
|