Skip to content

feat(core): Migrate Vercel AI event processor to span streaming#20608

Open
nicohrubec wants to merge 11 commits intodevelopfrom
nh/span-streaming-vercelai-migration
Open

feat(core): Migrate Vercel AI event processor to span streaming#20608
nicohrubec wants to merge 11 commits intodevelopfrom
nh/span-streaming-vercelai-migration

Conversation

@nicohrubec
Copy link
Copy Markdown
Member

@nicohrubec nicohrubec commented Apr 30, 2026

Migrates the Vercel AI event processor so it also works in the span streaming path via the processSpan hook. The event processor currently serves three purposes:

Attribute renaming. The Vercel AI SDK emits attributes under ai.* names that need to be renamed to OpenTelemetry semantic conventions (gen_ai.*) and the vercel.ai.* namespace. This logic is straightforward to port. It's extracted into a shared processVercelAiSpanAttributes helper that is now called from both the legacy event processor path (for transactions) and the new processSpan hook (for streamed spans).

Token accumulation on parent spans. The event processor aggregates token usage from child spans onto their parent invoke_agent spans. This is a cross-span operation that fundamentally doesn't work in the streaming model where spans are processed individually. The span streaming implementation guide explicitly lists this as a case that cannot be replaced. Since the plan is for parent-level token accumulation to go away regardless, we simply drop it for the streaming path.

Tool descriptions on execute_tool spans. The event processor iterates over all spans in a transaction to find gen_ai.request.available_tools on doGenerate spans and applies the matching description to sibling execute_tool spans. This cross-span iteration doesn't work when spans are processed individually. Instead, we parse and store tool descriptions in a map at spanStart time of the doGenerate span. Since execute_tool spans are siblings of doGenerate (both children of invoke_agent), we key the map by the parent span ID so execute_tool spans can look up descriptions by their own parent_span_id. This assumes a flat sibling hierarchy, which holds for our test scenarios. If we encounter more complex cases down the road, I think it's fine to address in a follow-up.

Closes #20377

…sor migration

Closes #20377

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 26.3 kB - -
@sentry/browser - with treeshaking flags 24.78 kB - -
@sentry/browser (incl. Tracing) 44.17 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 46.39 kB - -
@sentry/browser (incl. Tracing, Profiling) 49.14 kB - -
@sentry/browser (incl. Tracing, Replay) 83.55 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 73.01 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 88.23 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 100.84 kB - -
@sentry/browser (incl. Feedback) 43.44 kB - -
@sentry/browser (incl. sendFeedback) 31.11 kB - -
@sentry/browser (incl. FeedbackAsync) 36.19 kB - -
@sentry/browser (incl. Metrics) 27.6 kB - -
@sentry/browser (incl. Logs) 27.73 kB - -
@sentry/browser (incl. Metrics & Logs) 28.43 kB - -
@sentry/react 28.04 kB - -
@sentry/react (incl. Tracing) 46.4 kB - -
@sentry/vue 31.18 kB - -
@sentry/vue (incl. Tracing) 46.02 kB - -
@sentry/svelte 26.32 kB - -
CDN Bundle 28.91 kB - -
CDN Bundle (incl. Tracing) 46.94 kB - -
CDN Bundle (incl. Logs, Metrics) 30.34 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 48.04 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 69.4 kB - -
CDN Bundle (incl. Tracing, Replay) 84.07 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 85.15 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 89.89 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 90.97 kB - -
CDN Bundle - uncompressed 84.88 kB - -
CDN Bundle (incl. Tracing) - uncompressed 140.44 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 89.08 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 143.9 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 212.99 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 258.24 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 261.69 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 271.94 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 275.38 kB - -
@sentry/nextjs (client) 48.9 kB - -
@sentry/sveltekit (client) 44.64 kB - -
@sentry/node-core 59.81 kB +0.02% +11 B 🔺
@sentry/node 163.55 kB +0.09% +133 B 🔺
@sentry/node - without tracing 72.28 kB +0.02% +9 B 🔺
@sentry/aws-serverless 106.95 kB +0.01% +8 B 🔺
@sentry/cloudflare (withSentry) - minified 168.38 kB - -
@sentry/cloudflare (withSentry) 424.9 kB - -

View base workflow run

@nicohrubec nicohrubec changed the title test(vercelai): Add span streaming integration tests for event processor migration feat(vercelai): Migrate Vercel AI event processor to span streaming May 5, 2026
nicohrubec and others added 5 commits May 5, 2026 19:40
… description side-channel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ions

The error scenario needs a try/catch so the process stays alive for flush.
The invoke_agent span doesn't have token attributes when the tool errors,
so remove those from the error test expectations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… test

In Vercel AI v6, the invoke_agent span does not get error status when a
tool errors (matching the non-streaming v6 test behavior).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nicohrubec nicohrubec marked this pull request as ready for review May 6, 2026 08:31
@nicohrubec nicohrubec requested a review from a team as a code owner May 6, 2026 08:31
@nicohrubec nicohrubec requested a review from chargome May 6, 2026 08:32
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 59d181c. Configure here.

const parentSpanId = spanToJSON(span).parent_span_id;
if (parentSpanId) {
toolDescriptionMap.set(parentSpanId, descriptions);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tool descriptions leak

Medium Severity

The toolDescriptionMap is populated for Vercel AI spans with tool descriptions, but its cleanup logic only runs for streamed spans. For static Vercel AI traces, this leaves entries in the map indefinitely, causing a memory leak.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 59d181c. Configure here.

[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
}),
}),
]),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Negative assertions are missing

Low Severity

This violates the PR review rule against relaxed assertions for negative payload expectations. The tests use expect.arrayContaining while relying on disabled-telemetry spans and gen_ai.tool.description being absent, so those regressions would still pass.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 59d181c. Configure here.

@nicohrubec nicohrubec changed the title feat(vercelai): Migrate Vercel AI event processor to span streaming feat(core): Migrate Vercel AI event processor to span streaming May 6, 2026
// so we key by the parent span ID (the invoke_agent span).
const parentSpanId = spanToJSON(span).parent_span_id;
if (parentSpanId) {
toolDescriptionMap.set(parentSpanId, descriptions);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is only meant to happen for streamed spans right? If so we should guard it

export type { MetricOptions } from './metrics/public-api';
export { createConsolaReporter } from './integrations/consola';
export { addVercelAiProcessors } from './tracing/vercel-ai';
export { addVercelAiProcessors, processVercelAiSpanAttributes } from './tracing/vercel-ai';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Why did we add this export here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event processor migration: Vercel AI event processor

2 participants