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,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Output Eval
|
|
3
|
+
*
|
|
4
|
+
* Tests model ability to generate valid structured JSON output
|
|
5
|
+
* matching specified schemas across all providers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { evalite } from 'evalite'
|
|
9
|
+
import { generateObject } from '../src/generate.js'
|
|
10
|
+
import { schema } from '../src/schema.js'
|
|
11
|
+
import { createModelVariants, getModelPricing, type EvalModel } from '../src/eval/models.js'
|
|
12
|
+
|
|
13
|
+
// Test cases for structured output
|
|
14
|
+
const TEST_CASES = [
|
|
15
|
+
{
|
|
16
|
+
name: 'Simple object',
|
|
17
|
+
prompt: 'Generate a greeting in French',
|
|
18
|
+
schema: {
|
|
19
|
+
greeting: 'A friendly greeting',
|
|
20
|
+
language: 'The language of the greeting',
|
|
21
|
+
},
|
|
22
|
+
expectedTypes: { greeting: 'string', language: 'string' },
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'With numbers',
|
|
26
|
+
prompt: 'Generate info about Tokyo',
|
|
27
|
+
schema: {
|
|
28
|
+
name: 'City name',
|
|
29
|
+
population: 'Population in millions (number)',
|
|
30
|
+
area: 'Area in square kilometers (number)',
|
|
31
|
+
},
|
|
32
|
+
expectedTypes: { name: 'string', population: 'number', area: 'number' },
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'With arrays',
|
|
36
|
+
prompt: 'Generate a simple pasta recipe',
|
|
37
|
+
schema: {
|
|
38
|
+
title: 'Recipe title',
|
|
39
|
+
ingredients: ['List of ingredients'],
|
|
40
|
+
steps: ['Cooking steps'],
|
|
41
|
+
},
|
|
42
|
+
expectedTypes: { title: 'string', ingredients: 'array', steps: 'array' },
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'With enum',
|
|
46
|
+
prompt: 'Analyze sentiment: "I love this product!"',
|
|
47
|
+
schema: {
|
|
48
|
+
sentiment: 'positive | negative | neutral',
|
|
49
|
+
confidence: 'Confidence score 0-1 (number)',
|
|
50
|
+
},
|
|
51
|
+
expectedTypes: { sentiment: 'string', confidence: 'number' },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Nested object',
|
|
55
|
+
prompt: 'Generate a fictional person living in Japan',
|
|
56
|
+
schema: {
|
|
57
|
+
person: { name: 'Full name', age: 'Age (number)' },
|
|
58
|
+
address: { city: 'City name', country: 'Country name' },
|
|
59
|
+
},
|
|
60
|
+
expectedTypes: { person: 'object', address: 'object' },
|
|
61
|
+
},
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
// Test across models - start with fast tier for quick iteration
|
|
65
|
+
const modelVariants = createModelVariants({ tiers: ['fast'] })
|
|
66
|
+
|
|
67
|
+
evalite.each(modelVariants)('Structured Output', {
|
|
68
|
+
data: TEST_CASES.map(tc => ({ input: tc })),
|
|
69
|
+
|
|
70
|
+
task: async (input, variant) => {
|
|
71
|
+
const model = variant as EvalModel
|
|
72
|
+
const startTime = Date.now()
|
|
73
|
+
|
|
74
|
+
const { object, usage } = await generateObject({
|
|
75
|
+
model: model.id,
|
|
76
|
+
schema: schema(input.schema),
|
|
77
|
+
prompt: input.prompt,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const latencyMs = Date.now() - startTime
|
|
81
|
+
|
|
82
|
+
// Calculate cost from language-models pricing
|
|
83
|
+
const pricing = getModelPricing(model.id)
|
|
84
|
+
const cost = pricing
|
|
85
|
+
? ((usage?.promptTokens ?? 0) * pricing.prompt + (usage?.completionTokens ?? 0) * pricing.completion) / 1_000_000
|
|
86
|
+
: 0
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
object,
|
|
90
|
+
expectedTypes: input.expectedTypes,
|
|
91
|
+
testName: input.name,
|
|
92
|
+
modelId: model.id,
|
|
93
|
+
modelName: model.name,
|
|
94
|
+
provider: model.provider,
|
|
95
|
+
latencyMs,
|
|
96
|
+
cost,
|
|
97
|
+
usage,
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
scorers: [
|
|
102
|
+
// Type accuracy
|
|
103
|
+
{
|
|
104
|
+
name: 'Type Accuracy',
|
|
105
|
+
description: 'Whether output fields have correct types',
|
|
106
|
+
scorer: ({ output }) => {
|
|
107
|
+
const obj = output.object as Record<string, unknown>
|
|
108
|
+
const expected = output.expectedTypes as Record<string, string>
|
|
109
|
+
const fields = Object.keys(expected)
|
|
110
|
+
|
|
111
|
+
let correct = 0
|
|
112
|
+
for (const field of fields) {
|
|
113
|
+
const val = obj[field]
|
|
114
|
+
const expectedType = expected[field]
|
|
115
|
+
const actualType = Array.isArray(val) ? 'array' : typeof val
|
|
116
|
+
|
|
117
|
+
if (actualType === expectedType) correct++
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { score: correct / fields.length }
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
// Latency
|
|
125
|
+
{
|
|
126
|
+
name: 'Latency',
|
|
127
|
+
description: 'Response time (target < 3s)',
|
|
128
|
+
scorer: ({ output }) => {
|
|
129
|
+
const ms = output.latencyMs as number
|
|
130
|
+
if (ms < 2000) return { score: 1 }
|
|
131
|
+
if (ms > 10000) return { score: 0 }
|
|
132
|
+
return { score: 1 - (ms - 2000) / 8000 }
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
|
|
137
|
+
columns: ({ output, scores }) => [
|
|
138
|
+
{ label: 'Model', value: output.modelName },
|
|
139
|
+
{ label: 'Test', value: output.testName },
|
|
140
|
+
{ label: 'Latency', value: `${output.latencyMs}ms` },
|
|
141
|
+
{ label: 'Cost', value: `$${(output.cost as number).toFixed(6)}` },
|
|
142
|
+
],
|
|
143
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing Quality Eval (LLM-as-Judge)
|
|
3
|
+
*
|
|
4
|
+
* Tests model writing capabilities using LLM-as-judge scoring.
|
|
5
|
+
* Uses a strong model (sonnet) to judge output quality.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { evalite } from 'evalite'
|
|
9
|
+
import { generateText, generateObject } from '../src/generate.js'
|
|
10
|
+
import { schema } from '../src/schema.js'
|
|
11
|
+
import { createModelVariants, type EvalModel } from '../src/eval/models.js'
|
|
12
|
+
|
|
13
|
+
// Use sonnet as the judge model
|
|
14
|
+
const JUDGE_MODEL = 'sonnet'
|
|
15
|
+
|
|
16
|
+
// Writing test cases
|
|
17
|
+
const TEST_CASES = [
|
|
18
|
+
{
|
|
19
|
+
name: 'Professional email',
|
|
20
|
+
prompt: 'Write a professional email declining a meeting invitation politely.',
|
|
21
|
+
criteria: ['Polite tone', 'Clear explanation', 'Proper email format'],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Product description',
|
|
25
|
+
prompt: 'Write a product description for wireless earbuds targeting tech-savvy consumers.',
|
|
26
|
+
criteria: ['Highlights features', 'Compelling language', 'Clear value proposition'],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Explanation',
|
|
30
|
+
prompt: 'Explain how photosynthesis works in simple terms for a high school student.',
|
|
31
|
+
criteria: ['Accurate content', 'Clear language', 'Logical flow'],
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
const modelVariants = createModelVariants({ tiers: ['fast'] })
|
|
36
|
+
|
|
37
|
+
evalite.each(modelVariants)('Writing Quality', {
|
|
38
|
+
data: TEST_CASES.map(tc => ({ input: tc })),
|
|
39
|
+
|
|
40
|
+
task: async (input, variant) => {
|
|
41
|
+
const model = variant as EvalModel
|
|
42
|
+
const startTime = Date.now()
|
|
43
|
+
|
|
44
|
+
// Generate the writing
|
|
45
|
+
const { text, usage } = await generateText({
|
|
46
|
+
model: model.id,
|
|
47
|
+
prompt: input.prompt,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const latencyMs = Date.now() - startTime
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
text,
|
|
54
|
+
testName: input.name,
|
|
55
|
+
criteria: input.criteria,
|
|
56
|
+
modelId: model.id,
|
|
57
|
+
modelName: model.name,
|
|
58
|
+
provider: model.provider,
|
|
59
|
+
latencyMs,
|
|
60
|
+
usage,
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
scorers: [
|
|
65
|
+
// LLM-as-judge for quality
|
|
66
|
+
{
|
|
67
|
+
name: 'Writing Quality',
|
|
68
|
+
description: 'LLM judge evaluation of writing quality',
|
|
69
|
+
scorer: async ({ input, output }) => {
|
|
70
|
+
const { object } = await generateObject({
|
|
71
|
+
model: JUDGE_MODEL,
|
|
72
|
+
schema: schema({
|
|
73
|
+
clarity: 'How clear is the writing? (number 0-1)',
|
|
74
|
+
engagement: 'How engaging is the content? (number 0-1)',
|
|
75
|
+
accuracy: 'How well does it meet the criteria? (number 0-1)',
|
|
76
|
+
reasoning: 'Brief explanation',
|
|
77
|
+
}),
|
|
78
|
+
prompt: `Evaluate this writing on a scale of 0-1.
|
|
79
|
+
|
|
80
|
+
Criteria: ${(input.criteria as string[]).join(', ')}
|
|
81
|
+
|
|
82
|
+
Writing:
|
|
83
|
+
"""
|
|
84
|
+
${output.text}
|
|
85
|
+
"""`,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const avg = ((object.clarity as number) + (object.engagement as number) + (object.accuracy as number)) / 3
|
|
89
|
+
return {
|
|
90
|
+
score: avg,
|
|
91
|
+
metadata: object,
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Word count check
|
|
97
|
+
{
|
|
98
|
+
name: 'Appropriate Length',
|
|
99
|
+
description: 'Whether output has reasonable length',
|
|
100
|
+
scorer: ({ output }) => {
|
|
101
|
+
const words = (output.text as string).split(/\s+/).length
|
|
102
|
+
if (words < 20) return { score: 0.3, metadata: { words } }
|
|
103
|
+
if (words > 500) return { score: 0.7, metadata: { words } }
|
|
104
|
+
return { score: 1, metadata: { words } }
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
|
|
109
|
+
columns: ({ output }) => [
|
|
110
|
+
{ label: 'Model', value: output.modelName },
|
|
111
|
+
{ label: 'Test', value: output.testName },
|
|
112
|
+
{ label: 'Words', value: (output.text as string).split(/\s+/).length },
|
|
113
|
+
{ label: 'Latency', value: `${output.latencyMs}ms` },
|
|
114
|
+
],
|
|
115
|
+
|
|
116
|
+
trialCount: 2, // Run twice for variance
|
|
117
|
+
})
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Blog Post Generation Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the new IMPLICIT batch processing:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* // Configure once (or use environment variables)
|
|
8
|
+
* configure({ provider: 'openai', model: 'gpt-4o', batchMode: 'auto' })
|
|
9
|
+
*
|
|
10
|
+
* // Use naturally - batching is automatic!
|
|
11
|
+
* const titles = await list`10 blog post titles about startups`
|
|
12
|
+
* const posts = titles.map(title => write`blog post: # ${title}`)
|
|
13
|
+
* console.log(await posts) // Batched automatically!
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Environment variables:
|
|
17
|
+
* - AI_PROVIDER: openai | anthropic | cloudflare | bedrock
|
|
18
|
+
* - AI_MODEL: model name (e.g., gpt-4o, claude-sonnet-4-20250514)
|
|
19
|
+
* - AI_BATCH_MODE: auto | immediate | deferred
|
|
20
|
+
* - AI_BATCH_THRESHOLD: minimum items for auto batch (default: 5)
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```bash
|
|
24
|
+
* # Using environment variables
|
|
25
|
+
* AI_PROVIDER=openai AI_MODEL=gpt-4o npx tsx examples/batch-blog-posts.ts
|
|
26
|
+
*
|
|
27
|
+
* # Or with API keys
|
|
28
|
+
* OPENAI_API_KEY=sk-... npx tsx examples/batch-blog-posts.ts
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
list,
|
|
34
|
+
write,
|
|
35
|
+
configure,
|
|
36
|
+
withContext,
|
|
37
|
+
type BatchProvider,
|
|
38
|
+
} from '../src/index.js'
|
|
39
|
+
|
|
40
|
+
// Import the batch adapter for your provider
|
|
41
|
+
// import '../src/batch/openai.js'
|
|
42
|
+
// import '../src/batch/anthropic.js'
|
|
43
|
+
// import '../src/batch/cloudflare.js'
|
|
44
|
+
// import '../src/batch/bedrock.js'
|
|
45
|
+
|
|
46
|
+
// For testing, use the memory adapter
|
|
47
|
+
import '../src/batch/memory.js'
|
|
48
|
+
|
|
49
|
+
async function main() {
|
|
50
|
+
console.log('\n🚀 Implicit Batch Blog Post Generation\n')
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Option 1: Global Configuration (recommended)
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
configure({
|
|
57
|
+
provider: 'openai',
|
|
58
|
+
model: 'gpt-4o',
|
|
59
|
+
batchMode: 'auto', // 'auto' | 'immediate' | 'deferred'
|
|
60
|
+
batchThreshold: 5, // Use batch API when >= 5 items
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
console.log('📝 Step 1: Generate titles (executes immediately)...')
|
|
64
|
+
const titles = await list`10 blog post titles about building startups in 2026`
|
|
65
|
+
|
|
66
|
+
console.log(`\nGenerated ${(titles as any).length || 10} titles`)
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Option 2: The Clean API (what you asked for!)
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
console.log('\n⚡ Step 2: Map titles to blog posts (automatic batching)...')
|
|
73
|
+
console.log(' Code: titles.map(title => write`blog post: # ${title}`)')
|
|
74
|
+
|
|
75
|
+
// This is the API you wanted!
|
|
76
|
+
// - No explicit batch creation
|
|
77
|
+
// - No provider/model in the code
|
|
78
|
+
// - Automatic batch detection based on context
|
|
79
|
+
const posts = (titles as string[]).map(title =>
|
|
80
|
+
write`Write a comprehensive blog post for startup founders:
|
|
81
|
+
|
|
82
|
+
# ${title}
|
|
83
|
+
|
|
84
|
+
Include:
|
|
85
|
+
- Attention-grabbing introduction
|
|
86
|
+
- 3-5 key sections with actionable insights
|
|
87
|
+
- Real-world examples
|
|
88
|
+
- Compelling conclusion with call-to-action`
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
console.log(` Created ${posts.length} deferred operations`)
|
|
92
|
+
|
|
93
|
+
// When you await, it resolves via batch API if beneficial
|
|
94
|
+
console.log('\n⏳ Step 3: Await results (batched automatically)...')
|
|
95
|
+
// Note: Each item is an AIPromise, we'd await them all
|
|
96
|
+
// const results = await Promise.all(posts)
|
|
97
|
+
|
|
98
|
+
console.log('\n✅ Done!')
|
|
99
|
+
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Option 3: Scoped Context (for different providers in same code)
|
|
102
|
+
// ============================================================================
|
|
103
|
+
|
|
104
|
+
console.log('\n🔄 Bonus: Using withContext for scoped configuration...')
|
|
105
|
+
|
|
106
|
+
await withContext(
|
|
107
|
+
{ provider: 'anthropic', model: 'claude-sonnet-4-20250514', batchMode: 'deferred' },
|
|
108
|
+
async () => {
|
|
109
|
+
console.log(' Inside context: Using Anthropic with deferred batching')
|
|
110
|
+
// All operations here use Anthropic
|
|
111
|
+
// const summaries = titles.map(title => write`summarize: ${title}`)
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
console.log(' Outside context: Back to OpenAI')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Summary of the API
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
The new API is clean and implicit:
|
|
124
|
+
|
|
125
|
+
1. Configure once (globally or via environment):
|
|
126
|
+
```ts
|
|
127
|
+
configure({ provider: 'openai', model: 'gpt-4o', batchMode: 'auto' })
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
2. Use naturally:
|
|
131
|
+
```ts
|
|
132
|
+
const titles = await list`10 blog post titles`
|
|
133
|
+
const posts = titles.map(title => write`blog post: # ${title}`)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
3. Batching happens automatically when:
|
|
137
|
+
- batchMode is 'auto' and items >= batchThreshold
|
|
138
|
+
- batchMode is 'deferred' (always batch)
|
|
139
|
+
|
|
140
|
+
4. No batching when:
|
|
141
|
+
- batchMode is 'immediate'
|
|
142
|
+
- batchMode is 'auto' and items < batchThreshold
|
|
143
|
+
|
|
144
|
+
5. Provider batch APIs supported:
|
|
145
|
+
- OpenAI: 50% discount, 24hr turnaround
|
|
146
|
+
- Anthropic: 50% discount, 24hr turnaround
|
|
147
|
+
- Cloudflare: Via AI Gateway
|
|
148
|
+
- AWS Bedrock: Native batch inference
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
// Run the example
|
|
152
|
+
main()
|
|
153
|
+
.then(() => {
|
|
154
|
+
console.log('\n✨ Example complete!\n')
|
|
155
|
+
process.exit(0)
|
|
156
|
+
})
|
|
157
|
+
.catch((error) => {
|
|
158
|
+
console.error('\n❌ Error:', error.message)
|
|
159
|
+
process.exit(1)
|
|
160
|
+
})
|
package/package.json
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-functions",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Core AI primitives for building intelligent applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
-
"module": "dist/index.js",
|
|
8
7
|
"types": "dist/index.d.ts",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./embeddings": {
|
|
14
|
+
"import": "./dist/embeddings.js",
|
|
15
|
+
"types": "./dist/embeddings.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./providers": {
|
|
18
|
+
"import": "./dist/providers/index.js",
|
|
19
|
+
"types": "./dist/providers/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./providers/cloudflare": {
|
|
22
|
+
"import": "./dist/providers/cloudflare.js",
|
|
23
|
+
"types": "./dist/providers/cloudflare.d.ts"
|
|
24
|
+
}
|
|
11
25
|
},
|
|
12
|
-
"files": [
|
|
13
|
-
"dist",
|
|
14
|
-
"bin"
|
|
15
|
-
],
|
|
16
26
|
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"
|
|
19
|
-
"test
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
29
|
+
"test": "vitest",
|
|
30
|
+
"test:unit": "vitest run --exclude 'test/evals/**' --exclude 'test/e2e-*'",
|
|
31
|
+
"test:evals": "vitest run test/evals/",
|
|
32
|
+
"test:evals:primitives": "vitest run test/evals/primitives.eval.test.ts",
|
|
33
|
+
"test:evals:define": "vitest run test/evals/define-function.eval.test.ts",
|
|
34
|
+
"eval": "tsx evals/run-evals.ts",
|
|
35
|
+
"eval:fast": "tsx evals/run-evals.ts",
|
|
36
|
+
"eval:all": "tsx evals/run-evals.ts --all",
|
|
37
|
+
"eval:math": "tsx evals/run-evals.ts --math",
|
|
38
|
+
"eval:class": "tsx evals/run-evals.ts --class",
|
|
39
|
+
"eval:marketing": "tsx evals/marketing.eval.ts",
|
|
40
|
+
"eval:marketing:all": "tsx evals/marketing.eval.ts --all",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"clean": "rm -rf dist"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@ai-sdk/amazon-bedrock": "^3.0.0",
|
|
47
|
+
"ai": "^5.0.0",
|
|
48
|
+
"ai-providers": "workspace:^",
|
|
49
|
+
"language-models": "workspace:*",
|
|
50
|
+
"rpc.do": "^0.1.0",
|
|
51
|
+
"yaml": "^2.8.0",
|
|
52
|
+
"zod": "^3.23.0"
|
|
53
|
+
},
|
|
54
|
+
"optionalDependencies": {
|
|
55
|
+
"oauth.do": "^0.0.1"
|
|
24
56
|
},
|
|
25
57
|
"keywords": [
|
|
26
58
|
"ai",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
59
|
+
"functions",
|
|
60
|
+
"embeddings",
|
|
61
|
+
"cloudflare",
|
|
62
|
+
"oauth",
|
|
63
|
+
"primitives"
|
|
32
64
|
],
|
|
33
|
-
"author": "AI Primitives",
|
|
34
65
|
"license": "MIT",
|
|
35
|
-
"homepage": "https://github.com/ai-primitives/ai-functions#readme",
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/ai-primitives/ai-functions.git"
|
|
39
|
-
},
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://github.com/ai-primitives/ai-functions/issues"
|
|
42
|
-
},
|
|
43
66
|
"devDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"@opentelemetry/api": "^1.9.0",
|
|
49
|
-
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
50
|
-
"@semantic-release/github": "^11.0.1",
|
|
51
|
-
"@semantic-release/npm": "^12.0.1",
|
|
52
|
-
"@semantic-release/release-notes-generator": "^14.0.1",
|
|
53
|
-
"@types/node": "^22.10.2",
|
|
54
|
-
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
55
|
-
"@typescript-eslint/parser": "^8.18.0",
|
|
56
|
-
"dotenv": "^16.4.7",
|
|
57
|
-
"eslint": "^9.17.0",
|
|
58
|
-
"prettier": "^3.4.2",
|
|
59
|
-
"semantic-release": "^24.2.0",
|
|
60
|
-
"typescript": "^5.7.2",
|
|
61
|
-
"undici": "^7.2.0",
|
|
62
|
-
"vitest": "^2.1.8",
|
|
63
|
-
"web-streams-polyfill": "^4.0.0"
|
|
64
|
-
},
|
|
65
|
-
"dependencies": {
|
|
66
|
-
"@ai-sdk/openai": "^1.0.10",
|
|
67
|
-
"@ai-sdk/openai-compatible": "^0.0.9",
|
|
68
|
-
"ai": "^4.0.20",
|
|
69
|
-
"p-queue": "^8.0.1",
|
|
70
|
-
"zod": "^3.24.1"
|
|
67
|
+
"dotenv": "^17.2.3",
|
|
68
|
+
"evalite": "^0.19.0",
|
|
69
|
+
"tsx": "^4.0.0",
|
|
70
|
+
"vitest": "^2.1.0"
|
|
71
71
|
}
|
|
72
72
|
}
|