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/batch-map.ts
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch Map - Automatic batch detection for .map() operations
|
|
3
|
+
*
|
|
4
|
+
* When you call .map() on a list result, the individual operations
|
|
5
|
+
* are captured and automatically batched when resolved.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // This automatically batches the write operations
|
|
10
|
+
* const titles = await list`10 blog post titles`
|
|
11
|
+
* const posts = titles.map(title => write`blog post: # ${title}`)
|
|
12
|
+
*
|
|
13
|
+
* // When awaited, posts are generated via batch API
|
|
14
|
+
* console.log(await posts) // 10 blog posts
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Note: We avoid importing AIPromise here to prevent circular dependencies
|
|
21
|
+
// The AI promise module imports from this file for recording mode
|
|
22
|
+
import {
|
|
23
|
+
getContext,
|
|
24
|
+
getExecutionTier,
|
|
25
|
+
getProvider,
|
|
26
|
+
getModel,
|
|
27
|
+
isFlexAvailable,
|
|
28
|
+
type ExecutionTier,
|
|
29
|
+
} from './context.js'
|
|
30
|
+
import { createBatch, getBatchAdapter, type BatchItem, type BatchResult } from './batch-queue.js'
|
|
31
|
+
import { generateObject, generateText } from './generate.js'
|
|
32
|
+
import type { SimpleSchema } from './schema.js'
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Types
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/** Symbol to identify BatchMapPromise instances */
|
|
39
|
+
export const BATCH_MAP_SYMBOL = Symbol.for('ai-batch-map')
|
|
40
|
+
|
|
41
|
+
/** A captured operation from the map callback */
|
|
42
|
+
export interface CapturedOperation {
|
|
43
|
+
/** Unique ID for this operation */
|
|
44
|
+
id: string
|
|
45
|
+
/** The prompt template */
|
|
46
|
+
prompt: string
|
|
47
|
+
/** The item value that will be substituted */
|
|
48
|
+
itemPlaceholder: string
|
|
49
|
+
/** Schema for structured output */
|
|
50
|
+
schema?: SimpleSchema
|
|
51
|
+
/** Generation type */
|
|
52
|
+
type: 'text' | 'object' | 'boolean' | 'list'
|
|
53
|
+
/** System prompt */
|
|
54
|
+
system?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Options for batch map */
|
|
58
|
+
export interface BatchMapOptions {
|
|
59
|
+
/** Force immediate execution (no batching) */
|
|
60
|
+
immediate?: boolean
|
|
61
|
+
/** Force batch API (even for small batches) */
|
|
62
|
+
deferred?: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// BatchMapPromise
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A promise that represents a batch of mapped operations.
|
|
71
|
+
* Resolves by either:
|
|
72
|
+
* - Executing via batch API (for large batches or when deferred)
|
|
73
|
+
* - Executing concurrently (for small batches or when immediate)
|
|
74
|
+
*/
|
|
75
|
+
export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
76
|
+
readonly [BATCH_MAP_SYMBOL] = true
|
|
77
|
+
|
|
78
|
+
/** The source list items */
|
|
79
|
+
private _items: unknown[]
|
|
80
|
+
|
|
81
|
+
/** The captured operations (one per item) */
|
|
82
|
+
private _operations: CapturedOperation[][]
|
|
83
|
+
|
|
84
|
+
/** Options for batch execution */
|
|
85
|
+
private _options: BatchMapOptions
|
|
86
|
+
|
|
87
|
+
/** Cached resolver promise */
|
|
88
|
+
private _resolver: Promise<T[]> | null = null
|
|
89
|
+
|
|
90
|
+
constructor(
|
|
91
|
+
items: unknown[],
|
|
92
|
+
operations: CapturedOperation[][],
|
|
93
|
+
options: BatchMapOptions = {}
|
|
94
|
+
) {
|
|
95
|
+
this._items = items
|
|
96
|
+
this._operations = operations
|
|
97
|
+
this._options = options
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the number of items in the batch
|
|
102
|
+
*/
|
|
103
|
+
get size(): number {
|
|
104
|
+
return this._items.length
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the batch
|
|
109
|
+
*/
|
|
110
|
+
async resolve(): Promise<T[]> {
|
|
111
|
+
const totalOperations = this._operations.reduce((sum, ops) => sum + ops.length, 0)
|
|
112
|
+
|
|
113
|
+
// Determine execution tier
|
|
114
|
+
let tier: ExecutionTier
|
|
115
|
+
|
|
116
|
+
if (this._options.deferred) {
|
|
117
|
+
tier = 'batch'
|
|
118
|
+
} else if (this._options.immediate) {
|
|
119
|
+
tier = 'immediate'
|
|
120
|
+
} else {
|
|
121
|
+
tier = getExecutionTier(totalOperations)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Execute based on tier
|
|
125
|
+
switch (tier) {
|
|
126
|
+
case 'immediate':
|
|
127
|
+
return this._resolveImmediately()
|
|
128
|
+
|
|
129
|
+
case 'flex':
|
|
130
|
+
// Use flex processing if available, otherwise fall back to immediate
|
|
131
|
+
if (isFlexAvailable()) {
|
|
132
|
+
return this._resolveViaFlex()
|
|
133
|
+
}
|
|
134
|
+
console.warn(`Flex processing not available for ${getProvider()}, using immediate execution`)
|
|
135
|
+
return this._resolveImmediately()
|
|
136
|
+
|
|
137
|
+
case 'batch':
|
|
138
|
+
return this._resolveViaBatchAPI()
|
|
139
|
+
|
|
140
|
+
default:
|
|
141
|
+
return this._resolveImmediately()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Execute via flex processing (faster than batch, ~50% discount)
|
|
147
|
+
* Available for OpenAI and AWS Bedrock
|
|
148
|
+
*/
|
|
149
|
+
private async _resolveViaFlex(): Promise<T[]> {
|
|
150
|
+
const provider = getProvider()
|
|
151
|
+
const model = getModel()
|
|
152
|
+
|
|
153
|
+
// Try to get the flex adapter
|
|
154
|
+
try {
|
|
155
|
+
const { getFlexAdapter } = await import('./batch-queue.js')
|
|
156
|
+
const adapter = getFlexAdapter(provider)
|
|
157
|
+
|
|
158
|
+
// Build batch items
|
|
159
|
+
const batchItems: BatchItem[] = []
|
|
160
|
+
const itemOperationMap: Map<string, { itemIndex: number; opIndex: number }> = new Map()
|
|
161
|
+
|
|
162
|
+
for (let itemIndex = 0; itemIndex < this._items.length; itemIndex++) {
|
|
163
|
+
const item = this._items[itemIndex]
|
|
164
|
+
const operations = this._operations[itemIndex] || []
|
|
165
|
+
|
|
166
|
+
for (let opIndex = 0; opIndex < operations.length; opIndex++) {
|
|
167
|
+
const op = operations[opIndex]
|
|
168
|
+
if (!op) continue
|
|
169
|
+
|
|
170
|
+
const id = `item_${itemIndex}_op_${opIndex}`
|
|
171
|
+
const prompt = op.prompt.replace(op.itemPlaceholder, String(item))
|
|
172
|
+
|
|
173
|
+
batchItems.push({
|
|
174
|
+
id,
|
|
175
|
+
prompt,
|
|
176
|
+
schema: op.schema,
|
|
177
|
+
options: { system: op.system, model },
|
|
178
|
+
status: 'pending',
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
itemOperationMap.set(id, { itemIndex, opIndex })
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Submit via flex adapter
|
|
186
|
+
const results = await adapter.submitFlex(batchItems, { model })
|
|
187
|
+
return this._reconstructResults(results, itemOperationMap)
|
|
188
|
+
} catch {
|
|
189
|
+
// Flex adapter not available, fall back to batch or immediate
|
|
190
|
+
console.warn(`Flex adapter not available, falling back to batch API`)
|
|
191
|
+
return this._resolveViaBatchAPI()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Execute via provider batch API (deferred, 50% discount)
|
|
197
|
+
*/
|
|
198
|
+
private async _resolveViaBatchAPI(): Promise<T[]> {
|
|
199
|
+
const ctx = getContext()
|
|
200
|
+
const provider = getProvider()
|
|
201
|
+
const model = getModel()
|
|
202
|
+
|
|
203
|
+
// Try to get the batch adapter
|
|
204
|
+
let adapter
|
|
205
|
+
try {
|
|
206
|
+
adapter = getBatchAdapter(provider)
|
|
207
|
+
} catch {
|
|
208
|
+
// Adapter not registered, fall back to immediate execution
|
|
209
|
+
console.warn(`Batch adapter for ${provider} not available, falling back to immediate execution`)
|
|
210
|
+
return this._resolveImmediately()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Flatten all operations into a single batch
|
|
214
|
+
const batchItems: BatchItem[] = []
|
|
215
|
+
const itemOperationMap: Map<string, { itemIndex: number; opIndex: number }> = new Map()
|
|
216
|
+
|
|
217
|
+
for (let itemIndex = 0; itemIndex < this._items.length; itemIndex++) {
|
|
218
|
+
const item = this._items[itemIndex]
|
|
219
|
+
const operations = this._operations[itemIndex] || []
|
|
220
|
+
|
|
221
|
+
for (let opIndex = 0; opIndex < operations.length; opIndex++) {
|
|
222
|
+
const op = operations[opIndex]
|
|
223
|
+
if (!op) continue
|
|
224
|
+
|
|
225
|
+
const id = `item_${itemIndex}_op_${opIndex}`
|
|
226
|
+
|
|
227
|
+
// Substitute the actual item value into the prompt
|
|
228
|
+
const prompt = op.prompt.replace(op.itemPlaceholder, String(item))
|
|
229
|
+
|
|
230
|
+
batchItems.push({
|
|
231
|
+
id,
|
|
232
|
+
prompt,
|
|
233
|
+
schema: op.schema,
|
|
234
|
+
options: { system: op.system, model },
|
|
235
|
+
status: 'pending',
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
itemOperationMap.set(id, { itemIndex, opIndex })
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Submit batch
|
|
243
|
+
const batch = createBatch({
|
|
244
|
+
provider,
|
|
245
|
+
model,
|
|
246
|
+
webhookUrl: ctx.webhookUrl,
|
|
247
|
+
metadata: ctx.metadata,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
for (const item of batchItems) {
|
|
251
|
+
batch.add(item.prompt, {
|
|
252
|
+
schema: item.schema,
|
|
253
|
+
options: item.options,
|
|
254
|
+
customId: item.id,
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const { completion } = await batch.submit()
|
|
259
|
+
const results = await completion
|
|
260
|
+
|
|
261
|
+
// Reconstruct the results array
|
|
262
|
+
return this._reconstructResults(results, itemOperationMap)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Execute immediately (concurrent requests)
|
|
267
|
+
*/
|
|
268
|
+
private async _resolveImmediately(): Promise<T[]> {
|
|
269
|
+
const model = getModel()
|
|
270
|
+
const results: T[] = []
|
|
271
|
+
|
|
272
|
+
// Process each item
|
|
273
|
+
for (let itemIndex = 0; itemIndex < this._items.length; itemIndex++) {
|
|
274
|
+
const item = this._items[itemIndex]
|
|
275
|
+
const operations = this._operations[itemIndex] || []
|
|
276
|
+
|
|
277
|
+
// If there's only one operation per item, resolve it directly
|
|
278
|
+
if (operations.length === 1) {
|
|
279
|
+
const op = operations[0]
|
|
280
|
+
if (op) {
|
|
281
|
+
const prompt = op.prompt.replace(op.itemPlaceholder, String(item))
|
|
282
|
+
const result = await this._executeOperation(op, prompt, model)
|
|
283
|
+
results.push(result as T)
|
|
284
|
+
}
|
|
285
|
+
} else if (operations.length > 0) {
|
|
286
|
+
// Multiple operations per item - resolve as object
|
|
287
|
+
const opResults: Record<string, unknown> = {}
|
|
288
|
+
|
|
289
|
+
await Promise.all(
|
|
290
|
+
operations.map(async (op, opIndex) => {
|
|
291
|
+
if (!op) return
|
|
292
|
+
const prompt = op.prompt.replace(op.itemPlaceholder, String(item))
|
|
293
|
+
opResults[`op_${opIndex}`] = await this._executeOperation(op, prompt, model)
|
|
294
|
+
})
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
results.push(opResults as T)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return results
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Execute a single operation
|
|
306
|
+
*/
|
|
307
|
+
private async _executeOperation(
|
|
308
|
+
op: CapturedOperation,
|
|
309
|
+
prompt: string,
|
|
310
|
+
model: string
|
|
311
|
+
): Promise<unknown> {
|
|
312
|
+
switch (op.type) {
|
|
313
|
+
case 'text':
|
|
314
|
+
const textResult = await generateText({ model, prompt, system: op.system })
|
|
315
|
+
return textResult.text
|
|
316
|
+
|
|
317
|
+
case 'boolean':
|
|
318
|
+
const boolResult = await generateObject({
|
|
319
|
+
model,
|
|
320
|
+
schema: { answer: 'true | false' },
|
|
321
|
+
prompt,
|
|
322
|
+
system: op.system || 'Answer with true or false.',
|
|
323
|
+
})
|
|
324
|
+
return (boolResult.object as { answer: string }).answer === 'true'
|
|
325
|
+
|
|
326
|
+
case 'list':
|
|
327
|
+
const listResult = await generateObject({
|
|
328
|
+
model,
|
|
329
|
+
schema: { items: ['List items'] },
|
|
330
|
+
prompt,
|
|
331
|
+
system: op.system,
|
|
332
|
+
})
|
|
333
|
+
return (listResult.object as { items: string[] }).items
|
|
334
|
+
|
|
335
|
+
case 'object':
|
|
336
|
+
default:
|
|
337
|
+
const objResult = await generateObject({
|
|
338
|
+
model,
|
|
339
|
+
schema: op.schema || { result: 'The result' },
|
|
340
|
+
prompt,
|
|
341
|
+
system: op.system,
|
|
342
|
+
})
|
|
343
|
+
return objResult.object
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Reconstruct results from batch response
|
|
349
|
+
*/
|
|
350
|
+
private _reconstructResults(
|
|
351
|
+
batchResults: BatchResult[],
|
|
352
|
+
itemOperationMap: Map<string, { itemIndex: number; opIndex: number }>
|
|
353
|
+
): T[] {
|
|
354
|
+
const results: (T | Record<string, unknown>)[] = new Array(this._items.length)
|
|
355
|
+
|
|
356
|
+
// Initialize results array
|
|
357
|
+
for (let i = 0; i < this._items.length; i++) {
|
|
358
|
+
const operations = this._operations[i] || []
|
|
359
|
+
if (operations.length === 1) {
|
|
360
|
+
results[i] = undefined as T
|
|
361
|
+
} else {
|
|
362
|
+
results[i] = {}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Fill in results
|
|
367
|
+
for (const batchResult of batchResults) {
|
|
368
|
+
const mapping = itemOperationMap.get(batchResult.id)
|
|
369
|
+
if (!mapping) continue
|
|
370
|
+
|
|
371
|
+
const { itemIndex, opIndex } = mapping
|
|
372
|
+
const operations = this._operations[itemIndex] || []
|
|
373
|
+
|
|
374
|
+
if (operations.length === 1) {
|
|
375
|
+
results[itemIndex] = batchResult.result as T
|
|
376
|
+
} else {
|
|
377
|
+
(results[itemIndex] as Record<string, unknown>)[`op_${opIndex}`] = batchResult.result
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return results as T[]
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Promise interface - then()
|
|
386
|
+
*/
|
|
387
|
+
then<TResult1 = T[], TResult2 = never>(
|
|
388
|
+
onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null,
|
|
389
|
+
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
|
|
390
|
+
): Promise<TResult1 | TResult2> {
|
|
391
|
+
if (!this._resolver) {
|
|
392
|
+
this._resolver = this.resolve()
|
|
393
|
+
}
|
|
394
|
+
return this._resolver.then(onfulfilled, onrejected)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Promise interface - catch()
|
|
399
|
+
*/
|
|
400
|
+
catch<TResult = never>(
|
|
401
|
+
onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
|
|
402
|
+
): Promise<T[] | TResult> {
|
|
403
|
+
return this.then(null, onrejected)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Promise interface - finally()
|
|
408
|
+
*/
|
|
409
|
+
finally(onfinally?: (() => void) | null): Promise<T[]> {
|
|
410
|
+
return this.then(
|
|
411
|
+
(value) => {
|
|
412
|
+
onfinally?.()
|
|
413
|
+
return value
|
|
414
|
+
},
|
|
415
|
+
(reason) => {
|
|
416
|
+
onfinally?.()
|
|
417
|
+
throw reason
|
|
418
|
+
}
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// Recording Context
|
|
425
|
+
// ============================================================================
|
|
426
|
+
|
|
427
|
+
/** Current item value being recorded */
|
|
428
|
+
let currentRecordingItem: unknown = null
|
|
429
|
+
|
|
430
|
+
/** Current item placeholder string */
|
|
431
|
+
let currentItemPlaceholder: string = ''
|
|
432
|
+
|
|
433
|
+
/** Captured operations during recording */
|
|
434
|
+
let capturedOperations: CapturedOperation[] = []
|
|
435
|
+
|
|
436
|
+
/** Recording mode flag */
|
|
437
|
+
let isRecording = false
|
|
438
|
+
|
|
439
|
+
/** Operation counter for unique IDs */
|
|
440
|
+
let operationCounter = 0
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Check if we're in recording mode
|
|
444
|
+
*/
|
|
445
|
+
export function isInRecordingMode(): boolean {
|
|
446
|
+
return isRecording
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Get the current item placeholder for template substitution
|
|
451
|
+
*/
|
|
452
|
+
export function getCurrentItemPlaceholder(): string | null {
|
|
453
|
+
return isRecording ? currentItemPlaceholder : null
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Capture an operation during recording
|
|
458
|
+
*/
|
|
459
|
+
export function captureOperation(
|
|
460
|
+
prompt: string,
|
|
461
|
+
type: CapturedOperation['type'],
|
|
462
|
+
schema?: SimpleSchema,
|
|
463
|
+
system?: string
|
|
464
|
+
): void {
|
|
465
|
+
if (!isRecording) return
|
|
466
|
+
|
|
467
|
+
capturedOperations.push({
|
|
468
|
+
id: `op_${++operationCounter}`,
|
|
469
|
+
prompt,
|
|
470
|
+
itemPlaceholder: currentItemPlaceholder,
|
|
471
|
+
schema,
|
|
472
|
+
type,
|
|
473
|
+
system,
|
|
474
|
+
})
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// ============================================================================
|
|
478
|
+
// Batch Map Factory
|
|
479
|
+
// ============================================================================
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Create a batch map from an array and a callback
|
|
483
|
+
*
|
|
484
|
+
* This is called internally by AIPromise.map()
|
|
485
|
+
*/
|
|
486
|
+
export function createBatchMap<T, U>(
|
|
487
|
+
items: T[],
|
|
488
|
+
callback: (item: T, index: number) => U,
|
|
489
|
+
options: BatchMapOptions = {}
|
|
490
|
+
): BatchMapPromise<U> {
|
|
491
|
+
const allOperations: CapturedOperation[][] = []
|
|
492
|
+
|
|
493
|
+
for (let i = 0; i < items.length; i++) {
|
|
494
|
+
const item = items[i] as T
|
|
495
|
+
|
|
496
|
+
// Enter recording mode
|
|
497
|
+
isRecording = true
|
|
498
|
+
currentRecordingItem = item
|
|
499
|
+
currentItemPlaceholder = `__BATCH_ITEM_${i}__`
|
|
500
|
+
capturedOperations = []
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
// Execute the callback to capture operations
|
|
504
|
+
callback(item, i)
|
|
505
|
+
|
|
506
|
+
// Operations should have been captured via captureOperation()
|
|
507
|
+
allOperations.push([...capturedOperations])
|
|
508
|
+
} finally {
|
|
509
|
+
// Exit recording mode
|
|
510
|
+
isRecording = false
|
|
511
|
+
currentRecordingItem = null
|
|
512
|
+
currentItemPlaceholder = ''
|
|
513
|
+
capturedOperations = []
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return new BatchMapPromise<U>(items, allOperations, options)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// ============================================================================
|
|
521
|
+
// Helpers
|
|
522
|
+
// ============================================================================
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Check if a value is a BatchMapPromise
|
|
526
|
+
*/
|
|
527
|
+
export function isBatchMapPromise(value: unknown): value is BatchMapPromise<unknown> {
|
|
528
|
+
return (
|
|
529
|
+
value !== null &&
|
|
530
|
+
typeof value === 'object' &&
|
|
531
|
+
BATCH_MAP_SYMBOL in value &&
|
|
532
|
+
(value as any)[BATCH_MAP_SYMBOL] === true
|
|
533
|
+
)
|
|
534
|
+
}
|