Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'),
gzip: false,
brotli: false,
limit: '250 KB',
limit: '251 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
sendDefaultPii: false,
transport: loggingTransport,
integrations: [
Sentry.googleGenAIIntegration({
recordInputs: true,
recordOutputs: true,
enableTruncation: false,
}),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { instrumentGoogleGenAIClient } from '@sentry/core';
import * as Sentry from '@sentry/node';

class MockGoogleGenerativeAI {
constructor(config) {
this.apiKey = config.apiKey;
this.models = {
generateContent: this._generateContent.bind(this),
};
}

async _generateContent() {
await new Promise(resolve => setTimeout(resolve, 10));
return {
response: {
text: () => 'Response',
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 },
candidates: [
{
content: { parts: [{ text: 'Response' }], role: 'model' },
finishReason: 'STOP',
},
],
},
};
}
}

async function run() {
await Sentry.startSpan({ op: 'function', name: 'main' }, async () => {
const mockClient = new MockGoogleGenerativeAI({ apiKey: 'mock-api-key' });
const client = instrumentGoogleGenAIClient(mockClient, { enableTruncation: false, recordInputs: true });

// Long content that would normally be truncated
const longContent = 'A'.repeat(50_000);
await client.models.generateContent({
model: 'gemini-1.5-flash',
contents: [{ role: 'user', parts: [{ text: longContent }] }],
Comment thread
nicohrubec marked this conversation as resolved.
Outdated
});
});
}

run();
Original file line number Diff line number Diff line change
Expand Up @@ -653,4 +653,33 @@ describe('Google GenAI integration', () => {
.completed();
});
});

const longContent = 'A'.repeat(50_000);

const EXPECTED_TRANSACTION_NO_TRUNCATION = {
transaction: 'main',
spans: expect.arrayContaining([
expect.objectContaining({
data: expect.objectContaining({
[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: JSON.stringify([{ role: 'user', parts: [{ text: longContent }] }]),
[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: 1,
}),
}),
]),
};

createEsmAndCjsTests(
__dirname,
'scenario-no-truncation.mjs',
'instrument-no-truncation.mjs',
(createRunner, test) => {
test('does not truncate input messages when enableTruncation is false', async () => {
await createRunner()
.ignore('event')
.expect({ transaction: EXPECTED_TRANSACTION_NO_TRUNCATION })
.start()
.completed();
});
},
);
});
24 changes: 18 additions & 6 deletions packages/core/src/tracing/google-genai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ import {
GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,
GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE,
} from '../ai/gen-ai-attributes';
import { truncateGenAiMessages } from '../ai/messageTruncation';
import type { InstrumentedMethodEntry } from '../ai/utils';
import { buildMethodPath, extractSystemInstructions, resolveAIRecordingOptions } from '../ai/utils';
import {
buildMethodPath,
extractSystemInstructions,
getJsonString,
getTruncatedJsonString,
resolveAIRecordingOptions,
} from '../ai/utils';
import { GOOGLE_GENAI_METHOD_REGISTRY, GOOGLE_GENAI_SYSTEM_NAME } from './constants';
import { instrumentStream } from './streaming';
import type { Candidate, ContentPart, GoogleGenAIOptions, GoogleGenAIResponse } from './types';
Expand Down Expand Up @@ -134,7 +139,12 @@ function extractRequestAttributes(
* This is only recorded if recordInputs is true.
* Handles different parameter formats for different Google GenAI methods.
*/
function addPrivateRequestAttributes(span: Span, params: Record<string, unknown>, isEmbeddings: boolean): void {
function addPrivateRequestAttributes(
span: Span,
params: Record<string, unknown>,
isEmbeddings: boolean,
enableTruncation: boolean,
): void {
if (isEmbeddings) {
const contents = params.contents;
if (contents != null) {
Expand Down Expand Up @@ -184,7 +194,9 @@ function addPrivateRequestAttributes(span: Span, params: Record<string, unknown>
const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0;
span.setAttributes({
[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: filteredLength,
[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: JSON.stringify(truncateGenAiMessages(filteredMessages as unknown[])),
[GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: enableTruncation
? getTruncatedJsonString(filteredMessages)
: getJsonString(filteredMessages),
});
}
}
Expand Down Expand Up @@ -285,7 +297,7 @@ function instrumentMethod<T extends unknown[], R>(
async (span: Span) => {
try {
if (options.recordInputs && params) {
addPrivateRequestAttributes(span, params, isEmbeddings);
addPrivateRequestAttributes(span, params, isEmbeddings, options.enableTruncation ?? true);
}
const stream = await target.apply(context, args);
return instrumentStream(stream, span, Boolean(options.recordOutputs)) as R;
Expand Down Expand Up @@ -313,7 +325,7 @@ function instrumentMethod<T extends unknown[], R>(
},
(span: Span) => {
if (options.recordInputs && params) {
addPrivateRequestAttributes(span, params, isEmbeddings);
addPrivateRequestAttributes(span, params, isEmbeddings, options.enableTruncation ?? true);
}

return handleCallbackErrors(
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/tracing/google-genai/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export interface GoogleGenAIOptions {
* Enable or disable output recording.
*/
recordOutputs?: boolean;
/**
* Enable or disable truncation of recorded input messages.
* Defaults to `true`.
*/
enableTruncation?: boolean;
}

/**
Expand Down
Loading