Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

if (process.env.USE_ORCHESTRION) {
Sentry.experimentalUseDiagnosticsChannelInjection();
}

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

if (process.env.USE_ORCHESTRION) {
Sentry.experimentalUseDiagnosticsChannelInjection();
}

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
import { cleanupChildProcesses, createEsmAndCjsTests, createEsmTests } from '../../../../utils/runner';

describe.each([
['6', '^6.0.0'],
['7', '7.0.0-beta.179'],
])('Vercel AI integration (version %s)', (version, vercelAiVersion) => {
['6', {}, '^6.0.0'],
['6', { USE_ORCHESTRION: 'true' }, '^6.0.0'],
['7', {}, '7.0.0-beta.179'],
['7', { USE_ORCHESTRION: 'true' }, '7.0.0-beta.179'],
])('Vercel AI integration (version %s, env %o)', (version, env: Record<string, string>, vercelAiVersion) => {
afterAll(() => {
cleanupChildProcesses();
});
Expand All @@ -36,9 +38,12 @@
const nodeVersion = NODE_VERSION.major;
const failsOnCjs = version === '7' && nodeVersion === 18;

// v6 is instrumented via the OTel processor, v7 via the `ai:telemetry` tracing-channel subscriber,
// so the span origin differs by version.
const expectedOrigin = version === '7' ? 'auto.vercelai.channel' : 'auto.vercelai.otel';
const useOrchestrion = env.USE_ORCHESTRION === 'true';
const usesChannels = version === '7' || useOrchestrion;

// in v7 and orchestrion mode, we use the channel-based integration
// else, we use the OTel processor
const expectedOrigin = usesChannels ? 'auto.vercelai.channel' : 'auto.vercelai.otel';

// We only run this in ESM and CJS to verify full support
// Other suites we only run in ESM to simplify the test setup
Expand All @@ -49,6 +54,7 @@
(createRunner, test) => {
test('creates ai spans for dataCollection defaults', async () => {
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -172,8 +178,9 @@
'scenario.mjs',
'instrument.mjs',
(createRunner, test) => {
test('creates ai spans when dataCollection.genAi has inputs and outputs disabled', async () => {

Check failure on line 181 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai spans when dataCollection.genAi has inputs and outputs disabled

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:181:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:176:3

Check failure on line 181 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai spans when dataCollection.genAi has inputs and outputs disabled

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:181:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:176:3

Check failure on line 181 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai spans when dataCollection.genAi has inputs and outputs disabled

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:181:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:176:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -233,10 +240,10 @@
// On v6, vercel AI natively defaults to recording inputs and outputs by default when telemetry is enabled
// On v7, we do not have access to this, so this defaults to false in this case
expect(secondInvokeAgentSpan.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value).toEqual(
version === '6' ? '[{"role":"user","content":"Where is the second span?"}]' : undefined,
!usesChannels ? '[{"role":"user","content":"Where is the second span?"}]' : undefined,
);
expect(secondInvokeAgentSpan.attributes?.[GEN_AI_OUTPUT_MESSAGES_ATTRIBUTE]?.value).toEqual(
version === '6'
!usesChannels
? '[{"role":"assistant","parts":[{"type":"text","content":"Second span here!"}],"finish_reason":"stop"}]'
: undefined,
);
Expand Down Expand Up @@ -295,11 +302,12 @@
'scenario-error-in-tool.mjs',
'instrument.mjs',
(createRunner, test) => {
test('captures error in tool', async () => {

Check failure on line 305 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > captures error in tool

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:305:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:300:3

Check failure on line 305 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > captures error in tool

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:305:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:300:3

Check failure on line 305 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > captures error in tool

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:305:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:300:3
let transactionEvent: Event | undefined;
let errorEvent: Event | undefined;

await createRunner()
.withEnv(env)
.expect({
transaction: transaction => {
transactionEvent = transaction;
Expand Down Expand Up @@ -367,8 +375,9 @@
'scenario.mjs',
'instrument.mjs',
(createRunner, test) => {
test('creates ai related spans', async () => {

Check failure on line 378 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai related spans

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:378:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:373:3

Check failure on line 378 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai related spans

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:378:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:373:3

Check failure on line 378 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates ai related spans

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:378:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:373:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -409,8 +418,9 @@
'scenario-tool-loop-agent.mjs',
'instrument.mjs',
(createRunner, test) => {
test('creates spans for ToolLoopAgent with tool calls', async () => {

Check failure on line 421 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates spans for ToolLoopAgent with tool calls

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:421:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:416:3

Check failure on line 421 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates spans for ToolLoopAgent with tool calls

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:421:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:416:3

Check failure on line 421 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates spans for ToolLoopAgent with tool calls

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:421:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:416:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -472,8 +482,9 @@
'scenario-concurrent.mjs',
'instrument.mjs',
(createRunner, test) => {
test('parents concurrent calls that share one model instance correctly', async () => {

Check failure on line 485 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > parents concurrent calls that share one model instance correctly

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:485:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:480:3

Check failure on line 485 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > parents concurrent calls that share one model instance correctly

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:485:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:480:3

Check failure on line 485 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > parents concurrent calls that share one model instance correctly

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:485:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:480:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -525,10 +536,11 @@
// Node 20.16 / 22.3 and never backported to Node 18. On Node 18 that lookup returns undefined,
// so the `streamText` event is never published and no `invoke_agent` span is created. The
// non-streaming ops load the channel via dynamic `import()` and are unaffected.
test.skipIf(version === '7' && nodeVersion === 18)(

Check failure on line 539 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates streamText spans with the model call parented to invoke_agent

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:539:56 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:529:3

Check failure on line 539 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates streamText spans with the model call parented to invoke_agent

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:539:56 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:529:3

Check failure on line 539 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > creates streamText spans with the model call parented to invoke_agent

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:539:56 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:529:3
'creates streamText spans with the model call parented to invoke_agent',
async () => {
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -567,8 +579,9 @@
'scenario-rejected-model.mjs',
'instrument.mjs',
(createRunner, test) => {
test('finishes spans with an error status when the operation rejects', async () => {

Check failure on line 582 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > finishes spans with an error status when the operation rejects

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:582:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:577:3

Check failure on line 582 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > finishes spans with an error status when the operation rejects

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:582:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:577:3

Check failure on line 582 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > finishes spans with an error status when the operation rejects

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:582:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:577:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down Expand Up @@ -607,8 +620,9 @@
'scenario-provider-metadata.mjs',
'instrument.mjs',
(createRunner, test) => {
test('derives provider-metadata token breakdown, conversation id and system instructions', async () => {

Check failure on line 623 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > derives provider-metadata token breakdown, conversation id and system instructions

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:623:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:618:3

Check failure on line 623 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (26) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > derives provider-metadata token breakdown, conversation id and system instructions

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:623:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:618:3

Check failure on line 623 in dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/vercelai/v6_v7/test.ts > Vercel AI integration (version 6, env { USE_ORCHESTRION: 'true' }) > derives provider-metadata token breakdown, conversation id and system instructions

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ additionalDependencies.ai suites/tracing/vercelai/v6_v7/test.ts:623:7 ❯ createEsmTests utils/runner/createEsmAndCjsTests.ts:89:3 ❯ suites/tracing/vercelai/v6_v7/test.ts:618:3
await createRunner()
.withEnv(env)
.expect({ transaction: { transaction: 'main' } })
.expect({
span: container => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
mysqlChannelIntegration,
lruMemoizerChannelIntegration,
detectOrchestrionSetup,
vercelAiChannelIntegration,
} from '@sentry/server-utils/orchestrion';
import { registerDiagnosticsChannelInjection } from '@sentry/server-utils/orchestrion/register';
import type { DiagnosticsChannelInjection } from './diagnosticsChannelInjection';
Expand Down Expand Up @@ -42,8 +43,8 @@ import { setDiagnosticsChannelInjectionLoader } from './diagnosticsChannelInject
export function experimentalUseDiagnosticsChannelInjection(): void {
setDiagnosticsChannelInjectionLoader(
(): DiagnosticsChannelInjection => ({
integrations: [mysqlChannelIntegration(), lruMemoizerChannelIntegration()],
replacedOtelIntegrationNames: ['Mysql', 'LruMemoizer'],
integrations: [mysqlChannelIntegration(), lruMemoizerChannelIntegration(), vercelAiChannelIntegration()],
replacedOtelIntegrationNames: ['Mysql', 'LruMemoizer', 'VercelAI'],
register: registerDiagnosticsChannelInjection,
detect: detectOrchestrionSetup,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { IntegrationFn } from '@sentry/core';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { vercelAiIntegration as baseVercelAiIntegration } from '../../vercel-ai';
import * as dc from 'node:diagnostics_channel';
import { subscribeVercelAiOrchestrionChannels } from '../../vercel-ai/vercel-ai-orchestrion-v6-subscriber';

type VercelAiOptions = Parameters<typeof baseVercelAiIntegration>[0];

// In channel-based (orchestrion) mode we emit our own `gen_ai.*` spans from the
// diagnostics channels. The `ai` SDK still emits its own native OpenTelemetry
// spans whenever the user enables `experimental_telemetry`, which would be
// duplicates. Every native `ai` span carries an `ai.operationId` attribute
// (e.g. `ai.generateText`, `ai.generateText.doGenerate`, `ai.toolCall`) at span
// start, whereas our channel spans use `vercel.ai.operationId` — so we drop the
// native ones up front via `ignoreSpans`, before any vercel-ai processing runs.
const NATIVE_VERCEL_AI_SPANS = { attributes: { 'ai.operationId': /^ai\./ } };

const _vercelAiChannelIntegration = ((options: VercelAiOptions = {}) => {
const parentIntegration = baseVercelAiIntegration(options);

return extendIntegration(parentIntegration, {
options,
beforeSetup(client) {
// Ensure we drop spans emitted by ai v6 or below
// To avoid double-instrumentation - in this scenario, we only want to rely on our own spans
const options = client.getOptions();
options.ignoreSpans = [...(options.ignoreSpans || []), NATIVE_VERCEL_AI_SPANS];
},
setupOnce() {
// Bail if this is not available
if (!dc.tracingChannel) {
return;
}

waitForTracingChannelBinding(() => {
subscribeVercelAiOrchestrionChannels(dc.tracingChannel, options);
});
},
});
}) satisfies IntegrationFn;

/**
* Auto-instrument the `ai` SDK. Supported are:
* - v7 via native `ai:telemetry` tracing channel
* - v6 via orchestrion `orchestrion:ai:*` channels
*/
export const vercelAiChannelIntegration = defineIntegration(_vercelAiChannelIntegration);
12 changes: 12 additions & 0 deletions packages/server-utils/src/orchestrion/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
export const CHANNELS = {
MYSQL_QUERY: 'orchestrion:mysql:query',
LRU_MEMOIZER_LOAD: 'orchestrion:lru-memoizer:load',
// Vercel AI (`ai`) v6: orchestrion injects these so the same channel-based
// integration that consumes `ai`'s native `ai:telemetry` channel (v7) can
// also instrument v6. Each maps to a top-level function in `ai`'s bundle.
VERCEL_AI_GENERATE_TEXT: 'orchestrion:ai:generateText',
VERCEL_AI_STREAM_TEXT: 'orchestrion:ai:streamText',
VERCEL_AI_EMBED: 'orchestrion:ai:embed',
VERCEL_AI_EXECUTE_TOOL_CALL: 'orchestrion:ai:executeToolCall',
// `resolveLanguageModel` is the single chokepoint every model call flows
// through; we wrap it to monkey-patch `doGenerate`/`doStream` on the returned
// model (the model-call site itself is an inline call with no injectable
// definition).
VERCEL_AI_RESOLVE_LANGUAGE_MODEL: 'orchestrion:ai:resolveLanguageModel',
} as const;

export type ChannelName = (typeof CHANNELS)[keyof typeof CHANNELS];
26 changes: 26 additions & 0 deletions packages/server-utils/src/orchestrion/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';
* `channelName` here is the unprefixed suffix; the actual diagnostics_channel
* name is `orchestrion:${module.name}:${channelName}` (see `channels.ts`).
*/
/**
* `ai` ships a single bundled entry per module system, so each instrumented
* function needs one config entry per file (the app loads whichever matches its
* module system). This expands a single target into both.
*/
function vercelAiV6Entries(channelName: string, functionName: string, kind: 'Async' | 'Sync'): InstrumentationConfig[] {
return ['dist/index.js', 'dist/index.mjs'].map(filePath => ({
channelName,
module: { name: 'ai', versionRange: '>=6.0.0 <7.0.0', filePath },
functionQuery: { functionName, kind },
}));
}

export const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [
{
channelName: 'query',
Expand Down Expand Up @@ -38,6 +51,19 @@ export const SENTRY_INSTRUMENTATIONS: InstrumentationConfig[] = [
module: { name: 'lru-memoizer', versionRange: '>=2.1.0 <4', filePath: 'lib/async.js' },
functionQuery: { functionName: 'memoizedFunction', kind: 'Callback' },
},
// Vercel AI v6: mirror the v7 native `ai:telemetry` channel by injecting
// channels into the top-level entry points. `resolveLanguageModel` is wrapped
// not to span it, but so the subscriber can monkey-patch `doGenerate`/
// `doStream` on the returned model (the only way to span the model call,
// which is an inline call with no injectable definition in `ai`).
// `streamText` returns its result synchronously (streaming is lazy), so it's
// `Sync`; the subscriber binds the span via `bindTracingChannelToSpan`, which
// ends it when the (synchronous) call returns.
...vercelAiV6Entries('generateText', 'generateText', 'Async'),
...vercelAiV6Entries('streamText', 'streamText', 'Sync'),
...vercelAiV6Entries('embed', 'embed', 'Async'),
...vercelAiV6Entries('executeToolCall', 'executeToolCall', 'Async'),
...vercelAiV6Entries('resolveLanguageModel', 'resolveLanguageModel', 'Sync'),
];

/**
Expand Down
1 change: 1 addition & 0 deletions packages/server-utils/src/orchestrion/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { detectOrchestrionSetup } from './detect';
export { mysqlChannelIntegration } from '../integrations/tracing-channel/mysql';
export { lruMemoizerChannelIntegration } from '../integrations/tracing-channel/lru-memoizer';
export { vercelAiChannelIntegration } from '../integrations/tracing-channel/vercel-ai';
18 changes: 14 additions & 4 deletions packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,21 @@ export function clearOperationId(data: VercelAiChannelMessage): void {
}
const callId = asString(data.event.callId);
if (callId) {
operationIdByCallId.delete(callId);
toolDescriptionsByCallId.delete(callId);
clearOperationCallId(callId);
}
}

/**
* Drop the per-operation `callId` maps for a single id. The v6 orchestrion adapter uses this to clear a
* `streamText` operation only after its lazily-run model call settles — the operation's own span ends
* synchronously (when `streamText` returns) but the model call runs later as the stream is consumed, and
* it still needs the operation's `operationId`/`isStream` entry to name itself `ai.streamText.doStream`.
*/
export function clearOperationCallId(callId: string): void {
operationIdByCallId.delete(callId);
toolDescriptionsByCallId.delete(callId);
}

/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */
function recordToolDescriptions(callId: string | undefined, tools: unknown): void {
if (!callId || !Array.isArray(tools)) {
Expand Down Expand Up @@ -172,10 +182,10 @@ export interface VercelAiChannelMessage {
* nested AI SDK operations (model calls, tool calls) become children of the enclosing span without
* any manual parent bookkeeping here.
*/
type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;
export type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;

/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */
interface VercelAiChannelOptions {
export interface VercelAiChannelOptions {
recordInputs?: boolean;
recordOutputs?: boolean;
enableTruncation?: boolean;
Expand Down
Loading
Loading