Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion packages/core/src/tracing/vercel-ai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,40 @@ function vercelAiEventProcessor(event: Event): Event {
* Post-process spans emitted by the Vercel AI SDK.
*/
function processEndedVercelAiSpan(span: SpanJSON): void {
const { data: attributes, origin } = span;
const { data: attributes, origin, description: name } = span;

if (origin !== 'auto.vercelai.otel') {
return;
}

// Ensure ended spans still get a GenAI op even if the required attributes were not present at span start.
// This is important for Sentry's AI Agents UI, which relies on `span.op` being set to `gen_ai.*`.
// See: https://github.com/getsentry/sentry-javascript/issues/18448
if (name && (span.op === 'default' || !span.op)) {
if (
name === 'ai.generateText' ||
name === 'ai.streamText' ||
name === 'ai.generateObject' ||
name === 'ai.streamObject' ||
name === 'ai.embed' ||
name === 'ai.embedMany'
) {
span.op = 'gen_ai.invoke_agent';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're generally using constants for these attributes, you'll find other occurrences in this file.

} else if (name === 'ai.generateText.doGenerate') {
span.op = 'gen_ai.generate_text';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

op should be updated like this:

span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, YOUR_AI_ATTRIBUTE);

} else if (name === 'ai.streamText.doStream') {
span.op = 'gen_ai.stream_text';
} else if (name === 'ai.generateObject.doGenerate') {
span.op = 'gen_ai.generate_object';
} else if (name === 'ai.streamObject.doStream') {
span.op = 'gen_ai.stream_object';
} else if (name === 'ai.embed.doEmbed') {
span.op = 'gen_ai.embed';
} else if (name === 'ai.embedMany.doEmbed') {
span.op = 'gen_ai.embed_many';
}
}
Comment on lines +121 to +144
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a cleaner approach would be to determine the name with a map here.


renameAttributeKey(attributes, AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE);
renameAttributeKey(attributes, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE);
renameAttributeKey(attributes, AI_USAGE_CACHED_INPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE);
Expand Down