Skip to content

Commit 7284606

Browse files
authored
fix(core): Set conversation_id only on gen_ai spans (#20274)
We should only set the `conversation_id` for `gen_ai` spans. These are the only spans for which this attribute is relevant and setting it on other spans can lead to unnecessarily slow queries in the product. This works fine for all our AI integrations except Vercel. This is because the Vercel ai integration also registers a `spanStart` hook that transforms Vercel spans to sentry `gen_ai` spans. The conversation id integration fires before that happens, so we have to special case the Vercel ai spans here for this to work properly. Closes #20272
1 parent 5b3eb7e commit 7284606

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

.size-limit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ module.exports = [
234234
path: createCDNPath('bundle.min.js'),
235235
gzip: false,
236236
brotli: false,
237-
limit: '83 KB',
237+
limit: '83.5 KB',
238238
},
239239
{
240240
name: 'CDN Bundle (incl. Tracing) - uncompressed',

packages/core/src/integrations/conversationId.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { defineIntegration } from '../integration';
44
import { GEN_AI_CONVERSATION_ID_ATTRIBUTE } from '../semanticAttributes';
55
import type { IntegrationFn } from '../types-hoist/integration';
66
import type { Span } from '../types-hoist/span';
7+
import { spanToJSON } from '../utils/spanUtils';
78

89
const INTEGRATION_NAME = 'ConversationId';
910

@@ -18,6 +19,16 @@ const _conversationIdIntegration = (() => {
1819
const conversationId = scopeData.conversationId || isolationScopeData.conversationId;
1920

2021
if (conversationId) {
22+
const { op, data: attributes, description: name } = spanToJSON(span);
23+
24+
// Only apply conversation ID to gen_ai spans.
25+
// We also check for Vercel AI spans (ai.operationId attribute or ai.* span name)
26+
// because the Vercel AI integration sets the gen_ai.* op in its own spanStart handler
27+
// which fires after this, so the op is not yet available at this point.
28+
if (!op?.startsWith('gen_ai.') && !attributes['ai.operationId'] && !name?.startsWith('ai.')) {
29+
return;
30+
}
31+
2132
span.setAttribute(GEN_AI_CONVERSATION_ID_ATTRIBUTE, conversationId);
2233
}
2334
});

packages/core/test/lib/integrations/conversationId.test.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('ConversationId', () => {
2626
it('applies conversation ID from current scope to span', () => {
2727
getCurrentScope().setConversationId('conv_test_123');
2828

29-
startSpan({ name: 'test-span' }, span => {
29+
startSpan({ name: 'test-span', op: 'gen_ai.chat' }, span => {
3030
const spanJSON = spanToJSON(span);
3131
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBe('conv_test_123');
3232
});
@@ -35,7 +35,7 @@ describe('ConversationId', () => {
3535
it('applies conversation ID from isolation scope when current scope does not have one', () => {
3636
getIsolationScope().setConversationId('conv_isolation_456');
3737

38-
startSpan({ name: 'test-span' }, span => {
38+
startSpan({ name: 'test-span', op: 'gen_ai.chat' }, span => {
3939
const spanJSON = spanToJSON(span);
4040
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBe('conv_isolation_456');
4141
});
@@ -45,14 +45,14 @@ describe('ConversationId', () => {
4545
getCurrentScope().setConversationId('conv_current_789');
4646
getIsolationScope().setConversationId('conv_isolation_999');
4747

48-
startSpan({ name: 'test-span' }, span => {
48+
startSpan({ name: 'test-span', op: 'gen_ai.chat' }, span => {
4949
const spanJSON = spanToJSON(span);
5050
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBe('conv_current_789');
5151
});
5252
});
5353

5454
it('does not apply conversation ID when not set in scope', () => {
55-
startSpan({ name: 'test-span' }, span => {
55+
startSpan({ name: 'test-span', op: 'gen_ai.chat' }, span => {
5656
const spanJSON = spanToJSON(span);
5757
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBeUndefined();
5858
});
@@ -62,7 +62,7 @@ describe('ConversationId', () => {
6262
getCurrentScope().setConversationId('conv_test_123');
6363
getCurrentScope().setConversationId(null);
6464

65-
startSpan({ name: 'test-span' }, span => {
65+
startSpan({ name: 'test-span', op: 'gen_ai.chat' }, span => {
6666
const spanJSON = spanToJSON(span);
6767
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBeUndefined();
6868
});
@@ -71,8 +71,8 @@ describe('ConversationId', () => {
7171
it('applies conversation ID to nested spans', () => {
7272
getCurrentScope().setConversationId('conv_nested_abc');
7373

74-
startSpan({ name: 'parent-span' }, () => {
75-
startSpan({ name: 'child-span' }, childSpan => {
74+
startSpan({ name: 'parent-span', op: 'gen_ai.invoke_agent' }, () => {
75+
startSpan({ name: 'child-span', op: 'gen_ai.chat' }, childSpan => {
7676
const childJSON = spanToJSON(childSpan);
7777
expect(childJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBe('conv_nested_abc');
7878
});
@@ -85,6 +85,7 @@ describe('ConversationId', () => {
8585
startSpan(
8686
{
8787
name: 'test-span',
88+
op: 'gen_ai.chat',
8889
attributes: {
8990
[GEN_AI_CONVERSATION_ID_ATTRIBUTE]: 'conv_explicit',
9091
},
@@ -95,4 +96,13 @@ describe('ConversationId', () => {
9596
},
9697
);
9798
});
99+
100+
it('does not apply conversation ID to non-gen_ai spans', () => {
101+
getCurrentScope().setConversationId('conv_test_123');
102+
103+
startSpan({ name: 'db-query', op: 'db.query' }, span => {
104+
const spanJSON = spanToJSON(span);
105+
expect(spanJSON.data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]).toBeUndefined();
106+
});
107+
});
98108
});

0 commit comments

Comments
 (0)