feat(core): Migrate Vercel AI event processor to span streaming#20608
feat(core): Migrate Vercel AI event processor to span streaming#20608nicohrubec wants to merge 11 commits intodevelopfrom
Conversation
…sor migration Closes #20377 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
size-limit report 📦
|
… 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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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); | ||
| } |
There was a problem hiding this comment.
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.
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'), | ||
| }), | ||
| }), | ||
| ]), |
There was a problem hiding this comment.
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)
Triggered by project rule: PR Review Guidelines for Cursor Bot
Reviewed by Cursor Bugbot for commit 59d181c. Configure here.
| // 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); |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
q: Why did we add this export here?


Migrates the Vercel AI event processor so it also works in the span streaming path via the
processSpanhook. 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 thevercel.ai.*namespace. This logic is straightforward to port. It's extracted into a sharedprocessVercelAiSpanAttributeshelper that is now called from both the legacy event processor path (for transactions) and the newprocessSpanhook (for streamed spans).Token accumulation on parent spans. The event processor aggregates token usage from child spans onto their parent
invoke_agentspans. 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_toolsondoGeneratespans and applies the matching description to siblingexecute_toolspans. This cross-span iteration doesn't work when spans are processed individually. Instead, we parse and store tool descriptions in a map atspanStarttime of thedoGeneratespan. Sinceexecute_toolspans are siblings ofdoGenerate(both children ofinvoke_agent), we key the map by the parent span ID soexecute_toolspans can look up descriptions by their ownparent_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