@ai-sdk/otel 1.0.110 → 1.0.111

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/otel",
3
- "version": "1.0.110",
3
+ "version": "1.0.111",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "@ai-sdk/provider": "4.0.17",
30
30
  "@opentelemetry/api": "1.9.1",
31
- "ai": "7.0.110"
31
+ "ai": "7.0.111"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@ai-sdk/provider-utils": "5.0.45",
@@ -26,6 +26,10 @@ import type {
26
26
  GenerateTextStepStartEvent,
27
27
  ToolExecutionEndEvent,
28
28
  ToolExecutionStartEvent,
29
+ Experimental_EvaluateEndEvent as EvaluateEndEvent,
30
+ Experimental_EvaluateStartEvent as EvaluateStartEvent,
31
+ Experimental_EvaluationModelCallEndEvent as EvaluationModelCallEndEvent,
32
+ Experimental_EvaluationModelCallStartEvent as EvaluationModelCallStartEvent,
29
33
  Output,
30
34
  RerankingModelCallEndEvent,
31
35
  RerankEndEvent,
@@ -136,6 +140,7 @@ interface CallState {
136
140
  stepContext: OpenTelemetryContext | undefined;
137
141
  embedSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
138
142
  rerankSpan: { span: Span; context: OpenTelemetryContext } | undefined;
143
+ evaluationSpan: { span: Span; context: OpenTelemetryContext } | undefined;
139
144
  toolSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
140
145
  baseTelemetryAttributes: Attributes;
141
146
  settings: Record<string, unknown>;
@@ -295,6 +300,7 @@ export class LegacyOpenTelemetry implements Telemetry {
295
300
  stepContext: undefined,
296
301
  embedSpans: new Map(),
297
302
  rerankSpan: undefined,
303
+ evaluationSpan: undefined,
298
304
  toolSpans: new Map(),
299
305
  baseTelemetryAttributes,
300
306
  settings,
@@ -362,6 +368,7 @@ export class LegacyOpenTelemetry implements Telemetry {
362
368
  stepContext: undefined,
363
369
  embedSpans: new Map(),
364
370
  rerankSpan: undefined,
371
+ evaluationSpan: undefined,
365
372
  toolSpans: new Map(),
366
373
  baseTelemetryAttributes,
367
374
  settings,
@@ -529,6 +536,7 @@ export class LegacyOpenTelemetry implements Telemetry {
529
536
  stepContext: undefined,
530
537
  embedSpans: new Map(),
531
538
  rerankSpan: undefined,
539
+ evaluationSpan: undefined,
532
540
  toolSpans: new Map(),
533
541
  baseTelemetryAttributes,
534
542
  settings,
@@ -1044,6 +1052,7 @@ export class LegacyOpenTelemetry implements Telemetry {
1044
1052
  stepContext: undefined,
1045
1053
  embedSpans: new Map(),
1046
1054
  rerankSpan: undefined,
1055
+ evaluationSpan: undefined,
1047
1056
  toolSpans: new Map(),
1048
1057
  baseTelemetryAttributes,
1049
1058
  settings,
@@ -1105,6 +1114,129 @@ export class LegacyOpenTelemetry implements Telemetry {
1105
1114
  state.rerankSpan = undefined;
1106
1115
  }
1107
1116
 
1117
+ private onEvaluateOperationStart(
1118
+ event: InferTelemetryEvent<EvaluateStartEvent>,
1119
+ ): void {
1120
+ const telemetry: TelemetryOptions = {
1121
+ recordInputs: event.recordInputs,
1122
+ recordOutputs: event.recordOutputs,
1123
+ functionId: event.functionId,
1124
+ };
1125
+ const settings: Record<string, unknown> = {
1126
+ maxRetries: event.maxRetries,
1127
+ };
1128
+ const baseTelemetryAttributes = getBaseTelemetryAttributes({
1129
+ model: { provider: event.provider, modelId: event.modelId },
1130
+ headers: event.headers,
1131
+ settings,
1132
+ context: event.runtimeContext,
1133
+ });
1134
+ const attributes = selectAttributes(telemetry, {
1135
+ ...assembleOperationName({ operationId: event.operationId, telemetry }),
1136
+ ...baseTelemetryAttributes,
1137
+ 'ai.evaluation.state': {
1138
+ input: () => JSON.stringify(event.state),
1139
+ },
1140
+ 'ai.evaluation.questions': {
1141
+ input: () => JSON.stringify(event.questions),
1142
+ },
1143
+ });
1144
+ const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
1145
+ const rootContext = trace.setSpan(context.active(), rootSpan);
1146
+
1147
+ this.callStates.set(event.callId, {
1148
+ operationId: event.operationId,
1149
+ telemetry,
1150
+ rootSpan,
1151
+ rootContext,
1152
+ stepSpan: undefined,
1153
+ stepContext: undefined,
1154
+ embedSpans: new Map(),
1155
+ rerankSpan: undefined,
1156
+ evaluationSpan: undefined,
1157
+ toolSpans: new Map(),
1158
+ baseTelemetryAttributes,
1159
+ settings,
1160
+ });
1161
+ }
1162
+
1163
+ private onEvaluateOperationEnd(event: EvaluateEndEvent): void {
1164
+ const state = this.getCallState(event.callId);
1165
+ if (!state?.rootSpan) return;
1166
+
1167
+ state.rootSpan.setAttributes(
1168
+ selectAttributes(state.telemetry, {
1169
+ 'ai.evaluation.answers': {
1170
+ output: () => JSON.stringify(event.answers),
1171
+ },
1172
+ }),
1173
+ );
1174
+ state.rootSpan.end();
1175
+ this.cleanupCallState(event.callId);
1176
+ }
1177
+
1178
+ experimental_onEvaluateStart(
1179
+ event: InferTelemetryEvent<EvaluateStartEvent>,
1180
+ ): void {
1181
+ this.onEvaluateOperationStart(event);
1182
+ }
1183
+
1184
+ experimental_onEvaluateEnd(event: EvaluateEndEvent): void {
1185
+ this.onEvaluateOperationEnd(event);
1186
+ }
1187
+
1188
+ experimental_onEvaluationModelCallStart(
1189
+ event: EvaluationModelCallStartEvent,
1190
+ ): void {
1191
+ const state = this.getCallState(event.callId);
1192
+ if (!state?.rootSpan || !state.rootContext) return;
1193
+
1194
+ const attributes = selectAttributes(state.telemetry, {
1195
+ ...assembleOperationName({
1196
+ operationId: event.operationId,
1197
+ telemetry: state.telemetry,
1198
+ }),
1199
+ ...state.baseTelemetryAttributes,
1200
+ 'ai.evaluation.state': {
1201
+ input: () => JSON.stringify(event.state),
1202
+ },
1203
+ 'ai.evaluation.questions': {
1204
+ input: () => JSON.stringify(event.questions),
1205
+ },
1206
+ });
1207
+ const span = this.tracer.startSpan(
1208
+ event.operationId,
1209
+ { attributes },
1210
+ state.rootContext,
1211
+ );
1212
+ state.evaluationSpan = {
1213
+ span,
1214
+ context: trace.setSpan(state.rootContext, span),
1215
+ };
1216
+ }
1217
+
1218
+ experimental_onEvaluationModelCallEnd(
1219
+ event: EvaluationModelCallEndEvent,
1220
+ ): void {
1221
+ const state = this.getCallState(event.callId);
1222
+ if (!state?.evaluationSpan) return;
1223
+
1224
+ state.evaluationSpan.span.setAttributes(
1225
+ selectAttributes(state.telemetry, {
1226
+ 'ai.evaluation.answers': {
1227
+ output: () => JSON.stringify(event.answers),
1228
+ },
1229
+ 'ai.usage.inputTokens': event.usage?.inputTokens,
1230
+ 'ai.usage.outputTokens': event.usage?.outputTokens,
1231
+ 'ai.response.providerMetadata': event.providerMetadata
1232
+ ? JSON.stringify(event.providerMetadata)
1233
+ : undefined,
1234
+ }),
1235
+ );
1236
+ state.evaluationSpan.span.end();
1237
+ state.evaluationSpan = undefined;
1238
+ }
1239
+
1108
1240
  onAbort(event: GenerateTextAbortEvent<ToolSet>): void {
1109
1241
  const state = this.getCallState(event.callId);
1110
1242
  if (!state?.rootSpan) return;
@@ -1130,6 +1262,11 @@ export class LegacyOpenTelemetry implements Telemetry {
1130
1262
  state.rerankSpan = undefined;
1131
1263
  }
1132
1264
 
1265
+ if (state.evaluationSpan) {
1266
+ state.evaluationSpan.span.end();
1267
+ state.evaluationSpan = undefined;
1268
+ }
1269
+
1133
1270
  state.rootSpan.end();
1134
1271
  this.cleanupCallState(event.callId);
1135
1272
  }
@@ -1160,6 +1297,12 @@ export class LegacyOpenTelemetry implements Telemetry {
1160
1297
  state.rerankSpan = undefined;
1161
1298
  }
1162
1299
 
1300
+ if (state.evaluationSpan) {
1301
+ recordSpanError(state.evaluationSpan.span, actualError);
1302
+ state.evaluationSpan.span.end();
1303
+ state.evaluationSpan = undefined;
1304
+ }
1305
+
1163
1306
  recordSpanError(state.rootSpan, actualError);
1164
1307
 
1165
1308
  state.rootSpan.end();
@@ -25,6 +25,10 @@ import type {
25
25
  GenerateTextStepStartEvent,
26
26
  ToolExecutionEndEvent,
27
27
  ToolExecutionStartEvent,
28
+ Experimental_EvaluateEndEvent as EvaluateEndEvent,
29
+ Experimental_EvaluateStartEvent as EvaluateStartEvent,
30
+ Experimental_EvaluationModelCallEndEvent as EvaluationModelCallEndEvent,
31
+ Experimental_EvaluationModelCallStartEvent as EvaluationModelCallStartEvent,
28
32
  RerankingModelCallEndEvent,
29
33
  RerankEndEvent,
30
34
  RerankStartEvent,
@@ -80,6 +84,7 @@ interface CallState {
80
84
  inferenceToolDefinitions?: ReadonlyArray<Record<string, unknown>>;
81
85
  embedSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
82
86
  rerankSpan: { span: Span; context: OpenTelemetryContext } | undefined;
87
+ evaluationSpan: { span: Span; context: OpenTelemetryContext } | undefined;
83
88
  toolSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
84
89
  settings: Record<string, unknown>;
85
90
  provider: string;
@@ -327,6 +332,7 @@ export class OpenTelemetry implements Telemetry {
327
332
  inferenceContext: undefined,
328
333
  embedSpans: new Map(),
329
334
  rerankSpan: undefined,
335
+ evaluationSpan: undefined,
330
336
  toolSpans: new Map(),
331
337
  settings,
332
338
  provider: event.provider,
@@ -433,6 +439,7 @@ export class OpenTelemetry implements Telemetry {
433
439
  inferenceContext: undefined,
434
440
  embedSpans: new Map(),
435
441
  rerankSpan: undefined,
442
+ evaluationSpan: undefined,
436
443
  toolSpans: new Map(),
437
444
  settings,
438
445
  provider: event.provider,
@@ -631,6 +638,7 @@ export class OpenTelemetry implements Telemetry {
631
638
  inferenceContext: undefined,
632
639
  embedSpans: new Map(),
633
640
  rerankSpan: undefined,
641
+ evaluationSpan: undefined,
634
642
  toolSpans: new Map(),
635
643
  settings: { maxRetries: event.maxRetries },
636
644
  provider: event.provider,
@@ -1315,6 +1323,7 @@ export class OpenTelemetry implements Telemetry {
1315
1323
  inferenceContext: undefined,
1316
1324
  embedSpans: new Map(),
1317
1325
  rerankSpan: undefined,
1326
+ evaluationSpan: undefined,
1318
1327
  toolSpans: new Map(),
1319
1328
  settings: { maxRetries: event.maxRetries },
1320
1329
  provider: event.provider,
@@ -1395,6 +1404,182 @@ export class OpenTelemetry implements Telemetry {
1395
1404
  state.rerankSpan = undefined;
1396
1405
  }
1397
1406
 
1407
+ private onEvaluateOperationStart(
1408
+ event: InferTelemetryEvent<EvaluateStartEvent>,
1409
+ ): void {
1410
+ const telemetry: TelemetryOptions = {
1411
+ recordInputs: event.recordInputs,
1412
+ recordOutputs: event.recordOutputs,
1413
+ functionId: event.functionId,
1414
+ };
1415
+ const runtimeContext = event.runtimeContext;
1416
+ const baseSupplementalAttributes = selectSupplementalAttributes(
1417
+ telemetry,
1418
+ this.supplementalAttributes,
1419
+ {
1420
+ runtimeContext: getRuntimeContextAttributes(runtimeContext),
1421
+ headers: getHeaderAttributes(event.headers),
1422
+ },
1423
+ );
1424
+ const attributes = selectAttributes(telemetry, {
1425
+ 'gen_ai.operation.name': 'evaluate',
1426
+ 'gen_ai.provider.name': mapProviderName(event.provider),
1427
+ 'gen_ai.request.model': event.modelId,
1428
+ ...baseSupplementalAttributes,
1429
+ ...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
1430
+ experimental_evaluation: {
1431
+ 'ai.evaluation.state': {
1432
+ input: () => JSON.stringify(event.state),
1433
+ },
1434
+ 'ai.evaluation.questions': {
1435
+ input: () => JSON.stringify(event.questions),
1436
+ },
1437
+ },
1438
+ }),
1439
+ });
1440
+ const rootSpan = this.tracer.startSpan(`evaluate ${event.modelId}`, {
1441
+ attributes: this.getSpanAttributes({
1442
+ attributes,
1443
+ spanType: 'operation',
1444
+ operationId: event.operationId,
1445
+ callId: event.callId,
1446
+ runtimeContext,
1447
+ }),
1448
+ kind: SpanKind.CLIENT,
1449
+ });
1450
+ const rootContext = trace.setSpan(context.active(), rootSpan);
1451
+
1452
+ this.callStates.set(event.callId, {
1453
+ operationId: event.operationId,
1454
+ telemetry,
1455
+ rootSpan,
1456
+ rootContext,
1457
+ stepSpan: undefined,
1458
+ stepContext: undefined,
1459
+ inferenceSpan: undefined,
1460
+ inferenceContext: undefined,
1461
+ embedSpans: new Map(),
1462
+ rerankSpan: undefined,
1463
+ evaluationSpan: undefined,
1464
+ toolSpans: new Map(),
1465
+ settings: { maxRetries: event.maxRetries },
1466
+ provider: event.provider,
1467
+ modelId: event.modelId,
1468
+ runtimeContext,
1469
+ baseSupplementalAttributes,
1470
+ });
1471
+ }
1472
+
1473
+ private onEvaluateOperationEnd(event: EvaluateEndEvent): void {
1474
+ const state = this.getCallState(event.callId);
1475
+ if (!state?.rootSpan) return;
1476
+
1477
+ state.rootSpan.setAttributes(
1478
+ selectSupplementalAttributes(
1479
+ state.telemetry,
1480
+ this.supplementalAttributes,
1481
+ {
1482
+ experimental_evaluation: {
1483
+ 'ai.evaluation.answers': {
1484
+ output: () => JSON.stringify(event.answers),
1485
+ },
1486
+ },
1487
+ },
1488
+ ),
1489
+ );
1490
+ state.rootSpan.end();
1491
+ this.cleanupCallState(event.callId);
1492
+ }
1493
+
1494
+ experimental_onEvaluateStart(
1495
+ event: InferTelemetryEvent<EvaluateStartEvent>,
1496
+ ): void {
1497
+ this.onEvaluateOperationStart(event);
1498
+ }
1499
+
1500
+ experimental_onEvaluateEnd(event: EvaluateEndEvent): void {
1501
+ this.onEvaluateOperationEnd(event);
1502
+ }
1503
+
1504
+ experimental_onEvaluationModelCallStart(
1505
+ event: EvaluationModelCallStartEvent,
1506
+ ): void {
1507
+ const state = this.getCallState(event.callId);
1508
+ if (!state?.rootSpan || !state.rootContext) return;
1509
+
1510
+ const attributes = selectAttributes(state.telemetry, {
1511
+ 'gen_ai.operation.name': 'evaluate',
1512
+ 'gen_ai.provider.name': mapProviderName(state.provider),
1513
+ 'gen_ai.request.model': state.modelId,
1514
+ ...state.baseSupplementalAttributes,
1515
+ ...selectSupplementalAttributes(
1516
+ state.telemetry,
1517
+ this.supplementalAttributes,
1518
+ {
1519
+ experimental_evaluation: {
1520
+ 'ai.evaluation.state': {
1521
+ input: () => JSON.stringify(event.state),
1522
+ },
1523
+ 'ai.evaluation.questions': {
1524
+ input: () => JSON.stringify(event.questions),
1525
+ },
1526
+ },
1527
+ },
1528
+ ),
1529
+ });
1530
+ const span = this.tracer.startSpan(
1531
+ `evaluate ${state.modelId}`,
1532
+ {
1533
+ attributes: this.getSpanAttributes({
1534
+ attributes,
1535
+ spanType: 'experimental_evaluation',
1536
+ operationId: event.operationId,
1537
+ callId: event.callId,
1538
+ runtimeContext: state.runtimeContext,
1539
+ }),
1540
+ kind: SpanKind.CLIENT,
1541
+ },
1542
+ state.rootContext,
1543
+ );
1544
+
1545
+ state.evaluationSpan = {
1546
+ span,
1547
+ context: trace.setSpan(state.rootContext, span),
1548
+ };
1549
+ }
1550
+
1551
+ experimental_onEvaluationModelCallEnd(
1552
+ event: EvaluationModelCallEndEvent,
1553
+ ): void {
1554
+ const state = this.getCallState(event.callId);
1555
+ if (!state?.evaluationSpan) return;
1556
+
1557
+ state.evaluationSpan.span.setAttributes(
1558
+ selectAttributes(state.telemetry, {
1559
+ 'gen_ai.usage.input_tokens': event.usage?.inputTokens,
1560
+ 'gen_ai.usage.output_tokens': event.usage?.outputTokens,
1561
+ ...selectSupplementalAttributes(
1562
+ state.telemetry,
1563
+ this.supplementalAttributes,
1564
+ {
1565
+ experimental_evaluation: {
1566
+ 'ai.evaluation.answers': {
1567
+ output: () => JSON.stringify(event.answers),
1568
+ },
1569
+ },
1570
+ providerMetadata: {
1571
+ 'ai.response.providerMetadata': event.providerMetadata
1572
+ ? JSON.stringify(event.providerMetadata)
1573
+ : undefined,
1574
+ },
1575
+ },
1576
+ ),
1577
+ }),
1578
+ );
1579
+ state.evaluationSpan.span.end();
1580
+ state.evaluationSpan = undefined;
1581
+ }
1582
+
1398
1583
  onAbort(event: GenerateTextAbortEvent<ToolSet>): void {
1399
1584
  const state = this.getCallState(event.callId);
1400
1585
  if (!state?.rootSpan) return;
@@ -1426,6 +1611,11 @@ export class OpenTelemetry implements Telemetry {
1426
1611
  state.rerankSpan = undefined;
1427
1612
  }
1428
1613
 
1614
+ if (state.evaluationSpan) {
1615
+ state.evaluationSpan.span.end();
1616
+ state.evaluationSpan = undefined;
1617
+ }
1618
+
1429
1619
  state.rootSpan.end();
1430
1620
  this.cleanupCallState(event.callId);
1431
1621
  }
@@ -1471,6 +1661,12 @@ export class OpenTelemetry implements Telemetry {
1471
1661
  state.rerankSpan = undefined;
1472
1662
  }
1473
1663
 
1664
+ if (state.evaluationSpan) {
1665
+ recordErrorOnSpan(state.evaluationSpan.span, actualError);
1666
+ state.evaluationSpan.span.end();
1667
+ state.evaluationSpan = undefined;
1668
+ }
1669
+
1474
1670
  recordErrorOnSpan(state.rootSpan, actualError);
1475
1671
 
1476
1672
  state.rootSpan.end();
@@ -11,6 +11,7 @@ type SupplementalAttributeOption =
11
11
  | 'providerMetadata'
12
12
  | 'embedding'
13
13
  | 'reranking'
14
+ | 'experimental_evaluation'
14
15
  | 'runtimeContext'
15
16
  | 'headers'
16
17
  | 'toolChoice'
@@ -27,7 +28,8 @@ export type OpenTelemetrySpanType =
27
28
  | 'languageModel'
28
29
  | 'tool'
29
30
  | 'embedding'
30
- | 'reranking';
31
+ | 'reranking'
32
+ | 'experimental_evaluation';
31
33
 
32
34
  export type EnrichSpan = (options: {
33
35
  spanType: OpenTelemetrySpanType;
@@ -68,6 +70,11 @@ export type OpenTelemetryOptions = {
68
70
  */
69
71
  reranking?: boolean;
70
72
 
73
+ /**
74
+ * Emit evaluation state, questions, and answers.
75
+ */
76
+ experimental_evaluation?: boolean;
77
+
71
78
  /**
72
79
  * Emit runtime context values.
73
80
  */
@@ -94,6 +101,7 @@ const disabledSupplementalAttributes: SupplementalAttributeOptions = {
94
101
  providerMetadata: false,
95
102
  embedding: false,
96
103
  reranking: false,
104
+ experimental_evaluation: false,
97
105
  runtimeContext: false,
98
106
  headers: false,
99
107
  toolChoice: false,
@@ -109,6 +117,7 @@ export function normalizeSupplementalAttributes(
109
117
  providerMetadata: options.providerMetadata ?? false,
110
118
  embedding: options.embedding ?? false,
111
119
  reranking: options.reranking ?? false,
120
+ experimental_evaluation: options.experimental_evaluation ?? false,
112
121
  runtimeContext: options.runtimeContext ?? false,
113
122
  headers: options.headers ?? false,
114
123
  toolChoice: options.toolChoice ?? false,