diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 239602450fd3..671bc629aa7e 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -20,6 +20,7 @@ * Tracing: added the "parts" array to "gen_ai.input.messages" and "gen_ai.output.messages". * Tracing: removed "role" as a separate attribute and added "role" to "gen_ai.input.messages" and "gen_ai.output.messages" content. * Tracing: added "finish_reason" as part of "gen_ai.output.messages" content. +* Tracing: changed the tool calls to use the api definitions as the types in traces. For example "function_call" instead of "function" and "function_call_output" instead of "function" ### Bugs Fixed * Tracing: fixed a bug with computer use tool call output including screenshot binary data even when binary data tracing is off. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py index d822ce2fd7c5..af942b7619ef 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/telemetry/_responses_instrumentor.py @@ -606,11 +606,8 @@ def _add_tool_message_events( # pylint: disable=too-many-branches if not item_type: continue # Skip if no type - # Convert function_call_output to "function" - if item_type == "function_call_output": - tool_output["type"] = "function" - else: - tool_output["type"] = item_type + # Use the API type directly + tool_output["type"] = item_type # Add call_id as "id" - handle both dict and object if isinstance(output_item, dict): @@ -1035,7 +1032,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle function_call type if item_type == "function_call": tool_call = { - "type": "function", + "type": item_type, } # Always include id (needed to correlate with function output) @@ -1057,7 +1054,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle file_search_call type elif item_type == "file_search_call": tool_call = { - "type": "file_search", + "type": item_type, } if hasattr(output_item, "id"): @@ -1083,7 +1080,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle code_interpreter_call type elif item_type == "code_interpreter_call": tool_call = { - "type": "code_interpreter", + "type": item_type, } if hasattr(output_item, "id"): @@ -1112,7 +1109,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle web_search_call type elif item_type == "web_search_call": tool_call = { - "type": "web_search", + "type": item_type, } if hasattr(output_item, "id"): @@ -1143,7 +1140,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle azure_ai_search_call type elif item_type == "azure_ai_search_call": tool_call = { - "type": "azure_ai_search", + "type": item_type, } if hasattr(output_item, "id"): @@ -1175,7 +1172,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle image_generation_call type elif item_type == "image_generation_call": tool_call = { - "type": "image_generation", + "type": item_type, } if hasattr(output_item, "id"): @@ -1204,7 +1201,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle mcp_call type (Model Context Protocol) elif item_type == "mcp_call": tool_call = { - "type": "mcp", + "type": item_type, } if hasattr(output_item, "id"): @@ -1254,7 +1251,7 @@ def _add_tool_call_events( # pylint: disable=too-many-branches # Handle computer_call type (for computer use) elif item_type == "computer_call": tool_call = { - "type": "computer", + "type": item_type, } if hasattr(output_item, "call_id"): @@ -3493,7 +3490,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "tool" # Override role for tool outputs tool_output: Dict[str, Any] = { - "type": "function", + "type": item_type, } # Add call_id as "id" - always include for correlation @@ -3529,7 +3526,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for function calls tool_call = { - "type": "function", + "type": item_type, } # Always include ID (needed for correlation) @@ -3568,7 +3565,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for file search calls tool_call = { - "type": "file_search", + "type": item_type, } # Always include ID (needed for correlation) @@ -3611,7 +3608,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for code interpreter calls tool_call = { - "type": "code_interpreter", + "type": item_type, } # Always include ID (needed for correlation) @@ -3667,7 +3664,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for web search calls tool_call = { - "type": "web_search", + "type": item_type, } # Always include ID (needed for correlation) @@ -3708,7 +3705,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for Azure AI Search calls tool_call = { - "type": "azure_ai_search", + "type": item_type, } # Always include ID (needed for correlation) @@ -3754,7 +3751,7 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- role = "assistant" # Override role for image generation calls tool_call = { - "type": "image_generation", + "type": item_type, } # Always include ID (needed for correlation) @@ -3796,11 +3793,14 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- # Remote function call (like Bing Custom Search call) role = "assistant" # Override role for remote function calls - # Extract the tool name - tool_name = getattr(item, "name", None) if hasattr(item, "name") else None + # Check if there's a more specific type in name field (e.g., "bing_custom_search_preview_call") + specific_type = None + if hasattr(item, "name") and item.name: + # Use the API type directly without transformation + specific_type = item.name tool_call = { - "type": tool_name if tool_name else "remote_function", + "type": specific_type if specific_type else item_type, } # Always include ID (needed for correlation) @@ -3877,11 +3877,14 @@ def _add_conversation_item_event( # pylint: disable=too-many-branches,too-many- # Remote function call output (like Bing Custom Search output) role = "tool" # Tool outputs use role "tool" - # Extract the tool name - tool_name = getattr(item, "name", None) if hasattr(item, "name") else None + # Check if there's a more specific type in name field (e.g., "bing_custom_search_preview_call_output") + specific_type = None + if hasattr(item, "name") and item.name: + # Use the API type directly without transformation + specific_type = item.name tool_output = { - "type": tool_name if tool_name else "remote_function", + "type": specific_type if specific_type else item_type, } # Always include ID (needed for correlation) diff --git a/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor.py b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor.py index 0eb5a32af572..2519e4b6f7ba 100644 --- a/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor.py +++ b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor.py @@ -868,7 +868,7 @@ def test_sync_function_tool_with_content_recording_non_streaming(self, **kwargs) "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, ] @@ -899,7 +899,7 @@ def test_sync_function_tool_with_content_recording_non_streaming(self, **kwargs) "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', }, }, { @@ -1064,7 +1064,7 @@ def test_sync_function_tool_with_content_recording_streaming(self, **kwargs): "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, ] @@ -1095,7 +1095,7 @@ def test_sync_function_tool_with_content_recording_streaming(self, **kwargs): "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', }, }, { @@ -1236,7 +1236,7 @@ def test_sync_function_tool_without_content_recording_non_streaming(self, **kwar "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, ] @@ -1267,7 +1267,7 @@ def test_sync_function_tool_without_content_recording_non_streaming(self, **kwar "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, { @@ -1426,7 +1426,7 @@ def test_sync_function_tool_without_content_recording_streaming(self, **kwargs): "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, ] @@ -1457,7 +1457,7 @@ def test_sync_function_tool_without_content_recording_streaming(self, **kwargs): "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, { @@ -1598,7 +1598,7 @@ def test_sync_function_tool_list_conversation_items_with_content_recording(self, "attributes": { "gen_ai.provider.name": "azure.openai", "gen_ai.conversation.item.id": "*", - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', }, }, { @@ -1606,7 +1606,7 @@ def test_sync_function_tool_list_conversation_items_with_content_recording(self, "attributes": { "gen_ai.provider.name": "azure.openai", "gen_ai.conversation.item.id": "*", - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, { @@ -1747,7 +1747,7 @@ def test_sync_function_tool_list_conversation_items_without_content_recording(se "attributes": { "gen_ai.provider.name": "azure.openai", "gen_ai.conversation.item.id": "*", - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, { @@ -1755,7 +1755,7 @@ def test_sync_function_tool_list_conversation_items_without_content_recording(se "attributes": { "gen_ai.provider.name": "azure.openai", "gen_ai.conversation.item.id": "*", - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, { @@ -3647,7 +3647,7 @@ def test_responses_stream_method_with_tools_with_content_recording(self, **kwarg "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, ] @@ -3662,7 +3662,7 @@ def test_responses_stream_method_with_tools_with_content_recording(self, **kwarg "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "65°F", "condition": "cloudy"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "65°F", "condition": "cloudy"}}}]}]', }, }, { @@ -3785,7 +3785,7 @@ def test_responses_stream_method_with_tools_without_content_recording(self, **kw "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, ] @@ -3800,7 +3800,7 @@ def test_responses_stream_method_with_tools_without_content_recording(self, **kw "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, { diff --git a/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor_async.py b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor_async.py index 1700e7d87988..de7b02aa31a0 100644 --- a/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor_async.py +++ b/sdk/ai/azure-ai-projects/tests/agents/telemetry/test_responses_instrumentor_async.py @@ -482,7 +482,7 @@ async def test_async_function_tool_with_content_recording_streaming(self, **kwar "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, ] @@ -513,7 +513,7 @@ async def test_async_function_tool_with_content_recording_streaming(self, **kwar "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "72°F", "condition": "sunny"}}}]}]', }, }, { @@ -674,7 +674,7 @@ async def test_async_function_tool_without_content_recording_streaming(self, **k "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, ] @@ -705,7 +705,7 @@ async def test_async_function_tool_without_content_recording_streaming(self, **k "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, { @@ -2643,7 +2643,7 @@ async def test_async_responses_stream_method_with_tools_with_content_recording(s "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*", "function": {"name": "get_weather", "arguments": "*"}}}]}]', }, }, ] @@ -2658,7 +2658,7 @@ async def test_async_responses_stream_method_with_tools_with_content_recording(s "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*", "output": {"temperature": "65°F", "condition": "cloudy"}}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*", "output": {"temperature": "65°F", "condition": "cloudy"}}}]}]', }, }, { @@ -2781,7 +2781,7 @@ async def test_async_responses_stream_method_with_tools_without_content_recordin "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "assistant", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "assistant", "parts": [{"type": "tool_call", "content": {"type": "function_call", "id": "*"}}]}]', }, }, ] @@ -2796,7 +2796,7 @@ async def test_async_responses_stream_method_with_tools_without_content_recordin "attributes": { "gen_ai.provider.name": "azure.openai", # "gen_ai.message.role": "tool", # Commented out - now in event content - "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function", "id": "*"}}]}]', + "gen_ai.event.content": '[{"role": "tool", "parts": [{"type": "tool_call_output", "content": {"type": "function_call_output", "id": "*"}}]}]', }, }, {