ai-functions 2.1.1 → 2.3.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 (286) hide show
  1. package/.turbo/turbo-build.log +1 -4
  2. package/CHANGELOG.md +68 -1
  3. package/README.md +397 -157
  4. package/dist/ai-promise.d.ts +50 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +410 -51
  7. package/dist/ai-promise.js.map +1 -1
  8. package/dist/ai-schemas.d.ts +56 -0
  9. package/dist/ai-schemas.d.ts.map +1 -0
  10. package/dist/ai-schemas.js +53 -0
  11. package/dist/ai-schemas.js.map +1 -0
  12. package/dist/ai.d.ts +16 -242
  13. package/dist/ai.d.ts.map +1 -1
  14. package/dist/ai.js +54 -837
  15. package/dist/ai.js.map +1 -1
  16. package/dist/batch/anthropic.d.ts +6 -4
  17. package/dist/batch/anthropic.d.ts.map +1 -1
  18. package/dist/batch/anthropic.js +83 -145
  19. package/dist/batch/anthropic.js.map +1 -1
  20. package/dist/batch/bedrock.d.ts +8 -30
  21. package/dist/batch/bedrock.d.ts.map +1 -1
  22. package/dist/batch/bedrock.js +155 -338
  23. package/dist/batch/bedrock.js.map +1 -1
  24. package/dist/batch/cloudflare.d.ts +8 -20
  25. package/dist/batch/cloudflare.d.ts.map +1 -1
  26. package/dist/batch/cloudflare.js +68 -189
  27. package/dist/batch/cloudflare.js.map +1 -1
  28. package/dist/batch/google.d.ts +6 -20
  29. package/dist/batch/google.d.ts.map +1 -1
  30. package/dist/batch/google.js +70 -238
  31. package/dist/batch/google.js.map +1 -1
  32. package/dist/batch/index.d.ts +4 -1
  33. package/dist/batch/index.d.ts.map +1 -1
  34. package/dist/batch/index.js +4 -1
  35. package/dist/batch/index.js.map +1 -1
  36. package/dist/batch/memory.d.ts +1 -1
  37. package/dist/batch/memory.d.ts.map +1 -1
  38. package/dist/batch/memory.js +14 -10
  39. package/dist/batch/memory.js.map +1 -1
  40. package/dist/batch/openai.d.ts +11 -14
  41. package/dist/batch/openai.d.ts.map +1 -1
  42. package/dist/batch/openai.js +52 -156
  43. package/dist/batch/openai.js.map +1 -1
  44. package/dist/batch/provider.d.ts +111 -0
  45. package/dist/batch/provider.d.ts.map +1 -0
  46. package/dist/batch/provider.js +233 -0
  47. package/dist/batch/provider.js.map +1 -0
  48. package/dist/batch-map.d.ts.map +1 -1
  49. package/dist/batch-map.js +23 -17
  50. package/dist/batch-map.js.map +1 -1
  51. package/dist/batch-queue.d.ts +65 -0
  52. package/dist/batch-queue.d.ts.map +1 -1
  53. package/dist/batch-queue.js +169 -14
  54. package/dist/batch-queue.js.map +1 -1
  55. package/dist/budget.d.ts +272 -0
  56. package/dist/budget.d.ts.map +1 -0
  57. package/dist/budget.js +513 -0
  58. package/dist/budget.js.map +1 -0
  59. package/dist/cache.d.ts +295 -0
  60. package/dist/cache.d.ts.map +1 -0
  61. package/dist/cache.js +433 -0
  62. package/dist/cache.js.map +1 -0
  63. package/dist/context.d.ts +42 -8
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +64 -62
  66. package/dist/context.js.map +1 -1
  67. package/dist/digital-objects-registry.d.ts +229 -0
  68. package/dist/digital-objects-registry.d.ts.map +1 -0
  69. package/dist/digital-objects-registry.js +617 -0
  70. package/dist/digital-objects-registry.js.map +1 -0
  71. package/dist/embeddings.d.ts +2 -2
  72. package/dist/embeddings.d.ts.map +1 -1
  73. package/dist/errors.d.ts +22 -0
  74. package/dist/errors.d.ts.map +1 -0
  75. package/dist/errors.js +35 -0
  76. package/dist/errors.js.map +1 -0
  77. package/dist/eval/runner.d.ts +10 -1
  78. package/dist/eval/runner.d.ts.map +1 -1
  79. package/dist/eval/runner.js +41 -35
  80. package/dist/eval/runner.js.map +1 -1
  81. package/dist/eval-log/in-memory.d.ts +34 -0
  82. package/dist/eval-log/in-memory.d.ts.map +1 -0
  83. package/dist/eval-log/in-memory.js +84 -0
  84. package/dist/eval-log/in-memory.js.map +1 -0
  85. package/dist/eval-log/index.d.ts +29 -0
  86. package/dist/eval-log/index.d.ts.map +1 -0
  87. package/dist/eval-log/index.js +39 -0
  88. package/dist/eval-log/index.js.map +1 -0
  89. package/dist/eval-log/types.d.ts +101 -0
  90. package/dist/eval-log/types.d.ts.map +1 -0
  91. package/dist/eval-log/types.js +16 -0
  92. package/dist/eval-log/types.js.map +1 -0
  93. package/dist/function-registry.d.ts +116 -0
  94. package/dist/function-registry.d.ts.map +1 -0
  95. package/dist/function-registry.js +546 -0
  96. package/dist/function-registry.js.map +1 -0
  97. package/dist/generate.d.ts +9 -3
  98. package/dist/generate.d.ts.map +1 -1
  99. package/dist/generate.js +18 -22
  100. package/dist/generate.js.map +1 -1
  101. package/dist/index.d.ts +35 -20
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +89 -42
  104. package/dist/index.js.map +1 -1
  105. package/dist/logger.d.ts +118 -0
  106. package/dist/logger.d.ts.map +1 -0
  107. package/dist/logger.js +187 -0
  108. package/dist/logger.js.map +1 -0
  109. package/dist/middleware/budget.d.ts +84 -0
  110. package/dist/middleware/budget.d.ts.map +1 -0
  111. package/dist/middleware/budget.js +110 -0
  112. package/dist/middleware/budget.js.map +1 -0
  113. package/dist/middleware/cache.d.ts +103 -0
  114. package/dist/middleware/cache.d.ts.map +1 -0
  115. package/dist/middleware/cache.js +228 -0
  116. package/dist/middleware/cache.js.map +1 -0
  117. package/dist/middleware/embed-cache.d.ts +99 -0
  118. package/dist/middleware/embed-cache.d.ts.map +1 -0
  119. package/dist/middleware/embed-cache.js +128 -0
  120. package/dist/middleware/embed-cache.js.map +1 -0
  121. package/dist/middleware/index.d.ts +11 -0
  122. package/dist/middleware/index.d.ts.map +1 -0
  123. package/dist/middleware/index.js +11 -0
  124. package/dist/middleware/index.js.map +1 -0
  125. package/dist/middleware/trace.d.ts +103 -0
  126. package/dist/middleware/trace.d.ts.map +1 -0
  127. package/dist/middleware/trace.js +176 -0
  128. package/dist/middleware/trace.js.map +1 -0
  129. package/dist/primitives.d.ts +120 -1
  130. package/dist/primitives.d.ts.map +1 -1
  131. package/dist/primitives.js +398 -26
  132. package/dist/primitives.js.map +1 -1
  133. package/dist/retry.d.ts +368 -0
  134. package/dist/retry.d.ts.map +1 -0
  135. package/dist/retry.js +646 -0
  136. package/dist/retry.js.map +1 -0
  137. package/dist/schema.d.ts.map +1 -1
  138. package/dist/schema.js +2 -10
  139. package/dist/schema.js.map +1 -1
  140. package/dist/telemetry.d.ts +128 -0
  141. package/dist/telemetry.d.ts.map +1 -0
  142. package/dist/telemetry.js +285 -0
  143. package/dist/telemetry.js.map +1 -0
  144. package/dist/template.d.ts.map +1 -1
  145. package/dist/template.js +6 -1
  146. package/dist/template.js.map +1 -1
  147. package/dist/tool-orchestration.d.ts +453 -0
  148. package/dist/tool-orchestration.d.ts.map +1 -0
  149. package/dist/tool-orchestration.js +763 -0
  150. package/dist/tool-orchestration.js.map +1 -0
  151. package/dist/type-guards.d.ts +28 -0
  152. package/dist/type-guards.d.ts.map +1 -0
  153. package/dist/type-guards.js +29 -0
  154. package/dist/type-guards.js.map +1 -0
  155. package/dist/types.d.ts +135 -17
  156. package/dist/types.d.ts.map +1 -1
  157. package/dist/types.js +36 -1
  158. package/dist/types.js.map +1 -1
  159. package/dist/wrap-for-v3.d.ts +80 -0
  160. package/dist/wrap-for-v3.d.ts.map +1 -0
  161. package/dist/wrap-for-v3.js +89 -0
  162. package/dist/wrap-for-v3.js.map +1 -0
  163. package/examples/00-quickstart.ts +232 -0
  164. package/examples/01-rag-chatbot.ts +212 -0
  165. package/examples/02-multi-agent-research.ts +290 -0
  166. package/examples/03-email-classification.ts +379 -0
  167. package/examples/04-content-moderation.ts +400 -0
  168. package/examples/05-document-extraction.ts +455 -0
  169. package/examples/06-streaming-chat-nextjs.ts +437 -0
  170. package/examples/07-cloudflare-worker.ts +483 -0
  171. package/examples/08-batch-processing.ts +491 -0
  172. package/examples/09-budget-constrained.ts +527 -0
  173. package/examples/10-tool-orchestration.ts +565 -0
  174. package/examples/11-retry-resilience.ts +403 -0
  175. package/examples/12-caching-strategies.ts +422 -0
  176. package/examples/README.md +145 -0
  177. package/package.json +10 -6
  178. package/src/ai-promise.ts +528 -99
  179. package/src/ai-schemas.ts +122 -0
  180. package/src/ai.ts +69 -1153
  181. package/src/batch/anthropic.ts +96 -161
  182. package/src/batch/bedrock.ts +203 -454
  183. package/src/batch/cloudflare.ts +99 -282
  184. package/src/batch/google.ts +91 -297
  185. package/src/batch/index.ts +4 -1
  186. package/src/batch/memory.ts +15 -10
  187. package/src/batch/openai.ts +65 -193
  188. package/src/batch/provider.ts +336 -0
  189. package/src/batch-map.ts +29 -24
  190. package/src/batch-queue.ts +200 -11
  191. package/src/budget.ts +740 -0
  192. package/src/cache.ts +681 -0
  193. package/src/context.ts +122 -76
  194. package/src/digital-objects-registry.ts +750 -0
  195. package/src/errors.ts +37 -0
  196. package/src/eval/runner.ts +63 -38
  197. package/src/eval-log/in-memory.ts +90 -0
  198. package/src/eval-log/index.ts +46 -0
  199. package/src/eval-log/types.ts +110 -0
  200. package/src/function-registry.ts +671 -0
  201. package/src/generate.ts +33 -33
  202. package/src/index.ts +325 -49
  203. package/src/logger.ts +232 -0
  204. package/src/middleware/budget.ts +171 -0
  205. package/src/middleware/cache.ts +299 -0
  206. package/src/middleware/embed-cache.ts +195 -0
  207. package/src/middleware/index.ts +23 -0
  208. package/src/middleware/trace.ts +248 -0
  209. package/src/primitives.ts +589 -62
  210. package/src/retry.ts +902 -0
  211. package/src/schema.ts +8 -17
  212. package/src/telemetry.ts +403 -0
  213. package/src/template.ts +8 -4
  214. package/src/tool-orchestration.ts +1173 -0
  215. package/src/type-guards.ts +31 -0
  216. package/src/types.ts +164 -25
  217. package/src/wrap-for-v3.ts +105 -0
  218. package/test/ai-promise.test.ts +1080 -0
  219. package/test/ai-proxy.test.ts +1 -1
  220. package/test/backward-compat.test.ts +147 -0
  221. package/test/batch-autosubmit-errors.test.ts +610 -0
  222. package/test/batch-blog-posts.test.ts +87 -129
  223. package/test/budget-tracking.test.ts +800 -0
  224. package/test/cache.test.ts +712 -0
  225. package/test/context-isolation.test.ts +687 -0
  226. package/test/core-functions.test.ts +183 -579
  227. package/test/decide.test.ts +154 -322
  228. package/test/define.test.ts +211 -8
  229. package/test/digital-objects-registry.test.ts +760 -0
  230. package/test/embedding-cache-middleware.test.ts +140 -0
  231. package/test/evals/deterministic.eval.test.ts +376 -0
  232. package/test/generate-core.test.ts +140 -229
  233. package/test/implicit-batch.test.ts +22 -65
  234. package/test/json-parse-error-handling.test.ts +463 -0
  235. package/test/retry-policy-integration.test.ts +117 -0
  236. package/test/retry.test.ts +1016 -0
  237. package/test/schema.test.ts +55 -19
  238. package/test/streaming.test.ts +316 -0
  239. package/test/template.test.ts +1164 -0
  240. package/test/tool-orchestration.test.ts +1040 -0
  241. package/test/wrap-for-v3.test.ts +612 -0
  242. package/vitest.config.js +6 -0
  243. package/vitest.config.ts +20 -0
  244. package/dist/rpc/auth.d.ts +0 -69
  245. package/dist/rpc/auth.d.ts.map +0 -1
  246. package/dist/rpc/auth.js +0 -136
  247. package/dist/rpc/auth.js.map +0 -1
  248. package/dist/rpc/client.d.ts +0 -62
  249. package/dist/rpc/client.d.ts.map +0 -1
  250. package/dist/rpc/client.js +0 -103
  251. package/dist/rpc/client.js.map +0 -1
  252. package/dist/rpc/deferred.d.ts +0 -60
  253. package/dist/rpc/deferred.d.ts.map +0 -1
  254. package/dist/rpc/deferred.js +0 -96
  255. package/dist/rpc/deferred.js.map +0 -1
  256. package/dist/rpc/index.d.ts +0 -22
  257. package/dist/rpc/index.d.ts.map +0 -1
  258. package/dist/rpc/index.js +0 -38
  259. package/dist/rpc/index.js.map +0 -1
  260. package/dist/rpc/local.d.ts +0 -42
  261. package/dist/rpc/local.d.ts.map +0 -1
  262. package/dist/rpc/local.js +0 -50
  263. package/dist/rpc/local.js.map +0 -1
  264. package/dist/rpc/server.d.ts +0 -165
  265. package/dist/rpc/server.d.ts.map +0 -1
  266. package/dist/rpc/server.js +0 -405
  267. package/dist/rpc/server.js.map +0 -1
  268. package/dist/rpc/session.d.ts +0 -32
  269. package/dist/rpc/session.d.ts.map +0 -1
  270. package/dist/rpc/session.js +0 -43
  271. package/dist/rpc/session.js.map +0 -1
  272. package/dist/rpc/transport.d.ts +0 -306
  273. package/dist/rpc/transport.d.ts.map +0 -1
  274. package/dist/rpc/transport.js +0 -731
  275. package/dist/rpc/transport.js.map +0 -1
  276. package/src/batch/anthropic.js +0 -256
  277. package/src/batch/bedrock.js +0 -584
  278. package/src/batch/cloudflare.js +0 -287
  279. package/src/batch/google.js +0 -359
  280. package/src/batch/index.js +0 -30
  281. package/src/batch/memory.js +0 -187
  282. package/src/batch/openai.js +0 -402
  283. package/src/eval/index.js +0 -7
  284. package/src/eval/models.js +0 -119
  285. package/src/eval/runner.js +0 -147
  286. package/test/schema.test.js +0 -96
