From 29f97f5dde85ce319e2b2f462d0c598edaefbcbd Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 22 Jul 2026 15:19:31 +0200 Subject: [PATCH] feat(core)!: Remove `streamGenAiSpans` flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the `streamGenAiSpans` client option. The behavior it gated (extracting gen_ai spans from transactions into a v2 span envelope container) was already the default and is now unconditional — there is no longer a way to opt out. For static transactions, `extractGenAiSpansFromEvent` always pulls gen_ai spans into a v2 span container. For full span streaming (`traceLifecycle: 'stream'`), the existing `hasSpanStreamingEnabled` guard still short-circuits so spans are not extracted twice. Because gen_ai spans now always take the v2 path (not subject to the transaction payload-size limits that truncation works around), `shouldEnableTruncation` simplifies to `!getClient()`. The `enableTruncation` escape hatch is intentionally left in place for a follow-up. Test suites that set `streamGenAiSpans: false` to assert on `transaction.spans` are migrated to assert on the v2 span container; redundant `streamGenAiSpans: true` config lines are removed. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../suites/tracing/anthropic-ai/index.ts | 1 - .../suites/tracing/google-genai/index.ts | 1 - .../suites/tracing/langchain/index.ts | 1 - .../suites/tracing/langgraph/index.ts | 1 - .../suites/tracing/openai/index.ts | 1 - .../suites/tracing/workers-ai/index.ts | 3 - .../suites/tracing/workers-ai/test.ts | 104 +++++++----------- .../cloudflare-vercelai-v7-als/src/index.ts | 1 - .../tests/index.test.ts | 26 +++-- .../src/index.ts | 1 - .../tests/index.test.ts | 63 ++++------- .../nextjs-15/sentry.server.config.ts | 1 - .../nextjs-16/sentry.server.config.ts | 1 - .../anthropic/instrument-no-truncation.mjs | 1 - .../instrument-streaming-with-truncation.mjs | 1 - .../anthropic/instrument-streaming.mjs | 1 - .../anthropic/instrument-with-options.mjs | 1 - .../tracing/anthropic/instrument-with-pii.mjs | 1 - .../anthropic/instrument-with-truncation.mjs | 1 - .../suites/tracing/anthropic/instrument.mjs | 1 - .../google-genai/instrument-no-truncation.mjs | 1 - .../instrument-streaming-with-truncation.mjs | 1 - .../google-genai/instrument-streaming.mjs | 1 - .../google-genai/instrument-with-options.mjs | 1 - .../google-genai/instrument-with-pii.mjs | 1 - .../instrument-with-truncation.mjs | 1 - .../tracing/google-genai/instrument.mjs | 1 - .../langchain/instrument-no-truncation.mjs | 1 - .../instrument-streaming-with-truncation.mjs | 1 - .../langchain/instrument-streaming.mjs | 1 - .../tracing/langchain/instrument-with-pii.mjs | 1 - .../langchain/instrument-with-truncation.mjs | 1 - .../suites/tracing/langchain/instrument.mjs | 1 - .../langchain/v1/instrument-with-pii.mjs | 1 - .../v1/instrument-with-truncation.mjs | 1 - .../tracing/langchain/v1/instrument.mjs | 1 - .../tracing/langgraph/instrument-agent.mjs | 2 - .../langgraph/instrument-no-truncation.mjs | 1 - .../instrument-streaming-with-truncation.mjs | 1 - .../langgraph/instrument-streaming.mjs | 1 - .../tracing/langgraph/instrument-with-pii.mjs | 1 - .../suites/tracing/langgraph/instrument.mjs | 1 - .../suites/tracing/langgraph/test.ts | 96 +++++++++------- .../openai/instrument-no-truncation.mjs | 1 - .../tracing/openai/instrument-root-span.mjs | 1 - .../instrument-streaming-with-truncation.mjs | 1 - .../tracing/openai/instrument-streaming.mjs | 1 - .../openai/instrument-with-options.mjs | 1 - .../tracing/openai/instrument-with-pii.mjs | 1 - .../openai/instrument-with-truncation.mjs | 1 - .../suites/tracing/openai/instrument.mjs | 1 - .../openai-tool-calls/instrument-with-pii.mjs | 1 - .../openai/openai-tool-calls/instrument.mjs | 1 - .../openai/v6/instrument-root-span.mjs | 1 - .../openai/v6/instrument-with-options.mjs | 1 - .../tracing/openai/v6/instrument-with-pii.mjs | 1 - .../suites/tracing/openai/v6/instrument.mjs | 1 - .../vercelai/instrument-no-truncation.mjs | 1 - .../tracing/vercelai/instrument-with-pii.mjs | 1 - .../vercelai/instrument-with-truncation.mjs | 1 - .../suites/tracing/vercelai/instrument.mjs | 1 - .../vercelai/v5/instrument-with-pii.mjs | 1 - .../suites/tracing/vercelai/v5/instrument.mjs | 1 - packages/core/src/tracing/ai/utils.ts | 16 +-- .../src/tracing/spans/extractGenAiSpans.ts | 1 - packages/core/src/types/options.ts | 14 --- .../core/test/lib/tracing/ai/utils.test.ts | 16 +-- .../tracing/spans/extractGenAiSpans.test.ts | 10 +- 68 files changed, 147 insertions(+), 261 deletions(-) diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts index 384fba1965dd..c28839548208 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts @@ -27,7 +27,6 @@ export default Sentry.withSentry( (env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, - streamGenAiSpans: true, }), { async fetch(_request, _env, _ctx) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/index.ts index 635de9ebe95f..31f4c29fe859 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/index.ts @@ -45,7 +45,6 @@ export default Sentry.withSentry( (env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, - streamGenAiSpans: true, }), { async fetch(_request, _env, _ctx) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/langchain/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/langchain/index.ts index bae6245a01ad..006027d1b3a9 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/langchain/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/langchain/index.ts @@ -30,7 +30,6 @@ export default Sentry.withSentry( (env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, - streamGenAiSpans: true, }), { async fetch(_request, _env, _ctx) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/langgraph/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/langgraph/index.ts index 2c021727fcc6..8bf8f8acd720 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/langgraph/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/langgraph/index.ts @@ -10,7 +10,6 @@ export default Sentry.withSentry( dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: true, outputs: true } }, - streamGenAiSpans: true, }), { async fetch(_request, _env, _ctx) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/openai/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/openai/index.ts index 9ef8eabc831e..0da42f9ea10b 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/openai/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/openai/index.ts @@ -32,7 +32,6 @@ export default Sentry.withSentry( (env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, - streamGenAiSpans: true, }), { async fetch(_request, _env, _ctx) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/index.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/index.ts index 9064ea1cbd4e..0cf287055c99 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/index.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/index.ts @@ -12,9 +12,6 @@ export default Sentry.withSentry( (env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 1.0, - // Keep gen_ai spans embedded in the transaction (instead of streamed as a - // separate envelope container) so they can be asserted on `transaction.spans`. - streamGenAiSpans: false, }), { async fetch(request) { diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/test.ts index e5c835f37508..080a6dcb9d6e 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/test.ts @@ -22,40 +22,29 @@ it('traces a basic Workers AI text generation request', async ({ signal }) => { .ignore('event') .expect(envelope => { const transactionEvent = envelope[1]?.[0]?.[1] as any; + expect(transactionEvent.transaction).toBe('GET /'); - // The transaction event is framework-generated and carries non-deterministic fields - // (random ports, ids, timestamps, sdk version), so we assert the stable subset. - expect(transactionEvent).toEqual( + const container = envelope[1]?.[1]?.[1] as any; + expect(container).toBeDefined(); + expect(container.items).toHaveLength(1); + + expect(container.items[0]).toEqual( expect.objectContaining({ - type: 'transaction', - transaction: 'GET /', - transaction_info: { source: 'route' }, - contexts: expect.objectContaining({ - trace: expect.objectContaining({ - op: 'http.server', - origin: 'auto.http.cloudflare', - status: 'ok', - }), - }), - spans: [ - expect.objectContaining({ - description: 'chat @cf/meta/llama-3.1-8b-instruct', - op: 'gen_ai.chat', - origin: 'auto.ai.cloudflare.workers_ai', - data: { - 'sentry.origin': 'auto.ai.cloudflare.workers_ai', - 'sentry.op': 'gen_ai.chat', - [GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai', - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.7, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 100, - [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12, - [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7, - [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19, - }, - }), - ], + name: 'chat @cf/meta/llama-3.1-8b-instruct', + status: 'ok', + is_segment: false, + attributes: { + 'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' }, + 'sentry.op': { value: 'gen_ai.chat', type: 'string' }, + [GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' }, + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' }, + [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' }, + [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: { value: 0.7, type: 'double' }, + [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: { value: 100, type: 'integer' }, + [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' }, + [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' }, + [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' }, + }, }), ); }) @@ -69,38 +58,29 @@ it('traces a streaming Workers AI text generation request', async ({ signal }) = .ignore('event') .expect(envelope => { const transactionEvent = envelope[1]?.[0]?.[1] as any; + expect(transactionEvent.transaction).toBe('GET /stream'); - expect(transactionEvent).toEqual( + const container = envelope[1]?.[1]?.[1] as any; + expect(container).toBeDefined(); + expect(container.items).toHaveLength(1); + + expect(container.items[0]).toEqual( expect.objectContaining({ - type: 'transaction', - transaction: 'GET /stream', - transaction_info: { source: 'url' }, - contexts: expect.objectContaining({ - trace: expect.objectContaining({ - op: 'http.server', - origin: 'auto.http.cloudflare', - status: 'ok', - }), - }), - spans: [ - expect.objectContaining({ - description: 'chat @cf/meta/llama-3.1-8b-instruct', - op: 'gen_ai.chat', - origin: 'auto.ai.cloudflare.workers_ai', - data: { - 'sentry.origin': 'auto.ai.cloudflare.workers_ai', - 'sentry.op': 'gen_ai.chat', - [GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai', - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct', - [GEN_AI_REQUEST_STREAM_ATTRIBUTE]: true, - [GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: true, - [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12, - [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7, - [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19, - }, - }), - ], + name: 'chat @cf/meta/llama-3.1-8b-instruct', + status: 'ok', + is_segment: false, + attributes: { + 'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' }, + 'sentry.op': { value: 'gen_ai.chat', type: 'string' }, + [GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' }, + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' }, + [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' }, + [GEN_AI_REQUEST_STREAM_ATTRIBUTE]: { value: true, type: 'boolean' }, + [GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: { value: true, type: 'boolean' }, + [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' }, + [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' }, + [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' }, + }, }), ); }) diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/src/index.ts b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/src/index.ts index f4110426ef99..c310afdf5658 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/src/index.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/src/index.ts @@ -7,7 +7,6 @@ export default Sentry.withSentry( dsn: env.E2E_TEST_DSN, environment: 'qa', tunnel: 'http://localhost:3031/', - streamGenAiSpans: false, tracesSampleRate: 1.0, integrations: [Sentry.vercelAIIntegration()], }), diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/tests/index.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/tests/index.test.ts index 8aea274ca40d..078452236a44 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/tests/index.test.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/tests/index.test.ts @@ -1,23 +1,33 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { getSpanOp, waitForRequest } from '@sentry-internal/test-utils'; test('does not capture Vercel AI v7 spans without nodejs_compat', async ({ baseURL }) => { - const transactionPromise = waitForTransaction('cloudflare-vercelai-v7-als', txn => { - return txn.transaction === 'GET /generate'; + // The transaction envelope also carries any extracted gen_ai spans as a span v2 container item, + // so we wait for the whole envelope and assert neither place contains AI spans. + const envelopePromise = waitForRequest('cloudflare-vercelai-v7-als', ({ envelope }) => { + const transactionItem = envelope[1].find(([header]) => header.type === 'transaction'); + return (transactionItem?.[1] as any)?.transaction === 'GET /generate'; }); const response = await fetch(`${baseURL}/generate`); expect(response.status).toBe(200); - const transaction = await transactionPromise; + const { envelope } = await envelopePromise; + const transaction = envelope[1].find(([header]) => header.type === 'transaction')?.[1] as any; expect(transaction.transaction).toBe('GET /generate'); expect(transaction.contexts?.trace?.op).toBe('http.server'); - // v7 uses diagnostics_channel which is not available with nodejs_als, - // so no AI spans should be present. - const aiSpans = (transaction.spans || []).filter( + // v7 uses diagnostics_channel which is not available with nodejs_als, so no AI spans should be + // present — neither embedded in the transaction nor streamed as a span v2 container item. + const embeddedAiSpans = (transaction.spans || []).filter( (span: any) => span.op?.startsWith('gen_ai.') || span.description?.includes('generateText'), ); - expect(aiSpans).toHaveLength(0); + expect(embeddedAiSpans).toHaveLength(0); + + const streamedGenAiSpans = envelope[1] + .filter(([header]) => header.type === 'span') + .flatMap(([, payload]) => (payload as any).items ?? []) + .filter((span: any) => getSpanOp(span)?.startsWith('gen_ai.')); + expect(streamedGenAiSpans).toHaveLength(0); }); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/src/index.ts b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/src/index.ts index 91c6eae65f81..8425fcf0eac6 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/src/index.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/src/index.ts @@ -8,7 +8,6 @@ export default Sentry.withSentry( environment: 'qa', tunnel: 'http://localhost:3031/', tracesSampleRate: 1.0, - streamGenAiSpans: false, integrations: [Sentry.vercelAIIntegration()], }), { diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/tests/index.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/tests/index.test.ts index 4b1a983c629b..3ca6c9342870 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/tests/index.test.ts +++ b/dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/tests/index.test.ts @@ -1,57 +1,40 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { getSpanOp, waitForStreamedSpans } from '@sentry-internal/test-utils'; test('captures Vercel AI v7 spans with nodejs_compat using tracing channels', async ({ baseURL }) => { - const transactionPromise = waitForTransaction('cloudflare-vercelai-v7-compat', txn => { - return txn.transaction === 'GET /generate'; - }); + // gen_ai spans are extracted into a separate span v2 envelope item + const genAiSpansPromise = waitForStreamedSpans('cloudflare-vercelai-v7-compat', spans => + spans.some(span => getSpanOp(span) === 'gen_ai.invoke_agent'), + ); const response = await fetch(`${baseURL}/generate`); expect(response.status).toBe(200); - const transaction = await transactionPromise; - - expect(transaction.transaction).toBe('GET /generate'); - expect(transaction.contexts?.trace?.op).toBe('http.server'); - expect(transaction.spans).toHaveLength(2); + const genAiSpans = await genAiSpansPromise; - expect(transaction.spans).toEqual( + expect(genAiSpans).toEqual( expect.arrayContaining([ expect.objectContaining({ - description: 'invoke_agent', - op: 'gen_ai.invoke_agent', - origin: 'auto.vercelai.channel', - parent_span_id: expect.any(String), - span_id: expect.any(String), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: expect.any(String), - data: expect.objectContaining({ - 'gen_ai.operation.name': 'invoke_agent', - 'gen_ai.usage.input_tokens': 10, - 'gen_ai.usage.output_tokens': 20, - 'gen_ai.usage.total_tokens': 30, - 'sentry.op': 'gen_ai.invoke_agent', - 'sentry.origin': 'auto.vercelai.channel', + name: 'invoke_agent', + attributes: expect.objectContaining({ + 'gen_ai.operation.name': { value: 'invoke_agent', type: 'string' }, + 'gen_ai.usage.input_tokens': { value: 10, type: 'integer' }, + 'gen_ai.usage.output_tokens': { value: 20, type: 'integer' }, + 'gen_ai.usage.total_tokens': { value: 30, type: 'integer' }, + 'sentry.op': { value: 'gen_ai.invoke_agent', type: 'string' }, + 'sentry.origin': { value: 'auto.vercelai.channel', type: 'string' }, }), }), expect.objectContaining({ - description: 'generate_content mock-model-id', - op: 'gen_ai.generate_content', - origin: 'auto.vercelai.channel', - parent_span_id: expect.any(String), - span_id: expect.any(String), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: expect.any(String), - data: expect.objectContaining({ - 'gen_ai.operation.name': 'generate_content', - 'gen_ai.usage.input_tokens': 10, - 'gen_ai.usage.output_tokens': 20, - 'gen_ai.usage.total_tokens': 30, - 'sentry.op': 'gen_ai.generate_content', - 'sentry.origin': 'auto.vercelai.channel', + name: 'generate_content mock-model-id', + attributes: expect.objectContaining({ + 'gen_ai.operation.name': { value: 'generate_content', type: 'string' }, + 'gen_ai.usage.input_tokens': { value: 10, type: 'integer' }, + 'gen_ai.usage.output_tokens': { value: 20, type: 'integer' }, + 'gen_ai.usage.total_tokens': { value: 30, type: 'integer' }, + 'sentry.op': { value: 'gen_ai.generate_content', type: 'string' }, + 'sentry.origin': { value: 'auto.vercelai.channel', type: 'string' }, }), }), ]), diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/sentry.server.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/sentry.server.config.ts index a5e60d7787c6..f15dc320f937 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/sentry.server.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/sentry.server.config.ts @@ -11,5 +11,4 @@ Sentry.init({ bufferSize: 1000, }, integrations: [Sentry.vercelAIIntegration()], - streamGenAiSpans: true, }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16/sentry.server.config.ts b/dev-packages/e2e-tests/test-applications/nextjs-16/sentry.server.config.ts index 8b9eaa651f6d..9712d67e65e2 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16/sentry.server.config.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16/sentry.server.config.ts @@ -9,7 +9,6 @@ Sentry.init({ dataCollection: { userInfo: true }, // debug: true, integrations: [Sentry.vercelAIIntegration(), Sentry.nodeRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 })], - streamGenAiSpans: true, // Verify Log type is available beforeSendLog(log: Log) { return log; diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-no-truncation.mjs index db17234a62c9..1fa73dda80a2 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-no-truncation.mjs @@ -21,5 +21,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming-with-truncation.mjs index f5bc3383d644..048f3de408e2 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming-with-truncation.mjs @@ -13,5 +13,4 @@ Sentry.init({ enableTruncation: true, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming.mjs index 1e13bd68fe06..657bed0a3a8c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-streaming.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, traceLifecycle: 'stream', - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-options.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-options.mjs index 9818715bf760..7ab9939ce1f7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-options.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-options.mjs @@ -20,5 +20,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-pii.mjs index 208730fdc71c..398e7d549ce4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-pii.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-truncation.mjs index 2adcdd009640..c593efd41fdb 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument-with-truncation.mjs @@ -15,5 +15,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument.mjs index 2ee8a8fce693..21631b0899a3 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/instrument.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs index 5daa0a199d49..2b1f9cfd1dca 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs @@ -21,5 +21,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming-with-truncation.mjs index 88f1e7f0ebff..0e148493344a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming-with-truncation.mjs @@ -13,5 +13,4 @@ Sentry.init({ enableTruncation: true, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming.mjs index 1e13bd68fe06..657bed0a3a8c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-streaming.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, traceLifecycle: 'stream', - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-options.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-options.mjs index 63a4584f2519..65c88a9108ea 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-options.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-options.mjs @@ -20,5 +20,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-pii.mjs index 1f9f31b35bcc..e9a8a751fded 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-pii.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-truncation.mjs index 895e7d2ffbad..7cccb1ccb487 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-with-truncation.mjs @@ -15,5 +15,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument.mjs index effe80f853e9..407194162e2e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-no-truncation.mjs index d8f1eb096f93..0a647f326044 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-no-truncation.mjs @@ -21,5 +21,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming-with-truncation.mjs index 3774b2665391..2a2365407b79 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming-with-truncation.mjs @@ -13,5 +13,4 @@ Sentry.init({ enableTruncation: true, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming.mjs index 1e13bd68fe06..657bed0a3a8c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-streaming.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, traceLifecycle: 'stream', - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-pii.mjs index 47c720f58d1d..36ff67811fd0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-pii.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-truncation.mjs index f402bce82071..cb677ddd9926 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument-with-truncation.mjs @@ -15,5 +15,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument.mjs index 84be44cf74ad..0bfc489afdab 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/instrument.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-pii.mjs index f76143531c55..18e541e6547f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-pii.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-truncation.mjs index fd4ea4c13751..b773dbad2e6b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument-with-truncation.mjs @@ -15,5 +15,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument.mjs index e00ed754f8c6..ad89287dca0f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/v1/instrument.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-agent.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-agent.mjs index 12f7f0db96a2..c969422030d6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-agent.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-agent.mjs @@ -6,8 +6,6 @@ Sentry.init({ release: '1.0', tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: true, outputs: true } }, - // This suite asserts on gen_ai spans embedded in the transaction, so opt out of span streaming. - streamGenAiSpans: false, transport: loggingTransport, beforeSendTransaction: event => { // Filter out mock express server transactions diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-no-truncation.mjs index f869054e9d3e..c3933a5ea129 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-no-truncation.mjs @@ -14,5 +14,4 @@ Sentry.init({ enableTruncation: false, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs index 3650f937ce19..c76ba5928e20 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs @@ -13,5 +13,4 @@ Sentry.init({ enableTruncation: true, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs index 1e13bd68fe06..657bed0a3a8c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, traceLifecycle: 'stream', - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs index 4f7d6be20fc3..1c507ba84b9c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-with-pii.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs index c4cc37267b72..cc192fb89834 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: false, outputs: false } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index 817aef2923ca..f293ba29218e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -377,27 +377,31 @@ describe('LangGraph integration', () => { transaction: event => { const spans = event.spans ?? []; expect(event.transaction).toBe('main'); - expect(spans).toHaveLength(3); + expect(spans).toContainEqual(expect.objectContaining({ op: 'http.client' })); + }, + }) + .expect({ + span: container => { + const spans = container.items; expect(spans).toContainEqual( expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', - [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'helpful_assistant', - [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: 'helpful_assistant', - }), - description: 'invoke_agent helpful_assistant', - op: 'gen_ai.invoke_agent', - origin: 'auto.ai.langgraph', + name: 'invoke_agent helpful_assistant', status: 'ok', + attributes: expect.objectContaining({ + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'invoke_agent' }), + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: expect.objectContaining({ value: 'gen_ai.invoke_agent' }), + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: expect.objectContaining({ value: 'auto.ai.langgraph' }), + [GEN_AI_AGENT_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'helpful_assistant' }), + [GEN_AI_PIPELINE_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'helpful_assistant' }), + }), }), ); - expect(spans).toContainEqual(expect.objectContaining({ op: 'http.client' })); expect(spans).toContainEqual( expect.objectContaining({ - data: expect.objectContaining({ [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'helpful_assistant' }), - op: 'gen_ai.chat', + attributes: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: expect.objectContaining({ value: 'gen_ai.chat' }), + [GEN_AI_AGENT_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'helpful_assistant' }), + }), }), ); }, @@ -416,43 +420,49 @@ describe('LangGraph integration', () => { transaction: event => { const spans = event.spans ?? []; expect(event.transaction).toBe('main'); - expect(spans).toHaveLength(9); + expect(spans.filter(span => span.op === 'http.client')).toHaveLength(3); + }, + }) + .expect({ + span: container => { + const spans = container.items; expect(spans).toContainEqual( expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', - [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'math_assistant', - }), - op: 'gen_ai.invoke_agent', status: 'ok', + attributes: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: expect.objectContaining({ value: 'gen_ai.invoke_agent' }), + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'invoke_agent' }), + [GEN_AI_AGENT_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'math_assistant' }), + }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'execute_tool', - [GEN_AI_TOOL_NAME_ATTRIBUTE]: 'add', - 'gen_ai.tool.type': 'function', - }), - description: 'execute_tool add', - op: 'gen_ai.execute_tool', + name: 'execute_tool add', status: 'ok', + attributes: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: expect.objectContaining({ value: 'gen_ai.execute_tool' }), + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'execute_tool' }), + [GEN_AI_TOOL_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'add' }), + 'gen_ai.tool.type': expect.objectContaining({ value: 'function' }), + }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'execute_tool', - [GEN_AI_TOOL_NAME_ATTRIBUTE]: 'multiply', - 'gen_ai.tool.type': 'function', - }), - description: 'execute_tool multiply', - op: 'gen_ai.execute_tool', + name: 'execute_tool multiply', status: 'ok', + attributes: expect.objectContaining({ + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: expect.objectContaining({ value: 'gen_ai.execute_tool' }), + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'execute_tool' }), + [GEN_AI_TOOL_NAME_ATTRIBUTE]: expect.objectContaining({ value: 'multiply' }), + 'gen_ai.tool.type': expect.objectContaining({ value: 'function' }), + }), }), ); - expect(spans.filter(span => span.op === 'http.client')).toHaveLength(3); - expect(spans.filter(span => span.op === 'gen_ai.chat')).toHaveLength(3); + expect( + spans.filter(span => span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP]?.value === 'gen_ai.chat'), + ).toHaveLength(3); }, }) .start() @@ -466,12 +476,16 @@ describe('LangGraph integration', () => { .ignore('event') .expect({ transaction: event => { - const spans = event.spans ?? []; - const chatSpans = spans.filter(s => s.op === 'gen_ai.chat'); + expect(event.transaction).toBe('main'); + }, + }) + .expect({ + span: container => { + const chatSpans = container.items.filter( + s => s.attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP]?.value === 'gen_ai.chat', + ); expect(chatSpans).toHaveLength(1); - expect(chatSpans[0]?.data).toMatchObject({ - [GEN_AI_AGENT_NAME_ATTRIBUTE]: 'plain_assistant', - }); + expect(chatSpans[0]?.attributes[GEN_AI_AGENT_NAME_ATTRIBUTE]?.value).toBe('plain_assistant'); }, }) .start() diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-no-truncation.mjs index 11ae3216f624..442163b75b6e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-no-truncation.mjs @@ -20,5 +20,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-root-span.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-root-span.mjs index c4cc37267b72..cc192fb89834 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-root-span.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-root-span.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: false, outputs: false } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming-with-truncation.mjs index e31ae664dc52..c61dffa4c1f1 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming-with-truncation.mjs @@ -13,5 +13,4 @@ Sentry.init({ enableTruncation: true, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming.mjs index 1e13bd68fe06..657bed0a3a8c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-streaming.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, traceLifecycle: 'stream', - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-options.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-options.mjs index a1461f7b0633..fcc876ff8a72 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-options.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-options.mjs @@ -19,5 +19,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-pii.mjs index 3ce66a394943..e6d09440d3f8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-pii.mjs @@ -13,5 +13,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-truncation.mjs index abb1e1efdc28..cbd50b7a2262 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument-with-truncation.mjs @@ -14,5 +14,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/instrument.mjs index 8dc2049189b4..39221a5e1cf5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/instrument.mjs @@ -13,5 +13,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument-with-pii.mjs index 4f7d6be20fc3..1c507ba84b9c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument-with-pii.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument.mjs index c4cc37267b72..cc192fb89834 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/instrument.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: false, outputs: false } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-root-span.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-root-span.mjs index c4cc37267b72..cc192fb89834 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-root-span.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-root-span.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: false, outputs: false } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-options.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-options.mjs index 21716c6812d2..8b67890a613c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-options.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-options.mjs @@ -20,5 +20,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-pii.mjs index 3ce66a394943..e6d09440d3f8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument-with-pii.mjs @@ -13,5 +13,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument.mjs index 8dc2049189b4..39221a5e1cf5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/v6/instrument.mjs @@ -13,5 +13,4 @@ Sentry.init({ } return event; }, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-no-truncation.mjs index a5f66ed4ceba..64adb199d9c6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-no-truncation.mjs @@ -14,5 +14,4 @@ Sentry.init({ enableTruncation: false, }), ], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-pii.mjs index 4f7d6be20fc3..1c507ba84b9c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-pii.mjs @@ -7,5 +7,4 @@ Sentry.init({ tracesSampleRate: 1.0, dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-truncation.mjs index 7afab89bece4..0147e17d7b5f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument-with-truncation.mjs @@ -8,5 +8,4 @@ Sentry.init({ dataCollection: { genAI: { inputs: true, outputs: true } }, transport: loggingTransport, integrations: [Sentry.vercelAIIntegration({ enableTruncation: true })], - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument.mjs index dfe8424bb811..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/instrument.mjs @@ -6,5 +6,4 @@ Sentry.init({ release: '1.0', tracesSampleRate: 1.0, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument-with-pii.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument-with-pii.mjs index 29e017aa78f4..7a021162097c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument-with-pii.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument-with-pii.mjs @@ -8,5 +8,4 @@ Sentry.init({ // inputs and outputs are enabeld by default when opting into dataCollection dataCollection: {}, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument.mjs index dfe8424bb811..46a27dd03b74 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/instrument.mjs @@ -6,5 +6,4 @@ Sentry.init({ release: '1.0', tracesSampleRate: 1.0, transport: loggingTransport, - streamGenAiSpans: true, }); diff --git a/packages/core/src/tracing/ai/utils.ts b/packages/core/src/tracing/ai/utils.ts index fd762e61fbe3..ff9e072bf271 100644 --- a/packages/core/src/tracing/ai/utils.ts +++ b/packages/core/src/tracing/ai/utils.ts @@ -3,7 +3,6 @@ */ import { captureException } from '../../exports'; import { getClient } from '../../currentScopes'; -import { hasSpanStreamingEnabled } from '../spans/hasSpanStreamingEnabled'; import type { Span } from '../../types/span'; import { isThenable } from '../../utils/is'; import { @@ -60,22 +59,17 @@ export function resolveAIRecordingOptions(options? /** * Resolves whether truncation should be enabled. * If the user explicitly set `enableTruncation`, that value is used. - * Otherwise, truncation is disabled whenever gen_ai spans are sent through the span streaming / v2 - * span path, i.e. full span streaming (`traceLifecycle: 'stream'`) or `streamGenAiSpans`. That path - * is not subject to the transaction payload-size limits that truncation works around, so the full - * message data can be retained. `streamGenAiSpans` is opt-out (on unless explicitly set to `false`). + * Otherwise, truncation is disabled because gen_ai spans are always sent through the v2 span path + * (full span streaming via `traceLifecycle: 'stream'`, or extraction into a v2 span envelope for + * static transactions). That path is not subject to the transaction payload-size limits that + * truncation works around, so the full message data can be retained. */ export function shouldEnableTruncation(enableTruncation: boolean | undefined): boolean { if (enableTruncation !== undefined) { return enableTruncation; } - const client = getClient(); - if (!client) { - return true; - } - - return !hasSpanStreamingEnabled(client) && client.getOptions().streamGenAiSpans === false; + return !getClient(); } /** diff --git a/packages/core/src/tracing/spans/extractGenAiSpans.ts b/packages/core/src/tracing/spans/extractGenAiSpans.ts index 73bce236f7f0..05aaa3ff3e12 100644 --- a/packages/core/src/tracing/spans/extractGenAiSpans.ts +++ b/packages/core/src/tracing/spans/extractGenAiSpans.ts @@ -20,7 +20,6 @@ export function extractGenAiSpansFromEvent(event: Event, client: Client): SpanCo event.type !== 'transaction' || !event.spans?.length || !event.sdkProcessingMetadata?.hasGenAiSpans || - client.getOptions().streamGenAiSpans === false || hasSpanStreamingEnabled(client) ) { return undefined; diff --git a/packages/core/src/types/options.ts b/packages/core/src/types/options.ts index 3d55c5f17498..39dd00f38e0f 100644 --- a/packages/core/src/types/options.ts +++ b/packages/core/src/types/options.ts @@ -564,20 +564,6 @@ export interface ClientOptions { expect(shouldEnableTruncation(undefined)).toBe(true); }); - it('defaults to false with a default client (streamGenAiSpans is opt-out)', () => { + it('defaults to false with a default client', () => { setupClient(); expect(shouldEnableTruncation(undefined)).toBe(false); }); - it('defaults to true when streamGenAiSpans is explicitly disabled', () => { - setupClient({ streamGenAiSpans: false }); - expect(shouldEnableTruncation(undefined)).toBe(true); - }); - - it('defaults to false when streamGenAiSpans is enabled', () => { - setupClient({ streamGenAiSpans: true }); - expect(shouldEnableTruncation(undefined)).toBe(false); - }); - it('defaults to false when span streaming is enabled (traceLifecycle: stream)', () => { setupClient({ traceLifecycle: 'stream' }); expect(shouldEnableTruncation(undefined)).toBe(false); }); - it('explicit enableTruncation: true overrides streamGenAiSpans', () => { - setupClient({ streamGenAiSpans: true }); + it('explicit enableTruncation: true overrides the default', () => { + setupClient(); expect(shouldEnableTruncation(true)).toBe(true); }); diff --git a/packages/core/test/lib/tracing/spans/extractGenAiSpans.test.ts b/packages/core/test/lib/tracing/spans/extractGenAiSpans.test.ts index de47457144d3..f21e164dccba 100644 --- a/packages/core/test/lib/tracing/spans/extractGenAiSpans.test.ts +++ b/packages/core/test/lib/tracing/spans/extractGenAiSpans.test.ts @@ -120,7 +120,7 @@ describe('extractGenAiSpansFromEvent', () => { expect(extractGenAiSpansFromEvent(event, makeClient())).toBeUndefined(); }); - it('extracts gen_ai spans by default when streamGenAiSpans is unset', () => { + it('extracts gen_ai spans by default', () => { const event = makeTransactionEvent([makeSpanJSON({ op: 'gen_ai.chat', span_id: 'genai001' })], true); const result = extractGenAiSpansFromEvent(event, makeClient()); @@ -130,14 +130,6 @@ describe('extractGenAiSpansFromEvent', () => { expect(event.spans).toHaveLength(0); }); - it('returns undefined when streamGenAiSpans is explicitly disabled', () => { - const event = makeTransactionEvent([makeSpanJSON({ op: 'gen_ai.chat' })]); - const client = makeClient({ streamGenAiSpans: false }); - - expect(extractGenAiSpansFromEvent(event, client)).toBeUndefined(); - expect(event.spans).toHaveLength(1); - }); - it('returns undefined when span streaming is enabled', () => { const event = makeTransactionEvent([makeSpanJSON({ op: 'gen_ai.chat' })]); const client = makeClient({ traceLifecycle: 'stream' });