diff --git a/MIGRATION.md b/MIGRATION.md index 3df87e01f304..efbeec6c97ea 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -262,6 +262,7 @@ Affected SDKs: All SDKs. - Legacy messaging (`messaging.*`) and database (`db.statement`, …) span attributes on the AMQP and Redis instrumentations were replaced by their current semantic-convention equivalents. - The gen_ai cache token attributes `gen_ai.usage.cache_creation_input_tokens` and `gen_ai.usage.cache_read_input_tokens` were renamed to `gen_ai.usage.cache_creation.input_tokens` and `gen_ai.usage.cache_read.input_tokens`. - The `gen_ai.system` span attribute was renamed to `gen_ai.provider.name` across all AI integrations. +- The `gen_ai.request.available_tools` span attribute was renamed to `gen_ai.tool.definitions` across all AI integrations. - Span attributes now use the shared `@sentry/conventions` package under the hood. If you reference these attributes in custom instrumentation, `beforeSendSpan`, dashboards, or alerts, update them to the new names. diff --git a/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts b/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts index 65eafea375be..4f14ede7c609 100644 --- a/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/anthropic/test.ts @@ -3,7 +3,6 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MAX_TOKENS, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_TEMPERATURE, @@ -14,6 +13,7 @@ import { GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, @@ -453,7 +453,7 @@ describe('Anthropic integration', () => { expect(firstSpan!.name).toBe('chat claude-3-haiku-20240307'); expect(firstSpan!.status).toBe('ok'); expect(firstSpan!.attributes['sentry.op'].value).toBe('gen_ai.chat'); - expect(firstSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS].value).toBe(EXPECTED_TOOLS_JSON); + expect(firstSpan!.attributes[GEN_AI_TOOL_DEFINITIONS].value).toBe(EXPECTED_TOOLS_JSON); expect(firstSpan!.attributes[GEN_AI_RESPONSE_TOOL_CALLS].value).toBe(EXPECTED_TOOL_CALLS_JSON); }, }) @@ -483,7 +483,7 @@ describe('Anthropic integration', () => { expect(span.attributes['sentry.op'].value).toBe('gen_ai.chat'); expect(span.attributes[GEN_AI_RESPONSE_STREAMING].value).toBe(true); expect(span.attributes[GEN_AI_RESPONSE_FINISH_REASONS].value).toBe('["tool_use"]'); - expect(span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS].value).toBe(EXPECTED_TOOLS_JSON); + expect(span.attributes[GEN_AI_TOOL_DEFINITIONS].value).toBe(EXPECTED_TOOLS_JSON); expect(span.attributes[GEN_AI_RESPONSE_TOOL_CALLS].value).toBe(EXPECTED_TOOL_CALLS_JSON); } 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 0e195be2f668..f0ab73ca0123 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 @@ -4,7 +4,6 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MAX_TOKENS, GEN_AI_REQUEST_MODEL, GEN_AI_REQUEST_TEMPERATURE, @@ -16,6 +15,7 @@ import { GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, @@ -182,7 +182,7 @@ describe('Google GenAI integration', () => { expect(container.items).toHaveLength(3); const nonStreamingToolsSpan = container.items.find( span => - span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]?.value === EXPECTED_AVAILABLE_TOOLS_JSON && + span.attributes[GEN_AI_TOOL_DEFINITIONS]?.value === EXPECTED_AVAILABLE_TOOLS_JSON && span.attributes[GEN_AI_RESPONSE_STREAMING] === undefined, ); expect(nonStreamingToolsSpan).toBeDefined(); @@ -198,7 +198,7 @@ describe('Google GenAI integration', () => { const streamingToolsSpan = container.items.find( span => - span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]?.value === EXPECTED_AVAILABLE_TOOLS_JSON && + span.attributes[GEN_AI_TOOL_DEFINITIONS]?.value === EXPECTED_AVAILABLE_TOOLS_JSON && span.attributes[GEN_AI_RESPONSE_STREAMING]?.value === true, ); expect(streamingToolsSpan).toBeDefined(); @@ -214,9 +214,7 @@ describe('Google GenAI integration', () => { expect(streamingToolsSpan!.attributes[GEN_AI_USAGE_OUTPUT_TOKENS].value).toBe(10); expect(streamingToolsSpan!.attributes[GEN_AI_USAGE_TOTAL_TOKENS].value).toBe(22); - const noToolsSpan = container.items.find( - span => span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] === undefined, - ); + const noToolsSpan = container.items.find(span => span.attributes[GEN_AI_TOOL_DEFINITIONS] === undefined); expect(noToolsSpan).toBeDefined(); expect(noToolsSpan!.name).toBe('generate_content gemini-2.0-flash-001'); expect(noToolsSpan!.status).toBe('ok'); diff --git a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts index 91481ae3d82c..52b43e922525 100644 --- a/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/langgraph/test.ts @@ -6,11 +6,11 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PIPELINE_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_RESPONSE_MODEL, GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_NAME, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, @@ -107,7 +107,7 @@ describe('LangGraph integration', () => { expect(toolAgentInvokeSpan).toBeDefined(); expect(toolAgentInvokeSpan!.status).toBe('ok'); expect(toolAgentInvokeSpan!.attributes['sentry.op'].value).toBe('gen_ai.invoke_agent'); - expect(toolAgentInvokeSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS].value).toContain('get_weather'); + expect(toolAgentInvokeSpan!.attributes[GEN_AI_TOOL_DEFINITIONS].value).toContain('get_weather'); expect(toolAgentInvokeSpan!.attributes[GEN_AI_INPUT_MESSAGES].value).toContain('What is the weather?'); expect(toolAgentInvokeSpan!.attributes[GEN_AI_RESPONSE_MODEL].value).toBe('gpt-4-0613'); expect(toolAgentInvokeSpan!.attributes[GEN_AI_RESPONSE_TEXT].value).toContain( 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 8be980920f18..b14d5a56901f 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 @@ -4,7 +4,6 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_ID, @@ -12,6 +11,7 @@ import { GEN_AI_RESPONSE_STREAMING, GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, @@ -106,7 +106,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: 'gpt-4', }); - expect(chatToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(chatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -165,7 +165,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'boolean', value: true, }); - expect(streamingChatToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(streamingChatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -225,7 +225,7 @@ describe('OpenAI Tool Calls integration', () => { value: 'gpt-4', }); expect(responsesToolsSpan!.attributes[GEN_AI_REQUEST_STREAM_ATTRIBUTE]).toBeUndefined(); - expect(responsesToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(responsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -284,7 +284,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'boolean', value: true, }); - expect(streamingResponsesToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(streamingResponsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -358,7 +358,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: '[{"role":"user","content":"What is the weather like in Paris today?"}]', }); - expect(chatToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(chatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -429,7 +429,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: '[{"role":"user","content":"What is the weather like in Paris today?"}]', }); - expect(streamingChatToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(streamingChatToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -497,7 +497,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: '[{"role":"user","content":"What is the weather like in Paris today?"}]', }); - expect(responsesToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(responsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); @@ -564,7 +564,7 @@ describe('OpenAI Tool Calls integration', () => { type: 'string', value: '[{"role":"user","content":"What is the weather like in Paris today?"}]', }); - expect(streamingResponsesToolsSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toEqual({ + expect(streamingResponsesToolsSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toEqual({ type: 'string', value: WEATHER_TOOL_DEFINITION, }); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v4/test.ts b/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v4/test.ts index 2f61639c50b6..8c0d21b8692f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v4/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v4/test.ts @@ -4,9 +4,9 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_MESSAGES, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -209,12 +209,12 @@ describe('Vercel AI integration (streaming v4)', () => { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin), }), }), - // Fifth span - tool call generate_content with available_tools + // Fifth span - tool call generate_content with tool definitions expect.objectContaining({ name: 'generate_content mock-model-id', status: 'ok', attributes: expect.objectContaining({ - [GEN_AI_REQUEST_AVAILABLE_TOOLS]: expect.objectContaining({ + [GEN_AI_TOOL_DEFINITIONS]: expect.objectContaining({ value: expect.stringContaining('getWeather'), }), [GEN_AI_REQUEST_MODEL]: attr('mock-model-id'), diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v6/test.ts b/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v6/test.ts index 4136c7e6bb70..4f9fb903c3a0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v6/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/span-streaming-v6/test.ts @@ -4,9 +4,9 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_MESSAGES, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -203,12 +203,12 @@ describe('Vercel AI integration (streaming, v6)', () => { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(origin), }), }), - // Fifth span - tool call generate_content with available_tools + // Fifth span - tool call generate_content with tool definitions expect.objectContaining({ name: 'generate_content mock-model-id', status: 'ok', attributes: expect.objectContaining({ - [GEN_AI_REQUEST_AVAILABLE_TOOLS]: expect.objectContaining({ + [GEN_AI_TOOL_DEFINITIONS]: expect.objectContaining({ value: expect.stringContaining('getWeather'), }), [GEN_AI_REQUEST_MODEL]: attr('mock-model-id'), diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/test.ts b/dev-packages/node-integration-tests/suites/tracing/vercelai/test.ts index e7ff55ad8427..c93015784926 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/test.ts @@ -6,10 +6,10 @@ import { GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_MESSAGES, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -223,13 +223,13 @@ describe('Vercel AI integration (v4)', () => { const toolGenerateContentSpan = container.items.find( span => span.name === 'generate_content mock-model-id' && - getStringAttributeValue(span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]?.value)?.includes('getWeather'), + getStringAttributeValue(span.attributes[GEN_AI_TOOL_DEFINITIONS]?.value)?.includes('getWeather'), ); expect(toolGenerateContentSpan).toBeDefined(); expect(toolGenerateContentSpan!.name).toBe('generate_content mock-model-id'); expect(toolGenerateContentSpan!.status).toBe('ok'); expect(toolGenerateContentSpan!.attributes['sentry.op'].value).toBe('gen_ai.generate_content'); - expect(toolGenerateContentSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS].value).toContain('getWeather'); + expect(toolGenerateContentSpan!.attributes[GEN_AI_TOOL_DEFINITIONS].value).toContain('getWeather'); expect(toolGenerateContentSpan!.attributes[GEN_AI_USAGE_INPUT_TOKENS].value).toBe(15); const toolExecutionSpan = container.items.find(span => span.name === 'execute_tool getWeather'); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/test.ts index 4fd5ccd3e3a0..36a2f7da12c5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/v5/test.ts @@ -4,9 +4,9 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -229,13 +229,13 @@ describe('Vercel AI integration (v5)', () => { const toolGenerateContentSpan = container.items.find( span => span.name === 'generate_content mock-model-id' && - span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] !== undefined, + span.attributes[GEN_AI_TOOL_DEFINITIONS] !== undefined, ); expect(toolGenerateContentSpan).toBeDefined(); expect(toolGenerateContentSpan!.name).toBe('generate_content mock-model-id'); expect(toolGenerateContentSpan!.status).toBe('ok'); expect(toolGenerateContentSpan!.attributes['sentry.op'].value).toBe('gen_ai.generate_content'); - expect(toolGenerateContentSpan!.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toBeDefined(); + expect(toolGenerateContentSpan!.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeDefined(); expect(toolGenerateContentSpan!.attributes[GEN_AI_USAGE_INPUT_TOKENS].value).toBe(15); const toolExecutionSpan = container.items.find(span => span.name === 'execute_tool getWeather'); diff --git a/dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts b/dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts index 19f784b339aa..c48783c19eb0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/vercelai/v6_v7/test.ts @@ -6,11 +6,11 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_MODEL, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -132,13 +132,13 @@ describe.each(matrix)('Vercel AI integration (version %s)', (version, vercelAiVe const toolGenerateContentSpan = container.items.find( span => span.name === 'generate_content mock-model-id' && - span.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] !== undefined, + span.attributes[GEN_AI_TOOL_DEFINITIONS] !== undefined, )!; expect(toolGenerateContentSpan).toBeDefined(); expect(toolGenerateContentSpan.name).toBe('generate_content mock-model-id'); expect(toolGenerateContentSpan.status).toBe('ok'); expect(toolGenerateContentSpan.attributes['sentry.op']?.value).toBe('gen_ai.generate_content'); - expect(toolGenerateContentSpan.attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS]).toBeDefined(); + expect(toolGenerateContentSpan.attributes[GEN_AI_TOOL_DEFINITIONS]).toBeDefined(); expect(toolGenerateContentSpan.attributes[GEN_AI_USAGE_INPUT_TOKENS]?.value).toBe(15); const toolExecutionSpan = container.items.find(span => span.name === 'execute_tool getWeather')!; diff --git a/packages/server-utils/src/ai/anthropic-ai/index.ts b/packages/server-utils/src/ai/anthropic-ai/index.ts index 4b824b09cc98..88c335734d01 100644 --- a/packages/server-utils/src/ai/anthropic-ai/index.ts +++ b/packages/server-utils/src/ai/anthropic-ai/index.ts @@ -11,7 +11,6 @@ import { GEN_AI_OPERATION_NAME, GEN_AI_PROMPT, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_FREQUENCY_PENALTY, GEN_AI_REQUEST_MAX_TOKENS, GEN_AI_REQUEST_MODEL, @@ -22,6 +21,7 @@ import { GEN_AI_RESPONSE_MODEL, GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, + GEN_AI_TOOL_DEFINITIONS, } from '@sentry/conventions/attributes'; import { GEN_AI_REQUEST_STREAM_ATTRIBUTE } from '../core/gen-ai-attributes'; import type { InstrumentedMethodEntry } from '../core/utils'; @@ -62,7 +62,7 @@ export function extractRequestAttributes( if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null) { const params = args[0] as Record; if (params.tools && Array.isArray(params.tools)) { - attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] = JSON.stringify(params.tools); + attributes[GEN_AI_TOOL_DEFINITIONS] = JSON.stringify(params.tools); } attributes[GEN_AI_REQUEST_MODEL] = params.model ?? 'unknown'; diff --git a/packages/server-utils/src/ai/google-genai/index.ts b/packages/server-utils/src/ai/google-genai/index.ts index 32a830a7113c..15ac50ca0fa9 100644 --- a/packages/server-utils/src/ai/google-genai/index.ts +++ b/packages/server-utils/src/ai/google-genai/index.ts @@ -15,7 +15,6 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_FREQUENCY_PENALTY, GEN_AI_REQUEST_MAX_TOKENS, GEN_AI_REQUEST_MODEL, @@ -27,6 +26,7 @@ import { GEN_AI_RESPONSE_TEXT, GEN_AI_RESPONSE_TOOL_CALLS, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, @@ -128,7 +128,7 @@ export function extractRequestAttributes( const functionDeclarations = config.tools.flatMap( (tool: { functionDeclarations: unknown[] }) => tool.functionDeclarations, ); - attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] = JSON.stringify(functionDeclarations); + attributes[GEN_AI_TOOL_DEFINITIONS] = JSON.stringify(functionDeclarations); } } } else { diff --git a/packages/server-utils/src/ai/langchain/index.ts b/packages/server-utils/src/ai/langchain/index.ts index 37b6f98b8b0a..0ded43a6d291 100644 --- a/packages/server-utils/src/ai/langchain/index.ts +++ b/packages/server-utils/src/ai/langchain/index.ts @@ -10,8 +10,8 @@ import { import type { Span, SpanAttributeValue } from '@sentry/core'; import { GEN_AI_OPERATION_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, GEN_AI_TOOL_OUTPUT, @@ -145,7 +145,7 @@ export function createLangChainCallbackHandler(options: LangChainOptions = {}): const toolDefsJson = extractToolDefinitions(extraParams); if (toolDefsJson) { - attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] = toolDefsJson; + attributes[GEN_AI_TOOL_DEFINITIONS] = toolDefsJson; } const modelName = attributes[GEN_AI_REQUEST_MODEL]; diff --git a/packages/server-utils/src/ai/langgraph/index.ts b/packages/server-utils/src/ai/langgraph/index.ts index 3f4feeb5f2ca..28e2f42c37a8 100644 --- a/packages/server-utils/src/ai/langgraph/index.ts +++ b/packages/server-utils/src/ai/langgraph/index.ts @@ -13,9 +13,9 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PIPELINE_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, } from '@sentry/conventions/attributes'; import { GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE } from '../core/gen-ai-attributes'; import { @@ -157,7 +157,7 @@ export function instrumentCompiledGraphInvoke( // Extract available tools from the graph instance const tools = extractToolsFromCompiledGraph(graphInstance); if (tools) { - span.setAttribute(GEN_AI_REQUEST_AVAILABLE_TOOLS, JSON.stringify(tools)); + span.setAttribute(GEN_AI_TOOL_DEFINITIONS, JSON.stringify(tools)); } // Parse input messages diff --git a/packages/server-utils/src/ai/openai/index.ts b/packages/server-utils/src/ai/openai/index.ts index 71f2a717bec8..8e33c540e5ce 100644 --- a/packages/server-utils/src/ai/openai/index.ts +++ b/packages/server-utils/src/ai/openai/index.ts @@ -15,9 +15,9 @@ import { GEN_AI_INPUT_MESSAGES, GEN_AI_OPERATION_NAME, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, } from '@sentry/conventions/attributes'; import type { InstrumentedMethodEntry } from '../core/utils'; import { @@ -71,7 +71,7 @@ export function extractRequestAttributes(args: unknown[], operationName: string) const availableTools = extractAvailableTools(params); if (availableTools) { - attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS] = availableTools; + attributes[GEN_AI_TOOL_DEFINITIONS] = availableTools; } Object.assign(attributes, extractRequestParameters(params)); diff --git a/packages/server-utils/src/ai/vercel-ai/index.ts b/packages/server-utils/src/ai/vercel-ai/index.ts index f5f332fb2429..1fb9b96eeb69 100644 --- a/packages/server-utils/src/ai/vercel-ai/index.ts +++ b/packages/server-utils/src/ai/vercel-ai/index.ts @@ -20,6 +20,7 @@ import { GEN_AI_PROVIDER_NAME, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_MODEL, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_DESCRIPTION, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, @@ -317,7 +318,7 @@ export function processVercelAiSpanAttributes(attributes: Record description from all spans with available_tools. + * Builds a map of tool name -> description from all spans with tool definitions. * This avoids O(n²) iteration and repeated JSON parsing. */ function buildToolDescriptionMap(spans: SpanJSON[]): Map { const toolDescriptions = new Map(); for (const span of spans) { - const availableTools = span.data[GEN_AI_REQUEST_AVAILABLE_TOOLS]; + const availableTools = span.data[GEN_AI_TOOL_DEFINITIONS]; if (typeof availableTools !== 'string') { continue; } @@ -99,7 +98,7 @@ function buildToolDescriptionMap(spans: SpanJSON[]): Map { * Applies tool descriptions and accumulated tokens to spans in a single pass. * * - For `gen_ai.execute_tool` spans: looks up tool description from - * `gen_ai.request.available_tools` on sibling spans + * `gen_ai.tool.definitions` on sibling spans * - For `gen_ai.invoke_agent` spans: applies accumulated token data from children */ export function applyToolDescriptionsAndTokens(spans: SpanJSON[], tokenAccumulator: Map): void { diff --git a/packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts b/packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts index dd06780c5987..9fbda5cf91ea 100644 --- a/packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts +++ b/packages/server-utils/src/vercel-ai/vercel-ai-dc-subscriber.ts @@ -7,13 +7,13 @@ import { GEN_AI_OPERATION_NAME, GEN_AI_OUTPUT_MESSAGES, GEN_AI_PROVIDER_NAME, - GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_ID, GEN_AI_RESPONSE_MODEL, GEN_AI_RESPONSE_STREAMING, GEN_AI_SYSTEM_INSTRUCTIONS, + GEN_AI_TOOL_DEFINITIONS, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, GEN_AI_TOOL_OUTPUT, @@ -80,7 +80,7 @@ const operationIdByCallId = new Map>(); // A streamed `streamText` operation's own channel result is always `undefined` (the SDK exposes the @@ -370,7 +370,7 @@ export function createSpanFromMessage( const maxRetries = asNumber(event.maxRetries); // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them. - // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then. + // Gated on `recordInputs` to match the OTel path, which only records `tool.definitions` then. if (recordInputs) { recordToolDescriptions(callId, event.tools); } @@ -465,7 +465,7 @@ function buildModelCallSpan( ...baseAttributes, [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}), - ...(recordInputs && Array.isArray(event.tools) ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: stringify(event.tools) } : {}), + ...(recordInputs && Array.isArray(event.tools) ? { [GEN_AI_TOOL_DEFINITIONS]: stringify(event.tools) } : {}), }); } diff --git a/packages/server-utils/src/vercel-ai/vercel-ai-orchestrion-subscriber.ts b/packages/server-utils/src/vercel-ai/vercel-ai-orchestrion-subscriber.ts index ad67f26f442c..72fc3a04b4bc 100644 --- a/packages/server-utils/src/vercel-ai/vercel-ai-orchestrion-subscriber.ts +++ b/packages/server-utils/src/vercel-ai/vercel-ai-orchestrion-subscriber.ts @@ -487,7 +487,7 @@ function patchModelMethod( provider: model.provider, modelId: model.modelId, // v4 nests the tool list under `mode.tools` (the `LanguageModelV1` call shape); v5+ passes a - // top-level `tools` array. Reading both keeps `available_tools` populated on the model-call span. + // top-level `tools` array. Reading both keeps `tool.definitions` populated on the model-call span. tools: callArgs.tools ?? (isObjectLike(callArgs.mode) ? callArgs.mode.tools : undefined), messages: callArgs.prompt, // Inherit the enclosing operation's per-call recording flags so inputs/tools/outputs are recorded on