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,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for embedding utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure unit tests for utility functions that don't require AI API calls.
|
|
5
|
+
* Gateway-dependent tests are skipped if no gateway is configured.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest'
|
|
9
|
+
import {
|
|
10
|
+
cosineSimilarity,
|
|
11
|
+
findSimilar,
|
|
12
|
+
pairwiseSimilarity,
|
|
13
|
+
clusterBySimilarity,
|
|
14
|
+
averageEmbeddings,
|
|
15
|
+
normalizeEmbedding,
|
|
16
|
+
} from '../src/index.js'
|
|
17
|
+
|
|
18
|
+
describe('cosineSimilarity', () => {
|
|
19
|
+
it('returns 1 for identical vectors', () => {
|
|
20
|
+
const a = [1, 0, 0]
|
|
21
|
+
const b = [1, 0, 0]
|
|
22
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(1)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('returns 0 for orthogonal vectors', () => {
|
|
26
|
+
const a = [1, 0, 0]
|
|
27
|
+
const b = [0, 1, 0]
|
|
28
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(0)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('returns -1 for opposite vectors', () => {
|
|
32
|
+
const a = [1, 0, 0]
|
|
33
|
+
const b = [-1, 0, 0]
|
|
34
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(-1)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('handles non-normalized vectors', () => {
|
|
38
|
+
const a = [2, 0, 0]
|
|
39
|
+
const b = [5, 0, 0]
|
|
40
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(1)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('works with higher dimensional vectors', () => {
|
|
44
|
+
const a = [1, 2, 3, 4, 5]
|
|
45
|
+
const b = [1, 2, 3, 4, 5]
|
|
46
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(1)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('findSimilar', () => {
|
|
51
|
+
const embeddings = [
|
|
52
|
+
[1, 0, 0],
|
|
53
|
+
[0.9, 0.1, 0],
|
|
54
|
+
[0, 1, 0],
|
|
55
|
+
[0, 0, 1],
|
|
56
|
+
[-1, 0, 0],
|
|
57
|
+
]
|
|
58
|
+
const items = ['A', 'B', 'C', 'D', 'E']
|
|
59
|
+
|
|
60
|
+
it('finds most similar items', () => {
|
|
61
|
+
const query = [1, 0, 0]
|
|
62
|
+
const results = findSimilar(query, embeddings, items, { topK: 3 })
|
|
63
|
+
|
|
64
|
+
expect(results).toHaveLength(3)
|
|
65
|
+
expect(results[0].item).toBe('A') // Exact match
|
|
66
|
+
expect(results[0].score).toBeCloseTo(1)
|
|
67
|
+
expect(results[1].item).toBe('B') // Very similar
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('respects topK parameter', () => {
|
|
71
|
+
const query = [1, 0, 0]
|
|
72
|
+
const results = findSimilar(query, embeddings, items, { topK: 2 })
|
|
73
|
+
|
|
74
|
+
expect(results).toHaveLength(2)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('filters by minScore', () => {
|
|
78
|
+
const query = [1, 0, 0]
|
|
79
|
+
const results = findSimilar(query, embeddings, items, { minScore: 0.5 })
|
|
80
|
+
|
|
81
|
+
// Only A and B should have score >= 0.5
|
|
82
|
+
expect(results.every(r => r.score >= 0.5)).toBe(true)
|
|
83
|
+
expect(results).toHaveLength(2)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('returns index in results', () => {
|
|
87
|
+
const query = [0, 1, 0]
|
|
88
|
+
const results = findSimilar(query, embeddings, items, { topK: 1 })
|
|
89
|
+
|
|
90
|
+
expect(results[0].item).toBe('C')
|
|
91
|
+
expect(results[0].index).toBe(2)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('handles empty embeddings', () => {
|
|
95
|
+
const query = [1, 0, 0]
|
|
96
|
+
const results = findSimilar(query, [], [], { topK: 5 })
|
|
97
|
+
|
|
98
|
+
expect(results).toHaveLength(0)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('pairwiseSimilarity', () => {
|
|
103
|
+
it('creates a symmetric matrix', () => {
|
|
104
|
+
const embeddings = [
|
|
105
|
+
[1, 0, 0],
|
|
106
|
+
[0, 1, 0],
|
|
107
|
+
[0, 0, 1],
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
const matrix = pairwiseSimilarity(embeddings)
|
|
111
|
+
|
|
112
|
+
expect(matrix).toHaveLength(3)
|
|
113
|
+
expect(matrix[0]).toHaveLength(3)
|
|
114
|
+
|
|
115
|
+
// Check symmetry
|
|
116
|
+
for (let i = 0; i < 3; i++) {
|
|
117
|
+
for (let j = 0; j < 3; j++) {
|
|
118
|
+
expect(matrix[i][j]).toBeCloseTo(matrix[j][i])
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('has 1s on the diagonal', () => {
|
|
124
|
+
const embeddings = [
|
|
125
|
+
[1, 2, 3],
|
|
126
|
+
[4, 5, 6],
|
|
127
|
+
[7, 8, 9],
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
const matrix = pairwiseSimilarity(embeddings)
|
|
131
|
+
|
|
132
|
+
for (let i = 0; i < 3; i++) {
|
|
133
|
+
expect(matrix[i][i]).toBeCloseTo(1)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('correctly computes similarity for orthogonal vectors', () => {
|
|
138
|
+
const embeddings = [
|
|
139
|
+
[1, 0, 0],
|
|
140
|
+
[0, 1, 0],
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
const matrix = pairwiseSimilarity(embeddings)
|
|
144
|
+
|
|
145
|
+
expect(matrix[0][1]).toBeCloseTo(0)
|
|
146
|
+
expect(matrix[1][0]).toBeCloseTo(0)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe('clusterBySimilarity', () => {
|
|
151
|
+
it('groups similar items together', () => {
|
|
152
|
+
const embeddings = [
|
|
153
|
+
[1, 0, 0],
|
|
154
|
+
[0.95, 0.05, 0],
|
|
155
|
+
[0, 1, 0],
|
|
156
|
+
[0.05, 0.95, 0],
|
|
157
|
+
]
|
|
158
|
+
const items = ['A', 'A-like', 'B', 'B-like']
|
|
159
|
+
|
|
160
|
+
const clusters = clusterBySimilarity(embeddings, items, { threshold: 0.9 })
|
|
161
|
+
|
|
162
|
+
// Should create 2 clusters
|
|
163
|
+
expect(clusters).toHaveLength(2)
|
|
164
|
+
|
|
165
|
+
// Find the cluster with A
|
|
166
|
+
const clusterA = clusters.find(c => c.includes('A'))
|
|
167
|
+
expect(clusterA).toContain('A-like')
|
|
168
|
+
|
|
169
|
+
// Find the cluster with B
|
|
170
|
+
const clusterB = clusters.find(c => c.includes('B'))
|
|
171
|
+
expect(clusterB).toContain('B-like')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('creates single-item clusters for dissimilar items', () => {
|
|
175
|
+
const embeddings = [
|
|
176
|
+
[1, 0, 0],
|
|
177
|
+
[0, 1, 0],
|
|
178
|
+
[0, 0, 1],
|
|
179
|
+
]
|
|
180
|
+
const items = ['X', 'Y', 'Z']
|
|
181
|
+
|
|
182
|
+
const clusters = clusterBySimilarity(embeddings, items, { threshold: 0.9 })
|
|
183
|
+
|
|
184
|
+
// Each item should be in its own cluster
|
|
185
|
+
expect(clusters).toHaveLength(3)
|
|
186
|
+
expect(clusters.every(c => c.length === 1)).toBe(true)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('puts all items in one cluster at low threshold', () => {
|
|
190
|
+
const embeddings = [
|
|
191
|
+
[1, 0.1, 0.1],
|
|
192
|
+
[0.9, 0.2, 0.1],
|
|
193
|
+
[0.8, 0.3, 0.1],
|
|
194
|
+
]
|
|
195
|
+
const items = ['A', 'B', 'C']
|
|
196
|
+
|
|
197
|
+
const clusters = clusterBySimilarity(embeddings, items, { threshold: 0.5 })
|
|
198
|
+
|
|
199
|
+
// All items similar enough to be in one cluster
|
|
200
|
+
expect(clusters).toHaveLength(1)
|
|
201
|
+
expect(clusters[0]).toHaveLength(3)
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
describe('averageEmbeddings', () => {
|
|
206
|
+
it('averages multiple embeddings', () => {
|
|
207
|
+
const embeddings = [
|
|
208
|
+
[1, 0, 0],
|
|
209
|
+
[0, 1, 0],
|
|
210
|
+
[0, 0, 1],
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
const avg = averageEmbeddings(embeddings)
|
|
214
|
+
|
|
215
|
+
expect(avg).toHaveLength(3)
|
|
216
|
+
expect(avg[0]).toBeCloseTo(1 / 3)
|
|
217
|
+
expect(avg[1]).toBeCloseTo(1 / 3)
|
|
218
|
+
expect(avg[2]).toBeCloseTo(1 / 3)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('returns empty array for empty input', () => {
|
|
222
|
+
const avg = averageEmbeddings([])
|
|
223
|
+
expect(avg).toEqual([])
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('returns same vector for single input', () => {
|
|
227
|
+
const embedding = [1, 2, 3]
|
|
228
|
+
const avg = averageEmbeddings([embedding])
|
|
229
|
+
|
|
230
|
+
expect(avg).toEqual([1, 2, 3])
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe('normalizeEmbedding', () => {
|
|
235
|
+
it('normalizes a vector to unit length', () => {
|
|
236
|
+
const embedding = [3, 4, 0] // length = 5
|
|
237
|
+
const normalized = normalizeEmbedding(embedding)
|
|
238
|
+
|
|
239
|
+
expect(normalized[0]).toBeCloseTo(0.6)
|
|
240
|
+
expect(normalized[1]).toBeCloseTo(0.8)
|
|
241
|
+
expect(normalized[2]).toBeCloseTo(0)
|
|
242
|
+
|
|
243
|
+
// Check magnitude is 1
|
|
244
|
+
const magnitude = Math.sqrt(
|
|
245
|
+
normalized.reduce((sum, val) => sum + val * val, 0)
|
|
246
|
+
)
|
|
247
|
+
expect(magnitude).toBeCloseTo(1)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('handles already normalized vectors', () => {
|
|
251
|
+
const embedding = [1, 0, 0]
|
|
252
|
+
const normalized = normalizeEmbedding(embedding)
|
|
253
|
+
|
|
254
|
+
expect(normalized).toEqual([1, 0, 0])
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('handles zero vector', () => {
|
|
258
|
+
const embedding = [0, 0, 0]
|
|
259
|
+
const normalized = normalizeEmbedding(embedding)
|
|
260
|
+
|
|
261
|
+
expect(normalized).toEqual([0, 0, 0])
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// Skip API-dependent tests if no gateway
|
|
266
|
+
const hasGateway = !!process.env.AI_GATEWAY_URL || !!process.env.CF_ACCOUNT_ID
|
|
267
|
+
|
|
268
|
+
describe.skipIf(!hasGateway)('embedText and embedTexts', () => {
|
|
269
|
+
// These tests would require actual API calls
|
|
270
|
+
// They're here as placeholders for when the gateway is available
|
|
271
|
+
|
|
272
|
+
it.skip('embeds a single text', async () => {
|
|
273
|
+
// const { embedText } = await import('../src/index.js')
|
|
274
|
+
// const { embedding } = await embedText('hello world')
|
|
275
|
+
// expect(Array.isArray(embedding)).toBe(true)
|
|
276
|
+
// expect(embedding.length).toBeGreaterThan(0)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it.skip('embeds multiple texts', async () => {
|
|
280
|
+
// const { embedTexts } = await import('../src/index.js')
|
|
281
|
+
// const { embeddings } = await embedTexts(['doc1', 'doc2', 'doc3'])
|
|
282
|
+
// expect(embeddings).toHaveLength(3)
|
|
283
|
+
})
|
|
284
|
+
})
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineFunction Eval Suite
|
|
3
|
+
*
|
|
4
|
+
* Tests the defineFunction API with real AI calls across multiple models.
|
|
5
|
+
* Ensures that defined functions work correctly with generative, agentic, and code types.
|
|
6
|
+
*
|
|
7
|
+
* Run with: pnpm test -- test/evals/define-function.eval.test.ts
|
|
8
|
+
* Run specific model: MODEL=sonnet pnpm test -- test/evals/define-function.eval.test.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
12
|
+
import { define, defineFunction, functions } from '../../src/index.js'
|
|
13
|
+
import { EVAL_MODELS, type EvalModel, type ModelTier } from '../../src/eval/models.js'
|
|
14
|
+
|
|
15
|
+
// Configuration
|
|
16
|
+
const EVAL_TIERS: ModelTier[] = (process.env.EVAL_TIERS?.split(',') as ModelTier[]) || ['fast']
|
|
17
|
+
const SINGLE_MODEL = process.env.MODEL
|
|
18
|
+
|
|
19
|
+
// Get models to test
|
|
20
|
+
function getTestModels(): EvalModel[] {
|
|
21
|
+
if (SINGLE_MODEL) {
|
|
22
|
+
const model = EVAL_MODELS.find(m => m.id === SINGLE_MODEL || m.name.toLowerCase().includes(SINGLE_MODEL.toLowerCase()))
|
|
23
|
+
return model ? [model] : EVAL_MODELS.filter(m => m.tier === 'fast').slice(0, 1)
|
|
24
|
+
}
|
|
25
|
+
return EVAL_MODELS.filter(m => EVAL_TIERS.includes(m.tier))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Skip if no API access
|
|
29
|
+
const hasAPI = !!process.env.AI_GATEWAY_URL || !!process.env.ANTHROPIC_API_KEY || !!process.env.OPENAI_API_KEY
|
|
30
|
+
|
|
31
|
+
// Test timeout for AI calls
|
|
32
|
+
const AI_TIMEOUT = 30000
|
|
33
|
+
|
|
34
|
+
describe.skipIf(!hasAPI)('defineFunction Evals', () => {
|
|
35
|
+
const models = getTestModels()
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
functions.clear()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// ==========================================================================
|
|
42
|
+
// Generative Functions
|
|
43
|
+
// ==========================================================================
|
|
44
|
+
describe('define.generative()', () => {
|
|
45
|
+
describe('string output functions', () => {
|
|
46
|
+
for (const model of models) {
|
|
47
|
+
describe(`[${model.name}]`, () => {
|
|
48
|
+
it('executes greeting function', async () => {
|
|
49
|
+
const greet = define.generative({
|
|
50
|
+
name: 'greet',
|
|
51
|
+
args: { name: 'Name to greet' },
|
|
52
|
+
output: 'string',
|
|
53
|
+
promptTemplate: 'Say hello to {{name}} in a friendly way',
|
|
54
|
+
model: model.id,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const result = await greet.call({ name: 'World' })
|
|
58
|
+
expect(typeof result).toBe('string')
|
|
59
|
+
expect((result as string).toLowerCase()).toMatch(/hello|hi|hey|greet/)
|
|
60
|
+
}, AI_TIMEOUT)
|
|
61
|
+
|
|
62
|
+
it('executes translation function', async () => {
|
|
63
|
+
const translate = define.generative({
|
|
64
|
+
name: 'translate',
|
|
65
|
+
args: {
|
|
66
|
+
text: 'Text to translate',
|
|
67
|
+
targetLang: 'Target language',
|
|
68
|
+
},
|
|
69
|
+
output: 'string',
|
|
70
|
+
promptTemplate: 'Translate "{{text}}" to {{targetLang}}',
|
|
71
|
+
model: model.id,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const result = await translate.call({ text: 'Hello', targetLang: 'Spanish' })
|
|
75
|
+
expect(typeof result).toBe('string')
|
|
76
|
+
expect((result as string).toLowerCase()).toMatch(/hola|buenos/)
|
|
77
|
+
}, AI_TIMEOUT)
|
|
78
|
+
|
|
79
|
+
it('executes summarization function', async () => {
|
|
80
|
+
const summarizeText = define.generative({
|
|
81
|
+
name: 'summarizeText',
|
|
82
|
+
args: { text: 'Text to summarize' },
|
|
83
|
+
output: 'string',
|
|
84
|
+
promptTemplate: 'Summarize the following in one sentence: {{text}}',
|
|
85
|
+
model: model.id,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const longText = 'TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale. TypeScript adds additional syntax to JavaScript to support a tighter integration with your editor. Catch errors early in your editor.'
|
|
89
|
+
|
|
90
|
+
const result = await summarizeText.call({ text: longText })
|
|
91
|
+
expect(typeof result).toBe('string')
|
|
92
|
+
expect((result as string).length).toBeGreaterThan(10)
|
|
93
|
+
expect((result as string).length).toBeLessThan(longText.length)
|
|
94
|
+
}, AI_TIMEOUT)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('object output functions', () => {
|
|
100
|
+
for (const model of models) {
|
|
101
|
+
describe(`[${model.name}]`, () => {
|
|
102
|
+
it('executes sentiment analysis function', async () => {
|
|
103
|
+
const analyzeSentiment = define.generative({
|
|
104
|
+
name: 'analyzeSentiment',
|
|
105
|
+
args: { text: 'Text to analyze' },
|
|
106
|
+
output: 'object',
|
|
107
|
+
returnType: {
|
|
108
|
+
sentiment: 'positive | negative | neutral',
|
|
109
|
+
confidence: 'Confidence score 0-1 (number)',
|
|
110
|
+
reasoning: 'Brief explanation',
|
|
111
|
+
},
|
|
112
|
+
promptTemplate: 'Analyze the sentiment of: {{text}}',
|
|
113
|
+
model: model.id,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const result = await analyzeSentiment.call({ text: 'I absolutely love this product!' }) as {
|
|
117
|
+
sentiment: string
|
|
118
|
+
confidence: number
|
|
119
|
+
reasoning: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
expect(result).toBeDefined()
|
|
123
|
+
expect(['positive', 'negative', 'neutral']).toContain(result.sentiment)
|
|
124
|
+
expect(typeof result.confidence).toBe('number')
|
|
125
|
+
expect(result.confidence).toBeGreaterThanOrEqual(0)
|
|
126
|
+
expect(result.confidence).toBeLessThanOrEqual(1)
|
|
127
|
+
}, AI_TIMEOUT)
|
|
128
|
+
|
|
129
|
+
it('executes entity extraction function', async () => {
|
|
130
|
+
const extractEntities = define.generative({
|
|
131
|
+
name: 'extractEntities',
|
|
132
|
+
args: { text: 'Text to extract entities from' },
|
|
133
|
+
output: 'object',
|
|
134
|
+
returnType: {
|
|
135
|
+
people: ['Names of people mentioned'],
|
|
136
|
+
organizations: ['Names of organizations mentioned'],
|
|
137
|
+
locations: ['Location names mentioned'],
|
|
138
|
+
},
|
|
139
|
+
promptTemplate: 'Extract named entities from: {{text}}',
|
|
140
|
+
model: model.id,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const result = await extractEntities.call({
|
|
144
|
+
text: 'John Smith from Google met with Jane Doe at the New York office to discuss the project.',
|
|
145
|
+
}) as {
|
|
146
|
+
people: string[]
|
|
147
|
+
organizations: string[]
|
|
148
|
+
locations: string[]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
expect(result).toBeDefined()
|
|
152
|
+
expect(Array.isArray(result.people)).toBe(true)
|
|
153
|
+
expect(Array.isArray(result.organizations)).toBe(true)
|
|
154
|
+
expect(Array.isArray(result.locations)).toBe(true)
|
|
155
|
+
|
|
156
|
+
// Should find at least some entities
|
|
157
|
+
const totalEntities = result.people.length + result.organizations.length + result.locations.length
|
|
158
|
+
expect(totalEntities).toBeGreaterThanOrEqual(2)
|
|
159
|
+
}, AI_TIMEOUT)
|
|
160
|
+
|
|
161
|
+
it('executes classification function', async () => {
|
|
162
|
+
const classifyTicket = define.generative({
|
|
163
|
+
name: 'classifyTicket',
|
|
164
|
+
args: { ticket: 'Support ticket text' },
|
|
165
|
+
output: 'object',
|
|
166
|
+
returnType: {
|
|
167
|
+
category: 'billing | technical | account | shipping',
|
|
168
|
+
priority: 'low | medium | high',
|
|
169
|
+
suggestedAction: 'Brief suggested action',
|
|
170
|
+
},
|
|
171
|
+
promptTemplate: 'Classify this support ticket: {{ticket}}',
|
|
172
|
+
model: model.id,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const result = await classifyTicket.call({
|
|
176
|
+
ticket: 'My credit card was charged twice for the same order and I need a refund immediately!',
|
|
177
|
+
}) as {
|
|
178
|
+
category: string
|
|
179
|
+
priority: string
|
|
180
|
+
suggestedAction: string
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
expect(result).toBeDefined()
|
|
184
|
+
expect(['billing', 'technical', 'account', 'shipping']).toContain(result.category)
|
|
185
|
+
expect(['low', 'medium', 'high']).toContain(result.priority)
|
|
186
|
+
expect(typeof result.suggestedAction).toBe('string')
|
|
187
|
+
}, AI_TIMEOUT)
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// ==========================================================================
|
|
194
|
+
// Code Generation Functions
|
|
195
|
+
// ==========================================================================
|
|
196
|
+
describe('define.code()', () => {
|
|
197
|
+
for (const model of models) {
|
|
198
|
+
describe(`[${model.name}]`, () => {
|
|
199
|
+
it('generates TypeScript function', async () => {
|
|
200
|
+
const generateTs = define.code({
|
|
201
|
+
name: 'generateTypeScript',
|
|
202
|
+
args: { spec: 'Function specification' },
|
|
203
|
+
language: 'typescript',
|
|
204
|
+
model: model.id,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
const result = await generateTs.call({
|
|
208
|
+
spec: 'A function that checks if a string is a valid URL',
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(typeof result).toBe('string')
|
|
212
|
+
expect((result as string).length).toBeGreaterThan(20)
|
|
213
|
+
// Should contain TypeScript/JavaScript-like syntax
|
|
214
|
+
expect(result as string).toMatch(/function|const|=>|return/)
|
|
215
|
+
}, AI_TIMEOUT)
|
|
216
|
+
|
|
217
|
+
it('generates Python function', async () => {
|
|
218
|
+
const generatePy = define.code({
|
|
219
|
+
name: 'generatePython',
|
|
220
|
+
args: { spec: 'Function specification' },
|
|
221
|
+
language: 'python',
|
|
222
|
+
model: model.id,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const result = await generatePy.call({
|
|
226
|
+
spec: 'A function that calculates the factorial of a number',
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
expect(typeof result).toBe('string')
|
|
230
|
+
expect((result as string).length).toBeGreaterThan(20)
|
|
231
|
+
// Should contain Python-like syntax
|
|
232
|
+
expect(result as string).toMatch(/def|return|if|else/)
|
|
233
|
+
}, AI_TIMEOUT)
|
|
234
|
+
|
|
235
|
+
it('generates SQL query', async () => {
|
|
236
|
+
const generateSql = define.code({
|
|
237
|
+
name: 'generateSql',
|
|
238
|
+
args: { spec: 'Query specification' },
|
|
239
|
+
language: 'sql',
|
|
240
|
+
model: model.id,
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const result = await generateSql.call({
|
|
244
|
+
spec: 'Select all users who signed up in the last 30 days',
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
expect(typeof result).toBe('string')
|
|
248
|
+
expect((result as string).toUpperCase()).toMatch(/SELECT|FROM|WHERE/)
|
|
249
|
+
}, AI_TIMEOUT)
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// ==========================================================================
|
|
255
|
+
// Function Registration and Tool Generation
|
|
256
|
+
// ==========================================================================
|
|
257
|
+
describe('function registry and tools', () => {
|
|
258
|
+
it('registers functions in the registry', () => {
|
|
259
|
+
const fn = define.generative({
|
|
260
|
+
name: 'testRegistration',
|
|
261
|
+
args: { input: 'Test input' },
|
|
262
|
+
output: 'string',
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
expect(functions.has('testRegistration')).toBe(true)
|
|
266
|
+
expect(functions.get('testRegistration')).toBeDefined()
|
|
267
|
+
expect(functions.list()).toContain('testRegistration')
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('generates valid tool schema', () => {
|
|
271
|
+
const fn = define.generative({
|
|
272
|
+
name: 'processData',
|
|
273
|
+
description: 'Process input data and return results',
|
|
274
|
+
args: {
|
|
275
|
+
data: 'The data to process',
|
|
276
|
+
format: 'Output format (json | csv | xml)',
|
|
277
|
+
},
|
|
278
|
+
output: 'string',
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const tool = fn.asTool()
|
|
282
|
+
|
|
283
|
+
expect(tool.name).toBe('processData')
|
|
284
|
+
expect(tool.description).toBe('Process input data and return results')
|
|
285
|
+
expect(tool.parameters.type).toBe('object')
|
|
286
|
+
expect(tool.parameters.properties).toHaveProperty('data')
|
|
287
|
+
expect(tool.parameters.properties).toHaveProperty('format')
|
|
288
|
+
expect(tool.parameters.required).toContain('data')
|
|
289
|
+
expect(tool.parameters.required).toContain('format')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('clears all registered functions', () => {
|
|
293
|
+
define.generative({ name: 'fn1', args: {}, output: 'string' })
|
|
294
|
+
define.generative({ name: 'fn2', args: {}, output: 'string' })
|
|
295
|
+
define.generative({ name: 'fn3', args: {}, output: 'string' })
|
|
296
|
+
|
|
297
|
+
expect(functions.list().length).toBeGreaterThanOrEqual(3)
|
|
298
|
+
|
|
299
|
+
functions.clear()
|
|
300
|
+
|
|
301
|
+
expect(functions.list()).toEqual([])
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
// ==========================================================================
|
|
306
|
+
// Complex Multi-Step Functions
|
|
307
|
+
// ==========================================================================
|
|
308
|
+
describe('complex functions', () => {
|
|
309
|
+
for (const model of models) {
|
|
310
|
+
describe(`[${model.name}]`, () => {
|
|
311
|
+
it('executes multi-output analysis function', async () => {
|
|
312
|
+
const analyzeArticle = define.generative({
|
|
313
|
+
name: 'analyzeArticle',
|
|
314
|
+
args: { article: 'Article text to analyze' },
|
|
315
|
+
output: 'object',
|
|
316
|
+
returnType: {
|
|
317
|
+
title: 'Generated title for the article',
|
|
318
|
+
summary: 'Brief summary (2-3 sentences)',
|
|
319
|
+
keyPoints: ['Main points from the article'],
|
|
320
|
+
readingTime: 'Estimated reading time in minutes (number)',
|
|
321
|
+
},
|
|
322
|
+
promptTemplate: 'Analyze this article and provide structured output: {{article}}',
|
|
323
|
+
model: model.id,
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
const article = `
|
|
327
|
+
Machine learning has revolutionized the tech industry. Companies are using AI to automate tasks,
|
|
328
|
+
improve customer service, and make better predictions. From healthcare to finance, the applications
|
|
329
|
+
are endless. However, there are concerns about job displacement and ethical use of AI.
|
|
330
|
+
`
|
|
331
|
+
|
|
332
|
+
const result = await analyzeArticle.call({ article }) as {
|
|
333
|
+
title: string
|
|
334
|
+
summary: string
|
|
335
|
+
keyPoints: string[]
|
|
336
|
+
readingTime: number
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
expect(result).toBeDefined()
|
|
340
|
+
expect(typeof result.title).toBe('string')
|
|
341
|
+
expect(result.title.length).toBeGreaterThan(5)
|
|
342
|
+
expect(typeof result.summary).toBe('string')
|
|
343
|
+
expect(result.summary.length).toBeGreaterThan(20)
|
|
344
|
+
expect(Array.isArray(result.keyPoints)).toBe(true)
|
|
345
|
+
expect(result.keyPoints.length).toBeGreaterThanOrEqual(1)
|
|
346
|
+
expect(typeof result.readingTime).toBe('number')
|
|
347
|
+
}, AI_TIMEOUT)
|
|
348
|
+
|
|
349
|
+
it('executes math problem solver', async () => {
|
|
350
|
+
const solveMath = define.generative({
|
|
351
|
+
name: 'solveMath',
|
|
352
|
+
args: { problem: 'Math problem to solve' },
|
|
353
|
+
output: 'object',
|
|
354
|
+
returnType: {
|
|
355
|
+
answer: 'The numeric answer (number)',
|
|
356
|
+
steps: ['Step by step solution'],
|
|
357
|
+
formula: 'Mathematical formula used (if applicable)',
|
|
358
|
+
},
|
|
359
|
+
promptTemplate: 'Solve this math problem step by step: {{problem}}',
|
|
360
|
+
model: model.id,
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
const result = await solveMath.call({
|
|
364
|
+
problem: 'What is 15% of 200?',
|
|
365
|
+
}) as {
|
|
366
|
+
answer: number
|
|
367
|
+
steps: string[]
|
|
368
|
+
formula: string
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
expect(result).toBeDefined()
|
|
372
|
+
expect(typeof result.answer).toBe('number')
|
|
373
|
+
expect(Math.abs(result.answer - 30)).toBeLessThan(1) // Allow small floating point difference
|
|
374
|
+
expect(Array.isArray(result.steps)).toBe(true)
|
|
375
|
+
}, AI_TIMEOUT)
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
})
|