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