package/src/primitives.ts CHANGED
@@ -39,7 +39,23 @@ import {
39
39
  } from './ai-promise.js'
40
40
  import { generateObject, generateText } from './generate.js'
41
41
  import type { SimpleSchema } from './schema.js'
42
- import type { HumanChannel } from './types.js'
42
+ import type {
43
+ AgenticFunctionDefinition,
44
+ AutoDefineResult,
45
+ CodeFunctionDefinition,
46
+ DefinedFunction,
47
+ FunctionDefinition,
48
+ FunctionRegistry,
49
+ GenerativeFunctionDefinition,
50
+ HumanChannel,
51
+ HumanFunctionDefinition,
52
+ } from './types.js'
53
+ import {
54
+ createDefinedFunction,
55
+ defineFunction,
56
+ functions,
57
+ generateCode,
58
+ } from './function-registry.js'
43
59
 
44
60
  // ============================================================================
45
61
  // Types
@@ -122,42 +138,77 @@ export async function generate(
122
138
  }
123
139
 
124
140
  // Helper functions
125
- async function generateTextContent(prompt: string, model: string, options: GenerateOptions): Promise<string> {
126
- const result = await generateText({ model, prompt, system: options.system, temperature: options.temperature, maxTokens: options.maxTokens })
141
+ async function generateTextContent(
142
+ prompt: string,
143
+ model: string,
144
+ options: GenerateOptions
145
+ ): Promise<string> {
146
+ const result = await generateText({
147
+ model,
148
+ prompt,
149
+ ...(options.system !== undefined && { system: options.system }),
150
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
151
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
152
+ })
127
153
  return result.text
128
154
  }
129
155
 
130
- async function generateJsonContent(prompt: string, model: string, schema: SimpleSchema | undefined, options: GenerateOptions): Promise<unknown> {
156
+ async function generateJsonContent(
157
+ prompt: string,
158
+ model: string,
159
+ schema: SimpleSchema | undefined,
160
+ options: GenerateOptions
161
+ ): Promise<unknown> {
131
162
  const effectiveSchema = schema || { result: 'The generated result' }
132
- const result = await generateObject({ model, schema: effectiveSchema, prompt, system: options.system, temperature: options.temperature, maxTokens: options.maxTokens })
163
+ const result = await generateObject({
164
+ model,
165
+ schema: effectiveSchema,
166
+ prompt,
167
+ ...(options.system !== undefined && { system: options.system }),
168
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
169
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
170
+ })
133
171
  return result.object
134
172
  }
135
173
 
136
- async function generateCodeContent(prompt: string, model: string, language: string, options: GenerateOptions): Promise<string> {
174
+ async function generateCodeContent(
175
+ prompt: string,
176
+ model: string,
177
+ language: string,
178
+ options: GenerateOptions
179
+ ): Promise<string> {
137
180
  const result = await generateObject({
138
181
  model,
139
182
  schema: { code: `The ${language} implementation code` },
140
183
  prompt: `Generate ${language} code for: ${prompt}`,
141
184
  system: `You are an expert ${language} developer. Generate clean, well-documented code.`,
142
- temperature: options.temperature,
143
- maxTokens: options.maxTokens,
185
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
186
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
144
187
  })
145
188
  return (result.object as { code: string }).code
146
189
  }
147
190
 
148
- async function generateListContent(prompt: string, model: string, options: GenerateOptions): Promise<string[]> {
191
+ async function generateListContent(
192
+ prompt: string,
193
+ model: string,
194
+ options: GenerateOptions
195
+ ): Promise<string[]> {
149
196
  const result = await generateObject({
150
197
  model,
151
198
  schema: { items: ['List items'] },
152
199
  prompt,
153
200
  system: options.system || 'Generate a list of items based on the prompt.',
154
- temperature: options.temperature,
155
- maxTokens: options.maxTokens,
201
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
202
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
156
203
  })
157
204
  return (result.object as { items: string[] }).items
158
205
  }
159
206
 
160
- async function generateListsContent(prompt: string, model: string, options: GenerateOptions): Promise<Record<string, string[]>> {
207
+ async function generateListsContent(
208
+ prompt: string,
209
+ model: string,
210
+ options: GenerateOptions
211
+ ): Promise<Record<string, string[]>> {
161
212
  const result = await generateObject({
162
213
  model,
163
214
  schema: {
@@ -165,9 +216,11 @@ async function generateListsContent(prompt: string, model: string, options: Gene
165
216
  data: 'JSON string containing the categorized lists',
166
217
  },
167
218
  prompt: `Generate categorized lists for: ${prompt}\n\nFirst identify appropriate category names, then provide the lists as a JSON object.`,
168
- system: options.system || 'Generate multiple categorized lists. Determine appropriate categories based on the prompt.',
169
- temperature: options.temperature,
170
- maxTokens: options.maxTokens,
219
+ system:
220
+ options.system ||
221
+ 'Generate multiple categorized lists. Determine appropriate categories based on the prompt.',
222
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
223
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
171
224
  })
172
225
  const obj = result.object as { categories: string[]; data: string }
173
226
  try {
@@ -181,79 +234,114 @@ async function generateListsContent(prompt: string, model: string, options: Gene
181
234
  }
182
235
  }
183
236
 
184
- async function generateBooleanContent(prompt: string, model: string, options: GenerateOptions): Promise<boolean> {
237
+ async function generateBooleanContent(
238
+ prompt: string,
239
+ model: string,
240
+ options: GenerateOptions
241
+ ): Promise<boolean> {
185
242
  const result = await generateObject({
186
243
  model,
187
244
  schema: { answer: 'true | false' },
188
245
  prompt,
189
246
  system: options.system || 'Answer the question with true or false.',
190
247
  temperature: options.temperature ?? 0,
191
- maxTokens: options.maxTokens,
248
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
192
249
  })
193
250
  return (result.object as { answer: string }).answer === 'true'
194
251
  }
195
252
 
196
- async function generateSummaryContent(prompt: string, model: string, options: GenerateOptions): Promise<string> {
253
+ async function generateSummaryContent(
254
+ prompt: string,
255
+ model: string,
256
+ options: GenerateOptions
257
+ ): Promise<string> {
197
258
  const result = await generateObject({
198
259
  model,
199
260
  schema: { summary: 'A concise summary of the content' },
200
261
  prompt: `Summarize the following:\n\n${prompt}`,
201
262
  system: options.system || 'Create a clear, concise summary.',
202
- temperature: options.temperature,
203
- maxTokens: options.maxTokens,
263
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
264
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
204
265
  })
205
266
  return (result.object as { summary: string }).summary
206
267
  }
207
268
 
208
- async function generateExtractContent(prompt: string, model: string, schema: SimpleSchema | undefined, options: GenerateOptions): Promise<unknown[]> {
209
- const effectiveSchema = schema || { items: ['Extracted items'] }
269
+ async function generateExtractContent(
270
+ prompt: string,
271
+ model: string,
272
+ schema: SimpleSchema | undefined,
273
+ options: GenerateOptions
274
+ ): Promise<unknown[]> {
275
+ const effectiveSchema = schema || {
276
+ items: ['Array of extracted items as strings - extract ALL matching items from the text'],
277
+ }
210
278
  const result = await generateObject({
211
279
  model,
212
280
  schema: effectiveSchema,
213
- prompt: `Extract from the following:\n\n${prompt}`,
214
- system: options.system || 'Extract the requested information.',
215
- temperature: options.temperature,
216
- maxTokens: options.maxTokens,
281
+ prompt: `Extract the following from the text below. Return ALL matching items in the items array.
282
+
283
+ Task: ${prompt}
284
+
285
+ IMPORTANT: Return the extracted items as an array. If the task asks for email addresses, return all email addresses found. If it asks for names, return all names found. Do not return an empty array if there are items to extract.`,
286
+ system:
287
+ options.system ||
288
+ 'You are a precise data extraction assistant. Extract exactly what is requested and return it as an array of items. Be thorough - find ALL matching items in the text.',
289
+ temperature: options.temperature ?? 0, // Use low temperature for extraction tasks
290
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
217
291
  })
218
292
  const obj = result.object as Record<string, unknown>
219
- if ('items' in obj && Array.isArray(obj.items)) {
220
- return obj.items
293
+ if ('items' in obj && Array.isArray(obj['items'])) {
294
+ return obj['items']
221
295
  }
222
296
  return Object.values(obj).flat() as unknown[]
223
297
  }
224
298
 
225
- async function generateYamlContent(prompt: string, model: string, options: GenerateOptions): Promise<string> {
299
+ async function generateYamlContent(
300
+ prompt: string,
301
+ model: string,
302
+ options: GenerateOptions
303
+ ): Promise<string> {
226
304
  const result = await generateObject({
227
305
  model,
228
306
  schema: { yaml: 'The YAML content' },
229
307
  prompt: `Generate YAML for: ${prompt}`,
230
308
  system: options.system || 'Generate valid YAML content.',
231
- temperature: options.temperature,
232
- maxTokens: options.maxTokens,
309
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
310
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
233
311
  })
234
312
  return (result.object as { yaml: string }).yaml
235
313
  }
236
314
 
237
- async function generateDiagramContent(prompt: string, model: string, format: string, options: GenerateOptions): Promise<string> {
315
+ async function generateDiagramContent(
316
+ prompt: string,
317
+ model: string,
318
+ format: string,
319
+ options: GenerateOptions
320
+ ): Promise<string> {
238
321
  const result = await generateObject({
239
322
  model,
240
323
  schema: { diagram: `The ${format} diagram code` },
241
324
  prompt: `Generate a ${format} diagram for: ${prompt}`,
242
325
  system: options.system || `Generate ${format} diagram syntax.`,
243
- temperature: options.temperature,
244
- maxTokens: options.maxTokens,
326
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
327
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
245
328
  })
246
329
  return (result.object as { diagram: string }).diagram
247
330
  }
248
331
 
249
- async function generateSlidesContent(prompt: string, model: string, slideCount: number, options: GenerateOptions): Promise<string> {
332
+ async function generateSlidesContent(
333
+ prompt: string,
334
+ model: string,
335
+ slideCount: number,
336
+ options: GenerateOptions
337
+ ): Promise<string> {
250
338
  const result = await generateObject({
251
339
  model,
252
340
  schema: { slides: `Slidev/Marp markdown with ${slideCount} slides` },
253
341
  prompt: `Generate a ${slideCount}-slide presentation about: ${prompt}`,
254
342
  system: options.system || 'Generate markdown slides in Slidev/Marp format.',
255
- temperature: options.temperature,
256
- maxTokens: options.maxTokens,
343
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
344
+ ...(options.maxTokens !== undefined && { maxTokens: options.maxTokens }),
257
345
  })
258
346
  return (result.object as { slides: string }).slides
259
347
  }
@@ -445,17 +533,14 @@ function doImpl(
445
533
  prompt = promptOrStrings as string
446
534
  }
447
535
 
448
- const promise = new AIPromise<{ summary: string; actions: string[] }>(
449
- prompt,
450
- {
451
- type: 'object',
452
- baseSchema: {
453
- summary: 'Summary of what was done',
454
- actions: ['List of actions taken'],
455
- },
456
- system: 'You are a task executor. Describe what actions you would take.',
457
- }
458
- )
536
+ const promise = new AIPromise<{ summary: string; actions: string[] }>(prompt, {
537
+ type: 'object',
538
+ baseSchema: {
539
+ summary: 'Summary of what was done',
540
+ actions: ['List of actions taken'],
541
+ },
542
+ system: 'You are a task executor. Describe what actions you would take.',
543
+ })
459
544
 
460
545
  for (const dep of dependencies) {
461
546
  promise.addDependency(dep.promise, dep.path)
@@ -474,7 +559,11 @@ export { doImpl as do }
474
559
  * const { summary, findings, sources } = await research`${competitor} vs our product`
475
560
  * ```
476
561
  */
477
- export const research = createAITemplateFunction<{ summary: string; findings: string[]; sources: string[] }>('object', {
562
+ export const research = createAITemplateFunction<{
563
+ summary: string
564
+ findings: string[]
565
+ sources: string[]
566
+ }>('object', {
478
567
  system: 'You are a research analyst. Provide thorough research.',
479
568
  })
480
569
 
@@ -545,10 +634,7 @@ export function decide(
545
634
  let criteria: string
546
635
 
547
636
  if (Array.isArray(criteriaOrStrings) && 'raw' in criteriaOrStrings) {
548
- criteria = criteriaOrStrings.reduce(
549
- (acc, str, i) => acc + str + (templateArgs[i] ?? ''),
550
- ''
551
- )
637
+ criteria = criteriaOrStrings.reduce((acc, str, i) => acc + str + (templateArgs[i] ?? ''), '')
552
638
  } else {
553
639
  criteria = criteriaOrStrings as string
554
640
  }
@@ -571,11 +657,12 @@ export function decide(
571
657
 
572
658
  // Override resolve to return the actual option
573
659
  const originalResolve = promise.resolve.bind(promise)
574
- ;(promise as any).resolve = async () => {
575
- const result = await originalResolve() as { chosenIndex: string | number }
576
- const index = typeof result.chosenIndex === 'string'
577
- ? parseInt(result.chosenIndex, 10)
578
- : result.chosenIndex
660
+ ;(promise as { resolve: () => Promise<T> }).resolve = async () => {
661
+ const result = (await originalResolve()) as { chosenIndex: string | number }
662
+ const index =
663
+ typeof result.chosenIndex === 'string'
664
+ ? parseInt(result.chosenIndex, 10)
665
+ : result.chosenIndex
579
666
  return options[index - 1] as T
580
667
  }
581
668
 
@@ -618,13 +705,453 @@ export const ask = createAITemplateFunction<HumanResult<string>>('object', {
618
705
  /**
619
706
  * Request human approval
620
707
  */
621
- export const approve = createAITemplateFunction<HumanResult<{ approved: boolean; notes?: string }>>('object', {
622
- system: 'Generate an approval request.',
623
- })
708
+ export const approve = createAITemplateFunction<HumanResult<{ approved: boolean; notes?: string }>>(
709
+ 'object',
710
+ {
711
+ system: 'Generate an approval request.',
712
+ }
713
+ )
624
714
 
625
715
  /**
626
716
  * Request human review
627
717
  */
628
- export const review = createAITemplateFunction<HumanResult<{ rating?: number; feedback: string; approved?: boolean }>>('object', {
718
+ export const review = createAITemplateFunction<
719
+ HumanResult<{ rating?: number; feedback: string; approved?: boolean }>
720
+ >('object', {
629
721
  system: 'Generate a review request.',
630
722
  })
723
+
724
+ // ============================================================================
725
+ // Auto-Define Functions
726
+ //
727
+ // Inlined from former ai-proxy.ts. These helpers provide the auto-define
728
+ // convenience layer (analyze a name + example args, infer a function type and
729
+ // schema, register it). Property-access tracking lives entirely in
730
+ // ai-promise.ts; this section is a shallow dispatch over define() and
731
+ // generateObject().
732
+ // ============================================================================
733
+
734
+ /**
735
+ * Analyze a function call and determine what type of function it should be
736
+ */
737
+ async function analyzeFunction(
738
+ name: string,
739
+ args: Record<string, unknown>
740
+ ): Promise<AutoDefineResult> {
741
+ // Convert camelCase/snake_case to readable name
742
+ const readableName = name
743
+ .replace(/([A-Z])/g, ' $1')
744
+ .replace(/_/g, ' ')
745
+ .toLowerCase()
746
+ .trim()
747
+
748
+ const argDescriptions = Object.entries(args)
749
+ .map(([key, value]) => {
750
+ const type = Array.isArray(value) ? 'array' : typeof value
751
+ return ` - ${key}: ${type} (example: ${JSON.stringify(value).slice(0, 50)})`
752
+ })
753
+ .join('\n')
754
+
755
+ const result = await generateObject({
756
+ model: 'sonnet',
757
+ schema: {
758
+ type: 'code | generative | agentic | human',
759
+ reasoning: 'Why this function type is appropriate (1-2 sentences)',
760
+ description: 'What this function does',
761
+ output: 'string | object | image | video | audio',
762
+ returnType: 'Schema for the return type as a SimpleSchema object',
763
+ system: 'System prompt for the AI (if generative/agentic)',
764
+ promptTemplate: 'Prompt template with {{arg}} placeholders',
765
+ instructions: 'Instructions for agentic/human functions',
766
+ needsTools: 'true | false',
767
+ suggestedTools: ['Names of tools that might be needed'],
768
+ channel: 'slack | email | web | sms | custom',
769
+ },
770
+ system: `You are an expert at designing AI functions. Analyze the function name and arguments to determine the best function type.
771
+
772
+ Function Types:
773
+ - "code": A DETERMINISTIC algorithm with no model at run time — a pure calculation, data transformation, or rule that always produces the same output for the same input (e.g. tax math, parsing, formatting). A self-contained TypeScript body can be written for it once.
774
+ - "generative": For generating content (text, summaries, translations, creative writing, structured data) — needs a model on every call
775
+ - "agentic": For complex tasks requiring multiple steps, research, or tool use (research, planning, multi-step workflows)
776
+ - "human": For tasks requiring human judgment, approval, or input (approvals, reviews, decisions)
777
+
778
+ Guidelines:
779
+ - Most functions should be "generative" - they generate content or structured data
780
+ - Use "code" ONLY when the task is a deterministic algorithm expressible as a self-contained function body (no model, no external state). If it needs a model at run time, it is "generative", not "code".
781
+ - Use "agentic" when the task requires research, multiple steps, or external tool use
782
+ - Use "human" when human judgment/approval is essential`,
783
+ prompt: `Analyze this function call and determine how to define it:
784
+
785
+ Function Name: ${name}
786
+ Readable Name: ${readableName}
787
+ Arguments:
788
+ ${argDescriptions || ' (no arguments)'}
789
+
790
+ Determine:
791
+ 1. What type of function this should be
792
+ 2. What it should return
793
+ 3. How it should be implemented`,
794
+ })
795
+
796
+ const analysis = result.object as {
797
+ type: string
798
+ reasoning: string
799
+ description: string
800
+ output: string
801
+ returnType: unknown
802
+ system: string
803
+ promptTemplate: string
804
+ instructions: string
805
+ needsTools: string
806
+ suggestedTools: string[]
807
+ channel: string
808
+ }
809
+
810
+ // Build the function definition based on the analysis
811
+ let definition: FunctionDefinition
812
+
813
+ const baseDefinition = {
814
+ name,
815
+ description: analysis.description,
816
+ args: inferArgsSchema(args),
817
+ returnType: analysis.returnType as SimpleSchema,
818
+ }
819
+
820
+ switch (analysis.type) {
821
+ case 'code': {
822
+ // `Code` is deterministic — it cannot be a call-time model invocation.
823
+ // Author a self-contained TS body ONCE here (at define time), then carry
824
+ // it as an inline `code` body so every subsequent call is deterministic.
825
+ const authored = await generateCode(
826
+ {
827
+ name,
828
+ description: analysis.description,
829
+ args: baseDefinition.args,
830
+ returnType: baseDefinition.returnType,
831
+ language: 'typescript',
832
+ instructions: `${analysis.instructions ?? ''}\n\nWrite the body of a single function that receives a parameter named \`args\` (an object with the keys above) and \`return\`s the result. Do not include the function signature, imports, or surrounding declarations — only the statements that go inside the function body.`.trim(),
833
+ },
834
+ args
835
+ )
836
+ definition = {
837
+ ...baseDefinition,
838
+ type: 'code' as const,
839
+ language: 'typescript' as const,
840
+ instructions: analysis.instructions,
841
+ code: authored,
842
+ }
843
+ break
844
+ }
845
+
846
+ case 'agentic':
847
+ definition = {
848
+ ...baseDefinition,
849
+ type: 'agentic' as const,
850
+ instructions: analysis.instructions || `Complete the ${readableName} task`,
851
+ promptTemplate: analysis.promptTemplate,
852
+ tools: [], // Tools would need to be provided separately
853
+ maxIterations: 10,
854
+ }
855
+ break
856
+
857
+ case 'human':
858
+ definition = {
859
+ ...baseDefinition,
860
+ type: 'human' as const,
861
+ channel: (analysis.channel || 'web') as HumanChannel,
862
+ instructions:
863
+ analysis.instructions || `Please review and respond to this ${readableName} request`,
864
+ promptTemplate: analysis.promptTemplate,
865
+ }
866
+ break
867
+
868
+ case 'generative':
869
+ default:
870
+ definition = {
871
+ ...baseDefinition,
872
+ type: 'generative' as const,
873
+ output: (analysis.output || 'object') as 'string' | 'object' | 'image' | 'video',
874
+ system: analysis.system,
875
+ promptTemplate: analysis.promptTemplate || `{{${Object.keys(args)[0] || 'input'}}}`,
876
+ }
877
+ break
878
+ }
879
+
880
+ return {
881
+ type: analysis.type as 'code' | 'generative' | 'agentic' | 'human',
882
+ reasoning: analysis.reasoning,
883
+ definition,
884
+ }
885
+ }
886
+
887
+ /**
888
+ * Infer a schema from example arguments
889
+ */
890
+ function inferArgsSchema(
891
+ args: Record<string, unknown>
892
+ ): Record<string, string | string[] | Record<string, unknown>> {
893
+ const schema: Record<string, string | string[] | Record<string, unknown>> = {}
894
+
895
+ for (const [key, value] of Object.entries(args)) {
896
+ if (typeof value === 'string') {
897
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
898
+ } else if (typeof value === 'number') {
899
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (number)`
900
+ } else if (typeof value === 'boolean') {
901
+ schema[key] = `Whether ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (boolean)`
902
+ } else if (Array.isArray(value)) {
903
+ if (value.length > 0 && typeof value[0] === 'string') {
904
+ schema[key] = [`List of ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
905
+ } else {
906
+ schema[key] = [`Items for ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
907
+ }
908
+ } else if (typeof value === 'object' && value !== null) {
909
+ schema[key] = inferArgsSchema(value as Record<string, unknown>)
910
+ } else {
911
+ schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
912
+ }
913
+ }
914
+
915
+ return schema
916
+ }
917
+
918
+ /**
919
+ * Auto-define a function based on its name and arguments, or define with explicit definition
920
+ *
921
+ * When called with (name, args), uses AI to analyze and determine:
922
+ * - What type of function it should be (code, generative, agentic, human)
923
+ * - What it should return
924
+ * - How it should be implemented
925
+ *
926
+ * When called with a FunctionDefinition, creates the function directly.
927
+ *
928
+ * @example
929
+ * ```ts
930
+ * // Auto-define from name and example args
931
+ * const planTrip = await define('planTrip', { destination: 'Tokyo', travelers: 2 })
932
+ *
933
+ * // Or define explicitly
934
+ * const summarize = define.generative({
935
+ * name: 'summarize',
936
+ * args: { text: 'Text to summarize' },
937
+ * output: 'string',
938
+ * })
939
+ *
940
+ * // Or with full definition
941
+ * const fn = defineFunction({
942
+ * type: 'generative',
943
+ * name: 'translate',
944
+ * args: { text: 'Text', lang: 'Target language' },
945
+ * output: 'string',
946
+ * })
947
+ * ```
948
+ */
949
+ async function autoDefineImpl(
950
+ name: string,
951
+ args: Record<string, unknown>
952
+ ): Promise<DefinedFunction> {
953
+ // Check if already defined
954
+ const existing = functions.get(name)
955
+ if (existing) {
956
+ return existing
957
+ }
958
+
959
+ // Analyze and define the function
960
+ const { definition } = await analyzeFunction(name, args)
961
+
962
+ // Create the defined function
963
+ const definedFn = createDefinedFunction(definition)
964
+
965
+ // Store in registry
966
+ functions.set(name, definedFn)
967
+
968
+ return definedFn
969
+ }
970
+
971
+ /**
972
+ * Define functions - auto-define or use typed helpers
973
+ */
974
+ export const define = Object.assign(autoDefineImpl, {
975
+ /**
976
+ * Define a **deterministic** code function (a handler — no LLM at call time).
977
+ *
978
+ * Supply a `handler` (canonical) or an inline `code` body. To have a model
979
+ * *author* code instead, use {@link generateCode} or `define.generative`.
980
+ */
981
+ code: <TOutput, TInput>(
982
+ definition: Omit<CodeFunctionDefinition<TOutput, TInput>, 'type'>
983
+ ): DefinedFunction<TOutput, TInput> => {
984
+ const fn = defineFunction({ type: 'code', ...definition } as CodeFunctionDefinition<
985
+ TOutput,
986
+ TInput
987
+ >)
988
+ functions.set(definition.name, fn as DefinedFunction)
989
+ return fn
990
+ },
991
+
992
+ /**
993
+ * Define a generative function
994
+ */
995
+ generative: <TOutput, TInput>(
996
+ definition: Omit<GenerativeFunctionDefinition<TOutput, TInput>, 'type'>
997
+ ): DefinedFunction<TOutput, TInput> => {
998
+ const fn = defineFunction({ type: 'generative', ...definition } as GenerativeFunctionDefinition<
999
+ TOutput,
1000
+ TInput
1001
+ >)
1002
+ functions.set(definition.name, fn as DefinedFunction)
1003
+ return fn
1004
+ },
1005
+
1006
+ /**
1007
+ * Define an agentic function
1008
+ */
1009
+ agentic: <TOutput, TInput>(
1010
+ definition: Omit<AgenticFunctionDefinition<TOutput, TInput>, 'type'>
1011
+ ): DefinedFunction<TOutput, TInput> => {
1012
+ const fn = defineFunction({ type: 'agentic', ...definition } as AgenticFunctionDefinition<
1013
+ TOutput,
1014
+ TInput
1015
+ >)
1016
+ functions.set(definition.name, fn as DefinedFunction)
1017
+ return fn
1018
+ },
1019
+
1020
+ /**
1021
+ * Define a human-in-the-loop function
1022
+ */
1023
+ human: <TOutput, TInput>(
1024
+ definition: Omit<HumanFunctionDefinition<TOutput, TInput>, 'type'>
1025
+ ): DefinedFunction<TOutput, TInput> => {
1026
+ const fn = defineFunction({ type: 'human', ...definition } as HumanFunctionDefinition<
1027
+ TOutput,
1028
+ TInput
1029
+ >)
1030
+ functions.set(definition.name, fn as DefinedFunction)
1031
+ return fn
1032
+ },
1033
+ })
1034
+
1035
+ // ============================================================================
1036
+ // AI Proxy - Smart AI Client with Auto-Definition
1037
+ // ============================================================================
1038
+
1039
+ /** Known built-in method names that should not be auto-defined */
1040
+ const BUILTIN_METHODS = new Set([
1041
+ 'do',
1042
+ 'is',
1043
+ 'code',
1044
+ 'decide',
1045
+ 'diagram',
1046
+ 'generate',
1047
+ 'image',
1048
+ 'video',
1049
+ 'write',
1050
+ 'list',
1051
+ 'lists',
1052
+ 'functions',
1053
+ 'define',
1054
+ 'defineFunction',
1055
+ 'then',
1056
+ 'catch',
1057
+ 'finally',
1058
+ ])
1059
+
1060
+ /**
1061
+ * Type for the AI proxy with auto-define capability
1062
+ */
1063
+ export interface AIProxy {
1064
+ /** Function registry */
1065
+ functions: FunctionRegistry
1066
+ /** Define functions */
1067
+ define: typeof define
1068
+ /** Define a function with full definition */
1069
+ defineFunction: typeof defineFunction
1070
+ /** Dynamic function calls */
1071
+ [key: string]: unknown
1072
+ }
1073
+
1074
+ /**
1075
+ * Create a smart AI client that auto-defines functions on first call
1076
+ *
1077
+ * @example
1078
+ * ```ts
1079
+ * const ai = createSmartAI()
1080
+ *
1081
+ * // First call - auto-defines the function
1082
+ * const trip = await ai.planTrip({
1083
+ * destination: 'Tokyo',
1084
+ * dates: { start: '2024-03-01', end: '2024-03-10' },
1085
+ * travelers: 2,
1086
+ * })
1087
+ *
1088
+ * // Second call - uses cached definition (in-memory)
1089
+ * const trip2 = await ai.planTrip({
1090
+ * destination: 'Paris',
1091
+ * dates: { start: '2024-06-01', end: '2024-06-07' },
1092
+ * travelers: 4,
1093
+ * })
1094
+ *
1095
+ * // Access registry and define
1096
+ * console.log(ai.functions.list()) // ['planTrip']
1097
+ * ai.define.generative({ name: 'summarize', ... })
1098
+ * ```
1099
+ */
1100
+ export function createSmartAI(): AIProxy {
1101
+ const base = {
1102
+ functions,
1103
+ define,
1104
+ defineFunction,
1105
+ }
1106
+
1107
+ return new Proxy(base as AIProxy, {
1108
+ get(target, prop: string) {
1109
+ // Return built-in properties
1110
+ if (prop in target) {
1111
+ return (target as Record<string, unknown>)[prop]
1112
+ }
1113
+
1114
+ // Skip internal properties
1115
+ if (typeof prop === 'symbol' || prop.startsWith('_') || BUILTIN_METHODS.has(prop)) {
1116
+ return undefined
1117
+ }
1118
+
1119
+ // Return a function that auto-defines and calls
1120
+ return async (args: Record<string, unknown> = {}) => {
1121
+ // Check if function is already defined
1122
+ let fn = functions.get(prop)
1123
+
1124
+ if (!fn) {
1125
+ // Auto-define the function
1126
+ fn = await define(prop, args)
1127
+ }
1128
+
1129
+ // Call the function
1130
+ return fn.call(args)
1131
+ }
1132
+ },
1133
+ })
1134
+ }
1135
+
1136
+ /**
1137
+ * Default AI proxy instance with auto-define capability.
1138
+ *
1139
+ * This is the smart proxy `aiProxy` (re-exported from index.ts as `aiProxy`).
1140
+ * It is intentionally distinct from the `ai` template-tag primitive above —
1141
+ * see `ai` (line ~354) for the template function used like `ai\`...\``.
1142
+ *
1143
+ * @example
1144
+ * ```ts
1145
+ * import { aiProxy } from 'ai-functions'
1146
+ *
1147
+ * // Auto-define and call
1148
+ * const result = await aiProxy.summarize({ text: 'Long article...' })
1149
+ *
1150
+ * // Access functions registry
1151
+ * aiProxy.functions.list()
1152
+ *
1153
+ * // Define explicitly
1154
+ * aiProxy.define.generative({ name: 'translate', ... })
1155
+ * ```
1156
+ */
1157
+ export const aiProxy: AIProxy = createSmartAI()