From cee1039057b01879d2afa3987177c5fdeabef8a8 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 4 Dec 2025 11:55:09 +0100 Subject: [PATCH 01/27] Add failing test --- .../tracing/langgraph/agent-scenario.mjs | 73 +++++++++++++++++++ .../suites/tracing/langgraph/test.ts | 40 ++++++++++ 2 files changed, 113 insertions(+) create mode 100644 dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs new file mode 100644 index 000000000000..a0db57c630dd --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs @@ -0,0 +1,73 @@ +import { ChatAnthropic } from '@langchain/anthropic'; +import { createReactAgent } from '@langchain/langgraph/prebuilt'; +import { HumanMessage, SystemMessage } from '@langchain/core/messages'; +import * as Sentry from '@sentry/node'; +import express from 'express'; + +function startMockAnthropicServer() { + const app = express(); + app.use(express.json()); + + app.post('/v1/messages', (req, res) => { + const model = req.body.model; + + // Simulate basic response + res.json({ + id: 'msg_react_agent_123', + type: 'message', + role: 'assistant', + content: [ + { + type: 'text', + text: 'Mock response from Anthropic!', + }, + ], + model: model, + stop_reason: 'end_turn', + stop_sequence: null, + usage: { + input_tokens: 10, + output_tokens: 15, + }, + }); + }); + + return new Promise(resolve => { + const server = app.listen(0, () => { + resolve(server); + }); + }); +} + +async function run() { + const server = await startMockAnthropicServer(); + const baseUrl = `http://localhost:${server.address().port}`; + + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { + // Create mocked LLM instance + const llm = new ChatAnthropic({ + model: 'claude-3-5-sonnet-20241022', + apiKey: 'mock-api-key', + clientOptions: { + baseURL: baseUrl, + }, + }); + + // Create a simple react agent with no tools + const agent = createReactAgent({ llm, tools: [] }); + + // Test: basic invocation + await agent.invoke({ + messages: [ + new SystemMessage('You are a helpful assistant.'), + new HumanMessage('What is the weather today?'), + ], + }); + }); + + await Sentry.flush(2000); + + server.close(); +} + +run(); 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 6a67b5cd1e86..bc1646db5468 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -205,4 +205,44 @@ describe('LangGraph integration', () => { await createRunner().ignore('event').expect({ transaction: EXPECTED_TRANSACTION_WITH_TOOLS }).start().completed(); }); }); + + const EXPECTED_TRANSACTION_REACT_AGENT = { + transaction: 'main', + spans: expect.arrayContaining([ + // create_agent span + expect.objectContaining({ + data: expect.objectContaining({ + 'gen_ai.operation.name': 'create_agent', + 'sentry.op': 'gen_ai.create_agent', + 'sentry.origin': 'auto.ai.langgraph', + }), + description: expect.stringContaining('create_agent'), + op: 'gen_ai.create_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + // invoke_agent span + expect.objectContaining({ + data: expect.objectContaining({ + 'gen_ai.operation.name': 'invoke_agent', + 'sentry.op': 'gen_ai.invoke_agent', + 'sentry.origin': 'auto.ai.langgraph', + }), + description: expect.stringContaining('invoke_agent'), + op: 'gen_ai.invoke_agent', + origin: 'auto.ai.langgraph', + status: 'ok', + }), + ]), + }; + + createEsmAndCjsTests(__dirname, 'agent-scenario.mjs', 'instrument.mjs', (createRunner, test) => { + test('should instrument LangGraph createReactAgent with default PII settings', async () => { + await createRunner() + .ignore('event') + .expect({ transaction: EXPECTED_TRANSACTION_REACT_AGENT }) + .start() + .completed(); + }); + }); }); From 877b10edfb81133c50952a87fb5786d5324a1c93 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 4 Dec 2025 15:10:18 +0100 Subject: [PATCH 02/27] Try to instrument createReactAgent (doesn't work yet) --- packages/core/src/index.ts | 2 +- packages/core/src/tracing/langgraph/index.ts | 45 +++++++++++++++++++ .../tracing/langgraph/instrumentation.ts | 32 ++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 387ba0aba4a2..d0e355b0e55f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -152,7 +152,7 @@ export type { GoogleGenAIResponse } from './tracing/google-genai/types'; export { createLangChainCallbackHandler } from './tracing/langchain'; export { LANGCHAIN_INTEGRATION_NAME } from './tracing/langchain/constants'; export type { LangChainOptions, LangChainIntegration } from './tracing/langchain/types'; -export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/langgraph'; +export { instrumentStateGraphCompile, instrumentCreateReactAgent, instrumentLangGraph } from './tracing/langgraph'; export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index 5601cddf458b..c323d2d0d701 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -156,6 +156,51 @@ function instrumentCompiledGraphInvoke( }) as (...args: unknown[]) => Promise; } +/** + * Instruments createReactAgent to create spans for agent creation and invocation + * + * Creates a `gen_ai.create_agent` span when createReactAgent() is called + */ +export function instrumentCreateReactAgent( + originalCreateReactAgent: (...args: unknown[]) => CompiledGraph, + options: LangGraphOptions, +): (...args: unknown[]) => CompiledGraph { + return new Proxy(originalCreateReactAgent, { + apply(target, thisArg, args: unknown[]): CompiledGraph { + return startSpan( + { + op: 'gen_ai.create_agent', + name: 'create_agent', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN, + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent', + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', + }, + }, + () => { + try { + const compiledGraph = Reflect.apply(target, thisArg, args); + const compiledOptions = args.length > 0 ? (args[0] as Record) : {}; + const originalInvoke = compiledGraph.invoke; + if (originalInvoke && typeof originalInvoke === 'function') { + compiledGraph.invoke = instrumentCompiledGraphInvoke( + originalInvoke.bind(compiledGraph) as (...args: unknown[]) => Promise, + compiledGraph, + compiledOptions, + options, + ) as typeof originalInvoke; + } + + return compiledGraph; + } catch (error) { + throw error; + } + }, + ); + }, + }) as (...args: unknown[]) => CompiledGraph; +} + /** * Directly instruments a StateGraph instance to add tracing spans * diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index d275e1b9d39b..03713f10a258 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -6,7 +6,7 @@ import { InstrumentationNodeModuleFile, } from '@opentelemetry/instrumentation'; import type { CompiledGraph, LangGraphOptions } from '@sentry/core'; -import { getClient, instrumentStateGraphCompile, SDK_VERSION } from '@sentry/core'; +import { getClient, instrumentStateGraphCompile, instrumentCreateReactAgent, SDK_VERSION } from '@sentry/core'; const supportedVersions = ['>=0.0.0 <2.0.0']; @@ -50,6 +50,16 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), + new InstrumentationNodeModuleFile( + /** + * Patch the prebuilt subpath exports for CJS. + * The @langchain/langgraph/prebuilt entry point re-exports from dist/prebuilt/index.cjs + */ + '@langchain/langgraph/dist/prebuilt/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), ], ); return module; @@ -83,6 +93,26 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, + options, + ); + /* + const originalCreateReactAgent = exports.createReactAgent; + Object.defineProperty(exports, 'createReactAgent', { + value: instrumentCreateReactAgent( + originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, + options, + ), + writable: true, + enumerable: true, + configurable: true, + }); + */ + } + return exports; } } From 335ee5a382e0e6ce3d0626cbf47edd0617c204d0 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 10 Dec 2025 17:34:24 +0100 Subject: [PATCH 03/27] Use two modules --- .../tracing/langgraph/instrumentation.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 03713f10a258..7126a45321ff 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -31,8 +31,8 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), + ], + ); + + const prebuiltModule = new InstrumentationNodeModuleDefinition( + '@langchain/langgraph/prebuilt', + supportedVersions, + this._patch.bind(this), + exports => exports, + [ new InstrumentationNodeModuleFile( /** * Patch the prebuilt subpath exports for CJS. @@ -62,7 +71,8 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase Date: Wed, 10 Dec 2025 17:38:26 +0100 Subject: [PATCH 04/27] clean comment --- .../tracing/langgraph/instrumentation.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 7126a45321ff..4538ddcda7ee 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -109,18 +109,6 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options, ); - /* - const originalCreateReactAgent = exports.createReactAgent; - Object.defineProperty(exports, 'createReactAgent', { - value: instrumentCreateReactAgent( - originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, - options, - ), - writable: true, - enumerable: true, - configurable: true, - }); - */ } return exports; From 819cf399962ea879cf5fb8f11ddca938d370cc64 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 10 Dec 2025 17:55:49 +0100 Subject: [PATCH 05/27] Split patch for main and prebuilt langgraph modules --- .../tracing/langgraph/instrumentation.ts | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 4538ddcda7ee..e8fe0ca2fbe0 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -35,7 +35,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, [ new InstrumentationNodeModuleFile( @@ -43,11 +43,11 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), ], @@ -56,17 +56,19 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, [ new InstrumentationNodeModuleFile( /** - * Patch the prebuilt subpath exports for CJS. - * The @langchain/langgraph/prebuilt entry point re-exports from dist/prebuilt/index.cjs - */ + * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patchPrebuiltModule runs again on the concrete implementation + */ '@langchain/langgraph/dist/prebuilt/index.cjs', supportedVersions, - this._patch.bind(this), + this._patchPrebuiltModule.bind(this), exports => exports, ), ], @@ -76,20 +78,10 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase Date: Thu, 11 Dec 2025 17:27:29 +0100 Subject: [PATCH 06/27] revert stuff --- .../tracing/langgraph/instrumentation.ts | 85 +++++++------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index e8fe0ca2fbe0..646460720721 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -6,7 +6,7 @@ import { InstrumentationNodeModuleFile, } from '@opentelemetry/instrumentation'; import type { CompiledGraph, LangGraphOptions } from '@sentry/core'; -import { getClient, instrumentStateGraphCompile, instrumentCreateReactAgent, SDK_VERSION } from '@sentry/core'; +import { getClient, instrumentCreateReactAgent, instrumentStateGraphCompile, SDK_VERSION } from '@sentry/core'; const supportedVersions = ['>=0.0.0 <2.0.0']; @@ -31,11 +31,11 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, [ new InstrumentationNodeModuleFile( @@ -43,45 +43,45 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), - ], - ); - - const prebuiltModule = new InstrumentationNodeModuleDefinition( - '@langchain/langgraph/prebuilt', - supportedVersions, - this._patchPrebuiltModule.bind(this), - exports => exports, - [ new InstrumentationNodeModuleFile( /** * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. * Patching only the root module sometimes misses the real implementation or * gets overwritten when that file is loaded. We add a file-level patch so that - * _patchPrebuiltModule runs again on the concrete implementation - */ + * _patch runs again on the concrete implementation + */ '@langchain/langgraph/dist/prebuilt/index.cjs', supportedVersions, - this._patchPrebuiltModule.bind(this), + this._patch.bind(this), exports => exports, ), ], ); - - return [mainModule, prebuiltModule]; + return module; } /** - * Patch logic applying instrumentation to the LangGraph main module. + * Core patch logic applying instrumentation to the LangGraph module. */ - private _patchMainModule(exports: PatchedModuleExports): PatchedModuleExports | void { - const options = this._getOptions(); + private _patch(exports: PatchedModuleExports): PatchedModuleExports | void { + const client = getClient(); + const defaultPii = Boolean(client?.getOptions().sendDefaultPii); + + const config = this.getConfig(); + const recordInputs = config.recordInputs ?? defaultPii; + const recordOutputs = config.recordOutputs ?? defaultPii; + + const options: LangGraphOptions = { + recordInputs, + recordOutputs, + }; // Patch StateGraph.compile to instrument both compile() and invoke() if (exports.StateGraph && typeof exports.StateGraph === 'function') { @@ -95,37 +95,18 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, + const originalCreateReactAgent = exports.createReactAgent; + Object.defineProperty(exports, 'createReactAgent', { + value: instrumentCreateReactAgent( + originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, options, - ); - } + ), + writable: true, + enumerable: true, + configurable: true, + }); return exports; } - - /** - * Helper to get instrumentation options - */ - private _getOptions(): LangGraphOptions { - const client = getClient(); - const defaultPii = Boolean(client?.getOptions().sendDefaultPii); - const config = this.getConfig(); - - return { - recordInputs: config.recordInputs ?? defaultPii, - recordOutputs: config.recordOutputs ?? defaultPii, - }; - } } From 0246624f4c8fa973cd8c65c2fafe795ac94aa1a2 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 11 Dec 2025 17:45:33 +0100 Subject: [PATCH 07/27] now we get some spans for cjs --- .../tracing/langgraph/instrumentation.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 646460720721..0aac8801fee4 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -71,6 +71,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, - options, - ), - writable: true, - enumerable: true, - configurable: true, - }); + if (exports.createReactAgent && typeof exports.createReactAgent === 'function') { + const originalCreateReactAgent = exports.createReactAgent; + Object.defineProperty(exports, 'createReactAgent', { + value: instrumentCreateReactAgent( + originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, + options, + ), + writable: true, + enumerable: true, + configurable: true, + }); + } return exports; } From e9862c0d141b5d892677031d7db34b3312ca3baa Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 11 Dec 2025 18:00:08 +0100 Subject: [PATCH 08/27] try to fix esm with explicit file wrap --- .../tracing/langgraph/instrumentation.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 0aac8801fee4..c739472cd758 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -62,8 +62,19 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), + new InstrumentationNodeModuleFile( + /** + * ESM builds use dist/prebuilt/index.js (without .cjs extension) + * This catches ESM imports that resolve through the main package + */ + '@langchain/langgraph/dist/prebuilt/index.js', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), ], ); + return module; } @@ -71,7 +82,6 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase Date: Thu, 11 Dec 2025 19:03:24 +0100 Subject: [PATCH 09/27] Fix formatting --- .../suites/tracing/langgraph/agent-scenario.mjs | 7 ++----- packages/core/src/tracing/langgraph/index.ts | 9 ++++++++- .../integrations/tracing/langgraph/instrumentation.ts | 5 +---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs index a0db57c630dd..cef786b8988f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/agent-scenario.mjs @@ -1,6 +1,6 @@ import { ChatAnthropic } from '@langchain/anthropic'; -import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { HumanMessage, SystemMessage } from '@langchain/core/messages'; +import { createReactAgent } from '@langchain/langgraph/prebuilt'; import * as Sentry from '@sentry/node'; import express from 'express'; @@ -58,10 +58,7 @@ async function run() { // Test: basic invocation await agent.invoke({ - messages: [ - new SystemMessage('You are a helpful assistant.'), - new HumanMessage('What is the weather today?'), - ], + messages: [new SystemMessage('You are a helpful assistant.'), new HumanMessage('What is the weather today?')], }); }); diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index c323d2d0d701..d29b97262aa3 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -177,7 +177,7 @@ export function instrumentCreateReactAgent( [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', }, }, - () => { + span => { try { const compiledGraph = Reflect.apply(target, thisArg, args); const compiledOptions = args.length > 0 ? (args[0] as Record) : {}; @@ -193,6 +193,13 @@ export function instrumentCreateReactAgent( return compiledGraph; } catch (error) { + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + captureException(error, { + mechanism: { + handled: false, + type: 'auto.ai.langgraph.error', + }, + }); throw error; } }, diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index c739472cd758..87077d457909 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -110,10 +110,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, - options, - ), + value: instrumentCreateReactAgent(originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, options), writable: true, enumerable: true, configurable: true, From 481fc12164371a6e1206cad2f2e7aafb8275e410 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 11 Dec 2025 19:30:26 +0100 Subject: [PATCH 10/27] try catch --- .../src/integrations/tracing/langgraph/instrumentation.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 87077d457909..6bf50718b66b 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -107,10 +107,12 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options); + try { + exports.createReactAgent = wrappedCreateReactAgent; + } catch (error) { Object.defineProperty(exports, 'createReactAgent', { - value: instrumentCreateReactAgent(originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, options), + value: wrappedCreateReactAgent, writable: true, enumerable: true, configurable: true, From 5ec44274aa59bdf2e8e4bab7f77cbec59a6acf14 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 11 Dec 2025 19:48:13 +0100 Subject: [PATCH 11/27] Revert "try catch" This reverts commit 481fc12164371a6e1206cad2f2e7aafb8275e410. --- .../src/integrations/tracing/langgraph/instrumentation.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 6bf50718b66b..87077d457909 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -107,12 +107,10 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options); - try { - exports.createReactAgent = wrappedCreateReactAgent; - } catch (error) { + if (exports.createReactAgent && typeof exports.createReactAgent === 'function') { + const originalCreateReactAgent = exports.createReactAgent; Object.defineProperty(exports, 'createReactAgent', { - value: wrappedCreateReactAgent, + value: instrumentCreateReactAgent(originalCreateReactAgent as (...args: unknown[]) => CompiledGraph, options), writable: true, enumerable: true, configurable: true, From cade4aa0ad8abee8b86f0bd87fd43d4c3fac91b7 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 19 Dec 2025 10:05:34 +0100 Subject: [PATCH 12/27] bump iitm --- packages/node-core/package.json | 2 +- packages/node/package.json | 2 +- yarn.lock | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/node-core/package.json b/packages/node-core/package.json index d9ddb61ed79f..b06941f69a71 100644 --- a/packages/node-core/package.json +++ b/packages/node-core/package.json @@ -69,7 +69,7 @@ "@apm-js-collab/tracing-hooks": "^0.3.1", "@sentry/core": "10.31.0", "@sentry/opentelemetry": "10.31.0", - "import-in-the-middle": "^2" + "import-in-the-middle": "^2.0.1" }, "devDependencies": { "@apm-js-collab/code-transformer": "^0.8.2", diff --git a/packages/node/package.json b/packages/node/package.json index 6809b5b56710..f88f27f3b36e 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -98,7 +98,7 @@ "@sentry/core": "10.31.0", "@sentry/node-core": "10.31.0", "@sentry/opentelemetry": "10.31.0", - "import-in-the-middle": "^2", + "import-in-the-middle": "^2.0.1", "minimatch": "^9.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index db80dbff16ae..232266a262db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19238,7 +19238,7 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^2, import-in-the-middle@^2.0.0: +import-in-the-middle@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz#295948cee94d0565314824c6bd75379d13e5b1a5" integrity sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A== @@ -19248,6 +19248,16 @@ import-in-the-middle@^2, import-in-the-middle@^2.0.0: cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" +import-in-the-middle@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.1.tgz#8d1aa2db18374f2c811de2aa4756ebd6e9859243" + integrity sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA== + dependencies: + acorn "^8.14.0" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + import-local@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" From 5ac942fa781ed451160f1715188312fcc15265ae Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 19 Dec 2025 10:54:41 +0100 Subject: [PATCH 13/27] resolve iitm --- package.json | 3 ++- yarn.lock | 12 +----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index b0f32aa93a50..25bfcbba603d 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,8 @@ "gauge/strip-ansi": "6.0.1", "wide-align/string-width": "4.2.3", "cliui/wrap-ansi": "7.0.0", - "sucrase": "getsentry/sucrase#es2020-polyfills" + "sucrase": "getsentry/sucrase#es2020-polyfills", + "import-in-the-middle": "^2.0.1" }, "version": "0.0.0", "name": "sentry-javascript", diff --git a/yarn.lock b/yarn.lock index df79ec4ceba6..4e4f4e00b0ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19238,17 +19238,7 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz#295948cee94d0565314824c6bd75379d13e5b1a5" - integrity sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A== - dependencies: - acorn "^8.14.0" - acorn-import-attributes "^1.9.5" - cjs-module-lexer "^1.2.2" - module-details-from-path "^1.0.3" - -import-in-the-middle@^2.0.1: +import-in-the-middle@^2.0.0, import-in-the-middle@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.1.tgz#8d1aa2db18374f2c811de2aa4756ebd6e9859243" integrity sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA== From 703c29a106bf80a55c092d5db96f946ca72959af Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 19 Dec 2025 17:29:56 +0100 Subject: [PATCH 14/27] remove file --- .../tracing/langgraph/instrumentation.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 87077d457909..82591a12eb23 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -62,16 +62,6 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), - new InstrumentationNodeModuleFile( - /** - * ESM builds use dist/prebuilt/index.js (without .cjs extension) - * This catches ESM imports that resolve through the main package - */ - '@langchain/langgraph/dist/prebuilt/index.js', - supportedVersions, - this._patch.bind(this), - exports => exports, - ), ], ); @@ -82,6 +72,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase; }; @@ -108,6 +100,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options), From 7e81978421493b03fbb0f818643f1b450d6beb0a Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 19 Dec 2025 18:18:59 +0100 Subject: [PATCH 15/27] try as separate module --- .../tracing/langgraph/instrumentation.ts | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 82591a12eb23..9cfa98bb2932 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -18,6 +18,7 @@ type LangGraphInstrumentationOptions = InstrumentationConfig & LangGraphOptions; interface PatchedModuleExports { [key: string]: unknown; StateGraph?: abstract new (...args: unknown[]) => unknown; + createReactAgent?: (...args: unknown[]) => CompiledGraph; } /** @@ -31,41 +32,43 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, - [ - new InstrumentationNodeModuleFile( - /** - * In CJS, LangGraph packages re-export from dist/index.cjs files. - * Patching only the root module sometimes misses the real implementation or - * gets overwritten when that file is loaded. We add a file-level patch so that - * _patch runs again on the concrete implementation - */ - '@langchain/langgraph/dist/index.cjs', - supportedVersions, - this._patch.bind(this), - exports => exports, - ), - new InstrumentationNodeModuleFile( - /** - * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. - * Patching only the root module sometimes misses the real implementation or - * gets overwritten when that file is loaded. We add a file-level patch so that - * _patch runs again on the concrete implementation - */ - '@langchain/langgraph/dist/prebuilt/index.cjs', - supportedVersions, - this._patch.bind(this), - exports => exports, - ), - ], - ); - - return module; + public init(): InstrumentationModuleDefinition[] { + return [ + new InstrumentationNodeModuleDefinition( + '@langchain/langgraph', + supportedVersions, + this._patch.bind(this), + exports => exports, + [ + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + ], + ), + new InstrumentationNodeModuleDefinition( + '@langchain/langgraph/prebuilt', + supportedVersions, + this._patch.bind(this), + exports => exports, + [ + new InstrumentationNodeModuleFile( + '@langchain/langgraph/dist/prebuilt/index.js', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + ], + ), + ]; } /** From e22b7079708985a9bdb2b0983e26bdc5f1307d30 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 15 Dec 2025 15:58:06 -0800 Subject: [PATCH 16/27] fix(tracing): get llm request model in createReactAgent --- packages/core/src/tracing/langchain/types.ts | 8 + packages/core/src/tracing/langgraph/index.ts | 12 +- packages/core/src/tracing/langgraph/utils.ts | 19 +- .../test/lib/utils/langraph-utils.test.ts | 255 ++++++++++++++++++ 4 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 packages/core/test/lib/utils/langraph-utils.test.ts diff --git a/packages/core/src/tracing/langchain/types.ts b/packages/core/src/tracing/langchain/types.ts index 7379de764817..e2aa08f4631c 100644 --- a/packages/core/src/tracing/langchain/types.ts +++ b/packages/core/src/tracing/langchain/types.ts @@ -30,6 +30,14 @@ export interface LangChainSerialized { kwargs?: Record; } +/** + * Subset of the 'llm' param passed to createReactAgent + */ +export interface BaseChatModel { + lc_namespace: string[]; + modelName: string; +} + /** * LangChain message structure * Supports both regular messages and LangChain serialized format diff --git a/packages/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index d29b97262aa3..67f90357d713 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -8,14 +8,15 @@ import { GEN_AI_PIPELINE_NAME_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, + GEN_AI_REQUEST_MODEL_ATTRIBUTE, } from '../ai/gen-ai-attributes'; import { truncateGenAiMessages } from '../ai/messageTruncation'; -import type { LangChainMessage } from '../langchain/types'; +import type { BaseChatModel, LangChainMessage } from '../langchain/types'; import { normalizeLangChainMessages } from '../langchain/utils'; import { startSpan } from '../trace'; import { LANGGRAPH_ORIGIN } from './constants'; import type { CompiledGraph, LangGraphOptions } from './types'; -import { extractToolsFromCompiledGraph, setResponseAttributes } from './utils'; +import { extractLLMFromParams, extractToolsFromCompiledGraph, setResponseAttributes } from './utils'; /** * Instruments StateGraph's compile method to create spans for agent creation and invocation @@ -90,9 +91,11 @@ function instrumentCompiledGraphInvoke( graphInstance: CompiledGraph, compileOptions: Record, options: LangGraphOptions, + llm?: BaseChatModel | null, ): (...args: unknown[]) => Promise { return new Proxy(originalInvoke, { apply(target, thisArg, args: unknown[]): Promise { + const modelName = llm?.modelName; return startSpan( { op: 'gen_ai.invoke_agent', @@ -112,6 +115,9 @@ function instrumentCompiledGraphInvoke( span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, graphName); span.updateName(`invoke_agent ${graphName}`); } + if (modelName) { + span.setAttribute(GEN_AI_REQUEST_MODEL_ATTRIBUTE, modelName); + } // Extract available tools from the graph instance const tools = extractToolsFromCompiledGraph(graphInstance); @@ -167,6 +173,7 @@ export function instrumentCreateReactAgent( ): (...args: unknown[]) => CompiledGraph { return new Proxy(originalCreateReactAgent, { apply(target, thisArg, args: unknown[]): CompiledGraph { + const llm = extractLLMFromParams(args); return startSpan( { op: 'gen_ai.create_agent', @@ -188,6 +195,7 @@ export function instrumentCreateReactAgent( compiledGraph, compiledOptions, options, + llm, ) as typeof originalInvoke; } diff --git a/packages/core/src/tracing/langgraph/utils.ts b/packages/core/src/tracing/langgraph/utils.ts index 4b1990058924..f2c64052c071 100644 --- a/packages/core/src/tracing/langgraph/utils.ts +++ b/packages/core/src/tracing/langgraph/utils.ts @@ -8,10 +8,25 @@ import { GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, } from '../ai/gen-ai-attributes'; -import type { LangChainMessage } from '../langchain/types'; +import type { BaseChatModel, LangChainMessage } from '../langchain/types'; import { normalizeLangChainMessages } from '../langchain/utils'; import type { CompiledGraph, LangGraphTool } from './types'; +/** + * Extract LLM model object from createReactAgent params + */ +export function extractLLMFromParams(args: unknown[]): null | BaseChatModel { + const arg = args[0]; + return typeof arg === 'object' && + !!arg && + 'llm' in arg && + !!arg.llm && + typeof arg.llm === 'object' && + typeof (arg.llm as BaseChatModel).modelName === 'string' + ? (arg.llm as BaseChatModel) + : null; +} + /** * Extract tool calls from messages */ @@ -139,7 +154,9 @@ export function setResponseAttributes(span: Span, inputMessages: LangChainMessag } // Get new messages (delta between input and output) + /* v8 ignore start - coverage gets confused by this somehow */ const inputCount = inputMessages?.length ?? 0; + /* v8 ignore stop */ const newMessages = outputMessages.length > inputCount ? outputMessages.slice(inputCount) : []; if (newMessages.length === 0) { diff --git a/packages/core/test/lib/utils/langraph-utils.test.ts b/packages/core/test/lib/utils/langraph-utils.test.ts new file mode 100644 index 000000000000..bd8d5c7193ec --- /dev/null +++ b/packages/core/test/lib/utils/langraph-utils.test.ts @@ -0,0 +1,255 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import type { Span } from '../../../src'; +import { + extractLLMFromParams, + extractModelMetadata, + extractTokenUsageFromMessage, + extractToolCalls, + extractToolsFromCompiledGraph, + setResponseAttributes, +} from '../../../src/tracing/langgraph/utils'; + +describe('extractLLMFromParams', () => { + it('handles invalid args or missing llm object', () => { + // @ts-expect-error should be arguments array, at least. + expect(extractLLMFromParams({})).toBe(null); + expect(extractLLMFromParams([])).toBe(null); + expect(extractLLMFromParams([null])).toBe(null); + expect(extractLLMFromParams([{}])).toBe(null); + expect(extractLLMFromParams([{ llm: false }])).toBe(null); + expect(extractLLMFromParams([{ llm: 123 }])).toBe(null); + expect(extractLLMFromParams([{ llm: {} }])).toBe(null); + }); + it('extracts llm object if found', () => { + expect(extractLLMFromParams([{ llm: { modelName: 'model-name-1' } }])).toStrictEqual({ modelName: 'model-name-1' }); + }); +}); + +describe('extractToolCalls', () => { + it('returns null for missing/empty messages', () => { + expect(extractToolCalls(null)).toBe(null); + expect(extractToolCalls([])).toBe(null); + expect(extractToolCalls([{}])).toBe(null); + expect(extractToolCalls([{ tool_calls: null }])).toBe(null); + expect(extractToolCalls([{ tool_calls: [] }])).toBe(null); + }); + it('extracts tool call from messages array', () => { + expect( + extractToolCalls([ + { tool_calls: [{ name: 'tool a' }] }, + { tool_calls: [{ name: 'tool b' }, { name: 'tool c' }] }, + ]), + ).toStrictEqual([{ name: 'tool a' }, { name: 'tool b' }, { name: 'tool c' }]); + }); +}); + +describe('extractTokenUsageFromMessage', () => { + it('extracts from usage_metadata', () => { + const inputs = [{}, { input_tokens: 10 }]; + const outputs = [{}, { output_tokens: 20 }]; + const totals = [{}, { total_tokens: 30 }]; + for (const i of inputs) { + for (const o of outputs) { + for (const t of totals) { + expect( + extractTokenUsageFromMessage({ + usage_metadata: { + ...i, + ...o, + ...t, + }, + }), + ).toStrictEqual({ + inputTokens: i?.input_tokens ?? 0, + outputTokens: o?.output_tokens ?? 0, + totalTokens: t?.total_tokens ?? 0, + }); + } + } + } + }); + it('falls back to response_metadata', () => { + const inputs = [{}, { promptTokens: 10 }]; + const outputs = [{}, { completionTokens: 20 }]; + const totals = [{}, { totalTokens: 30 }]; + for (const i of inputs) { + for (const o of outputs) { + for (const t of totals) { + expect( + extractTokenUsageFromMessage({ + response_metadata: { + // @ts-expect-error using old tokenUsage field + tokenUsage: { + ...i, + ...o, + ...t, + }, + }, + }), + ).toStrictEqual({ + inputTokens: i?.promptTokens ?? 0, + outputTokens: o?.completionTokens ?? 0, + totalTokens: t?.totalTokens ?? 0, + }); + } + } + } + }); +}); + +describe('extractModelMetadata', () => { + let attributes: Record = {}; + const span = { + setAttribute(key: string, value: unknown) { + attributes[key] = value; + }, + } as unknown as Span; + beforeEach(() => (attributes = {})); + + it('handles lacking metadata ok', () => { + extractModelMetadata(span, {}); + expect(attributes).toStrictEqual({}); + }); + + it('extracts response model name from metadata', () => { + extractModelMetadata(span, { + response_metadata: { + model_name: 'model-name', + finish_reason: 'stop', + }, + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.model': 'model-name', + 'gen_ai.response.finish_reasons': ['stop'], + }); + }); +}); + +describe('extractToolsFromCompiledGraph', () => { + it('returns null if no tools found', () => { + expect(extractToolsFromCompiledGraph({})).toBe(null); + expect( + extractToolsFromCompiledGraph({ + builder: { nodes: { tools: { runnable: {} } } }, + }), + ).toBe(null); + expect( + extractToolsFromCompiledGraph({ + // @ts-expect-error Wants LangGraphTool[] + builder: { nodes: { tools: { runnable: { tools: 'not an array' } } } }, + }), + ).toBe(null); + expect( + extractToolsFromCompiledGraph({ + builder: { nodes: { tools: { runnable: { tools: [] } } } }, + }), + ).toBe(null); + }); + it('returns the tools found', () => { + expect( + extractToolsFromCompiledGraph({ + builder: { + nodes: { + tools: { + runnable: { + tools: [ + {}, + { lc_kwargs: { name: 'name' } }, + { lc_kwargs: { name: 'name', description: 'desc' } }, + { lc_kwargs: { name: 'name', description: 'desc', schema: 'schema' } }, + ], + }, + }, + }, + }, + }), + ).toStrictEqual([ + { name: undefined, description: undefined, schema: undefined }, + { name: 'name', description: undefined, schema: undefined }, + { name: 'name', description: 'desc', schema: undefined }, + { name: 'name', description: 'desc', schema: 'schema' }, + ]); + }); +}); + +describe('setResponseAttribute', () => { + let attributes: Record = {}; + const span = { + setAttribute(key: string, value: unknown) { + attributes[key] = value; + }, + } as unknown as Span; + beforeEach(() => (attributes = {})); + + it('handles lack of messages', () => { + setResponseAttributes(span, [], undefined); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, undefined); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, {}); + expect(attributes).toStrictEqual({}); + + setResponseAttributes(span, null, { messages: null }); + expect(attributes).toStrictEqual({}); + + // no new messages + setResponseAttributes(span, [{}], { messages: [{}] }); + expect(attributes).toStrictEqual({}); + setResponseAttributes(span, [], { messages: [] }); + expect(attributes).toStrictEqual({}); + + // @ts-expect-error cover excessive type safety case + setResponseAttributes(span, { length: undefined }, []); + expect(attributes).toStrictEqual({}); + }); + + it('extracts tool calls', () => { + setResponseAttributes(span, [], { + messages: [{ tool_calls: [{ name: 'tool a' }] }], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.response.tool_calls': JSON.stringify([{ name: 'tool a' }]), + }); + }); + + it('extracts token usage', () => { + setResponseAttributes(span, [], { + messages: [ + { + usage_metadata: { + input_tokens: 1, + output_tokens: 2, + total_tokens: 3, + }, + }, + ], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.usage.input_tokens': 1, + 'gen_ai.usage.output_tokens': 2, + 'gen_ai.usage.total_tokens': 3, + }); + }); + + it('extracts model metadata', () => { + setResponseAttributes(span, [], { + messages: [ + { + response_metadata: { + model_name: 'model-name-1', + finish_reason: 'stop', + }, + }, + ], + }); + expect(attributes).toStrictEqual({ + 'gen_ai.response.text': '[{"role":"user"}]', + 'gen_ai.response.model': 'model-name-1', + 'gen_ai.response.finish_reasons': ['stop'], + }); + }); +}); From ffe34fb6a27d61017d6d5d2975bf0da2c6f893bb Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 22 Dec 2025 09:45:48 -0800 Subject: [PATCH 17/27] fix(tracing): patch @langchain/langgraph/prebuilt in ESM mode This patches the `@langchain/langgraph/prebuilt` submodule export, when loading in ESM mode. Requires an update to `@opentelemetry/instrumentation`, in order to not block the patching of ESM submodule exports. depends-on: https://github.com/open-telemetry/opentelemetry-js/pull/6246 --- .../tracing/langgraph/instrumentation.ts | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index 9cfa98bb2932..b3f897e70363 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -52,6 +52,18 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, ), + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/prebuilt/index.cjs', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), ], ), new InstrumentationNodeModuleDefinition( @@ -61,7 +73,24 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, [ new InstrumentationNodeModuleFile( - '@langchain/langgraph/dist/prebuilt/index.js', + /** + * ESM builds use dist/prebuilt/index.js (without .cjs extension) + * This catches ESM imports that resolve through the main package, + * using the package.json submodule export + */ + '@langchain/langgraph/prebuilt', + supportedVersions, + this._patch.bind(this), + exports => exports, + ), + new InstrumentationNodeModuleFile( + /** + * In CJS, LangGraph packages re-export from dist/prebuilt/index.cjs files. + * Patching only the root module sometimes misses the real implementation or + * gets overwritten when that file is loaded. We add a file-level patch so that + * _patch runs again on the concrete implementation + */ + '@langchain/langgraph/dist/prebuilt/index.cjs', supportedVersions, this._patch.bind(this), exports => exports, @@ -75,7 +104,6 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase; }; @@ -103,7 +130,6 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase CompiledGraph, options), From 180e987726407f6ac98f26fd8c3b375345795750 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 23 Dec 2025 11:01:47 +0100 Subject: [PATCH 18/27] iitm 2.0.0 --- package.json | 3 +-- packages/node-core/package.json | 2 +- packages/node/package.json | 2 +- yarn.lock | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 25bfcbba603d..b0f32aa93a50 100644 --- a/package.json +++ b/package.json @@ -150,8 +150,7 @@ "gauge/strip-ansi": "6.0.1", "wide-align/string-width": "4.2.3", "cliui/wrap-ansi": "7.0.0", - "sucrase": "getsentry/sucrase#es2020-polyfills", - "import-in-the-middle": "^2.0.1" + "sucrase": "getsentry/sucrase#es2020-polyfills" }, "version": "0.0.0", "name": "sentry-javascript", diff --git a/packages/node-core/package.json b/packages/node-core/package.json index ad99efc5fcba..5308dd52ae48 100644 --- a/packages/node-core/package.json +++ b/packages/node-core/package.json @@ -69,7 +69,7 @@ "@apm-js-collab/tracing-hooks": "^0.3.1", "@sentry/core": "10.32.1", "@sentry/opentelemetry": "10.32.1", - "import-in-the-middle": "^2.0.1" + "import-in-the-middle": "^2" }, "devDependencies": { "@apm-js-collab/code-transformer": "^0.8.2", diff --git a/packages/node/package.json b/packages/node/package.json index bd312657e92f..7a4926837e52 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -98,7 +98,7 @@ "@sentry/core": "10.32.1", "@sentry/node-core": "10.32.1", "@sentry/opentelemetry": "10.32.1", - "import-in-the-middle": "^2.0.1", + "import-in-the-middle": "^2", "minimatch": "^9.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 74c50e69e987..a23e2252a810 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19238,7 +19238,7 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^2.0.0, import-in-the-middle@^2.0.1: +import-in-the-middle@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.1.tgz#8d1aa2db18374f2c811de2aa4756ebd6e9859243" integrity sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA== From 7cf879633361bab8424a599aa26430c9f011666c Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 23 Dec 2025 11:05:45 +0100 Subject: [PATCH 19/27] revert yarn.lock --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a23e2252a810..82a6d2eaa541 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19238,10 +19238,10 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^2.0.0: +import-in-the-middle@^2, import-in-the-middle@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.1.tgz#8d1aa2db18374f2c811de2aa4756ebd6e9859243" - integrity sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA== + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz#295948cee94d0565314824c6bd75379d13e5b1a5" + integrity sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A== dependencies: acorn "^8.14.0" acorn-import-attributes "^1.9.5" From 957b6ed5d544feade4d7f3f95716da7c5eba4fdd Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 23 Dec 2025 11:06:10 +0100 Subject: [PATCH 20/27] revert yarn.lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 82a6d2eaa541..eaceb4f6e0da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19239,7 +19239,7 @@ import-fresh@^3.2.1: resolve-from "^4.0.0" import-in-the-middle@^2, import-in-the-middle@^2.0.0: - version "2.0.1" + version "2.0.0" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz#295948cee94d0565314824c6bd75379d13e5b1a5" integrity sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A== dependencies: From df9c9d2ceb5063482086c85fcb328885c8c3e48f Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 16 Feb 2026 12:01:35 -0800 Subject: [PATCH 21/27] chore: update @opentelemetry dependencies --- .../package.json | 12 +-- .../package.json | 16 ++-- .../node-core-express-otel-v2/package.json | 12 +-- .../node-core-integration-tests/package.json | 12 +-- packages/aws-serverless/package.json | 2 +- packages/nestjs/package.json | 4 +- packages/node-core/package.json | 10 +-- packages/node/package.json | 12 +-- packages/opentelemetry/package.json | 6 +- packages/react-router/package.json | 4 +- packages/remix/package.json | 2 +- packages/vercel-edge/package.json | 6 +- yarn.lock | 79 +++++++++++-------- 13 files changed, 96 insertions(+), 81 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json index 68e8f7ac1f24..88bc1dd1f844 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-custom-sampler/package.json @@ -12,12 +12,12 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-node": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json index b79d084997bf..2c9e8917755f 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2-sdk-node/package.json @@ -12,15 +12,15 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-node": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", - "@opentelemetry/sdk-node": "^0.211.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.211.0", + "@opentelemetry/sdk-node": "^0.212.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.212.0", "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@types/express": "4.17.17", diff --git a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json index 7da50bef037a..210ed53d2733 100644 --- a/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json +++ b/dev-packages/e2e-tests/test-applications/node-core-express-otel-v2/package.json @@ -14,12 +14,12 @@ "@sentry/node-core": "latest || *", "@sentry/opentelemetry": "latest || *", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "^0.211.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-node": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "^0.212.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-node": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@types/express": "^4.17.21", "@types/node": "^18.19.1", diff --git a/dev-packages/node-core-integration-tests/package.json b/dev-packages/node-core-integration-tests/package.json index 79b879519dba..d0b5ef9231ad 100644 --- a/dev-packages/node-core-integration-tests/package.json +++ b/dev-packages/node-core-integration-tests/package.json @@ -27,12 +27,12 @@ "@nestjs/core": "^11", "@nestjs/platform-express": "^11", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-http": "0.211.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/instrumentation-http": "0.212.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/core": "10.38.0", "@sentry/node-core": "10.38.0", diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index bc8ede780edc..bf19be346a38 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -66,7 +66,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-aws-sdk": "0.66.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/core": "10.38.0", diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 33a57ae165a3..d42b78e98d9a 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -45,8 +45,8 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-nestjs-core": "0.57.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/core": "10.38.0", diff --git a/packages/node-core/package.json b/packages/node-core/package.json index 6727558470a8..f9a331b4e48e 100644 --- a/packages/node-core/package.json +++ b/packages/node-core/package.json @@ -74,11 +74,11 @@ "devDependencies": { "@apm-js-collab/code-transformer": "^0.8.2", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@types/node": "^18.19.1" }, diff --git a/packages/node/package.json b/packages/node/package.json index 376170b88234..5c8d51189683 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -66,9 +66,9 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/instrumentation-amqplib": "0.58.0", "@opentelemetry/instrumentation-connect": "0.54.0", "@opentelemetry/instrumentation-dataloader": "0.28.0", @@ -77,7 +77,7 @@ "@opentelemetry/instrumentation-generic-pool": "0.54.0", "@opentelemetry/instrumentation-graphql": "0.58.0", "@opentelemetry/instrumentation-hapi": "0.57.0", - "@opentelemetry/instrumentation-http": "0.211.0", + "@opentelemetry/instrumentation-http": "0.212.0", "@opentelemetry/instrumentation-ioredis": "0.59.0", "@opentelemetry/instrumentation-kafkajs": "0.20.0", "@opentelemetry/instrumentation-knex": "0.55.0", @@ -91,8 +91,8 @@ "@opentelemetry/instrumentation-redis": "0.59.0", "@opentelemetry/instrumentation-tedious": "0.30.0", "@opentelemetry/instrumentation-undici": "0.21.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/resources": "^2.5.1", + "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@prisma/instrumentation": "7.2.0", "@sentry/core": "10.38.0", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 0f8f52498747..55775c5acc3c 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -50,9 +50,9 @@ }, "devDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/context-async-hooks": "^2.5.1", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0" }, "scripts": { diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 7ab6dd30b03b..854f46d7eec1 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/browser": "10.38.0", "@sentry/cli": "^2.58.4", diff --git a/packages/remix/package.json b/packages/remix/package.json index f964c4026cc8..8d9111dd4558 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -65,7 +65,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@remix-run/router": "^1.23.2", "@sentry/cli": "^2.58.2", diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 7e1392e6d17e..a1cba949361e 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -40,13 +40,13 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/resources": "^2.5.0", + "@opentelemetry/resources": "^2.5.1", "@sentry/core": "10.38.0" }, "devDependencies": { "@edge-runtime/types": "4.0.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/core": "^2.5.1", + "@opentelemetry/sdk-trace-base": "^2.5.1", "@opentelemetry/semantic-conventions": "^1.39.0", "@sentry/opentelemetry": "10.38.0" }, diff --git a/yarn.lock b/yarn.lock index f03469ec13ce..312af586d296 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6027,20 +6027,27 @@ dependencies: "@opentelemetry/api" "^1.3.0" +"@opentelemetry/api-logs@0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.212.0.tgz#ec66a0951b84b1f082e13fd8a027b9f9d65a3f7a" + integrity sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg== + dependencies: + "@opentelemetry/api" "^1.3.0" + "@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/context-async-hooks@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.0.tgz#0e6bf31f0dbdd159731f7dbcd266d20f028a6915" - integrity sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw== +"@opentelemetry/context-async-hooks@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.1.tgz#457b8f9c1e219bf6e22b549d90f773db0a38fe06" + integrity sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw== -"@opentelemetry/core@2.5.0", "@opentelemetry/core@^2.0.0", "@opentelemetry/core@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.5.0.tgz#3b2ac6cf471ed9a85eea836048a4de77a2e549d3" - integrity sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ== +"@opentelemetry/core@2.5.1", "@opentelemetry/core@^2.0.0", "@opentelemetry/core@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.5.1.tgz#b5d830ab499bc13e29f6efa88a165630f25d2ad2" + integrity sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA== dependencies: "@opentelemetry/semantic-conventions" "^1.29.0" @@ -6119,13 +6126,13 @@ "@opentelemetry/instrumentation" "^0.211.0" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.211.0": - version "0.211.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz#2f12f83f0c21d37917fd9710fb5b755f28858cf6" - integrity sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA== +"@opentelemetry/instrumentation-http@0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.212.0.tgz#e9b08e500afb6f8feb0ffeeadf81cf8af77e9457" + integrity sha512-t2nt16Uyv9irgR+tqnX96YeToOStc3X5js7Ljn3EKlI2b4Fe76VhMkTXtsTQ0aId6AsYgefrCRnXSCo/Fn/vww== dependencies: - "@opentelemetry/core" "2.5.0" - "@opentelemetry/instrumentation" "0.211.0" + "@opentelemetry/core" "2.5.1" + "@opentelemetry/instrumentation" "0.212.0" "@opentelemetry/semantic-conventions" "^1.29.0" forwarded-parse "2.1.2" @@ -6252,13 +6259,13 @@ "@opentelemetry/instrumentation" "^0.211.0" "@opentelemetry/semantic-conventions" "^1.24.0" -"@opentelemetry/instrumentation@0.211.0", "@opentelemetry/instrumentation@^0.211.0": - version "0.211.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz#d45e20eafa75b5d3e8a9745a6205332893c55f37" - integrity sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q== +"@opentelemetry/instrumentation@0.212.0", "@opentelemetry/instrumentation@^0.212.0": + version "0.212.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.212.0.tgz#238b6e3e2131217ff4acfe7e8e7b6ce1f0ac0ba0" + integrity sha512-IyXmpNnifNouMOe0I/gX7ENfv2ZCNdYTF0FpCsoBcpbIHzk81Ww9rQTYTnvghszCg7qGrIhNvWC8dhEifgX9Jg== dependencies: - "@opentelemetry/api-logs" "0.211.0" - import-in-the-middle "^2.0.0" + "@opentelemetry/api-logs" "0.212.0" + import-in-the-middle "^2.0.6" require-in-the-middle "^8.0.0" "@opentelemetry/instrumentation@^0.207.0": @@ -6270,26 +6277,35 @@ import-in-the-middle "^2.0.0" require-in-the-middle "^8.0.0" +"@opentelemetry/instrumentation@^0.211.0": + version "0.211.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz#d45e20eafa75b5d3e8a9745a6205332893c55f37" + integrity sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q== + dependencies: + "@opentelemetry/api-logs" "0.211.0" + import-in-the-middle "^2.0.0" + require-in-the-middle "^8.0.0" + "@opentelemetry/redis-common@^0.38.2": version "0.38.2" resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz#cefa4f3e79db1cd54f19e233b7dfb56621143955" integrity sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA== -"@opentelemetry/resources@2.5.0", "@opentelemetry/resources@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.5.0.tgz#e7a575b2c534961a9db5153f9498931c786a607a" - integrity sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g== +"@opentelemetry/resources@2.5.1", "@opentelemetry/resources@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.5.1.tgz#90ccc27cea02b543f20a7db9834852ec11784c1a" + integrity sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ== dependencies: - "@opentelemetry/core" "2.5.0" + "@opentelemetry/core" "2.5.1" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/sdk-trace-base@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz#4b96ae2494a4de5e3bfb36ef7459b30a1ce3332a" - integrity sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ== +"@opentelemetry/sdk-trace-base@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.1.tgz#4f55f37e18ac3f971936d4717b6bfd43cfd72d61" + integrity sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw== dependencies: - "@opentelemetry/core" "2.5.0" - "@opentelemetry/resources" "2.5.0" + "@opentelemetry/core" "2.5.1" + "@opentelemetry/resources" "2.5.1" "@opentelemetry/semantic-conventions" "^1.29.0" "@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.37.0", "@opentelemetry/semantic-conventions@^1.39.0": @@ -29076,7 +29092,6 @@ stylus@0.59.0, stylus@^0.59.0: sucrase@^3.27.0, sucrase@^3.35.0, sucrase@getsentry/sucrase#es2020-polyfills: version "3.36.0" - uid fd682f6129e507c00bb4e6319cc5d6b767e36061 resolved "https://codeload.github.com/getsentry/sucrase/tar.gz/fd682f6129e507c00bb4e6319cc5d6b767e36061" dependencies: "@jridgewell/gen-mapping" "^0.3.2" From 8b05eaa8a85bec4b8fc41b0ae84cdacd8d3e6173 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 16 Feb 2026 14:13:58 -0800 Subject: [PATCH 22/27] fix opentelemetry instrumentation in node unit test --- packages/node/test/sdk/client.test.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/node/test/sdk/client.test.ts b/packages/node/test/sdk/client.test.ts index ff58698a7931..513e61b73024 100644 --- a/packages/node/test/sdk/client.test.ts +++ b/packages/node/test/sdk/client.test.ts @@ -1,5 +1,5 @@ import { ProxyTracer } from '@opentelemetry/api'; -import * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumentation'; +import type * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumentation'; import type { Event, EventHint, Log } from '@sentry/core'; import { getCurrentScope, getGlobalScope, getIsolationScope, Scope, SDK_VERSION } from '@sentry/core'; import { setOpenTelemetryContextAsyncContextStrategy } from '@sentry/opentelemetry'; @@ -285,18 +285,22 @@ describe('NodeClient', () => { }); it('registers instrumentations provided with `openTelemetryInstrumentations`', () => { - const registerInstrumentationsSpy = vi - .spyOn(opentelemetryInstrumentationPackage, 'registerInstrumentations') - .mockImplementationOnce(() => () => undefined); - const instrumentationsArray = ['foobar'] as unknown as opentelemetryInstrumentationPackage.Instrumentation[]; + const mockInstrumentation = { + setTracerProvider: vi.fn(), + setMeterProvider: vi.fn(), + enable: vi.fn(), + disable: vi.fn(), + getConfig: vi.fn(() => ({})), + setConfig: vi.fn(), + getModuleDefinitions: vi.fn(() => []), + } as unknown as opentelemetryInstrumentationPackage.Instrumentation; + + const instrumentationsArray = [mockInstrumentation]; new NodeClient(getDefaultNodeClientOptions({ openTelemetryInstrumentations: instrumentationsArray })); - expect(registerInstrumentationsSpy).toHaveBeenCalledWith( - expect.objectContaining({ - instrumentations: instrumentationsArray, - }), - ); + // Verify that the instrumentation was registered by checking if its methods were called + expect(mockInstrumentation.setTracerProvider).toHaveBeenCalled(); }); describe('log capture', () => { From 121bd49f15e4f9de837b9f48619c9664b13972b6 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 17 Feb 2026 09:21:18 -0800 Subject: [PATCH 23/27] fix lint --- packages/node/test/sdk/client.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node/test/sdk/client.test.ts b/packages/node/test/sdk/client.test.ts index 513e61b73024..9a6029826567 100644 --- a/packages/node/test/sdk/client.test.ts +++ b/packages/node/test/sdk/client.test.ts @@ -300,6 +300,7 @@ describe('NodeClient', () => { new NodeClient(getDefaultNodeClientOptions({ openTelemetryInstrumentations: instrumentationsArray })); // Verify that the instrumentation was registered by checking if its methods were called + /* eslint-disable-next-line @typescript-eslint/unbound-method */ expect(mockInstrumentation.setTracerProvider).toHaveBeenCalled(); }); From bf435e686da7cc233044d1a625bd7e595e539d90 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 17 Feb 2026 10:49:39 -0800 Subject: [PATCH 24/27] fix: load langgraph/prebuilt ESM file properly --- .../node/src/integrations/tracing/langgraph/instrumentation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts index b3f897e70363..85244aa9bc6c 100644 --- a/packages/node/src/integrations/tracing/langgraph/instrumentation.ts +++ b/packages/node/src/integrations/tracing/langgraph/instrumentation.ts @@ -78,7 +78,7 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase exports, From 7be8df2c537887bb2b3a9482eba24360ff3e1393 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 17 Feb 2026 11:47:51 -0800 Subject: [PATCH 25/27] fix flaky langgraph test, exclude Express spans --- .../suites/tracing/langgraph/instrument-with-pii.mjs | 7 +++++++ .../suites/tracing/langgraph/instrument.mjs | 7 +++++++ 2 files changed, 14 insertions(+) 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 be512ed2f773..cb68a6f7683e 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,4 +7,11 @@ Sentry.init({ tracesSampleRate: 1.0, sendDefaultPii: true, transport: loggingTransport, + beforeSendTransaction: event => { + // Filter out mock express server transactions + if (event.transaction.includes('/v1/messages')) { + return null; + } + return event; + }, }); 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 06cc1a32e93e..b4ce44f3e91a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/instrument.mjs @@ -7,4 +7,11 @@ Sentry.init({ tracesSampleRate: 1.0, sendDefaultPii: false, transport: loggingTransport, + beforeSendTransaction: event => { + // Filter out mock express server transactions + if (event.transaction.includes('/v1/messages')) { + return null; + } + return event; + }, }); From 0920edfe136ba805e2a3fefc2b828b8982886d34 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 10:29:39 +0100 Subject: [PATCH 26/27] constants --- .../suites/tracing/langgraph/test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 a595d767d6d1..45b40fc0340a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -229,9 +229,9 @@ describe('LangGraph integration', () => { // create_agent span expect.objectContaining({ data: expect.objectContaining({ - 'gen_ai.operation.name': 'create_agent', - 'sentry.op': 'gen_ai.create_agent', - 'sentry.origin': 'auto.ai.langgraph', + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', }), description: expect.stringContaining('create_agent'), op: 'gen_ai.create_agent', @@ -241,9 +241,9 @@ describe('LangGraph integration', () => { // invoke_agent span expect.objectContaining({ data: expect.objectContaining({ - 'gen_ai.operation.name': 'invoke_agent', - 'sentry.op': 'gen_ai.invoke_agent', - 'sentry.origin': 'auto.ai.langgraph', + [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.invoke_agent', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langgraph', }), description: expect.stringContaining('invoke_agent'), op: 'gen_ai.invoke_agent', From 11a976f4c88b173abe92ec03bcfa172a7e7763df Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 18 Feb 2026 10:37:37 +0100 Subject: [PATCH 27/27] fix file formatting --- .../test-applications/astro-5-cf-workers/wrangler.jsonc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/wrangler.jsonc index 5ef4f1ff11f6..0b7b36047973 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/wrangler.jsonc +++ b/dev-packages/e2e-tests/test-applications/astro-5-cf-workers/wrangler.jsonc @@ -8,11 +8,10 @@ "SENTRY_DSN": "https://username@domain/123", "SENTRY_ENVIRONMENT": "qa", "SENTRY_TRACES_SAMPLE_RATE": "1.0", - "SENTRY_TUNNEL": "http://localhost:3031/" + "SENTRY_TUNNEL": "http://localhost:3031/", }, "assets": { "binding": "ASSETS", - "directory": "./dist" - } + "directory": "./dist", + }, } -