ai-functions 2.1.3 → 2.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 +1 -1
- package/CHANGELOG.md +90 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +176 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +685 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/sandbox.d.ts +36 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +44 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +155 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +29 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +71 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +874 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +122 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/sandbox.ts +52 -0
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +186 -27
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/fill-template.test.ts +89 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/sandbox-execution.test.ts +155 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BatchProvider Port
|
|
3
|
+
*
|
|
4
|
+
* Defines the explicit port (interface) that every batch provider adapter must
|
|
5
|
+
* satisfy, plus helpers that concentrate logic genuinely shared across adapters
|
|
6
|
+
* (polling, concurrent processing, in-memory job tracking, JSON-Schema conversion).
|
|
7
|
+
*
|
|
8
|
+
* Each provider file (`anthropic.ts`, `openai.ts`, `google.ts`, `bedrock.ts`,
|
|
9
|
+
* `cloudflare.ts`, `memory.ts`) is now a small adapter that satisfies this port
|
|
10
|
+
* and concentrates on provider-specific HTTP/SDK calls and request/response shapes.
|
|
11
|
+
*
|
|
12
|
+
* Port + 4 adapters = real seam. The port lives here so provider files don't
|
|
13
|
+
* import shared logic from each other and can't accidentally diverge.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
BatchAdapter,
|
|
20
|
+
BatchItem,
|
|
21
|
+
BatchJob,
|
|
22
|
+
BatchQueueOptions,
|
|
23
|
+
BatchResult,
|
|
24
|
+
BatchStatus,
|
|
25
|
+
} from '../batch-queue.js'
|
|
26
|
+
|
|
27
|
+
// Re-export the port and its types so adapters can import everything from
|
|
28
|
+
// `./provider.js` rather than reaching into `../batch-queue.js`.
|
|
29
|
+
export type {
|
|
30
|
+
BatchAdapter,
|
|
31
|
+
BatchItem,
|
|
32
|
+
BatchJob,
|
|
33
|
+
BatchQueueOptions,
|
|
34
|
+
BatchResult,
|
|
35
|
+
BatchSubmitResult,
|
|
36
|
+
BatchProvider,
|
|
37
|
+
BatchStatus,
|
|
38
|
+
FlexAdapter,
|
|
39
|
+
} from '../batch-queue.js'
|
|
40
|
+
|
|
41
|
+
export { registerBatchAdapter, registerFlexAdapter } from '../batch-queue.js'
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Polling helper (shared by all adapters that have async batch jobs)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
/** Terminal states for a batch job — polling stops here. */
|
|
48
|
+
const TERMINAL_STATUSES: ReadonlySet<BatchStatus> = new Set([
|
|
49
|
+
'completed',
|
|
50
|
+
'failed',
|
|
51
|
+
'cancelled',
|
|
52
|
+
'expired',
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
/** Statuses that should trigger result fetch. */
|
|
56
|
+
const RESULT_STATUSES: ReadonlySet<BatchStatus> = new Set(['completed', 'cancelled', 'failed'])
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Default `waitForCompletion` implementation built on top of `getStatus` +
|
|
60
|
+
* `getResults`. Adapters with non-standard completion semantics can still
|
|
61
|
+
* override `waitForCompletion`, but most don't need to.
|
|
62
|
+
*
|
|
63
|
+
* @param adapter The batch adapter to poll
|
|
64
|
+
* @param batchId The batch id to poll
|
|
65
|
+
* @param options.pollInterval Poll interval in ms (default: 5000)
|
|
66
|
+
* @param options.fetchResultsOn Statuses for which results should be fetched.
|
|
67
|
+
* Defaults to `completed`, `cancelled`, `failed`.
|
|
68
|
+
* @param options.throwOn Statuses that should throw rather than fetch results.
|
|
69
|
+
* Useful for OpenAI which throws on `cancelled`/`expired`.
|
|
70
|
+
*/
|
|
71
|
+
export async function pollUntilComplete(
|
|
72
|
+
adapter: Pick<BatchAdapter, 'getStatus' | 'getResults'>,
|
|
73
|
+
batchId: string,
|
|
74
|
+
options: {
|
|
75
|
+
pollInterval?: number
|
|
76
|
+
fetchResultsOn?: ReadonlySet<BatchStatus>
|
|
77
|
+
throwOn?: ReadonlySet<BatchStatus>
|
|
78
|
+
} = {}
|
|
79
|
+
): Promise<BatchResult[]> {
|
|
80
|
+
const pollInterval = options.pollInterval ?? 5000
|
|
81
|
+
const fetchResultsOn = options.fetchResultsOn ?? RESULT_STATUSES
|
|
82
|
+
const throwOn = options.throwOn
|
|
83
|
+
|
|
84
|
+
while (true) {
|
|
85
|
+
const status = await adapter.getStatus(batchId)
|
|
86
|
+
|
|
87
|
+
if (throwOn?.has(status.status)) {
|
|
88
|
+
throw new Error(`Batch ${status.status}`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (fetchResultsOn.has(status.status) || TERMINAL_STATUSES.has(status.status)) {
|
|
92
|
+
return adapter.getResults(batchId)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await sleep(pollInterval)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Concurrent processing helper (shared by flex adapters and "local" providers)
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Run `processItem` over `items` with bounded concurrency, optionally
|
|
105
|
+
* sleeping between waves to respect provider rate limits. Per-item failures
|
|
106
|
+
* are caught and emitted as `{ status: 'failed', error }` results so a single
|
|
107
|
+
* bad item never poisons the whole batch.
|
|
108
|
+
*
|
|
109
|
+
* Used by flex adapters (OpenAI, Google, Bedrock) and by the "local" providers
|
|
110
|
+
* (Google, Bedrock, Cloudflare) that fake batch processing with concurrent
|
|
111
|
+
* direct API calls.
|
|
112
|
+
*/
|
|
113
|
+
export async function processConcurrently(
|
|
114
|
+
items: BatchItem[],
|
|
115
|
+
processItem: (item: BatchItem) => Promise<BatchResult>,
|
|
116
|
+
options: {
|
|
117
|
+
concurrency?: number
|
|
118
|
+
delayBetweenWaves?: number
|
|
119
|
+
onWaveComplete?: (results: BatchResult[]) => void
|
|
120
|
+
} = {}
|
|
121
|
+
): Promise<BatchResult[]> {
|
|
122
|
+
const concurrency = options.concurrency ?? 10
|
|
123
|
+
const delay = options.delayBetweenWaves ?? 0
|
|
124
|
+
const results: BatchResult[] = []
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
127
|
+
const wave = items.slice(i, i + concurrency)
|
|
128
|
+
|
|
129
|
+
const waveResults = await Promise.all(
|
|
130
|
+
wave.map(async (item) => {
|
|
131
|
+
try {
|
|
132
|
+
return await processItem(item)
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return failedResult(item, error)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
results.push(...waveResults)
|
|
140
|
+
options.onWaveComplete?.(results)
|
|
141
|
+
|
|
142
|
+
if (delay > 0 && i + concurrency < items.length) {
|
|
143
|
+
await sleep(delay)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return results
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Build a `failed` BatchResult from an unknown thrown value. */
|
|
151
|
+
export function failedResult(item: BatchItem, error: unknown): BatchResult {
|
|
152
|
+
return {
|
|
153
|
+
id: item.id,
|
|
154
|
+
customId: item.id,
|
|
155
|
+
status: 'failed',
|
|
156
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// LocalJobStore — in-memory job tracking shared by google/bedrock/cloudflare
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Internal job state for adapters that don't have a real provider-side batch
|
|
166
|
+
* API and need to track jobs locally (Google, Bedrock, Cloudflare).
|
|
167
|
+
*/
|
|
168
|
+
export interface LocalJobState {
|
|
169
|
+
items: BatchItem[]
|
|
170
|
+
options: BatchQueueOptions
|
|
171
|
+
results: BatchResult[]
|
|
172
|
+
status: BatchStatus
|
|
173
|
+
createdAt: Date
|
|
174
|
+
completedAt?: Date
|
|
175
|
+
/** Optional adapter-specific metadata (e.g. Bedrock's jobArn) */
|
|
176
|
+
meta?: Record<string, unknown>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Per-provider in-memory job registry. Encapsulates the
|
|
181
|
+
* `Map<jobId, state>` + counter + status/result lookup pattern that
|
|
182
|
+
* google/bedrock/cloudflare were each duplicating.
|
|
183
|
+
*/
|
|
184
|
+
export class LocalJobStore {
|
|
185
|
+
private readonly jobs = new Map<string, LocalJobState>()
|
|
186
|
+
private counter = 0
|
|
187
|
+
|
|
188
|
+
constructor(private readonly idPrefix: string) {}
|
|
189
|
+
|
|
190
|
+
create(items: BatchItem[], options: BatchQueueOptions): { id: string; state: LocalJobState } {
|
|
191
|
+
const id = `${this.idPrefix}_${++this.counter}_${Date.now()}`
|
|
192
|
+
const state: LocalJobState = {
|
|
193
|
+
items,
|
|
194
|
+
options,
|
|
195
|
+
results: [],
|
|
196
|
+
status: 'pending',
|
|
197
|
+
createdAt: new Date(),
|
|
198
|
+
}
|
|
199
|
+
this.jobs.set(id, state)
|
|
200
|
+
return { id, state }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get(id: string): LocalJobState {
|
|
204
|
+
const state = this.jobs.get(id)
|
|
205
|
+
if (!state) {
|
|
206
|
+
throw new Error(`Batch not found: ${id}`)
|
|
207
|
+
}
|
|
208
|
+
return state
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
has(id: string): boolean {
|
|
212
|
+
return this.jobs.has(id)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Build a `BatchJob` snapshot for a tracked job. */
|
|
216
|
+
snapshot(id: string, provider: BatchJob['provider']): BatchJob {
|
|
217
|
+
const state = this.get(id)
|
|
218
|
+
const completedItems = state.results.filter((r) => r.status === 'completed').length
|
|
219
|
+
const failedItems = state.results.filter((r) => r.status === 'failed').length
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
id,
|
|
223
|
+
provider,
|
|
224
|
+
status: state.status,
|
|
225
|
+
totalItems: state.items.length,
|
|
226
|
+
completedItems,
|
|
227
|
+
failedItems,
|
|
228
|
+
createdAt: state.createdAt,
|
|
229
|
+
...(state.completedAt && { completedAt: state.completedAt }),
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Wait for a tracked job to reach a terminal status by polling its in-memory
|
|
235
|
+
* state. Adapters that drive the state machine in a background promise can
|
|
236
|
+
* call this from `waitForCompletion`.
|
|
237
|
+
*/
|
|
238
|
+
async waitForCompletion(id: string, pollInterval = 1000): Promise<BatchResult[]> {
|
|
239
|
+
const state = this.get(id)
|
|
240
|
+
while (
|
|
241
|
+
state.status !== 'completed' &&
|
|
242
|
+
state.status !== 'failed' &&
|
|
243
|
+
state.status !== 'cancelled'
|
|
244
|
+
) {
|
|
245
|
+
await sleep(pollInterval)
|
|
246
|
+
}
|
|
247
|
+
return state.results
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** For tests: drop everything. */
|
|
251
|
+
clear(): void {
|
|
252
|
+
this.jobs.clear()
|
|
253
|
+
this.counter = 0
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ============================================================================
|
|
258
|
+
// JSON-Schema conversion (used by Anthropic + OpenAI adapters)
|
|
259
|
+
// ============================================================================
|
|
260
|
+
|
|
261
|
+
/** Zod schema definition structure for type introspection. */
|
|
262
|
+
interface ZodDef {
|
|
263
|
+
typeName?: string
|
|
264
|
+
type?: unknown
|
|
265
|
+
shape?: () => Record<string, unknown>
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Zod schema with `_def` property for introspection. */
|
|
269
|
+
interface ZodSchemaLike {
|
|
270
|
+
_def?: ZodDef
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Minimal Zod -> JSON Schema converter.
|
|
275
|
+
*
|
|
276
|
+
* This is the same simplified converter that previously lived (duplicated)
|
|
277
|
+
* inside `anthropic.ts` and `openai.ts`. Extracted here so both adapters call
|
|
278
|
+
* the same implementation. For richer conversion use `zod-to-json-schema`.
|
|
279
|
+
*/
|
|
280
|
+
export function zodToJsonSchema(zodSchema: unknown): Record<string, unknown> {
|
|
281
|
+
const schema = zodSchema as ZodSchemaLike
|
|
282
|
+
|
|
283
|
+
if (!schema._def) {
|
|
284
|
+
return { type: 'object' }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
switch (schema._def.typeName) {
|
|
288
|
+
case 'ZodString':
|
|
289
|
+
return { type: 'string' }
|
|
290
|
+
case 'ZodNumber':
|
|
291
|
+
return { type: 'number' }
|
|
292
|
+
case 'ZodBoolean':
|
|
293
|
+
return { type: 'boolean' }
|
|
294
|
+
case 'ZodArray':
|
|
295
|
+
return { type: 'array', items: zodToJsonSchema(schema._def.type) }
|
|
296
|
+
case 'ZodObject': {
|
|
297
|
+
const shape = schema._def.shape?.() ?? {}
|
|
298
|
+
const properties: Record<string, unknown> = {}
|
|
299
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
300
|
+
properties[key] = zodToJsonSchema(value)
|
|
301
|
+
}
|
|
302
|
+
return { type: 'object', properties, required: Object.keys(properties) }
|
|
303
|
+
}
|
|
304
|
+
default:
|
|
305
|
+
return { type: 'object' }
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ============================================================================
|
|
310
|
+
// JSON parsing (used by every adapter that returns raw text)
|
|
311
|
+
// ============================================================================
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Try to parse `text` as JSON when it looks like JSON or a schema is expected,
|
|
315
|
+
* otherwise return the text unchanged. Never throws.
|
|
316
|
+
*/
|
|
317
|
+
export function tryParseJson(text: string | undefined, expectJson = false): unknown {
|
|
318
|
+
if (!text) return text
|
|
319
|
+
const trimmed = text.trim()
|
|
320
|
+
const looksLikeJson = trimmed.startsWith('{') || trimmed.startsWith('[')
|
|
321
|
+
if (!expectJson && !looksLikeJson) return text
|
|
322
|
+
try {
|
|
323
|
+
return JSON.parse(text)
|
|
324
|
+
} catch {
|
|
325
|
+
return text
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ============================================================================
|
|
330
|
+
// Misc
|
|
331
|
+
// ============================================================================
|
|
332
|
+
|
|
333
|
+
/** Promise-based setTimeout. */
|
|
334
|
+
export function sleep(ms: number): Promise<void> {
|
|
335
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
336
|
+
}
|
package/src/batch-map.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
isFlexAvailable,
|
|
28
28
|
type ExecutionTier,
|
|
29
29
|
} from './context.js'
|
|
30
|
+
import { getLogger } from './logger.js'
|
|
30
31
|
import { createBatch, getBatchAdapter, type BatchItem, type BatchResult } from './batch-queue.js'
|
|
31
32
|
import { generateObject, generateText } from './generate.js'
|
|
32
33
|
import type { SimpleSchema } from './schema.js'
|
|
@@ -100,11 +101,7 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
100
101
|
/** Cached resolver promise */
|
|
101
102
|
private _resolver: Promise<T[]> | null = null
|
|
102
103
|
|
|
103
|
-
constructor(
|
|
104
|
-
items: unknown[],
|
|
105
|
-
operations: CapturedOperation[][],
|
|
106
|
-
options: BatchMapOptions = {}
|
|
107
|
-
) {
|
|
104
|
+
constructor(items: unknown[], operations: CapturedOperation[][], options: BatchMapOptions = {}) {
|
|
108
105
|
this._items = items
|
|
109
106
|
this._operations = operations
|
|
110
107
|
this._options = options
|
|
@@ -144,7 +141,9 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
144
141
|
if (isFlexAvailable()) {
|
|
145
142
|
return this._resolveViaFlex()
|
|
146
143
|
}
|
|
147
|
-
|
|
144
|
+
getLogger().warn(
|
|
145
|
+
`Flex processing not available for ${getProvider()}, using immediate execution`
|
|
146
|
+
)
|
|
148
147
|
return this._resolveImmediately()
|
|
149
148
|
|
|
150
149
|
case 'batch':
|
|
@@ -186,8 +185,8 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
186
185
|
batchItems.push({
|
|
187
186
|
id,
|
|
188
187
|
prompt,
|
|
189
|
-
schema: op.schema,
|
|
190
|
-
options: { system: op.system
|
|
188
|
+
...(op.schema !== undefined && { schema: op.schema }),
|
|
189
|
+
options: Object.assign({ model }, op.system !== undefined ? { system: op.system } : {}),
|
|
191
190
|
status: 'pending',
|
|
192
191
|
})
|
|
193
192
|
|
|
@@ -200,7 +199,7 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
200
199
|
return this._reconstructResults(results, itemOperationMap)
|
|
201
200
|
} catch {
|
|
202
201
|
// Flex adapter not available, fall back to batch or immediate
|
|
203
|
-
|
|
202
|
+
getLogger().warn(`Flex adapter not available, falling back to batch API`)
|
|
204
203
|
return this._resolveViaBatchAPI()
|
|
205
204
|
}
|
|
206
205
|
}
|
|
@@ -219,7 +218,9 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
219
218
|
adapter = getBatchAdapter(provider)
|
|
220
219
|
} catch {
|
|
221
220
|
// Adapter not registered, fall back to immediate execution
|
|
222
|
-
|
|
221
|
+
getLogger().warn(
|
|
222
|
+
`Batch adapter for ${provider} not available, falling back to immediate execution`
|
|
223
|
+
)
|
|
223
224
|
return this._resolveImmediately()
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -243,8 +244,8 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
243
244
|
batchItems.push({
|
|
244
245
|
id,
|
|
245
246
|
prompt,
|
|
246
|
-
schema: op.schema,
|
|
247
|
-
options: { system: op.system
|
|
247
|
+
...(op.schema !== undefined && { schema: op.schema }),
|
|
248
|
+
options: Object.assign({ model }, op.system !== undefined ? { system: op.system } : {}),
|
|
248
249
|
status: 'pending',
|
|
249
250
|
})
|
|
250
251
|
|
|
@@ -256,15 +257,15 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
256
257
|
const batch = createBatch({
|
|
257
258
|
provider,
|
|
258
259
|
model,
|
|
259
|
-
webhookUrl: ctx.webhookUrl,
|
|
260
|
-
metadata: ctx.metadata,
|
|
260
|
+
...(ctx.webhookUrl !== undefined && { webhookUrl: ctx.webhookUrl }),
|
|
261
|
+
...(ctx.metadata !== undefined && { metadata: ctx.metadata }),
|
|
261
262
|
})
|
|
262
263
|
|
|
263
264
|
for (const item of batchItems) {
|
|
264
265
|
batch.add(item.prompt, {
|
|
265
|
-
schema: item.schema,
|
|
266
|
-
options: item.options,
|
|
267
|
-
customId: item.id,
|
|
266
|
+
...(item.schema !== undefined && { schema: item.schema }),
|
|
267
|
+
...(item.options !== undefined && { options: item.options }),
|
|
268
|
+
...(item.id !== undefined && { customId: item.id }),
|
|
268
269
|
})
|
|
269
270
|
}
|
|
270
271
|
|
|
@@ -324,7 +325,11 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
324
325
|
): Promise<unknown> {
|
|
325
326
|
switch (op.type) {
|
|
326
327
|
case 'text':
|
|
327
|
-
const textResult = await generateText({
|
|
328
|
+
const textResult = await generateText({
|
|
329
|
+
model,
|
|
330
|
+
prompt,
|
|
331
|
+
...(op.system !== undefined && { system: op.system }),
|
|
332
|
+
})
|
|
328
333
|
return textResult.text
|
|
329
334
|
|
|
330
335
|
case 'boolean':
|
|
@@ -341,7 +346,7 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
341
346
|
model,
|
|
342
347
|
schema: { items: ['List items'] },
|
|
343
348
|
prompt,
|
|
344
|
-
system: op.system,
|
|
349
|
+
...(op.system !== undefined && { system: op.system }),
|
|
345
350
|
})
|
|
346
351
|
return (listResult.object as { items: string[] }).items
|
|
347
352
|
|
|
@@ -351,7 +356,7 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
351
356
|
model,
|
|
352
357
|
schema: op.schema || { result: 'The result' },
|
|
353
358
|
prompt,
|
|
354
|
-
system: op.system,
|
|
359
|
+
...(op.system !== undefined && { system: op.system }),
|
|
355
360
|
})
|
|
356
361
|
return objResult.object
|
|
357
362
|
}
|
|
@@ -387,7 +392,7 @@ export class BatchMapPromise<T> implements PromiseLike<T[]> {
|
|
|
387
392
|
if (operations.length === 1) {
|
|
388
393
|
results[itemIndex] = batchResult.result as T
|
|
389
394
|
} else {
|
|
390
|
-
(results[itemIndex] as Record<string, unknown>)[`op_${opIndex}`] = batchResult.result
|
|
395
|
+
;(results[itemIndex] as Record<string, unknown>)[`op_${opIndex}`] = batchResult.result
|
|
391
396
|
}
|
|
392
397
|
}
|
|
393
398
|
|
|
@@ -499,9 +504,9 @@ export function captureOperation(
|
|
|
499
504
|
id: `op_${++operationCounter}`,
|
|
500
505
|
prompt,
|
|
501
506
|
itemPlaceholder: currentItemPlaceholder,
|
|
502
|
-
schema,
|
|
507
|
+
...(schema !== undefined && { schema }),
|
|
503
508
|
type,
|
|
504
|
-
system,
|
|
509
|
+
...(system !== undefined && { system }),
|
|
505
510
|
})
|
|
506
511
|
}
|
|
507
512
|
|
|
@@ -571,6 +576,6 @@ export function isBatchMapPromise(value: unknown): value is BatchMapPromise<unkn
|
|
|
571
576
|
value !== null &&
|
|
572
577
|
typeof value === 'object' &&
|
|
573
578
|
BATCH_MAP_SYMBOL in value &&
|
|
574
|
-
(value as
|
|
579
|
+
(value as Record<symbol, unknown>)[BATCH_MAP_SYMBOL] === true
|
|
575
580
|
)
|
|
576
581
|
}
|