diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/test.ts b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/test.ts index 0f6be4aea592..89bf6c7b0544 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/openai/openai-tool-calls/test.ts @@ -105,10 +105,8 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: 'gpt-4', }); - expect(chatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ - type: 'string', - value: WEATHER_TOOL_DEFINITION, - }); + // Tool definitions are gen AI input data, so `genAI.inputs: false` drops them. + expect(chatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeUndefined(); expect(chatToolsSpan!.attributes[GEN_AI_RESPONSE_MODEL]).toEqual({ type: 'string', value: 'gpt-4', @@ -164,10 +162,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'boolean', value: true, }); - expect(streamingChatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ - type: 'string', - value: WEATHER_TOOL_DEFINITION, - }); + expect(streamingChatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeUndefined(); expect(streamingChatToolsSpan!.attributes[GEN_AI_RESPONSE_MODEL]).toEqual({ type: 'string', value: 'gpt-4', @@ -224,10 +219,7 @@ describe('OpenAI Tool Calls integration', () => { value: 'gpt-4', }); expect(responsesToolsSpan!.attributes[GEN_AI_REQUEST_STREAM_ATTRIBUTE]).toBeUndefined(); - expect(responsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ - type: 'string', - value: WEATHER_TOOL_DEFINITION, - }); + expect(responsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeUndefined(); expect(responsesToolsSpan!.attributes[GEN_AI_RESPONSE_MODEL]).toEqual({ type: 'string', value: 'gpt-4', @@ -283,10 +275,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'boolean', value: true, }); - expect(streamingResponsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ - type: 'string', - value: WEATHER_TOOL_DEFINITION, - }); + expect(streamingResponsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeUndefined(); expect(streamingResponsesToolsSpan!.attributes[GEN_AI_RESPONSE_MODEL]).toEqual({ type: 'string', value: 'gpt-4', diff --git a/packages/server-utils/src/ai/anthropic-ai/index.ts b/packages/server-utils/src/ai/anthropic-ai/index.ts index a4a09e334043..f45d875d8b6e 100644 --- a/packages/server-utils/src/ai/anthropic-ai/index.ts +++ b/packages/server-utils/src/ai/anthropic-ai/index.ts @@ -48,7 +48,11 @@ const INSTRUMENTED_METHODS = new WeakSet(); /** * Extract request attributes from method arguments */ -export function extractRequestAttributes(args: unknown[], operationName: string): Record { +export function extractRequestAttributes( + args: unknown[], + operationName: string, + recordInputs: boolean, +): Record { const attributes: Record = { [GEN_AI_PROVIDER_NAME]: 'anthropic', [GEN_AI_OPERATION_NAME]: operationName, @@ -57,7 +61,7 @@ export function extractRequestAttributes(args: unknown[], operationName: string) if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null) { const params = args[0] as Record; - if (params.tools && Array.isArray(params.tools)) { + if (recordInputs && params.tools && Array.isArray(params.tools)) { attributes[GEN_AI_TOOL_DEFINITIONS] = JSON.stringify(params.tools); } @@ -259,7 +263,7 @@ function instrumentMethod( } const operationName = instrumentedMethod.operation || 'unknown'; - const requestAttributes = extractRequestAttributes(args, operationName); + const requestAttributes = extractRequestAttributes(args, operationName, !!options.recordInputs); const model = requestAttributes[GEN_AI_REQUEST_MODEL] || 'unknown'; const client = getClient(); // With span streaming, omit the `'unknown'` model sentinel so the name stays low-cardinality. diff --git a/packages/server-utils/src/ai/google-genai/index.ts b/packages/server-utils/src/ai/google-genai/index.ts index fc64222a291f..45aef0fd3136 100644 --- a/packages/server-utils/src/ai/google-genai/index.ts +++ b/packages/server-utils/src/ai/google-genai/index.ts @@ -103,6 +103,7 @@ export function extractRequestAttributes( operationName: string, params?: Record, context?: unknown, + recordInputs = true, ): Record { const attributes: Record = { [GEN_AI_PROVIDER_NAME]: GOOGLE_GENAI_SYSTEM_NAME, @@ -119,7 +120,7 @@ export function extractRequestAttributes( Object.assign(attributes, extractConfigAttributes(config)); // Extract available tools from config - if ('tools' in config && Array.isArray(config.tools)) { + if (recordInputs && 'tools' in config && Array.isArray(config.tools)) { const functionDeclarations = config.tools.flatMap( (tool: { functionDeclarations: unknown[] }) => tool.functionDeclarations, ); @@ -301,7 +302,12 @@ function instrumentMethod( const operationName = instrumentedMethod.operation || 'unknown'; const params = args[0] as Record | undefined; const attributeParams = resolveChatParams(operationName, params, context); - const requestAttributes = extractRequestAttributes(operationName, attributeParams, context); + const requestAttributes = extractRequestAttributes( + operationName, + attributeParams, + context, + !!options.recordInputs, + ); const model = requestAttributes[GEN_AI_REQUEST_MODEL] || 'unknown'; const client = getClient(); // With span streaming, omit the `'unknown'` model sentinel so the name stays low-cardinality. diff --git a/packages/server-utils/src/ai/langchain/index.ts b/packages/server-utils/src/ai/langchain/index.ts index 64b402518cb5..ea60eef3305e 100644 --- a/packages/server-utils/src/ai/langchain/index.ts +++ b/packages/server-utils/src/ai/langchain/index.ts @@ -148,7 +148,7 @@ export function createLangChainCallbackHandler(options: LangChainOptions = {}): metadata, ); - const toolDefsJson = extractToolDefinitions(extraParams); + const toolDefsJson = recordInputs ? extractToolDefinitions(extraParams) : undefined; if (toolDefsJson) { attributes[GEN_AI_TOOL_DEFINITIONS] = toolDefsJson; } diff --git a/packages/server-utils/src/ai/langgraph/index.ts b/packages/server-utils/src/ai/langgraph/index.ts index de8c0ec962c1..00825cbd6206 100644 --- a/packages/server-utils/src/ai/langgraph/index.ts +++ b/packages/server-utils/src/ai/langgraph/index.ts @@ -37,12 +37,15 @@ const SENTRY_PATCHED = '__sentry_patched__'; */ export function instrumentStateGraphCompile( originalCompile: (...args: unknown[]) => CompiledGraph, - options: LangGraphOptions, + rawOptions: LangGraphOptions, ): (...args: unknown[]) => CompiledGraph { if (Object.prototype.hasOwnProperty.call(originalCompile, SENTRY_PATCHED)) { return originalCompile; } + // This is exported, so callers can hand us an options object with no recording flags set. Resolving + // here (rather than only in `instrumentStateGraph`) keeps that path on the `dataCollection` defaults. + const options = resolveAIRecordingOptions(rawOptions); const sentryHandler = createLangChainCallbackHandler(options); const wrapped = new Proxy(originalCompile, { @@ -142,15 +145,16 @@ export function instrumentCompiledGraphInvoke( ); } + const recordInputs = options.recordInputs; + const recordOutputs = options.recordOutputs; + // Extract available tools from the graph instance - const tools = extractToolsFromCompiledGraph(graphInstance); + const tools = recordInputs ? extractToolsFromCompiledGraph(graphInstance) : null; if (tools) { span.setAttribute(GEN_AI_TOOL_DEFINITIONS, JSON.stringify(tools)); } // Parse input messages - const recordInputs = options.recordInputs; - const recordOutputs = options.recordOutputs; const inputMessages = args.length > 0 ? ((args[0] as { messages?: LangChainMessage[] } | null)?.messages ?? []) : []; diff --git a/packages/server-utils/src/ai/openai/index.ts b/packages/server-utils/src/ai/openai/index.ts index 701456f5f2af..7bed7ab27743 100644 --- a/packages/server-utils/src/ai/openai/index.ts +++ b/packages/server-utils/src/ai/openai/index.ts @@ -59,7 +59,11 @@ function extractAvailableTools(params: Record): string | undefi /** * Extract request attributes from method arguments */ -export function extractRequestAttributes(args: unknown[], operationName: string): Record { +export function extractRequestAttributes( + args: unknown[], + operationName: string, + recordInputs: boolean, +): Record { const attributes: Record = { [GEN_AI_PROVIDER_NAME]: 'openai', [GEN_AI_OPERATION_NAME]: operationName, @@ -69,7 +73,7 @@ export function extractRequestAttributes(args: unknown[], operationName: string) if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null) { const params = args[0] as Record; - const availableTools = extractAvailableTools(params); + const availableTools = recordInputs ? extractAvailableTools(params) : undefined; if (availableTools) { attributes[GEN_AI_TOOL_DEFINITIONS] = availableTools; } @@ -140,7 +144,7 @@ function instrumentMethod( ): (...args: T) => Promise { return function instrumentedCall(...args: T): Promise { const operationName = instrumentedMethod.operation || 'unknown'; - const requestAttributes = extractRequestAttributes(args, operationName); + const requestAttributes = extractRequestAttributes(args, operationName, !!options.recordInputs); const model = (requestAttributes[GEN_AI_REQUEST_MODEL] as string) || 'unknown'; const params = args[0] as Record | undefined; diff --git a/packages/server-utils/src/integrations/anthropic.ts b/packages/server-utils/src/integrations/anthropic.ts index b54223925494..87bce3b9c199 100644 --- a/packages/server-utils/src/integrations/anthropic.ts +++ b/packages/server-utils/src/integrations/anthropic.ts @@ -98,7 +98,7 @@ function createGenAiSpan( const { recordInputs } = resolveAIRecordingOptions(options); - const attributes = extractRequestAttributes(args, operation); + const attributes = extractRequestAttributes(args, operation, recordInputs); const model = (attributes[GEN_AI_REQUEST_MODEL] as string) || 'unknown'; attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; const client = getClient(); diff --git a/packages/server-utils/src/integrations/google-genai.ts b/packages/server-utils/src/integrations/google-genai.ts index 4a67a2f4d72d..9c37a5351f7e 100644 --- a/packages/server-utils/src/integrations/google-genai.ts +++ b/packages/server-utils/src/integrations/google-genai.ts @@ -114,7 +114,7 @@ function createGenAiSpan( const { recordInputs } = resolveAIRecordingOptions(options); - const attributes = extractRequestAttributes(operation, params, data.self); + const attributes = extractRequestAttributes(operation, params, data.self, recordInputs); const model = (attributes[GEN_AI_REQUEST_MODEL] as string) || 'unknown'; attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; const client = getClient(); diff --git a/packages/server-utils/src/integrations/openai.ts b/packages/server-utils/src/integrations/openai.ts index 6e900596aef4..e284d3b1bc4a 100644 --- a/packages/server-utils/src/integrations/openai.ts +++ b/packages/server-utils/src/integrations/openai.ts @@ -81,7 +81,7 @@ function createGenAiSpan(data: OpenAiChatChannelContext, operation: string, opti const { recordInputs } = resolveAIRecordingOptions(options); - const attributes = extractRequestAttributes(args, operation); + const attributes = extractRequestAttributes(args, operation, recordInputs); attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = ORIGIN; const model = (params?.model as string) || 'unknown'; const client = getClient();