Skip to content

Commit c8c81d0

Browse files
authored
feat(core): Automatically disable truncation when span streaming is enabled in Google GenAI integration (#20229)
When span streaming is enabled, the `enableTruncation` option now defaults to `false` unless the user has explicitly set it. Closes: #20223
1 parent 506d0bc commit c8c81d0

File tree

5 files changed

+136
-2
lines changed

5 files changed

+136
-2
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
sendDefaultPii: true,
9+
transport: loggingTransport,
10+
traceLifecycle: 'stream',
11+
integrations: [
12+
Sentry.googleGenAIIntegration({
13+
enableTruncation: true,
14+
}),
15+
],
16+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
sendDefaultPii: true,
9+
transport: loggingTransport,
10+
traceLifecycle: 'stream',
11+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { GoogleGenAI } from '@google/genai';
2+
import * as Sentry from '@sentry/node';
3+
import express from 'express';
4+
5+
function startMockGoogleGenAIServer() {
6+
const app = express();
7+
app.use(express.json({ limit: '10mb' }));
8+
9+
app.post('/v1beta/models/:model\\:generateContent', (req, res) => {
10+
res.json({
11+
candidates: [
12+
{
13+
content: { parts: [{ text: 'Response' }], role: 'model' },
14+
finishReason: 'STOP',
15+
},
16+
],
17+
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
18+
});
19+
});
20+
21+
return new Promise(resolve => {
22+
const server = app.listen(0, () => {
23+
resolve(server);
24+
});
25+
});
26+
}
27+
28+
async function run() {
29+
const server = await startMockGoogleGenAIServer();
30+
31+
await Sentry.startSpan({ op: 'function', name: 'main' }, async () => {
32+
const client = new GoogleGenAI({
33+
apiKey: 'mock-api-key',
34+
httpOptions: { baseUrl: `http://localhost:${server.address().port}` },
35+
});
36+
37+
// Long content that would normally be truncated
38+
const longContent = 'A'.repeat(50_000);
39+
await client.models.generateContent({
40+
model: 'gemini-1.5-flash',
41+
contents: [{ role: 'user', parts: [{ text: longContent }] }],
42+
});
43+
});
44+
45+
// Flush is required when span streaming is enabled to ensure streamed spans are sent before the process exits
46+
await Sentry.flush(2000);
47+
48+
server.close();
49+
}
50+
51+
run();

dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,4 +686,54 @@ describe('Google GenAI integration', () => {
686686
});
687687
},
688688
);
689+
690+
const streamingLongContent = 'A'.repeat(50_000);
691+
692+
createEsmAndCjsTests(__dirname, 'scenario-span-streaming.mjs', 'instrument-streaming.mjs', (createRunner, test) => {
693+
test('automatically disables truncation when span streaming is enabled', async () => {
694+
await createRunner()
695+
.expect({
696+
span: container => {
697+
const spans = container.items;
698+
699+
const chatSpan = spans.find(s =>
700+
s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.includes(streamingLongContent),
701+
);
702+
expect(chatSpan).toBeDefined();
703+
},
704+
})
705+
.start()
706+
.completed();
707+
});
708+
});
709+
710+
createEsmAndCjsTests(
711+
__dirname,
712+
'scenario-span-streaming.mjs',
713+
'instrument-streaming-with-truncation.mjs',
714+
(createRunner, test) => {
715+
test('respects explicit enableTruncation: true even when span streaming is enabled', async () => {
716+
await createRunner()
717+
.expect({
718+
span: container => {
719+
const spans = container.items;
720+
721+
// With explicit enableTruncation: true, content should be truncated despite streaming.
722+
// Find the chat span by matching the start of the truncated content (the 'A' repeated messages).
723+
const chatSpan = spans.find(s =>
724+
s.attributes?.[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]?.value?.startsWith(
725+
'[{"role":"user","parts":[{"text":"AAAA',
726+
),
727+
);
728+
expect(chatSpan).toBeDefined();
729+
expect(chatSpan!.attributes[GEN_AI_INPUT_MESSAGES_ATTRIBUTE].value.length).toBeLessThan(
730+
streamingLongContent.length,
731+
);
732+
},
733+
})
734+
.start()
735+
.completed();
736+
});
737+
},
738+
);
689739
});

packages/core/src/tracing/google-genai/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getJsonString,
3535
getTruncatedJsonString,
3636
resolveAIRecordingOptions,
37+
shouldEnableTruncation,
3738
} from '../ai/utils';
3839
import { GOOGLE_GENAI_METHOD_REGISTRY, GOOGLE_GENAI_SYSTEM_NAME } from './constants';
3940
import { instrumentStream } from './streaming';
@@ -297,7 +298,12 @@ function instrumentMethod<T extends unknown[], R>(
297298
async (span: Span) => {
298299
try {
299300
if (options.recordInputs && params) {
300-
addPrivateRequestAttributes(span, params, isEmbeddings, options.enableTruncation ?? true);
301+
addPrivateRequestAttributes(
302+
span,
303+
params,
304+
isEmbeddings,
305+
shouldEnableTruncation(options.enableTruncation),
306+
);
301307
}
302308
const stream = await target.apply(context, args);
303309
return instrumentStream(stream, span, Boolean(options.recordOutputs)) as R;
@@ -325,7 +331,7 @@ function instrumentMethod<T extends unknown[], R>(
325331
},
326332
(span: Span) => {
327333
if (options.recordInputs && params) {
328-
addPrivateRequestAttributes(span, params, isEmbeddings, options.enableTruncation ?? true);
334+
addPrivateRequestAttributes(span, params, isEmbeddings, shouldEnableTruncation(options.enableTruncation));
329335
}
330336

331337
return handleCallbackErrors(

0 commit comments

Comments
 (0)