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
package/src/ai.ts ADDED
@@ -0,0 +1,1183 @@
1
+ /**
2
+ * AI() and ai() - Core AI function constructors
3
+ *
4
+ * These provide the main entry points for AI-powered functions,
5
+ * with full RPC promise pipelining support via rpc.do.
6
+ */
7
+
8
+ import { RPC, http, ws, type RPCProxy, type RPCPromise as RpcPromiseType } from 'rpc.do'
9
+
10
+ // Use Promise as RpcPromise for type definitions
11
+ // The actual RPC layer handles pipelining transparently
12
+ type RpcPromise<T> = Promise<T>
13
+
14
+ /**
15
+ * Base class for RPC service targets
16
+ * This is a placeholder for services that want to expose methods over RPC
17
+ */
18
+ export abstract class RpcTarget {
19
+ // Subclasses implement methods that will be exposed over RPC
20
+ }
21
+
22
+ /**
23
+ * Options for RPC session (connection to remote AI service)
24
+ */
25
+ export interface RPCSessionOptions {
26
+ /** WebSocket URL for RPC connection */
27
+ wsUrl?: string
28
+ /** HTTP URL for RPC fallback */
29
+ httpUrl?: string
30
+ /** Authentication token */
31
+ token?: string
32
+ }
33
+ import { generateObject } from './generate.js'
34
+ import type { SimpleSchema } from './schema.js'
35
+ import type { LanguageModel } from 'ai'
36
+ import type {
37
+ AIClient,
38
+ AIFunctionDefinition,
39
+ AIGenerateOptions,
40
+ AIGenerateResult,
41
+ JSONSchema,
42
+ ImageOptions,
43
+ ImageResult,
44
+ VideoOptions,
45
+ VideoResult,
46
+ WriteOptions,
47
+ ListResult,
48
+ ListsResult,
49
+ FunctionDefinition,
50
+ DefinedFunction,
51
+ CodeFunctionDefinition,
52
+ CodeFunctionResult,
53
+ GenerativeFunctionDefinition,
54
+ AgenticFunctionDefinition,
55
+ HumanFunctionDefinition,
56
+ HumanChannel,
57
+ CodeLanguage,
58
+ FunctionRegistry,
59
+ AutoDefineResult
60
+ } from './types.js'
61
+ import { schema as convertSchema, type SimpleSchema as SimpleSchemaType } from './schema.js'
62
+
63
+ /**
64
+ * Options for creating an AI RPC client instance
65
+ */
66
+ export interface AIClientOptions extends RPCSessionOptions {
67
+ /** Default model to use */
68
+ model?: string
69
+ /** Default temperature */
70
+ temperature?: number
71
+ /** Default max tokens */
72
+ maxTokens?: number
73
+ /** Custom functions available to the AI */
74
+ functions?: AIFunctionDefinition[]
75
+ }
76
+
77
+ /**
78
+ * Options for AI schema functions (subset of generateObject options)
79
+ */
80
+ export interface AISchemaOptions {
81
+ /** Model to use (string alias or LanguageModel) */
82
+ model?: string | LanguageModel
83
+ /** System prompt */
84
+ system?: string
85
+ /** Generation mode */
86
+ mode?: 'auto' | 'json' | 'tool'
87
+ /** Temperature (0-2) */
88
+ temperature?: number
89
+ /** Top P sampling */
90
+ topP?: number
91
+ /** Top K sampling */
92
+ topK?: number
93
+ /** Presence penalty */
94
+ presencePenalty?: number
95
+ /** Frequency penalty */
96
+ frequencyPenalty?: number
97
+ /** Max tokens to generate */
98
+ maxTokens?: number
99
+ /** Max retries on failure */
100
+ maxRetries?: number
101
+ /** Abort signal */
102
+ abortSignal?: AbortSignal
103
+ /** Custom headers */
104
+ headers?: Record<string, string>
105
+ }
106
+
107
+ /**
108
+ * Schema-based functions
109
+ */
110
+ type SchemaFunctions<T extends Record<string, SimpleSchema>> = {
111
+ [K in keyof T]: (prompt?: string, options?: AISchemaOptions) => Promise<InferSimpleSchemaResult<T[K]>>
112
+ }
113
+
114
+ /**
115
+ * Create AI functions from schemas
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const ai = AI({
120
+ * storyBrand: {
121
+ * hero: 'Who is the customer?',
122
+ * problem: {
123
+ * internal: 'What internal problem do they face?',
124
+ * external: 'What external challenge exists?',
125
+ * philosophical: 'Why is this wrong?',
126
+ * },
127
+ * guide: 'Who helps them? (the brand)',
128
+ * plan: ['What are the steps to success?'],
129
+ * callToAction: 'What should they do?',
130
+ * success: 'What does success look like?',
131
+ * failure: 'What happens if they fail?',
132
+ * },
133
+ * recipe: {
134
+ * name: 'Recipe name',
135
+ * type: 'food | drink | dessert',
136
+ * servings: 'How many servings? (number)',
137
+ * ingredients: ['List all ingredients'],
138
+ * steps: ['Cooking steps in order'],
139
+ * },
140
+ * })
141
+ *
142
+ * // Call the generated functions
143
+ * const brand = await ai.storyBrand('Acme Corp sells widgets')
144
+ * const dinner = await ai.recipe('Italian pasta for 4 people')
145
+ * ```
146
+ */
147
+ export function AI<T extends Record<string, SimpleSchema>>(
148
+ schemas: T,
149
+ defaultOptions?: AISchemaOptions
150
+ ): SchemaFunctions<T>
151
+
152
+ /**
153
+ * Create an AI RPC client instance
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * // Connect to remote AI service
158
+ * const ai = AI({ wsUrl: 'wss://ai.example.com/rpc' })
159
+ *
160
+ * // Use promise pipelining - single round trip!
161
+ * const result = ai.generate({ prompt: 'Hello' })
162
+ * const upper = result.text.map(t => t.toUpperCase())
163
+ * console.log(await upper)
164
+ *
165
+ * // Dynamic function calling
166
+ * const summary = await ai.summarize({ text: longText })
167
+ * ```
168
+ */
169
+ export function AI(options: AIClientOptions): AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>
170
+
171
+ export function AI<T extends Record<string, SimpleSchema>>(
172
+ schemasOrOptions: T | AIClientOptions,
173
+ defaultOptions?: AISchemaOptions
174
+ ): SchemaFunctions<T> | (AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>) {
175
+ // Check if this is RPC client mode
176
+ if (isAIClientOptions(schemasOrOptions)) {
177
+ const { model, temperature, maxTokens, functions, wsUrl, httpUrl, token } = schemasOrOptions
178
+
179
+ // Create transport based on provided URLs
180
+ let transport
181
+ if (wsUrl) {
182
+ transport = ws(wsUrl, token ? () => token : undefined)
183
+ } else if (httpUrl) {
184
+ transport = http(httpUrl, token ? () => token : undefined)
185
+ } else {
186
+ throw new Error('AI client requires either wsUrl or httpUrl')
187
+ }
188
+
189
+ // Create RPC client
190
+ const rpcClient = RPC<AIClient>(transport)
191
+
192
+ // Create a proxy that handles both defined methods and dynamic function calls
193
+ return new Proxy(rpcClient, {
194
+ get(target, prop: string) {
195
+ // Return existing methods
196
+ if (prop in target) {
197
+ return (target as unknown as Record<string, unknown>)[prop]
198
+ }
199
+
200
+ // Handle dynamic function calls (ai.functionName())
201
+ return (...args: unknown[]) => {
202
+ const client = target as unknown as AIClient
203
+ return client.do(prop, args.length === 1 ? args[0] : args)
204
+ }
205
+ }
206
+ }) as AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>
207
+ }
208
+
209
+ // Schema functions mode - create a function for each schema
210
+ return createSchemaFunctions(schemasOrOptions as Record<string, SimpleSchema>, defaultOptions) as SchemaFunctions<T>
211
+ }
212
+
213
+ /**
214
+ * Check if options are AI client options vs schemas
215
+ */
216
+ function isAIClientOptions(value: unknown): value is AIClientOptions {
217
+ if (typeof value !== 'object' || value === null) return false
218
+ const obj = value as Record<string, unknown>
219
+ return 'wsUrl' in obj || 'httpUrl' in obj || 'functions' in obj
220
+ }
221
+
222
+ /**
223
+ * Infer result type from simple schema
224
+ */
225
+ type InferSimpleSchemaResult<T> = T extends string
226
+ ? string
227
+ : T extends [string]
228
+ ? string[]
229
+ : T extends { [K: string]: SimpleSchema }
230
+ ? { [K in keyof T]: InferSimpleSchemaResult<T[K]> }
231
+ : unknown
232
+
233
+ /**
234
+ * Create schema-based functions from a map of schemas
235
+ */
236
+ function createSchemaFunctions<T extends Record<string, SimpleSchema>>(
237
+ schemas: T,
238
+ defaultOptions: AISchemaOptions = {}
239
+ ): SchemaFunctions<T> {
240
+ const functions: Record<string, (prompt?: string, options?: AISchemaOptions) => Promise<unknown>> = {}
241
+
242
+ for (const [name, schema] of Object.entries(schemas)) {
243
+ functions[name] = async (prompt?: string, options?: AISchemaOptions) => {
244
+ const mergedOptions = { ...defaultOptions, ...options }
245
+ const { model = 'sonnet', system, ...rest } = mergedOptions
246
+
247
+ // Build prompt from schema descriptions if none provided
248
+ const schemaPrompt = prompt || buildPromptFromSchema(schema)
249
+
250
+ const result = await generateObject({
251
+ model,
252
+ schema,
253
+ prompt: schemaPrompt,
254
+ system,
255
+ ...rest,
256
+ })
257
+
258
+ return result.object
259
+ }
260
+ }
261
+
262
+ return functions as SchemaFunctions<T>
263
+ }
264
+
265
+ /**
266
+ * Build a prompt by extracting descriptions from the schema
267
+ */
268
+ function buildPromptFromSchema(schema: SimpleSchema, path = ''): string {
269
+ if (typeof schema === 'string') {
270
+ return schema
271
+ }
272
+
273
+ if (Array.isArray(schema)) {
274
+ return schema[0] as string || 'Generate items'
275
+ }
276
+
277
+ if (typeof schema === 'object' && schema !== null) {
278
+ const descriptions: string[] = []
279
+ for (const [key, value] of Object.entries(schema)) {
280
+ const subPrompt = buildPromptFromSchema(value as SimpleSchema, path ? `${path}.${key}` : key)
281
+ if (subPrompt) {
282
+ descriptions.push(`${key}: ${subPrompt}`)
283
+ }
284
+ }
285
+ return descriptions.length > 0 ? `Generate the following:\n${descriptions.join('\n')}` : ''
286
+ }
287
+
288
+ return ''
289
+ }
290
+
291
+ /**
292
+ * Create a defined function from a function definition
293
+ */
294
+ function createDefinedFunction<TArgs, TReturn>(
295
+ definition: FunctionDefinition<TArgs, TReturn>
296
+ ): DefinedFunction<TArgs, TReturn> {
297
+ const call = async (args: TArgs): Promise<TReturn> => {
298
+ switch (definition.type) {
299
+ case 'code':
300
+ return executeCodeFunction(definition, args) as Promise<TReturn>
301
+ case 'generative':
302
+ return executeGenerativeFunction(definition, args) as Promise<TReturn>
303
+ case 'agentic':
304
+ return executeAgenticFunction(definition, args) as Promise<TReturn>
305
+ case 'human':
306
+ return executeHumanFunction(definition, args) as Promise<TReturn>
307
+ default:
308
+ throw new Error(`Unknown function type: ${(definition as FunctionDefinition).type}`)
309
+ }
310
+ }
311
+
312
+ const asTool = (): AIFunctionDefinition<TArgs, TReturn> => {
313
+ return {
314
+ name: definition.name,
315
+ description: definition.description || `Execute ${definition.name}`,
316
+ parameters: convertArgsToJSONSchema(definition.args),
317
+ handler: call,
318
+ }
319
+ }
320
+
321
+ return { definition, call, asTool }
322
+ }
323
+
324
+ /**
325
+ * Convert args schema to JSON Schema
326
+ */
327
+ function convertArgsToJSONSchema(args: unknown): JSONSchema {
328
+ // If it's already a JSON schema-like object
329
+ if (typeof args === 'object' && args !== null && 'type' in args) {
330
+ return args as JSONSchema
331
+ }
332
+
333
+ // Convert SimpleSchema to JSON Schema
334
+ const properties: Record<string, JSONSchema> = {}
335
+ const required: string[] = []
336
+
337
+ if (typeof args === 'object' && args !== null) {
338
+ for (const [key, value] of Object.entries(args as Record<string, unknown>)) {
339
+ required.push(key) // All properties required for cross-provider compatibility
340
+ properties[key] = convertValueToJSONSchema(value)
341
+ }
342
+ }
343
+
344
+ return {
345
+ type: 'object',
346
+ properties,
347
+ required,
348
+ additionalProperties: false, // Required for OpenAI compatibility
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Convert a single value to JSON Schema
354
+ */
355
+ function convertValueToJSONSchema(value: unknown): JSONSchema {
356
+ if (typeof value === 'string') {
357
+ // Check for type hints: 'description (number)', 'description (boolean)', etc.
358
+ const typeMatch = value.match(/^(.+?)\s*\((number|boolean|integer|date)\)$/i)
359
+ if (typeMatch) {
360
+ const description = typeMatch[1]!
361
+ const type = typeMatch[2]!
362
+ switch (type.toLowerCase()) {
363
+ case 'number':
364
+ return { type: 'number', description: description.trim() }
365
+ case 'integer':
366
+ return { type: 'integer', description: description.trim() }
367
+ case 'boolean':
368
+ return { type: 'boolean', description: description.trim() }
369
+ case 'date':
370
+ return { type: 'string', format: 'date-time', description: description.trim() }
371
+ }
372
+ }
373
+
374
+ // Check for enum: 'option1 | option2 | option3'
375
+ if (value.includes(' | ')) {
376
+ const options = value.split(' | ').map(s => s.trim())
377
+ return { type: 'string', enum: options }
378
+ }
379
+
380
+ return { type: 'string', description: value }
381
+ }
382
+
383
+ if (Array.isArray(value) && value.length === 1) {
384
+ const [desc] = value
385
+ if (typeof desc === 'string') {
386
+ return { type: 'array', items: { type: 'string' }, description: desc }
387
+ }
388
+ if (typeof desc === 'number') {
389
+ return { type: 'array', items: { type: 'number' } }
390
+ }
391
+ return { type: 'array', items: convertValueToJSONSchema(desc) }
392
+ }
393
+
394
+ if (typeof value === 'object' && value !== null) {
395
+ return convertArgsToJSONSchema(value)
396
+ }
397
+
398
+ return { type: 'string' }
399
+ }
400
+
401
+ /**
402
+ * Fill template with values
403
+ */
404
+ function fillTemplate(template: string, args: Record<string, unknown>): string {
405
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => String(args[key] ?? ''))
406
+ }
407
+
408
+ /**
409
+ * Execute a code function - generates code with tests and examples
410
+ */
411
+ async function executeCodeFunction<TArgs>(
412
+ definition: CodeFunctionDefinition<TArgs>,
413
+ args: TArgs
414
+ ): Promise<CodeFunctionResult> {
415
+ const { name, description, language = 'typescript', instructions, includeTests = true, includeExamples = true } = definition
416
+
417
+ const argsDescription = JSON.stringify(args, null, 2)
418
+
419
+ const result = await generateObject({
420
+ model: 'sonnet',
421
+ schema: {
422
+ code: 'The complete implementation code with JSDoc comments',
423
+ tests: includeTests ? 'Vitest test code for the implementation' : undefined,
424
+ examples: includeExamples ? 'Example usage code' : undefined,
425
+ documentation: 'JSDoc or equivalent documentation string',
426
+ },
427
+ system: `You are an expert ${language} developer. Generate clean, well-documented, production-ready code.`,
428
+ prompt: `Generate a ${language} function with the following specification:
429
+
430
+ Name: ${name}
431
+ Description: ${description || 'No description provided'}
432
+ Arguments: ${argsDescription}
433
+ Return Type: ${JSON.stringify(definition.returnType)}
434
+
435
+ ${instructions ? `Additional Instructions: ${instructions}` : ''}
436
+
437
+ Requirements:
438
+ - Include comprehensive JSDoc comments
439
+ - Follow best practices for ${language}
440
+ - Handle edge cases appropriately
441
+ ${includeTests ? '- Include vitest tests that cover main functionality and edge cases' : ''}
442
+ ${includeExamples ? '- Include example usage showing how to call the function' : ''}`,
443
+ })
444
+
445
+ const obj = result.object as { code: string; tests?: string; examples?: string; documentation: string }
446
+ return {
447
+ code: obj.code,
448
+ tests: obj.tests,
449
+ examples: obj.examples,
450
+ language,
451
+ documentation: obj.documentation,
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Execute a generative function - uses AI to generate content
457
+ */
458
+ async function executeGenerativeFunction<TArgs, TReturn>(
459
+ definition: GenerativeFunctionDefinition<TArgs, TReturn>,
460
+ args: TArgs
461
+ ): Promise<TReturn> {
462
+ const { output, system, promptTemplate, model = 'sonnet', temperature, returnType } = definition
463
+
464
+ const prompt = promptTemplate
465
+ ? fillTemplate(promptTemplate, args as Record<string, unknown>)
466
+ : JSON.stringify(args)
467
+
468
+ switch (output) {
469
+ case 'string': {
470
+ const result = await generateObject({
471
+ model,
472
+ schema: { text: 'The generated text response' },
473
+ system,
474
+ prompt,
475
+ temperature,
476
+ })
477
+ return (result.object as { text: string }).text as TReturn
478
+ }
479
+
480
+ case 'object': {
481
+ const objectSchema = returnType || { result: 'The generated result' }
482
+ const result = await generateObject({
483
+ model,
484
+ schema: objectSchema as SimpleSchemaType,
485
+ system,
486
+ prompt,
487
+ temperature,
488
+ })
489
+ return result.object as TReturn
490
+ }
491
+
492
+ case 'image': {
493
+ const client = getDefaultAIClient()
494
+ return client.image(prompt) as unknown as Promise<TReturn>
495
+ }
496
+
497
+ case 'video': {
498
+ const client = getDefaultAIClient()
499
+ return client.video(prompt) as unknown as Promise<TReturn>
500
+ }
501
+
502
+ case 'audio': {
503
+ // Audio generation would need a specific implementation
504
+ throw new Error('Audio generation not yet implemented')
505
+ }
506
+
507
+ default:
508
+ throw new Error(`Unknown output type: ${output}`)
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Execute an agentic function - runs in a loop with tools
514
+ */
515
+ async function executeAgenticFunction<TArgs, TReturn>(
516
+ definition: AgenticFunctionDefinition<TArgs, TReturn>,
517
+ args: TArgs
518
+ ): Promise<TReturn> {
519
+ const { instructions, promptTemplate, tools = [], maxIterations = 10, model = 'sonnet', returnType } = definition
520
+
521
+ const prompt = promptTemplate
522
+ ? fillTemplate(promptTemplate, args as Record<string, unknown>)
523
+ : JSON.stringify(args)
524
+
525
+ // Build system prompt with tool descriptions
526
+ const toolDescriptions = tools.map(t => `- ${t.name}: ${t.description}`).join('\n')
527
+ const systemPrompt = `${instructions}
528
+
529
+ Available tools:
530
+ ${toolDescriptions || 'No tools available'}
531
+
532
+ Work step by step to accomplish the task. When you have completed the task, provide your final result.`
533
+
534
+ let iteration = 0
535
+ const toolResults: unknown[] = []
536
+
537
+ // Simple agent loop
538
+ while (iteration < maxIterations) {
539
+ iteration++
540
+
541
+ const result = await generateObject({
542
+ model,
543
+ schema: {
544
+ thinking: 'Your step-by-step reasoning',
545
+ toolCall: {
546
+ name: 'Tool to call (or "done" if finished)',
547
+ arguments: 'Arguments for the tool as JSON string',
548
+ },
549
+ finalResult: returnType || 'The final result if done',
550
+ },
551
+ system: systemPrompt,
552
+ prompt: `Task: ${prompt}
553
+
554
+ Previous tool results:
555
+ ${toolResults.map((r, i) => `Step ${i + 1}: ${JSON.stringify(r)}`).join('\n') || 'None yet'}
556
+
557
+ What is your next step?`,
558
+ })
559
+
560
+ const response = result.object as {
561
+ thinking: string
562
+ toolCall: { name: string; arguments: string }
563
+ finalResult: unknown
564
+ }
565
+
566
+ if (response.toolCall.name === 'done' || response.finalResult) {
567
+ return response.finalResult as TReturn
568
+ }
569
+
570
+ // Execute tool call
571
+ const tool = tools.find(t => t.name === response.toolCall.name)
572
+ if (tool) {
573
+ const toolArgs = JSON.parse(response.toolCall.arguments || '{}')
574
+ const toolResult = await tool.handler(toolArgs)
575
+ toolResults.push({ tool: response.toolCall.name, result: toolResult })
576
+ } else {
577
+ toolResults.push({ error: `Tool not found: ${response.toolCall.name}` })
578
+ }
579
+ }
580
+
581
+ throw new Error(`Agent exceeded maximum iterations (${maxIterations})`)
582
+ }
583
+
584
+ /**
585
+ * Execute a human function - generates UI and waits for human input
586
+ */
587
+ async function executeHumanFunction<TArgs, TReturn>(
588
+ definition: HumanFunctionDefinition<TArgs, TReturn>,
589
+ args: TArgs
590
+ ): Promise<TReturn> {
591
+ const { channel, instructions, promptTemplate, returnType } = definition
592
+
593
+ const prompt = promptTemplate
594
+ ? fillTemplate(promptTemplate, args as Record<string, unknown>)
595
+ : JSON.stringify(args)
596
+
597
+ // Generate channel-specific UI
598
+ const uiSchema: Record<string, SimpleSchemaType> = {
599
+ // New HumanChannel types
600
+ chat: {
601
+ message: 'Chat message to send',
602
+ options: ['Response options if applicable'],
603
+ },
604
+ email: {
605
+ subject: 'Email subject',
606
+ html: 'Email HTML body',
607
+ text: 'Plain text fallback',
608
+ },
609
+ phone: {
610
+ script: 'Phone call script',
611
+ keyPoints: ['Key points to cover'],
612
+ },
613
+ sms: {
614
+ text: 'SMS message text (max 160 chars)',
615
+ },
616
+ workspace: {
617
+ blocks: ['Workspace/Slack BlockKit blocks as JSON array'],
618
+ text: 'Plain text fallback',
619
+ },
620
+ web: {
621
+ component: 'React component code for the form',
622
+ schema: 'JSON schema for the form fields',
623
+ },
624
+ // Legacy fallback
625
+ custom: {
626
+ data: 'Structured data for custom implementation',
627
+ instructions: 'Instructions for the human',
628
+ },
629
+ }
630
+
631
+ const result = await generateObject({
632
+ model: 'sonnet',
633
+ schema: uiSchema[channel] ?? uiSchema.custom,
634
+ system: `Generate ${channel} UI/content for a human-in-the-loop task.`,
635
+ prompt: `Task: ${instructions}
636
+
637
+ Input data:
638
+ ${prompt}
639
+
640
+ Expected response format:
641
+ ${JSON.stringify(returnType)}
642
+
643
+ Generate the appropriate ${channel} UI/content to collect this response from a human.`,
644
+ })
645
+
646
+ // In a real implementation, this would:
647
+ // 1. Send the generated UI to the appropriate channel
648
+ // 2. Wait for human response
649
+ // 3. Return the validated response
650
+
651
+ // For now, return the generated artifacts as a placeholder
652
+ return {
653
+ _pending: true,
654
+ channel,
655
+ artifacts: result.object,
656
+ expectedResponseType: returnType,
657
+ } as unknown as TReturn
658
+ }
659
+
660
+ /**
661
+ * Helper to create a function that supports both regular calls and tagged template literals
662
+ * @example
663
+ * const fn = withTemplate((prompt) => doSomething(prompt))
664
+ * fn('hello') // regular call
665
+ * fn`hello ${name}` // tagged template literal
666
+ */
667
+ export function withTemplate<TArgs extends unknown[], TReturn>(
668
+ fn: (prompt: string, ...args: TArgs) => TReturn
669
+ ): ((prompt: string, ...args: TArgs) => TReturn) & ((strings: TemplateStringsArray, ...values: unknown[]) => TReturn) {
670
+ return function (promptOrStrings: string | TemplateStringsArray, ...args: unknown[]): TReturn {
671
+ if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
672
+ // Tagged template literal call - pass empty args for optional params
673
+ const strings = promptOrStrings as TemplateStringsArray
674
+ const values = args
675
+ const prompt = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '')
676
+ return fn(prompt, ...([] as unknown as TArgs))
677
+ }
678
+ // Regular function call
679
+ return fn(promptOrStrings as string, ...(args as TArgs))
680
+ } as ((prompt: string, ...args: TArgs) => TReturn) & ((strings: TemplateStringsArray, ...values: unknown[]) => TReturn)
681
+ }
682
+
683
+ // Default client management
684
+ let defaultClient: AIClient | null = null
685
+
686
+ /**
687
+ * Configure the default AI client
688
+ */
689
+ export function configureAI(options: AIClientOptions): void {
690
+ defaultClient = AI(options) as AIClient
691
+ }
692
+
693
+ /**
694
+ * Get the default AI client, throwing if not configured
695
+ */
696
+ function getDefaultAIClient(): AIClient {
697
+ if (!defaultClient) {
698
+ // Try to auto-configure from environment
699
+ const wsUrl = typeof process !== 'undefined' ? process.env?.AI_WS_URL : undefined
700
+ const httpUrl = typeof process !== 'undefined' ? process.env?.AI_HTTP_URL : undefined
701
+
702
+ if (wsUrl) {
703
+ defaultClient = AI({ wsUrl } as AIClientOptions) as unknown as AIClient
704
+ } else if (httpUrl) {
705
+ defaultClient = AI({ httpUrl } as AIClientOptions) as unknown as AIClient
706
+ } else {
707
+ throw new Error(
708
+ 'AI client not configured. Call configureAI() first or set AI_WS_URL/AI_HTTP_URL environment variables.'
709
+ )
710
+ }
711
+ }
712
+ return defaultClient
713
+ }
714
+
715
+ /**
716
+ * Base class for implementing AI services
717
+ *
718
+ * Extend this class to create your own AI service implementation.
719
+ *
720
+ * @example
721
+ * ```ts
722
+ * class MyAIService extends AIServiceTarget {
723
+ * async generate(options: AIGenerateOptions): Promise<AIGenerateResult> {
724
+ * // Your implementation
725
+ * }
726
+ * }
727
+ * ```
728
+ */
729
+ export abstract class AIServiceTarget extends RpcTarget implements AIClient {
730
+ abstract generate(options: AIGenerateOptions): RpcPromise<AIGenerateResult>
731
+ abstract do(action: string, context?: unknown): RpcPromise<unknown>
732
+ abstract is(value: unknown, type: string | JSONSchema): RpcPromise<boolean>
733
+ abstract code(prompt: string, language?: string): RpcPromise<string>
734
+ abstract decide<T extends string>(options: T[], context?: string): RpcPromise<T>
735
+ abstract diagram(description: string, format?: 'mermaid' | 'svg' | 'ascii'): RpcPromise<string>
736
+ abstract image(prompt: string, options?: ImageOptions): RpcPromise<ImageResult>
737
+ abstract video(prompt: string, options?: VideoOptions): RpcPromise<VideoResult>
738
+ abstract write(prompt: string, options?: WriteOptions): RpcPromise<string>
739
+ abstract list(prompt: string): RpcPromise<ListResult>
740
+ abstract lists(prompt: string): RpcPromise<ListsResult>
741
+ }
742
+
743
+ /**
744
+ * Standalone function for defining AI functions
745
+ *
746
+ * @example
747
+ * ```ts
748
+ * import { defineFunction } from 'ai-functions'
749
+ *
750
+ * const summarize = defineFunction({
751
+ * type: 'generative',
752
+ * name: 'summarize',
753
+ * args: { text: 'Text to summarize' },
754
+ * output: 'string',
755
+ * promptTemplate: 'Summarize: {{text}}',
756
+ * })
757
+ *
758
+ * const result = await summarize.call({ text: 'Long article...' })
759
+ * ```
760
+ */
761
+ export function defineFunction<TArgs, TReturn>(
762
+ definition: FunctionDefinition<TArgs, TReturn>
763
+ ): DefinedFunction<TArgs, TReturn> {
764
+ return createDefinedFunction(definition)
765
+ }
766
+
767
+ // ============================================================================
768
+ // Function Registry
769
+ // ============================================================================
770
+
771
+ /**
772
+ * In-memory function registry
773
+ */
774
+ class InMemoryFunctionRegistry implements FunctionRegistry {
775
+ private functions = new Map<string, DefinedFunction>()
776
+
777
+ get(name: string): DefinedFunction | undefined {
778
+ return this.functions.get(name)
779
+ }
780
+
781
+ set(name: string, fn: DefinedFunction): void {
782
+ this.functions.set(name, fn)
783
+ }
784
+
785
+ has(name: string): boolean {
786
+ return this.functions.has(name)
787
+ }
788
+
789
+ list(): string[] {
790
+ return Array.from(this.functions.keys())
791
+ }
792
+
793
+ delete(name: string): boolean {
794
+ return this.functions.delete(name)
795
+ }
796
+
797
+ clear(): void {
798
+ this.functions.clear()
799
+ }
800
+ }
801
+
802
+ /**
803
+ * Global function registry
804
+ *
805
+ * Note: This is in-memory only. For persistence, use mdxai or mdxdb packages.
806
+ */
807
+ export const functions: FunctionRegistry = new InMemoryFunctionRegistry()
808
+
809
+ // ============================================================================
810
+ // Auto-Define Functions
811
+ // ============================================================================
812
+
813
+ /**
814
+ * Analyze a function call and determine what type of function it should be
815
+ */
816
+ async function analyzeFunction(
817
+ name: string,
818
+ args: Record<string, unknown>
819
+ ): Promise<AutoDefineResult> {
820
+ // Convert camelCase/snake_case to readable name
821
+ const readableName = name
822
+ .replace(/([A-Z])/g, ' $1')
823
+ .replace(/_/g, ' ')
824
+ .toLowerCase()
825
+ .trim()
826
+
827
+ const argDescriptions = Object.entries(args)
828
+ .map(([key, value]) => {
829
+ const type = Array.isArray(value) ? 'array' : typeof value
830
+ return ` - ${key}: ${type} (example: ${JSON.stringify(value).slice(0, 50)})`
831
+ })
832
+ .join('\n')
833
+
834
+ const result = await generateObject({
835
+ model: 'sonnet',
836
+ schema: {
837
+ type: 'code | generative | agentic | human',
838
+ reasoning: 'Why this function type is appropriate (1-2 sentences)',
839
+ description: 'What this function does',
840
+ output: 'string | object | image | video | audio',
841
+ returnType: 'Schema for the return type as a SimpleSchema object',
842
+ system: 'System prompt for the AI (if generative/agentic)',
843
+ promptTemplate: 'Prompt template with {{arg}} placeholders',
844
+ instructions: 'Instructions for agentic/human functions',
845
+ needsTools: 'true | false',
846
+ suggestedTools: ['Names of tools that might be needed'],
847
+ channel: 'slack | email | web | sms | custom',
848
+ },
849
+ system: `You are an expert at designing AI functions. Analyze the function name and arguments to determine the best function type.
850
+
851
+ Function Types:
852
+ - "code": For generating executable code (calculations, algorithms, data transformations)
853
+ - "generative": For generating content (text, summaries, translations, creative writing, structured data)
854
+ - "agentic": For complex tasks requiring multiple steps, research, or tool use (research, planning, multi-step workflows)
855
+ - "human": For tasks requiring human judgment, approval, or input (approvals, reviews, decisions)
856
+
857
+ Guidelines:
858
+ - Most functions should be "generative" - they generate content or structured data
859
+ - Use "code" only when actual executable code needs to be generated
860
+ - Use "agentic" when the task requires research, multiple steps, or external tool use
861
+ - Use "human" when human judgment/approval is essential`,
862
+ prompt: `Analyze this function call and determine how to define it:
863
+
864
+ Function Name: ${name}
865
+ Readable Name: ${readableName}
866
+ Arguments:
867
+ ${argDescriptions || ' (no arguments)'}
868
+
869
+ Determine:
870
+ 1. What type of function this should be
871
+ 2. What it should return
872
+ 3. How it should be implemented`,
873
+ })
874
+
875
+ const analysis = result.object as {
876
+ type: string
877
+ reasoning: string
878
+ description: string
879
+ output: string
880
+ returnType: unknown
881
+ system: string
882
+ promptTemplate: string
883
+ instructions: string
884
+ needsTools: string
885
+ suggestedTools: string[]
886
+ channel: string
887
+ }
888
+
889
+ // Build the function definition based on the analysis
890
+ let definition: FunctionDefinition
891
+
892
+ const baseDefinition = {
893
+ name,
894
+ description: analysis.description,
895
+ args: inferArgsSchema(args),
896
+ returnType: analysis.returnType as SimpleSchemaType,
897
+ }
898
+
899
+ switch (analysis.type) {
900
+ case 'code':
901
+ definition = {
902
+ ...baseDefinition,
903
+ type: 'code' as const,
904
+ language: 'typescript' as const,
905
+ instructions: analysis.instructions,
906
+ }
907
+ break
908
+
909
+ case 'agentic':
910
+ definition = {
911
+ ...baseDefinition,
912
+ type: 'agentic' as const,
913
+ instructions: analysis.instructions || `Complete the ${readableName} task`,
914
+ promptTemplate: analysis.promptTemplate,
915
+ tools: [], // Tools would need to be provided separately
916
+ maxIterations: 10,
917
+ }
918
+ break
919
+
920
+ case 'human':
921
+ definition = {
922
+ ...baseDefinition,
923
+ type: 'human' as const,
924
+ channel: (analysis.channel || 'web') as HumanChannel,
925
+ instructions: analysis.instructions || `Please review and respond to this ${readableName} request`,
926
+ promptTemplate: analysis.promptTemplate,
927
+ }
928
+ break
929
+
930
+ case 'generative':
931
+ default:
932
+ definition = {
933
+ ...baseDefinition,
934
+ type: 'generative' as const,
935
+ output: (analysis.output || 'object') as 'string' | 'object' | 'image' | 'video' | 'audio',
936
+ system: analysis.system,
937
+ promptTemplate: analysis.promptTemplate || `{{${Object.keys(args)[0] || 'input'}}}`,
938
+ }
939
+ break
940
+ }
941
+
942
+ return {
943
+ type: analysis.type as 'code' | 'generative' | 'agentic' | 'human',
944
+ reasoning: analysis.reasoning,
945
+ definition,
946
+ }
947
+ }
948
+
949
+ /**
950
+ * Infer a schema from example arguments
951
+ */
952
+ function inferArgsSchema(args: Record<string, unknown>): Record<string, string | string[] | Record<string, unknown>> {
953
+ const schema: Record<string, string | string[] | Record<string, unknown>> = {}
954
+
955
+ for (const [key, value] of Object.entries(args)) {
956
+ if (typeof value === 'string') {
957
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
958
+ } else if (typeof value === 'number') {
959
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (number)`
960
+ } else if (typeof value === 'boolean') {
961
+ schema[key] = `Whether ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (boolean)`
962
+ } else if (Array.isArray(value)) {
963
+ if (value.length > 0 && typeof value[0] === 'string') {
964
+ schema[key] = [`List of ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
965
+ } else {
966
+ schema[key] = [`Items for ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
967
+ }
968
+ } else if (typeof value === 'object' && value !== null) {
969
+ schema[key] = inferArgsSchema(value as Record<string, unknown>)
970
+ } else {
971
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
972
+ }
973
+ }
974
+
975
+ return schema
976
+ }
977
+
978
+ /**
979
+ * Auto-define a function based on its name and arguments, or define with explicit definition
980
+ *
981
+ * When called with (name, args), uses AI to analyze and determine:
982
+ * - What type of function it should be (code, generative, agentic, human)
983
+ * - What it should return
984
+ * - How it should be implemented
985
+ *
986
+ * When called with a FunctionDefinition, creates the function directly.
987
+ *
988
+ * @example
989
+ * ```ts
990
+ * // Auto-define from name and example args
991
+ * const planTrip = await define('planTrip', { destination: 'Tokyo', travelers: 2 })
992
+ *
993
+ * // Or define explicitly
994
+ * const summarize = define.generative({
995
+ * name: 'summarize',
996
+ * args: { text: 'Text to summarize' },
997
+ * output: 'string',
998
+ * })
999
+ *
1000
+ * // Or with full definition
1001
+ * const fn = defineFunction({
1002
+ * type: 'generative',
1003
+ * name: 'translate',
1004
+ * args: { text: 'Text', lang: 'Target language' },
1005
+ * output: 'string',
1006
+ * })
1007
+ * ```
1008
+ */
1009
+ async function autoDefineImpl(
1010
+ name: string,
1011
+ args: Record<string, unknown>
1012
+ ): Promise<DefinedFunction> {
1013
+ // Check if already defined
1014
+ const existing = functions.get(name)
1015
+ if (existing) {
1016
+ return existing
1017
+ }
1018
+
1019
+ // Analyze and define the function
1020
+ const { definition } = await analyzeFunction(name, args)
1021
+
1022
+ // Create the defined function
1023
+ const definedFn = createDefinedFunction(definition)
1024
+
1025
+ // Store in registry
1026
+ functions.set(name, definedFn)
1027
+
1028
+ return definedFn
1029
+ }
1030
+
1031
+ /**
1032
+ * Define functions - auto-define or use typed helpers
1033
+ */
1034
+ export const define = Object.assign(autoDefineImpl, {
1035
+ /**
1036
+ * Define a code generation function
1037
+ */
1038
+ code: <TArgs, TReturn>(
1039
+ definition: Omit<CodeFunctionDefinition<TArgs, TReturn>, 'type'>
1040
+ ): DefinedFunction<TArgs, TReturn> => {
1041
+ const fn = defineFunction({ type: 'code', ...definition } as CodeFunctionDefinition<TArgs, TReturn>)
1042
+ functions.set(definition.name, fn as DefinedFunction)
1043
+ return fn
1044
+ },
1045
+
1046
+ /**
1047
+ * Define a generative function
1048
+ */
1049
+ generative: <TArgs, TReturn>(
1050
+ definition: Omit<GenerativeFunctionDefinition<TArgs, TReturn>, 'type'>
1051
+ ): DefinedFunction<TArgs, TReturn> => {
1052
+ const fn = defineFunction({ type: 'generative', ...definition } as GenerativeFunctionDefinition<TArgs, TReturn>)
1053
+ functions.set(definition.name, fn as DefinedFunction)
1054
+ return fn
1055
+ },
1056
+
1057
+ /**
1058
+ * Define an agentic function
1059
+ */
1060
+ agentic: <TArgs, TReturn>(
1061
+ definition: Omit<AgenticFunctionDefinition<TArgs, TReturn>, 'type'>
1062
+ ): DefinedFunction<TArgs, TReturn> => {
1063
+ const fn = defineFunction({ type: 'agentic', ...definition } as AgenticFunctionDefinition<TArgs, TReturn>)
1064
+ functions.set(definition.name, fn as DefinedFunction)
1065
+ return fn
1066
+ },
1067
+
1068
+ /**
1069
+ * Define a human-in-the-loop function
1070
+ */
1071
+ human: <TArgs, TReturn>(
1072
+ definition: Omit<HumanFunctionDefinition<TArgs, TReturn>, 'type'>
1073
+ ): DefinedFunction<TArgs, TReturn> => {
1074
+ const fn = defineFunction({ type: 'human', ...definition } as HumanFunctionDefinition<TArgs, TReturn>)
1075
+ functions.set(definition.name, fn as DefinedFunction)
1076
+ return fn
1077
+ },
1078
+ })
1079
+
1080
+ // ============================================================================
1081
+ // AI() - Smart AI Client with Auto-Definition
1082
+ // ============================================================================
1083
+
1084
+ /** Known built-in method names that should not be auto-defined */
1085
+ const BUILTIN_METHODS = new Set([
1086
+ 'do', 'is', 'code', 'decide', 'diagram', 'generate', 'image', 'video', 'write', 'list', 'lists',
1087
+ 'functions', 'define', 'defineFunction', 'then', 'catch', 'finally',
1088
+ ])
1089
+
1090
+ /**
1091
+ * Create a smart AI client that auto-defines functions on first call
1092
+ *
1093
+ * @example
1094
+ * ```ts
1095
+ * const ai = AI()
1096
+ *
1097
+ * // First call - auto-defines the function
1098
+ * const trip = await ai.planTrip({
1099
+ * destination: 'Tokyo',
1100
+ * dates: { start: '2024-03-01', end: '2024-03-10' },
1101
+ * travelers: 2,
1102
+ * })
1103
+ *
1104
+ * // Second call - uses cached definition (in-memory)
1105
+ * const trip2 = await ai.planTrip({
1106
+ * destination: 'Paris',
1107
+ * dates: { start: '2024-06-01', end: '2024-06-07' },
1108
+ * travelers: 4,
1109
+ * })
1110
+ *
1111
+ * // Access registry and define
1112
+ * console.log(ai.functions.list()) // ['planTrip']
1113
+ * ai.define.generative({ name: 'summarize', ... })
1114
+ * ```
1115
+ */
1116
+ function createSmartAI(): AIProxy {
1117
+ const base = {
1118
+ functions,
1119
+ define,
1120
+ defineFunction,
1121
+ }
1122
+
1123
+ return new Proxy(base as AIProxy, {
1124
+ get(target, prop: string) {
1125
+ // Return built-in properties
1126
+ if (prop in target) {
1127
+ return (target as Record<string, unknown>)[prop]
1128
+ }
1129
+
1130
+ // Skip internal properties
1131
+ if (typeof prop === 'symbol' || prop.startsWith('_') || BUILTIN_METHODS.has(prop)) {
1132
+ return undefined
1133
+ }
1134
+
1135
+ // Return a function that auto-defines and calls
1136
+ return async (args: Record<string, unknown> = {}) => {
1137
+ // Check if function is already defined
1138
+ let fn = functions.get(prop)
1139
+
1140
+ if (!fn) {
1141
+ // Auto-define the function
1142
+ fn = await define(prop, args)
1143
+ }
1144
+
1145
+ // Call the function
1146
+ return fn.call(args)
1147
+ }
1148
+ },
1149
+ })
1150
+ }
1151
+
1152
+ /**
1153
+ * Type for the AI proxy with auto-define capability
1154
+ */
1155
+ export interface AIProxy {
1156
+ /** Function registry */
1157
+ functions: FunctionRegistry
1158
+ /** Define functions */
1159
+ define: typeof define
1160
+ /** Define a function with full definition */
1161
+ defineFunction: typeof defineFunction
1162
+ /** Dynamic function calls */
1163
+ [key: string]: unknown
1164
+ }
1165
+
1166
+ /**
1167
+ * Default AI instance with auto-define capability
1168
+ *
1169
+ * @example
1170
+ * ```ts
1171
+ * import { ai } from 'ai-functions'
1172
+ *
1173
+ * // Auto-define and call
1174
+ * const result = await ai.summarize({ text: 'Long article...' })
1175
+ *
1176
+ * // Access functions registry
1177
+ * ai.functions.list()
1178
+ *
1179
+ * // Define explicitly
1180
+ * ai.define.generative({ name: 'translate', ... })
1181
+ * ```
1182
+ */
1183
+ export const ai: AIProxy = createSmartAI()