Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand All @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand All @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand All @@ -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 => {
Expand Down Expand Up @@ -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');
Expand Down
17 changes: 4 additions & 13 deletions packages/server-utils/src/ai/langchain/embeddings.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -99,12 +93,9 @@ export function instrumentEmbeddingMethod(
return new Proxy(originalMethod, {
apply(target, thisArg, args: unknown[]): Promise<unknown> {
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);
});
},
});
Expand Down
34 changes: 9 additions & 25 deletions packages/server-utils/src/ai/langchain/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable max-lines */
import {
captureException,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SPAN_STATUS_ERROR,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading