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,482 @@
1
+ /**
2
+ * Tests for batch and background processing modes
3
+ *
4
+ * Batch mode: fn.batch([inputs]) - Process many inputs at ~50% discount
5
+ * Background mode: fn(..., { mode: 'background' }) - Returns job ID immediately
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
9
+
10
+ // ============================================================================
11
+ // Mock implementations
12
+ // ============================================================================
13
+
14
+ const mockBatchProcess = vi.fn()
15
+ const mockBackgroundProcess = vi.fn()
16
+
17
+ /**
18
+ * Create a mock function with batch support
19
+ */
20
+ function createMockFunctionWithBatch<T>(
21
+ defaultHandler: (prompt: string) => Promise<T>
22
+ ) {
23
+ const fn = async (prompt: string, options?: Record<string, unknown>) => {
24
+ if (options?.mode === 'background') {
25
+ return mockBackgroundProcess(prompt, options)
26
+ }
27
+ return defaultHandler(prompt)
28
+ }
29
+
30
+ // Add batch method
31
+ fn.batch = async (inputs: Array<string | Record<string, unknown>>) => {
32
+ return mockBatchProcess(inputs)
33
+ }
34
+
35
+ return fn
36
+ }
37
+
38
+ /**
39
+ * Create a tagged template function with batch support
40
+ */
41
+ function createMockTemplateFunctionWithBatch<T>(
42
+ defaultHandler: (prompt: string) => Promise<T>
43
+ ) {
44
+ function fn(promptOrStrings: string | TemplateStringsArray, ...args: unknown[]) {
45
+ let prompt: string
46
+
47
+ if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
48
+ prompt = (promptOrStrings as TemplateStringsArray).reduce((acc, str, i) => {
49
+ return acc + str + (args[i] ?? '')
50
+ }, '')
51
+ } else {
52
+ prompt = promptOrStrings as string
53
+ }
54
+
55
+ return defaultHandler(prompt)
56
+ }
57
+
58
+ // Add batch method
59
+ fn.batch = async (inputs: Array<string | Record<string, unknown>>) => {
60
+ return mockBatchProcess(inputs)
61
+ }
62
+
63
+ return fn
64
+ }
65
+
66
+ // ============================================================================
67
+ // Batch mode tests
68
+ // ============================================================================
69
+
70
+ describe('batch mode', () => {
71
+ beforeEach(() => {
72
+ mockBatchProcess.mockReset()
73
+ })
74
+
75
+ describe('write.batch()', () => {
76
+ it('processes multiple prompts in batch', async () => {
77
+ const write = createMockFunctionWithBatch(async () => 'Generated content')
78
+
79
+ const prompts = [
80
+ 'blog post about TypeScript',
81
+ 'blog post about React',
82
+ 'blog post about Next.js',
83
+ ]
84
+
85
+ mockBatchProcess.mockResolvedValue([
86
+ 'TypeScript content...',
87
+ 'React content...',
88
+ 'Next.js content...',
89
+ ])
90
+
91
+ const results = await write.batch(prompts)
92
+
93
+ expect(mockBatchProcess).toHaveBeenCalledWith(prompts)
94
+ expect(results).toHaveLength(3)
95
+ })
96
+
97
+ it('processes object inputs with context', async () => {
98
+ const write = createMockFunctionWithBatch(async () => 'Generated content')
99
+
100
+ const brand = { voice: 'professional', audience: 'developers' }
101
+ const titles = ['Getting Started', 'Advanced Patterns', 'Best Practices']
102
+
103
+ const inputs = titles.map(title => ({
104
+ title,
105
+ brand,
106
+ tone: 'technical',
107
+ }))
108
+
109
+ mockBatchProcess.mockResolvedValue([
110
+ 'Getting Started content...',
111
+ 'Advanced Patterns content...',
112
+ 'Best Practices content...',
113
+ ])
114
+
115
+ const results = await write.batch(inputs)
116
+
117
+ expect(mockBatchProcess).toHaveBeenCalledWith(inputs)
118
+ expect(results).toHaveLength(3)
119
+ })
120
+
121
+ it('returns results in same order as inputs', async () => {
122
+ const write = createMockFunctionWithBatch(async () => 'content')
123
+
124
+ const inputs = ['first', 'second', 'third']
125
+
126
+ mockBatchProcess.mockResolvedValue([
127
+ 'Result for first',
128
+ 'Result for second',
129
+ 'Result for third',
130
+ ])
131
+
132
+ const results = await write.batch(inputs)
133
+
134
+ expect(results[0]).toContain('first')
135
+ expect(results[1]).toContain('second')
136
+ expect(results[2]).toContain('third')
137
+ })
138
+ })
139
+
140
+ describe('list.batch()', () => {
141
+ it('generates multiple lists in batch', async () => {
142
+ const list = createMockFunctionWithBatch(async () => ['item'])
143
+
144
+ mockBatchProcess.mockResolvedValue([
145
+ ['TypeScript tip 1', 'TypeScript tip 2'],
146
+ ['React tip 1', 'React tip 2'],
147
+ ['Next.js tip 1', 'Next.js tip 2'],
148
+ ])
149
+
150
+ const results = await list.batch([
151
+ '3 TypeScript tips',
152
+ '3 React tips',
153
+ '3 Next.js tips',
154
+ ])
155
+
156
+ expect(results).toHaveLength(3)
157
+ expect(results[0]).toEqual(['TypeScript tip 1', 'TypeScript tip 2'])
158
+ })
159
+ })
160
+
161
+ describe('code.batch()', () => {
162
+ it('generates multiple code snippets in batch', async () => {
163
+ const code = createMockFunctionWithBatch(async () => 'code')
164
+
165
+ mockBatchProcess.mockResolvedValue([
166
+ 'function validateEmail(email) { ... }',
167
+ 'function validatePhone(phone) { ... }',
168
+ 'function validateUrl(url) { ... }',
169
+ ])
170
+
171
+ const results = await code.batch([
172
+ { description: 'email validator', language: 'typescript' },
173
+ { description: 'phone validator', language: 'typescript' },
174
+ { description: 'url validator', language: 'typescript' },
175
+ ])
176
+
177
+ expect(results).toHaveLength(3)
178
+ expect(results[0]).toContain('validateEmail')
179
+ })
180
+ })
181
+
182
+ describe('batch with options', () => {
183
+ it('accepts batch-level options', async () => {
184
+ const write = createMockFunctionWithBatch(async () => 'content')
185
+
186
+ // Simulating batch with options
187
+ const mockBatchWithOptions = vi.fn().mockResolvedValue(['r1', 'r2'])
188
+
189
+ const inputs = ['prompt1', 'prompt2']
190
+ const options = { model: 'claude-opus-4-5' }
191
+
192
+ await mockBatchWithOptions(inputs, options)
193
+
194
+ expect(mockBatchWithOptions).toHaveBeenCalledWith(inputs, options)
195
+ })
196
+
197
+ it('supports priority option for urgent batches', async () => {
198
+ const mockBatchWithPriority = vi.fn().mockResolvedValue(['result'])
199
+
200
+ await mockBatchWithPriority(['prompt'], { priority: 'high' })
201
+
202
+ expect(mockBatchWithPriority).toHaveBeenCalledWith(
203
+ ['prompt'],
204
+ expect.objectContaining({ priority: 'high' })
205
+ )
206
+ })
207
+ })
208
+ })
209
+
210
+ // ============================================================================
211
+ // Background mode tests
212
+ // ============================================================================
213
+
214
+ describe('background mode', () => {
215
+ beforeEach(() => {
216
+ mockBackgroundProcess.mockReset()
217
+ })
218
+
219
+ it('returns job ID immediately', async () => {
220
+ const write = createMockFunctionWithBatch(async () => 'content')
221
+
222
+ mockBackgroundProcess.mockResolvedValue({
223
+ jobId: 'job_abc123',
224
+ status: 'pending',
225
+ })
226
+
227
+ const job = await write('long form article', { mode: 'background' })
228
+
229
+ expect(mockBackgroundProcess).toHaveBeenCalledWith(
230
+ 'long form article',
231
+ expect.objectContaining({ mode: 'background' })
232
+ )
233
+ expect(job).toHaveProperty('jobId')
234
+ expect(job.status).toBe('pending')
235
+ })
236
+
237
+ it('can check job status', async () => {
238
+ const mockGetJobStatus = vi.fn()
239
+
240
+ // Simulating job status check
241
+ mockGetJobStatus.mockResolvedValueOnce({ status: 'processing' })
242
+ mockGetJobStatus.mockResolvedValueOnce({ status: 'completed', result: 'Generated content' })
243
+
244
+ const status1 = await mockGetJobStatus('job_abc123')
245
+ expect(status1.status).toBe('processing')
246
+
247
+ const status2 = await mockGetJobStatus('job_abc123')
248
+ expect(status2.status).toBe('completed')
249
+ expect(status2.result).toBe('Generated content')
250
+ })
251
+
252
+ it('supports webhook callback', async () => {
253
+ const write = createMockFunctionWithBatch(async () => 'content')
254
+
255
+ mockBackgroundProcess.mockResolvedValue({
256
+ jobId: 'job_xyz789',
257
+ status: 'pending',
258
+ })
259
+
260
+ await write('content', {
261
+ mode: 'background',
262
+ webhook: 'https://myapp.com/webhooks/ai-complete',
263
+ })
264
+
265
+ expect(mockBackgroundProcess).toHaveBeenCalledWith(
266
+ 'content',
267
+ expect.objectContaining({
268
+ mode: 'background',
269
+ webhook: 'https://myapp.com/webhooks/ai-complete',
270
+ })
271
+ )
272
+ })
273
+
274
+ it('supports polling for result', async () => {
275
+ // Simulating a poll function
276
+ const mockPollForResult = vi.fn()
277
+
278
+ mockPollForResult.mockImplementation(async (jobId: string) => {
279
+ // Simulate polling - would normally check periodically
280
+ return { status: 'completed', result: 'Final result' }
281
+ })
282
+
283
+ const result = await mockPollForResult('job_abc123')
284
+
285
+ expect(result.status).toBe('completed')
286
+ expect(result.result).toBe('Final result')
287
+ })
288
+ })
289
+
290
+ // ============================================================================
291
+ // Combined batch and background
292
+ // ============================================================================
293
+
294
+ describe('batch + background mode', () => {
295
+ it('can run batch in background', async () => {
296
+ const mockBatchBackground = vi.fn()
297
+
298
+ mockBatchBackground.mockResolvedValue({
299
+ jobId: 'batch_job_123',
300
+ status: 'pending',
301
+ inputCount: 100,
302
+ })
303
+
304
+ // Large batch job in background
305
+ const job = await mockBatchBackground(
306
+ Array(100).fill('Generate content'),
307
+ { mode: 'background' }
308
+ )
309
+
310
+ expect(job.jobId).toBe('batch_job_123')
311
+ expect(job.inputCount).toBe(100)
312
+ })
313
+
314
+ it('tracks progress of background batch', async () => {
315
+ const mockBatchProgress = vi.fn()
316
+
317
+ mockBatchProgress
318
+ .mockResolvedValueOnce({ status: 'processing', completed: 10, total: 100 })
319
+ .mockResolvedValueOnce({ status: 'processing', completed: 50, total: 100 })
320
+ .mockResolvedValueOnce({ status: 'completed', completed: 100, total: 100 })
321
+
322
+ const p1 = await mockBatchProgress('batch_job_123')
323
+ expect(p1.completed).toBe(10)
324
+
325
+ const p2 = await mockBatchProgress('batch_job_123')
326
+ expect(p2.completed).toBe(50)
327
+
328
+ const p3 = await mockBatchProgress('batch_job_123')
329
+ expect(p3.status).toBe('completed')
330
+ })
331
+ })
332
+
333
+ // ============================================================================
334
+ // Batch pricing and limits
335
+ // ============================================================================
336
+
337
+ describe('batch characteristics', () => {
338
+ it('batch provides cost savings (documentation test)', () => {
339
+ // This documents expected behavior
340
+ const batchInfo = {
341
+ discount: '50%',
342
+ turnaround: '24 hours max',
343
+ minBatchSize: 1,
344
+ maxBatchSize: 10000,
345
+ }
346
+
347
+ expect(batchInfo.discount).toBe('50%')
348
+ expect(batchInfo.turnaround).toBe('24 hours max')
349
+ })
350
+
351
+ it('batch handles errors gracefully', async () => {
352
+ const mockBatchWithErrors = vi.fn()
353
+
354
+ // Some items succeed, some fail
355
+ mockBatchWithErrors.mockResolvedValue({
356
+ results: [
357
+ { index: 0, status: 'success', result: 'Content 1' },
358
+ { index: 1, status: 'error', error: 'Content policy violation' },
359
+ { index: 2, status: 'success', result: 'Content 3' },
360
+ ],
361
+ summary: { succeeded: 2, failed: 1 },
362
+ })
363
+
364
+ const response = await mockBatchWithErrors(['p1', 'p2', 'p3'])
365
+
366
+ expect(response.summary.succeeded).toBe(2)
367
+ expect(response.summary.failed).toBe(1)
368
+ expect(response.results[1].status).toBe('error')
369
+ })
370
+ })
371
+
372
+ // ============================================================================
373
+ // Use cases from README
374
+ // ============================================================================
375
+
376
+ describe('batch use cases', () => {
377
+ beforeEach(() => {
378
+ mockBatchProcess.mockReset()
379
+ })
380
+
381
+ it('content generation at scale', async () => {
382
+ const write = createMockFunctionWithBatch(async () => 'content')
383
+
384
+ // Generate blog posts for 100 topics
385
+ const topics = Array(100)
386
+ .fill(null)
387
+ .map((_, i) => `Topic ${i + 1}`)
388
+
389
+ const inputs = topics.map(topic => ({
390
+ topic,
391
+ length: 'medium',
392
+ style: 'informative',
393
+ }))
394
+
395
+ mockBatchProcess.mockResolvedValue(
396
+ topics.map(t => `Blog post about ${t}...`)
397
+ )
398
+
399
+ const posts = await write.batch(inputs)
400
+
401
+ expect(posts).toHaveLength(100)
402
+ })
403
+
404
+ it('product description generation', async () => {
405
+ const write = createMockFunctionWithBatch(async () => 'description')
406
+
407
+ const products = [
408
+ { name: 'Widget Pro', category: 'tools', features: ['durable', 'lightweight'] },
409
+ { name: 'Gadget Plus', category: 'electronics', features: ['wireless', 'rechargeable'] },
410
+ ]
411
+
412
+ mockBatchProcess.mockResolvedValue([
413
+ 'Widget Pro is a durable, lightweight tool...',
414
+ 'Gadget Plus is a wireless, rechargeable electronic...',
415
+ ])
416
+
417
+ const descriptions = await write.batch(
418
+ products.map(p => ({
419
+ prompt: `product description for ${p.name}`,
420
+ product: p,
421
+ }))
422
+ )
423
+
424
+ expect(descriptions).toHaveLength(2)
425
+ expect(descriptions[0]).toContain('Widget Pro')
426
+ })
427
+
428
+ it('code generation for multiple functions', async () => {
429
+ const code = createMockFunctionWithBatch(async () => 'code')
430
+
431
+ const functions = [
432
+ { name: 'validateEmail', description: 'Validate email format' },
433
+ { name: 'validatePhone', description: 'Validate phone number' },
434
+ { name: 'validateUrl', description: 'Validate URL format' },
435
+ ]
436
+
437
+ mockBatchProcess.mockResolvedValue(
438
+ functions.map(f => `function ${f.name}(value) { ... }`)
439
+ )
440
+
441
+ const implementations = await code.batch(
442
+ functions.map(f => ({
443
+ description: f.description,
444
+ functionName: f.name,
445
+ language: 'typescript',
446
+ }))
447
+ )
448
+
449
+ expect(implementations).toHaveLength(3)
450
+ functions.forEach((f, i) => {
451
+ expect(implementations[i]).toContain(f.name)
452
+ })
453
+ })
454
+ })
455
+
456
+ // ============================================================================
457
+ // Error handling
458
+ // ============================================================================
459
+
460
+ describe('batch error handling', () => {
461
+ beforeEach(() => {
462
+ mockBatchProcess.mockReset()
463
+ })
464
+
465
+ it('handles empty input array', async () => {
466
+ const write = createMockFunctionWithBatch(async () => 'content')
467
+
468
+ mockBatchProcess.mockResolvedValue([])
469
+
470
+ const results = await write.batch([])
471
+
472
+ expect(results).toEqual([])
473
+ })
474
+
475
+ it('propagates batch-level errors', async () => {
476
+ const write = createMockFunctionWithBatch(async () => 'content')
477
+
478
+ mockBatchProcess.mockRejectedValue(new Error('Batch quota exceeded'))
479
+
480
+ await expect(write.batch(['prompt'])).rejects.toThrow('Batch quota exceeded')
481
+ })
482
+ })