1
+ import type { Client } from '../client' ;
1
2
import { SEMANTIC_ATTRIBUTE_SENTRY_OP , SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes' ;
2
- import type { Span , SpanAttributes , SpanOrigin } from '../types-hoist/span' ;
3
+ import type { Event } from '../types-hoist/event' ;
4
+ import type { Span , SpanAttributes , SpanJSON , SpanOrigin } from '../types-hoist/span' ;
3
5
import { spanToJSON } from './spanUtils' ;
4
6
import {
5
7
AI_MODEL_ID_ATTRIBUTE ,
@@ -25,9 +27,9 @@ function addOriginToSpan(span: Span, origin: SpanOrigin): void {
25
27
26
28
/**
27
29
* Post-process spans emitted by the Vercel AI SDK.
28
- * This is supposed to be used in `client.on('spanEnd ', ...)`, to ensure all data is already finished.
30
+ * This is supposed to be used in `client.on('spanStart ', ...)
29
31
*/
30
- export function processVercelAiSpan ( span : Span ) : void {
32
+ function onVercelAiSpanStart ( span : Span ) : void {
31
33
const { data : attributes , description : name } = spanToJSON ( span ) ;
32
34
33
35
if ( ! name ) {
@@ -38,7 +40,6 @@ export function processVercelAiSpan(span: Span): void {
38
40
// https://ai-sdk.dev/docs/ai-sdk-core/telemetry#tool-call-spans
39
41
if ( attributes [ AI_TOOL_CALL_NAME_ATTRIBUTE ] && attributes [ AI_TOOL_CALL_ID_ATTRIBUTE ] && name === 'ai.toolCall' ) {
40
42
processToolCallSpan ( span , attributes ) ;
41
- sharedProcessSpan ( span , attributes ) ;
42
43
return ;
43
44
}
44
45
@@ -52,7 +53,47 @@ export function processVercelAiSpan(span: Span): void {
52
53
}
53
54
54
55
processGenerateSpan ( span , name , attributes ) ;
55
- sharedProcessSpan ( span , attributes ) ;
56
+ }
57
+
58
+ const vercelAiEventProcessor = Object . assign (
59
+ ( event : Event ) : Event => {
60
+ if ( event . type === 'transaction' && event . spans ) {
61
+ for ( const span of event . spans ) {
62
+ // this mutates spans in-place
63
+ processEndedVercelAiSpan ( span ) ;
64
+ }
65
+ }
66
+ return event ;
67
+ } ,
68
+ { id : 'VercelAiEventProcessor' } ,
69
+ ) ;
70
+
71
+ /**
72
+ * Post-process spans emitted by the Vercel AI SDK.
73
+ */
74
+ function processEndedVercelAiSpan ( span : SpanJSON ) : void {
75
+ const { data : attributes , origin } = span ;
76
+
77
+ if ( origin !== 'auto.vercelai.otel' ) {
78
+ return ;
79
+ }
80
+
81
+ renameAttributeKey ( attributes , AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE , GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ) ;
82
+ renameAttributeKey ( attributes , AI_USAGE_PROMPT_TOKENS_ATTRIBUTE , GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ) ;
83
+
84
+ if (
85
+ typeof attributes [ GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ] === 'number' &&
86
+ typeof attributes [ GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ] === 'number'
87
+ ) {
88
+ attributes [ 'gen_ai.usage.total_tokens' ] =
89
+ attributes [ GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ] + attributes [ GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ] ;
90
+ }
91
+
92
+ // Rename AI SDK attributes to standardized gen_ai attributes
93
+ renameAttributeKey ( attributes , AI_PROMPT_MESSAGES_ATTRIBUTE , 'gen_ai.request.messages' ) ;
94
+ renameAttributeKey ( attributes , AI_RESPONSE_TEXT_ATTRIBUTE , 'gen_ai.response.text' ) ;
95
+ renameAttributeKey ( attributes , AI_RESPONSE_TOOL_CALLS_ATTRIBUTE , 'gen_ai.response.tool_calls' ) ;
96
+ renameAttributeKey ( attributes , AI_PROMPT_TOOLS_ATTRIBUTE , 'gen_ai.request.available_tools' ) ;
56
97
}
57
98
58
99
/**
@@ -170,22 +211,11 @@ function processGenerateSpan(span: Span, name: string, attributes: SpanAttribute
170
211
}
171
212
}
172
213
173
- // Processing for both tool call and non-tool call spans
174
- function sharedProcessSpan ( span : Span , attributes : SpanAttributes ) : void {
175
- renameAttributeKey ( attributes , AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE , GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ) ;
176
- renameAttributeKey ( attributes , AI_USAGE_PROMPT_TOKENS_ATTRIBUTE , GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ) ;
177
-
178
- if (
179
- typeof attributes [ GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ] === 'number' &&
180
- typeof attributes [ GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ] === 'number'
181
- ) {
182
- attributes [ 'gen_ai.usage.total_tokens' ] =
183
- attributes [ GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE ] + attributes [ GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE ] ;
184
- }
185
-
186
- // Rename AI SDK attributes to standardized gen_ai attributes
187
- renameAttributeKey ( attributes , AI_PROMPT_MESSAGES_ATTRIBUTE , 'gen_ai.request.messages' ) ;
188
- renameAttributeKey ( attributes , AI_RESPONSE_TEXT_ATTRIBUTE , 'gen_ai.response.text' ) ;
189
- renameAttributeKey ( attributes , AI_RESPONSE_TOOL_CALLS_ATTRIBUTE , 'gen_ai.response.tool_calls' ) ;
190
- renameAttributeKey ( attributes , AI_PROMPT_TOOLS_ATTRIBUTE , 'gen_ai.request.available_tools' ) ;
214
+ /**
215
+ * Add event processors to the given client to process Vercel AI spans.
216
+ */
217
+ export function addVercelAiProcessors ( client : Client ) : void {
218
+ client . on ( 'spanStart' , onVercelAiSpanStart ) ;
219
+ // Note: We cannot do this on `spanEnd`, because the span cannot be mutated anymore at this point
220
+ client . addEventProcessor ( vercelAiEventProcessor ) ;
191
221
}
0 commit comments