@ai-sdk/otel 1.0.0-beta.13 → 1.0.0-beta.130
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/CHANGELOG.md +934 -0
- package/README.md +119 -0
- package/dist/index.d.ts +139 -22
- package/dist/index.js +1800 -168
- package/dist/index.js.map +1 -1
- package/package.json +16 -13
- package/src/assemble-operation-name.ts +2 -2
- package/src/gen-ai-format-messages.ts +644 -0
- package/src/get-base-telemetry-attributes.ts +8 -15
- package/src/get-tracer.ts +1 -1
- package/src/index.ts +7 -1
- package/src/{open-telemetry-integration.ts → legacy-open-telemetry.ts} +211 -147
- package/src/mock-tracer.ts +1 -1
- package/src/noop-tracer.ts +1 -1
- package/src/open-telemetry.ts +1336 -0
- package/src/record-span.ts +3 -4
- package/src/sanitize-attribute-value.ts +53 -0
- package/src/select-attributes.ts +65 -0
- package/src/select-telemetry-attributes.ts +3 -3
- package/src/stringify-for-telemetry.ts +23 -5
- package/src/supplemental-attributes.ts +201 -0
- package/dist/index.d.mts +0 -52
- package/dist/index.mjs +0 -845
- package/dist/index.mjs.map +0 -1
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
import { LanguageModelV4Prompt } from '@ai-sdk/provider';
|
|
1
|
+
import type { LanguageModelV4Prompt } from '@ai-sdk/provider';
|
|
2
|
+
import type { Context as AISDKContext } from '@ai-sdk/provider-utils';
|
|
2
3
|
import {
|
|
3
|
-
Attributes,
|
|
4
|
-
AttributeValue,
|
|
5
4
|
context,
|
|
6
|
-
Context,
|
|
7
|
-
Span,
|
|
8
5
|
SpanStatusCode,
|
|
9
6
|
trace,
|
|
10
|
-
|
|
7
|
+
type Attributes,
|
|
8
|
+
type AttributeValue,
|
|
9
|
+
type Context as OpenTelemetryContext,
|
|
10
|
+
type Span,
|
|
11
|
+
type Tracer,
|
|
11
12
|
} from '@opentelemetry/api';
|
|
12
13
|
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
EmbedOnStartEvent,
|
|
14
|
+
EmbeddingModelCallEndEvent,
|
|
15
|
+
EmbedEndEvent,
|
|
16
16
|
EmbedStartEvent,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
RerankOnStartEvent,
|
|
17
|
+
EmbeddingModelCallStartEvent,
|
|
18
|
+
GenerateObjectEndEvent,
|
|
19
|
+
GenerateObjectStartEvent,
|
|
20
|
+
GenerateObjectStepEndEvent,
|
|
21
|
+
GenerateObjectStepStartEvent,
|
|
22
|
+
GenerateTextAbortEvent,
|
|
23
|
+
GenerateTextEndEvent,
|
|
24
|
+
GenerateTextStartEvent,
|
|
25
|
+
GenerateTextStepEndEvent,
|
|
26
|
+
GenerateTextStepStartEvent,
|
|
27
|
+
ToolExecutionEndEvent,
|
|
28
|
+
ToolExecutionStartEvent,
|
|
29
|
+
Output,
|
|
30
|
+
RerankingModelCallEndEvent,
|
|
31
|
+
RerankEndEvent,
|
|
33
32
|
RerankStartEvent,
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
RerankingModelCallStartEvent,
|
|
34
|
+
InferTelemetryEvent,
|
|
35
|
+
Telemetry,
|
|
36
|
+
TelemetryOptions,
|
|
36
37
|
ToolSet,
|
|
37
38
|
} from 'ai';
|
|
38
39
|
import { assembleOperationName } from './assemble-operation-name';
|
|
39
40
|
import { getBaseTelemetryAttributes } from './get-base-telemetry-attributes';
|
|
41
|
+
import { sanitizeAttributeValue } from './sanitize-attribute-value';
|
|
40
42
|
import { stringifyForTelemetry } from './stringify-for-telemetry';
|
|
41
43
|
|
|
42
44
|
function recordSpanError(span: Span, error: unknown): void {
|
|
@@ -56,13 +58,13 @@ function recordSpanError(span: Span, error: unknown): void {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
function shouldRecord(
|
|
59
|
-
telemetry:
|
|
60
|
-
): telemetry is
|
|
61
|
-
return telemetry?.isEnabled
|
|
61
|
+
telemetry: TelemetryOptions | undefined,
|
|
62
|
+
): telemetry is TelemetryOptions {
|
|
63
|
+
return telemetry?.isEnabled !== false;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
function selectAttributes(
|
|
65
|
-
telemetry:
|
|
67
|
+
telemetry: TelemetryOptions | undefined,
|
|
66
68
|
attributes: Record<
|
|
67
69
|
string,
|
|
68
70
|
| AttributeValue
|
|
@@ -87,7 +89,10 @@ function selectAttributes(
|
|
|
87
89
|
) {
|
|
88
90
|
if (telemetry?.recordInputs === false) continue;
|
|
89
91
|
const resolved = value.input();
|
|
90
|
-
if (resolved != null)
|
|
92
|
+
if (resolved != null) {
|
|
93
|
+
const sanitized = sanitizeAttributeValue(resolved);
|
|
94
|
+
if (sanitized != null) result[key] = sanitized;
|
|
95
|
+
}
|
|
91
96
|
continue;
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -98,11 +103,15 @@ function selectAttributes(
|
|
|
98
103
|
) {
|
|
99
104
|
if (telemetry?.recordOutputs === false) continue;
|
|
100
105
|
const resolved = value.output();
|
|
101
|
-
if (resolved != null)
|
|
106
|
+
if (resolved != null) {
|
|
107
|
+
const sanitized = sanitizeAttributeValue(resolved);
|
|
108
|
+
if (sanitized != null) result[key] = sanitized;
|
|
109
|
+
}
|
|
102
110
|
continue;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
|
|
113
|
+
const sanitized = sanitizeAttributeValue(value as AttributeValue);
|
|
114
|
+
if (sanitized != null) result[key] = sanitized;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
return result;
|
|
@@ -110,9 +119,9 @@ function selectAttributes(
|
|
|
110
119
|
|
|
111
120
|
interface OtelStepStartEvent<
|
|
112
121
|
TOOLS extends ToolSet = ToolSet,
|
|
113
|
-
|
|
114
|
-
OUTPUT extends Output = Output,
|
|
115
|
-
> extends
|
|
122
|
+
RUNTIME_CONTEXT extends AISDKContext = AISDKContext,
|
|
123
|
+
OUTPUT extends Output.Output = Output.Output,
|
|
124
|
+
> extends GenerateTextStepStartEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
|
|
116
125
|
readonly promptMessages?: LanguageModelV4Prompt;
|
|
117
126
|
readonly stepTools?: ReadonlyArray<Record<string, unknown>>;
|
|
118
127
|
readonly stepToolChoice?: unknown;
|
|
@@ -120,19 +129,19 @@ interface OtelStepStartEvent<
|
|
|
120
129
|
|
|
121
130
|
interface CallState {
|
|
122
131
|
operationId: string;
|
|
123
|
-
telemetry:
|
|
132
|
+
telemetry: TelemetryOptions | undefined;
|
|
124
133
|
rootSpan: Span | undefined;
|
|
125
|
-
rootContext:
|
|
134
|
+
rootContext: OpenTelemetryContext | undefined;
|
|
126
135
|
stepSpan: Span | undefined;
|
|
127
|
-
stepContext:
|
|
128
|
-
embedSpans: Map<string, { span: Span; context:
|
|
129
|
-
rerankSpan: { span: Span; context:
|
|
130
|
-
toolSpans: Map<string, { span: Span; context:
|
|
136
|
+
stepContext: OpenTelemetryContext | undefined;
|
|
137
|
+
embedSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
|
|
138
|
+
rerankSpan: { span: Span; context: OpenTelemetryContext } | undefined;
|
|
139
|
+
toolSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
|
|
131
140
|
baseTelemetryAttributes: Attributes;
|
|
132
141
|
settings: Record<string, unknown>;
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
export class
|
|
144
|
+
export class LegacyOpenTelemetry implements Telemetry {
|
|
136
145
|
private readonly callStates = new Map<string, CallState>();
|
|
137
146
|
|
|
138
147
|
/**
|
|
@@ -174,25 +183,45 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
174
183
|
return context.with(toolSpanEntry.context, execute);
|
|
175
184
|
}
|
|
176
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Runs the provider `doGenerate`/`doStream` call with the active legacy
|
|
188
|
+
* model-call context.
|
|
189
|
+
*/
|
|
190
|
+
executeLanguageModelCall<T>({
|
|
191
|
+
callId,
|
|
192
|
+
execute,
|
|
193
|
+
}: {
|
|
194
|
+
callId: string;
|
|
195
|
+
execute: () => PromiseLike<T>;
|
|
196
|
+
}): PromiseLike<T> {
|
|
197
|
+
const stepContext = this.getCallState(callId)?.stepContext;
|
|
198
|
+
|
|
199
|
+
if (stepContext == null) {
|
|
200
|
+
return execute();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return context.with(stepContext, execute);
|
|
204
|
+
}
|
|
205
|
+
|
|
177
206
|
onStart(
|
|
178
207
|
event:
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
208
|
+
| InferTelemetryEvent<GenerateTextStartEvent>
|
|
209
|
+
| InferTelemetryEvent<GenerateObjectStartEvent>
|
|
210
|
+
| InferTelemetryEvent<EmbedStartEvent>
|
|
211
|
+
| InferTelemetryEvent<RerankStartEvent>,
|
|
183
212
|
): void {
|
|
184
|
-
if (event.isEnabled !== true) return;
|
|
185
|
-
|
|
186
213
|
if (
|
|
187
214
|
event.operationId === 'ai.embed' ||
|
|
188
215
|
event.operationId === 'ai.embedMany'
|
|
189
216
|
) {
|
|
190
|
-
this.onEmbedOperationStart(event as
|
|
217
|
+
this.onEmbedOperationStart(event as InferTelemetryEvent<EmbedStartEvent>);
|
|
191
218
|
return;
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
if (event.operationId === 'ai.rerank') {
|
|
195
|
-
this.onRerankOperationStart(
|
|
222
|
+
this.onRerankOperationStart(
|
|
223
|
+
event as InferTelemetryEvent<RerankStartEvent>,
|
|
224
|
+
);
|
|
196
225
|
return;
|
|
197
226
|
}
|
|
198
227
|
|
|
@@ -200,20 +229,22 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
200
229
|
event.operationId === 'ai.generateObject' ||
|
|
201
230
|
event.operationId === 'ai.streamObject'
|
|
202
231
|
) {
|
|
203
|
-
this.onObjectOperationStart(
|
|
232
|
+
this.onObjectOperationStart(
|
|
233
|
+
event as InferTelemetryEvent<GenerateObjectStartEvent>,
|
|
234
|
+
);
|
|
204
235
|
return;
|
|
205
236
|
}
|
|
206
237
|
|
|
207
|
-
this.onGenerateStart(event as
|
|
238
|
+
this.onGenerateStart(event as InferTelemetryEvent<GenerateTextStartEvent>);
|
|
208
239
|
}
|
|
209
240
|
|
|
210
|
-
private onGenerateStart(
|
|
211
|
-
|
|
212
|
-
|
|
241
|
+
private onGenerateStart(
|
|
242
|
+
event: InferTelemetryEvent<GenerateTextStartEvent>,
|
|
243
|
+
): void {
|
|
244
|
+
const telemetry: TelemetryOptions = {
|
|
213
245
|
recordInputs: event.recordInputs,
|
|
214
246
|
recordOutputs: event.recordOutputs,
|
|
215
247
|
functionId: event.functionId,
|
|
216
|
-
metadata: event.metadata,
|
|
217
248
|
};
|
|
218
249
|
|
|
219
250
|
const settings: Record<string, unknown> = {
|
|
@@ -230,9 +261,9 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
230
261
|
|
|
231
262
|
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
232
263
|
model: { provider: event.provider, modelId: event.modelId },
|
|
233
|
-
telemetry,
|
|
234
264
|
headers: event.headers,
|
|
235
265
|
settings,
|
|
266
|
+
context: event.runtimeContext as Record<string, unknown> | undefined,
|
|
236
267
|
});
|
|
237
268
|
|
|
238
269
|
const attributes = selectAttributes(telemetry, {
|
|
@@ -246,8 +277,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
246
277
|
'ai.prompt': {
|
|
247
278
|
input: () =>
|
|
248
279
|
JSON.stringify({
|
|
249
|
-
system: event.
|
|
250
|
-
prompt: event.prompt,
|
|
280
|
+
system: event.instructions,
|
|
251
281
|
messages: event.messages,
|
|
252
282
|
}),
|
|
253
283
|
},
|
|
@@ -271,13 +301,13 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
271
301
|
});
|
|
272
302
|
}
|
|
273
303
|
|
|
274
|
-
private onObjectOperationStart(
|
|
275
|
-
|
|
276
|
-
|
|
304
|
+
private onObjectOperationStart(
|
|
305
|
+
event: InferTelemetryEvent<GenerateObjectStartEvent>,
|
|
306
|
+
): void {
|
|
307
|
+
const telemetry: TelemetryOptions = {
|
|
277
308
|
recordInputs: event.recordInputs,
|
|
278
309
|
recordOutputs: event.recordOutputs,
|
|
279
310
|
functionId: event.functionId,
|
|
280
|
-
metadata: event.metadata,
|
|
281
311
|
};
|
|
282
312
|
|
|
283
313
|
const settings: Record<string, unknown> = {
|
|
@@ -293,9 +323,9 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
293
323
|
|
|
294
324
|
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
295
325
|
model: { provider: event.provider, modelId: event.modelId },
|
|
296
|
-
telemetry,
|
|
297
326
|
headers: event.headers,
|
|
298
327
|
settings,
|
|
328
|
+
context: undefined,
|
|
299
329
|
});
|
|
300
330
|
|
|
301
331
|
const attributes = selectAttributes(telemetry, {
|
|
@@ -339,7 +369,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
339
369
|
}
|
|
340
370
|
|
|
341
371
|
/** @deprecated */
|
|
342
|
-
onObjectStepStart(event:
|
|
372
|
+
onObjectStepStart(event: GenerateObjectStepStartEvent): void {
|
|
343
373
|
const state = this.getCallState(event.callId);
|
|
344
374
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
345
375
|
|
|
@@ -390,7 +420,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
390
420
|
}
|
|
391
421
|
|
|
392
422
|
/** @deprecated */
|
|
393
|
-
|
|
423
|
+
onObjectStepEnd(event: GenerateObjectStepEndEvent): void {
|
|
394
424
|
const state = this.getCallState(event.callId);
|
|
395
425
|
if (!state?.stepSpan) return;
|
|
396
426
|
|
|
@@ -418,8 +448,10 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
418
448
|
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
419
449
|
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
420
450
|
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
421
|
-
'ai.usage.reasoningTokens':
|
|
422
|
-
|
|
451
|
+
'ai.usage.reasoningTokens':
|
|
452
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
453
|
+
'ai.usage.cachedInputTokens':
|
|
454
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
423
455
|
|
|
424
456
|
'gen_ai.response.finish_reasons': [event.finishReason],
|
|
425
457
|
'gen_ai.response.id': event.response.id,
|
|
@@ -443,13 +475,13 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
443
475
|
state.stepContext = undefined;
|
|
444
476
|
}
|
|
445
477
|
|
|
446
|
-
private onEmbedOperationStart(
|
|
447
|
-
|
|
448
|
-
|
|
478
|
+
private onEmbedOperationStart(
|
|
479
|
+
event: InferTelemetryEvent<EmbedStartEvent>,
|
|
480
|
+
): void {
|
|
481
|
+
const telemetry: TelemetryOptions = {
|
|
449
482
|
recordInputs: event.recordInputs,
|
|
450
483
|
recordOutputs: event.recordOutputs,
|
|
451
484
|
functionId: event.functionId,
|
|
452
|
-
metadata: event.metadata,
|
|
453
485
|
};
|
|
454
486
|
|
|
455
487
|
const settings: Record<string, unknown> = {
|
|
@@ -458,9 +490,9 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
458
490
|
|
|
459
491
|
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
460
492
|
model: { provider: event.provider, modelId: event.modelId },
|
|
461
|
-
telemetry,
|
|
462
493
|
headers: event.headers,
|
|
463
494
|
settings,
|
|
495
|
+
context: undefined,
|
|
464
496
|
});
|
|
465
497
|
|
|
466
498
|
const value = event.value;
|
|
@@ -503,7 +535,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
503
535
|
});
|
|
504
536
|
}
|
|
505
537
|
|
|
506
|
-
onStepStart(event: OtelStepStartEvent
|
|
538
|
+
onStepStart(event: OtelStepStartEvent): void {
|
|
507
539
|
const state = this.getCallState(event.callId);
|
|
508
540
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
509
541
|
|
|
@@ -568,7 +600,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
568
600
|
state.stepContext = trace.setSpan(state.rootContext, state.stepSpan);
|
|
569
601
|
}
|
|
570
602
|
|
|
571
|
-
|
|
603
|
+
onToolExecutionStart(event: ToolExecutionStartEvent<ToolSet>): void {
|
|
572
604
|
const state = this.getCallState(event.callId);
|
|
573
605
|
if (!state?.stepContext) return;
|
|
574
606
|
|
|
@@ -600,7 +632,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
600
632
|
});
|
|
601
633
|
}
|
|
602
634
|
|
|
603
|
-
|
|
635
|
+
onToolExecutionEnd(event: ToolExecutionEndEvent<ToolSet>): void {
|
|
604
636
|
const state = this.getCallState(event.callId);
|
|
605
637
|
if (!state) return;
|
|
606
638
|
|
|
@@ -610,31 +642,33 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
610
642
|
const { span } = toolSpanEntry;
|
|
611
643
|
const { telemetry } = state;
|
|
612
644
|
|
|
613
|
-
|
|
645
|
+
const { toolOutput } = event;
|
|
646
|
+
if (toolOutput.type === 'tool-result') {
|
|
614
647
|
try {
|
|
615
648
|
span.setAttributes(
|
|
616
649
|
selectAttributes(telemetry, {
|
|
617
650
|
'ai.toolCall.result': {
|
|
618
|
-
output: () => JSON.stringify(
|
|
651
|
+
output: () => JSON.stringify(toolOutput.output),
|
|
619
652
|
},
|
|
620
653
|
}),
|
|
621
654
|
);
|
|
622
|
-
} catch
|
|
655
|
+
} catch {
|
|
623
656
|
// JSON.stringify might fail for non-serializable results
|
|
624
657
|
}
|
|
625
658
|
} else {
|
|
626
|
-
recordSpanError(span,
|
|
659
|
+
recordSpanError(span, toolOutput.error);
|
|
627
660
|
}
|
|
628
661
|
|
|
629
662
|
span.end();
|
|
630
663
|
state.toolSpans.delete(event.toolCall.toolCallId);
|
|
631
664
|
}
|
|
632
665
|
|
|
633
|
-
|
|
666
|
+
onStepEnd(event: GenerateTextStepEndEvent<ToolSet>): void {
|
|
634
667
|
const state = this.getCallState(event.callId);
|
|
635
668
|
if (!state?.stepSpan) return;
|
|
636
669
|
|
|
637
670
|
const { telemetry } = state;
|
|
671
|
+
const isStreamText = state.operationId === 'ai.streamText';
|
|
638
672
|
|
|
639
673
|
state.stepSpan.setAttributes(
|
|
640
674
|
selectAttributes(telemetry, {
|
|
@@ -681,12 +715,23 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
681
715
|
'ai.response.providerMetadata': event.providerMetadata
|
|
682
716
|
? JSON.stringify(event.providerMetadata)
|
|
683
717
|
: undefined,
|
|
718
|
+
'ai.response.msToFirstChunk': isStreamText
|
|
719
|
+
? event.performance.timeToFirstOutputMs
|
|
720
|
+
: undefined,
|
|
721
|
+
'ai.response.msToFinish': isStreamText
|
|
722
|
+
? event.performance.responseTimeMs
|
|
723
|
+
: undefined,
|
|
724
|
+
'ai.response.avgOutputTokensPerSecond': isStreamText
|
|
725
|
+
? event.performance.effectiveOutputTokensPerSecond
|
|
726
|
+
: undefined,
|
|
684
727
|
|
|
685
728
|
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
686
729
|
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
687
730
|
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
688
|
-
'ai.usage.reasoningTokens':
|
|
689
|
-
|
|
731
|
+
'ai.usage.reasoningTokens':
|
|
732
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
733
|
+
'ai.usage.cachedInputTokens':
|
|
734
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
690
735
|
'ai.usage.inputTokenDetails.noCacheTokens':
|
|
691
736
|
event.usage.inputTokenDetails?.noCacheTokens,
|
|
692
737
|
'ai.usage.inputTokenDetails.cacheReadTokens':
|
|
@@ -706,17 +751,36 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
706
751
|
}),
|
|
707
752
|
);
|
|
708
753
|
|
|
754
|
+
if (isStreamText && event.performance.timeToFirstOutputMs != null) {
|
|
755
|
+
state.stepSpan.addEvent('ai.stream.firstChunk', {
|
|
756
|
+
'ai.response.msToFirstChunk': event.performance.timeToFirstOutputMs,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (isStreamText) {
|
|
761
|
+
state.stepSpan.addEvent('ai.stream.finish', {
|
|
762
|
+
'ai.response.msToFinish': event.performance.responseTimeMs,
|
|
763
|
+
'ai.response.avgOutputTokensPerSecond':
|
|
764
|
+
event.performance.effectiveOutputTokensPerSecond,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
709
768
|
state.stepSpan.end();
|
|
710
769
|
state.stepSpan = undefined;
|
|
711
770
|
state.stepContext = undefined;
|
|
712
771
|
}
|
|
713
772
|
|
|
714
|
-
|
|
773
|
+
/** @deprecated Use `onStepEnd` instead. */
|
|
774
|
+
onStepFinish(event: GenerateTextStepEndEvent<ToolSet>): void {
|
|
775
|
+
this.onStepEnd(event);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
onEnd(
|
|
715
779
|
event:
|
|
716
|
-
|
|
|
717
|
-
|
|
|
718
|
-
|
|
|
719
|
-
|
|
|
780
|
+
| GenerateTextEndEvent<ToolSet>
|
|
781
|
+
| GenerateObjectEndEvent<unknown>
|
|
782
|
+
| EmbedEndEvent
|
|
783
|
+
| RerankEndEvent,
|
|
720
784
|
): void {
|
|
721
785
|
const state = this.getCallState(event.callId);
|
|
722
786
|
if (!state?.rootSpan) return;
|
|
@@ -725,12 +789,12 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
725
789
|
state.operationId === 'ai.embed' ||
|
|
726
790
|
state.operationId === 'ai.embedMany'
|
|
727
791
|
) {
|
|
728
|
-
this.
|
|
792
|
+
this.onEmbedOperationEnd(event as EmbedEndEvent);
|
|
729
793
|
return;
|
|
730
794
|
}
|
|
731
795
|
|
|
732
796
|
if (state.operationId === 'ai.rerank') {
|
|
733
|
-
this.
|
|
797
|
+
this.onRerankOperationEnd(event as RerankEndEvent);
|
|
734
798
|
return;
|
|
735
799
|
}
|
|
736
800
|
|
|
@@ -738,14 +802,14 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
738
802
|
state.operationId === 'ai.generateObject' ||
|
|
739
803
|
state.operationId === 'ai.streamObject'
|
|
740
804
|
) {
|
|
741
|
-
this.
|
|
805
|
+
this.onObjectOperationEnd(event as GenerateObjectEndEvent<unknown>);
|
|
742
806
|
return;
|
|
743
807
|
}
|
|
744
808
|
|
|
745
|
-
this.
|
|
809
|
+
this.onGenerateEnd(event as GenerateTextEndEvent<ToolSet>);
|
|
746
810
|
}
|
|
747
811
|
|
|
748
|
-
private
|
|
812
|
+
private onGenerateEnd(event: GenerateTextEndEvent<ToolSet>): void {
|
|
749
813
|
const state = this.getCallState(event.callId);
|
|
750
814
|
if (!state?.rootSpan) return;
|
|
751
815
|
|
|
@@ -759,8 +823,8 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
759
823
|
},
|
|
760
824
|
'ai.response.reasoning': {
|
|
761
825
|
output: () =>
|
|
762
|
-
event.reasoning.length > 0
|
|
763
|
-
? event.reasoning
|
|
826
|
+
event.finalStep.reasoning.length > 0
|
|
827
|
+
? event.finalStep.reasoning
|
|
764
828
|
.filter(part => 'text' in part)
|
|
765
829
|
.map(part => part.text)
|
|
766
830
|
.join('\n')
|
|
@@ -790,25 +854,27 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
790
854
|
)
|
|
791
855
|
: undefined,
|
|
792
856
|
},
|
|
793
|
-
'ai.response.providerMetadata': event.providerMetadata
|
|
794
|
-
? JSON.stringify(event.providerMetadata)
|
|
857
|
+
'ai.response.providerMetadata': event.finalStep.providerMetadata
|
|
858
|
+
? JSON.stringify(event.finalStep.providerMetadata)
|
|
795
859
|
: undefined,
|
|
796
860
|
|
|
797
|
-
'ai.usage.inputTokens': event.
|
|
798
|
-
'ai.usage.outputTokens': event.
|
|
799
|
-
'ai.usage.totalTokens': event.
|
|
800
|
-
'ai.usage.reasoningTokens':
|
|
801
|
-
|
|
861
|
+
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
862
|
+
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
863
|
+
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
864
|
+
'ai.usage.reasoningTokens':
|
|
865
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
866
|
+
'ai.usage.cachedInputTokens':
|
|
867
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
802
868
|
'ai.usage.inputTokenDetails.noCacheTokens':
|
|
803
|
-
event.
|
|
869
|
+
event.usage.inputTokenDetails?.noCacheTokens,
|
|
804
870
|
'ai.usage.inputTokenDetails.cacheReadTokens':
|
|
805
|
-
event.
|
|
871
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
806
872
|
'ai.usage.inputTokenDetails.cacheWriteTokens':
|
|
807
|
-
event.
|
|
873
|
+
event.usage.inputTokenDetails?.cacheWriteTokens,
|
|
808
874
|
'ai.usage.outputTokenDetails.textTokens':
|
|
809
|
-
event.
|
|
875
|
+
event.usage.outputTokenDetails?.textTokens,
|
|
810
876
|
'ai.usage.outputTokenDetails.reasoningTokens':
|
|
811
|
-
event.
|
|
877
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
812
878
|
}),
|
|
813
879
|
);
|
|
814
880
|
|
|
@@ -816,7 +882,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
816
882
|
this.cleanupCallState(event.callId);
|
|
817
883
|
}
|
|
818
884
|
|
|
819
|
-
private
|
|
885
|
+
private onObjectOperationEnd(event: GenerateObjectEndEvent<unknown>): void {
|
|
820
886
|
const state = this.getCallState(event.callId);
|
|
821
887
|
if (!state?.rootSpan) return;
|
|
822
888
|
|
|
@@ -836,8 +902,10 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
836
902
|
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
837
903
|
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
838
904
|
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
839
|
-
'ai.usage.reasoningTokens':
|
|
840
|
-
|
|
905
|
+
'ai.usage.reasoningTokens':
|
|
906
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
907
|
+
'ai.usage.cachedInputTokens':
|
|
908
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
841
909
|
}),
|
|
842
910
|
);
|
|
843
911
|
|
|
@@ -845,7 +913,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
845
913
|
this.cleanupCallState(event.callId);
|
|
846
914
|
}
|
|
847
915
|
|
|
848
|
-
private
|
|
916
|
+
private onEmbedOperationEnd(event: EmbedEndEvent): void {
|
|
849
917
|
const state = this.getCallState(event.callId);
|
|
850
918
|
if (!state?.rootSpan) return;
|
|
851
919
|
|
|
@@ -874,7 +942,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
874
942
|
this.cleanupCallState(event.callId);
|
|
875
943
|
}
|
|
876
944
|
|
|
877
|
-
onEmbedStart(event:
|
|
945
|
+
onEmbedStart(event: EmbeddingModelCallStartEvent): void {
|
|
878
946
|
const state = this.getCallState(event.callId);
|
|
879
947
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
880
948
|
|
|
@@ -904,7 +972,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
904
972
|
});
|
|
905
973
|
}
|
|
906
974
|
|
|
907
|
-
|
|
975
|
+
onEmbedEnd(event: EmbeddingModelCallEndEvent): void {
|
|
908
976
|
const state = this.getCallState(event.callId);
|
|
909
977
|
if (!state) return;
|
|
910
978
|
|
|
@@ -928,13 +996,13 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
928
996
|
state.embedSpans.delete(event.embedCallId);
|
|
929
997
|
}
|
|
930
998
|
|
|
931
|
-
private onRerankOperationStart(
|
|
932
|
-
|
|
933
|
-
|
|
999
|
+
private onRerankOperationStart(
|
|
1000
|
+
event: InferTelemetryEvent<RerankStartEvent>,
|
|
1001
|
+
): void {
|
|
1002
|
+
const telemetry: TelemetryOptions = {
|
|
934
1003
|
recordInputs: event.recordInputs,
|
|
935
1004
|
recordOutputs: event.recordOutputs,
|
|
936
1005
|
functionId: event.functionId,
|
|
937
|
-
metadata: event.metadata,
|
|
938
1006
|
};
|
|
939
1007
|
|
|
940
1008
|
const settings: Record<string, unknown> = {
|
|
@@ -943,9 +1011,9 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
943
1011
|
|
|
944
1012
|
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
945
1013
|
model: { provider: event.provider, modelId: event.modelId },
|
|
946
|
-
telemetry,
|
|
947
1014
|
headers: event.headers,
|
|
948
1015
|
settings,
|
|
1016
|
+
context: undefined,
|
|
949
1017
|
});
|
|
950
1018
|
|
|
951
1019
|
const attributes = selectAttributes(telemetry, {
|
|
@@ -977,7 +1045,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
977
1045
|
});
|
|
978
1046
|
}
|
|
979
1047
|
|
|
980
|
-
private
|
|
1048
|
+
private onRerankOperationEnd(event: RerankEndEvent): void {
|
|
981
1049
|
const state = this.getCallState(event.callId);
|
|
982
1050
|
if (!state?.rootSpan) return;
|
|
983
1051
|
|
|
@@ -985,7 +1053,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
985
1053
|
this.cleanupCallState(event.callId);
|
|
986
1054
|
}
|
|
987
1055
|
|
|
988
|
-
onRerankStart(event:
|
|
1056
|
+
onRerankStart(event: RerankingModelCallStartEvent): void {
|
|
989
1057
|
const state = this.getCallState(event.callId);
|
|
990
1058
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
991
1059
|
|
|
@@ -1012,7 +1080,7 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
1012
1080
|
state.rerankSpan = { span: rerankSpan, context: rerankContext };
|
|
1013
1081
|
}
|
|
1014
1082
|
|
|
1015
|
-
|
|
1083
|
+
onRerankEnd(event: RerankingModelCallEndEvent): void {
|
|
1016
1084
|
const state = this.getCallState(event.callId);
|
|
1017
1085
|
if (!state?.rerankSpan) return;
|
|
1018
1086
|
|
|
@@ -1032,37 +1100,33 @@ export class OpenTelemetryIntegration implements TelemetryIntegration {
|
|
|
1032
1100
|
state.rerankSpan = undefined;
|
|
1033
1101
|
}
|
|
1034
1102
|
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
callId?: unknown;
|
|
1039
|
-
attributes?: unknown;
|
|
1040
|
-
};
|
|
1103
|
+
onAbort(event: GenerateTextAbortEvent<ToolSet>): void {
|
|
1104
|
+
const state = this.getCallState(event.callId);
|
|
1105
|
+
if (!state?.rootSpan) return;
|
|
1041
1106
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1107
|
+
for (const { span: toolSpan } of state.toolSpans.values()) {
|
|
1108
|
+
toolSpan.end();
|
|
1044
1109
|
}
|
|
1110
|
+
state.toolSpans.clear();
|
|
1045
1111
|
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
return;
|
|
1112
|
+
if (state.stepSpan) {
|
|
1113
|
+
state.stepSpan.end();
|
|
1114
|
+
state.stepSpan = undefined;
|
|
1115
|
+
state.stepContext = undefined;
|
|
1051
1116
|
}
|
|
1052
1117
|
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
Object.entries(
|
|
1058
|
-
(chunk.attributes as Record<string, unknown>) ?? {},
|
|
1059
|
-
).filter(([, value]) => value != null),
|
|
1060
|
-
) as Attributes;
|
|
1118
|
+
for (const { span: embedSpan } of state.embedSpans.values()) {
|
|
1119
|
+
embedSpan.end();
|
|
1120
|
+
}
|
|
1121
|
+
state.embedSpans.clear();
|
|
1061
1122
|
|
|
1062
|
-
state.
|
|
1063
|
-
|
|
1064
|
-
state.
|
|
1123
|
+
if (state.rerankSpan) {
|
|
1124
|
+
state.rerankSpan.span.end();
|
|
1125
|
+
state.rerankSpan = undefined;
|
|
1065
1126
|
}
|
|
1127
|
+
|
|
1128
|
+
state.rootSpan.end();
|
|
1129
|
+
this.cleanupCallState(event.callId);
|
|
1066
1130
|
}
|
|
1067
1131
|
|
|
1068
1132
|
onError(error: unknown): void {
|