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
package/src/context.ts
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Context for AI Functions
|
|
3
|
+
*
|
|
4
|
+
* Provides configuration without polluting function signatures.
|
|
5
|
+
* Settings flow from environment → global context → local context.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Set global defaults (from environment or initialization)
|
|
10
|
+
* configure({
|
|
11
|
+
* provider: 'anthropic',
|
|
12
|
+
* model: 'claude-sonnet-4-20250514',
|
|
13
|
+
* batchMode: 'auto', // 'auto' | 'immediate' | 'deferred'
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
16
|
+
* // Or use execution context for specific operations
|
|
17
|
+
* await withContext({ provider: 'openai', model: 'gpt-4o' }, async () => {
|
|
18
|
+
* const titles = await list`10 blog titles`
|
|
19
|
+
* return titles.map(title => write`blog post: ${title}`)
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { FunctionOptions } from './template.js'
|
|
27
|
+
import type { BatchProvider } from './batch-queue.js'
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Types
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/** Batch execution mode */
|
|
34
|
+
export type BatchMode =
|
|
35
|
+
| 'auto' // Smart selection: immediate < flexThreshold, flex < batchThreshold, batch above
|
|
36
|
+
| 'immediate' // Execute immediately (concurrent requests, full price)
|
|
37
|
+
| 'flex' // Use flex processing (faster than batch, ~50% discount, minutes)
|
|
38
|
+
| 'deferred' // Always use provider batch API (50% discount, up to 24hr)
|
|
39
|
+
|
|
40
|
+
/** Execution context configuration */
|
|
41
|
+
export interface ExecutionContext extends FunctionOptions {
|
|
42
|
+
/** Batch provider to use */
|
|
43
|
+
provider?: BatchProvider
|
|
44
|
+
/** Batch execution mode */
|
|
45
|
+
batchMode?: BatchMode
|
|
46
|
+
/** Minimum items to use flex processing (for 'auto' mode, default: 5) */
|
|
47
|
+
flexThreshold?: number
|
|
48
|
+
/** Minimum items to use batch API (for 'auto' mode, default: 500) */
|
|
49
|
+
batchThreshold?: number
|
|
50
|
+
/** Webhook URL for batch completion notifications */
|
|
51
|
+
webhookUrl?: string
|
|
52
|
+
/** Custom metadata for batch jobs */
|
|
53
|
+
metadata?: Record<string, unknown>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Global Context
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
let globalContext: ExecutionContext = {}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Configure global defaults for AI functions
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* configure({
|
|
68
|
+
* model: 'claude-sonnet-4-20250514',
|
|
69
|
+
* provider: 'anthropic',
|
|
70
|
+
* batchMode: 'auto',
|
|
71
|
+
* batchThreshold: 5,
|
|
72
|
+
* })
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function configure(context: ExecutionContext): void {
|
|
76
|
+
globalContext = { ...globalContext, ...context }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the current global context
|
|
81
|
+
*/
|
|
82
|
+
export function getGlobalContext(): ExecutionContext {
|
|
83
|
+
return { ...globalContext }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Reset global context to defaults
|
|
88
|
+
*/
|
|
89
|
+
export function resetContext(): void {
|
|
90
|
+
globalContext = {}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Async Local Storage for Execution Context
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
// Use AsyncLocalStorage if available (Node.js), otherwise fallback to global
|
|
98
|
+
let asyncLocalStorage: {
|
|
99
|
+
getStore: () => ExecutionContext | undefined
|
|
100
|
+
run: <T>(store: ExecutionContext, callback: () => T) => T
|
|
101
|
+
} | null = null
|
|
102
|
+
|
|
103
|
+
// Lazy initialization of AsyncLocalStorage
|
|
104
|
+
let asyncLocalStorageInitialized = false
|
|
105
|
+
|
|
106
|
+
async function initAsyncLocalStorage(): Promise<void> {
|
|
107
|
+
if (asyncLocalStorageInitialized) return
|
|
108
|
+
asyncLocalStorageInitialized = true
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const { AsyncLocalStorage } = await import('async_hooks')
|
|
112
|
+
asyncLocalStorage = new AsyncLocalStorage<ExecutionContext>()
|
|
113
|
+
} catch {
|
|
114
|
+
// Not in Node.js environment, use global context only
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Initialize synchronously if possible (for Node.js environments)
|
|
119
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
120
|
+
import('async_hooks')
|
|
121
|
+
.then(({ AsyncLocalStorage }) => {
|
|
122
|
+
asyncLocalStorage = new AsyncLocalStorage<ExecutionContext>()
|
|
123
|
+
asyncLocalStorageInitialized = true
|
|
124
|
+
})
|
|
125
|
+
.catch(() => {
|
|
126
|
+
asyncLocalStorageInitialized = true
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the current execution context
|
|
132
|
+
* Merges: environment defaults → global context → local context
|
|
133
|
+
*/
|
|
134
|
+
export function getContext(): ExecutionContext {
|
|
135
|
+
const envContext = getEnvContext()
|
|
136
|
+
const localContext = asyncLocalStorage?.getStore()
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
...envContext,
|
|
140
|
+
...globalContext,
|
|
141
|
+
...localContext,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Run a function with a specific execution context
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* const posts = await withContext({ provider: 'openai', batchMode: 'deferred' }, async () => {
|
|
151
|
+
* const titles = await list`10 blog titles`
|
|
152
|
+
* return titles.map(title => write`blog post: ${title}`)
|
|
153
|
+
* })
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function withContext<T>(
|
|
157
|
+
context: ExecutionContext,
|
|
158
|
+
fn: () => T | Promise<T>
|
|
159
|
+
): T | Promise<T> {
|
|
160
|
+
const mergedContext = { ...getContext(), ...context }
|
|
161
|
+
|
|
162
|
+
if (asyncLocalStorage) {
|
|
163
|
+
return asyncLocalStorage.run(mergedContext, fn)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Fallback: temporarily modify global context
|
|
167
|
+
const previousContext = globalContext
|
|
168
|
+
globalContext = mergedContext
|
|
169
|
+
try {
|
|
170
|
+
return fn()
|
|
171
|
+
} finally {
|
|
172
|
+
globalContext = previousContext
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Environment Defaults
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
function getEnvContext(): ExecutionContext {
|
|
181
|
+
if (typeof process === 'undefined') return {}
|
|
182
|
+
|
|
183
|
+
const context: ExecutionContext = {}
|
|
184
|
+
|
|
185
|
+
// Model defaults
|
|
186
|
+
if (process.env.AI_MODEL) {
|
|
187
|
+
context.model = process.env.AI_MODEL
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Provider defaults
|
|
191
|
+
if (process.env.AI_PROVIDER) {
|
|
192
|
+
context.provider = process.env.AI_PROVIDER as BatchProvider
|
|
193
|
+
} else if (process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) {
|
|
194
|
+
context.provider = 'anthropic'
|
|
195
|
+
} else if (process.env.OPENAI_API_KEY) {
|
|
196
|
+
context.provider = 'openai'
|
|
197
|
+
} else if (process.env.CLOUDFLARE_API_TOKEN) {
|
|
198
|
+
context.provider = 'cloudflare'
|
|
199
|
+
} else if (process.env.AWS_ACCESS_KEY_ID) {
|
|
200
|
+
context.provider = 'bedrock'
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Batch mode
|
|
204
|
+
if (process.env.AI_BATCH_MODE) {
|
|
205
|
+
context.batchMode = process.env.AI_BATCH_MODE as BatchMode
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Flex threshold (when to start using flex processing)
|
|
209
|
+
if (process.env.AI_FLEX_THRESHOLD) {
|
|
210
|
+
context.flexThreshold = parseInt(process.env.AI_FLEX_THRESHOLD, 10)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Batch threshold (when to switch from flex to full batch)
|
|
214
|
+
if (process.env.AI_BATCH_THRESHOLD) {
|
|
215
|
+
context.batchThreshold = parseInt(process.env.AI_BATCH_THRESHOLD, 10)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Webhook URL
|
|
219
|
+
if (process.env.AI_BATCH_WEBHOOK_URL) {
|
|
220
|
+
context.webhookUrl = process.env.AI_BATCH_WEBHOOK_URL
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return context
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// Context Helpers
|
|
228
|
+
// ============================================================================
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the effective model from context
|
|
232
|
+
*/
|
|
233
|
+
export function getModel(): string {
|
|
234
|
+
const ctx = getContext()
|
|
235
|
+
return ctx.model || 'sonnet'
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get the effective provider from context
|
|
240
|
+
*/
|
|
241
|
+
export function getProvider(): BatchProvider {
|
|
242
|
+
const ctx = getContext()
|
|
243
|
+
return ctx.provider || 'openai'
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get the effective batch mode from context
|
|
248
|
+
*/
|
|
249
|
+
export function getBatchMode(): BatchMode {
|
|
250
|
+
const ctx = getContext()
|
|
251
|
+
return ctx.batchMode || 'auto'
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get the flex threshold from context (minimum items to use flex)
|
|
256
|
+
* Default: 5 items
|
|
257
|
+
*/
|
|
258
|
+
export function getFlexThreshold(): number {
|
|
259
|
+
const ctx = getContext()
|
|
260
|
+
return ctx.flexThreshold || 5
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get the batch threshold from context (minimum items to use full batch)
|
|
265
|
+
* Default: 500 items
|
|
266
|
+
*/
|
|
267
|
+
export function getBatchThreshold(): number {
|
|
268
|
+
const ctx = getContext()
|
|
269
|
+
return ctx.batchThreshold || 500
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Execution tier for processing */
|
|
273
|
+
export type ExecutionTier = 'immediate' | 'flex' | 'batch'
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Determine the execution tier for a given number of items
|
|
277
|
+
*
|
|
278
|
+
* Auto mode tiers:
|
|
279
|
+
* - immediate: < flexThreshold (default 5) - concurrent requests, full price
|
|
280
|
+
* - flex: flexThreshold to batchThreshold (5-500) - ~50% discount, minutes
|
|
281
|
+
* - batch: >= batchThreshold (500+) - 50% discount, up to 24hr
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* getExecutionTier(3) // 'immediate' (< 5)
|
|
286
|
+
* getExecutionTier(50) // 'flex' (5-500)
|
|
287
|
+
* getExecutionTier(1000) // 'batch' (500+)
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
export function getExecutionTier(itemCount: number): ExecutionTier {
|
|
291
|
+
const mode = getBatchMode()
|
|
292
|
+
|
|
293
|
+
switch (mode) {
|
|
294
|
+
case 'immediate':
|
|
295
|
+
return 'immediate'
|
|
296
|
+
case 'flex':
|
|
297
|
+
return 'flex'
|
|
298
|
+
case 'deferred':
|
|
299
|
+
return 'batch'
|
|
300
|
+
case 'auto':
|
|
301
|
+
default: {
|
|
302
|
+
const flexThreshold = getFlexThreshold()
|
|
303
|
+
const batchThreshold = getBatchThreshold()
|
|
304
|
+
|
|
305
|
+
if (itemCount < flexThreshold) {
|
|
306
|
+
return 'immediate'
|
|
307
|
+
} else if (itemCount < batchThreshold) {
|
|
308
|
+
return 'flex'
|
|
309
|
+
} else {
|
|
310
|
+
return 'batch'
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Check if we should use the batch API for a given number of items
|
|
318
|
+
* @deprecated Use getExecutionTier() instead for more granular control
|
|
319
|
+
*/
|
|
320
|
+
export function shouldUseBatchAPI(itemCount: number): boolean {
|
|
321
|
+
const tier = getExecutionTier(itemCount)
|
|
322
|
+
return tier === 'flex' || tier === 'batch'
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Check if flex processing is available for the current provider
|
|
327
|
+
* Only OpenAI and AWS Bedrock support flex processing currently
|
|
328
|
+
*/
|
|
329
|
+
export function isFlexAvailable(): boolean {
|
|
330
|
+
const provider = getProvider()
|
|
331
|
+
return provider === 'openai' || provider === 'bedrock' || provider === 'google'
|
|
332
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding utilities from AI SDK
|
|
3
|
+
*
|
|
4
|
+
* Re-exports embed, embedMany, and cosineSimilarity from the Vercel AI SDK
|
|
5
|
+
* with additional convenience wrappers.
|
|
6
|
+
*
|
|
7
|
+
* Default model: Cloudflare Workers AI @cf/baai/bge-m3
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Re-export core embedding functions from AI SDK
|
|
13
|
+
export { embed, embedMany, cosineSimilarity } from 'ai'
|
|
14
|
+
|
|
15
|
+
// Re-export types
|
|
16
|
+
export type {
|
|
17
|
+
EmbeddingModel,
|
|
18
|
+
Embedding
|
|
19
|
+
} from 'ai'
|
|
20
|
+
|
|
21
|
+
// Re-export Cloudflare provider from ai-providers
|
|
22
|
+
export { cloudflare, cloudflareEmbedding, DEFAULT_CF_EMBEDDING_MODEL } from 'ai-providers/cloudflare'
|
|
23
|
+
|
|
24
|
+
import { embed as aiEmbed, embedMany as aiEmbedMany } from 'ai'
|
|
25
|
+
import { cloudflareEmbedding, DEFAULT_CF_EMBEDDING_MODEL } from 'ai-providers/cloudflare'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the default embedding model (Cloudflare @cf/baai/bge-m3)
|
|
29
|
+
*/
|
|
30
|
+
export function getDefaultEmbeddingModel() {
|
|
31
|
+
return cloudflareEmbedding(DEFAULT_CF_EMBEDDING_MODEL)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Embed a single value using the default Cloudflare model
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { embedText } from 'ai-functions'
|
|
40
|
+
*
|
|
41
|
+
* const { embedding } = await embedText('hello world')
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export async function embedText(value: string) {
|
|
45
|
+
return aiEmbed({
|
|
46
|
+
model: getDefaultEmbeddingModel(),
|
|
47
|
+
value
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Embed multiple values using the default Cloudflare model
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { embedTexts } from 'ai-functions'
|
|
57
|
+
*
|
|
58
|
+
* const { embeddings } = await embedTexts(['doc1', 'doc2', 'doc3'])
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export async function embedTexts(values: string[]) {
|
|
62
|
+
return aiEmbedMany({
|
|
63
|
+
model: getDefaultEmbeddingModel(),
|
|
64
|
+
values
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Result of an embed operation
|
|
70
|
+
*/
|
|
71
|
+
export interface EmbedResult<T = string> {
|
|
72
|
+
/** The original input value */
|
|
73
|
+
value: T
|
|
74
|
+
/** The generated embedding vector */
|
|
75
|
+
embedding: number[]
|
|
76
|
+
/** Token usage */
|
|
77
|
+
usage: {
|
|
78
|
+
tokens: number
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Result of an embedMany operation
|
|
84
|
+
*/
|
|
85
|
+
export interface EmbedManyResult<T = string> {
|
|
86
|
+
/** The original input values */
|
|
87
|
+
values: T[]
|
|
88
|
+
/** The generated embedding vectors */
|
|
89
|
+
embeddings: number[][]
|
|
90
|
+
/** Token usage */
|
|
91
|
+
usage: {
|
|
92
|
+
tokens: number
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Find the most similar items to a query embedding
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { embed, embedMany, findSimilar } from 'ai-functions'
|
|
102
|
+
*
|
|
103
|
+
* const documents = ['doc1', 'doc2', 'doc3']
|
|
104
|
+
* const { embeddings } = await embedMany({ model, values: documents })
|
|
105
|
+
* const { embedding: queryEmbedding } = await embed({ model, value: 'search query' })
|
|
106
|
+
*
|
|
107
|
+
* const results = findSimilar(queryEmbedding, embeddings, documents, { topK: 2 })
|
|
108
|
+
* // [{ item: 'doc1', score: 0.95, index: 0 }, { item: 'doc2', score: 0.82, index: 1 }]
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function findSimilar<T>(
|
|
112
|
+
queryEmbedding: number[],
|
|
113
|
+
embeddings: number[][],
|
|
114
|
+
items: T[],
|
|
115
|
+
options: {
|
|
116
|
+
/** Number of results to return (default: 10) */
|
|
117
|
+
topK?: number
|
|
118
|
+
/** Minimum similarity score (default: 0) */
|
|
119
|
+
minScore?: number
|
|
120
|
+
} = {}
|
|
121
|
+
): Array<{ item: T; score: number; index: number }> {
|
|
122
|
+
const { topK = 10, minScore = 0 } = options
|
|
123
|
+
|
|
124
|
+
// Import cosineSimilarity dynamically to avoid issues if ai isn't installed
|
|
125
|
+
const { cosineSimilarity } = require('ai')
|
|
126
|
+
|
|
127
|
+
const scored = embeddings
|
|
128
|
+
.map((embedding, index) => ({
|
|
129
|
+
item: items[index]!,
|
|
130
|
+
score: cosineSimilarity(queryEmbedding, embedding) as number,
|
|
131
|
+
index
|
|
132
|
+
}))
|
|
133
|
+
.filter(result => result.score >= minScore)
|
|
134
|
+
.sort((a, b) => b.score - a.score)
|
|
135
|
+
.slice(0, topK)
|
|
136
|
+
|
|
137
|
+
return scored
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Calculate pairwise similarities between all embeddings
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const matrix = pairwiseSimilarity(embeddings)
|
|
146
|
+
* // matrix[i][j] = similarity between embeddings[i] and embeddings[j]
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export function pairwiseSimilarity(embeddings: number[][]): number[][] {
|
|
150
|
+
const { cosineSimilarity } = require('ai')
|
|
151
|
+
|
|
152
|
+
const n = embeddings.length
|
|
153
|
+
const matrix: number[][] = Array(n).fill(null).map(() => Array(n).fill(0))
|
|
154
|
+
|
|
155
|
+
for (let i = 0; i < n; i++) {
|
|
156
|
+
matrix[i]![i] = 1 // Self-similarity is always 1
|
|
157
|
+
for (let j = i + 1; j < n; j++) {
|
|
158
|
+
const sim = cosineSimilarity(embeddings[i], embeddings[j])
|
|
159
|
+
matrix[i]![j] = sim
|
|
160
|
+
matrix[j]![i] = sim
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return matrix
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Cluster embeddings by similarity using a simple threshold-based approach
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```ts
|
|
172
|
+
* const clusters = clusterBySimilarity(embeddings, items, { threshold: 0.8 })
|
|
173
|
+
* // [[item1, item2], [item3], [item4, item5, item6]]
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export function clusterBySimilarity<T>(
|
|
177
|
+
embeddings: number[][],
|
|
178
|
+
items: T[],
|
|
179
|
+
options: {
|
|
180
|
+
/** Similarity threshold for clustering (default: 0.8) */
|
|
181
|
+
threshold?: number
|
|
182
|
+
} = {}
|
|
183
|
+
): T[][] {
|
|
184
|
+
const { threshold = 0.8 } = options
|
|
185
|
+
const { cosineSimilarity } = require('ai')
|
|
186
|
+
|
|
187
|
+
const n = embeddings.length
|
|
188
|
+
const assigned = new Set<number>()
|
|
189
|
+
const clusters: T[][] = []
|
|
190
|
+
|
|
191
|
+
for (let i = 0; i < n; i++) {
|
|
192
|
+
if (assigned.has(i)) continue
|
|
193
|
+
|
|
194
|
+
const cluster: T[] = [items[i]!]
|
|
195
|
+
assigned.add(i)
|
|
196
|
+
|
|
197
|
+
for (let j = i + 1; j < n; j++) {
|
|
198
|
+
if (assigned.has(j)) continue
|
|
199
|
+
|
|
200
|
+
const sim = cosineSimilarity(embeddings[i], embeddings[j])
|
|
201
|
+
if (sim >= threshold) {
|
|
202
|
+
cluster.push(items[j]!)
|
|
203
|
+
assigned.add(j)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
clusters.push(cluster)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return clusters
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Average multiple embeddings into a single embedding
|
|
215
|
+
* Useful for creating document embeddings from chunk embeddings
|
|
216
|
+
*/
|
|
217
|
+
export function averageEmbeddings(embeddings: number[][]): number[] {
|
|
218
|
+
if (embeddings.length === 0) return []
|
|
219
|
+
|
|
220
|
+
const dim = embeddings[0]!.length
|
|
221
|
+
const result = new Array(dim).fill(0)
|
|
222
|
+
|
|
223
|
+
for (const embedding of embeddings) {
|
|
224
|
+
for (let i = 0; i < dim; i++) {
|
|
225
|
+
result[i] += embedding[i]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const n = embeddings.length
|
|
230
|
+
for (let i = 0; i < dim; i++) {
|
|
231
|
+
result[i] /= n
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Normalize an embedding to unit length
|
|
239
|
+
*/
|
|
240
|
+
export function normalizeEmbedding(embedding: number[]): number[] {
|
|
241
|
+
const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0))
|
|
242
|
+
if (magnitude === 0) return embedding
|
|
243
|
+
return embedding.map(val => val / magnitude)
|
|
244
|
+
}
|