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.
Files changed (284) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +90 -1
  3. package/README.md +38 -0
  4. package/dist/ai-promise.d.ts +3 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +135 -64
  7. package/dist/ai-promise.js.map +1 -1
  8. package/dist/ai-schemas.d.ts +56 -0
  9. package/dist/ai-schemas.d.ts.map +1 -0
  10. package/dist/ai-schemas.js +53 -0
  11. package/dist/ai-schemas.js.map +1 -0
  12. package/dist/ai.d.ts +16 -242
  13. package/dist/ai.d.ts.map +1 -1
  14. package/dist/ai.js +51 -858
  15. package/dist/ai.js.map +1 -1
  16. package/dist/batch/anthropic.d.ts +6 -4
  17. package/dist/batch/anthropic.d.ts.map +1 -1
  18. package/dist/batch/anthropic.js +83 -145
  19. package/dist/batch/anthropic.js.map +1 -1
  20. package/dist/batch/bedrock.d.ts +8 -30
  21. package/dist/batch/bedrock.d.ts.map +1 -1
  22. package/dist/batch/bedrock.js +155 -338
  23. package/dist/batch/bedrock.js.map +1 -1
  24. package/dist/batch/cloudflare.d.ts +8 -20
  25. package/dist/batch/cloudflare.d.ts.map +1 -1
  26. package/dist/batch/cloudflare.js +68 -189
  27. package/dist/batch/cloudflare.js.map +1 -1
  28. package/dist/batch/google.d.ts +6 -20
  29. package/dist/batch/google.d.ts.map +1 -1
  30. package/dist/batch/google.js +70 -238
  31. package/dist/batch/google.js.map +1 -1
  32. package/dist/batch/index.d.ts +4 -1
  33. package/dist/batch/index.d.ts.map +1 -1
  34. package/dist/batch/index.js +4 -1
  35. package/dist/batch/index.js.map +1 -1
  36. package/dist/batch/memory.d.ts +1 -1
  37. package/dist/batch/memory.d.ts.map +1 -1
  38. package/dist/batch/memory.js +14 -10
  39. package/dist/batch/memory.js.map +1 -1
  40. package/dist/batch/openai.d.ts +11 -14
  41. package/dist/batch/openai.d.ts.map +1 -1
  42. package/dist/batch/openai.js +52 -156
  43. package/dist/batch/openai.js.map +1 -1
  44. package/dist/batch/provider.d.ts +111 -0
  45. package/dist/batch/provider.d.ts.map +1 -0
  46. package/dist/batch/provider.js +233 -0
  47. package/dist/batch/provider.js.map +1 -0
  48. package/dist/batch-map.d.ts.map +1 -1
  49. package/dist/batch-map.js +23 -17
  50. package/dist/batch-map.js.map +1 -1
  51. package/dist/batch-queue.d.ts +65 -0
  52. package/dist/batch-queue.d.ts.map +1 -1
  53. package/dist/batch-queue.js +169 -14
  54. package/dist/batch-queue.js.map +1 -1
  55. package/dist/budget.d.ts.map +1 -1
  56. package/dist/budget.js +27 -14
  57. package/dist/budget.js.map +1 -1
  58. package/dist/cache.d.ts +23 -0
  59. package/dist/cache.d.ts.map +1 -1
  60. package/dist/cache.js +36 -15
  61. package/dist/cache.js.map +1 -1
  62. package/dist/context.d.ts +26 -8
  63. package/dist/context.d.ts.map +1 -1
  64. package/dist/context.js +64 -62
  65. package/dist/context.js.map +1 -1
  66. package/dist/digital-objects-registry.d.ts +229 -0
  67. package/dist/digital-objects-registry.d.ts.map +1 -0
  68. package/dist/digital-objects-registry.js +617 -0
  69. package/dist/digital-objects-registry.js.map +1 -0
  70. package/dist/embeddings.d.ts +2 -2
  71. package/dist/embeddings.d.ts.map +1 -1
  72. package/dist/errors.d.ts +22 -0
  73. package/dist/errors.d.ts.map +1 -0
  74. package/dist/errors.js +35 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/eval/runner.d.ts +8 -0
  77. package/dist/eval/runner.d.ts.map +1 -1
  78. package/dist/eval/runner.js +41 -35
  79. package/dist/eval/runner.js.map +1 -1
  80. package/dist/eval-log/in-memory.d.ts +34 -0
  81. package/dist/eval-log/in-memory.d.ts.map +1 -0
  82. package/dist/eval-log/in-memory.js +84 -0
  83. package/dist/eval-log/in-memory.js.map +1 -0
  84. package/dist/eval-log/index.d.ts +29 -0
  85. package/dist/eval-log/index.d.ts.map +1 -0
  86. package/dist/eval-log/index.js +39 -0
  87. package/dist/eval-log/index.js.map +1 -0
  88. package/dist/eval-log/types.d.ts +101 -0
  89. package/dist/eval-log/types.d.ts.map +1 -0
  90. package/dist/eval-log/types.js +16 -0
  91. package/dist/eval-log/types.js.map +1 -0
  92. package/dist/function-registry.d.ts +176 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +685 -0
  95. package/dist/function-registry.js.map +1 -0
  96. package/dist/generate.d.ts +9 -3
  97. package/dist/generate.d.ts.map +1 -1
  98. package/dist/generate.js +18 -18
  99. package/dist/generate.js.map +1 -1
  100. package/dist/index.d.ts +18 -11
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +35 -18
  103. package/dist/index.js.map +1 -1
  104. package/dist/logger.d.ts +118 -0
  105. package/dist/logger.d.ts.map +1 -0
  106. package/dist/logger.js +187 -0
  107. package/dist/logger.js.map +1 -0
  108. package/dist/middleware/budget.d.ts +84 -0
  109. package/dist/middleware/budget.d.ts.map +1 -0
  110. package/dist/middleware/budget.js +110 -0
  111. package/dist/middleware/budget.js.map +1 -0
  112. package/dist/middleware/cache.d.ts +103 -0
  113. package/dist/middleware/cache.d.ts.map +1 -0
  114. package/dist/middleware/cache.js +228 -0
  115. package/dist/middleware/cache.js.map +1 -0
  116. package/dist/middleware/embed-cache.d.ts +99 -0
  117. package/dist/middleware/embed-cache.d.ts.map +1 -0
  118. package/dist/middleware/embed-cache.js +128 -0
  119. package/dist/middleware/embed-cache.js.map +1 -0
  120. package/dist/middleware/index.d.ts +11 -0
  121. package/dist/middleware/index.d.ts.map +1 -0
  122. package/dist/middleware/index.js +11 -0
  123. package/dist/middleware/index.js.map +1 -0
  124. package/dist/middleware/trace.d.ts +103 -0
  125. package/dist/middleware/trace.d.ts.map +1 -0
  126. package/dist/middleware/trace.js +176 -0
  127. package/dist/middleware/trace.js.map +1 -0
  128. package/dist/primitives.d.ts +120 -1
  129. package/dist/primitives.d.ts.map +1 -1
  130. package/dist/primitives.js +398 -26
  131. package/dist/primitives.js.map +1 -1
  132. package/dist/retry.d.ts +66 -1
  133. package/dist/retry.d.ts.map +1 -1
  134. package/dist/retry.js +115 -8
  135. package/dist/retry.js.map +1 -1
  136. package/dist/sandbox.d.ts +36 -0
  137. package/dist/sandbox.d.ts.map +1 -0
  138. package/dist/sandbox.js +44 -0
  139. package/dist/sandbox.js.map +1 -0
  140. package/dist/schema.js +2 -2
  141. package/dist/schema.js.map +1 -1
  142. package/dist/telemetry.d.ts +128 -0
  143. package/dist/telemetry.d.ts.map +1 -0
  144. package/dist/telemetry.js +285 -0
  145. package/dist/telemetry.js.map +1 -0
  146. package/dist/template.d.ts.map +1 -1
  147. package/dist/template.js +6 -1
  148. package/dist/template.js.map +1 -1
  149. package/dist/tool-orchestration.d.ts +66 -4
  150. package/dist/tool-orchestration.d.ts.map +1 -1
  151. package/dist/tool-orchestration.js +123 -23
  152. package/dist/tool-orchestration.js.map +1 -1
  153. package/dist/type-guards.d.ts +28 -0
  154. package/dist/type-guards.d.ts.map +1 -0
  155. package/dist/type-guards.js +29 -0
  156. package/dist/type-guards.js.map +1 -0
  157. package/dist/types.d.ts +155 -19
  158. package/dist/types.d.ts.map +1 -1
  159. package/dist/types.js +36 -1
  160. package/dist/types.js.map +1 -1
  161. package/dist/wrap-for-v3.d.ts +80 -0
  162. package/dist/wrap-for-v3.d.ts.map +1 -0
  163. package/dist/wrap-for-v3.js +89 -0
  164. package/dist/wrap-for-v3.js.map +1 -0
  165. package/examples/00-quickstart.ts +232 -0
  166. package/examples/01-rag-chatbot.ts +212 -0
  167. package/examples/02-multi-agent-research.ts +290 -0
  168. package/examples/03-email-classification.ts +379 -0
  169. package/examples/04-content-moderation.ts +400 -0
  170. package/examples/05-document-extraction.ts +455 -0
  171. package/examples/06-streaming-chat-nextjs.ts +437 -0
  172. package/examples/07-cloudflare-worker.ts +483 -0
  173. package/examples/08-batch-processing.ts +491 -0
  174. package/examples/09-budget-constrained.ts +527 -0
  175. package/examples/10-tool-orchestration.ts +565 -0
  176. package/examples/11-retry-resilience.ts +403 -0
  177. package/examples/12-caching-strategies.ts +422 -0
  178. package/examples/README.md +145 -0
  179. package/package.json +29 -25
  180. package/src/ai-promise.ts +226 -140
  181. package/src/ai-schemas.ts +122 -0
  182. package/src/ai.ts +71 -1176
  183. package/src/batch/anthropic.ts +96 -161
  184. package/src/batch/bedrock.ts +203 -454
  185. package/src/batch/cloudflare.ts +99 -282
  186. package/src/batch/google.ts +91 -297
  187. package/src/batch/index.ts +4 -1
  188. package/src/batch/memory.ts +15 -10
  189. package/src/batch/openai.ts +65 -193
  190. package/src/batch/provider.ts +336 -0
  191. package/src/batch-map.ts +29 -24
  192. package/src/batch-queue.ts +200 -11
  193. package/src/budget.ts +31 -18
  194. package/src/cache.ts +45 -17
  195. package/src/context.ts +106 -77
  196. package/src/digital-objects-registry.ts +750 -0
  197. package/src/errors.ts +37 -0
  198. package/src/eval/runner.ts +60 -36
  199. package/src/eval-log/in-memory.ts +90 -0
  200. package/src/eval-log/index.ts +46 -0
  201. package/src/eval-log/types.ts +110 -0
  202. package/src/function-registry.ts +874 -0
  203. package/src/generate.ts +33 -28
  204. package/src/index.ts +122 -21
  205. package/src/logger.ts +232 -0
  206. package/src/middleware/budget.ts +171 -0
  207. package/src/middleware/cache.ts +299 -0
  208. package/src/middleware/embed-cache.ts +195 -0
  209. package/src/middleware/index.ts +23 -0
  210. package/src/middleware/trace.ts +248 -0
  211. package/src/primitives.ts +589 -62
  212. package/src/retry.ts +144 -18
  213. package/src/sandbox.ts +52 -0
  214. package/src/schema.ts +8 -8
  215. package/src/telemetry.ts +403 -0
  216. package/src/template.ts +8 -4
  217. package/src/tool-orchestration.ts +213 -48
  218. package/src/type-guards.ts +31 -0
  219. package/src/types.ts +186 -27
  220. package/src/wrap-for-v3.ts +105 -0
  221. package/test/ai-promise.test.ts +1080 -0
  222. package/test/ai-proxy.test.ts +1 -1
  223. package/test/batch-autosubmit-errors.test.ts +49 -37
  224. package/test/batch-blog-posts.test.ts +87 -129
  225. package/test/core-functions.test.ts +183 -579
  226. package/test/decide.test.ts +154 -322
  227. package/test/define.test.ts +211 -8
  228. package/test/digital-objects-registry.test.ts +760 -0
  229. package/test/embedding-cache-middleware.test.ts +140 -0
  230. package/test/fill-template.test.ts +89 -0
  231. package/test/generate-core.test.ts +140 -229
  232. package/test/implicit-batch.test.ts +22 -65
  233. package/test/retry-policy-integration.test.ts +117 -0
  234. package/test/sandbox-execution.test.ts +155 -0
  235. package/test/schema.test.ts +55 -19
  236. package/test/template.test.ts +1164 -0
  237. package/test/tool-orchestration.test.ts +270 -0
  238. package/test/wrap-for-v3.test.ts +612 -0
  239. package/vitest.config.js +6 -0
  240. package/vitest.config.ts +20 -0
  241. package/LICENSE +0 -21
  242. package/dist/rpc/auth.d.ts +0 -69
  243. package/dist/rpc/auth.d.ts.map +0 -1
  244. package/dist/rpc/auth.js +0 -136
  245. package/dist/rpc/auth.js.map +0 -1
  246. package/dist/rpc/client.d.ts +0 -62
  247. package/dist/rpc/client.d.ts.map +0 -1
  248. package/dist/rpc/client.js +0 -103
  249. package/dist/rpc/client.js.map +0 -1
  250. package/dist/rpc/deferred.d.ts +0 -60
  251. package/dist/rpc/deferred.d.ts.map +0 -1
  252. package/dist/rpc/deferred.js +0 -96
  253. package/dist/rpc/deferred.js.map +0 -1
  254. package/dist/rpc/index.d.ts +0 -22
  255. package/dist/rpc/index.d.ts.map +0 -1
  256. package/dist/rpc/index.js +0 -38
  257. package/dist/rpc/index.js.map +0 -1
  258. package/dist/rpc/local.d.ts +0 -42
  259. package/dist/rpc/local.d.ts.map +0 -1
  260. package/dist/rpc/local.js +0 -50
  261. package/dist/rpc/local.js.map +0 -1
  262. package/dist/rpc/server.d.ts +0 -165
  263. package/dist/rpc/server.d.ts.map +0 -1
  264. package/dist/rpc/server.js +0 -405
  265. package/dist/rpc/server.js.map +0 -1
  266. package/dist/rpc/session.d.ts +0 -32
  267. package/dist/rpc/session.d.ts.map +0 -1
  268. package/dist/rpc/session.js +0 -43
  269. package/dist/rpc/session.js.map +0 -1
  270. package/dist/rpc/transport.d.ts +0 -306
  271. package/dist/rpc/transport.d.ts.map +0 -1
  272. package/dist/rpc/transport.js +0 -731
  273. package/dist/rpc/transport.js.map +0 -1
  274. package/src/batch/anthropic.js +0 -256
  275. package/src/batch/bedrock.js +0 -584
  276. package/src/batch/cloudflare.js +0 -287
  277. package/src/batch/google.js +0 -359
  278. package/src/batch/index.js +0 -30
  279. package/src/batch/memory.js +0 -187
  280. package/src/batch/openai.js +0 -402
  281. package/src/eval/index.js +0 -7
  282. package/src/eval/models.js +0 -119
  283. package/src/eval/runner.js +0 -147
  284. package/test/schema.test.js +0 -96
