@ai-sdk/otel 1.0.0-beta.57 → 1.0.0-beta.59
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 +48 -0
- package/dist/index.d.ts +29 -32
- package/dist/index.js +121 -73
- package/dist/index.js.map +1 -1
- package/package.json +9 -7
- package/src/index.ts +1 -1
- package/src/{gen-ai-open-telemetry.ts → legacy-open-telemetry.ts} +462 -282
- package/src/open-telemetry.ts +379 -485
|
@@ -6,45 +6,39 @@ import {
|
|
|
6
6
|
context,
|
|
7
7
|
Context as OpenTelemetryContext,
|
|
8
8
|
Span,
|
|
9
|
-
SpanKind,
|
|
10
9
|
SpanStatusCode,
|
|
11
10
|
trace,
|
|
12
11
|
Tracer,
|
|
13
12
|
} from '@opentelemetry/api';
|
|
14
13
|
import type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
EmbedOnStartEvent,
|
|
14
|
+
EmbeddingModelCallEndEvent,
|
|
15
|
+
EmbedEndEvent,
|
|
18
16
|
EmbedStartEvent,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
EmbeddingModelCallStartEvent,
|
|
18
|
+
GenerateObjectEndEvent,
|
|
19
|
+
GenerateObjectStartEvent,
|
|
20
|
+
GenerateObjectStepEndEvent,
|
|
21
|
+
GenerateObjectStepStartEvent,
|
|
22
|
+
StreamTextChunkEvent,
|
|
23
|
+
GenerateTextEndEvent,
|
|
24
|
+
GenerateTextStartEvent,
|
|
25
|
+
GenerateTextStepEndEvent,
|
|
26
|
+
GenerateTextStepStartEvent,
|
|
28
27
|
ToolExecutionEndEvent,
|
|
29
28
|
ToolExecutionStartEvent,
|
|
30
29
|
OutputInterface as Output,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
RerankOnStartEvent,
|
|
30
|
+
RerankingModelCallEndEvent,
|
|
31
|
+
RerankEndEvent,
|
|
34
32
|
RerankStartEvent,
|
|
33
|
+
RerankingModelCallStartEvent,
|
|
34
|
+
InferTelemetryEvent,
|
|
35
35
|
Telemetry,
|
|
36
36
|
TelemetryOptions,
|
|
37
37
|
ToolSet,
|
|
38
38
|
} from 'ai';
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
formatObjectOutputMessages,
|
|
43
|
-
formatOutputMessages,
|
|
44
|
-
formatSystemInstructions,
|
|
45
|
-
mapOperationName,
|
|
46
|
-
mapProviderName,
|
|
47
|
-
} from './gen-ai-format-messages';
|
|
39
|
+
import { assembleOperationName } from './assemble-operation-name';
|
|
40
|
+
import { getBaseTelemetryAttributes } from './get-base-telemetry-attributes';
|
|
41
|
+
import { stringifyForTelemetry } from './stringify-for-telemetry';
|
|
48
42
|
|
|
49
43
|
function recordSpanError(span: Span, error: unknown): void {
|
|
50
44
|
if (error instanceof Error) {
|
|
@@ -119,7 +113,7 @@ interface OtelStepStartEvent<
|
|
|
119
113
|
TOOLS extends ToolSet = ToolSet,
|
|
120
114
|
RUNTIME_CONTEXT extends AISDKContext = AISDKContext,
|
|
121
115
|
OUTPUT extends Output = Output,
|
|
122
|
-
> extends
|
|
116
|
+
> extends GenerateTextStepStartEvent<TOOLS, RUNTIME_CONTEXT, OUTPUT> {
|
|
123
117
|
readonly promptMessages?: LanguageModelV4Prompt;
|
|
124
118
|
readonly stepTools?: ReadonlyArray<Record<string, unknown>>;
|
|
125
119
|
readonly stepToolChoice?: unknown;
|
|
@@ -135,14 +129,16 @@ interface CallState {
|
|
|
135
129
|
embedSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
|
|
136
130
|
rerankSpan: { span: Span; context: OpenTelemetryContext } | undefined;
|
|
137
131
|
toolSpans: Map<string, { span: Span; context: OpenTelemetryContext }>;
|
|
132
|
+
baseTelemetryAttributes: Attributes;
|
|
138
133
|
settings: Record<string, unknown>;
|
|
139
|
-
provider: string;
|
|
140
|
-
modelId: string;
|
|
141
134
|
}
|
|
142
135
|
|
|
143
|
-
export class
|
|
136
|
+
export class LegacyOpenTelemetry implements Telemetry {
|
|
144
137
|
private readonly callStates = new Map<string, CallState>();
|
|
145
138
|
|
|
139
|
+
/**
|
|
140
|
+
* The tracer to use for the telemetry data.
|
|
141
|
+
*/
|
|
146
142
|
private readonly tracer: Tracer;
|
|
147
143
|
|
|
148
144
|
constructor(
|
|
@@ -150,7 +146,7 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
150
146
|
tracer?: Tracer;
|
|
151
147
|
} = {},
|
|
152
148
|
) {
|
|
153
|
-
this.tracer = options.tracer ?? trace.getTracer('
|
|
149
|
+
this.tracer = options.tracer ?? trace.getTracer('ai');
|
|
154
150
|
}
|
|
155
151
|
|
|
156
152
|
private getCallState(callId: string): CallState | undefined {
|
|
@@ -181,23 +177,23 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
181
177
|
|
|
182
178
|
onStart(
|
|
183
179
|
event:
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
|
|
|
180
|
+
| InferTelemetryEvent<GenerateTextStartEvent>
|
|
181
|
+
| InferTelemetryEvent<GenerateObjectStartEvent>
|
|
182
|
+
| InferTelemetryEvent<EmbedStartEvent>
|
|
183
|
+
| InferTelemetryEvent<RerankStartEvent>,
|
|
188
184
|
): void {
|
|
189
|
-
if (event.isEnabled === false) return;
|
|
190
|
-
|
|
191
185
|
if (
|
|
192
186
|
event.operationId === 'ai.embed' ||
|
|
193
187
|
event.operationId === 'ai.embedMany'
|
|
194
188
|
) {
|
|
195
|
-
this.onEmbedOperationStart(event as
|
|
189
|
+
this.onEmbedOperationStart(event as InferTelemetryEvent<EmbedStartEvent>);
|
|
196
190
|
return;
|
|
197
191
|
}
|
|
198
192
|
|
|
199
193
|
if (event.operationId === 'ai.rerank') {
|
|
200
|
-
this.onRerankOperationStart(
|
|
194
|
+
this.onRerankOperationStart(
|
|
195
|
+
event as InferTelemetryEvent<RerankStartEvent>,
|
|
196
|
+
);
|
|
201
197
|
return;
|
|
202
198
|
}
|
|
203
199
|
|
|
@@ -205,16 +201,19 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
205
201
|
event.operationId === 'ai.generateObject' ||
|
|
206
202
|
event.operationId === 'ai.streamObject'
|
|
207
203
|
) {
|
|
208
|
-
this.onObjectOperationStart(
|
|
204
|
+
this.onObjectOperationStart(
|
|
205
|
+
event as InferTelemetryEvent<GenerateObjectStartEvent>,
|
|
206
|
+
);
|
|
209
207
|
return;
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
this.onGenerateStart(event as
|
|
210
|
+
this.onGenerateStart(event as InferTelemetryEvent<GenerateTextStartEvent>);
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
private onGenerateStart(
|
|
213
|
+
private onGenerateStart(
|
|
214
|
+
event: InferTelemetryEvent<GenerateTextStartEvent>,
|
|
215
|
+
): void {
|
|
216
216
|
const telemetry: TelemetryOptions = {
|
|
217
|
-
isEnabled: event.isEnabled,
|
|
218
217
|
recordInputs: event.recordInputs,
|
|
219
218
|
recordOutputs: event.recordOutputs,
|
|
220
219
|
functionId: event.functionId,
|
|
@@ -232,46 +231,31 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
232
231
|
maxRetries: event.maxRetries,
|
|
233
232
|
};
|
|
234
233
|
|
|
235
|
-
const
|
|
236
|
-
|
|
234
|
+
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
235
|
+
model: { provider: event.provider, modelId: event.modelId },
|
|
236
|
+
headers: event.headers,
|
|
237
|
+
settings,
|
|
238
|
+
context: event.runtimeContext as Record<string, unknown> | undefined,
|
|
239
|
+
});
|
|
237
240
|
|
|
238
241
|
const attributes = selectAttributes(telemetry, {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
'
|
|
245
|
-
'
|
|
246
|
-
'
|
|
247
|
-
| number
|
|
248
|
-
| undefined,
|
|
249
|
-
'gen_ai.request.top_k': event.topK,
|
|
250
|
-
'gen_ai.request.top_p': event.topP,
|
|
251
|
-
'gen_ai.request.stop_sequences': event.stopSequences,
|
|
252
|
-
'gen_ai.request.seed': event.seed,
|
|
253
|
-
'gen_ai.system_instructions': event.system
|
|
254
|
-
? {
|
|
255
|
-
input: () =>
|
|
256
|
-
JSON.stringify(formatSystemInstructions(event.system!)),
|
|
257
|
-
}
|
|
258
|
-
: undefined,
|
|
259
|
-
'gen_ai.input.messages': {
|
|
242
|
+
...assembleOperationName({
|
|
243
|
+
operationId: event.operationId,
|
|
244
|
+
telemetry,
|
|
245
|
+
}),
|
|
246
|
+
...baseTelemetryAttributes,
|
|
247
|
+
'ai.model.provider': event.provider,
|
|
248
|
+
'ai.model.id': event.modelId,
|
|
249
|
+
'ai.prompt': {
|
|
260
250
|
input: () =>
|
|
261
|
-
JSON.stringify(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}),
|
|
266
|
-
),
|
|
251
|
+
JSON.stringify({
|
|
252
|
+
system: event.system,
|
|
253
|
+
messages: event.messages,
|
|
254
|
+
}),
|
|
267
255
|
},
|
|
268
256
|
});
|
|
269
257
|
|
|
270
|
-
const
|
|
271
|
-
const rootSpan = this.tracer.startSpan(spanName, {
|
|
272
|
-
attributes,
|
|
273
|
-
kind: SpanKind.INTERNAL,
|
|
274
|
-
});
|
|
258
|
+
const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
|
|
275
259
|
const rootContext = trace.setSpan(context.active(), rootSpan);
|
|
276
260
|
|
|
277
261
|
this.callStates.set(event.callId, {
|
|
@@ -284,15 +268,15 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
284
268
|
embedSpans: new Map(),
|
|
285
269
|
rerankSpan: undefined,
|
|
286
270
|
toolSpans: new Map(),
|
|
271
|
+
baseTelemetryAttributes,
|
|
287
272
|
settings,
|
|
288
|
-
provider: event.provider,
|
|
289
|
-
modelId: event.modelId,
|
|
290
273
|
});
|
|
291
274
|
}
|
|
292
275
|
|
|
293
|
-
private onObjectOperationStart(
|
|
276
|
+
private onObjectOperationStart(
|
|
277
|
+
event: InferTelemetryEvent<GenerateObjectStartEvent>,
|
|
278
|
+
): void {
|
|
294
279
|
const telemetry: TelemetryOptions = {
|
|
295
|
-
isEnabled: event.isEnabled,
|
|
296
280
|
recordInputs: event.recordInputs,
|
|
297
281
|
recordOutputs: event.recordOutputs,
|
|
298
282
|
functionId: event.functionId,
|
|
@@ -309,46 +293,36 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
309
293
|
maxRetries: event.maxRetries,
|
|
310
294
|
};
|
|
311
295
|
|
|
312
|
-
const
|
|
313
|
-
|
|
296
|
+
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
297
|
+
model: { provider: event.provider, modelId: event.modelId },
|
|
298
|
+
headers: event.headers,
|
|
299
|
+
settings,
|
|
300
|
+
context: undefined,
|
|
301
|
+
});
|
|
314
302
|
|
|
315
303
|
const attributes = selectAttributes(telemetry, {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
'
|
|
322
|
-
'gen_ai.request.max_tokens': event.maxOutputTokens,
|
|
323
|
-
'gen_ai.request.presence_penalty': event.presencePenalty,
|
|
324
|
-
'gen_ai.request.temperature': (event.temperature ?? undefined) as
|
|
325
|
-
| number
|
|
326
|
-
| undefined,
|
|
327
|
-
'gen_ai.request.top_k': event.topK,
|
|
328
|
-
'gen_ai.request.top_p': event.topP,
|
|
329
|
-
'gen_ai.request.seed': event.seed,
|
|
330
|
-
'gen_ai.system_instructions': event.system
|
|
331
|
-
? {
|
|
332
|
-
input: () =>
|
|
333
|
-
JSON.stringify(formatSystemInstructions(event.system!)),
|
|
334
|
-
}
|
|
335
|
-
: undefined,
|
|
336
|
-
'gen_ai.input.messages': {
|
|
304
|
+
...assembleOperationName({
|
|
305
|
+
operationId: event.operationId,
|
|
306
|
+
telemetry,
|
|
307
|
+
}),
|
|
308
|
+
...baseTelemetryAttributes,
|
|
309
|
+
'ai.prompt': {
|
|
337
310
|
input: () =>
|
|
338
|
-
JSON.stringify(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
),
|
|
311
|
+
JSON.stringify({
|
|
312
|
+
system: event.system,
|
|
313
|
+
prompt: event.prompt,
|
|
314
|
+
messages: event.messages,
|
|
315
|
+
}),
|
|
344
316
|
},
|
|
317
|
+
'ai.schema': event.schema
|
|
318
|
+
? { input: () => JSON.stringify(event.schema) }
|
|
319
|
+
: undefined,
|
|
320
|
+
'ai.schema.name': event.schemaName,
|
|
321
|
+
'ai.schema.description': event.schemaDescription,
|
|
322
|
+
'ai.settings.output': event.output,
|
|
345
323
|
});
|
|
346
324
|
|
|
347
|
-
const
|
|
348
|
-
const rootSpan = this.tracer.startSpan(spanName, {
|
|
349
|
-
attributes,
|
|
350
|
-
kind: SpanKind.INTERNAL,
|
|
351
|
-
});
|
|
325
|
+
const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
|
|
352
326
|
const rootContext = trace.setSpan(context.active(), rootSpan);
|
|
353
327
|
|
|
354
328
|
this.callStates.set(event.callId, {
|
|
@@ -361,25 +335,38 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
361
335
|
embedSpans: new Map(),
|
|
362
336
|
rerankSpan: undefined,
|
|
363
337
|
toolSpans: new Map(),
|
|
338
|
+
baseTelemetryAttributes,
|
|
364
339
|
settings,
|
|
365
|
-
provider: event.provider,
|
|
366
|
-
modelId: event.modelId,
|
|
367
340
|
});
|
|
368
341
|
}
|
|
369
342
|
|
|
370
343
|
/** @deprecated */
|
|
371
|
-
onObjectStepStart(event:
|
|
344
|
+
onObjectStepStart(event: GenerateObjectStepStartEvent): void {
|
|
372
345
|
const state = this.getCallState(event.callId);
|
|
373
346
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
374
347
|
|
|
375
348
|
const { telemetry } = state;
|
|
376
|
-
|
|
349
|
+
|
|
350
|
+
const stepOperationId =
|
|
351
|
+
state.operationId === 'ai.streamObject'
|
|
352
|
+
? 'ai.streamObject.doStream'
|
|
353
|
+
: 'ai.generateObject.doGenerate';
|
|
377
354
|
|
|
378
355
|
const attributes = selectAttributes(telemetry, {
|
|
379
|
-
|
|
380
|
-
|
|
356
|
+
...assembleOperationName({
|
|
357
|
+
operationId: stepOperationId,
|
|
358
|
+
telemetry,
|
|
359
|
+
}),
|
|
360
|
+
...state.baseTelemetryAttributes,
|
|
361
|
+
'ai.prompt.messages': {
|
|
362
|
+
input: () =>
|
|
363
|
+
event.promptMessages
|
|
364
|
+
? stringifyForTelemetry(event.promptMessages)
|
|
365
|
+
: undefined,
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
'gen_ai.system': event.provider,
|
|
381
369
|
'gen_ai.request.model': event.modelId,
|
|
382
|
-
'gen_ai.output.type': 'json',
|
|
383
370
|
'gen_ai.request.frequency_penalty': state.settings.frequencyPenalty as
|
|
384
371
|
| number
|
|
385
372
|
| undefined,
|
|
@@ -394,25 +381,18 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
394
381
|
| undefined,
|
|
395
382
|
'gen_ai.request.top_k': state.settings.topK as number | undefined,
|
|
396
383
|
'gen_ai.request.top_p': state.settings.topP as number | undefined,
|
|
397
|
-
'gen_ai.input.messages': {
|
|
398
|
-
input: () =>
|
|
399
|
-
event.promptMessages
|
|
400
|
-
? JSON.stringify(formatInputMessages(event.promptMessages))
|
|
401
|
-
: undefined,
|
|
402
|
-
},
|
|
403
384
|
});
|
|
404
385
|
|
|
405
|
-
const spanName = `chat ${event.modelId}`;
|
|
406
386
|
state.stepSpan = this.tracer.startSpan(
|
|
407
|
-
|
|
408
|
-
{ attributes
|
|
387
|
+
stepOperationId,
|
|
388
|
+
{ attributes },
|
|
409
389
|
state.rootContext,
|
|
410
390
|
);
|
|
411
391
|
state.stepContext = trace.setSpan(state.rootContext, state.stepSpan);
|
|
412
392
|
}
|
|
413
393
|
|
|
414
394
|
/** @deprecated */
|
|
415
|
-
onObjectStepFinish(event:
|
|
395
|
+
onObjectStepFinish(event: GenerateObjectStepEndEvent): void {
|
|
416
396
|
const state = this.getCallState(event.callId);
|
|
417
397
|
if (!state?.stepSpan) return;
|
|
418
398
|
|
|
@@ -420,37 +400,55 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
420
400
|
|
|
421
401
|
state.stepSpan.setAttributes(
|
|
422
402
|
selectAttributes(telemetry, {
|
|
423
|
-
'
|
|
424
|
-
'
|
|
425
|
-
'gen_ai.response.model': event.response.modelId,
|
|
426
|
-
'gen_ai.usage.input_tokens': event.usage.inputTokens,
|
|
427
|
-
'gen_ai.usage.output_tokens': event.usage.outputTokens,
|
|
428
|
-
'gen_ai.usage.cache_read.input_tokens': event.usage.cachedInputTokens,
|
|
429
|
-
'gen_ai.output.messages': {
|
|
403
|
+
'ai.response.finishReason': event.finishReason,
|
|
404
|
+
'ai.response.object': {
|
|
430
405
|
output: () => {
|
|
431
406
|
try {
|
|
432
|
-
return JSON.stringify(
|
|
433
|
-
formatObjectOutputMessages({
|
|
434
|
-
objectText: event.objectText,
|
|
435
|
-
finishReason: event.finishReason,
|
|
436
|
-
}),
|
|
437
|
-
);
|
|
407
|
+
return JSON.stringify(JSON.parse(event.objectText));
|
|
438
408
|
} catch {
|
|
439
409
|
return event.objectText;
|
|
440
410
|
}
|
|
441
411
|
},
|
|
442
412
|
},
|
|
413
|
+
'ai.response.id': event.response.id,
|
|
414
|
+
'ai.response.model': event.response.modelId,
|
|
415
|
+
'ai.response.timestamp': event.response.timestamp.toISOString(),
|
|
416
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
417
|
+
? JSON.stringify(event.providerMetadata)
|
|
418
|
+
: undefined,
|
|
419
|
+
|
|
420
|
+
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
421
|
+
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
422
|
+
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
423
|
+
'ai.usage.reasoningTokens': event.usage.reasoningTokens,
|
|
424
|
+
'ai.usage.cachedInputTokens': event.usage.cachedInputTokens,
|
|
425
|
+
|
|
426
|
+
'gen_ai.response.finish_reasons': [event.finishReason],
|
|
427
|
+
'gen_ai.response.id': event.response.id,
|
|
428
|
+
'gen_ai.response.model': event.response.modelId,
|
|
429
|
+
'gen_ai.usage.input_tokens': event.usage.inputTokens,
|
|
430
|
+
'gen_ai.usage.output_tokens': event.usage.outputTokens,
|
|
443
431
|
}),
|
|
444
432
|
);
|
|
445
433
|
|
|
434
|
+
if (event.msToFirstChunk != null) {
|
|
435
|
+
state.stepSpan.addEvent('ai.stream.firstChunk', {
|
|
436
|
+
'ai.stream.msToFirstChunk': event.msToFirstChunk,
|
|
437
|
+
});
|
|
438
|
+
state.stepSpan.setAttributes({
|
|
439
|
+
'ai.stream.msToFirstChunk': event.msToFirstChunk,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
446
443
|
state.stepSpan.end();
|
|
447
444
|
state.stepSpan = undefined;
|
|
448
445
|
state.stepContext = undefined;
|
|
449
446
|
}
|
|
450
447
|
|
|
451
|
-
private onEmbedOperationStart(
|
|
448
|
+
private onEmbedOperationStart(
|
|
449
|
+
event: InferTelemetryEvent<EmbedStartEvent>,
|
|
450
|
+
): void {
|
|
452
451
|
const telemetry: TelemetryOptions = {
|
|
453
|
-
isEnabled: event.isEnabled,
|
|
454
452
|
recordInputs: event.recordInputs,
|
|
455
453
|
recordOutputs: event.recordOutputs,
|
|
456
454
|
functionId: event.functionId,
|
|
@@ -460,19 +458,36 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
460
458
|
maxRetries: event.maxRetries,
|
|
461
459
|
};
|
|
462
460
|
|
|
463
|
-
const
|
|
461
|
+
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
462
|
+
model: { provider: event.provider, modelId: event.modelId },
|
|
463
|
+
headers: event.headers,
|
|
464
|
+
settings,
|
|
465
|
+
context: undefined,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const value = event.value;
|
|
469
|
+
const isMany = event.operationId === 'ai.embedMany';
|
|
464
470
|
|
|
465
471
|
const attributes = selectAttributes(telemetry, {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
472
|
+
...assembleOperationName({
|
|
473
|
+
operationId: event.operationId,
|
|
474
|
+
telemetry,
|
|
475
|
+
}),
|
|
476
|
+
...baseTelemetryAttributes,
|
|
477
|
+
...(isMany
|
|
478
|
+
? {
|
|
479
|
+
'ai.values': {
|
|
480
|
+
input: () => (value as string[]).map(v => JSON.stringify(v)),
|
|
481
|
+
},
|
|
482
|
+
}
|
|
483
|
+
: {
|
|
484
|
+
'ai.value': {
|
|
485
|
+
input: () => JSON.stringify(value),
|
|
486
|
+
},
|
|
487
|
+
}),
|
|
469
488
|
});
|
|
470
489
|
|
|
471
|
-
const
|
|
472
|
-
const rootSpan = this.tracer.startSpan(spanName, {
|
|
473
|
-
attributes,
|
|
474
|
-
kind: SpanKind.CLIENT,
|
|
475
|
-
});
|
|
490
|
+
const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
|
|
476
491
|
const rootContext = trace.setSpan(context.active(), rootSpan);
|
|
477
492
|
|
|
478
493
|
this.callStates.set(event.callId, {
|
|
@@ -485,9 +500,8 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
485
500
|
embedSpans: new Map(),
|
|
486
501
|
rerankSpan: undefined,
|
|
487
502
|
toolSpans: new Map(),
|
|
503
|
+
baseTelemetryAttributes,
|
|
488
504
|
settings,
|
|
489
|
-
provider: event.provider,
|
|
490
|
-
modelId: event.modelId,
|
|
491
505
|
});
|
|
492
506
|
}
|
|
493
507
|
|
|
@@ -496,11 +510,38 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
496
510
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
497
511
|
|
|
498
512
|
const { telemetry } = state;
|
|
499
|
-
|
|
513
|
+
|
|
514
|
+
const stepOperationId =
|
|
515
|
+
state.operationId === 'ai.streamText'
|
|
516
|
+
? 'ai.streamText.doStream'
|
|
517
|
+
: 'ai.generateText.doGenerate';
|
|
500
518
|
|
|
501
519
|
const attributes = selectAttributes(telemetry, {
|
|
502
|
-
|
|
503
|
-
|
|
520
|
+
...assembleOperationName({
|
|
521
|
+
operationId: stepOperationId,
|
|
522
|
+
telemetry,
|
|
523
|
+
}),
|
|
524
|
+
...state.baseTelemetryAttributes,
|
|
525
|
+
'ai.model.provider': event.provider,
|
|
526
|
+
'ai.model.id': event.modelId,
|
|
527
|
+
|
|
528
|
+
'ai.prompt.messages': {
|
|
529
|
+
input: () =>
|
|
530
|
+
event.promptMessages
|
|
531
|
+
? stringifyForTelemetry(event.promptMessages)
|
|
532
|
+
: undefined,
|
|
533
|
+
},
|
|
534
|
+
'ai.prompt.tools': {
|
|
535
|
+
input: () => event.stepTools?.map(tool => JSON.stringify(tool)),
|
|
536
|
+
},
|
|
537
|
+
'ai.prompt.toolChoice': {
|
|
538
|
+
input: () =>
|
|
539
|
+
event.stepToolChoice != null
|
|
540
|
+
? JSON.stringify(event.stepToolChoice)
|
|
541
|
+
: undefined,
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
'gen_ai.system': event.provider,
|
|
504
545
|
'gen_ai.request.model': event.modelId,
|
|
505
546
|
'gen_ai.request.frequency_penalty': state.settings.frequencyPenalty as
|
|
506
547
|
| number
|
|
@@ -519,22 +560,11 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
519
560
|
| undefined,
|
|
520
561
|
'gen_ai.request.top_k': state.settings.topK as number | undefined,
|
|
521
562
|
'gen_ai.request.top_p': state.settings.topP as number | undefined,
|
|
522
|
-
'gen_ai.input.messages': {
|
|
523
|
-
input: () =>
|
|
524
|
-
event.promptMessages
|
|
525
|
-
? JSON.stringify(formatInputMessages(event.promptMessages))
|
|
526
|
-
: undefined,
|
|
527
|
-
},
|
|
528
|
-
'gen_ai.tool.definitions': {
|
|
529
|
-
input: () =>
|
|
530
|
-
event.stepTools ? JSON.stringify(event.stepTools) : undefined,
|
|
531
|
-
},
|
|
532
563
|
});
|
|
533
564
|
|
|
534
|
-
const spanName = `chat ${event.modelId}`;
|
|
535
565
|
state.stepSpan = this.tracer.startSpan(
|
|
536
|
-
|
|
537
|
-
{ attributes
|
|
566
|
+
stepOperationId,
|
|
567
|
+
{ attributes },
|
|
538
568
|
state.rootContext,
|
|
539
569
|
);
|
|
540
570
|
state.stepContext = trace.setSpan(state.rootContext, state.stepSpan);
|
|
@@ -548,19 +578,20 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
548
578
|
const { toolCall } = event;
|
|
549
579
|
|
|
550
580
|
const attributes = selectAttributes(telemetry, {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
'
|
|
556
|
-
|
|
581
|
+
...assembleOperationName({
|
|
582
|
+
operationId: 'ai.toolCall',
|
|
583
|
+
telemetry,
|
|
584
|
+
}),
|
|
585
|
+
'ai.toolCall.name': toolCall.toolName,
|
|
586
|
+
'ai.toolCall.id': toolCall.toolCallId,
|
|
587
|
+
'ai.toolCall.args': {
|
|
588
|
+
output: () => JSON.stringify(toolCall.input),
|
|
557
589
|
},
|
|
558
590
|
});
|
|
559
591
|
|
|
560
|
-
const spanName = `execute_tool ${toolCall.toolName}`;
|
|
561
592
|
const toolSpan = this.tracer.startSpan(
|
|
562
|
-
|
|
563
|
-
{ attributes
|
|
593
|
+
'ai.toolCall',
|
|
594
|
+
{ attributes },
|
|
564
595
|
state.stepContext,
|
|
565
596
|
);
|
|
566
597
|
const toolContext = trace.setSpan(state.stepContext, toolSpan);
|
|
@@ -581,12 +612,13 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
581
612
|
const { span } = toolSpanEntry;
|
|
582
613
|
const { telemetry } = state;
|
|
583
614
|
|
|
584
|
-
|
|
615
|
+
const { toolOutput } = event;
|
|
616
|
+
if (toolOutput.type === 'tool-result') {
|
|
585
617
|
try {
|
|
586
618
|
span.setAttributes(
|
|
587
619
|
selectAttributes(telemetry, {
|
|
588
|
-
'
|
|
589
|
-
output: () => JSON.stringify(
|
|
620
|
+
'ai.toolCall.result': {
|
|
621
|
+
output: () => JSON.stringify(toolOutput.output),
|
|
590
622
|
},
|
|
591
623
|
}),
|
|
592
624
|
);
|
|
@@ -594,14 +626,14 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
594
626
|
// JSON.stringify might fail for non-serializable results
|
|
595
627
|
}
|
|
596
628
|
} else {
|
|
597
|
-
recordSpanError(span,
|
|
629
|
+
recordSpanError(span, toolOutput.error);
|
|
598
630
|
}
|
|
599
631
|
|
|
600
632
|
span.end();
|
|
601
633
|
state.toolSpans.delete(event.toolCall.toolCallId);
|
|
602
634
|
}
|
|
603
635
|
|
|
604
|
-
onStepFinish(event:
|
|
636
|
+
onStepFinish(event: GenerateTextStepEndEvent<ToolSet>): void {
|
|
605
637
|
const state = this.getCallState(event.callId);
|
|
606
638
|
if (!state?.stepSpan) return;
|
|
607
639
|
|
|
@@ -609,28 +641,71 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
609
641
|
|
|
610
642
|
state.stepSpan.setAttributes(
|
|
611
643
|
selectAttributes(telemetry, {
|
|
644
|
+
'ai.response.finishReason': event.finishReason,
|
|
645
|
+
'ai.response.text': {
|
|
646
|
+
output: () => event.text ?? undefined,
|
|
647
|
+
},
|
|
648
|
+
'ai.response.reasoning': {
|
|
649
|
+
output: () =>
|
|
650
|
+
event.reasoning.length > 0
|
|
651
|
+
? event.reasoning
|
|
652
|
+
.filter(part => 'text' in part)
|
|
653
|
+
.map(part => part.text)
|
|
654
|
+
.join('\n')
|
|
655
|
+
: undefined,
|
|
656
|
+
},
|
|
657
|
+
'ai.response.toolCalls': {
|
|
658
|
+
output: () =>
|
|
659
|
+
event.toolCalls.length > 0
|
|
660
|
+
? JSON.stringify(
|
|
661
|
+
event.toolCalls.map(toolCall => ({
|
|
662
|
+
toolCallId: toolCall.toolCallId,
|
|
663
|
+
toolName: toolCall.toolName,
|
|
664
|
+
input: toolCall.input,
|
|
665
|
+
})),
|
|
666
|
+
)
|
|
667
|
+
: undefined,
|
|
668
|
+
},
|
|
669
|
+
'ai.response.files': {
|
|
670
|
+
output: () =>
|
|
671
|
+
event.files.length > 0
|
|
672
|
+
? JSON.stringify(
|
|
673
|
+
event.files.map(file => ({
|
|
674
|
+
type: 'file',
|
|
675
|
+
mediaType: file.mediaType,
|
|
676
|
+
data: file.base64,
|
|
677
|
+
})),
|
|
678
|
+
)
|
|
679
|
+
: undefined,
|
|
680
|
+
},
|
|
681
|
+
'ai.response.id': event.response.id,
|
|
682
|
+
'ai.response.model': event.response.modelId,
|
|
683
|
+
'ai.response.timestamp': event.response.timestamp.toISOString(),
|
|
684
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
685
|
+
? JSON.stringify(event.providerMetadata)
|
|
686
|
+
: undefined,
|
|
687
|
+
|
|
688
|
+
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
689
|
+
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
690
|
+
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
691
|
+
'ai.usage.reasoningTokens': event.usage.reasoningTokens,
|
|
692
|
+
'ai.usage.cachedInputTokens': event.usage.cachedInputTokens,
|
|
693
|
+
'ai.usage.inputTokenDetails.noCacheTokens':
|
|
694
|
+
event.usage.inputTokenDetails?.noCacheTokens,
|
|
695
|
+
'ai.usage.inputTokenDetails.cacheReadTokens':
|
|
696
|
+
event.usage.inputTokenDetails?.cacheReadTokens,
|
|
697
|
+
'ai.usage.inputTokenDetails.cacheWriteTokens':
|
|
698
|
+
event.usage.inputTokenDetails?.cacheWriteTokens,
|
|
699
|
+
'ai.usage.outputTokenDetails.textTokens':
|
|
700
|
+
event.usage.outputTokenDetails?.textTokens,
|
|
701
|
+
'ai.usage.outputTokenDetails.reasoningTokens':
|
|
702
|
+
event.usage.outputTokenDetails?.reasoningTokens,
|
|
703
|
+
|
|
612
704
|
'gen_ai.response.finish_reasons': [event.finishReason],
|
|
613
705
|
'gen_ai.response.id': event.response.id,
|
|
614
706
|
'gen_ai.response.model': event.response.modelId,
|
|
615
707
|
'gen_ai.usage.input_tokens': event.usage.inputTokens,
|
|
616
708
|
'gen_ai.usage.output_tokens': event.usage.outputTokens,
|
|
617
|
-
'gen_ai.usage.cache_read.input_tokens':
|
|
618
|
-
event.usage.inputTokenDetails?.cacheReadTokens ??
|
|
619
|
-
event.usage.cachedInputTokens,
|
|
620
|
-
'gen_ai.usage.cache_creation.input_tokens':
|
|
621
|
-
event.usage.inputTokenDetails?.cacheWriteTokens,
|
|
622
|
-
'gen_ai.output.messages': {
|
|
623
|
-
output: () =>
|
|
624
|
-
JSON.stringify(
|
|
625
|
-
formatOutputMessages({
|
|
626
|
-
text: event.text ?? undefined,
|
|
627
|
-
reasoning: event.reasoning as ReadonlyArray<{ text?: string }>,
|
|
628
|
-
toolCalls: event.toolCalls,
|
|
629
|
-
files: event.files,
|
|
630
|
-
finishReason: event.finishReason,
|
|
631
|
-
}),
|
|
632
|
-
),
|
|
633
|
-
},
|
|
634
709
|
}),
|
|
635
710
|
);
|
|
636
711
|
|
|
@@ -641,10 +716,10 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
641
716
|
|
|
642
717
|
onFinish(
|
|
643
718
|
event:
|
|
644
|
-
|
|
|
645
|
-
|
|
|
646
|
-
|
|
|
647
|
-
|
|
|
719
|
+
| GenerateTextEndEvent<ToolSet>
|
|
720
|
+
| GenerateObjectEndEvent<unknown>
|
|
721
|
+
| EmbedEndEvent
|
|
722
|
+
| RerankEndEvent,
|
|
648
723
|
): void {
|
|
649
724
|
const state = this.getCallState(event.callId);
|
|
650
725
|
if (!state?.rootSpan) return;
|
|
@@ -653,12 +728,12 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
653
728
|
state.operationId === 'ai.embed' ||
|
|
654
729
|
state.operationId === 'ai.embedMany'
|
|
655
730
|
) {
|
|
656
|
-
this.onEmbedOperationFinish(event as
|
|
731
|
+
this.onEmbedOperationFinish(event as EmbedEndEvent);
|
|
657
732
|
return;
|
|
658
733
|
}
|
|
659
734
|
|
|
660
735
|
if (state.operationId === 'ai.rerank') {
|
|
661
|
-
this.onRerankOperationFinish(event as
|
|
736
|
+
this.onRerankOperationFinish(event as RerankEndEvent);
|
|
662
737
|
return;
|
|
663
738
|
}
|
|
664
739
|
|
|
@@ -666,14 +741,14 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
666
741
|
state.operationId === 'ai.generateObject' ||
|
|
667
742
|
state.operationId === 'ai.streamObject'
|
|
668
743
|
) {
|
|
669
|
-
this.onObjectOperationFinish(event as
|
|
744
|
+
this.onObjectOperationFinish(event as GenerateObjectEndEvent<unknown>);
|
|
670
745
|
return;
|
|
671
746
|
}
|
|
672
747
|
|
|
673
|
-
this.onGenerateFinish(event as
|
|
748
|
+
this.onGenerateFinish(event as GenerateTextEndEvent<ToolSet>);
|
|
674
749
|
}
|
|
675
750
|
|
|
676
|
-
private onGenerateFinish(event:
|
|
751
|
+
private onGenerateFinish(event: GenerateTextEndEvent<ToolSet>): void {
|
|
677
752
|
const state = this.getCallState(event.callId);
|
|
678
753
|
if (!state?.rootSpan) return;
|
|
679
754
|
|
|
@@ -681,26 +756,62 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
681
756
|
|
|
682
757
|
state.rootSpan.setAttributes(
|
|
683
758
|
selectAttributes(telemetry, {
|
|
684
|
-
'
|
|
685
|
-
'
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
759
|
+
'ai.response.finishReason': event.finishReason,
|
|
760
|
+
'ai.response.text': {
|
|
761
|
+
output: () => event.text ?? undefined,
|
|
762
|
+
},
|
|
763
|
+
'ai.response.reasoning': {
|
|
764
|
+
output: () =>
|
|
765
|
+
event.reasoning.length > 0
|
|
766
|
+
? event.reasoning
|
|
767
|
+
.filter(part => 'text' in part)
|
|
768
|
+
.map(part => part.text)
|
|
769
|
+
.join('\n')
|
|
770
|
+
: undefined,
|
|
771
|
+
},
|
|
772
|
+
'ai.response.toolCalls': {
|
|
773
|
+
output: () =>
|
|
774
|
+
event.toolCalls.length > 0
|
|
775
|
+
? JSON.stringify(
|
|
776
|
+
event.toolCalls.map(toolCall => ({
|
|
777
|
+
toolCallId: toolCall.toolCallId,
|
|
778
|
+
toolName: toolCall.toolName,
|
|
779
|
+
input: toolCall.input,
|
|
780
|
+
})),
|
|
781
|
+
)
|
|
782
|
+
: undefined,
|
|
783
|
+
},
|
|
784
|
+
'ai.response.files': {
|
|
693
785
|
output: () =>
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
786
|
+
event.files.length > 0
|
|
787
|
+
? JSON.stringify(
|
|
788
|
+
event.files.map(file => ({
|
|
789
|
+
type: 'file',
|
|
790
|
+
mediaType: file.mediaType,
|
|
791
|
+
data: file.base64,
|
|
792
|
+
})),
|
|
793
|
+
)
|
|
794
|
+
: undefined,
|
|
703
795
|
},
|
|
796
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
797
|
+
? JSON.stringify(event.providerMetadata)
|
|
798
|
+
: undefined,
|
|
799
|
+
|
|
800
|
+
'ai.usage.inputTokens': event.totalUsage.inputTokens,
|
|
801
|
+
'ai.usage.outputTokens': event.totalUsage.outputTokens,
|
|
802
|
+
'ai.usage.totalTokens': event.totalUsage.totalTokens,
|
|
803
|
+
'ai.usage.reasoningTokens': event.totalUsage.reasoningTokens,
|
|
804
|
+
'ai.usage.cachedInputTokens': event.totalUsage.cachedInputTokens,
|
|
805
|
+
'ai.usage.inputTokenDetails.noCacheTokens':
|
|
806
|
+
event.totalUsage.inputTokenDetails?.noCacheTokens,
|
|
807
|
+
'ai.usage.inputTokenDetails.cacheReadTokens':
|
|
808
|
+
event.totalUsage.inputTokenDetails?.cacheReadTokens,
|
|
809
|
+
'ai.usage.inputTokenDetails.cacheWriteTokens':
|
|
810
|
+
event.totalUsage.inputTokenDetails?.cacheWriteTokens,
|
|
811
|
+
'ai.usage.outputTokenDetails.textTokens':
|
|
812
|
+
event.totalUsage.outputTokenDetails?.textTokens,
|
|
813
|
+
'ai.usage.outputTokenDetails.reasoningTokens':
|
|
814
|
+
event.totalUsage.outputTokenDetails?.reasoningTokens,
|
|
704
815
|
}),
|
|
705
816
|
);
|
|
706
817
|
|
|
@@ -708,7 +819,9 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
708
819
|
this.cleanupCallState(event.callId);
|
|
709
820
|
}
|
|
710
821
|
|
|
711
|
-
private onObjectOperationFinish(
|
|
822
|
+
private onObjectOperationFinish(
|
|
823
|
+
event: GenerateObjectEndEvent<unknown>,
|
|
824
|
+
): void {
|
|
712
825
|
const state = this.getCallState(event.callId);
|
|
713
826
|
if (!state?.rootSpan) return;
|
|
714
827
|
|
|
@@ -716,21 +829,20 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
716
829
|
|
|
717
830
|
state.rootSpan.setAttributes(
|
|
718
831
|
selectAttributes(telemetry, {
|
|
719
|
-
'
|
|
720
|
-
'
|
|
721
|
-
'gen_ai.usage.output_tokens': event.usage.outputTokens,
|
|
722
|
-
'gen_ai.usage.cache_read.input_tokens': event.usage.cachedInputTokens,
|
|
723
|
-
'gen_ai.output.messages': {
|
|
832
|
+
'ai.response.finishReason': event.finishReason,
|
|
833
|
+
'ai.response.object': {
|
|
724
834
|
output: () =>
|
|
725
|
-
event.object != null
|
|
726
|
-
? JSON.stringify(
|
|
727
|
-
formatObjectOutputMessages({
|
|
728
|
-
objectText: JSON.stringify(event.object),
|
|
729
|
-
finishReason: event.finishReason,
|
|
730
|
-
}),
|
|
731
|
-
)
|
|
732
|
-
: undefined,
|
|
835
|
+
event.object != null ? JSON.stringify(event.object) : undefined,
|
|
733
836
|
},
|
|
837
|
+
'ai.response.providerMetadata': event.providerMetadata
|
|
838
|
+
? JSON.stringify(event.providerMetadata)
|
|
839
|
+
: undefined,
|
|
840
|
+
|
|
841
|
+
'ai.usage.inputTokens': event.usage.inputTokens,
|
|
842
|
+
'ai.usage.outputTokens': event.usage.outputTokens,
|
|
843
|
+
'ai.usage.totalTokens': event.usage.totalTokens,
|
|
844
|
+
'ai.usage.reasoningTokens': event.usage.reasoningTokens,
|
|
845
|
+
'ai.usage.cachedInputTokens': event.usage.cachedInputTokens,
|
|
734
846
|
}),
|
|
735
847
|
);
|
|
736
848
|
|
|
@@ -738,15 +850,28 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
738
850
|
this.cleanupCallState(event.callId);
|
|
739
851
|
}
|
|
740
852
|
|
|
741
|
-
private onEmbedOperationFinish(event:
|
|
853
|
+
private onEmbedOperationFinish(event: EmbedEndEvent): void {
|
|
742
854
|
const state = this.getCallState(event.callId);
|
|
743
855
|
if (!state?.rootSpan) return;
|
|
744
856
|
|
|
745
857
|
const { telemetry } = state;
|
|
858
|
+
const isMany = state.operationId === 'ai.embedMany';
|
|
746
859
|
|
|
747
860
|
state.rootSpan.setAttributes(
|
|
748
861
|
selectAttributes(telemetry, {
|
|
749
|
-
|
|
862
|
+
...(isMany
|
|
863
|
+
? {
|
|
864
|
+
'ai.embeddings': {
|
|
865
|
+
output: () =>
|
|
866
|
+
(event.embedding as number[][]).map(e => JSON.stringify(e)),
|
|
867
|
+
},
|
|
868
|
+
}
|
|
869
|
+
: {
|
|
870
|
+
'ai.embedding': {
|
|
871
|
+
output: () => JSON.stringify(event.embedding),
|
|
872
|
+
},
|
|
873
|
+
}),
|
|
874
|
+
'ai.usage.tokens': event.usage.tokens,
|
|
750
875
|
}),
|
|
751
876
|
);
|
|
752
877
|
|
|
@@ -754,23 +879,26 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
754
879
|
this.cleanupCallState(event.callId);
|
|
755
880
|
}
|
|
756
881
|
|
|
757
|
-
onEmbedStart(event:
|
|
882
|
+
onEmbedStart(event: EmbeddingModelCallStartEvent): void {
|
|
758
883
|
const state = this.getCallState(event.callId);
|
|
759
884
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
760
885
|
|
|
761
886
|
const { telemetry } = state;
|
|
762
|
-
const providerName = mapProviderName(state.provider);
|
|
763
887
|
|
|
764
888
|
const attributes = selectAttributes(telemetry, {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
889
|
+
...assembleOperationName({
|
|
890
|
+
operationId: event.operationId,
|
|
891
|
+
telemetry,
|
|
892
|
+
}),
|
|
893
|
+
...state.baseTelemetryAttributes,
|
|
894
|
+
'ai.values': {
|
|
895
|
+
input: () => event.values.map(v => JSON.stringify(v)),
|
|
896
|
+
},
|
|
768
897
|
});
|
|
769
898
|
|
|
770
|
-
const spanName = `embeddings ${state.modelId}`;
|
|
771
899
|
const embedSpan = this.tracer.startSpan(
|
|
772
|
-
|
|
773
|
-
{ attributes
|
|
900
|
+
event.operationId,
|
|
901
|
+
{ attributes },
|
|
774
902
|
state.rootContext,
|
|
775
903
|
);
|
|
776
904
|
const embedContext = trace.setSpan(state.rootContext, embedSpan);
|
|
@@ -781,7 +909,7 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
781
909
|
});
|
|
782
910
|
}
|
|
783
911
|
|
|
784
|
-
onEmbedFinish(event:
|
|
912
|
+
onEmbedFinish(event: EmbeddingModelCallEndEvent): void {
|
|
785
913
|
const state = this.getCallState(event.callId);
|
|
786
914
|
if (!state) return;
|
|
787
915
|
|
|
@@ -793,7 +921,11 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
793
921
|
|
|
794
922
|
span.setAttributes(
|
|
795
923
|
selectAttributes(telemetry, {
|
|
796
|
-
'
|
|
924
|
+
'ai.embeddings': {
|
|
925
|
+
output: () =>
|
|
926
|
+
event.embeddings.map(embedding => JSON.stringify(embedding)),
|
|
927
|
+
},
|
|
928
|
+
'ai.usage.tokens': event.usage.tokens,
|
|
797
929
|
}),
|
|
798
930
|
);
|
|
799
931
|
|
|
@@ -801,9 +933,10 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
801
933
|
state.embedSpans.delete(event.embedCallId);
|
|
802
934
|
}
|
|
803
935
|
|
|
804
|
-
private onRerankOperationStart(
|
|
936
|
+
private onRerankOperationStart(
|
|
937
|
+
event: InferTelemetryEvent<RerankStartEvent>,
|
|
938
|
+
): void {
|
|
805
939
|
const telemetry: TelemetryOptions = {
|
|
806
|
-
isEnabled: event.isEnabled,
|
|
807
940
|
recordInputs: event.recordInputs,
|
|
808
941
|
recordOutputs: event.recordOutputs,
|
|
809
942
|
functionId: event.functionId,
|
|
@@ -813,19 +946,25 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
813
946
|
maxRetries: event.maxRetries,
|
|
814
947
|
};
|
|
815
948
|
|
|
816
|
-
const
|
|
949
|
+
const baseTelemetryAttributes = getBaseTelemetryAttributes({
|
|
950
|
+
model: { provider: event.provider, modelId: event.modelId },
|
|
951
|
+
headers: event.headers,
|
|
952
|
+
settings,
|
|
953
|
+
context: undefined,
|
|
954
|
+
});
|
|
817
955
|
|
|
818
956
|
const attributes = selectAttributes(telemetry, {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
957
|
+
...assembleOperationName({
|
|
958
|
+
operationId: event.operationId,
|
|
959
|
+
telemetry,
|
|
960
|
+
}),
|
|
961
|
+
...baseTelemetryAttributes,
|
|
962
|
+
'ai.documents': {
|
|
963
|
+
input: () => event.documents.map(d => JSON.stringify(d)),
|
|
964
|
+
},
|
|
822
965
|
});
|
|
823
966
|
|
|
824
|
-
const
|
|
825
|
-
const rootSpan = this.tracer.startSpan(spanName, {
|
|
826
|
-
attributes,
|
|
827
|
-
kind: SpanKind.CLIENT,
|
|
828
|
-
});
|
|
967
|
+
const rootSpan = this.tracer.startSpan(event.operationId, { attributes });
|
|
829
968
|
const rootContext = trace.setSpan(context.active(), rootSpan);
|
|
830
969
|
|
|
831
970
|
this.callStates.set(event.callId, {
|
|
@@ -838,13 +977,12 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
838
977
|
embedSpans: new Map(),
|
|
839
978
|
rerankSpan: undefined,
|
|
840
979
|
toolSpans: new Map(),
|
|
980
|
+
baseTelemetryAttributes,
|
|
841
981
|
settings,
|
|
842
|
-
provider: event.provider,
|
|
843
|
-
modelId: event.modelId,
|
|
844
982
|
});
|
|
845
983
|
}
|
|
846
984
|
|
|
847
|
-
private onRerankOperationFinish(event:
|
|
985
|
+
private onRerankOperationFinish(event: RerankEndEvent): void {
|
|
848
986
|
const state = this.getCallState(event.callId);
|
|
849
987
|
if (!state?.rootSpan) return;
|
|
850
988
|
|
|
@@ -852,23 +990,26 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
852
990
|
this.cleanupCallState(event.callId);
|
|
853
991
|
}
|
|
854
992
|
|
|
855
|
-
onRerankStart(event:
|
|
993
|
+
onRerankStart(event: RerankingModelCallStartEvent): void {
|
|
856
994
|
const state = this.getCallState(event.callId);
|
|
857
995
|
if (!state?.rootSpan || !state.rootContext) return;
|
|
858
996
|
|
|
859
997
|
const { telemetry } = state;
|
|
860
|
-
const providerName = mapProviderName(state.provider);
|
|
861
998
|
|
|
862
999
|
const attributes = selectAttributes(telemetry, {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1000
|
+
...assembleOperationName({
|
|
1001
|
+
operationId: event.operationId,
|
|
1002
|
+
telemetry,
|
|
1003
|
+
}),
|
|
1004
|
+
...state.baseTelemetryAttributes,
|
|
1005
|
+
'ai.documents': {
|
|
1006
|
+
input: () => event.documents.map(d => JSON.stringify(d)),
|
|
1007
|
+
},
|
|
866
1008
|
});
|
|
867
1009
|
|
|
868
|
-
const spanName = `rerank ${state.modelId}`;
|
|
869
1010
|
const rerankSpan = this.tracer.startSpan(
|
|
870
|
-
|
|
871
|
-
{ attributes
|
|
1011
|
+
event.operationId,
|
|
1012
|
+
{ attributes },
|
|
872
1013
|
state.rootContext,
|
|
873
1014
|
);
|
|
874
1015
|
const rerankContext = trace.setSpan(state.rootContext, rerankSpan);
|
|
@@ -876,18 +1017,57 @@ export class GenAIOpenTelemetry implements Telemetry {
|
|
|
876
1017
|
state.rerankSpan = { span: rerankSpan, context: rerankContext };
|
|
877
1018
|
}
|
|
878
1019
|
|
|
879
|
-
onRerankFinish(event:
|
|
1020
|
+
onRerankFinish(event: RerankingModelCallEndEvent): void {
|
|
880
1021
|
const state = this.getCallState(event.callId);
|
|
881
1022
|
if (!state?.rerankSpan) return;
|
|
882
1023
|
|
|
883
1024
|
const { span } = state.rerankSpan;
|
|
1025
|
+
const { telemetry } = state;
|
|
1026
|
+
|
|
1027
|
+
span.setAttributes(
|
|
1028
|
+
selectAttributes(telemetry, {
|
|
1029
|
+
'ai.ranking.type': event.documentsType,
|
|
1030
|
+
'ai.ranking': {
|
|
1031
|
+
output: () => event.ranking.map(r => JSON.stringify(r)),
|
|
1032
|
+
},
|
|
1033
|
+
}),
|
|
1034
|
+
);
|
|
884
1035
|
|
|
885
1036
|
span.end();
|
|
886
1037
|
state.rerankSpan = undefined;
|
|
887
1038
|
}
|
|
888
1039
|
|
|
889
|
-
onChunk(
|
|
890
|
-
|
|
1040
|
+
onChunk(event: StreamTextChunkEvent<ToolSet>): void {
|
|
1041
|
+
const chunk = event.chunk as {
|
|
1042
|
+
type: string;
|
|
1043
|
+
callId?: unknown;
|
|
1044
|
+
attributes?: unknown;
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
if (typeof chunk.callId !== 'string') {
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (
|
|
1052
|
+
chunk.type !== 'ai.stream.firstChunk' &&
|
|
1053
|
+
chunk.type !== 'ai.stream.finish'
|
|
1054
|
+
) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const state = this.getCallState(chunk.callId);
|
|
1059
|
+
if (!state?.stepSpan) return;
|
|
1060
|
+
|
|
1061
|
+
const attributes = Object.fromEntries(
|
|
1062
|
+
Object.entries(
|
|
1063
|
+
(chunk.attributes as Record<string, unknown>) ?? {},
|
|
1064
|
+
).filter(([, value]) => value != null),
|
|
1065
|
+
) as Attributes;
|
|
1066
|
+
|
|
1067
|
+
state.stepSpan.addEvent(chunk.type, attributes);
|
|
1068
|
+
if (Object.keys(attributes).length > 0) {
|
|
1069
|
+
state.stepSpan.setAttributes(attributes);
|
|
1070
|
+
}
|
|
891
1071
|
}
|
|
892
1072
|
|
|
893
1073
|
onError(error: unknown): void {
|