ai-functions 2.1.3 → 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 (277) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +55 -1
  3. package/README.md +38 -0
  4. package/dist/ai-promise.d.ts +3 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +135 -64
  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 +51 -858
  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.map +1 -1
  56. package/dist/budget.js +27 -14
  57. package/dist/budget.js.map +1 -1
  58. package/dist/cache.d.ts +23 -0
  59. package/dist/cache.d.ts.map +1 -1
  60. package/dist/cache.js +36 -15
  61. package/dist/cache.js.map +1 -1
  62. package/dist/context.d.ts +26 -8
  63. package/dist/context.d.ts.map +1 -1
  64. package/dist/context.js +64 -62
  65. package/dist/context.js.map +1 -1
  66. package/dist/digital-objects-registry.d.ts +229 -0
  67. package/dist/digital-objects-registry.d.ts.map +1 -0
  68. package/dist/digital-objects-registry.js +617 -0
  69. package/dist/digital-objects-registry.js.map +1 -0
  70. package/dist/embeddings.d.ts +2 -2
  71. package/dist/embeddings.d.ts.map +1 -1
  72. package/dist/errors.d.ts +22 -0
  73. package/dist/errors.d.ts.map +1 -0
  74. package/dist/errors.js +35 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/eval/runner.d.ts +8 -0
  77. package/dist/eval/runner.d.ts.map +1 -1
  78. package/dist/eval/runner.js +41 -35
  79. package/dist/eval/runner.js.map +1 -1
  80. package/dist/eval-log/in-memory.d.ts +34 -0
  81. package/dist/eval-log/in-memory.d.ts.map +1 -0
  82. package/dist/eval-log/in-memory.js +84 -0
  83. package/dist/eval-log/in-memory.js.map +1 -0
  84. package/dist/eval-log/index.d.ts +29 -0
  85. package/dist/eval-log/index.d.ts.map +1 -0
  86. package/dist/eval-log/index.js +39 -0
  87. package/dist/eval-log/index.js.map +1 -0
  88. package/dist/eval-log/types.d.ts +101 -0
  89. package/dist/eval-log/types.d.ts.map +1 -0
  90. package/dist/eval-log/types.js +16 -0
  91. package/dist/eval-log/types.js.map +1 -0
  92. package/dist/function-registry.d.ts +116 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +546 -0
  95. package/dist/function-registry.js.map +1 -0
  96. package/dist/generate.d.ts +9 -3
  97. package/dist/generate.d.ts.map +1 -1
  98. package/dist/generate.js +18 -18
  99. package/dist/generate.js.map +1 -1
  100. package/dist/index.d.ts +18 -11
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +35 -18
  103. package/dist/index.js.map +1 -1
  104. package/dist/logger.d.ts +118 -0
  105. package/dist/logger.d.ts.map +1 -0
  106. package/dist/logger.js +187 -0
  107. package/dist/logger.js.map +1 -0
  108. package/dist/middleware/budget.d.ts +84 -0
  109. package/dist/middleware/budget.d.ts.map +1 -0
  110. package/dist/middleware/budget.js +110 -0
  111. package/dist/middleware/budget.js.map +1 -0
  112. package/dist/middleware/cache.d.ts +103 -0
  113. package/dist/middleware/cache.d.ts.map +1 -0
  114. package/dist/middleware/cache.js +228 -0
  115. package/dist/middleware/cache.js.map +1 -0
  116. package/dist/middleware/embed-cache.d.ts +99 -0
  117. package/dist/middleware/embed-cache.d.ts.map +1 -0
  118. package/dist/middleware/embed-cache.js +128 -0
  119. package/dist/middleware/embed-cache.js.map +1 -0
  120. package/dist/middleware/index.d.ts +11 -0
  121. package/dist/middleware/index.d.ts.map +1 -0
  122. package/dist/middleware/index.js +11 -0
  123. package/dist/middleware/index.js.map +1 -0
  124. package/dist/middleware/trace.d.ts +103 -0
  125. package/dist/middleware/trace.d.ts.map +1 -0
  126. package/dist/middleware/trace.js +176 -0
  127. package/dist/middleware/trace.js.map +1 -0
  128. package/dist/primitives.d.ts +120 -1
  129. package/dist/primitives.d.ts.map +1 -1
  130. package/dist/primitives.js +398 -26
  131. package/dist/primitives.js.map +1 -1
  132. package/dist/retry.d.ts +66 -1
  133. package/dist/retry.d.ts.map +1 -1
  134. package/dist/retry.js +115 -8
  135. package/dist/retry.js.map +1 -1
  136. package/dist/schema.js +2 -2
  137. package/dist/schema.js.map +1 -1
  138. package/dist/telemetry.d.ts +128 -0
  139. package/dist/telemetry.d.ts.map +1 -0
  140. package/dist/telemetry.js +285 -0
  141. package/dist/telemetry.js.map +1 -0
  142. package/dist/template.d.ts.map +1 -1
  143. package/dist/template.js +6 -1
  144. package/dist/template.js.map +1 -1
  145. package/dist/tool-orchestration.d.ts +66 -4
  146. package/dist/tool-orchestration.d.ts.map +1 -1
  147. package/dist/tool-orchestration.js +123 -23
  148. package/dist/tool-orchestration.js.map +1 -1
  149. package/dist/type-guards.d.ts +28 -0
  150. package/dist/type-guards.d.ts.map +1 -0
  151. package/dist/type-guards.js +29 -0
  152. package/dist/type-guards.js.map +1 -0
  153. package/dist/types.d.ts +135 -17
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +36 -1
  156. package/dist/types.js.map +1 -1
  157. package/dist/wrap-for-v3.d.ts +80 -0
  158. package/dist/wrap-for-v3.d.ts.map +1 -0
  159. package/dist/wrap-for-v3.js +89 -0
  160. package/dist/wrap-for-v3.js.map +1 -0
  161. package/examples/00-quickstart.ts +232 -0
  162. package/examples/01-rag-chatbot.ts +212 -0
  163. package/examples/02-multi-agent-research.ts +290 -0
  164. package/examples/03-email-classification.ts +379 -0
  165. package/examples/04-content-moderation.ts +400 -0
  166. package/examples/05-document-extraction.ts +455 -0
  167. package/examples/06-streaming-chat-nextjs.ts +437 -0
  168. package/examples/07-cloudflare-worker.ts +483 -0
  169. package/examples/08-batch-processing.ts +491 -0
  170. package/examples/09-budget-constrained.ts +527 -0
  171. package/examples/10-tool-orchestration.ts +565 -0
  172. package/examples/11-retry-resilience.ts +403 -0
  173. package/examples/12-caching-strategies.ts +422 -0
  174. package/examples/README.md +145 -0
  175. package/package.json +28 -25
  176. package/src/ai-promise.ts +226 -140
  177. package/src/ai-schemas.ts +122 -0
  178. package/src/ai.ts +69 -1176
  179. package/src/batch/anthropic.ts +96 -161
  180. package/src/batch/bedrock.ts +203 -454
  181. package/src/batch/cloudflare.ts +99 -282
  182. package/src/batch/google.ts +91 -297
  183. package/src/batch/index.ts +4 -1
  184. package/src/batch/memory.ts +15 -10
  185. package/src/batch/openai.ts +65 -193
  186. package/src/batch/provider.ts +336 -0
  187. package/src/batch-map.ts +29 -24
  188. package/src/batch-queue.ts +200 -11
  189. package/src/budget.ts +31 -18
  190. package/src/cache.ts +45 -17
  191. package/src/context.ts +106 -77
  192. package/src/digital-objects-registry.ts +750 -0
  193. package/src/errors.ts +37 -0
  194. package/src/eval/runner.ts +60 -36
  195. package/src/eval-log/in-memory.ts +90 -0
  196. package/src/eval-log/index.ts +46 -0
  197. package/src/eval-log/types.ts +110 -0
  198. package/src/function-registry.ts +671 -0
  199. package/src/generate.ts +33 -28
  200. package/src/index.ts +119 -21
  201. package/src/logger.ts +232 -0
  202. package/src/middleware/budget.ts +171 -0
  203. package/src/middleware/cache.ts +299 -0
  204. package/src/middleware/embed-cache.ts +195 -0
  205. package/src/middleware/index.ts +23 -0
  206. package/src/middleware/trace.ts +248 -0
  207. package/src/primitives.ts +589 -62
  208. package/src/retry.ts +144 -18
  209. package/src/schema.ts +8 -8
  210. package/src/telemetry.ts +403 -0
  211. package/src/template.ts +8 -4
  212. package/src/tool-orchestration.ts +213 -48
  213. package/src/type-guards.ts +31 -0
  214. package/src/types.ts +164 -25
  215. package/src/wrap-for-v3.ts +105 -0
  216. package/test/ai-promise.test.ts +1080 -0
  217. package/test/ai-proxy.test.ts +1 -1
  218. package/test/batch-autosubmit-errors.test.ts +49 -37
  219. package/test/batch-blog-posts.test.ts +87 -129
  220. package/test/core-functions.test.ts +183 -579
  221. package/test/decide.test.ts +154 -322
  222. package/test/define.test.ts +211 -8
  223. package/test/digital-objects-registry.test.ts +760 -0
  224. package/test/embedding-cache-middleware.test.ts +140 -0
  225. package/test/generate-core.test.ts +140 -229
  226. package/test/implicit-batch.test.ts +22 -65
  227. package/test/retry-policy-integration.test.ts +117 -0
  228. package/test/schema.test.ts +55 -19
  229. package/test/template.test.ts +1164 -0
  230. package/test/tool-orchestration.test.ts +270 -0
  231. package/test/wrap-for-v3.test.ts +612 -0
  232. package/vitest.config.js +6 -0
  233. package/vitest.config.ts +20 -0
  234. package/LICENSE +0 -21
  235. package/dist/rpc/auth.d.ts +0 -69
  236. package/dist/rpc/auth.d.ts.map +0 -1
  237. package/dist/rpc/auth.js +0 -136
  238. package/dist/rpc/auth.js.map +0 -1
  239. package/dist/rpc/client.d.ts +0 -62
  240. package/dist/rpc/client.d.ts.map +0 -1
  241. package/dist/rpc/client.js +0 -103
  242. package/dist/rpc/client.js.map +0 -1
  243. package/dist/rpc/deferred.d.ts +0 -60
  244. package/dist/rpc/deferred.d.ts.map +0 -1
  245. package/dist/rpc/deferred.js +0 -96
  246. package/dist/rpc/deferred.js.map +0 -1
  247. package/dist/rpc/index.d.ts +0 -22
  248. package/dist/rpc/index.d.ts.map +0 -1
  249. package/dist/rpc/index.js +0 -38
  250. package/dist/rpc/index.js.map +0 -1
  251. package/dist/rpc/local.d.ts +0 -42
  252. package/dist/rpc/local.d.ts.map +0 -1
  253. package/dist/rpc/local.js +0 -50
  254. package/dist/rpc/local.js.map +0 -1
  255. package/dist/rpc/server.d.ts +0 -165
  256. package/dist/rpc/server.d.ts.map +0 -1
  257. package/dist/rpc/server.js +0 -405
  258. package/dist/rpc/server.js.map +0 -1
  259. package/dist/rpc/session.d.ts +0 -32
  260. package/dist/rpc/session.d.ts.map +0 -1
  261. package/dist/rpc/session.js +0 -43
  262. package/dist/rpc/session.js.map +0 -1
  263. package/dist/rpc/transport.d.ts +0 -306
  264. package/dist/rpc/transport.d.ts.map +0 -1
  265. package/dist/rpc/transport.js +0 -731
  266. package/dist/rpc/transport.js.map +0 -1
  267. package/src/batch/anthropic.js +0 -256
  268. package/src/batch/bedrock.js +0 -584
  269. package/src/batch/cloudflare.js +0 -287
  270. package/src/batch/google.js +0 -359
  271. package/src/batch/index.js +0 -30
  272. package/src/batch/memory.js +0 -187
  273. package/src/batch/openai.js +0 -402
  274. package/src/eval/index.js +0 -7
  275. package/src/eval/models.js +0 -119
  276. package/src/eval/runner.js +0 -147
  277. package/test/schema.test.js +0 -96