@@ -1,584 +0,0 @@
1
- /**
2
- * AWS Bedrock Batch Inference Adapter
3
- *
4
- * Implements batch processing using AWS Bedrock's batch inference API.
5
- * Bedrock batch inference provides cost-effective processing for large workloads.
6
- *
7
- * @see https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference.html
8
- *
9
- * @packageDocumentation
10
- */
11
- import { registerBatchAdapter, registerFlexAdapter, } from '../batch-queue.js';
12
- // ============================================================================
13
- // AWS Configuration
14
- // ============================================================================
15
- let awsRegion;
16
- let awsAccessKeyId;
17
- let awsSecretAccessKey;
18
- let awsSessionToken;
19
- let s3Bucket;
20
- let roleArn;
21
- // AI Gateway configuration (optional - for routing through Cloudflare AI Gateway)
22
- let gatewayUrl;
23
- let gatewayToken;
24
- /**
25
- * Configure AWS credentials and settings
26
- */
27
- export function configureAWSBedrock(options) {
28
- if (options.region)
29
- awsRegion = options.region;
30
- if (options.accessKeyId)
31
- awsAccessKeyId = options.accessKeyId;
32
- if (options.secretAccessKey)
33
- awsSecretAccessKey = options.secretAccessKey;
34
- if (options.sessionToken)
35
- awsSessionToken = options.sessionToken;
36
- if (options.s3Bucket)
37
- s3Bucket = options.s3Bucket;
38
- if (options.roleArn)
39
- roleArn = options.roleArn;
40
- if (options.gatewayUrl)
41
- gatewayUrl = options.gatewayUrl;
42
- if (options.gatewayToken)
43
- gatewayToken = options.gatewayToken;
44
- }
45
- function getConfig() {
46
- const region = awsRegion || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
47
- const accessKeyId = awsAccessKeyId || process.env.AWS_ACCESS_KEY_ID;
48
- const secretAccessKey = awsSecretAccessKey || process.env.AWS_SECRET_ACCESS_KEY;
49
- const sessionToken = awsSessionToken || process.env.AWS_SESSION_TOKEN;
50
- const bucket = s3Bucket || process.env.BEDROCK_BATCH_S3_BUCKET;
51
- const role = roleArn || process.env.BEDROCK_BATCH_ROLE_ARN;
52
- // Check for AI Gateway configuration
53
- const gwUrl = gatewayUrl || process.env.AI_GATEWAY_URL;
54
- const gwToken = gatewayToken || process.env.AI_GATEWAY_TOKEN;
55
- // If using gateway, we don't need AWS credentials
56
- if (gwUrl && gwToken) {
57
- return {
58
- region,
59
- accessKeyId: accessKeyId || '',
60
- secretAccessKey: secretAccessKey || '',
61
- sessionToken,
62
- bucket: bucket || '',
63
- role,
64
- gatewayUrl: gwUrl,
65
- gatewayToken: gwToken,
66
- };
67
- }
68
- if (!accessKeyId || !secretAccessKey) {
69
- throw new Error('AWS credentials not configured. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or use AI_GATEWAY_URL and AI_GATEWAY_TOKEN');
70
- }
71
- if (!bucket) {
72
- throw new Error('S3 bucket for Bedrock batch not configured. Set BEDROCK_BATCH_S3_BUCKET');
73
- }
74
- return { region, accessKeyId, secretAccessKey, sessionToken, bucket, role, gatewayUrl: undefined, gatewayToken: undefined };
75
- }
76
- // ============================================================================
77
- // AWS Signature V4 (Simplified)
78
- // ============================================================================
79
- async function signRequest(method, url, body, config, service) {
80
- // In production, use @aws-sdk/signature-v4 or similar
81
- // This is a simplified implementation for demonstration
82
- const headers = new Headers({
83
- 'Content-Type': 'application/json',
84
- 'X-Amz-Date': new Date().toISOString().replace(/[:-]|\.\d{3}/g, ''),
85
- });
86
- if (config.sessionToken) {
87
- headers.set('X-Amz-Security-Token', config.sessionToken);
88
- }
89
- // For actual implementation, compute proper AWS Signature V4
90
- // This requires crypto operations that vary by environment
91
- // Fallback: Use AWS SDK if available
92
- try {
93
- // Dynamic import to avoid build-time dependency
94
- // @ts-expect-error - Optional dependency
95
- const signatureV4Module = await import('@smithy/signature-v4');
96
- // @ts-expect-error - Optional dependency
97
- const sha256Module = await import('@aws-crypto/sha256-js');
98
- const SignatureV4 = signatureV4Module.SignatureV4;
99
- const Sha256 = sha256Module.Sha256;
100
- const signer = new SignatureV4({
101
- service,
102
- region: config.region,
103
- credentials: {
104
- accessKeyId: config.accessKeyId,
105
- secretAccessKey: config.secretAccessKey,
106
- sessionToken: config.sessionToken,
107
- },
108
- sha256: Sha256,
109
- });
110
- const signedRequest = await signer.sign({
111
- method,
112
- headers: Object.fromEntries(headers.entries()),
113
- hostname: new URL(url).hostname,
114
- path: new URL(url).pathname,
115
- body,
116
- });
117
- return new Headers(signedRequest.headers);
118
- }
119
- catch {
120
- // AWS SDK not available - return basic headers
121
- // In production, the SDK should always be available
122
- console.warn('AWS SDK not available for request signing. Install @smithy/signature-v4 and @aws-crypto/sha256-js');
123
- return headers;
124
- }
125
- }
126
- // ============================================================================
127
- // In-memory job tracking
128
- // ============================================================================
129
- const pendingJobs = new Map();
130
- let jobCounter = 0;
131
- // ============================================================================
132
- // Bedrock Batch Adapter
133
- // ============================================================================
134
- /**
135
- * AWS Bedrock batch adapter
136
- *
137
- * Bedrock batch inference:
138
- * 1. Uploads input JSONL to S3
139
- * 2. Creates a batch inference job
140
- * 3. Results are written to S3
141
- * 4. Download and parse results
142
- *
143
- * Note: This requires S3 bucket access and proper IAM roles.
144
- */
145
- const bedrockAdapter = {
146
- async submit(items, options) {
147
- const config = getConfig();
148
- const jobId = `bedrock_batch_${++jobCounter}_${Date.now()}`;
149
- // Default to Claude on Bedrock
150
- const model = options.model || 'anthropic.claude-3-sonnet-20240229-v1:0';
151
- // Store job state
152
- pendingJobs.set(jobId, {
153
- items,
154
- options,
155
- results: [],
156
- status: 'pending',
157
- createdAt: new Date(),
158
- });
159
- // For true Bedrock batch processing:
160
- // 1. Create JSONL file with requests
161
- // 2. Upload to S3
162
- // 3. Create batch inference job via Bedrock API
163
- // 4. Poll for completion
164
- // 5. Download and parse results from S3
165
- // For now, we implement a concurrent processing approach
166
- // (similar to Cloudflare) that works without S3 setup
167
- const completion = processBedrockRequestsConcurrently(jobId, items, config, model, options);
168
- const job = {
169
- id: jobId,
170
- provider: 'bedrock',
171
- status: 'pending',
172
- totalItems: items.length,
173
- completedItems: 0,
174
- failedItems: 0,
175
- createdAt: new Date(),
176
- webhookUrl: options.webhookUrl,
177
- };
178
- return { job, completion };
179
- },
180
- async getStatus(batchId) {
181
- const job = pendingJobs.get(batchId);
182
- if (!job) {
183
- throw new Error(`Batch not found: ${batchId}`);
184
- }
185
- const completedItems = job.results.filter((r) => r.status === 'completed').length;
186
- const failedItems = job.results.filter((r) => r.status === 'failed').length;
187
- return {
188
- id: batchId,
189
- provider: 'bedrock',
190
- status: job.status,
191
- totalItems: job.items.length,
192
- completedItems,
193
- failedItems,
194
- createdAt: job.createdAt,
195
- completedAt: job.completedAt,
196
- };
197
- },
198
- async cancel(batchId) {
199
- const job = pendingJobs.get(batchId);
200
- if (job) {
201
- job.status = 'cancelled';
202
- // If we have a Bedrock job ARN, cancel it
203
- if (job.jobArn) {
204
- const config = getConfig();
205
- const url = `https://bedrock.${config.region}.amazonaws.com/model-invocation-job/${encodeURIComponent(job.jobArn)}/stop`;
206
- try {
207
- await fetch(url, {
208
- method: 'POST',
209
- headers: await signRequest('POST', url, '', config, 'bedrock'),
210
- });
211
- }
212
- catch (error) {
213
- console.warn('Failed to cancel Bedrock job:', error);
214
- }
215
- }
216
- }
217
- },
218
- async getResults(batchId) {
219
- const job = pendingJobs.get(batchId);
220
- if (!job) {
221
- throw new Error(`Batch not found: ${batchId}`);
222
- }
223
- return job.results;
224
- },
225
- async waitForCompletion(batchId, pollInterval = 5000) {
226
- const job = pendingJobs.get(batchId);
227
- if (!job) {
228
- throw new Error(`Batch not found: ${batchId}`);
229
- }
230
- while (job.status !== 'completed' && job.status !== 'failed' && job.status !== 'cancelled') {
231
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
232
- }
233
- return job.results;
234
- },
235
- };
236
- // ============================================================================
237
- // Processing (Concurrent Mode)
238
- // ============================================================================
239
- /**
240
- * Process Bedrock requests concurrently
241
- * This is a fallback when true batch inference isn't configured
242
- */
243
- async function processBedrockRequestsConcurrently(jobId, items, config, model, options) {
244
- const job = pendingJobs.get(jobId);
245
- if (!job) {
246
- throw new Error(`Job not found: ${jobId}`);
247
- }
248
- job.status = 'in_progress';
249
- // Process with concurrency limit
250
- const CONCURRENCY = 5; // Bedrock has stricter rate limits
251
- const results = [];
252
- for (let i = 0; i < items.length; i += CONCURRENCY) {
253
- const batch = items.slice(i, i + CONCURRENCY);
254
- const batchResults = await Promise.all(batch.map(async (item) => {
255
- try {
256
- return await processBedrockItem(item, config, model);
257
- }
258
- catch (error) {
259
- return {
260
- id: item.id,
261
- customId: item.id,
262
- status: 'failed',
263
- error: error instanceof Error ? error.message : 'Unknown error',
264
- };
265
- }
266
- }));
267
- results.push(...batchResults);
268
- job.results = results;
269
- // Respect rate limits
270
- if (i + CONCURRENCY < items.length) {
271
- await new Promise((resolve) => setTimeout(resolve, 1000));
272
- }
273
- }
274
- job.status = results.every((r) => r.status === 'completed') ? 'completed' : 'failed';
275
- job.completedAt = new Date();
276
- return results;
277
- }
278
- async function processBedrockItem(item, config, model) {
279
- // Check if using AI Gateway
280
- if (config.gatewayUrl && config.gatewayToken) {
281
- return processBedrockItemViaGateway(item, config, model);
282
- }
283
- const url = `https://bedrock-runtime.${config.region}.amazonaws.com/model/${encodeURIComponent(model)}/invoke`;
284
- // Build the request body based on the model type
285
- let body;
286
- if (model.includes('anthropic')) {
287
- // Anthropic models on Bedrock
288
- body = {
289
- anthropic_version: 'bedrock-2023-05-31',
290
- max_tokens: item.options?.maxTokens || 4096,
291
- messages: [{ role: 'user', content: item.prompt }],
292
- system: item.options?.system,
293
- temperature: item.options?.temperature,
294
- };
295
- }
296
- else if (model.includes('amazon')) {
297
- // Amazon Titan models
298
- body = {
299
- inputText: item.prompt,
300
- textGenerationConfig: {
301
- maxTokenCount: item.options?.maxTokens || 4096,
302
- temperature: item.options?.temperature || 0.7,
303
- },
304
- };
305
- }
306
- else if (model.includes('meta')) {
307
- // Meta Llama models
308
- body = {
309
- prompt: item.prompt,
310
- max_gen_len: item.options?.maxTokens || 4096,
311
- temperature: item.options?.temperature || 0.7,
312
- };
313
- }
314
- else if (model.includes('mistral')) {
315
- // Mistral models
316
- body = {
317
- prompt: `<s>[INST] ${item.prompt} [/INST]`,
318
- max_tokens: item.options?.maxTokens || 4096,
319
- temperature: item.options?.temperature || 0.7,
320
- };
321
- }
322
- else {
323
- // Generic format (Claude-style)
324
- body = {
325
- anthropic_version: 'bedrock-2023-05-31',
326
- max_tokens: item.options?.maxTokens || 4096,
327
- messages: [{ role: 'user', content: item.prompt }],
328
- temperature: item.options?.temperature,
329
- };
330
- }
331
- const bodyStr = JSON.stringify(body);
332
- const headers = await signRequest('POST', url, bodyStr, config, 'bedrock');
333
- const response = await fetch(url, {
334
- method: 'POST',
335
- headers,
336
- body: bodyStr,
337
- });
338
- if (!response.ok) {
339
- const error = await response.text();
340
- throw new Error(`Bedrock API error: ${response.status} ${error}`);
341
- }
342
- const data = await response.json();
343
- // Extract content based on model response format
344
- let content;
345
- let usage;
346
- if (data.content) {
347
- // Anthropic format
348
- const textContent = data.content.find((c) => c.type === 'text');
349
- content = textContent?.text;
350
- if (data.usage) {
351
- usage = {
352
- promptTokens: data.usage.input_tokens,
353
- completionTokens: data.usage.output_tokens,
354
- totalTokens: data.usage.input_tokens + data.usage.output_tokens,
355
- };
356
- }
357
- }
358
- else if (data.results?.[0]) {
359
- // Titan format
360
- content = data.results[0].outputText;
361
- usage = {
362
- promptTokens: 0, // Titan doesn't return this
363
- completionTokens: data.results[0].tokenCount || 0,
364
- totalTokens: data.results[0].tokenCount || 0,
365
- };
366
- }
367
- else if (data.generation) {
368
- // Llama/Mistral format
369
- content = data.generation;
370
- if (data.generation_token_count !== undefined) {
371
- usage = {
372
- promptTokens: data.prompt_token_count || 0,
373
- completionTokens: data.generation_token_count,
374
- totalTokens: (data.prompt_token_count || 0) + data.generation_token_count,
375
- };
376
- }
377
- }
378
- let result = content;
379
- // Try to parse JSON if schema was provided
380
- if (item.schema && content) {
381
- try {
382
- result = JSON.parse(content);
383
- }
384
- catch {
385
- // Keep as string
386
- }
387
- }
388
- return {
389
- id: item.id,
390
- customId: item.id,
391
- status: 'completed',
392
- result,
393
- usage,
394
- };
395
- }
396
- /**
397
- * Process a Bedrock item via Cloudflare AI Gateway
398
- *
399
- * NOTE: Unlike OpenAI and Google, Bedrock via AI Gateway still requires AWS Signature V4 signing.
400
- * The gateway routes the request but doesn't handle authentication.
401
- * @see https://developers.cloudflare.com/ai-gateway/usage/providers/bedrock/
402
- *
403
- * Gateway URL format: {gateway_url}/aws-bedrock/bedrock-runtime/{region}/model/{model}/invoke
404
- */
405
- async function processBedrockItemViaGateway(item, config, model) {
406
- // AI Gateway URL for Bedrock - requires full path including region
407
- // Format: {gateway_url}/aws-bedrock/bedrock-runtime/{region}/model/{model}/invoke
408
- const url = `${config.gatewayUrl}/aws-bedrock/bedrock-runtime/${config.region}/model/${encodeURIComponent(model)}/invoke`;
409
- // Build the request body (Anthropic format for Claude models)
410
- const body = {
411
- anthropic_version: 'bedrock-2023-05-31',
412
- max_tokens: item.options?.maxTokens || 4096,
413
- messages: [{ role: 'user', content: item.prompt }],
414
- system: item.options?.system,
415
- temperature: item.options?.temperature,
416
- };
417
- const bodyStr = JSON.stringify(body);
418
- // NOTE: Bedrock via Gateway still requires AWS SigV4 signing
419
- // We need both the gateway token AND AWS credentials
420
- if (!config.accessKeyId || !config.secretAccessKey) {
421
- throw new Error('Bedrock via AI Gateway still requires AWS credentials for SigV4 signing. ' +
422
- 'Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.');
423
- }
424
- const headers = await signRequest('POST', url, bodyStr, config, 'bedrock');
425
- headers.set('cf-aig-authorization', `Bearer ${config.gatewayToken}`);
426
- const response = await fetch(url, {
427
- method: 'POST',
428
- headers,
429
- body: bodyStr,
430
- });
431
- if (!response.ok) {
432
- const error = await response.text();
433
- throw new Error(`Bedrock via Gateway error: ${response.status} ${error}`);
434
- }
435
- const data = await response.json();
436
- // Extract content (Anthropic format)
437
- const textContent = data.content?.find((c) => c.type === 'text');
438
- let content = textContent?.text;
439
- let usage;
440
- if (data.usage) {
441
- usage = {
442
- promptTokens: data.usage.input_tokens,
443
- completionTokens: data.usage.output_tokens,
444
- totalTokens: data.usage.input_tokens + data.usage.output_tokens,
445
- };
446
- }
447
- let result = content;
448
- // Try to parse JSON if schema was provided
449
- if (item.schema && content) {
450
- try {
451
- result = JSON.parse(content);
452
- }
453
- catch {
454
- // Keep as string
455
- }
456
- }
457
- return {
458
- id: item.id,
459
- customId: item.id,
460
- status: 'completed',
461
- result,
462
- usage,
463
- };
464
- }
465
- // ============================================================================
466
- // True Batch Inference (S3-based)
467
- // ============================================================================
468
- /**
469
- * Create and submit a true Bedrock batch inference job
470
- * This requires S3 bucket access and proper IAM setup
471
- */
472
- export async function createBedrockBatchJob(items, model, options) {
473
- const config = getConfig();
474
- // Build JSONL content
475
- const jsonlLines = items.map((item) => {
476
- const request = {
477
- recordId: item.id,
478
- modelInput: {
479
- anthropic_version: 'bedrock-2023-05-31',
480
- max_tokens: item.options?.maxTokens || 4096,
481
- messages: [{ role: 'user', content: item.prompt }],
482
- system: item.options?.system,
483
- temperature: item.options?.temperature,
484
- },
485
- };
486
- return JSON.stringify(request);
487
- });
488
- const inputKey = `${options.s3InputPrefix || 'bedrock-batch/input'}/${options.jobName}.jsonl`;
489
- const outputPrefix = `${options.s3OutputPrefix || 'bedrock-batch/output'}/${options.jobName}/`;
490
- // Upload to S3
491
- // In production, use @aws-sdk/client-s3
492
- const s3Url = `https://${config.bucket}.s3.${config.region}.amazonaws.com/${inputKey}`;
493
- const content = jsonlLines.join('\n');
494
- const s3Response = await fetch(s3Url, {
495
- method: 'PUT',
496
- headers: await signRequest('PUT', s3Url, content, config, 's3'),
497
- body: content,
498
- });
499
- if (!s3Response.ok) {
500
- throw new Error(`Failed to upload to S3: ${s3Response.status}`);
501
- }
502
- // Create batch inference job
503
- const jobUrl = `https://bedrock.${config.region}.amazonaws.com/model-invocation-job`;
504
- const jobBody = JSON.stringify({
505
- jobName: options.jobName,
506
- modelId: model,
507
- roleArn: options.roleArn,
508
- inputDataConfig: {
509
- s3InputDataConfig: {
510
- s3Uri: `s3://${config.bucket}/${inputKey}`,
511
- },
512
- },
513
- outputDataConfig: {
514
- s3OutputDataConfig: {
515
- s3Uri: `s3://${config.bucket}/${outputPrefix}`,
516
- },
517
- },
518
- });
519
- const jobResponse = await fetch(jobUrl, {
520
- method: 'POST',
521
- headers: await signRequest('POST', jobUrl, jobBody, config, 'bedrock'),
522
- body: jobBody,
523
- });
524
- if (!jobResponse.ok) {
525
- const error = await jobResponse.text();
526
- throw new Error(`Failed to create Bedrock batch job: ${jobResponse.status} ${error}`);
527
- }
528
- const jobData = await jobResponse.json();
529
- return jobData;
530
- }
531
- // ============================================================================
532
- // Register Adapter
533
- // ============================================================================
534
- // ============================================================================
535
- // Bedrock Flex Adapter
536
- // ============================================================================
537
- /**
538
- * AWS Bedrock Flex Adapter
539
- *
540
- * Flex processing uses concurrent requests for medium-sized batches (5-500 items).
541
- * This provides a balance between:
542
- * - Immediate execution (fast but full price, <5 items)
543
- * - Full batch inference (50% discount but 24hr turnaround, 500+ items)
544
- *
545
- * Flex tier uses concurrent API calls with rate limiting, providing results
546
- * in minutes rather than hours while still benefiting from efficient processing.
547
- */
548
- const bedrockFlexAdapter = {
549
- async submitFlex(items, options) {
550
- const config = getConfig();
551
- const model = options.model || 'anthropic.claude-3-sonnet-20240229-v1:0';
552
- const CONCURRENCY = 8; // Bedrock has stricter rate limits than OpenAI
553
- const results = [];
554
- // Process items concurrently with rate limiting
555
- for (let i = 0; i < items.length; i += CONCURRENCY) {
556
- const batch = items.slice(i, i + CONCURRENCY);
557
- const batchResults = await Promise.all(batch.map(async (item) => {
558
- try {
559
- return await processBedrockItem(item, config, model);
560
- }
561
- catch (error) {
562
- return {
563
- id: item.id,
564
- customId: item.id,
565
- status: 'failed',
566
- error: error instanceof Error ? error.message : 'Unknown error',
567
- };
568
- }
569
- }));
570
- results.push(...batchResults);
571
- // Add delay between batches to respect rate limits
572
- if (i + CONCURRENCY < items.length) {
573
- await new Promise((resolve) => setTimeout(resolve, 500));
574
- }
575
- }
576
- return results;
577
- },
578
- };
579
- // ============================================================================
580
- // Register Adapters
581
- // ============================================================================
582
- registerBatchAdapter('bedrock', bedrockAdapter);
583
- registerFlexAdapter('bedrock', bedrockFlexAdapter);
584
- export { bedrockAdapter, bedrockFlexAdapter };