diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs index 16eb0a6d74c6..5daa0a199d49 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/instrument-no-truncation.mjs @@ -14,5 +14,12 @@ Sentry.init({ enableTruncation: false, }), ], + beforeSendTransaction: event => { + // Filter out mock express server transactions + if (event.transaction.includes('/v1beta/')) { + return null; + } + return event; + }, streamGenAiSpans: true, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-message-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-message-truncation.mjs index e6146214d7fe..d3cb34648f4a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-message-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-message-truncation.mjs @@ -1,48 +1,40 @@ -import { instrumentGoogleGenAIClient } from '@sentry/core'; +import { GoogleGenAI } from '@google/genai'; import * as Sentry from '@sentry/node'; +import express from 'express'; -class MockGoogleGenerativeAI { - constructor(config) { - this.apiKey = config.apiKey; +function startMockGoogleGenAIServer() { + const app = express(); + app.use(express.json({ limit: '10mb' })); - this.models = { - generateContent: this._generateContent.bind(this), - }; - } - - async _generateContent() { - await new Promise(resolve => setTimeout(resolve, 10)); - - return { - response: { - text: () => 'Response to truncated messages', - usageMetadata: { - promptTokenCount: 10, - candidatesTokenCount: 15, - totalTokenCount: 25, + app.post('/v1beta/models/:model\\:generateContent', (req, res) => { + res.send({ + candidates: [ + { + content: { parts: [{ text: 'Response to truncated messages' }], role: 'model' }, + finishReason: 'stop', + index: 0, }, - candidates: [ - { - content: { - parts: [{ text: 'Response to truncated messages' }], - role: 'model', - }, - finishReason: 'STOP', - }, - ], - }, - }; - } + ], + usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 15, totalTokenCount: 25 }, + }); + }); + + return new Promise(resolve => { + const server = app.listen(0, () => { + resolve(server); + }); + }); } async function run() { + const server = await startMockGoogleGenAIServer(); + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { - const mockClient = new MockGoogleGenerativeAI({ + const client = new GoogleGenAI({ apiKey: 'mock-api-key', + httpOptions: { baseUrl: `http://localhost:${server.address().port}` }, }); - const client = instrumentGoogleGenAIClient(mockClient, { enableTruncation: true, recordInputs: true }); - // Test 1: Given an array of messages only the last message should be kept // The last message should be truncated to fit within the 20KB limit const largeContent1 = 'A'.repeat(15000); // ~15KB @@ -80,6 +72,10 @@ async function run() { ], }); }); + + await Sentry.flush(2000); + + server.close(); } run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-no-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-no-truncation.mjs index 13b271a23878..67ece6759577 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-no-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-no-truncation.mjs @@ -1,35 +1,39 @@ -import { instrumentGoogleGenAIClient } from '@sentry/core'; +import { GoogleGenAI } from '@google/genai'; import * as Sentry from '@sentry/node'; +import express from 'express'; -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', - }, - ], - }, - }; - } +function startMockGoogleGenAIServer() { + const app = express(); + app.use(express.json({ limit: '10mb' })); + + app.post('/v1beta/models/:model\\:generateContent', (req, res) => { + res.send({ + candidates: [ + { + content: { parts: [{ text: 'Response' }], role: 'model' }, + finishReason: 'stop', + index: 0, + }, + ], + usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 }, + }); + }); + + return new Promise(resolve => { + const server = app.listen(0, () => { + resolve(server); + }); + }); } async function run() { + const server = await startMockGoogleGenAIServer(); + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { - const mockClient = new MockGoogleGenerativeAI({ apiKey: 'mock-api-key' }); - const client = instrumentGoogleGenAIClient(mockClient, { enableTruncation: false, recordInputs: true }); + const client = new GoogleGenAI({ + apiKey: 'mock-api-key', + httpOptions: { baseUrl: `http://localhost:${server.address().port}` }, + }); // Long content that would normally be truncated const longContent = 'A'.repeat(50_000); @@ -42,6 +46,10 @@ async function run() { ], }); }); + + await Sentry.flush(2000); + + server.close(); } run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-system-instructions.mjs b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-system-instructions.mjs index d4081d052968..1a6f7d81d49e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-system-instructions.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/scenario-system-instructions.mjs @@ -1,41 +1,40 @@ -import { instrumentGoogleGenAIClient } from '@sentry/core'; +import { GoogleGenAI } from '@google/genai'; import * as Sentry from '@sentry/node'; +import express from 'express'; -class MockGoogleGenAI { - constructor(config) { - this.apiKey = config.apiKey; - this.models = { - generateContent: async params => { - await new Promise(resolve => setTimeout(resolve, 10)); - return { - response: { - text: () => 'Response', - modelVersion: params.model, - usageMetadata: { - promptTokenCount: 10, - candidatesTokenCount: 5, - totalTokenCount: 15, - }, - candidates: [ - { - content: { - parts: [{ text: 'Response' }], - role: 'model', - }, - finishReason: 'STOP', - }, - ], - }, - }; - }, - }; - } +function startMockGoogleGenAIServer() { + const app = express(); + app.use(express.json()); + + app.post('/v1beta/models/:model\\:generateContent', (req, res) => { + res.send({ + candidates: [ + { + content: { parts: [{ text: 'Response' }], role: 'model' }, + finishReason: 'stop', + index: 0, + }, + ], + modelVersion: req.params.model, + usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5, totalTokenCount: 15 }, + }); + }); + + return new Promise(resolve => { + const server = app.listen(0, () => { + resolve(server); + }); + }); } async function run() { + const server = await startMockGoogleGenAIServer(); + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { - const mockClient = new MockGoogleGenAI({ apiKey: 'mock-api-key' }); - const client = instrumentGoogleGenAIClient(mockClient); + const client = new GoogleGenAI({ + apiKey: 'mock-api-key', + httpOptions: { baseUrl: `http://localhost:${server.address().port}` }, + }); await client.models.generateContent({ model: 'gemini-1.5-flash', @@ -45,6 +44,10 @@ async function run() { contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], }); }); + + await Sentry.flush(2000); + + server.close(); } run();