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.
Files changed (400) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-test.log +105 -0
  3. package/README.md +190 -86
  4. package/TODO.md +138 -0
  5. package/dist/ai-promise.d.ts +219 -0
  6. package/dist/ai-promise.d.ts.map +1 -0
  7. package/dist/ai-promise.js +610 -0
  8. package/dist/ai-promise.js.map +1 -0
  9. package/dist/ai.d.ts +285 -0
  10. package/dist/ai.d.ts.map +1 -0
  11. package/dist/ai.js +842 -0
  12. package/dist/ai.js.map +1 -0
  13. package/dist/batch/anthropic.d.ts +23 -0
  14. package/dist/batch/anthropic.d.ts.map +1 -0
  15. package/dist/batch/anthropic.js +257 -0
  16. package/dist/batch/anthropic.js.map +1 -0
  17. package/dist/batch/bedrock.d.ts +64 -0
  18. package/dist/batch/bedrock.d.ts.map +1 -0
  19. package/dist/batch/bedrock.js +586 -0
  20. package/dist/batch/bedrock.js.map +1 -0
  21. package/dist/batch/cloudflare.d.ts +37 -0
  22. package/dist/batch/cloudflare.d.ts.map +1 -0
  23. package/dist/batch/cloudflare.js +289 -0
  24. package/dist/batch/cloudflare.js.map +1 -0
  25. package/dist/batch/google.d.ts +41 -0
  26. package/dist/batch/google.d.ts.map +1 -0
  27. package/dist/batch/google.js +360 -0
  28. package/dist/batch/google.js.map +1 -0
  29. package/dist/batch/index.d.ts +31 -0
  30. package/dist/batch/index.d.ts.map +1 -0
  31. package/dist/batch/index.js +31 -0
  32. package/dist/batch/index.js.map +1 -0
  33. package/dist/batch/memory.d.ts +44 -0
  34. package/dist/batch/memory.d.ts.map +1 -0
  35. package/dist/batch/memory.js +188 -0
  36. package/dist/batch/memory.js.map +1 -0
  37. package/dist/batch/openai.d.ts +37 -0
  38. package/dist/batch/openai.d.ts.map +1 -0
  39. package/dist/batch/openai.js +403 -0
  40. package/dist/batch/openai.js.map +1 -0
  41. package/dist/batch-map.d.ts +125 -0
  42. package/dist/batch-map.d.ts.map +1 -0
  43. package/dist/batch-map.js +406 -0
  44. package/dist/batch-map.js.map +1 -0
  45. package/dist/batch-queue.d.ts +273 -0
  46. package/dist/batch-queue.d.ts.map +1 -0
  47. package/dist/batch-queue.js +271 -0
  48. package/dist/batch-queue.js.map +1 -0
  49. package/dist/context.d.ts +133 -0
  50. package/dist/context.d.ts.map +1 -0
  51. package/dist/context.js +267 -0
  52. package/dist/context.js.map +1 -0
  53. package/dist/embeddings.d.ts +123 -0
  54. package/dist/embeddings.d.ts.map +1 -0
  55. package/dist/embeddings.js +170 -0
  56. package/dist/embeddings.js.map +1 -0
  57. package/dist/eval/index.d.ts +8 -0
  58. package/dist/eval/index.d.ts.map +1 -0
  59. package/dist/eval/index.js +8 -0
  60. package/dist/eval/index.js.map +1 -0
  61. package/dist/eval/models.d.ts +66 -0
  62. package/dist/eval/models.d.ts.map +1 -0
  63. package/dist/eval/models.js +120 -0
  64. package/dist/eval/models.js.map +1 -0
  65. package/dist/eval/runner.d.ts +64 -0
  66. package/dist/eval/runner.d.ts.map +1 -0
  67. package/dist/eval/runner.js +148 -0
  68. package/dist/eval/runner.js.map +1 -0
  69. package/dist/generate.d.ts +168 -0
  70. package/dist/generate.d.ts.map +1 -0
  71. package/dist/generate.js +174 -0
  72. package/dist/generate.js.map +1 -0
  73. package/dist/index.d.ts +29 -4
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +53 -52
  76. package/dist/index.js.map +1 -1
  77. package/dist/primitives.d.ts +292 -0
  78. package/dist/primitives.d.ts.map +1 -0
  79. package/dist/primitives.js +471 -0
  80. package/dist/primitives.js.map +1 -0
  81. package/dist/providers/cloudflare.d.ts +9 -0
  82. package/dist/providers/cloudflare.d.ts.map +1 -0
  83. package/dist/providers/cloudflare.js +9 -0
  84. package/dist/providers/cloudflare.js.map +1 -0
  85. package/dist/providers/index.d.ts +9 -0
  86. package/dist/providers/index.d.ts.map +1 -0
  87. package/dist/providers/index.js +9 -0
  88. package/dist/providers/index.js.map +1 -0
  89. package/dist/schema.d.ts +54 -0
  90. package/dist/schema.d.ts.map +1 -0
  91. package/dist/schema.js +109 -0
  92. package/dist/schema.js.map +1 -0
  93. package/dist/template.d.ts +73 -0
  94. package/dist/template.d.ts.map +1 -0
  95. package/dist/template.js +129 -0
  96. package/dist/template.js.map +1 -0
  97. package/dist/types.d.ts +474 -106
  98. package/dist/types.d.ts.map +1 -1
  99. package/dist/types.js +4 -8
  100. package/dist/types.js.map +1 -1
  101. package/evalite.config.ts +19 -0
  102. package/evals/README.md +212 -0
  103. package/evals/classification.eval.ts +108 -0
  104. package/evals/marketing.eval.ts +370 -0
  105. package/evals/math.eval.ts +94 -0
  106. package/evals/run-evals.ts +166 -0
  107. package/evals/structured-output.eval.ts +143 -0
  108. package/evals/writing.eval.ts +117 -0
  109. package/examples/batch-blog-posts.ts +160 -0
  110. package/package.json +57 -57
  111. package/src/ai-promise.ts +784 -0
  112. package/src/ai.ts +1183 -0
  113. package/src/batch/anthropic.ts +375 -0
  114. package/src/batch/bedrock.ts +801 -0
  115. package/src/batch/cloudflare.ts +421 -0
  116. package/src/batch/google.ts +491 -0
  117. package/src/batch/index.ts +31 -0
  118. package/src/batch/memory.ts +253 -0
  119. package/src/batch/openai.ts +557 -0
  120. package/src/batch-map.ts +534 -0
  121. package/src/batch-queue.ts +493 -0
  122. package/src/context.ts +332 -0
  123. package/src/embeddings.ts +244 -0
  124. package/src/eval/index.ts +8 -0
  125. package/src/eval/models.ts +158 -0
  126. package/src/eval/runner.ts +217 -0
  127. package/src/generate.ts +245 -0
  128. package/src/index.ts +154 -0
  129. package/src/primitives.ts +612 -0
  130. package/src/providers/cloudflare.ts +15 -0
  131. package/src/providers/index.ts +14 -0
  132. package/src/schema.ts +147 -0
  133. package/src/template.ts +209 -0
  134. package/src/types.ts +540 -0
  135. package/test/README.md +105 -0
  136. package/test/ai-proxy.test.ts +192 -0
  137. package/test/async-iterators.test.ts +327 -0
  138. package/test/batch-background.test.ts +482 -0
  139. package/test/batch-blog-posts.test.ts +387 -0
  140. package/test/blog-generation.test.ts +510 -0
  141. package/test/browse-read.test.ts +611 -0
  142. package/test/core-functions.test.ts +694 -0
  143. package/test/decide.test.ts +393 -0
  144. package/test/define.test.ts +274 -0
  145. package/test/e2e-bedrock-manual.ts +163 -0
  146. package/test/e2e-bedrock.test.ts +191 -0
  147. package/test/e2e-flex-gateway.ts +157 -0
  148. package/test/e2e-flex-manual.ts +183 -0
  149. package/test/e2e-flex.test.ts +209 -0
  150. package/test/e2e-google-manual.ts +178 -0
  151. package/test/e2e-google.test.ts +216 -0
  152. package/test/embeddings.test.ts +284 -0
  153. package/test/evals/define-function.eval.test.ts +379 -0
  154. package/test/evals/primitives.eval.test.ts +384 -0
  155. package/test/function-types.test.ts +492 -0
  156. package/test/generate-core.test.ts +319 -0
  157. package/test/generate.test.ts +163 -0
  158. package/test/implicit-batch.test.ts +422 -0
  159. package/test/schema.test.ts +109 -0
  160. package/test/tagged-templates.test.ts +302 -0
  161. package/tsconfig.json +10 -0
  162. package/vitest.config.ts +42 -0
  163. package/LICENSE +0 -21
  164. package/bin/cli.js +0 -5
  165. package/dist/cli/index.d.ts +0 -10
  166. package/dist/cli/index.d.ts.map +0 -1
  167. package/dist/cli/index.js +0 -38
  168. package/dist/cli/index.js.map +0 -1
  169. package/dist/cli/index.test.d.ts +0 -2
  170. package/dist/cli/index.test.d.ts.map +0 -1
  171. package/dist/cli/index.test.js +0 -35
  172. package/dist/cli/index.test.js.map +0 -1
  173. package/dist/constants/models.d.ts +0 -10
  174. package/dist/constants/models.d.ts.map +0 -1
  175. package/dist/constants/models.js +0 -12
  176. package/dist/constants/models.js.map +0 -1
  177. package/dist/converters/index.d.ts +0 -3
  178. package/dist/converters/index.d.ts.map +0 -1
  179. package/dist/converters/index.js +0 -3
  180. package/dist/converters/index.js.map +0 -1
  181. package/dist/converters/model.d.ts +0 -4
  182. package/dist/converters/model.d.ts.map +0 -1
  183. package/dist/converters/model.js +0 -19
  184. package/dist/converters/model.js.map +0 -1
  185. package/dist/converters/schema.d.ts +0 -4
  186. package/dist/converters/schema.d.ts.map +0 -1
  187. package/dist/converters/schema.js +0 -25
  188. package/dist/converters/schema.js.map +0 -1
  189. package/dist/core/responses.d.ts +0 -5
  190. package/dist/core/responses.d.ts.map +0 -1
  191. package/dist/core/responses.js +0 -16
  192. package/dist/core/responses.js.map +0 -1
  193. package/dist/core/responses.test.d.ts +0 -2
  194. package/dist/core/responses.test.d.ts.map +0 -1
  195. package/dist/core/responses.test.js +0 -31
  196. package/dist/core/responses.test.js.map +0 -1
  197. package/dist/errors.d.ts +0 -6
  198. package/dist/errors.d.ts.map +0 -1
  199. package/dist/errors.js +0 -9
  200. package/dist/errors.js.map +0 -1
  201. package/dist/examples/streaming.test.d.ts +0 -2
  202. package/dist/examples/streaming.test.d.ts.map +0 -1
  203. package/dist/examples/streaming.test.js +0 -176
  204. package/dist/examples/streaming.test.js.map +0 -1
  205. package/dist/factory/__tests__/index.test.d.ts +0 -2
  206. package/dist/factory/__tests__/index.test.d.ts.map +0 -1
  207. package/dist/factory/__tests__/index.test.js +0 -430
  208. package/dist/factory/__tests__/index.test.js.map +0 -1
  209. package/dist/factory/__tests__/list.test.d.ts +0 -2
  210. package/dist/factory/__tests__/list.test.d.ts.map +0 -1
  211. package/dist/factory/__tests__/list.test.js +0 -92
  212. package/dist/factory/__tests__/list.test.js.map +0 -1
  213. package/dist/factory/index.d.ts +0 -20
  214. package/dist/factory/index.d.ts.map +0 -1
  215. package/dist/factory/index.js +0 -287
  216. package/dist/factory/index.js.map +0 -1
  217. package/dist/factory/index.test.d.ts +0 -2
  218. package/dist/factory/index.test.d.ts.map +0 -1
  219. package/dist/factory/index.test.js +0 -287
  220. package/dist/factory/index.test.js.map +0 -1
  221. package/dist/factory/list.d.ts +0 -3
  222. package/dist/factory/list.d.ts.map +0 -1
  223. package/dist/factory/list.js +0 -221
  224. package/dist/factory/list.js.map +0 -1
  225. package/dist/factory/list.test.d.ts +0 -2
  226. package/dist/factory/list.test.d.ts.map +0 -1
  227. package/dist/factory/list.test.js +0 -84
  228. package/dist/factory/list.test.js.map +0 -1
  229. package/dist/generate/index.d.ts +0 -5
  230. package/dist/generate/index.d.ts.map +0 -1
  231. package/dist/generate/index.js +0 -17
  232. package/dist/generate/index.js.map +0 -1
  233. package/dist/index.test.d.ts +0 -2
  234. package/dist/index.test.d.ts.map +0 -1
  235. package/dist/index.test.js +0 -59
  236. package/dist/index.test.js.map +0 -1
  237. package/dist/list/await.d.ts +0 -3
  238. package/dist/list/await.d.ts.map +0 -1
  239. package/dist/list/await.js +0 -28
  240. package/dist/list/await.js.map +0 -1
  241. package/dist/list/constants.d.ts +0 -4
  242. package/dist/list/constants.d.ts.map +0 -1
  243. package/dist/list/constants.js +0 -5
  244. package/dist/list/constants.js.map +0 -1
  245. package/dist/list/create-function.d.ts +0 -3
  246. package/dist/list/create-function.d.ts.map +0 -1
  247. package/dist/list/create-function.js +0 -11
  248. package/dist/list/create-function.js.map +0 -1
  249. package/dist/list/index.d.ts +0 -4
  250. package/dist/list/index.d.ts.map +0 -1
  251. package/dist/list/index.js +0 -5
  252. package/dist/list/index.js.map +0 -1
  253. package/dist/list/prompt.d.ts +0 -3
  254. package/dist/list/prompt.d.ts.map +0 -1
  255. package/dist/list/prompt.js +0 -6
  256. package/dist/list/prompt.js.map +0 -1
  257. package/dist/list/schemas.d.ts +0 -4
  258. package/dist/list/schemas.d.ts.map +0 -1
  259. package/dist/list/schemas.js +0 -8
  260. package/dist/list/schemas.js.map +0 -1
  261. package/dist/list/stream.d.ts +0 -3
  262. package/dist/list/stream.d.ts.map +0 -1
  263. package/dist/list/stream.js +0 -33
  264. package/dist/list/stream.js.map +0 -1
  265. package/dist/list/types.d.ts +0 -11
  266. package/dist/list/types.d.ts.map +0 -1
  267. package/dist/list/types.js +0 -2
  268. package/dist/list/types.js.map +0 -1
  269. package/dist/list/validation.d.ts +0 -3
  270. package/dist/list/validation.d.ts.map +0 -1
  271. package/dist/list/validation.js +0 -12
  272. package/dist/list/validation.js.map +0 -1
  273. package/dist/providers/config.d.ts +0 -4
  274. package/dist/providers/config.d.ts.map +0 -1
  275. package/dist/providers/config.js +0 -21
  276. package/dist/providers/config.js.map +0 -1
  277. package/dist/providers/config.test.d.ts +0 -2
  278. package/dist/providers/config.test.d.ts.map +0 -1
  279. package/dist/providers/config.test.js +0 -37
  280. package/dist/providers/config.test.js.map +0 -1
  281. package/dist/proxy/constants.d.ts +0 -4
  282. package/dist/proxy/constants.d.ts.map +0 -1
  283. package/dist/proxy/constants.js +0 -5
  284. package/dist/proxy/constants.js.map +0 -1
  285. package/dist/proxy/create-function.d.ts +0 -4
  286. package/dist/proxy/create-function.d.ts.map +0 -1
  287. package/dist/proxy/create-function.js +0 -24
  288. package/dist/proxy/create-function.js.map +0 -1
  289. package/dist/proxy/create-proxy.d.ts +0 -2
  290. package/dist/proxy/create-proxy.d.ts.map +0 -1
  291. package/dist/proxy/create-proxy.js +0 -11
  292. package/dist/proxy/create-proxy.js.map +0 -1
  293. package/dist/proxy/function-generator.d.ts +0 -9
  294. package/dist/proxy/function-generator.d.ts.map +0 -1
  295. package/dist/proxy/function-generator.js +0 -29
  296. package/dist/proxy/function-generator.js.map +0 -1
  297. package/dist/proxy/index.d.ts +0 -4
  298. package/dist/proxy/index.d.ts.map +0 -1
  299. package/dist/proxy/index.js +0 -4
  300. package/dist/proxy/index.js.map +0 -1
  301. package/dist/proxy/prompt.d.ts +0 -2
  302. package/dist/proxy/prompt.d.ts.map +0 -1
  303. package/dist/proxy/prompt.js +0 -6
  304. package/dist/proxy/prompt.js.map +0 -1
  305. package/dist/proxy/types.d.ts +0 -7
  306. package/dist/proxy/types.d.ts.map +0 -1
  307. package/dist/proxy/types.js +0 -2
  308. package/dist/proxy/types.js.map +0 -1
  309. package/dist/queue/manager.d.ts +0 -5
  310. package/dist/queue/manager.d.ts.map +0 -1
  311. package/dist/queue/manager.js +0 -37
  312. package/dist/queue/manager.js.map +0 -1
  313. package/dist/queue/manager.test.d.ts +0 -2
  314. package/dist/queue/manager.test.d.ts.map +0 -1
  315. package/dist/queue/manager.test.js +0 -52
  316. package/dist/queue/manager.test.js.map +0 -1
  317. package/dist/schema-converter.d.ts +0 -4
  318. package/dist/schema-converter.d.ts.map +0 -1
  319. package/dist/schema-converter.js +0 -30
  320. package/dist/schema-converter.js.map +0 -1
  321. package/dist/stream/index.d.ts +0 -7
  322. package/dist/stream/index.d.ts.map +0 -1
  323. package/dist/stream/index.js +0 -23
  324. package/dist/stream/index.js.map +0 -1
  325. package/dist/streaming/utils.d.ts +0 -4
  326. package/dist/streaming/utils.d.ts.map +0 -1
  327. package/dist/streaming/utils.js +0 -131
  328. package/dist/streaming/utils.js.map +0 -1
  329. package/dist/streaming/utils.test.d.ts +0 -2
  330. package/dist/streaming/utils.test.d.ts.map +0 -1
  331. package/dist/streaming/utils.test.js +0 -84
  332. package/dist/streaming/utils.test.js.map +0 -1
  333. package/dist/templates/result.d.ts +0 -7
  334. package/dist/templates/result.d.ts.map +0 -1
  335. package/dist/templates/result.js +0 -40
  336. package/dist/templates/result.js.map +0 -1
  337. package/dist/templates/result.test.d.ts +0 -2
  338. package/dist/templates/result.test.d.ts.map +0 -1
  339. package/dist/templates/result.test.js +0 -75
  340. package/dist/templates/result.test.js.map +0 -1
  341. package/dist/test/setup.d.ts +0 -2
  342. package/dist/test/setup.d.ts.map +0 -1
  343. package/dist/test/setup.js +0 -21
  344. package/dist/test/setup.js.map +0 -1
  345. package/dist/test-types.d.ts +0 -13
  346. package/dist/test-types.d.ts.map +0 -1
  347. package/dist/test-types.js +0 -55
  348. package/dist/test-types.js.map +0 -1
  349. package/dist/types/index.d.ts +0 -4
  350. package/dist/types/index.d.ts.map +0 -1
  351. package/dist/types/index.js +0 -4
  352. package/dist/types/index.js.map +0 -1
  353. package/dist/types/list.d.ts +0 -10
  354. package/dist/types/list.d.ts.map +0 -1
  355. package/dist/types/list.js +0 -2
  356. package/dist/types/list.js.map +0 -1
  357. package/dist/types/model.d.ts +0 -7
  358. package/dist/types/model.d.ts.map +0 -1
  359. package/dist/types/model.js +0 -2
  360. package/dist/types/model.js.map +0 -1
  361. package/dist/types/options.d.ts +0 -25
  362. package/dist/types/options.d.ts.map +0 -1
  363. package/dist/types/options.js +0 -2
  364. package/dist/types/options.js.map +0 -1
  365. package/dist/types/schema.d.ts +0 -5
  366. package/dist/types/schema.d.ts.map +0 -1
  367. package/dist/types/schema.js +0 -2
  368. package/dist/types/schema.js.map +0 -1
  369. package/dist/utils/__tests__/request-handler.test.d.ts +0 -2
  370. package/dist/utils/__tests__/request-handler.test.d.ts.map +0 -1
  371. package/dist/utils/__tests__/request-handler.test.js +0 -134
  372. package/dist/utils/__tests__/request-handler.test.js.map +0 -1
  373. package/dist/utils/__tests__/schema.test.d.ts +0 -2
  374. package/dist/utils/__tests__/schema.test.d.ts.map +0 -1
  375. package/dist/utils/__tests__/schema.test.js +0 -49
  376. package/dist/utils/__tests__/schema.test.js.map +0 -1
  377. package/dist/utils/__tests__/stream-progress.test.d.ts +0 -2
  378. package/dist/utils/__tests__/stream-progress.test.d.ts.map +0 -1
  379. package/dist/utils/__tests__/stream-progress.test.js +0 -85
  380. package/dist/utils/__tests__/stream-progress.test.js.map +0 -1
  381. package/dist/utils/index.d.ts +0 -2
  382. package/dist/utils/index.d.ts.map +0 -1
  383. package/dist/utils/index.js +0 -2
  384. package/dist/utils/index.js.map +0 -1
  385. package/dist/utils/request-handler.d.ts +0 -17
  386. package/dist/utils/request-handler.d.ts.map +0 -1
  387. package/dist/utils/request-handler.js +0 -105
  388. package/dist/utils/request-handler.js.map +0 -1
  389. package/dist/utils/schema.d.ts +0 -11
  390. package/dist/utils/schema.d.ts.map +0 -1
  391. package/dist/utils/schema.js +0 -51
  392. package/dist/utils/schema.js.map +0 -1
  393. package/dist/utils/stream-progress.d.ts +0 -17
  394. package/dist/utils/stream-progress.d.ts.map +0 -1
  395. package/dist/utils/stream-progress.js +0 -86
  396. package/dist/utils/stream-progress.js.map +0 -1
  397. package/dist/utils/validation.d.ts +0 -3
  398. package/dist/utils/validation.d.ts.map +0 -1
  399. package/dist/utils/validation.js +0 -30
  400. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Manual E2E Test for AWS Bedrock Flex Tier Processing
