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
@@ -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();
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,48 @@
});

createEsmAndCjsTests(__dirname, 'scenario-tools.mjs', 'instrument-with-pii.mjs', (createRunner, test) => {
test('should capture tools from LangGraph agent', { timeout: 30000 }, async () => {

Check failure on line 204 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (18) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should capture tools from LangGraph agent

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:204:5 ❯ utils/runner.ts:307:7

Check failure on line 204 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (22) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should capture tools from LangGraph agent

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:204:5 ❯ utils/runner.ts:307:7

Check failure on line 204 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (20) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should capture tools from LangGraph agent

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:204:5 ❯ utils/runner.ts:307:7

Check failure on line 204 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should capture tools from LangGraph agent

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:204:5 ❯ utils/runner.ts:307:7

Check failure on line 204 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should capture tools from LangGraph agent

Error: Test timed out in 30000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:204:5 ❯ utils/runner.ts:307:7
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 () => {

Check failure on line 240 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (18) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should instrument LangGraph createReactAgent with default PII settings

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:240:5 ❯ utils/runner.ts:307:7

Check failure on line 240 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (22) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should instrument LangGraph createReactAgent with default PII settings

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:240:5 ❯ utils/runner.ts:307:7

Check failure on line 240 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (20) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should instrument LangGraph createReactAgent with default PII settings

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:240:5 ❯ utils/runner.ts:307:7

Check failure on line 240 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should instrument LangGraph createReactAgent with default PII settings

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:240:5 ❯ utils/runner.ts:307:7

Check failure on line 240 in dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts

View workflow job for this annotation

GitHub Actions / Node (24) (TS 3.8) Integration Tests

suites/tracing/langgraph/test.ts > LangGraph integration > esm/cjs > cjs > should instrument LangGraph createReactAgent with default PII settings

Error: Test timed out in 15000ms. If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout". ❯ suites/tracing/langgraph/test.ts:240:5 ❯ utils/runner.ts:307:7
await createRunner()
.ignore('event')
.expect({ transaction: EXPECTED_TRANSACTION_REACT_AGENT })
.start()
.completed();
});
});
});
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
45 changes: 45 additions & 0 deletions packages/core/src/tracing/langgraph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,51 @@ function instrumentCompiledGraphInvoke(
}) as (...args: unknown[]) => Promise<unknown>;
}

/**
* 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<string, unknown>) : {};
const originalInvoke = compiledGraph.invoke;
if (originalInvoke && typeof originalInvoke === 'function') {
compiledGraph.invoke = instrumentCompiledGraphInvoke(
originalInvoke.bind(compiledGraph) as (...args: unknown[]) => Promise<unknown>,
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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand Down Expand Up @@ -50,6 +50,16 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase<LangGrap
this._patch.bind(this),
exports => 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;
Expand Down Expand Up @@ -83,6 +93,26 @@ export class SentryLangGraphInstrumentation extends InstrumentationBase<LangGrap
);
}

// Patch createReactAgent to instrument the agent creation and invocation
if (exports.createReactAgent && typeof exports.createReactAgent === 'function') {
exports.createReactAgent = instrumentCreateReactAgent(
exports.createReactAgent as (...args: unknown[]) => 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;
}
}
Loading