@ai-sdk/otel 1.0.0-beta.59 → 1.0.0-beta.60
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 +16 -0
- package/dist/index.d.ts +46 -5
- package/dist/index.js +344 -61
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/gen-ai-format-messages.ts +74 -3
- package/src/get-base-telemetry-attributes.ts +1 -1
- package/src/get-tracer.ts +1 -1
- package/src/legacy-open-telemetry.ts +6 -6
- package/src/mock-tracer.ts +1 -1
- package/src/noop-tracer.ts +1 -1
- package/src/open-telemetry.ts +234 -97
- package/src/record-span.ts +3 -4
- package/src/select-attributes.ts +57 -0
- package/src/stringify-for-telemetry.ts +23 -5
- package/src/supplemental-attributes.ts +151 -0
package/src/open-telemetry.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Attributes,
|
|
3
|
-
AttributeValue,
|
|
4
2
|
context,
|
|
5
|
-
Context as OpenTelemetryContext,
|
|
6
|
-
Span,
|
|
7
3
|
SpanKind,
|
|
8
|
-
SpanStatusCode,
|
|
9
4
|
trace,
|
|
10
|
-
|
|
5
|
+
type Attributes,
|
|
6
|
+
type Context as OpenTelemetryContext,
|
|
7
|
+
type Span,
|
|
8
|
+
type Tracer,
|
|
11
9
|
} from '@opentelemetry/api';
|
|
12
10
|
import type {
|
|
13
11
|
EmbeddingModelCallEndEvent,
|
|
@@ -45,74 +43,22 @@ import {
|
|
|
45
43
|
mapOperationName,
|
|
46
44
|
mapProviderName,
|
|
47
45
|
} from './gen-ai-format-messages';
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
function shouldRecord(
|
|
66
|
-
telemetry: TelemetryOptions | undefined,
|
|
67
|
-
): telemetry is TelemetryOptions {
|
|
68
|
-
return telemetry?.isEnabled !== false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function selectAttributes(
|
|
72
|
-
telemetry: TelemetryOptions | undefined,
|
|
73
|
-
attributes: Record<
|
|
74
|
-
string,
|
|
75
|
-
| AttributeValue
|
|
76
|
-
| { input: () => AttributeValue | undefined }
|
|
77
|
-
| { output: () => AttributeValue | undefined }
|
|
78
|
-
| undefined
|
|
79
|
-
>,
|
|
80
|
-
): Attributes {
|
|
81
|
-
if (!shouldRecord(telemetry)) {
|
|
82
|
-
return {};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const result: Attributes = {};
|
|
86
|
-
|
|
87
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
88
|
-
if (value == null) continue;
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
typeof value === 'object' &&
|
|
92
|
-
'input' in value &&
|
|
93
|
-
typeof value.input === 'function'
|
|
94
|
-
) {
|
|
95
|
-
if (telemetry?.recordInputs === false) continue;
|
|
96
|
-
const resolved = value.input();
|
|
97
|
-
if (resolved != null) result[key] = resolved;
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
typeof value === 'object' &&
|
|
103
|
-
'output' in value &&
|
|
104
|
-
typeof value.output === 'function'
|
|
105
|
-
) {
|
|
106
|
-
if (telemetry?.recordOutputs === false) continue;
|
|
107
|
-
const resolved = value.output();
|
|
108
|
-
if (resolved != null) result[key] = resolved;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
result[key] = value as AttributeValue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return result;
|
|
46
|
+
import { recordErrorOnSpan } from './record-span';
|
|
47
|
+
import { selectAttributes } from './select-attributes';
|
|
48
|
+
import {
|
|
49
|
+
getDetailedUsageAttributes,
|
|
50
|
+
getHeaderAttributes,
|
|
51
|
+
getRuntimeContextAttributes,
|
|
52
|
+
normalizeSupplementalAttributes,
|
|
53
|
+
selectSupplementalAttributes,
|
|
54
|
+
type OpenTelemetryOptions,
|
|
55
|
+
type SupplementalAttributeOptions,
|
|
56
|
+
} from './supplemental-attributes';
|
|
57
|
+
|
|
58
|
+
export type { OpenTelemetryOptions } from './supplemental-attributes';
|
|
59
|
+
|
|
60
|
+
interface OtelStepStartEvent extends GenerateTextStepStartEvent<ToolSet> {
|
|
61
|
+
readonly stepToolChoice?: unknown;
|
|
116
62
|
}
|
|
117
63
|
|
|
118
64
|
interface CallState {
|
|
@@ -130,19 +76,18 @@ interface CallState {
|
|
|
130
76
|
settings: Record<string, unknown>;
|
|
131
77
|
provider: string;
|
|
132
78
|
modelId: string;
|
|
79
|
+
baseSupplementalAttributes: Attributes;
|
|
133
80
|
}
|
|
134
81
|
|
|
135
82
|
export class OpenTelemetry implements Telemetry {
|
|
136
83
|
private readonly callStates = new Map<string, CallState>();
|
|
137
84
|
|
|
138
85
|
private readonly tracer: Tracer;
|
|
86
|
+
private readonly supplementalAttributes: SupplementalAttributeOptions;
|
|
139
87
|
|
|
140
|
-
constructor(
|
|
141
|
-
options: {
|
|
142
|
-
tracer?: Tracer;
|
|
143
|
-
} = {},
|
|
144
|
-
) {
|
|
88
|
+
constructor(options: OpenTelemetryOptions = {}) {
|
|
145
89
|
this.tracer = options.tracer ?? trace.getTracer('gen_ai');
|
|
90
|
+
this.supplementalAttributes = normalizeSupplementalAttributes(options);
|
|
146
91
|
}
|
|
147
92
|
|
|
148
93
|
private getCallState(callId: string): CallState | undefined {
|
|
@@ -229,6 +174,16 @@ export class OpenTelemetry implements Telemetry {
|
|
|
229
174
|
|
|
230
175
|
const providerName = mapProviderName(event.provider);
|
|
231
176
|
const operationName = mapOperationName(event.operationId);
|
|
177
|
+
const baseSupplementalAttributes = selectSupplementalAttributes(
|
|
178
|
+
telemetry,
|
|
179
|
+
this.supplementalAttributes,
|
|
180
|
+
{
|
|
181
|
+
runtimeContext: getRuntimeContextAttributes(
|
|
182
|
+
event.runtimeContext as Record<string, unknown> | undefined,
|
|
183
|
+
),
|
|
184
|
+
headers: getHeaderAttributes(event.headers),
|
|
185
|
+
},
|
|
186
|
+
);
|
|
232
187
|
|
|
233
188
|
const attributes = selectAttributes(telemetry, {
|
|
234
189
|
'gen_ai.operation.name': operationName,
|
|
@@ -260,6 +215,7 @@ export class OpenTelemetry implements Telemetry {
|
|
|
260
215
|
}),
|
|
261
216
|
),
|
|
262
217
|
},
|
|
218
|
+
...baseSupplementalAttributes,
|
|
263
219
|
});
|
|
264
220
|
|
|
265
221
|
const spanName = `${operationName} ${event.modelId}`;
|
|
@@ -284,6 +240,7 @@ export class OpenTelemetry implements Telemetry {
|
|
|
284
240
|
settings,
|
|
285
241
|
provider: event.provider,
|
|
286
242
|
modelId: event.modelId,
|
|
243
|
+
baseSupplementalAttributes,
|
|
287
244
|
});
|
|
288
245
|
}
|
|
289
246
|
|
|
@@ -309,6 +266,13 @@ export class OpenTelemetry implements Telemetry {
|
|
|
309
266
|
|
|
310
267
|
const providerName = mapProviderName(event.provider);
|
|
311
268
|
const operationName = mapOperationName(event.operationId);
|
|
269
|
+
const baseSupplementalAttributes = selectSupplementalAttributes(
|
|
270
|
+
telemetry,
|
|
271
|
+
this.supplementalAttributes,
|
|
272
|
+
{
|
|
273
|
+
headers: getHeaderAttributes(event.headers),
|
|
274
|
+
},
|
|
275
|
+
);
|
|
312
276
|
|
|
313
277
|
const attributes = selectAttributes(telemetry, {
|
|
314
278
|
'gen_ai.operation.name': operationName,
|
|
@@ -340,6 +304,17 @@ export class OpenTelemetry implements Telemetry {
|
|
|
340
304
|
}),
|
|
341
305
|
),
|
|
342
306
|
},
|
|
307
|
+
...baseSupplementalAttributes,
|
|
308
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
309
|
+
schema: {
|
|
310
|
+
'ai.schema': event.schema
|
|
311
|
+
? { input: () => JSON.stringify(event.schema) }
|
|
312
|
+
: undefined,
|
|
313
|
+
'ai.schema.name': event.schemaName,
|
|
314
|
+
'ai.schema.description': event.schemaDescription,
|
|
315
|
+
'ai.settings.output': event.output,
|
|
316
|
+
},
|
|
317
|
+
}),
|
|
343
318
|
});
|
|
344
319
|
|
|
345
320
|
const spanName = `${operationName} ${event.modelId}`;
|
|
@@ -364,6 +339,7 @@ export class OpenTelemetry implements Telemetry {
|
|
|
364
339
|
settings,
|
|
365
340
|
provider: event.provider,
|
|
366
341
|
modelId: event.modelId,
|
|
342
|
+
baseSupplementalAttributes,
|
|
367
343
|
});
|
|
368
344
|
}
|
|
369
345
|
|
|
@@ -400,6 +376,7 @@ export class OpenTelemetry implements Telemetry {
|
|
|
400
376
|
? JSON.stringify(formatInputMessages(event.promptMessages))
|
|
401
377
|
: undefined,
|
|
402
378
|
},
|
|
379
|
+
...state.baseSupplementalAttributes,
|
|
403
380
|
});
|
|
404
381
|
|
|
405
382
|
const spanName = `chat ${event.modelId}`;
|
|
@@ -443,6 +420,18 @@ export class OpenTelemetry implements Telemetry {
|
|
|
443
420
|
}
|
|
444
421
|
},
|
|
445
422
|
},
|
|
423
|
+
...selectSupplementalAttributes(
|
|
424
|
+
telemetry,
|
|
425
|
+
this.supplementalAttributes,
|
|
426
|
+
{
|
|
427
|
+
providerMetadata: {
|
|
428
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
429
|
+
? JSON.stringify(event.providerMetadata)
|
|
430
|
+
: undefined,
|
|
431
|
+
},
|
|
432
|
+
usage: getDetailedUsageAttributes(event.usage),
|
|
433
|
+
},
|
|
434
|
+
),
|
|
446
435
|
}),
|
|
447
436
|
);
|
|
448
437
|
|
|
@@ -460,16 +449,35 @@ export class OpenTelemetry implements Telemetry {
|
|
|
460
449
|
functionId: event.functionId,
|
|
461
450
|
};
|
|
462
451
|
|
|
463
|
-
const settings: Record<string, unknown> = {
|
|
464
|
-
maxRetries: event.maxRetries,
|
|
465
|
-
};
|
|
466
|
-
|
|
467
452
|
const providerName = mapProviderName(event.provider);
|
|
453
|
+
const baseSupplementalAttributes = selectSupplementalAttributes(
|
|
454
|
+
telemetry,
|
|
455
|
+
this.supplementalAttributes,
|
|
456
|
+
{
|
|
457
|
+
headers: getHeaderAttributes(event.headers),
|
|
458
|
+
},
|
|
459
|
+
);
|
|
460
|
+
const value = event.value;
|
|
461
|
+
const isMany = event.operationId === 'ai.embedMany';
|
|
468
462
|
|
|
469
463
|
const attributes = selectAttributes(telemetry, {
|
|
470
464
|
'gen_ai.operation.name': 'embeddings',
|
|
471
465
|
'gen_ai.provider.name': providerName,
|
|
472
466
|
'gen_ai.request.model': event.modelId,
|
|
467
|
+
...baseSupplementalAttributes,
|
|
468
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
469
|
+
embedding: isMany
|
|
470
|
+
? {
|
|
471
|
+
'ai.values': {
|
|
472
|
+
input: () => (value as string[]).map(v => JSON.stringify(v)),
|
|
473
|
+
},
|
|
474
|
+
}
|
|
475
|
+
: {
|
|
476
|
+
'ai.value': {
|
|
477
|
+
input: () => JSON.stringify(value),
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
}),
|
|
473
481
|
});
|
|
474
482
|
|
|
475
483
|
const spanName = `embeddings ${event.modelId}`;
|
|
@@ -491,19 +499,31 @@ export class OpenTelemetry implements Telemetry {
|
|
|
491
499
|
embedSpans: new Map(),
|
|
492
500
|
rerankSpan: undefined,
|
|
493
501
|
toolSpans: new Map(),
|
|
494
|
-
settings,
|
|
502
|
+
settings: { maxRetries: event.maxRetries },
|
|
495
503
|
provider: event.provider,
|
|
496
504
|
modelId: event.modelId,
|
|
505
|
+
baseSupplementalAttributes,
|
|
497
506
|
});
|
|
498
507
|
}
|
|
499
508
|
|
|
500
|
-
onStepStart(event:
|
|
509
|
+
onStepStart(event: OtelStepStartEvent): void {
|
|
501
510
|
const state = this.getCallState(event.callId);
|
|
502
511
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
503
512
|
|
|
504
513
|
const { telemetry } = state;
|
|
505
514
|
const stepAttributes = selectAttributes(telemetry, {
|
|
506
515
|
'gen_ai.operation.name': 'agent_step',
|
|
516
|
+
...state.baseSupplementalAttributes,
|
|
517
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
518
|
+
toolChoice: {
|
|
519
|
+
'ai.prompt.toolChoice': {
|
|
520
|
+
input: () =>
|
|
521
|
+
event.stepToolChoice != null
|
|
522
|
+
? JSON.stringify(event.stepToolChoice)
|
|
523
|
+
: undefined,
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
}),
|
|
507
527
|
});
|
|
508
528
|
|
|
509
529
|
state.stepSpan = this.tracer.startSpan(
|
|
@@ -605,6 +625,13 @@ export class OpenTelemetry implements Telemetry {
|
|
|
605
625
|
}),
|
|
606
626
|
),
|
|
607
627
|
},
|
|
628
|
+
...selectSupplementalAttributes(
|
|
629
|
+
telemetry,
|
|
630
|
+
this.supplementalAttributes,
|
|
631
|
+
{
|
|
632
|
+
usage: getDetailedUsageAttributes(event.usage),
|
|
633
|
+
},
|
|
634
|
+
),
|
|
608
635
|
}),
|
|
609
636
|
);
|
|
610
637
|
|
|
@@ -668,7 +695,7 @@ export class OpenTelemetry implements Telemetry {
|
|
|
668
695
|
// JSON.stringify might fail for non-serializable results
|
|
669
696
|
}
|
|
670
697
|
} else {
|
|
671
|
-
|
|
698
|
+
recordErrorOnSpan(span, toolOutput.error);
|
|
672
699
|
}
|
|
673
700
|
|
|
674
701
|
span.end();
|
|
@@ -679,6 +706,19 @@ export class OpenTelemetry implements Telemetry {
|
|
|
679
706
|
const state = this.getCallState(event.callId);
|
|
680
707
|
if (!state?.stepSpan) return;
|
|
681
708
|
|
|
709
|
+
const { telemetry } = state;
|
|
710
|
+
|
|
711
|
+
state.stepSpan.setAttributes(
|
|
712
|
+
selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
713
|
+
providerMetadata: {
|
|
714
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
715
|
+
? JSON.stringify(event.providerMetadata)
|
|
716
|
+
: undefined,
|
|
717
|
+
},
|
|
718
|
+
usage: getDetailedUsageAttributes(event.usage),
|
|
719
|
+
}),
|
|
720
|
+
);
|
|
721
|
+
|
|
682
722
|
state.stepSpan.end();
|
|
683
723
|
state.stepSpan = undefined;
|
|
684
724
|
state.stepContext = undefined;
|
|
@@ -746,6 +786,18 @@ export class OpenTelemetry implements Telemetry {
|
|
|
746
786
|
}),
|
|
747
787
|
),
|
|
748
788
|
},
|
|
789
|
+
...selectSupplementalAttributes(
|
|
790
|
+
telemetry,
|
|
791
|
+
this.supplementalAttributes,
|
|
792
|
+
{
|
|
793
|
+
providerMetadata: {
|
|
794
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
795
|
+
? JSON.stringify(event.providerMetadata)
|
|
796
|
+
: undefined,
|
|
797
|
+
},
|
|
798
|
+
usage: getDetailedUsageAttributes(event.totalUsage),
|
|
799
|
+
},
|
|
800
|
+
),
|
|
749
801
|
}),
|
|
750
802
|
);
|
|
751
803
|
|
|
@@ -778,6 +830,18 @@ export class OpenTelemetry implements Telemetry {
|
|
|
778
830
|
)
|
|
779
831
|
: undefined,
|
|
780
832
|
},
|
|
833
|
+
...selectSupplementalAttributes(
|
|
834
|
+
telemetry,
|
|
835
|
+
this.supplementalAttributes,
|
|
836
|
+
{
|
|
837
|
+
providerMetadata: {
|
|
838
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
839
|
+
? JSON.stringify(event.providerMetadata)
|
|
840
|
+
: undefined,
|
|
841
|
+
},
|
|
842
|
+
usage: getDetailedUsageAttributes(event.usage),
|
|
843
|
+
},
|
|
844
|
+
),
|
|
781
845
|
}),
|
|
782
846
|
);
|
|
783
847
|
|
|
@@ -790,10 +854,31 @@ export class OpenTelemetry implements Telemetry {
|
|
|
790
854
|
if (!state?.rootSpan) return;
|
|
791
855
|
|
|
792
856
|
const { telemetry } = state;
|
|
857
|
+
const isMany = state.operationId === 'ai.embedMany';
|
|
793
858
|
|
|
794
859
|
state.rootSpan.setAttributes(
|
|
795
860
|
selectAttributes(telemetry, {
|
|
796
861
|
'gen_ai.usage.input_tokens': event.usage.tokens,
|
|
862
|
+
...selectSupplementalAttributes(
|
|
863
|
+
telemetry,
|
|
864
|
+
this.supplementalAttributes,
|
|
865
|
+
{
|
|
866
|
+
embedding: isMany
|
|
867
|
+
? {
|
|
868
|
+
'ai.embeddings': {
|
|
869
|
+
output: () =>
|
|
870
|
+
(event.embedding as number[][]).map(e =>
|
|
871
|
+
JSON.stringify(e),
|
|
872
|
+
),
|
|
873
|
+
},
|
|
874
|
+
}
|
|
875
|
+
: {
|
|
876
|
+
'ai.embedding': {
|
|
877
|
+
output: () => JSON.stringify(event.embedding),
|
|
878
|
+
},
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
),
|
|
797
882
|
}),
|
|
798
883
|
);
|
|
799
884
|
|
|
@@ -812,6 +897,14 @@ export class OpenTelemetry implements Telemetry {
|
|
|
812
897
|
'gen_ai.operation.name': 'embeddings',
|
|
813
898
|
'gen_ai.provider.name': providerName,
|
|
814
899
|
'gen_ai.request.model': state.modelId,
|
|
900
|
+
...state.baseSupplementalAttributes,
|
|
901
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
902
|
+
embedding: {
|
|
903
|
+
'ai.values': {
|
|
904
|
+
input: () => event.values.map(v => JSON.stringify(v)),
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
}),
|
|
815
908
|
});
|
|
816
909
|
|
|
817
910
|
const spanName = `embeddings ${state.modelId}`;
|
|
@@ -841,6 +934,18 @@ export class OpenTelemetry implements Telemetry {
|
|
|
841
934
|
span.setAttributes(
|
|
842
935
|
selectAttributes(telemetry, {
|
|
843
936
|
'gen_ai.usage.input_tokens': event.usage.tokens,
|
|
937
|
+
...selectSupplementalAttributes(
|
|
938
|
+
telemetry,
|
|
939
|
+
this.supplementalAttributes,
|
|
940
|
+
{
|
|
941
|
+
embedding: {
|
|
942
|
+
'ai.embeddings': {
|
|
943
|
+
output: () =>
|
|
944
|
+
event.embeddings.map(embedding => JSON.stringify(embedding)),
|
|
945
|
+
},
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
),
|
|
844
949
|
}),
|
|
845
950
|
);
|
|
846
951
|
|
|
@@ -857,16 +962,27 @@ export class OpenTelemetry implements Telemetry {
|
|
|
857
962
|
functionId: event.functionId,
|
|
858
963
|
};
|
|
859
964
|
|
|
860
|
-
const settings: Record<string, unknown> = {
|
|
861
|
-
maxRetries: event.maxRetries,
|
|
862
|
-
};
|
|
863
|
-
|
|
864
965
|
const providerName = mapProviderName(event.provider);
|
|
966
|
+
const baseSupplementalAttributes = selectSupplementalAttributes(
|
|
967
|
+
telemetry,
|
|
968
|
+
this.supplementalAttributes,
|
|
969
|
+
{
|
|
970
|
+
headers: getHeaderAttributes(event.headers),
|
|
971
|
+
},
|
|
972
|
+
);
|
|
865
973
|
|
|
866
974
|
const attributes = selectAttributes(telemetry, {
|
|
867
975
|
'gen_ai.operation.name': 'rerank',
|
|
868
976
|
'gen_ai.provider.name': providerName,
|
|
869
977
|
'gen_ai.request.model': event.modelId,
|
|
978
|
+
...baseSupplementalAttributes,
|
|
979
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
980
|
+
reranking: {
|
|
981
|
+
'ai.documents': {
|
|
982
|
+
input: () => event.documents.map(d => JSON.stringify(d)),
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
}),
|
|
870
986
|
});
|
|
871
987
|
|
|
872
988
|
const spanName = `rerank ${event.modelId}`;
|
|
@@ -888,9 +1004,10 @@ export class OpenTelemetry implements Telemetry {
|
|
|
888
1004
|
embedSpans: new Map(),
|
|
889
1005
|
rerankSpan: undefined,
|
|
890
1006
|
toolSpans: new Map(),
|
|
891
|
-
settings,
|
|
1007
|
+
settings: { maxRetries: event.maxRetries },
|
|
892
1008
|
provider: event.provider,
|
|
893
1009
|
modelId: event.modelId,
|
|
1010
|
+
baseSupplementalAttributes,
|
|
894
1011
|
});
|
|
895
1012
|
}
|
|
896
1013
|
|
|
@@ -913,6 +1030,14 @@ export class OpenTelemetry implements Telemetry {
|
|
|
913
1030
|
'gen_ai.operation.name': 'rerank',
|
|
914
1031
|
'gen_ai.provider.name': providerName,
|
|
915
1032
|
'gen_ai.request.model': state.modelId,
|
|
1033
|
+
...state.baseSupplementalAttributes,
|
|
1034
|
+
...selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
1035
|
+
reranking: {
|
|
1036
|
+
'ai.documents': {
|
|
1037
|
+
input: () => event.documents.map(d => JSON.stringify(d)),
|
|
1038
|
+
},
|
|
1039
|
+
},
|
|
1040
|
+
}),
|
|
916
1041
|
});
|
|
917
1042
|
|
|
918
1043
|
const spanName = `rerank ${state.modelId}`;
|
|
@@ -931,6 +1056,18 @@ export class OpenTelemetry implements Telemetry {
|
|
|
931
1056
|
if (!state?.rerankSpan) return;
|
|
932
1057
|
|
|
933
1058
|
const { span } = state.rerankSpan;
|
|
1059
|
+
const { telemetry } = state;
|
|
1060
|
+
|
|
1061
|
+
span.setAttributes(
|
|
1062
|
+
selectSupplementalAttributes(telemetry, this.supplementalAttributes, {
|
|
1063
|
+
reranking: {
|
|
1064
|
+
'ai.ranking.type': event.documentsType,
|
|
1065
|
+
'ai.ranking': {
|
|
1066
|
+
output: () => event.ranking.map(r => JSON.stringify(r)),
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
}),
|
|
1070
|
+
);
|
|
934
1071
|
|
|
935
1072
|
span.end();
|
|
936
1073
|
state.rerankSpan = undefined;
|
|
@@ -950,38 +1087,38 @@ export class OpenTelemetry implements Telemetry {
|
|
|
950
1087
|
const actualError = event.error ?? error;
|
|
951
1088
|
|
|
952
1089
|
for (const { span: toolSpan } of state.toolSpans.values()) {
|
|
953
|
-
|
|
1090
|
+
recordErrorOnSpan(toolSpan, actualError);
|
|
954
1091
|
toolSpan.end();
|
|
955
1092
|
}
|
|
956
1093
|
state.toolSpans.clear();
|
|
957
1094
|
|
|
958
1095
|
if (state.inferenceSpan) {
|
|
959
|
-
|
|
1096
|
+
recordErrorOnSpan(state.inferenceSpan, actualError);
|
|
960
1097
|
state.inferenceSpan.end();
|
|
961
1098
|
state.inferenceSpan = undefined;
|
|
962
1099
|
state.inferenceContext = undefined;
|
|
963
1100
|
}
|
|
964
1101
|
|
|
965
1102
|
if (state.stepSpan) {
|
|
966
|
-
|
|
1103
|
+
recordErrorOnSpan(state.stepSpan, actualError);
|
|
967
1104
|
state.stepSpan.end();
|
|
968
1105
|
state.stepSpan = undefined;
|
|
969
1106
|
state.stepContext = undefined;
|
|
970
1107
|
}
|
|
971
1108
|
|
|
972
1109
|
for (const { span: embedSpan } of state.embedSpans.values()) {
|
|
973
|
-
|
|
1110
|
+
recordErrorOnSpan(embedSpan, actualError);
|
|
974
1111
|
embedSpan.end();
|
|
975
1112
|
}
|
|
976
1113
|
state.embedSpans.clear();
|
|
977
1114
|
|
|
978
1115
|
if (state.rerankSpan) {
|
|
979
|
-
|
|
1116
|
+
recordErrorOnSpan(state.rerankSpan.span, actualError);
|
|
980
1117
|
state.rerankSpan.span.end();
|
|
981
1118
|
state.rerankSpan = undefined;
|
|
982
1119
|
}
|
|
983
1120
|
|
|
984
|
-
|
|
1121
|
+
recordErrorOnSpan(state.rootSpan, actualError);
|
|
985
1122
|
|
|
986
1123
|
state.rootSpan.end();
|
|
987
1124
|
this.cleanupCallState(event.callId);
|
package/src/record-span.ts
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Attributes, AttributeValue } from '@opentelemetry/api';
|
|
2
|
+
import type { TelemetryOptions } from 'ai';
|
|
3
|
+
|
|
4
|
+
export type AttributeSpec =
|
|
5
|
+
| AttributeValue
|
|
6
|
+
| { input: () => AttributeValue | undefined }
|
|
7
|
+
| { output: () => AttributeValue | undefined }
|
|
8
|
+
| undefined;
|
|
9
|
+
|
|
10
|
+
export type AttributeSpecMap = Record<string, AttributeSpec>;
|
|
11
|
+
|
|
12
|
+
function shouldRecord(
|
|
13
|
+
telemetry: TelemetryOptions | undefined,
|
|
14
|
+
): telemetry is TelemetryOptions {
|
|
15
|
+
return telemetry?.isEnabled !== false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function selectAttributes(
|
|
19
|
+
telemetry: TelemetryOptions | undefined,
|
|
20
|
+
attributes: AttributeSpecMap,
|
|
21
|
+
): Attributes {
|
|
22
|
+
if (!shouldRecord(telemetry)) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result: Attributes = {};
|
|
27
|
+
|
|
28
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
29
|
+
if (value == null) continue;
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
typeof value === 'object' &&
|
|
33
|
+
'input' in value &&
|
|
34
|
+
typeof value.input === 'function'
|
|
35
|
+
) {
|
|
36
|
+
if (telemetry?.recordInputs === false) continue;
|
|
37
|
+
const resolved = value.input();
|
|
38
|
+
if (resolved != null) result[key] = resolved;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
typeof value === 'object' &&
|
|
44
|
+
'output' in value &&
|
|
45
|
+
typeof value.output === 'function'
|
|
46
|
+
) {
|
|
47
|
+
if (telemetry?.recordOutputs === false) continue;
|
|
48
|
+
const resolved = value.output();
|
|
49
|
+
if (resolved != null) result[key] = resolved;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
result[key] = value as AttributeValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
LanguageModelV4Message,
|
|
3
3
|
LanguageModelV4Prompt,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
@@ -21,13 +21,31 @@ export function stringifyForTelemetry(prompt: LanguageModelV4Prompt): string {
|
|
|
21
21
|
part.type === 'file'
|
|
22
22
|
? {
|
|
23
23
|
...part,
|
|
24
|
-
data:
|
|
25
|
-
part.data instanceof Uint8Array
|
|
26
|
-
? convertDataContentToBase64String(part.data)
|
|
27
|
-
: part.data,
|
|
24
|
+
data: serializeFileData(part.data),
|
|
28
25
|
}
|
|
29
26
|
: part,
|
|
30
27
|
),
|
|
31
28
|
})),
|
|
32
29
|
);
|
|
33
30
|
}
|
|
31
|
+
|
|
32
|
+
function serializeFileData(
|
|
33
|
+
data:
|
|
34
|
+
| { type: 'data'; data: string | Uint8Array }
|
|
35
|
+
| { type: 'url'; url: URL }
|
|
36
|
+
| { type: 'reference'; reference: Record<string, string> }
|
|
37
|
+
| { type: 'text'; text: string },
|
|
38
|
+
): unknown {
|
|
39
|
+
switch (data.type) {
|
|
40
|
+
case 'data':
|
|
41
|
+
return data.data instanceof Uint8Array
|
|
42
|
+
? convertDataContentToBase64String(data.data)
|
|
43
|
+
: data.data;
|
|
44
|
+
case 'url':
|
|
45
|
+
return data.url.toString();
|
|
46
|
+
case 'reference':
|
|
47
|
+
return data.reference;
|
|
48
|
+
case 'text':
|
|
49
|
+
return data.text;
|
|
50
|
+
}
|
|
51
|
+
}
|