@@ -1077,3 +1077,110 @@ def test_openai_agents_message_role_mapping(sentry_init, capture_events):
10771077 # Verify no "ai" roles remain in any message
10781078 for message in stored_messages :
10791079 assert message ["role" ] != "ai"
1080+
1081+
1082+ @pytest .mark .asyncio
1083+ async def test_tool_execution_error_tracing (sentry_init , capture_events , test_agent ):
1084+ """
1085+ Test that tool execution errors are properly tracked via error tracing patch.
1086+
1087+ This tests the patch of agents error tracing function to ensure execute_tool
1088+ spans are set to error status when tool execution fails.
1089+
1090+ The function location varies by version:
1091+ - Newer versions: agents.util._error_tracing.attach_error_to_current_span
1092+ - Older versions: agents._utils.attach_error_to_current_span
1093+ """
1094+
1095+ @agents .function_tool
1096+ def failing_tool (message : str ) -> str :
1097+ """A tool that fails"""
1098+ raise ValueError ("Tool execution failed" )
1099+
1100+ # Create agent with the failing tool
1101+ agent_with_tool = test_agent .clone (tools = [failing_tool ])
1102+
1103+ with patch .dict (os .environ , {"OPENAI_API_KEY" : "test-key" }):
1104+ with patch (
1105+ "agents.models.openai_responses.OpenAIResponsesModel.get_response"
1106+ ) as mock_get_response :
1107+ # Create a mock response that includes tool call
1108+ tool_call = ResponseFunctionToolCall (
1109+ id = "call_123" ,
1110+ call_id = "call_123" ,
1111+ name = "failing_tool" ,
1112+ type = "function_call" ,
1113+ arguments = '{"message": "test"}' ,
1114+ function = MagicMock (
1115+ name = "failing_tool" , arguments = '{"message": "test"}'
1116+ ),
1117+ )
1118+
1119+ # First response with tool call
1120+ tool_response = ModelResponse (
1121+ output = [tool_call ],
1122+ usage = Usage (
1123+ requests = 1 , input_tokens = 10 , output_tokens = 5 , total_tokens = 15
1124+ ),
1125+ response_id = "resp_tool_123" ,
1126+ )
1127+
1128+ # Second response after tool error (agents library handles the error and continues)
1129+ final_response = ModelResponse (
1130+ output = [
1131+ ResponseOutputMessage (
1132+ id = "msg_final" ,
1133+ type = "message" ,
1134+ status = "completed" ,
1135+ content = [
1136+ ResponseOutputText (
1137+ text = "An error occurred while running the tool" ,
1138+ type = "output_text" ,
1139+ annotations = [],
1140+ )
1141+ ],
1142+ role = "assistant" ,
1143+ )
1144+ ],
1145+ usage = Usage (
1146+ requests = 1 , input_tokens = 15 , output_tokens = 10 , total_tokens = 25
1147+ ),
1148+ response_id = "resp_final_123" ,
1149+ )
1150+
1151+ mock_get_response .side_effect = [tool_response , final_response ]
1152+
1153+ sentry_init (
1154+ integrations = [OpenAIAgentsIntegration ()],
1155+ traces_sample_rate = 1.0 ,
1156+ send_default_pii = True ,
1157+ )
1158+
1159+ events = capture_events ()
1160+
1161+ # Note: The agents library catches tool exceptions internally,
1162+ # so we don't expect this to raise
1163+ await agents .Runner .run (
1164+ agent_with_tool ,
1165+ "Please use the failing tool" ,
1166+ run_config = test_run_config ,
1167+ )
1168+
1169+ (transaction ,) = events
1170+ spans = transaction ["spans" ]
1171+
1172+ # Find the execute_tool span
1173+ execute_tool_span = None
1174+ for span in spans :
1175+ if span .get ("description" , "" ).startswith ("execute_tool failing_tool" ):
1176+ execute_tool_span = span
1177+ break
1178+
1179+ # Verify the execute_tool span was created
1180+ assert execute_tool_span is not None , "execute_tool span was not created"
1181+ assert execute_tool_span ["description" ] == "execute_tool failing_tool"
1182+ assert execute_tool_span ["data" ]["gen_ai.tool.name" ] == "failing_tool"
1183+
1184+ # Verify error status was set (this is the key test for our patch)
1185+ # The span should be marked as error because the tool execution failed
1186+ assert execute_tool_span ["tags" ]["status" ] == "error"
0 commit comments