Skip to content

Commit 7a7d5b1

Browse files
committed
feat(browser): Expose langgraph instrumentation
1 parent 98854d4 commit 7a7d5b1

File tree

9 files changed

+90
-0
lines changed

9 files changed

+90
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
tracesSampleRate: 1,
8+
debug: true,
9+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Mock LangGraph graph for browser testing
2+
3+
export class MockStateGraph {
4+
compile(options = {}) {
5+
const compiledGraph = {
6+
name: options.name,
7+
graph_name: options.name,
8+
lc_kwargs: {
9+
name: options.name,
10+
},
11+
builder: {
12+
nodes: {},
13+
},
14+
invoke: async (input) => {
15+
const messages = input?.messages;
16+
return {
17+
messages: [
18+
...messages,
19+
{
20+
role: 'assistant',
21+
content: 'Mock response from LangGraph',
22+
},
23+
],
24+
};
25+
},
26+
};
27+
28+
return compiledGraph;
29+
}
30+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { MockStateGraph } from './mocks.js';
2+
import { instrumentLangGraph } from '@sentry/browser';
3+
4+
// Test that manual instrumentation doesn't crash the browser
5+
// The instrumentation automatically creates spans
6+
// Test both agent creation and invocation
7+
8+
const graph = new MockStateGraph();
9+
instrumentLangGraph(graph, { recordInputs: false, recordOutputs: false });
10+
const compiledGraph = graph.compile({ name: 'mock-graph' });
11+
12+
const response = await compiledGraph.invoke({
13+
messages: [{ role: 'user', content: 'What is the capital of France?' }],
14+
});
15+
16+
console.log('Received response', response);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../../utils/fixtures';
3+
import { envelopeRequestParser, waitForTransactionRequest } from '../../../../utils/helpers';
4+
5+
// These tests are not exhaustive because the instrumentation is
6+
// already tested in the node integration tests and we merely
7+
// want to test that the instrumentation does not crash in the browser
8+
// and that gen_ai transactions are sent.
9+
10+
sentryTest('manual LangGraph instrumentation sends gen_ai transactions', async ({ getLocalTestUrl, page }) => {
11+
const transactionPromise = waitForTransactionRequest(page, event => {
12+
return !!event.transaction?.includes('mock-graph');
13+
});
14+
15+
const url = await getLocalTestUrl({ testDir: __dirname });
16+
await page.goto(url);
17+
18+
const req = await transactionPromise;
19+
20+
const eventData = envelopeRequestParser(req);
21+
22+
// Verify it's a gen_ai transaction
23+
expect(eventData.transaction).toBe('create_agent mock-graph');
24+
expect(eventData.contexts?.trace?.op).toBe('gen_ai.create_agent');
25+
expect(eventData.contexts?.trace?.origin).toBe('auto.ai.langgraph');
26+
expect(eventData.contexts?.trace?.data).toMatchObject({
27+
'gen_ai.operation.name': 'create_agent',
28+
'gen_ai.agent.name': 'mock-graph',
29+
});
30+
});

dev-packages/browser-integration-tests/utils/generatePlugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record<string, string> = {
4040
instrumentAnthropicAiClient: 'instrumentanthropicaiclient',
4141
instrumentOpenAiClient: 'instrumentopenaiclient',
4242
instrumentGoogleGenAIClient: 'instrumentgooglegenaiclient',
43+
instrumentLangGraph: 'instrumentlanggraph',
4344
// technically, this is not an integration, but let's add it anyway for simplicity
4445
makeMultiplexedTransport: 'multiplexedtransport',
4546
};

packages/browser/rollup.bundle.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const reexportedPluggableIntegrationFiles = [
1616
'instrumentanthropicaiclient',
1717
'instrumentopenaiclient',
1818
'instrumentgooglegenaiclient',
19+
'instrumentlanggraph',
1920
];
2021

2122
browserPluggableIntegrationFiles.forEach(integrationName => {

packages/browser/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export {
6666
instrumentAnthropicAiClient,
6767
instrumentOpenAiClient,
6868
instrumentGoogleGenAIClient,
69+
instrumentLangGraph,
6970
logger,
7071
} from '@sentry/core';
7172
export type { Span, FeatureFlagsIntegration } from '@sentry/core';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { instrumentLangGraph } from '@sentry/core';

packages/browser/src/utils/lazyLoadIntegration.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const LazyLoadableIntegrations = {
2424
instrumentAnthropicAiClient: 'instrumentanthropicaiclient',
2525
instrumentOpenAiClient: 'instrumentopenaiclient',
2626
instrumentGoogleGenAIClient: 'instrumentgooglegenaiclient',
27+
instrumentLangGraph: 'instrumentlanggraph',
2728
} as const;
2829

2930
const WindowWithMaybeIntegration = WINDOW as {

0 commit comments

Comments
 (0)