Skip to content

feat(ai): use tracing channels to track parent-child context - #15660

Merged
aayush-kapoor merged 37 commits into
mainfrom
aayush/tracing-channel
Jun 15, 2026
Merged

feat(ai): use tracing channels to track parent-child context#15660
aayush-kapoor merged 37 commits into
mainfrom
aayush/tracing-channel

Conversation

@aayush-kapoor

Copy link
Copy Markdown
Collaborator

Background

we introduced diagnostic channels in #14854 as a way to propagate event data.

but the problem was that there was no to establish a relation with the parent and child calls that happen when consuming information via the diagnostics channels. context propagation for subagents remained unsolved (meaning subagents will be detached from the main parent call)

Summary

  • use tracingChannel() instead of diagnostic channel()
  • model and tool execution are now wrapped with tracingChannel.tracePromise(...) via the existing executeLanguageModelCall and executeTool dispatcher paths
  • in unsupported/non-Node runtimes, tracing dynamically no-ops and directly runs the original callback/execution

Manual Verification

verified that event data is still transmitted at each lifecycle event by running the example examples/ai-functions/src/telemetry/tracing-channel/generate-text.ts

Checklist

  • All commits are signed (PRs with unsigned commits cannot be merged)
  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Comment on lines +38 to +39
expect(telemetry.executeLanguageModelCall).toBeDefined();
expect(telemetry.executeTool).toBeDefined();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

these are now defined since before executeLanguageModelCall and executeTool only existed if a user/global telemetry integration implemented those methods. but now, we have a native reason to wrap execution so that tracing channels propagate async context across the actual model/tool


