@ai-sdk/otel 0.0.1-beta.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,845 @@
1
+ // src/open-telemetry-integration.ts
2
+ import {
3
+ context,
4
+ trace,
5
+ SpanStatusCode
6
+ } from "@opentelemetry/api";
7
+
8
+ // src/assemble-operation-name.ts
9
+ function assembleOperationName({
10
+ operationId,
11
+ telemetry
12
+ }) {
13
+ return {
14
+ // standardized operation and resource name:
15
+ "operation.name": `${operationId}${(telemetry == null ? void 0 : telemetry.functionId) != null ? ` ${telemetry.functionId}` : ""}`,
16
+ "resource.name": telemetry == null ? void 0 : telemetry.functionId,
17
+ // detailed, AI SDK specific data:
18
+ "ai.operationId": operationId,
19
+ "ai.telemetry.functionId": telemetry == null ? void 0 : telemetry.functionId
20
+ };
21
+ }
22
+
23
+ // src/get-base-telemetry-attributes.ts
24
+ function getBaseTelemetryAttributes({
25
+ model,
26
+ settings,
27
+ telemetry,
28
+ headers
29
+ }) {
30
+ var _a;
31
+ return {
32
+ "ai.model.provider": model.provider,
33
+ "ai.model.id": model.modelId,
34
+ // settings:
35
+ ...Object.entries(settings).reduce((attributes, [key, value]) => {
36
+ attributes[`ai.settings.${key}`] = value;
37
+ return attributes;
38
+ }, {}),
39
+ // add metadata as attributes:
40
+ ...Object.entries((_a = telemetry == null ? void 0 : telemetry.metadata) != null ? _a : {}).reduce(
41
+ (attributes, [key, value]) => {
42
+ if (value != void 0) {
43
+ attributes[`ai.telemetry.metadata.${key}`] = value;
44
+ }
45
+ return attributes;
46
+ },
47
+ {}
48
+ ),
49
+ // request headers
50
+ ...Object.entries(headers != null ? headers : {}).reduce((attributes, [key, value]) => {
51
+ if (value !== void 0) {
52
+ attributes[`ai.request.headers.${key}`] = value;
53
+ }
54
+ return attributes;
55
+ }, {})
56
+ };
57
+ }
58
+
59
+ // src/stringify-for-telemetry.ts
60
+ import { convertDataContentToBase64String } from "ai";
61
+ function stringifyForTelemetry(prompt) {
62
+ return JSON.stringify(
63
+ prompt.map((message) => ({
64
+ ...message,
65
+ content: typeof message.content === "string" ? message.content : message.content.map(
66
+ (part) => part.type === "file" ? {
67
+ ...part,
68
+ data: part.data instanceof Uint8Array ? convertDataContentToBase64String(part.data) : part.data
69
+ } : part
70
+ )
71
+ }))
72
+ );
73
+ }
74
+
75
+ // src/open-telemetry-integration.ts
76
+ function recordSpanError(span, error) {
77
+ if (error instanceof Error) {
78
+ span.recordException({
79
+ name: error.name,
80
+ message: error.message,
81
+ stack: error.stack
82
+ });
83
+ span.setStatus({
84
+ code: SpanStatusCode.ERROR,
85
+ message: error.message
86
+ });
87
+ } else {
88
+ span.setStatus({ code: SpanStatusCode.ERROR });
89
+ }
90
+ }
91
+ function shouldRecord(telemetry) {
92
+ return (telemetry == null ? void 0 : telemetry.isEnabled) === true;
93
+ }
94
+ function selectAttributes(telemetry, attributes) {
95
+ if (!shouldRecord(telemetry)) {
96
+ return {};
97
+ }
98
+ const result = {};
99
+ for (const [key, value] of Object.entries(attributes)) {
100
+ if (value == null) continue;
101
+ if (typeof value === "object" && "input" in value && typeof value.input === "function") {
102
+ if ((telemetry == null ? void 0 : telemetry.recordInputs) === false) continue;
103
+ const resolved = value.input();
104
+ if (resolved != null) result[key] = resolved;
105
+ continue;
106
+ }
107
+ if (typeof value === "object" && "output" in value && typeof value.output === "function") {
108
+ if ((telemetry == null ? void 0 : telemetry.recordOutputs) === false) continue;
109
+ const resolved = value.output();
110
+ if (resolved != null) result[key] = resolved;
111
+ continue;
112
+ }
113
+ result[key] = value;
114
+ }
115
+ return result;
116
+ }
117
+ var OpenTelemetryIntegration = class {
118
+ constructor(options = {}) {
119
+ this.callStates = /* @__PURE__ */ new Map();
120
+ var _a;
121
+ this.tracer = (_a = options.tracer) != null ? _a : trace.getTracer("ai");
122
+ }
123
+ getCallState(callId) {
124
+ return this.callStates.get(callId);
125
+ }
126
+ cleanupCallState(callId) {
127
+ this.callStates.delete(callId);
128
+ }
129
+ executeTool({
130
+ callId,
131
+ toolCallId,
132
+ execute
133
+ }) {
134
+ var _a;
135
+ const toolSpanEntry = (_a = this.getCallState(callId)) == null ? void 0 : _a.toolSpans.get(toolCallId);
136
+ if (toolSpanEntry == null) {
137
+ return execute();
138
+ }
139
+ return context.with(toolSpanEntry.context, execute);
140
+ }
141
+ onStart(event) {
142
+ if (event.isEnabled !== true) return;
143
+ if (event.operationId === "ai.embed" || event.operationId === "ai.embedMany") {
144
+ this.onEmbedOperationStart(event);
145
+ return;
146
+ }
147
+ if (event.operationId === "ai.rerank") {
148
+ this.onRerankOperationStart(event);
149
+ return;
150
+ }
151
+ if (event.operationId === "ai.generateObject" || event.operationId === "ai.streamObject") {
152
+ this.onObjectOperationStart(event);
153
+ return;
154
+ }
155
+ this.onGenerateStart(event);
156
+ }
157
+ onGenerateStart(event) {
158
+ const telemetry = {
159
+ isEnabled: event.isEnabled,
160
+ recordInputs: event.recordInputs,
161
+ recordOutputs: event.recordOutputs,
162
+ functionId: event.functionId,
163
+ metadata: event.metadata
164
+ };
165
+ const settings = {
166
+ maxOutputTokens: event.maxOutputTokens,
167
+ temperature: event.temperature,
168
+ topP: event.topP,
169
+ topK: event.topK,
170
+ presencePenalty: event.presencePenalty,
171
+ frequencyPenalty: event.frequencyPenalty,
172
+ stopSequences: event.stopSequences,
173
+ seed: event.seed,
174
+ maxRetries: event.maxRetries
175
+ };
176
+ const baseTelemetryAttributes = getBaseTelemetryAttributes({
177
+ model: { provider: event.provider, modelId: event.modelId },
178
+ telemetry,
179
+ headers: event.headers,
180
+ settings
181
+ });
182
+ const attributes = selectAttributes(telemetry, {
183
+ ...assembleOperationName({
184
+ operationId: event.operationId,
185
+ telemetry
186
+ }),
187
+ ...baseTelemetryAttributes,
188
+ "ai.model.provider": event.provider,
189
+ "ai.model.id": event.modelId,
190
+ "ai.prompt": {
191
+ input: () => JSON.stringify({
192
+ system: event.system,
193
+ prompt: event.prompt,
194
+ messages: event.messages
195
+ })
196
+ }
197
+ });
198
+ const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
199
+ const rootContext = trace.setSpan(context.active(), rootSpan);
200
+ this.callStates.set(event.callId, {
201
+ operationId: event.operationId,
202
+ telemetry,
203
+ rootSpan,
204
+ rootContext,
205
+ stepSpan: void 0,
206
+ stepContext: void 0,
207
+ embedSpans: /* @__PURE__ */ new Map(),
208
+ rerankSpan: void 0,
209
+ toolSpans: /* @__PURE__ */ new Map(),
210
+ baseTelemetryAttributes,
211
+ settings
212
+ });
213
+ }
214
+ onObjectOperationStart(event) {
215
+ const telemetry = {
216
+ isEnabled: event.isEnabled,
217
+ recordInputs: event.recordInputs,
218
+ recordOutputs: event.recordOutputs,
219
+ functionId: event.functionId,
220
+ metadata: event.metadata
221
+ };
222
+ const settings = {
223
+ maxOutputTokens: event.maxOutputTokens,
224
+ temperature: event.temperature,
225
+ topP: event.topP,
226
+ topK: event.topK,
227
+ presencePenalty: event.presencePenalty,
228
+ frequencyPenalty: event.frequencyPenalty,
229
+ seed: event.seed,
230
+ maxRetries: event.maxRetries
231
+ };
232
+ const baseTelemetryAttributes = getBaseTelemetryAttributes({
233
+ model: { provider: event.provider, modelId: event.modelId },
234
+ telemetry,
235
+ headers: event.headers,
236
+ settings
237
+ });
238
+ const attributes = selectAttributes(telemetry, {
239
+ ...assembleOperationName({
240
+ operationId: event.operationId,
241
+ telemetry
242
+ }),
243
+ ...baseTelemetryAttributes,
244
+ "ai.prompt": {
245
+ input: () => JSON.stringify({
246
+ system: event.system,
247
+ prompt: event.prompt,
248
+ messages: event.messages
249
+ })
250
+ },
251
+ "ai.schema": event.schema ? { input: () => JSON.stringify(event.schema) } : void 0,
252
+ "ai.schema.name": event.schemaName,
253
+ "ai.schema.description": event.schemaDescription,
254
+ "ai.settings.output": event.output
255
+ });
256
+ const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
257
+ const rootContext = trace.setSpan(context.active(), rootSpan);
258
+ this.callStates.set(event.callId, {
259
+ operationId: event.operationId,
260
+ telemetry,
261
+ rootSpan,
262
+ rootContext,
263
+ stepSpan: void 0,
264
+ stepContext: void 0,
265
+ embedSpans: /* @__PURE__ */ new Map(),
266
+ rerankSpan: void 0,
267
+ toolSpans: /* @__PURE__ */ new Map(),
268
+ baseTelemetryAttributes,
269
+ settings
270
+ });
271
+ }
272
+ /** @deprecated */
273
+ onObjectStepStart(event) {
274
+ var _a;
275
+ const state = this.getCallState(event.callId);
276
+ if (!(state == null ? void 0 : state.rootSpan) || !state.rootContext) return;
277
+ const { telemetry } = state;
278
+ const stepOperationId = state.operationId === "ai.streamObject" ? "ai.streamObject.doStream" : "ai.generateObject.doGenerate";
279
+ const attributes = selectAttributes(telemetry, {
280
+ ...assembleOperationName({
281
+ operationId: stepOperationId,
282
+ telemetry
283
+ }),
284
+ ...state.baseTelemetryAttributes,
285
+ "ai.prompt.messages": {
286
+ input: () => event.promptMessages ? stringifyForTelemetry(event.promptMessages) : void 0
287
+ },
288
+ "gen_ai.system": event.provider,
289
+ "gen_ai.request.model": event.modelId,
290
+ "gen_ai.request.frequency_penalty": state.settings.frequencyPenalty,
291
+ "gen_ai.request.max_tokens": state.settings.maxOutputTokens,
292
+ "gen_ai.request.presence_penalty": state.settings.presencePenalty,
293
+ "gen_ai.request.temperature": (_a = state.settings.temperature) != null ? _a : void 0,
294
+ "gen_ai.request.top_k": state.settings.topK,
295
+ "gen_ai.request.top_p": state.settings.topP
296
+ });
297
+ state.stepSpan = this.tracer.startSpan(
298
+ stepOperationId,
299
+ { attributes },
300
+ state.rootContext
301
+ );
302
+ state.stepContext = trace.setSpan(state.rootContext, state.stepSpan);
303
+ }
304
+ /** @deprecated */
305
+ onObjectStepFinish(event) {
306
+ const state = this.getCallState(event.callId);
307
+ if (!(state == null ? void 0 : state.stepSpan)) return;
308
+ const { telemetry } = state;
309
+ state.stepSpan.setAttributes(
310
+ selectAttributes(telemetry, {
311
+ "ai.response.finishReason": event.finishReason,
312
+ "ai.response.object": {
313
+ output: () => {
314
+ try {
315
+ return JSON.stringify(JSON.parse(event.objectText));
316
+ } catch (e) {
317
+ return event.objectText;
318
+ }
319
+ }
320
+ },
321
+ "ai.response.id": event.response.id,
322
+ "ai.response.model": event.response.modelId,
323
+ "ai.response.timestamp": event.response.timestamp.toISOString(),
324
+ "ai.response.providerMetadata": event.providerMetadata ? JSON.stringify(event.providerMetadata) : void 0,
325
+ "ai.usage.inputTokens": event.usage.inputTokens,
326
+ "ai.usage.outputTokens": event.usage.outputTokens,
327
+ "ai.usage.totalTokens": event.usage.totalTokens,
328
+ "ai.usage.reasoningTokens": event.usage.reasoningTokens,
329
+ "ai.usage.cachedInputTokens": event.usage.cachedInputTokens,
330
+ "gen_ai.response.finish_reasons": [event.finishReason],
331
+ "gen_ai.response.id": event.response.id,
332
+ "gen_ai.response.model": event.response.modelId,
333
+ "gen_ai.usage.input_tokens": event.usage.inputTokens,
334
+ "gen_ai.usage.output_tokens": event.usage.outputTokens
335
+ })
336
+ );
337
+ if (event.msToFirstChunk != null) {
338
+ state.stepSpan.addEvent("ai.stream.firstChunk", {
339
+ "ai.stream.msToFirstChunk": event.msToFirstChunk
340
+ });
341
+ state.stepSpan.setAttributes({
342
+ "ai.stream.msToFirstChunk": event.msToFirstChunk
343
+ });
344
+ }
345
+ state.stepSpan.end();
346
+ state.stepSpan = void 0;
347
+ state.stepContext = void 0;
348
+ }
349
+ onEmbedOperationStart(event) {
350
+ const telemetry = {
351
+ isEnabled: event.isEnabled,
352
+ recordInputs: event.recordInputs,
353
+ recordOutputs: event.recordOutputs,
354
+ functionId: event.functionId,
355
+ metadata: event.metadata
356
+ };
357
+ const settings = {
358
+ maxRetries: event.maxRetries
359
+ };
360
+ const baseTelemetryAttributes = getBaseTelemetryAttributes({
361
+ model: { provider: event.provider, modelId: event.modelId },
362
+ telemetry,
363
+ headers: event.headers,
364
+ settings
365
+ });
366
+ const value = event.value;
367
+ const isMany = event.operationId === "ai.embedMany";
368
+ const attributes = selectAttributes(telemetry, {
369
+ ...assembleOperationName({
370
+ operationId: event.operationId,
371
+ telemetry
372
+ }),
373
+ ...baseTelemetryAttributes,
374
+ ...isMany ? {
375
+ "ai.values": {
376
+ input: () => value.map((v) => JSON.stringify(v))
377
+ }
378
+ } : {
379
+ "ai.value": {
380
+ input: () => JSON.stringify(value)
381
+ }
382
+ }
383
+ });
384
+ const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
385
+ const rootContext = trace.setSpan(context.active(), rootSpan);
386
+ this.callStates.set(event.callId, {
387
+ operationId: event.operationId,
388
+ telemetry,
389
+ rootSpan,
390
+ rootContext,
391
+ stepSpan: void 0,
392
+ stepContext: void 0,
393
+ embedSpans: /* @__PURE__ */ new Map(),
394
+ rerankSpan: void 0,
395
+ toolSpans: /* @__PURE__ */ new Map(),
396
+ baseTelemetryAttributes,
397
+ settings
398
+ });
399
+ }
400
+ onStepStart(event) {
401
+ var _a;
402
+ const state = this.getCallState(event.callId);
403
+ if (!(state == null ? void 0 : state.rootSpan) || !state.rootContext) return;
404
+ const { telemetry } = state;
405
+ const stepOperationId = state.operationId === "ai.streamText" ? "ai.streamText.doStream" : "ai.generateText.doGenerate";
406
+ const attributes = selectAttributes(telemetry, {
407
+ ...assembleOperationName({
408
+ operationId: stepOperationId,
409
+ telemetry
410
+ }),
411
+ ...state.baseTelemetryAttributes,
412
+ "ai.model.provider": event.provider,
413
+ "ai.model.id": event.modelId,
414
+ "ai.prompt.messages": {
415
+ input: () => event.promptMessages ? stringifyForTelemetry(event.promptMessages) : void 0
416
+ },
417
+ "ai.prompt.tools": {
418
+ input: () => {
419
+ var _a2;
420
+ return (_a2 = event.stepTools) == null ? void 0 : _a2.map((tool) => JSON.stringify(tool));
421
+ }
422
+ },
423
+ "ai.prompt.toolChoice": {
424
+ input: () => event.stepToolChoice != null ? JSON.stringify(event.stepToolChoice) : void 0
425
+ },
426
+ "gen_ai.system": event.provider,
427
+ "gen_ai.request.model": event.modelId,
428
+ "gen_ai.request.frequency_penalty": state.settings.frequencyPenalty,
429
+ "gen_ai.request.max_tokens": state.settings.maxOutputTokens,
430
+ "gen_ai.request.presence_penalty": state.settings.presencePenalty,
431
+ "gen_ai.request.stop_sequences": state.settings.stopSequences,
432
+ "gen_ai.request.temperature": (_a = state.settings.temperature) != null ? _a : void 0,
433
+ "gen_ai.request.top_k": state.settings.topK,
434
+ "gen_ai.request.top_p": state.settings.topP
435
+ });
436
+ state.stepSpan = this.tracer.startSpan(
437
+ stepOperationId,
438
+ { attributes },
439
+ state.rootContext
440
+ );
441
+ state.stepContext = trace.setSpan(state.rootContext, state.stepSpan);
442
+ }
443
+ onToolCallStart(event) {
444
+ const state = this.getCallState(event.callId);
445
+ if (!(state == null ? void 0 : state.stepContext)) return;
446
+ const { telemetry } = state;
447
+ const { toolCall } = event;
448
+ const attributes = selectAttributes(telemetry, {
449
+ ...assembleOperationName({
450
+ operationId: "ai.toolCall",
451
+ telemetry
452
+ }),
453
+ "ai.toolCall.name": toolCall.toolName,
454
+ "ai.toolCall.id": toolCall.toolCallId,
455
+ "ai.toolCall.args": {
456
+ output: () => JSON.stringify(toolCall.input)
457
+ }
458
+ });
459
+ const toolSpan = this.tracer.startSpan(
460
+ "ai.toolCall",
461
+ { attributes },
462
+ state.stepContext
463
+ );
464
+ const toolContext = trace.setSpan(state.stepContext, toolSpan);
465
+ state.toolSpans.set(toolCall.toolCallId, {
466
+ span: toolSpan,
467
+ context: toolContext
468
+ });
469
+ }
470
+ onToolCallFinish(event) {
471
+ const state = this.getCallState(event.callId);
472
+ if (!state) return;
473
+ const toolSpanEntry = state.toolSpans.get(event.toolCall.toolCallId);
474
+ if (!toolSpanEntry) return;
475
+ const { span } = toolSpanEntry;
476
+ const { telemetry } = state;
477
+ if (event.success) {
478
+ try {
479
+ span.setAttributes(
480
+ selectAttributes(telemetry, {
481
+ "ai.toolCall.result": {
482
+ output: () => JSON.stringify(event.output)
483
+ }
484
+ })
485
+ );
486
+ } catch (_ignored) {
487
+ }
488
+ } else {
489
+ recordSpanError(span, event.error);
490
+ }
491
+ span.end();
492
+ state.toolSpans.delete(event.toolCall.toolCallId);
493
+ }
494
+ onStepFinish(event) {
495
+ var _a, _b, _c, _d, _e;
496
+ const state = this.getCallState(event.callId);
497
+ if (!(state == null ? void 0 : state.stepSpan)) return;
498
+ const { telemetry } = state;
499
+ state.stepSpan.setAttributes(
500
+ selectAttributes(telemetry, {
501
+ "ai.response.finishReason": event.finishReason,
502
+ "ai.response.text": {
503
+ output: () => {
504
+ var _a2;
505
+ return (_a2 = event.text) != null ? _a2 : void 0;
506
+ }
507
+ },
508
+ "ai.response.reasoning": {
509
+ output: () => event.reasoning.length > 0 ? event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n") : void 0
510
+ },
511
+ "ai.response.toolCalls": {
512
+ output: () => event.toolCalls.length > 0 ? JSON.stringify(
513
+ event.toolCalls.map((toolCall) => ({
514
+ toolCallId: toolCall.toolCallId,
515
+ toolName: toolCall.toolName,
516
+ input: toolCall.input
517
+ }))
518
+ ) : void 0
519
+ },
520
+ "ai.response.files": {
521
+ output: () => event.files.length > 0 ? JSON.stringify(
522
+ event.files.map((file) => ({
523
+ type: "file",
524
+ mediaType: file.mediaType,
525
+ data: file.base64
526
+ }))
527
+ ) : void 0
528
+ },
529
+ "ai.response.id": event.response.id,
530
+ "ai.response.model": event.response.modelId,
531
+ "ai.response.timestamp": event.response.timestamp.toISOString(),
532
+ "ai.response.providerMetadata": event.providerMetadata ? JSON.stringify(event.providerMetadata) : void 0,
533
+ "ai.usage.inputTokens": event.usage.inputTokens,
534
+ "ai.usage.outputTokens": event.usage.outputTokens,
535
+ "ai.usage.totalTokens": event.usage.totalTokens,
536
+ "ai.usage.reasoningTokens": event.usage.reasoningTokens,
537
+ "ai.usage.cachedInputTokens": event.usage.cachedInputTokens,
538
+ "ai.usage.inputTokenDetails.noCacheTokens": (_a = event.usage.inputTokenDetails) == null ? void 0 : _a.noCacheTokens,
539
+ "ai.usage.inputTokenDetails.cacheReadTokens": (_b = event.usage.inputTokenDetails) == null ? void 0 : _b.cacheReadTokens,
540
+ "ai.usage.inputTokenDetails.cacheWriteTokens": (_c = event.usage.inputTokenDetails) == null ? void 0 : _c.cacheWriteTokens,
541
+ "ai.usage.outputTokenDetails.textTokens": (_d = event.usage.outputTokenDetails) == null ? void 0 : _d.textTokens,
542
+ "ai.usage.outputTokenDetails.reasoningTokens": (_e = event.usage.outputTokenDetails) == null ? void 0 : _e.reasoningTokens,
543
+ "gen_ai.response.finish_reasons": [event.finishReason],
544
+ "gen_ai.response.id": event.response.id,
545
+ "gen_ai.response.model": event.response.modelId,
546
+ "gen_ai.usage.input_tokens": event.usage.inputTokens,
547
+ "gen_ai.usage.output_tokens": event.usage.outputTokens
548
+ })
549
+ );
550
+ state.stepSpan.end();
551
+ state.stepSpan = void 0;
552
+ state.stepContext = void 0;
553
+ }
554
+ onFinish(event) {
555
+ const state = this.getCallState(event.callId);
556
+ if (!(state == null ? void 0 : state.rootSpan)) return;
557
+ if (state.operationId === "ai.embed" || state.operationId === "ai.embedMany") {
558
+ this.onEmbedOperationFinish(event);
559
+ return;
560
+ }
561
+ if (state.operationId === "ai.rerank") {
562
+ this.onRerankOperationFinish(event);
563
+ return;
564
+ }
565
+ if (state.operationId === "ai.generateObject" || state.operationId === "ai.streamObject") {
566
+ this.onObjectOperationFinish(event);
567
+ return;
568
+ }
569
+ this.onGenerateFinish(event);
570
+ }
571
+ onGenerateFinish(event) {
572
+ var _a, _b, _c, _d, _e;
573
+ const state = this.getCallState(event.callId);
574
+ if (!(state == null ? void 0 : state.rootSpan)) return;
575
+ const { telemetry } = state;
576
+ state.rootSpan.setAttributes(
577
+ selectAttributes(telemetry, {
578
+ "ai.response.finishReason": event.finishReason,
579
+ "ai.response.text": {
580
+ output: () => {
581
+ var _a2;
582
+ return (_a2 = event.text) != null ? _a2 : void 0;
583
+ }
584
+ },
585
+ "ai.response.reasoning": {
586
+ output: () => event.reasoning.length > 0 ? event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n") : void 0
587
+ },
588
+ "ai.response.toolCalls": {
589
+ output: () => event.toolCalls.length > 0 ? JSON.stringify(
590
+ event.toolCalls.map((toolCall) => ({
591
+ toolCallId: toolCall.toolCallId,
592
+ toolName: toolCall.toolName,
593
+ input: toolCall.input
594
+ }))
595
+ ) : void 0
596
+ },
597
+ "ai.response.files": {
598
+ output: () => event.files.length > 0 ? JSON.stringify(
599
+ event.files.map((file) => ({
600
+ type: "file",
601
+ mediaType: file.mediaType,
602
+ data: file.base64
603
+ }))
604
+ ) : void 0
605
+ },
606
+ "ai.response.providerMetadata": event.providerMetadata ? JSON.stringify(event.providerMetadata) : void 0,
607
+ "ai.usage.inputTokens": event.totalUsage.inputTokens,
608
+ "ai.usage.outputTokens": event.totalUsage.outputTokens,
609
+ "ai.usage.totalTokens": event.totalUsage.totalTokens,
610
+ "ai.usage.reasoningTokens": event.totalUsage.reasoningTokens,
611
+ "ai.usage.cachedInputTokens": event.totalUsage.cachedInputTokens,
612
+ "ai.usage.inputTokenDetails.noCacheTokens": (_a = event.totalUsage.inputTokenDetails) == null ? void 0 : _a.noCacheTokens,
613
+ "ai.usage.inputTokenDetails.cacheReadTokens": (_b = event.totalUsage.inputTokenDetails) == null ? void 0 : _b.cacheReadTokens,
614
+ "ai.usage.inputTokenDetails.cacheWriteTokens": (_c = event.totalUsage.inputTokenDetails) == null ? void 0 : _c.cacheWriteTokens,
615
+ "ai.usage.outputTokenDetails.textTokens": (_d = event.totalUsage.outputTokenDetails) == null ? void 0 : _d.textTokens,
616
+ "ai.usage.outputTokenDetails.reasoningTokens": (_e = event.totalUsage.outputTokenDetails) == null ? void 0 : _e.reasoningTokens
617
+ })
618
+ );
619
+ state.rootSpan.end();
620
+ this.cleanupCallState(event.callId);
621
+ }
622
+ onObjectOperationFinish(event) {
623
+ const state = this.getCallState(event.callId);
624
+ if (!(state == null ? void 0 : state.rootSpan)) return;
625
+ const { telemetry } = state;
626
+ state.rootSpan.setAttributes(
627
+ selectAttributes(telemetry, {
628
+ "ai.response.finishReason": event.finishReason,
629
+ "ai.response.object": {
630
+ output: () => event.object != null ? JSON.stringify(event.object) : void 0
631
+ },
632
+ "ai.response.providerMetadata": event.providerMetadata ? JSON.stringify(event.providerMetadata) : void 0,
633
+ "ai.usage.inputTokens": event.usage.inputTokens,
634
+ "ai.usage.outputTokens": event.usage.outputTokens,
635
+ "ai.usage.totalTokens": event.usage.totalTokens,
636
+ "ai.usage.reasoningTokens": event.usage.reasoningTokens,
637
+ "ai.usage.cachedInputTokens": event.usage.cachedInputTokens
638
+ })
639
+ );
640
+ state.rootSpan.end();
641
+ this.cleanupCallState(event.callId);
642
+ }
643
+ onEmbedOperationFinish(event) {
644
+ const state = this.getCallState(event.callId);
645
+ if (!(state == null ? void 0 : state.rootSpan)) return;
646
+ const { telemetry } = state;
647
+ const isMany = state.operationId === "ai.embedMany";
648
+ state.rootSpan.setAttributes(
649
+ selectAttributes(telemetry, {
650
+ ...isMany ? {
651
+ "ai.embeddings": {
652
+ output: () => event.embedding.map((e) => JSON.stringify(e))
653
+ }
654
+ } : {
655
+ "ai.embedding": {
656
+ output: () => JSON.stringify(event.embedding)
657
+ }
658
+ },
659
+ "ai.usage.tokens": event.usage.tokens
660
+ })
661
+ );
662
+ state.rootSpan.end();
663
+ this.cleanupCallState(event.callId);
664
+ }
665
+ onEmbedStart(event) {
666
+ const state = this.getCallState(event.callId);
667
+ if (!(state == null ? void 0 : state.rootSpan) || !state.rootContext) return;
668
+ const { telemetry } = state;
669
+ const attributes = selectAttributes(telemetry, {
670
+ ...assembleOperationName({
671
+ operationId: event.operationId,
672
+ telemetry
673
+ }),
674
+ ...state.baseTelemetryAttributes,
675
+ "ai.values": {
676
+ input: () => event.values.map((v) => JSON.stringify(v))
677
+ }
678
+ });
679
+ const embedSpan = this.tracer.startSpan(
680
+ event.operationId,
681
+ { attributes },
682
+ state.rootContext
683
+ );
684
+ const embedContext = trace.setSpan(state.rootContext, embedSpan);
685
+ state.embedSpans.set(event.embedCallId, {
686
+ span: embedSpan,
687
+ context: embedContext
688
+ });
689
+ }
690
+ onEmbedFinish(event) {
691
+ const state = this.getCallState(event.callId);
692
+ if (!state) return;
693
+ const embedSpanEntry = state.embedSpans.get(event.embedCallId);
694
+ if (!embedSpanEntry) return;
695
+ const { span } = embedSpanEntry;
696
+ const { telemetry } = state;
697
+ span.setAttributes(
698
+ selectAttributes(telemetry, {
699
+ "ai.embeddings": {
700
+ output: () => event.embeddings.map((embedding) => JSON.stringify(embedding))
701
+ },
702
+ "ai.usage.tokens": event.usage.tokens
703
+ })
704
+ );
705
+ span.end();
706
+ state.embedSpans.delete(event.embedCallId);
707
+ }
708
+ onRerankOperationStart(event) {
709
+ const telemetry = {
710
+ isEnabled: event.isEnabled,
711
+ recordInputs: event.recordInputs,
712
+ recordOutputs: event.recordOutputs,
713
+ functionId: event.functionId,
714
+ metadata: event.metadata
715
+ };
716
+ const settings = {
717
+ maxRetries: event.maxRetries
718
+ };
719
+ const baseTelemetryAttributes = getBaseTelemetryAttributes({
720
+ model: { provider: event.provider, modelId: event.modelId },
721
+ telemetry,
722
+ headers: event.headers,
723
+ settings
724
+ });
725
+ const attributes = selectAttributes(telemetry, {
726
+ ...assembleOperationName({
727
+ operationId: event.operationId,
728
+ telemetry
729
+ }),
730
+ ...baseTelemetryAttributes,
731
+ "ai.documents": {
732
+ input: () => event.documents.map((d) => JSON.stringify(d))
733
+ }
734
+ });
735
+ const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
736
+ const rootContext = trace.setSpan(context.active(), rootSpan);
737
+ this.callStates.set(event.callId, {
738
+ operationId: event.operationId,
739
+ telemetry,
740
+ rootSpan,
741
+ rootContext,
742
+ stepSpan: void 0,
743
+ stepContext: void 0,
744
+ embedSpans: /* @__PURE__ */ new Map(),
745
+ rerankSpan: void 0,
746
+ toolSpans: /* @__PURE__ */ new Map(),
747
+ baseTelemetryAttributes,
748
+ settings
749
+ });
750
+ }
751
+ onRerankOperationFinish(event) {
752
+ const state = this.getCallState(event.callId);
753
+ if (!(state == null ? void 0 : state.rootSpan)) return;
754
+ state.rootSpan.end();
755
+ this.cleanupCallState(event.callId);
756
+ }
757
+ onRerankStart(event) {
758
+ const state = this.getCallState(event.callId);
759
+ if (!(state == null ? void 0 : state.rootSpan) || !state.rootContext) return;
760
+ const { telemetry } = state;
761
+ const attributes = selectAttributes(telemetry, {
762
+ ...assembleOperationName({
763
+ operationId: event.operationId,
764
+ telemetry
765
+ }),
766
+ ...state.baseTelemetryAttributes,
767
+ "ai.documents": {
768
+ input: () => event.documents.map((d) => JSON.stringify(d))
769
+ }
770
+ });
771
+ const rerankSpan = this.tracer.startSpan(
772
+ event.operationId,
773
+ { attributes },
774
+ state.rootContext
775
+ );
776
+ const rerankContext = trace.setSpan(state.rootContext, rerankSpan);
777
+ state.rerankSpan = { span: rerankSpan, context: rerankContext };
778
+ }
779
+ onRerankFinish(event) {
780
+ const state = this.getCallState(event.callId);
781
+ if (!(state == null ? void 0 : state.rerankSpan)) return;
782
+ const { span } = state.rerankSpan;
783
+ const { telemetry } = state;
784
+ span.setAttributes(
785
+ selectAttributes(telemetry, {
786
+ "ai.ranking.type": event.documentsType,
787
+ "ai.ranking": {
788
+ output: () => event.ranking.map((r) => JSON.stringify(r))
789
+ }
790
+ })
791
+ );
792
+ span.end();
793
+ state.rerankSpan = void 0;
794
+ }
795
+ onChunk(event) {
796
+ var _a;
797
+ const chunk = event.chunk;
798
+ if (typeof chunk.callId !== "string") {
799
+ return;
800
+ }
801
+ if (chunk.type !== "ai.stream.firstChunk" && chunk.type !== "ai.stream.finish") {
802
+ return;
803
+ }
804
+ const state = this.getCallState(chunk.callId);
805
+ if (!(state == null ? void 0 : state.stepSpan)) return;
806
+ const attributes = Object.fromEntries(
807
+ Object.entries(
808
+ (_a = chunk.attributes) != null ? _a : {}
809
+ ).filter(([, value]) => value != null)
810
+ );
811
+ state.stepSpan.addEvent(chunk.type, attributes);
812
+ if (Object.keys(attributes).length > 0) {
813
+ state.stepSpan.setAttributes(attributes);
814
+ }
815
+ }
816
+ onError(error) {
817
+ var _a;
818
+ const event = error;
819
+ if (!(event == null ? void 0 : event.callId)) return;
820
+ const state = this.getCallState(event.callId);
821
+ if (!(state == null ? void 0 : state.rootSpan)) return;
822
+ const actualError = (_a = event.error) != null ? _a : error;
823
+ if (state.stepSpan) {
824
+ recordSpanError(state.stepSpan, actualError);
825
+ state.stepSpan.end();
826
+ }
827
+ for (const { span: embedSpan } of state.embedSpans.values()) {
828
+ recordSpanError(embedSpan, actualError);
829
+ embedSpan.end();
830
+ }
831
+ state.embedSpans.clear();
832
+ if (state.rerankSpan) {
833
+ recordSpanError(state.rerankSpan.span, actualError);
834
+ state.rerankSpan.span.end();
835
+ state.rerankSpan = void 0;
836
+ }
837
+ recordSpanError(state.rootSpan, actualError);
838
+ state.rootSpan.end();
839
+ this.cleanupCallState(event.callId);
840
+ }
841
+ };
842
+ export {
843
+ OpenTelemetryIntegration
844
+ };
845
+ //# sourceMappingURL=index.mjs.map