4
+ *
5
+ * Run with your AWS credentials:
6
+ *
7
+ * AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... npx tsx test/e2e-bedrock-manual.ts
8
+ *
9
+ * Or if using AWS SSO/profiles:
10
+ *
11
+ * AWS_PROFILE=your-profile npx tsx test/e2e-bedrock-manual.ts
12
+ */
13
+
14
+ import { configure, resetContext, getExecutionTier, isFlexAvailable, getProvider } from '../src/context.js'
15
+ import { type BatchItem } from '../src/batch-queue.js'
16
+ import { configureAWSBedrock, bedrockFlexAdapter } from '../src/batch/bedrock.js'
17
+
18
+ // Model IDs on Bedrock
19
+ const CLAUDE_OPUS_MODEL = 'anthropic.claude-opus-4-20250514-v1:0'
20
+ const CLAUDE_SONNET_MODEL = 'anthropic.claude-sonnet-4-20250514-v1:0'
21
+
22
+ async function main() {
23
+ console.log('\n🧪 E2E AWS Bedrock Flex Tier Test\n')
24
+
25
+ // Check for AWS credentials
26
+ if (!process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE) {
27
+ console.error('āŒ AWS credentials required')
28
+ console.error(' Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY')
29
+ console.error(' Or use AWS_PROFILE for SSO/credential profiles')
30
+ process.exit(1)
31
+ }
32
+
33
+ console.log('āœ… AWS credentials found')
34
+ console.log(` Region: ${process.env.AWS_REGION || 'us-east-1'}`)
35
+
36
+ // Configure Bedrock
37
+ configureAWSBedrock({
38
+ region: process.env.AWS_REGION || 'us-east-1',
39
+ s3Bucket: 'dummy-bucket', // Flex mode doesn't use S3
40
+ })
41
+
42
+ configure({
43
+ provider: 'bedrock',
44
+ model: CLAUDE_SONNET_MODEL,
45
+ batchMode: 'auto',
46
+ flexThreshold: 3,
47
+ batchThreshold: 500,
48
+ })
49
+
50
+ console.log(`āœ… Configured: provider=${getProvider()}, flexAvailable=${isFlexAvailable()}`)
51
+
52
+ // Test 1: Basic flex processing with Claude Sonnet
53
+ console.log('\nšŸ“ Test 1: Basic Flex Processing (Claude Sonnet)')
54
+ console.log(' Creating 3 simple prompts...')
55
+
56
+ const items: BatchItem[] = [
57
+ { id: 'test_1', prompt: 'What is 2 + 2? Reply with just the number.', status: 'pending' },
58
+ { id: 'test_2', prompt: 'What is 3 + 3? Reply with just the number.', status: 'pending' },
59
+ { id: 'test_3', prompt: 'What is 4 + 4? Reply with just the number.', status: 'pending' },
60
+ ]
61
+
62
+ console.log(' Submitting via flex adapter...')
63
+ const startTime = Date.now()
64
+
65
+ try {
66
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_SONNET_MODEL })
67
+ const duration = Date.now() - startTime
68
+
69
+ console.log(` āœ… Completed in ${duration}ms`)
70
+ console.log(' Results:')
71
+
72
+ results.forEach((result) => {
73
+ const status = result.status === 'completed' ? 'āœ…' : 'āŒ'
74
+ console.log(` ${status} ${result.id}: "${result.result}"`)
75
+ if (result.error) {
76
+ console.log(` Error: ${result.error}`)
77
+ }
78
+ })
79
+ } catch (error) {
80
+ console.error(` āŒ Error: ${error}`)
81
+ process.exit(1)
82
+ }
83
+
84
+ // Test 2: Token usage
85
+ console.log('\nšŸ“ Test 2: Token Usage Tracking')
86
+
87
+ const usageItems: BatchItem[] = [
88
+ { id: 'usage', prompt: 'Count from 1 to 5', status: 'pending' },
89
+ ]
90
+
91
+ try {
92
+ const results = await bedrockFlexAdapter.submitFlex(usageItems, { model: CLAUDE_SONNET_MODEL })
93
+ const usage = results[0].usage
94
+
95
+ if (usage) {
96
+ console.log(` Prompt tokens: ${usage.promptTokens}`)
97
+ console.log(` Completion tokens: ${usage.completionTokens}`)
98
+ console.log(` Total tokens: ${usage.totalTokens}`)
99
+ console.log(' āœ… Token usage tracking works')
100
+ } else {
101
+ console.log(' āš ļø No usage data returned')
102
+ }
103
+ } catch (error) {
104
+ console.error(` āŒ Error: ${error}`)
105
+ }
106
+
107
+ // Test 3: Try Claude Opus 4.5 (may not be available)
108
+ console.log('\nšŸ“ Test 3: Claude Opus 4.5 (optional)')
109
+
110
+ const opusItems: BatchItem[] = [
111
+ { id: 'opus', prompt: 'Write a haiku about AI.', status: 'pending' },
112
+ ]
113
+
114
+ try {
115
+ const results = await bedrockFlexAdapter.submitFlex(opusItems, { model: CLAUDE_OPUS_MODEL })
116
+ console.log(` āœ… Claude Opus 4.5 response:`)
117
+ console.log(` ${results[0].result}`)
118
+ } catch (error) {
119
+ console.log(` āš ļø Claude Opus 4.5 not available: ${error}`)
120
+ console.log(' (This is expected if Opus is not enabled in your AWS account)')
121
+ }
122
+
123
+ // Test 4: Concurrent processing
124
+ console.log('\nšŸ“ Test 4: Concurrent Processing (5 items)')
125
+
126
+ const concurrentItems: BatchItem[] = Array.from({ length: 5 }, (_, i) => ({
127
+ id: `concurrent_${i}`,
128
+ prompt: `What is ${i + 1} * 2? Reply with just the number.`,
129
+ status: 'pending' as const,
130
+ }))
131
+
132
+ try {
133
+ const startTime = Date.now()
134
+ const results = await bedrockFlexAdapter.submitFlex(concurrentItems, { model: CLAUDE_SONNET_MODEL })
135
+ const duration = Date.now() - startTime
136
+
137
+ const successCount = results.filter((r) => r.status === 'completed').length
138
+ console.log(` āœ… Processed ${successCount}/5 items in ${duration}ms`)
139
+
140
+ results.forEach((result) => {
141
+ console.log(` ${result.id}: "${result.result}"`)
142
+ })
143
+ } catch (error) {
144
+ console.error(` āŒ Error: ${error}`)
145
+ }
146
+
147
+ // Test 5: Tier selection
148
+ console.log('\nšŸ“ Test 5: Execution Tier Selection')
149
+
150
+ console.log(` 1 item → ${getExecutionTier(1)} (expected: immediate)`)
151
+ console.log(` 3 items → ${getExecutionTier(3)} (expected: flex)`)
152
+ console.log(` 10 items → ${getExecutionTier(10)} (expected: flex)`)
153
+ console.log(` 500 items → ${getExecutionTier(500)} (expected: batch)`)
154
+
155
+ console.log('\n✨ E2E Tests Complete!\n')
156
+
157
+ resetContext()
158
+ }
159
+
160
+ main().catch((error) => {
161
+ console.error('Fatal error:', error)
162
+ process.exit(1)
163
+ })
@@ -0,0 +1,191 @@
1
+ /**
2
+ * E2E Tests for AWS Bedrock Flex Tier Processing
3
+ *
4
+ * These tests hit real AWS Bedrock APIs to verify the flex tier actually works.
5
+ * Requires AWS credentials:
6
+ * - AWS_ACCESS_KEY_ID
7
+ * - AWS_SECRET_ACCESS_KEY
8
+ * - AWS_REGION (optional, defaults to us-east-1)
9
+ *
10
+ * Run with: npx vitest run test/e2e-bedrock.test.ts
11
+ */
12
+
13
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
14
+ import { configure, resetContext, getExecutionTier, isFlexAvailable } from '../src/context.js'
15
+ import { getFlexAdapter, type BatchItem } from '../src/batch-queue.js'
16
+
17
+ // Import the Bedrock adapter to register it
18
+ import { configureAWSBedrock, bedrockFlexAdapter } from '../src/batch/bedrock.js'
19
+ import '../src/batch/bedrock.js'
20
+
21
+ // Skip tests if no AWS credentials
22
+ // NOTE: Unlike OpenAI/Google, Bedrock via AI Gateway still requires AWS SigV4 signing
23
+ // Gateway alone is NOT sufficient - you need AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
24
+ const hasCredentials = !!(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY)
25
+
26
+ // Claude Opus 4.5 model ID on Bedrock
27
+ const CLAUDE_OPUS_MODEL = 'anthropic.claude-opus-4-20250514-v1:0'
28
+ // Fallback to Sonnet if Opus isn't available
29
+ const CLAUDE_SONNET_MODEL = 'anthropic.claude-sonnet-4-20250514-v1:0'
30
+
31
+ describe.skipIf(!hasCredentials)('E2E Bedrock Flex Tier Processing', () => {
32
+ beforeAll(() => {
33
+ // Configure Bedrock - can use either direct AWS credentials or AI Gateway
34
+ configureAWSBedrock({
35
+ region: process.env.AWS_REGION || 'us-east-1',
36
+ s3Bucket: process.env.BEDROCK_BATCH_S3_BUCKET || 'dummy-bucket',
37
+ // AI Gateway configuration (optional)
38
+ gatewayUrl: process.env.AI_GATEWAY_URL,
39
+ gatewayToken: process.env.AI_GATEWAY_TOKEN,
40
+ })
41
+
42
+ configure({
43
+ provider: 'bedrock',
44
+ model: CLAUDE_SONNET_MODEL, // Use Sonnet by default (more available)
45
+ batchMode: 'auto',
46
+ flexThreshold: 3,
47
+ batchThreshold: 500,
48
+ })
49
+ })
50
+
51
+ afterAll(() => {
52
+ resetContext()
53
+ })
54
+
55
+ describe('Flex Adapter Direct', () => {
56
+ it('processes a small batch via flex adapter', async () => {
57
+ const items: BatchItem[] = [
58
+ {
59
+ id: 'item_1',
60
+ prompt: 'What is 2 + 2? Reply with just the number.',
61
+ status: 'pending',
62
+ },
63
+ {
64
+ id: 'item_2',
65
+ prompt: 'What is 3 + 3? Reply with just the number.',
66
+ status: 'pending',
67
+ },
68
+ {
69
+ id: 'item_3',
70
+ prompt: 'What is 4 + 4? Reply with just the number.',
71
+ status: 'pending',
72
+ },
73
+ ]
74
+
75
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_SONNET_MODEL })
76
+
77
+ expect(results).toHaveLength(3)
78
+ results.forEach((result) => {
79
+ expect(result.status).toBe('completed')
80
+ expect(result.result).toBeDefined()
81
+ })
82
+
83
+ // Check the actual responses contain numbers
84
+ const result1 = results.find((r) => r.id === 'item_1')
85
+ const result2 = results.find((r) => r.id === 'item_2')
86
+ const result3 = results.find((r) => r.id === 'item_3')
87
+
88
+ expect(String(result1?.result)).toContain('4')
89
+ expect(String(result2?.result)).toContain('6')
90
+ expect(String(result3?.result)).toContain('8')
91
+ }, 60000) // 60 second timeout for Bedrock
92
+
93
+ it('reports token usage', async () => {
94
+ const items: BatchItem[] = [
95
+ {
96
+ id: 'usage_test',
97
+ prompt: 'Count from 1 to 5',
98
+ status: 'pending',
99
+ },
100
+ ]
101
+
102
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_SONNET_MODEL })
103
+
104
+ expect(results[0].usage).toBeDefined()
105
+ expect(results[0].usage?.promptTokens).toBeGreaterThan(0)
106
+ expect(results[0].usage?.completionTokens).toBeGreaterThan(0)
107
+ expect(results[0].usage?.totalTokens).toBeGreaterThan(0)
108
+ }, 60000)
109
+
110
+ it('handles system prompts', async () => {
111
+ const items: BatchItem[] = [
112
+ {
113
+ id: 'system_test',
114
+ prompt: 'What should I say?',
115
+ options: {
116
+ system: 'You are a helpful assistant that always responds with exactly "Hello World"',
117
+ },
118
+ status: 'pending',
119
+ },
120
+ ]
121
+
122
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_SONNET_MODEL })
123
+
124
+ expect(results[0].status).toBe('completed')
125
+ expect(String(results[0].result).toLowerCase()).toContain('hello')
126
+ }, 60000)
127
+ })
128
+
129
+ describe('Execution Tier Selection', () => {
130
+ it('selects immediate tier for < flexThreshold items', () => {
131
+ configure({ batchMode: 'auto', flexThreshold: 5, batchThreshold: 500 })
132
+ expect(getExecutionTier(1)).toBe('immediate')
133
+ expect(getExecutionTier(4)).toBe('immediate')
134
+ })
135
+
136
+ it('selects flex tier for flexThreshold to < batchThreshold items', () => {
137
+ configure({ batchMode: 'auto', flexThreshold: 5, batchThreshold: 500 })
138
+ expect(getExecutionTier(5)).toBe('flex')
139
+ expect(getExecutionTier(100)).toBe('flex')
140
+ })
141
+
142
+ it('reports flex as available for bedrock', () => {
143
+ configure({ provider: 'bedrock' })
144
+ expect(isFlexAvailable()).toBe(true)
145
+ })
146
+ })
147
+
148
+ describe('Concurrent Processing', () => {
149
+ it('processes multiple items concurrently', async () => {
150
+ // Create 5 items to test concurrent processing
151
+ const items: BatchItem[] = Array.from({ length: 5 }, (_, i) => ({
152
+ id: `concurrent_${i}`,
153
+ prompt: `What is ${i} + 1? Reply with just the number.`,
154
+ status: 'pending' as const,
155
+ }))
156
+
157
+ const startTime = Date.now()
158
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_SONNET_MODEL })
159
+ const duration = Date.now() - startTime
160
+
161
+ expect(results).toHaveLength(5)
162
+ results.forEach((result) => {
163
+ expect(result.status).toBe('completed')
164
+ })
165
+
166
+ console.log(`Processed 5 items in ${duration}ms`)
167
+ }, 120000)
168
+ })
169
+ })
170
+
171
+ // Optional test with Claude Opus 4.5 (may not be available in all regions)
172
+ describe.skipIf(!hasCredentials)('E2E Bedrock with Claude Opus 4.5', () => {
173
+ it.skip('processes with Claude Opus 4.5', async () => {
174
+ const items: BatchItem[] = [
175
+ {
176
+ id: 'opus_test',
177
+ prompt: 'Write a haiku about AI.',
178
+ status: 'pending',
179
+ },
180
+ ]
181
+
182
+ try {
183
+ const results = await bedrockFlexAdapter.submitFlex(items, { model: CLAUDE_OPUS_MODEL })
184
+ expect(results[0].status).toBe('completed')
185
+ expect(results[0].result).toBeDefined()
186
+ console.log('Claude Opus 4.5 response:', results[0].result)
187
+ } catch (error) {
188
+ console.log('Claude Opus 4.5 not available in this region:', error)
189
+ }
190
+ }, 120000)
191
+ })
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * E2E Test for Flex Tier via Cloudflare AI Gateway
4
+ *
5
+ * This test uses the Cloudflare AI Gateway to route requests to OpenAI,
6
+ * using the AI_GATEWAY_TOKEN and AI_GATEWAY_URL environment variables.
7
+ *
8
+ * Run with:
9
+ * npx tsx test/e2e-flex-gateway.ts
10
+ */
11
+
12
+ import { configure, resetContext, getExecutionTier, isFlexAvailable, getProvider } from '../src/context.js'
13
+ import { getFlexAdapter, type BatchItem } from '../src/batch-queue.js'
14
+ import { configureOpenAI, openaiFlexAdapter } from '../src/batch/openai.js'
15
+
16
+ async function main() {
17
+ console.log('\n🧪 E2E Flex Tier Test (via Cloudflare AI Gateway)\n')
18
+
19
+ // Check for gateway config
20
+ const gatewayUrl = process.env.AI_GATEWAY_URL
21
+ const gatewayToken = process.env.AI_GATEWAY_TOKEN
22
+
23
+ if (!gatewayUrl || !gatewayToken) {
24
+ console.error('āŒ AI_GATEWAY_URL and AI_GATEWAY_TOKEN environment variables are required')
25
+ console.error(' These should be in .env at the repo root')
26
+ process.exit(1)
27
+ }
28
+
29
+ console.log(`āœ… Gateway URL: ${gatewayUrl}`)
30
+ console.log('āœ… Gateway Token found')
31
+
32
+ // Configure OpenAI adapter to use the gateway
33
+ // The gateway URL format is: https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}
34
+ // We need to append /openai to route to OpenAI
35
+ const openaiGatewayUrl = `${gatewayUrl}/openai`
36
+ console.log(` Using base URL: ${openaiGatewayUrl}`)
37
+
38
+ configureOpenAI({
39
+ apiKey: gatewayToken,
40
+ baseUrl: openaiGatewayUrl,
41
+ })
42
+
43
+ // Configure context
44
+ configure({
45
+ provider: 'openai',
46
+ model: 'gpt-4o-mini',
47
+ batchMode: 'auto',
48
+ flexThreshold: 3,
49
+ batchThreshold: 500,
50
+ })
51
+
52
+ console.log(`āœ… Configured: provider=${getProvider()}, flexAvailable=${isFlexAvailable()}`)
53
+
54
+ // Test 1: Basic flex processing
55
+ console.log('\nšŸ“ Test 1: Basic Flex Processing')
56
+ console.log(' Creating 3 simple prompts...')
57
+
58
+ const items: BatchItem[] = [
59
+ { id: 'test_1', prompt: 'Say "hello" and nothing else.', status: 'pending' },
60
+ { id: 'test_2', prompt: 'Say "world" and nothing else.', status: 'pending' },
61
+ { id: 'test_3', prompt: 'Say "test" and nothing else.', status: 'pending' },
62
+ ]
63
+
64
+ console.log(' Submitting via flex adapter...')
65
+ const startTime = Date.now()
66
+
67
+ try {
68
+ const results = await openaiFlexAdapter.submitFlex(items, { model: 'gpt-4o-mini' })
69
+ const duration = Date.now() - startTime
70
+
71
+ console.log(` āœ… Completed in ${duration}ms`)
72
+ console.log(' Results:')
73
+
74
+ results.forEach((result) => {
75
+ const status = result.status === 'completed' ? 'āœ…' : 'āŒ'
76
+ console.log(` ${status} ${result.id}: "${result.result}"`)
77
+ if (result.error) {
78
+ console.log(` Error: ${result.error}`)
79
+ }
80
+ })
81
+
82
+ const successCount = results.filter((r) => r.status === 'completed').length
83
+ if (successCount === results.length) {
84
+ console.log(` āœ… All ${successCount} items completed successfully`)
85
+ } else {
86
+ console.log(` āš ļø ${successCount}/${results.length} items completed`)
87
+ }
88
+ } catch (error) {
89
+ console.error(` āŒ Error: ${error}`)
90
+ process.exit(1)
91
+ }
92
+
93
+ // Test 2: Token usage
94
+ console.log('\nšŸ“ Test 2: Token Usage Tracking')
95
+
96
+ const usageItems: BatchItem[] = [
97
+ { id: 'usage', prompt: 'Count from 1 to 5', status: 'pending' },
98
+ ]
99
+
100
+ try {
101
+ const results = await openaiFlexAdapter.submitFlex(usageItems, { model: 'gpt-4o-mini' })
102
+ const usage = results[0].usage
103
+
104
+ if (usage) {
105
+ console.log(` Prompt tokens: ${usage.promptTokens}`)
106
+ console.log(` Completion tokens: ${usage.completionTokens}`)
107
+ console.log(` Total tokens: ${usage.totalTokens}`)
108
+ console.log(' āœ… Token usage tracking works')
109
+ } else {
110
+ console.log(' āš ļø No usage data returned')
111
+ }
112
+ } catch (error) {
113
+ console.error(` āŒ Error: ${error}`)
114
+ }
115
+
116
+ // Test 3: Concurrent processing
117
+ console.log('\nšŸ“ Test 3: Concurrent Processing (5 items)')
118
+
119
+ const concurrentItems: BatchItem[] = Array.from({ length: 5 }, (_, i) => ({
120
+ id: `concurrent_${i}`,
121
+ prompt: `What is ${i + 1} + ${i + 1}? Reply with just the number.`,
122
+ status: 'pending' as const,
123
+ }))
124
+
125
+ try {
126
+ const startTime = Date.now()
127
+ const results = await openaiFlexAdapter.submitFlex(concurrentItems, { model: 'gpt-4o-mini' })
128
+ const duration = Date.now() - startTime
129
+
130
+ const successCount = results.filter((r) => r.status === 'completed').length
131
+ console.log(` āœ… Processed ${successCount}/5 items in ${duration}ms`)
132
+ console.log(` Average: ${Math.round(duration / 5)}ms per item`)
133
+
134
+ results.forEach((result) => {
135
+ console.log(` ${result.id}: "${result.result}"`)
136
+ })
137
+ } catch (error) {
138
+ console.error(` āŒ Error: ${error}`)
139
+ }
140
+
141
+ // Test 4: Tier selection
142
+ console.log('\nšŸ“ Test 4: Execution Tier Selection')
143
+
144
+ console.log(` 1 item → ${getExecutionTier(1)} (expected: immediate)`)
145
+ console.log(` 3 items → ${getExecutionTier(3)} (expected: flex)`)
146
+ console.log(` 10 items → ${getExecutionTier(10)} (expected: flex)`)
147
+ console.log(` 500 items → ${getExecutionTier(500)} (expected: batch)`)
148
+
149
+ console.log('\n✨ E2E Tests Complete!\n')
150
+
151
+ resetContext()
152
+ }
153
+
154
+ main().catch((error) => {
155
+ console.error('Fatal error:', error)
156
+ process.exit(1)
157
+ })
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Manual E2E Test for Flex Tier Processing
4
+ *
5
+ * Run this script manually with your OpenAI API key:
6
+ *
7
+ * OPENAI_API_KEY=sk-... npx tsx test/e2e-flex-manual.ts
8
+ *
9
+ * Or if you have the key in your environment:
10
+ *
11
+ * npx tsx test/e2e-flex-manual.ts
12
+ */
13
+
14
+ import { configure, resetContext, getExecutionTier, isFlexAvailable, getProvider } from '../src/context.js'
15
+ import { getFlexAdapter, type BatchItem } from '../src/batch-queue.js'
16
+
17
+ // Import the OpenAI adapter to register it
18
+ import '../src/batch/openai.js'
19
+
20
+ async function main() {
21
+ console.log('\n🧪 E2E Flex Tier Test\n')
22
+
23
+ // Check for API key
24
+ if (!process.env.OPENAI_API_KEY) {
25
+ console.error('āŒ OPENAI_API_KEY environment variable is required')
26
+ console.error(' Run with: OPENAI_API_KEY=sk-... npx tsx test/e2e-flex-manual.ts')
27
+ process.exit(1)
28
+ }
29
+
30
+ console.log('āœ… OpenAI API key found')
31
+
32
+ // Configure
33
+ configure({
34
+ provider: 'openai',
35
+ model: 'gpt-4o-mini',
36
+ batchMode: 'auto',
37
+ flexThreshold: 3,
38
+ batchThreshold: 500,
39
+ })
40
+
41
+ console.log(`āœ… Configured: provider=${getProvider()}, flexAvailable=${isFlexAvailable()}`)
42
+
43
+ // Test 1: Basic flex processing
44
+ console.log('\nšŸ“ Test 1: Basic Flex Processing')
45
+ console.log(' Creating 5 simple math prompts...')
46
+
47
+ const adapter = getFlexAdapter('openai')
48
+
49
+ const items: BatchItem[] = [
50
+ { id: 'math_1', prompt: 'What is 2 + 2? Reply with just the number.', status: 'pending' },
51
+ { id: 'math_2', prompt: 'What is 3 + 3? Reply with just the number.', status: 'pending' },
52
+ { id: 'math_3', prompt: 'What is 4 + 4? Reply with just the number.', status: 'pending' },
53
+ { id: 'math_4', prompt: 'What is 5 + 5? Reply with just the number.', status: 'pending' },
54
+ { id: 'math_5', prompt: 'What is 6 + 6? Reply with just the number.', status: 'pending' },
55
+ ]
56
+
57
+ console.log(' Submitting via flex adapter...')
58
+ const startTime = Date.now()
59
+
60
+ try {
61
+ const results = await adapter.submitFlex(items, { model: 'gpt-4o-mini' })
62
+ const duration = Date.now() - startTime
63
+
64
+ console.log(` āœ… Completed in ${duration}ms`)
65
+ console.log(' Results:')
66
+
67
+ let allPassed = true
68
+ const expected = ['4', '6', '8', '10', '12']
69
+
70
+ results.forEach((result, i) => {
71
+ const passed = result.status === 'completed' && result.result?.toString().includes(expected[i])
72
+ const icon = passed ? 'āœ…' : 'āŒ'
73
+ console.log(` ${icon} ${result.id}: "${result.result}" (expected: ${expected[i]})`)
74
+ if (!passed) allPassed = false
75
+ })
76
+
77
+ if (!allPassed) {
78
+ console.log('\nāš ļø Some results did not match expected values')
79
+ }
80
+ } catch (error) {
81
+ console.error(` āŒ Error: ${error}`)
82
+ process.exit(1)
83
+ }
84
+
85
+ // Test 2: Structured output
86
+ console.log('\nšŸ“ Test 2: Structured Output (JSON Schema)')
87
+
88
+ const structuredItems: BatchItem[] = [
89
+ {
90
+ id: 'person_1',
91
+ prompt: 'Generate a JSON object with name "Alice" and age 30. Format: {"name": "...", "age": ...}',
92
+ schema: { name: 'string', age: 'number' },
93
+ status: 'pending',
94
+ },
95
+ {
96
+ id: 'person_2',
97
+ prompt: 'Generate a JSON object with name "Bob" and age 25. Format: {"name": "...", "age": ...}',
98
+ schema: { name: 'string', age: 'number' },
99
+ status: 'pending',
100
+ },
101
+ ]
102
+
103
+ try {
104
+ const results = await adapter.submitFlex(structuredItems, { model: 'gpt-4o-mini' })
105
+
106
+ console.log(' Results:')
107
+ results.forEach((result) => {
108
+ const obj = result.result as { name?: string; age?: number }
109
+ console.log(` ${result.id}: name="${obj?.name}", age=${obj?.age}`)
110
+ })
111
+ console.log(' āœ… Structured output works')
112
+ } catch (error) {
113
+ console.error(` āŒ Error: ${error}`)
114
+ }
115
+
116
+ // Test 3: Token usage
117
+ console.log('\nšŸ“ Test 3: Token Usage Tracking')
118
+
119
+ const usageItems: BatchItem[] = [
120
+ { id: 'usage', prompt: 'Count from 1 to 10', status: 'pending' },
121
+ ]
122
+
123
+ try {
124
+ const results = await adapter.submitFlex(usageItems, { model: 'gpt-4o-mini' })
125
+ const usage = results[0].usage
126
+
127
+ if (usage) {
128
+ console.log(` Prompt tokens: ${usage.promptTokens}`)
129
+ console.log(` Completion tokens: ${usage.completionTokens}`)
130
+ console.log(` Total tokens: ${usage.totalTokens}`)
131
+ console.log(' āœ… Token usage tracking works')
132
+ } else {
133
+ console.log(' āš ļø No usage data returned')
134
+ }
135
+ } catch (error) {
136
+ console.error(` āŒ Error: ${error}`)
137
+ }
138
+
139
+ // Test 4: Concurrent processing performance
140
+ console.log('\nšŸ“ Test 4: Concurrent Processing Performance')
141
+ console.log(' Creating 10 items to test concurrency...')
142
+
143
+ const concurrentItems: BatchItem[] = Array.from({ length: 10 }, (_, i) => ({
144
+ id: `concurrent_${i}`,
145
+ prompt: `What is ${i + 1} * 2? Reply with just the number.`,
146
+ status: 'pending' as const,
147
+ }))
148
+
149
+ try {
150
+ const startTime = Date.now()
151
+ const results = await adapter.submitFlex(concurrentItems, { model: 'gpt-4o-mini' })
152
+ const duration = Date.now() - startTime
153
+
154
+ const successCount = results.filter((r) => r.status === 'completed').length
155
+ console.log(` āœ… Processed ${successCount}/10 items in ${duration}ms`)
156
+ console.log(` Average: ${Math.round(duration / 10)}ms per item`)
157
+
158
+ if (duration < 10000) {
159
+ console.log(' āœ… Concurrent processing is working (< 10s for 10 items)')
160
+ } else {
161
+ console.log(' āš ļø Slower than expected, may not be fully concurrent')
162
+ }
163
+ } catch (error) {
164
+ console.error(` āŒ Error: ${error}`)
165
+ }
166
+
167
+ // Test 5: Tier selection
168
+ console.log('\nšŸ“ Test 5: Execution Tier Selection')
169
+
170
+ console.log(` 1 item → ${getExecutionTier(1)} (expected: immediate)`)
171
+ console.log(` 3 item → ${getExecutionTier(3)} (expected: flex, threshold=3)`)
172
+ console.log(` 10 items → ${getExecutionTier(10)} (expected: flex)`)
173
+ console.log(` 500 items → ${getExecutionTier(500)} (expected: batch)`)
174
+
175
+ console.log('\n✨ E2E Tests Complete!\n')
176
+
177
+ resetContext()
178
+ }
179
+
180
+ main().catch((error) => {
181
+ console.error('Fatal error:', error)
182
+ process.exit(1)
183
+ })