describe('executeTool', () => {
it('returns undefined when no integrations implement executeTool', () => {
it('passes through when no integrations implement executeTool', async () => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

changed for the reason above


describe('executeLanguageModelCall', () => {
it('returns undefined when no integrations implement executeLanguageModelCall', () => {
it('passes through when no integrations implement executeLanguageModelCall', async () => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

changed for the reason above

executeToolInTelemetryContext?: <T>(params: {
callId: string;
toolCallId: string;
event?: ToolExecutionStartEvent<TOOLS>;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this event is what the tracing channel uses as it's base context

@logaretm logaretm left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Just wanted to drop a couple of suggestions, second one being a nit so up to you! But thanks for doing this, this sets a great example in the ecosystem on how to do telemetry like that!

Comment thread packages/ai/src/telemetry/tracing-channel-publisher.ts
@@ -1,6 +1,6 @@
export const AI_SDK_TELEMETRY_DIAGNOSTIC_CHANNEL = 'aisdk:telemetry';
export const AI_SDK_TELEMETRY_TRACING_CHANNEL = 'aisdk:telemetry';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is fine, but something you may want to consider:

Right now everything goes through a single aisdk:telemetry channel with a type discriminator. The diagnostics_channel API is designed so that many purpose-focused channels with their own subscriber sets make dispatch extremely cheap.

On the other hand, filtering a "firehose" channel adds continuous overhead on every published message (this feedback came directly from @Qard, the diagnostics_channel creator on various related proposals).

Maybe something like this if you would like to consider it:

Channel Wraps
aisdk:telemetry Top-level operation (generateText, streamText, generateObject, etc.)
aisdk:telemetry:languageModelCall Individual provider call (nested)
aisdk:telemetry:toolExecution Individual tool call (nested)

But again it is up to you, I think it is fine either way. The idea is some APMs can choose which channels to care about and not incur overhead over those channels.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

i guess my question would be if we do have different channels, will the context propagation still work? even if the tool execution and the model call are split?

@logaretm logaretm Jun 4, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yep, it works cross-channel as long as the subscribers have their stores bound to the channels.

GraphQL illustrates this as they have like 5 channels that all work together, so APMs can choose the level of "granularity" of the spans/metrics they want to get out of it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we made some changes in this PR that reduce the noise / events that are emitted through the channel.

what was before something like:

bindStart onStart
asyncEnd onStart
bindStart onStepStart
asyncEnd onStepStart
bindStart onLanguageModelCallStart
asyncEnd onLanguageModelCallStart
bindStart executeLanguageModelCall
asyncEnd executeLanguageModelCall
bindStart onLanguageModelCallEnd
asyncEnd onLanguageModelCallEnd
bindStart onToolExecutionStart
asyncEnd onToolExecutionStart
bindStart executeTool
asyncEnd executeTool
bindStart onToolExecutionEnd
asyncEnd onToolExecutionEnd
bindStart onStepFinish
asyncEnd onStepFinish
bindStart onStepStart
asyncEnd onStepStart
bindStart onLanguageModelCallStart
asyncEnd onLanguageModelCallStart
bindStart executeLanguageModelCall
asyncEnd executeLanguageModelCall
bindStart onLanguageModelCallEnd
asyncEnd onLanguageModelCallEnd
bindStart onStepFinish
asyncEnd onStepFinish
bindStart onEnd
asyncEnd onEnd

is now bundled into:

bindStart generateText
bindStart step
bindStart languageModelCall
asyncEnd languageModelCall
bindStart executeTool
asyncEnd executeTool
asyncEnd step
bindStart step
bindStart languageModelCall
asyncEnd languageModelCall
asyncEnd step
asyncEnd generateText

and so the above events are what's emitted through the channel (still only 1 ai:telemetry channel)

do you think that works @logaretm?

would appreciate any feedback you have on this! otherwise if this aligns dx wise with what you're expecting, we'll try to wrap this PR up

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think that works great! Just gave it a test round right now and looks fine DX wise

CleanShot 2026-06-04 at 17 41 33@2x

Thanks!

Comment thread content/docs/03-ai-sdk-core/60-telemetry.mdx Outdated
request,
} = await executeLanguageModelCallInTelemetryContext({
callId: effectiveCallId,
event: languageModelCallStartEvent,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i find it strange to pass in an event. if we really want to do this, we may want to rename to startEvent - an alternative would be to pass the individual parts of the event. This can lead to some duplication but is semantically cleaner imo.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

tracing channel depends on this event data to create the context afaik.i added this comment: https://github.com/vercel/ai/pull/15660/changes#r3314637416

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sorry actually not for context, but for the tracing subscribers to see the event data

Comment thread packages/ai/src/generate-text/stream-language-model-call.ts Outdated
Comment thread packages/ai/src/telemetry/telemetry.ts Outdated
Comment thread packages/ai/src/telemetry/tracing-channel-publisher.ts
@aayush-kapoor
aayush-kapoor marked this pull request as ready for review June 3, 2026 18:31
@lgrammel

Copy link
Copy Markdown
Collaborator

do we have test coverage for embed, rerank with tracing channels? the files were changed w/o test changes (also what is our test coverage for generate/streamText with tracing)? does embedMany also need adjustments?

@aayush-kapoor

Copy link
Copy Markdown
Collaborator Author

do we have test coverage for embed, rerank with tracing channels? the files were changed w/o test changes (also what is our test coverage for generate/streamText with tracing)? does embedMany also need adjustments?

adding now for embed and rerank.

for generateText and streamText, the test coverage is in packages/ai/src/telemetry/tracing-channel.test.ts that tests the complete flow (we import those functions specifically)

i think this is a better pattern because then all diagnostics channel imports live in packages/ai/telemetry

@aayush-kapoor
aayush-kapoor enabled auto-merge (squash) June 15, 2026 20:37
@aayush-kapoor
aayush-kapoor merged commit b097c52 into main Jun 15, 2026
48 checks passed
@aayush-kapoor
aayush-kapoor deleted the aayush/tracing-channel branch June 15, 2026 20:37
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Published in:

Package Version
ai 7.0.0-beta.178 github npm
@ai-sdk/angular 3.0.0-beta.178 github npm
@ai-sdk/gateway 4.0.0-beta.109 github npm
@ai-sdk/harness 1.0.0-beta.15 github npm
@ai-sdk/harness-claude-code 1.0.0-beta.11 github npm
@ai-sdk/harness-codex 1.0.0-beta.11 github npm
@ai-sdk/harness-pi 1.0.0-beta.11 github npm
@ai-sdk/langchain 3.0.0-beta.178 github npm
@ai-sdk/llamaindex 3.0.0-beta.178 github npm
@ai-sdk/otel 1.0.0-beta.124 github npm
@ai-sdk/policy-opa 1.0.0-beta.15 github npm
@ai-sdk/react 4.0.0-beta.182 github npm
@ai-sdk/rsc 3.0.0-beta.179 github npm
@ai-sdk/sandbox-just-bash 1.0.0-beta.15 github npm
@ai-sdk/sandbox-vercel 1.0.0-beta.15 github npm
@ai-sdk/svelte 5.0.0-beta.178 github npm
@ai-sdk/tui 1.0.0-beta.13 github npm
@ai-sdk/vue 4.0.0-beta.178 github npm
@ai-sdk/workflow 1.0.0-beta.95 github npm

logaretm added a commit to getsentry/js-tracing-channels-proposals that referenced this pull request Jun 16, 2026
vercel/ai#15660 merged 2026-06-15 (Vercel-driven), replacing the
aisdk:telemetry diagnostics_channel with the ai:telemetry TracingChannel.
Shipped in pre-release v7.0.0-beta.178; stable v6 line pending, so not
adoptable by most users yet (tracked like graphql's v17 rc).

- TRACKER.md: Vercel AI (Node) 🟡 PR open -> ✅ Merged (prerelease); Sentry-built
  Merged 0->1, PR Open 1->0; Total Merged 14->15, PR Open 6->5
- data/libraries.json: ai -> shipped + prerelease, shippedVersion 7.0.0-beta.178,
  ai:telemetry channel chip; meta.updated 2026-06-16
- scripts/coverage.py: add ai ("7.0.0", "sentry") to COVERED; regenerate block

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants