diff --git a/dev-packages/node-integration-tests/suites/tracing/langchain/test.ts b/dev-packages/node-integration-tests/suites/tracing/langchain/test.ts index e1ead1a89fe2..513537ad9a26 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langchain/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langchain/test.ts @@ -33,7 +33,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => { test('creates langchain related spans with genAI recording disabled', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -90,7 +89,6 @@ describe('LangChain integration', () => { test('does not create duplicate spans from double module patching', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -111,7 +109,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-with-pii.mjs', (createRunner, test) => { test('creates langchain related spans with genAI recording enabled', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -170,7 +167,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario-tools.mjs', 'instrument.mjs', (createRunner, test) => { test('creates langchain spans with tool calls', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -201,7 +197,6 @@ describe('LangChain integration', () => { createEsmTests(__dirname, 'scenario-openai-before-langchain.mjs', 'instrument.mjs', (createRunner, test) => { test('demonstrates timing issue with duplicate spans', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -236,7 +231,6 @@ describe('LangChain integration', () => { (createRunner, test) => { test('extracts system instructions from messages', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -260,7 +254,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario-chain.mjs', 'instrument.mjs', (createRunner, test) => { test('uses runName for chain spans instead of unknown_chain', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -306,7 +299,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario-embeddings.mjs', 'instrument.mjs', (createRunner, test) => { test('creates embedding spans with genAI recording disabled', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -343,7 +335,6 @@ describe('LangChain integration', () => { test('does not create duplicate embedding spans from double module patching', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -362,7 +353,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario-embeddings.mjs', 'instrument-with-pii.mjs', (createRunner, test) => { test('creates embedding spans with genAI recording enabled', async () => { await createRunner() - .ignore('event') .expect({ transaction: { transaction: 'main' } }) .expect({ span: container => { @@ -403,7 +393,6 @@ describe('LangChain integration', () => { createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-span-streaming.mjs', (createRunner, test) => { test('creates langchain related spans with span streaming enabled', async () => { await createRunner() - .ignore('event') .expect({ span: container => { const sonnetSpan = container.items.find(span => span.name === 'chat claude-3-5-sonnet-20241022'); diff --git a/packages/server-utils/src/ai/langchain/embeddings.ts b/packages/server-utils/src/ai/langchain/embeddings.ts index 23ba6fe07f69..cf052e76a684 100644 --- a/packages/server-utils/src/ai/langchain/embeddings.ts +++ b/packages/server-utils/src/ai/langchain/embeddings.ts @@ -1,10 +1,4 @@ -import { - captureException, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - startSpan, - stringify, -} from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan, stringify } from '@sentry/core'; import type { SpanAttributeValue } from '@sentry/core'; import { GEN_AI_EMBEDDINGS_INPUT, @@ -99,12 +93,9 @@ export function instrumentEmbeddingMethod( return new Proxy(originalMethod, { apply(target, thisArg, args: unknown[]): Promise { return startSpan(_INTERNAL_getLangChainEmbeddingsSpanOptions(thisArg, args[0], options), () => { - return Reflect.apply(target, thisArg, args).then(undefined, error => { - captureException(error, { - mechanism: { handled: false, type: 'auto.ai.langchain' }, - }); - throw error; - }); + // On rejection `startSpan` marks the span failed and rethrows to the caller, so we don't + // record the error ourselves. + return Reflect.apply(target, thisArg, args); }); }, }); diff --git a/packages/server-utils/src/ai/langchain/index.ts b/packages/server-utils/src/ai/langchain/index.ts index 1f6ba8754f79..f7d0de9d4b61 100644 --- a/packages/server-utils/src/ai/langchain/index.ts +++ b/packages/server-utils/src/ai/langchain/index.ts @@ -1,6 +1,5 @@ /* eslint-disable max-lines */ import { - captureException, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, @@ -184,19 +183,14 @@ export function createLangChainCallbackHandler(options: LangChainOptions = {}): }, // LLM Error Handler - note: handleLLMError with capital LLM - handleLLMError(error: Error, runId: string) { + handleLLMError(_error: Error, runId: string) { + // The error is surfaced to the caller (invoke() rejects), so we only mark the span failed and + // do not record it. const span = spanMap.get(runId); if (span?.isRecording()) { span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); exitSpan(runId); } - - captureException(error, { - mechanism: { - handled: false, - type: `${LANGCHAIN_ORIGIN}.llm_error_handler`, - }, - }); }, // Chain Start Handler @@ -258,19 +252,14 @@ export function createLangChainCallbackHandler(options: LangChainOptions = {}): }, // Chain Error Handler - handleChainError(error: Error, runId: string) { + handleChainError(_error: Error, runId: string) { + // The error is surfaced to the caller (invoke() rejects), so we only mark the span failed and + // do not record it. const span = spanMap.get(runId); if (span?.isRecording()) { span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); exitSpan(runId); } - - captureException(error, { - mechanism: { - handled: false, - type: `${LANGCHAIN_ORIGIN}.chain_error_handler`, - }, - }); }, // Tool Start Handler @@ -336,19 +325,14 @@ export function createLangChainCallbackHandler(options: LangChainOptions = {}): }, // Tool Error Handler - handleToolError(error: Error, runId: string) { + handleToolError(_error: Error, runId: string) { + // The error is surfaced to the caller (invoke() rejects), so we only mark the span failed and + // do not record it. const span = spanMap.get(runId); if (span?.isRecording()) { span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); exitSpan(runId); } - - captureException(error, { - mechanism: { - handled: false, - type: `${LANGCHAIN_ORIGIN}.tool_error_handler`, - }, - }); }, // LangChain BaseCallbackHandler required methods