From 95222a182e22050c281e5f6e5ab4fbe7a90e41ee Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Sun, 12 Apr 2026 02:19:27 +0900 Subject: [PATCH 1/4] feat(core): Automatically disable truncation when span streaming is enabled in LangGraph integration When span streaming is enabled, the `enableTruncation` option now defaults to `false` unless the user has explicitly set it. Closes: #20225 --- .../instrument-streaming-with-truncation.mjs | 16 ++++++ .../langgraph/instrument-streaming.mjs | 11 ++++ .../suites/tracing/langgraph/test.ts | 51 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs create mode 100644 dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs 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 new file mode 100644 index 000000000000..2d8d986a2cd1 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming-with-truncation.mjs @@ -0,0 +1,16 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + sendDefaultPii: true, + transport: loggingTransport, + traceLifecycle: 'stream', + integrations: [ + Sentry.langGraphIntegration({ + enableTruncation: 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 new file mode 100644 index 000000000000..48a860c510c5 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument-streaming.mjs @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/node'; +import { loggingTransport } from '@sentry-internal/node-integration-tests'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + sendDefaultPii: true, + transport: loggingTransport, + traceLifecycle: 'stream', +}); 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 329cb914851a..5a616ca97a9d 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -398,4 +398,55 @@ describe('LangGraph integration', () => { }); }, ); + + const streamingLongContent = 'A'.repeat(50_000); + + createEsmAndCjsTests( + __dirname, + 'scenario-no-truncation.mjs', + 'instrument-streaming.mjs', + (createRunner, test) => { + test('automatically disables truncation when span streaming is enabled', async () => { + await createRunner() + .expect({ + span: container => { + const spans = container.items; + + const chatSpan = spans.find( + s => s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes(streamingLongContent), + ); + expect(chatSpan).toBeDefined(); + }, + }) + .start() + .completed(); + }); + }, + ); + + createEsmAndCjsTests( + __dirname, + 'scenario-no-truncation.mjs', + 'instrument-streaming-with-truncation.mjs', + (createRunner, test) => { + test('respects explicit enableTruncation: true even when span streaming is enabled', async () => { + await createRunner() + .expect({ + span: container => { + const spans = container.items; + + // With explicit enableTruncation: true, truncation keeps only the last message + // and drops the long content. The result should NOT contain the full 50k 'A' string. + const chatSpan = spans.find( + s => s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes('Follow-up question'), + ); + expect(chatSpan).toBeDefined(); + expect(chatSpan!.attributes[GEN_AI_INPUT_MESSAGES_ATTRIBUTE].value).not.toContain(streamingLongContent); + }, + }) + .start() + .completed(); + }); + }, + ); }); From 670d7f6cdfac425191d45479c54de6db14727f09 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Sun, 12 Apr 2026 01:56:19 +0900 Subject: [PATCH 2/4] Add shouldEnableTruncation helper --- packages/core/src/tracing/ai/utils.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/core/src/tracing/ai/utils.ts b/packages/core/src/tracing/ai/utils.ts index d3cce644dbc1..05502b249efb 100644 --- a/packages/core/src/tracing/ai/utils.ts +++ b/packages/core/src/tracing/ai/utils.ts @@ -3,6 +3,7 @@ */ import { captureException } from '../../exports'; import { getClient } from '../../currentScopes'; +import { hasSpanStreamingEnabled } from '../spans/hasSpanStreamingEnabled'; import type { Span } from '../../types-hoist/span'; import { isThenable } from '../../utils/is'; import { @@ -56,6 +57,16 @@ export function resolveAIRecordingOptions(options? } as T & Required; } +/** + * Resolves whether truncation should be enabled. + * If the user explicitly set `enableTruncation`, that value is used. + * Otherwise, truncation is disabled when span streaming is active. + */ +export function shouldEnableTruncation(enableTruncation: boolean | undefined): boolean { + const client = getClient(); + return enableTruncation ?? !(client && hasSpanStreamingEnabled(client)); +} + /** * Build method path from current traversal */ From db1daef04347b298c01bfc0cb2a26eecdde1bb92 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Sun, 12 Apr 2026 02:20:44 +0900 Subject: [PATCH 3/4] Use shouldEnableTruncation in LangGraph integration --- packages/core/src/tracing/langgraph/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index 5230b43bb54d..d188fe90d97f 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -17,6 +17,7 @@ import { getJsonString, getTruncatedJsonString, resolveAIRecordingOptions, + shouldEnableTruncation, } from '../ai/utils'; import type { LangChainMessage } from '../langchain/types'; import { normalizeLangChainMessages } from '../langchain/utils'; @@ -150,7 +151,7 @@ function instrumentCompiledGraphInvoke( span.setAttribute(GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions); } - const enableTruncation = options.enableTruncation ?? true; + const enableTruncation = shouldEnableTruncation(options.enableTruncation); const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0; span.setAttributes({ [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: enableTruncation From 7912b20def693f8404f1dfef2dbba7286ea84f13 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Sun, 12 Apr 2026 02:22:21 +0900 Subject: [PATCH 4/4] Format test file --- .../suites/tracing/langgraph/test.ts | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) 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 5a616ca97a9d..387694c70563 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -401,28 +401,23 @@ describe('LangGraph integration', () => { const streamingLongContent = 'A'.repeat(50_000); - createEsmAndCjsTests( - __dirname, - 'scenario-no-truncation.mjs', - 'instrument-streaming.mjs', - (createRunner, test) => { - test('automatically disables truncation when span streaming is enabled', async () => { - await createRunner() - .expect({ - span: container => { - const spans = container.items; + createEsmAndCjsTests(__dirname, 'scenario-no-truncation.mjs', 'instrument-streaming.mjs', (createRunner, test) => { + test('automatically disables truncation when span streaming is enabled', async () => { + await createRunner() + .expect({ + span: container => { + const spans = container.items; - const chatSpan = spans.find( - s => s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes(streamingLongContent), - ); - expect(chatSpan).toBeDefined(); - }, - }) - .start() - .completed(); - }); - }, - ); + const chatSpan = spans.find(s => + s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes(streamingLongContent), + ); + expect(chatSpan).toBeDefined(); + }, + }) + .start() + .completed(); + }); + }); createEsmAndCjsTests( __dirname, @@ -437,8 +432,8 @@ describe('LangGraph integration', () => { // With explicit enableTruncation: true, truncation keeps only the last message // and drops the long content. The result should NOT contain the full 50k 'A' string. - const chatSpan = spans.find( - s => s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes('Follow-up question'), + const chatSpan = spans.find(s => + s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes('Follow-up question'), ); expect(chatSpan).toBeDefined(); expect(chatSpan!.attributes[GEN_AI_INPUT_MESSAGES_ATTRIBUTE].value).not.toContain(streamingLongContent);