From 058cd328afec764b1cd61657527d3d483ced8311 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 11:06:02 +0100 Subject: [PATCH 01/11] remove unneeded types --- packages/core/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 09a4f36ebdb2..43e177e067bb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -164,11 +164,10 @@ export type { LangChainOptions, LangChainIntegration } from './tracing/langchain export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/langgraph'; export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; -export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; +export type { OpenAiClient, OpenAiOptions } from './tracing/openai/types'; export type { AnthropicAiClient, AnthropicAiOptions, - AnthropicAiInstrumentedMethod, AnthropicAiResponse, } from './tracing/anthropic-ai/types'; export type { From 9cbae01b2d42fbdefd767bfb87cfacd5156f5955 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 11:11:02 +0100 Subject: [PATCH 02/11] . --- packages/core/src/index.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 43e177e067bb..07552aacdf61 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -170,12 +170,7 @@ export type { AnthropicAiOptions, AnthropicAiResponse, } from './tracing/anthropic-ai/types'; -export type { - GoogleGenAIClient, - GoogleGenAIChat, - GoogleGenAIOptions, - GoogleGenAIIstrumentedMethod, -} from './tracing/google-genai/types'; +export type { GoogleGenAIClient, GoogleGenAIChat, GoogleGenAIOptions } from './tracing/google-genai/types'; export type { FeatureFlag } from './utils/featureFlags'; export { From 51668e23141da8c3bb16c1db59b13ac0e1d8035a Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 11:18:21 +0100 Subject: [PATCH 03/11] lint --- packages/core/src/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 07552aacdf61..628b0b29cfa3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -165,11 +165,7 @@ export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/lang export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; export type { OpenAiClient, OpenAiOptions } from './tracing/openai/types'; -export type { - AnthropicAiClient, - AnthropicAiOptions, - AnthropicAiResponse, -} from './tracing/anthropic-ai/types'; +export type { AnthropicAiClient, AnthropicAiOptions, AnthropicAiResponse } from './tracing/anthropic-ai/types'; export type { GoogleGenAIClient, GoogleGenAIChat, GoogleGenAIOptions } from './tracing/google-genai/types'; export type { FeatureFlag } from './utils/featureFlags'; From 8b9b69a2bb26ac609442c53a1a64da8e94a313d3 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 12:33:20 +0100 Subject: [PATCH 04/11] no chats create span --- .../suites/tracing/google-genai/test.ts | 97 +++---------------- packages/core/src/tracing/ai/utils.ts | 6 +- .../core/src/tracing/anthropic-ai/index.ts | 2 +- .../src/tracing/google-genai/constants.ts | 5 +- .../core/src/tracing/google-genai/index.ts | 50 ++++------ packages/core/src/tracing/openai/index.ts | 2 +- 6 files changed, 42 insertions(+), 120 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts b/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts index 91784a2de0e5..5d79cdf94202 100644 --- a/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/google-genai/test.ts @@ -32,24 +32,7 @@ describe('Google GenAI integration', () => { const EXPECTED_TRANSACTION_DEFAULT_PII_FALSE = { transaction: 'main', spans: expect.arrayContaining([ - // First span - chats.create - expect.objectContaining({ - data: { - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.chat', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.google_genai', - [GEN_AI_SYSTEM_ATTRIBUTE]: 'google_genai', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: 'gemini-1.5-pro', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.8, - [GEN_AI_REQUEST_TOP_P_ATTRIBUTE]: 0.9, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 150, - }, - description: 'chat gemini-1.5-pro create', - op: 'gen_ai.chat', - origin: 'auto.ai.google_genai', - status: 'ok', - }), - // Second span - chat.sendMessage (should get model from context) + // chat.sendMessage (should get model from context) expect.objectContaining({ data: { [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', @@ -66,7 +49,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Third span - models.generateContent + // models.generateContent expect.objectContaining({ data: { [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -86,7 +69,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Fourth span - error handling + // error handling expect.objectContaining({ data: { [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -106,25 +89,7 @@ describe('Google GenAI integration', () => { const EXPECTED_TRANSACTION_DEFAULT_PII_TRUE = { transaction: 'main', spans: expect.arrayContaining([ - // First span - chats.create with PII - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.chat', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.google_genai', - [GEN_AI_SYSTEM_ATTRIBUTE]: 'google_genai', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: 'gemini-1.5-pro', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.8, - [GEN_AI_REQUEST_TOP_P_ATTRIBUTE]: 0.9, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 150, - [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: '[{"role":"user","parts":[{"text":"Hello, how are you?"}]}]', - }), - description: 'chat gemini-1.5-pro create', - op: 'gen_ai.chat', - origin: 'auto.ai.google_genai', - status: 'ok', - }), - // Second span - chat.sendMessage with PII + // chat.sendMessage with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', @@ -143,7 +108,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Third span - models.generateContent with PII + // models.generateContent with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -165,7 +130,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Fourth span - error handling with PII + // error handling with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -309,7 +274,7 @@ describe('Google GenAI integration', () => { const EXPECTED_TRANSACTION_STREAMING = { transaction: 'main', spans: expect.arrayContaining([ - // First span - models.generateContentStream (streaming) + // models.generateContentStream (streaming) expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -333,24 +298,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Second span - chat.create - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.chat', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.google_genai', - [GEN_AI_SYSTEM_ATTRIBUTE]: 'google_genai', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: 'gemini-1.5-pro', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.8, - [GEN_AI_REQUEST_TOP_P_ATTRIBUTE]: 0.9, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 150, - }), - description: 'chat gemini-1.5-pro create', - op: 'gen_ai.chat', - origin: 'auto.ai.google_genai', - status: 'ok', - }), - // Third span - chat.sendMessageStream (streaming) + // chat.sendMessageStream (streaming) expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', @@ -367,7 +315,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Fourth span - blocked content streaming + // blocked content streaming expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -379,7 +327,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'internal_error', }), - // Fifth span - error handling for streaming + // error handling for streaming expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -397,7 +345,7 @@ describe('Google GenAI integration', () => { const EXPECTED_TRANSACTION_STREAMING_PII_TRUE = { transaction: 'main', spans: expect.arrayContaining([ - // First span - models.generateContentStream (streaming) with PII + // models.generateContentStream (streaming) with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -422,24 +370,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Second span - chat.create - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.chat', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.google_genai', - [GEN_AI_SYSTEM_ATTRIBUTE]: 'google_genai', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: 'gemini-1.5-pro', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.8, - [GEN_AI_REQUEST_TOP_P_ATTRIBUTE]: 0.9, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 150, - }), - description: 'chat gemini-1.5-pro create', - op: 'gen_ai.chat', - origin: 'auto.ai.google_genai', - status: 'ok', - }), - // Third span - chat.sendMessageStream (streaming) with PII + // chat.sendMessageStream (streaming) with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', @@ -461,7 +392,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'ok', }), - // Fourth span - blocked content stream with PII + // blocked content stream with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -478,7 +409,7 @@ describe('Google GenAI integration', () => { origin: 'auto.ai.google_genai', status: 'internal_error', }), - // Fifth span - error handling for streaming with PII + // error handling for streaming with PII expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', diff --git a/packages/core/src/tracing/ai/utils.ts b/packages/core/src/tracing/ai/utils.ts index d9628e3c75e2..372e0f4dde66 100644 --- a/packages/core/src/tracing/ai/utils.ts +++ b/packages/core/src/tracing/ai/utils.ts @@ -22,10 +22,12 @@ export interface AIRecordingOptions { * which gen_ai operation it maps to and whether it is intrinsically streaming. */ export interface InstrumentedMethodEntry { - /** Operation name (e.g. 'chat', 'embeddings', 'generate_content') */ - operation: string; + /** Operation name (e.g. 'chat', 'embeddings', 'generate_content'). Omit for factory methods that only need result proxying. */ + operation?: string; /** True if the method itself is always streaming (not param-based) */ streaming?: boolean; + /** When set, the method's return value is re-proxied with this as the base path */ + proxyResultPath?: string; } /** diff --git a/packages/core/src/tracing/anthropic-ai/index.ts b/packages/core/src/tracing/anthropic-ai/index.ts index 226aa8e1d544..24f3a8f2e8b2 100644 --- a/packages/core/src/tracing/anthropic-ai/index.ts +++ b/packages/core/src/tracing/anthropic-ai/index.ts @@ -265,7 +265,7 @@ function instrumentMethod( ): (...args: T) => R | Promise { return new Proxy(originalMethod, { apply(target, thisArg, args: T): R | Promise { - const operationName = instrumentedMethod.operation; + const operationName = instrumentedMethod.operation as string; const requestAttributes = extractRequestAttributes(args, methodPath, operationName); const model = requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] ?? 'unknown'; diff --git a/packages/core/src/tracing/google-genai/constants.ts b/packages/core/src/tracing/google-genai/constants.ts index b23f870f49b7..9c58055fb035 100644 --- a/packages/core/src/tracing/google-genai/constants.ts +++ b/packages/core/src/tracing/google-genai/constants.ts @@ -10,13 +10,10 @@ export const GOOGLE_GENAI_METHOD_REGISTRY = { 'models.generateContent': { operation: 'generate_content' }, 'models.generateContentStream': { operation: 'generate_content', streaming: true }, 'models.embedContent': { operation: 'embeddings' }, - 'chats.create': { operation: 'chat' }, - // chat.* paths are built by createDeepProxy when it proxies the chat instance with CHAT_PATH as base + 'chats.create': { proxyResultPath: 'chat' }, 'chat.sendMessage': { operation: 'chat' }, 'chat.sendMessageStream': { operation: 'chat', streaming: true }, } as const satisfies InstrumentedMethodRegistry; // Constants for internal use export const GOOGLE_GENAI_SYSTEM_NAME = 'google_genai'; -export const CHATS_CREATE_METHOD = 'chats.create'; -export const CHAT_PATH = 'chat'; diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index b754a8c874c6..26361ec745b1 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -30,7 +30,7 @@ import { import { truncateGenAiMessages } from '../ai/messageTruncation'; import type { InstrumentedMethodEntry } from '../ai/utils'; import { buildMethodPath, extractSystemInstructions, resolveAIRecordingOptions } from '../ai/utils'; -import { CHAT_PATH, CHATS_CREATE_METHOD, GOOGLE_GENAI_METHOD_REGISTRY, GOOGLE_GENAI_SYSTEM_NAME } from './constants'; +import { GOOGLE_GENAI_METHOD_REGISTRY, GOOGLE_GENAI_SYSTEM_NAME } from './constants'; import { instrumentStream } from './streaming'; import type { Candidate, ContentPart, GoogleGenAIOptions, GoogleGenAIResponse } from './types'; import type { ContentListUnion, ContentUnion, Message, PartListUnion } from './utils'; @@ -253,8 +253,8 @@ function addResponseAttributes(span: Span, response: GoogleGenAIResponse, record } /** - * Instrument any async or synchronous genai method with Sentry spans - * Handles operations like models.generateContent and chat.sendMessage and chats.create + * Instrument any async genai method with Sentry spans + * Handles operations like models.generateContent and chat.sendMessage * @see https://docs.sentry.io/platforms/javascript/guides/node/tracing/instrumentation/ai-agents-module/#manual-instrumentation */ function instrumentMethod( @@ -264,12 +264,11 @@ function instrumentMethod( context: unknown, options: GoogleGenAIOptions, ): (...args: T) => R | Promise { - const isSyncCreate = methodPath === CHATS_CREATE_METHOD; const isEmbeddings = instrumentedMethod.operation === 'embeddings'; return new Proxy(originalMethod, { apply(target, _, args: T): R | Promise { - const operationName = instrumentedMethod.operation; + const operationName = instrumentedMethod.operation as string; const params = args[0] as Record | undefined; const requestAttributes = extractRequestAttributes(operationName, params, context); const model = requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] ?? 'unknown'; @@ -305,10 +304,10 @@ function instrumentMethod( }, ); } - // Single span for both sync and async operations + return startSpan( { - name: isSyncCreate ? `${operationName} ${model} create` : `${operationName} ${model}`, + name: `${operationName} ${model}`, op: `gen_ai.${operationName}`, attributes: requestAttributes, }, @@ -326,8 +325,8 @@ function instrumentMethod( }, () => {}, result => { - // Only add response attributes for content-producing methods, not for chats.create or embeddings - if (!isSyncCreate && !isEmbeddings) { + // Only add response attributes for content-producing methods, not for embeddings + if (!isEmbeddings) { addResponseAttributes(span, result, options.recordOutputs); } }, @@ -340,7 +339,6 @@ function instrumentMethod( /** * Create a deep proxy for Google GenAI client instrumentation - * Recursively instruments methods and handles special cases like chats.create */ function createDeepProxy(target: T, currentPath = '', options: GoogleGenAIOptions): T { return new Proxy(target, { @@ -348,34 +346,28 @@ function createDeepProxy(target: T, currentPath = '', options: const value = Reflect.get(t, prop, receiver); const methodPath = buildMethodPath(currentPath, String(prop)); - const instrumentedMethod = GOOGLE_GENAI_METHOD_REGISTRY[methodPath as keyof typeof GOOGLE_GENAI_METHOD_REGISTRY]; - if (typeof value === 'function' && instrumentedMethod) { - // Special case: chats.create is synchronous but needs both instrumentation AND result proxying - if (methodPath === CHATS_CREATE_METHOD) { - const wrappedMethod = instrumentMethod( - value as (...args: unknown[]) => unknown, + const registryEntry = GOOGLE_GENAI_METHOD_REGISTRY[methodPath]; + if (typeof value === 'function' && registryEntry) { + if (registryEntry.operation) { + return instrumentMethod( + value as (...args: unknown[]) => Promise, methodPath, - instrumentedMethod, + registryEntry, t, options, ); - return function instrumentedAndProxiedCreate(...args: unknown[]): unknown { - const result = wrappedMethod(...args); - // If the result is an object (like a chat instance), proxy it too + } + + if (registryEntry.proxyResultPath) { + const proxyPath = registryEntry.proxyResultPath; + return function proxyFactory(this: unknown, ...args: unknown[]): unknown { + const result = (value as (...a: unknown[]) => unknown).apply(t, args); if (result && typeof result === 'object') { - return createDeepProxy(result, CHAT_PATH, options); + return createDeepProxy(result as object, proxyPath, options); } return result; }; } - - return instrumentMethod( - value as (...args: unknown[]) => Promise, - methodPath, - instrumentedMethod, - t, - options, - ); } if (typeof value === 'function') { diff --git a/packages/core/src/tracing/openai/index.ts b/packages/core/src/tracing/openai/index.ts index 0f83bf2cd3eb..03f503a225e1 100644 --- a/packages/core/src/tracing/openai/index.ts +++ b/packages/core/src/tracing/openai/index.ts @@ -180,7 +180,7 @@ function instrumentMethod( options: OpenAiOptions, ): (...args: T) => Promise { return function instrumentedCall(...args: T): Promise { - const operationName = instrumentedMethod.operation; + const operationName = instrumentedMethod.operation as string; const requestAttributes = extractRequestAttributes(args, operationName); const model = (requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown'; From b9ccd305393d291bb262b83f0fec68a868224e54 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 14:25:05 +0100 Subject: [PATCH 05/11] lfg --- .../core/src/tracing/google-genai/index.ts | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index 26361ec745b1..ba978f2e85a2 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -346,28 +346,25 @@ function createDeepProxy(target: T, currentPath = '', options: const value = Reflect.get(t, prop, receiver); const methodPath = buildMethodPath(currentPath, String(prop)); - const registryEntry = GOOGLE_GENAI_METHOD_REGISTRY[methodPath]; - if (typeof value === 'function' && registryEntry) { - if (registryEntry.operation) { - return instrumentMethod( - value as (...args: unknown[]) => Promise, - methodPath, - registryEntry, - t, - options, - ); + const instrumentedMethod = GOOGLE_GENAI_METHOD_REGISTRY[methodPath]; + if (typeof value === 'function' && instrumentedMethod) { + // If an operation is specified, we need to instrument the method itself + const wrappedMethod = instrumentedMethod.operation + ? instrumentMethod(value as (...args: unknown[]) => unknown, methodPath, instrumentedMethod, t, options) + : value.bind(t); + + if (!instrumentedMethod.proxyResultPath) { + return wrappedMethod; } - if (registryEntry.proxyResultPath) { - const proxyPath = registryEntry.proxyResultPath; - return function proxyFactory(this: unknown, ...args: unknown[]): unknown { - const result = (value as (...a: unknown[]) => unknown).apply(t, args); - if (result && typeof result === 'object') { - return createDeepProxy(result as object, proxyPath, options); - } - return result; - }; - } + // If a proxyResultPath is specified, we need to proxy the result of the method + return function (...args: unknown[]): unknown { + const result = wrappedMethod(...args); + if (result && typeof result === 'object') { + return createDeepProxy(result as object, instrumentedMethod.proxyResultPath, options); + } + return result; + }; } if (typeof value === 'function') { From ec775e86d2b5c1ba1d39709bf141bd9b8dd8cff1 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 26 Mar 2026 14:28:20 +0100 Subject: [PATCH 06/11] . --- packages/core/src/tracing/google-genai/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index ba978f2e85a2..d43296248f1a 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -357,7 +357,10 @@ function createDeepProxy(target: T, currentPath = '', options: return wrappedMethod; } - // If a proxyResultPath is specified, we need to proxy the result of the method + // If a proxyResultPath is specified, we need to proxy the result of the method. + // Note: This currently only properly handles synchronous methods. For async methods, + // the Promise itself would be proxied instead of the resolved value. Currently we + // don't have a case where this is needed, so I'll keep it simple for now. return function (...args: unknown[]): unknown { const result = wrappedMethod(...args); if (result && typeof result === 'object') { From 15e6af4c0a4403b1e7131a33ee87c97a5c3a1453 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 27 Mar 2026 14:45:46 +0100 Subject: [PATCH 07/11] . --- packages/core/src/tracing/google-genai/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index d43296248f1a..0261ae0ada38 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -346,7 +346,8 @@ function createDeepProxy(target: T, currentPath = '', options: const value = Reflect.get(t, prop, receiver); const methodPath = buildMethodPath(currentPath, String(prop)); - const instrumentedMethod = GOOGLE_GENAI_METHOD_REGISTRY[methodPath]; + const instrumentedMethod: InstrumentedMethodEntry | undefined = + GOOGLE_GENAI_METHOD_REGISTRY[methodPath as keyof typeof GOOGLE_GENAI_METHOD_REGISTRY]; if (typeof value === 'function' && instrumentedMethod) { // If an operation is specified, we need to instrument the method itself const wrappedMethod = instrumentedMethod.operation From 53f139ba56eeb6b9a8544a7fec86542d5dd044b3 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 27 Mar 2026 14:50:55 +0100 Subject: [PATCH 08/11] do not remove exports --- packages/core/src/index.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 628b0b29cfa3..09a4f36ebdb2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -164,9 +164,19 @@ export type { LangChainOptions, LangChainIntegration } from './tracing/langchain export { instrumentStateGraphCompile, instrumentLangGraph } from './tracing/langgraph'; export { LANGGRAPH_INTEGRATION_NAME } from './tracing/langgraph/constants'; export type { LangGraphOptions, LangGraphIntegration, CompiledGraph } from './tracing/langgraph/types'; -export type { OpenAiClient, OpenAiOptions } from './tracing/openai/types'; -export type { AnthropicAiClient, AnthropicAiOptions, AnthropicAiResponse } from './tracing/anthropic-ai/types'; -export type { GoogleGenAIClient, GoogleGenAIChat, GoogleGenAIOptions } from './tracing/google-genai/types'; +export type { OpenAiClient, OpenAiOptions, InstrumentedMethod } from './tracing/openai/types'; +export type { + AnthropicAiClient, + AnthropicAiOptions, + AnthropicAiInstrumentedMethod, + AnthropicAiResponse, +} from './tracing/anthropic-ai/types'; +export type { + GoogleGenAIClient, + GoogleGenAIChat, + GoogleGenAIOptions, + GoogleGenAIIstrumentedMethod, +} from './tracing/google-genai/types'; export type { FeatureFlag } from './utils/featureFlags'; export { From 960ba98ec2c7bbee605c8ad83335d0868ec6c42d Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 27 Mar 2026 14:55:51 +0100 Subject: [PATCH 09/11] fallback --- packages/core/src/tracing/anthropic-ai/index.ts | 2 +- packages/core/src/tracing/google-genai/index.ts | 2 +- packages/core/src/tracing/openai/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/tracing/anthropic-ai/index.ts b/packages/core/src/tracing/anthropic-ai/index.ts index 24f3a8f2e8b2..c51ffbb9d363 100644 --- a/packages/core/src/tracing/anthropic-ai/index.ts +++ b/packages/core/src/tracing/anthropic-ai/index.ts @@ -265,7 +265,7 @@ function instrumentMethod( ): (...args: T) => R | Promise { return new Proxy(originalMethod, { apply(target, thisArg, args: T): R | Promise { - const operationName = instrumentedMethod.operation as string; + const operationName = instrumentedMethod.operation || 'unknown'; const requestAttributes = extractRequestAttributes(args, methodPath, operationName); const model = requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] ?? 'unknown'; diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index 0261ae0ada38..8efcca90260e 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -268,7 +268,7 @@ function instrumentMethod( return new Proxy(originalMethod, { apply(target, _, args: T): R | Promise { - const operationName = instrumentedMethod.operation as string; + const operationName = instrumentedMethod.operation || 'unknown'; const params = args[0] as Record | undefined; const requestAttributes = extractRequestAttributes(operationName, params, context); const model = requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] ?? 'unknown'; diff --git a/packages/core/src/tracing/openai/index.ts b/packages/core/src/tracing/openai/index.ts index 03f503a225e1..4de5259c52b8 100644 --- a/packages/core/src/tracing/openai/index.ts +++ b/packages/core/src/tracing/openai/index.ts @@ -180,7 +180,7 @@ function instrumentMethod( options: OpenAiOptions, ): (...args: T) => Promise { return function instrumentedCall(...args: T): Promise { - const operationName = instrumentedMethod.operation as string; + const operationName = instrumentedMethod.operation || 'unknown'; const requestAttributes = extractRequestAttributes(args, operationName); const model = (requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown'; From 4cb3a67705b9b86eb498b93e81c078591075e1a9 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 27 Mar 2026 14:58:42 +0100 Subject: [PATCH 10/11] restore comments --- packages/core/src/tracing/google-genai/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index 8efcca90260e..ac06d39a5784 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -253,8 +253,8 @@ function addResponseAttributes(span: Span, response: GoogleGenAIResponse, record } /** - * Instrument any async genai method with Sentry spans - * Handles operations like models.generateContent and chat.sendMessage + * Instrument any async or synchronous genai method with Sentry spans + * Handles operations like models.generateContent and chat.sendMessage and chats.create * @see https://docs.sentry.io/platforms/javascript/guides/node/tracing/instrumentation/ai-agents-module/#manual-instrumentation */ function instrumentMethod( @@ -304,7 +304,7 @@ function instrumentMethod( }, ); } - + // Single span for both sync and async operations return startSpan( { name: `${operationName} ${model}`, @@ -339,6 +339,7 @@ function instrumentMethod( /** * Create a deep proxy for Google GenAI client instrumentation + * Recursively instruments methods and handles special cases like chats.create */ function createDeepProxy(target: T, currentPath = '', options: GoogleGenAIOptions): T { return new Proxy(target, { From 720cdb736cde4aaf545ec8dc3c79ae39aba28198 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 27 Mar 2026 16:17:14 +0100 Subject: [PATCH 11/11] . --- .../tracing/ai-providers/google-genai/test.ts | 2 +- .../suites/tracing/google-genai/test.ts | 22 +++---------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts index c5c269d435e3..c6c9001b3e45 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/ai-providers/google-genai/test.ts @@ -20,7 +20,7 @@ sentryTest('manual Google GenAI instrumentation sends gen_ai transactions', asyn const eventData = envelopeRequestParser(req); // Verify it's a gen_ai transaction - expect(eventData.transaction).toBe('chat gemini-1.5-pro create'); + expect(eventData.transaction).toBe('chat gemini-1.5-pro'); expect(eventData.contexts?.trace?.op).toBe('gen_ai.chat'); expect(eventData.contexts?.trace?.origin).toBe('auto.ai.google_genai'); expect(eventData.contexts?.trace?.data).toMatchObject({ diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts index 7172366a54c5..5194e3d3a581 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/test.ts @@ -27,23 +27,7 @@ it('traces Google GenAI chat creation and message sending', async () => { expect(transactionEvent.transaction).toBe('GET /'); expect(transactionEvent.spans).toEqual( expect.arrayContaining([ - // First span - chats.create - expect.objectContaining({ - data: expect.objectContaining({ - [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.chat', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.google_genai', - [GEN_AI_SYSTEM_ATTRIBUTE]: 'google_genai', - [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: 'gemini-1.5-pro', - [GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.8, - [GEN_AI_REQUEST_TOP_P_ATTRIBUTE]: 0.9, - [GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 150, - }), - description: 'chat gemini-1.5-pro create', - op: 'gen_ai.chat', - origin: 'auto.ai.google_genai', - }), - // Second span - chat.sendMessage + // chat.sendMessage expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat', @@ -59,7 +43,7 @@ it('traces Google GenAI chat creation and message sending', async () => { op: 'gen_ai.chat', origin: 'auto.ai.google_genai', }), - // Third span - models.generateContent + // models.generateContent expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'generate_content', @@ -78,7 +62,7 @@ it('traces Google GenAI chat creation and message sending', async () => { op: 'gen_ai.generate_content', origin: 'auto.ai.google_genai', }), - // Fourth span - models.embedContent + // models.embedContent expect.objectContaining({ data: expect.objectContaining({ [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'embeddings',