@@ -0,0 +1,403 @@
1
+ /**
2
+ * OpenTelemetry Integration for ai-functions
3
+ *
4
+ * Provides instrumented wrappers and telemetry utilities for AI function calls.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { withTelemetry, instrumentGenerate } from 'ai-functions/telemetry'
9
+ *
10
+ * // Enable telemetry globally
11
+ * withTelemetry({ provider: createConsoleTelemetryProvider() }, async () => {
12
+ * const text = await generate('Explain quantum computing')
13
+ * })
14
+ *
15
+ * // Or instrument individual functions
16
+ * const traced = instrumentGenerate(generate)
17
+ * const text = await traced('Explain quantum computing')
18
+ * ```
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+
23
+ import {
24
+ getTracer,
25
+ getMeter,
26
+ getLogger,
27
+ setTelemetryProvider,
28
+ getTelemetryProvider,
29
+ createAIMetrics,
30
+ SemanticAttributes,
31
+ MetricNames,
32
+ type Tracer,
33
+ type Meter,
34
+ type Logger,
35
+ type Span,
36
+ type SpanAttributes,
37
+ type TelemetryProvider,
38
+ type Counter,
39
+ type Histogram,
40
+ } from '@org.ai/types'
41
+
42
+ // Package info
43
+ const PACKAGE_NAME = 'ai-functions'
44
+ const PACKAGE_VERSION = '2.1.4'
45
+
46
+ // ============================================================================
47
+ // Package-level Telemetry
48
+ // ============================================================================
49
+
50
+ let packageTracer: Tracer | undefined
51
+ let packageMeter: Meter | undefined
52
+ let packageLogger: Logger | undefined
53
+ let aiMetrics: ReturnType<typeof createAIMetrics> | undefined
54
+
55
+ /**
56
+ * Get the tracer for ai-functions
57
+ */
58
+ export function getFunctionsTracer(): Tracer {
59
+ if (!packageTracer) {
60
+ packageTracer = getTracer(PACKAGE_NAME, PACKAGE_VERSION)
61
+ }
62
+ return packageTracer
63
+ }
64
+
65
+ /**
66
+ * Get the meter for ai-functions
67
+ */
68
+ export function getFunctionsMeter(): Meter {
69
+ if (!packageMeter) {
70
+ packageMeter = getMeter(PACKAGE_NAME, PACKAGE_VERSION)
71
+ }
72
+ return packageMeter
73
+ }
74
+
75
+ /**
76
+ * Get the logger for ai-functions
77
+ */
78
+ export function getFunctionsLogger(): Logger {
79
+ if (!packageLogger) {
80
+ packageLogger = getLogger(PACKAGE_NAME)
81
+ }
82
+ return packageLogger
83
+ }
84
+
85
+ /**
86
+ * Get AI metrics for the package
87
+ */
88
+ export function getAIMetrics() {
89
+ if (!aiMetrics) {
90
+ aiMetrics = createAIMetrics(getFunctionsMeter())
91
+ }
92
+ return aiMetrics
93
+ }
94
+
95
+ /**
96
+ * Reset cached telemetry instances (useful after changing provider)
97
+ */
98
+ export function resetTelemetry(): void {
99
+ packageTracer = undefined
100
+ packageMeter = undefined
101
+ packageLogger = undefined
102
+ aiMetrics = undefined
103
+ }
104
+
105
+ // ============================================================================
106
+ // Telemetry Context
107
+ // ============================================================================
108
+
109
+ /**
110
+ * Options for withTelemetry
111
+ */
112
+ export interface WithTelemetryOptions {
113
+ /** Telemetry provider to use */
114
+ provider?: TelemetryProvider
115
+ /** Service name for telemetry */
116
+ serviceName?: string
117
+ /** Service version */
118
+ serviceVersion?: string
119
+ }
120
+
121
+ /**
122
+ * Execute a function with telemetry enabled
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { createConsoleTelemetryProvider } from '@org.ai/types'
127
+ *
128
+ * await withTelemetry({
129
+ * provider: createConsoleTelemetryProvider()
130
+ * }, async () => {
131
+ * const text = await generate('Hello')
132
+ * })
133
+ * ```
134
+ */
135
+ export async function withTelemetry<T>(
136
+ options: WithTelemetryOptions,
137
+ fn: () => Promise<T>
138
+ ): Promise<T> {
139
+ const previousProvider = getTelemetryProvider()
140
+
141
+ if (options.provider) {
142
+ setTelemetryProvider(options.provider)
143
+ resetTelemetry()
144
+ }
145
+
146
+ try {
147
+ return await fn()
148
+ } finally {
149
+ if (options.provider) {
150
+ setTelemetryProvider(previousProvider)
151
+ resetTelemetry()
152
+ }
153
+ }
154
+ }
155
+
156
+ // ============================================================================
157
+ // Instrumentation Wrappers
158
+ // ============================================================================
159
+
160
+ /**
161
+ * Record AI request metrics
162
+ */
163
+ export function recordAIRequest(params: {
164
+ model: string
165
+ provider: string
166
+ inputTokens: number
167
+ outputTokens: number
168
+ durationMs: number
169
+ success: boolean
170
+ costUsd?: number | undefined
171
+ }): void {
172
+ const metrics = getAIMetrics()
173
+ const labels = {
174
+ model: params.model,
175
+ provider: params.provider,
176
+ status: params.success ? 'success' : 'error',
177
+ }
178
+
179
+ metrics.requestTotal.add(1, labels)
180
+ metrics.requestDuration.record(params.durationMs, labels)
181
+ metrics.tokensUsed.add(params.inputTokens + params.outputTokens, {
182
+ ...labels,
183
+ type: 'total',
184
+ })
185
+
186
+ if (!params.success) {
187
+ metrics.requestErrors.add(1, labels)
188
+ }
189
+
190
+ if (params.costUsd !== undefined) {
191
+ metrics.costTotal.add(params.costUsd, labels)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Create a traced version of any async function
197
+ */
198
+ export function traced<TArgs extends unknown[], TResult>(
199
+ name: string,
200
+ fn: (...args: TArgs) => Promise<TResult>,
201
+ options: {
202
+ kind?: 'internal' | 'client' | 'server'
203
+ getAttributes?: (...args: TArgs) => SpanAttributes
204
+ } = {}
205
+ ): (...args: TArgs) => Promise<TResult> {
206
+ return async (...args: TArgs): Promise<TResult> => {
207
+ const tracer = getFunctionsTracer()
208
+ const logger = getFunctionsLogger()
209
+
210
+ const attributes: SpanAttributes = {
211
+ [SemanticAttributes.AI_FUNCTION_NAME]: name,
212
+ ...(options.getAttributes ? options.getAttributes(...args) : {}),
213
+ }
214
+
215
+ return tracer.withSpan(name, { kind: options.kind || 'internal', attributes }, async (span) => {
216
+ logger.debug(`Executing ${name}`, { attributes })
217
+
218
+ try {
219
+ const result = await fn(...args)
220
+ span.setStatus('ok')
221
+ return result
222
+ } catch (error) {
223
+ const message = error instanceof Error ? error.message : String(error)
224
+ span.setStatus('error', message)
225
+ span.setAttribute(SemanticAttributes.EXCEPTION_MESSAGE, message)
226
+ if (error instanceof Error && error.stack) {
227
+ span.setAttribute(SemanticAttributes.EXCEPTION_STACKTRACE, error.stack)
228
+ }
229
+ logger.error(`Error in ${name}`, error instanceof Error ? error : undefined, {
230
+ attributes,
231
+ })
232
+ throw error
233
+ }
234
+ }) as Promise<TResult>
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Create instrumented generate function
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * import { generate } from 'ai-functions'
244
+ * import { instrumentGenerate } from 'ai-functions/telemetry'
245
+ *
246
+ * const tracedGenerate = instrumentGenerate(generate)
247
+ * const result = await tracedGenerate('text', 'Hello world')
248
+ * ```
249
+ */
250
+ export function instrumentGenerate<T extends (...args: any[]) => Promise<any>>(generateFn: T): T {
251
+ return traced('ai.generate', generateFn as any, {
252
+ kind: 'client',
253
+ getAttributes: (type?: string) => ({
254
+ [SemanticAttributes.AI_FUNCTION_TYPE]: type || 'text',
255
+ }),
256
+ }) as T
257
+ }
258
+
259
+ /**
260
+ * Create instrumented AI function with full metrics
261
+ */
262
+ export function instrumentAIFunction<TInput, TOutput>(
263
+ name: string,
264
+ fn: (input: TInput) => Promise<TOutput>,
265
+ options: {
266
+ model?: string
267
+ provider?: string
268
+ getTokens?: (input: TInput, output: TOutput) => { input: number; output: number }
269
+ getCost?: (input: TInput, output: TOutput) => number
270
+ } = {}
271
+ ): (input: TInput) => Promise<TOutput> {
272
+ return async (input: TInput): Promise<TOutput> => {
273
+ const tracer = getFunctionsTracer()
274
+ const logger = getFunctionsLogger()
275
+ const startTime = Date.now()
276
+
277
+ const attributes: SpanAttributes = {
278
+ [SemanticAttributes.AI_FUNCTION_NAME]: name,
279
+ ...(options.model && { [SemanticAttributes.AI_MODEL]: options.model }),
280
+ ...(options.provider && { [SemanticAttributes.AI_PROVIDER]: options.provider }),
281
+ }
282
+
283
+ return tracer.withSpan(`ai.function.${name}`, { kind: 'client', attributes }, async (span) => {
284
+ logger.info(`AI function ${name} started`, { model: options.model })
285
+
286
+ try {
287
+ const output = await fn(input)
288
+ const durationMs = Date.now() - startTime
289
+
290
+ // Record metrics
291
+ const tokens = options.getTokens?.(input, output)
292
+ const cost = options.getCost?.(input, output)
293
+
294
+ if (tokens) {
295
+ span.setAttribute(SemanticAttributes.AI_INPUT_TOKENS, tokens.input)
296
+ span.setAttribute(SemanticAttributes.AI_OUTPUT_TOKENS, tokens.output)
297
+ span.setAttribute(SemanticAttributes.AI_TOTAL_TOKENS, tokens.input + tokens.output)
298
+ }
299
+
300
+ if (cost !== undefined) {
301
+ span.setAttribute(SemanticAttributes.AI_COST_USD, cost)
302
+ }
303
+
304
+ recordAIRequest({
305
+ model: options.model || 'unknown',
306
+ provider: options.provider || 'unknown',
307
+ inputTokens: tokens?.input || 0,
308
+ outputTokens: tokens?.output || 0,
309
+ durationMs,
310
+ success: true,
311
+ costUsd: cost,
312
+ })
313
+
314
+ span.setStatus('ok')
315
+ logger.info(`AI function ${name} completed`, {
316
+ durationMs,
317
+ tokens: tokens?.input !== undefined ? tokens.input + (tokens.output || 0) : undefined,
318
+ })
319
+
320
+ return output
321
+ } catch (error) {
322
+ const durationMs = Date.now() - startTime
323
+ const message = error instanceof Error ? error.message : String(error)
324
+
325
+ recordAIRequest({
326
+ model: options.model || 'unknown',
327
+ provider: options.provider || 'unknown',
328
+ inputTokens: 0,
329
+ outputTokens: 0,
330
+ durationMs,
331
+ success: false,
332
+ })
333
+
334
+ span.setStatus('error', message)
335
+ logger.error(`AI function ${name} failed`, error instanceof Error ? error : undefined, {
336
+ durationMs,
337
+ })
338
+
339
+ throw error
340
+ }
341
+ }) as Promise<TOutput>
342
+ }
343
+ }
344
+
345
+ // ============================================================================
346
+ // Span Helpers
347
+ // ============================================================================
348
+
349
+ /**
350
+ * Start a span for an AI operation
351
+ */
352
+ export function startAISpan(name: string, attributes?: SpanAttributes): Span {
353
+ const tracer = getFunctionsTracer()
354
+ return tracer.startSpan(name, {
355
+ kind: 'client',
356
+ attributes: {
357
+ [SemanticAttributes.SERVICE_NAME]: PACKAGE_NAME,
358
+ ...attributes,
359
+ },
360
+ })
361
+ }
362
+
363
+ /**
364
+ * Execute code within a span
365
+ */
366
+ export async function withAISpan<T>(
367
+ name: string,
368
+ fn: (span: Span) => Promise<T>,
369
+ attributes?: SpanAttributes
370
+ ): Promise<T> {
371
+ const tracer = getFunctionsTracer()
372
+ return tracer.withSpan(name, { kind: 'client', attributes }, fn) as Promise<T>
373
+ }
374
+
375
+ // ============================================================================
376
+ // Re-exports from @org.ai/types
377
+ // ============================================================================
378
+
379
+ export {
380
+ // Core types
381
+ type Tracer,
382
+ type Meter,
383
+ type Logger,
384
+ type Span,
385
+ type SpanAttributes,
386
+ type TelemetryProvider,
387
+ type Counter,
388
+ type Histogram,
389
+
390
+ // Global functions
391
+ getTracer,
392
+ getMeter,
393
+ getLogger,
394
+ setTelemetryProvider,
395
+ getTelemetryProvider,
396
+
397
+ // Constants
398
+ SemanticAttributes,
399
+ MetricNames,
400
+
401
+ // Utilities
402
+ createAIMetrics,
403
+ } from '@org.ai/types'
package/src/template.ts CHANGED
@@ -65,9 +65,15 @@ export function createChainablePromise<T>(
65
65
  executor: (options?: FunctionOptions) => Promise<T>,
66
66
  defaultOptions?: FunctionOptions
67
67
  ): ChainablePromise<T> {
68
- // Create the base promise
68
+ // Create the base promise with a no-op catch to prevent unhandled rejection
69
+ // The actual error will still be thrown when .then() or await is called
69
70
  const basePromise = executor(defaultOptions)
70
71
 
72
+ // Prevent unhandled rejection warning by attaching a no-op catch
73
+ // This doesn't swallow the error - it just prevents the warning when the
74
+ // promise is not immediately awaited (e.g., when chaining options)
75
+ basePromise.catch(() => {})
76
+
71
77
  // Create a function that accepts options
72
78
  const chainable = ((options?: FunctionOptions) => {
73
79
  return executor({ ...defaultOptions, ...options })
@@ -134,9 +140,7 @@ export function withBatch<T, TInput = string>(
134
140
  /**
135
141
  * Create an async iterable from a streaming generator
136
142
  */
137
- export function createAsyncIterable<T>(
138
- items: T[] | (() => AsyncGenerator<T>)
139
- ): AsyncIterable<T> {
143
+ export function createAsyncIterable<T>(items: T[] | (() => AsyncGenerator<T>)): AsyncIterable<T> {
140
144
  if (Array.isArray(items)) {
141
145
  return {
142
146
  async *[Symbol.asyncIterator]() {