From 69fd4c939a2f337ad7c6763806cb0df2f6edfe65 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 29 Jul 2026 14:28:07 +0200 Subject: [PATCH] ref(core)!: Rename `gen_ai.request.available_tools` to `gen_ai.tool.definitions` Emit the `gen_ai.tool.definitions` attribute from `@sentry/conventions` instead of the deprecated `gen_ai.request.available_tools` alias across all AI integrations (OpenAI, Anthropic, Google GenAI, LangChain, LangGraph, Vercel AI). Co-Authored-By: Claude Opus 4.8 (1M context) --- MIGRATION.md | 1 + .../suites/tracing/anthropic/test.ts | 6 +++--- .../suites/tracing/google-genai/test.ts | 10 ++++------ .../suites/tracing/langgraph/test.ts | 4 ++-- .../tracing/openai/openai-tool-calls/test.ts | 18 +++++++++--------- .../tracing/vercelai/span-streaming-v4/test.ts | 6 +++--- .../tracing/vercelai/span-streaming-v6/test.ts | 6 +++--- .../suites/tracing/vercelai/test.ts | 6 +++--- .../suites/tracing/vercelai/v5/test.ts | 6 +++--- .../suites/tracing/vercelai/v6_v7/test.ts | 6 +++--- .../core/src/tracing/anthropic-ai/index.ts | 4 ++-- .../core/src/tracing/google-genai/index.ts | 4 ++-- packages/core/src/tracing/langchain/index.ts | 4 ++-- packages/core/src/tracing/langgraph/index.ts | 4 ++-- packages/core/src/tracing/openai/index.ts | 4 ++-- packages/core/src/tracing/vercel-ai/index.ts | 3 ++- packages/core/src/tracing/vercel-ai/utils.ts | 9 ++++----- .../src/vercel-ai/vercel-ai-dc-subscriber.ts | 8 ++++---- .../vercel-ai-orchestrion-subscriber.ts | 2 +- 19 files changed, 55 insertions(+), 56 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 6de7ba165072..1cb76878c4ef 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -253,6 +253,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 33ec4221f831..32fbf973d70e 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 16d51d5f1962..17a567577738 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, @@ -128,7 +128,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 365c4d68cb32..691d8701237e 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 e28aa7ddbe41..fa33b15b8487 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 220a0314a77b..fe67a880e7b2 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 5a9eb127d1d2..ffe70e41f08e 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 706cb5e0ba15..114087f73b4a 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 4be1beeb3ab4..15d3d78aa4d5 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/core/src/tracing/anthropic-ai/index.ts b/packages/core/src/tracing/anthropic-ai/index.ts index dc47c5f80a86..168960f80bc6 100644 --- a/packages/core/src/tracing/anthropic-ai/index.ts +++ b/packages/core/src/tracing/anthropic-ai/index.ts @@ -8,7 +8,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, @@ -19,6 +18,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 '../ai/gen-ai-attributes'; import type { InstrumentedMethodEntry } from '../ai/utils'; @@ -59,7 +59,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/core/src/tracing/google-genai/index.ts b/packages/core/src/tracing/google-genai/index.ts index 22dfb179b19d..578bf1719e75 100644 --- a/packages/core/src/tracing/google-genai/index.ts +++ b/packages/core/src/tracing/google-genai/index.ts @@ -11,7 +11,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, @@ -23,6 +22,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, @@ -125,7 +125,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/core/src/tracing/langchain/index.ts b/packages/core/src/tracing/langchain/index.ts index b6407692e49a..682f70d15055 100644 --- a/packages/core/src/tracing/langchain/index.ts +++ b/packages/core/src/tracing/langchain/index.ts @@ -7,8 +7,8 @@ import { startSpanManual } from '../../tracing/trace'; import type { Span, SpanAttributeValue } from '../../types/span'; 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, @@ -142,7 +142,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/core/src/tracing/langgraph/index.ts b/packages/core/src/tracing/langgraph/index.ts index c5cdd753f79c..824def8f7021 100644 --- a/packages/core/src/tracing/langgraph/index.ts +++ b/packages/core/src/tracing/langgraph/index.ts @@ -8,9 +8,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 '../ai/gen-ai-attributes'; import { @@ -206,7 +206,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/core/src/tracing/openai/index.ts b/packages/core/src/tracing/openai/index.ts index 66ccdd5b6363..f15854b9c178 100644 --- a/packages/core/src/tracing/openai/index.ts +++ b/packages/core/src/tracing/openai/index.ts @@ -11,9 +11,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 '../ai/utils'; import { stringify } from '../../utils/string'; @@ -68,7 +68,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/core/src/tracing/vercel-ai/index.ts b/packages/core/src/tracing/vercel-ai/index.ts index a80f689e21da..18173b644c20 100644 --- a/packages/core/src/tracing/vercel-ai/index.ts +++ b/packages/core/src/tracing/vercel-ai/index.ts @@ -18,6 +18,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, @@ -316,7 +317,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; } @@ -100,7 +99,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 bf55e814683f..6671f366004e 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, @@ -82,7 +82,7 @@ const operationIdByCallId = new Map>(); // A streamed `streamText` operation's own channel result is always `undefined` (the SDK exposes the @@ -372,7 +372,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); } @@ -467,7 +467,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