@@ -986,85 +986,120 @@ def test_agent_invocation_span_sync(
986986
987987
988988@pytest .mark .asyncio
989- async def test_handoff_span (sentry_init , capture_events , mock_usage ):
989+ async def test_handoff_span (sentry_init , capture_events , get_model_response ):
990990 """
991991 Test that handoff spans are created when agents hand off to other agents.
992992 """
993+ client = AsyncOpenAI (api_key = "test-key" )
994+ model = OpenAIResponsesModel (model = "gpt-4-mini" , openai_client = client )
995+
993996 # Create two simple agents with a handoff relationship
994997 secondary_agent = agents .Agent (
995998 name = "secondary_agent" ,
996999 instructions = "You are a secondary agent." ,
997- model = "gpt-4o-mini" ,
1000+ model = model ,
9981001 )
9991002
10001003 primary_agent = agents .Agent (
10011004 name = "primary_agent" ,
10021005 instructions = "You are a primary agent that hands off to secondary agent." ,
1003- model = "gpt-4o-mini" ,
1006+ model = model ,
10041007 handoffs = [secondary_agent ],
10051008 )
10061009
1007- with patch .dict (os .environ , {"OPENAI_API_KEY" : "test-key" }):
1008- with patch (
1009- "agents.models.openai_responses.OpenAIResponsesModel.get_response"
1010- ) as mock_get_response :
1011- # Mock two responses:
1012- # 1. Primary agent calls handoff tool
1013- # 2. Secondary agent provides final response
1014- handoff_response = ModelResponse (
1015- output = [
1016- ResponseFunctionToolCall (
1017- id = "call_handoff_123" ,
1018- call_id = "call_handoff_123" ,
1019- name = "transfer_to_secondary_agent" ,
1020- type = "function_call" ,
1021- arguments = "{}" ,
1022- )
1023- ],
1024- usage = mock_usage ,
1025- response_id = "resp_handoff_123" ,
1026- )
1027-
1028- final_response = ModelResponse (
1029- output = [
1030- ResponseOutputMessage (
1031- id = "msg_final" ,
1032- type = "message" ,
1033- status = "completed" ,
1034- content = [
1035- ResponseOutputText (
1036- text = "I'm the specialist and I can help with that!" ,
1037- type = "output_text" ,
1038- annotations = [],
1039- )
1040- ],
1041- role = "assistant" ,
1042- )
1043- ],
1044- usage = mock_usage ,
1045- response_id = "resp_final_123" ,
1046- )
1010+ handoff_response = get_model_response (
1011+ Response (
1012+ id = "resp_tool_123" ,
1013+ output = [
1014+ ResponseFunctionToolCall (
1015+ id = "call_handoff_123" ,
1016+ call_id = "call_handoff_123" ,
1017+ name = "transfer_to_secondary_agent" ,
1018+ type = "function_call" ,
1019+ arguments = "{}" ,
1020+ )
1021+ ],
1022+ parallel_tool_calls = False ,
1023+ tool_choice = "none" ,
1024+ tools = [],
1025+ created_at = 10000000 ,
1026+ model = "gpt-4" ,
1027+ object = "response" ,
1028+ usage = ResponseUsage (
1029+ input_tokens = 10 ,
1030+ input_tokens_details = InputTokensDetails (
1031+ cached_tokens = 0 ,
1032+ ),
1033+ output_tokens = 20 ,
1034+ output_tokens_details = OutputTokensDetails (
1035+ reasoning_tokens = 5 ,
1036+ ),
1037+ total_tokens = 30 ,
1038+ ),
1039+ )
1040+ )
10471041
1048- mock_get_response .side_effect = [handoff_response , final_response ]
1042+ final_response = get_model_response (
1043+ Response (
1044+ id = "resp_final_123" ,
1045+ output = [
1046+ ResponseOutputMessage (
1047+ id = "msg_final" ,
1048+ type = "message" ,
1049+ status = "completed" ,
1050+ content = [
1051+ ResponseOutputText (
1052+ text = "I'm the specialist and I can help with that!" ,
1053+ type = "output_text" ,
1054+ annotations = [],
1055+ )
1056+ ],
1057+ role = "assistant" ,
1058+ )
1059+ ],
1060+ parallel_tool_calls = False ,
1061+ tool_choice = "none" ,
1062+ tools = [],
1063+ created_at = 10000000 ,
1064+ model = "gpt-4" ,
1065+ object = "response" ,
1066+ usage = ResponseUsage (
1067+ input_tokens = 10 ,
1068+ input_tokens_details = InputTokensDetails (
1069+ cached_tokens = 0 ,
1070+ ),
1071+ output_tokens = 20 ,
1072+ output_tokens_details = OutputTokensDetails (
1073+ reasoning_tokens = 5 ,
1074+ ),
1075+ total_tokens = 30 ,
1076+ ),
1077+ )
1078+ )
10491079
1050- sentry_init (
1051- integrations = [OpenAIAgentsIntegration ()],
1052- traces_sample_rate = 1.0 ,
1053- )
1080+ with patch .object (
1081+ primary_agent .model ._client ._client ,
1082+ "send" ,
1083+ side_effect = [handoff_response , final_response ],
1084+ ) as _ :
1085+ sentry_init (
1086+ integrations = [OpenAIAgentsIntegration ()],
1087+ traces_sample_rate = 1.0 ,
1088+ )
10541089
1055- events = capture_events ()
1090+ events = capture_events ()
10561091
1057- result = await agents .Runner .run (
1058- primary_agent ,
1059- "Please hand off to secondary agent" ,
1060- run_config = test_run_config ,
1061- )
1092+ result = await agents .Runner .run (
1093+ primary_agent ,
1094+ "Please hand off to secondary agent" ,
1095+ run_config = test_run_config ,
1096+ )
10621097
1063- assert result is not None
1098+ assert result is not None
10641099
10651100 (transaction ,) = events
10661101 spans = transaction ["spans" ]
1067- handoff_span = spans [ 2 ]
1102+ handoff_span = next ( span for span in spans if span . get ( "op" ) == OP . GEN_AI_HANDOFF )
10681103
10691104 # Verify handoff span was created
10701105 assert handoff_span is not None
@@ -1075,85 +1110,122 @@ async def test_handoff_span(sentry_init, capture_events, mock_usage):
10751110
10761111
10771112@pytest .mark .asyncio
1078- async def test_max_turns_before_handoff_span (sentry_init , capture_events , mock_usage ):
1113+ async def test_max_turns_before_handoff_span (
1114+ sentry_init , capture_events , get_model_response
1115+ ):
10791116 """
10801117 Example raising agents.exceptions.AgentsException after the agent invocation span is complete.
10811118 """
1119+ client = AsyncOpenAI (api_key = "test-key" )
1120+ model = OpenAIResponsesModel (model = "gpt-4-mini" , openai_client = client )
1121+
10821122 # Create two simple agents with a handoff relationship
10831123 secondary_agent = agents .Agent (
10841124 name = "secondary_agent" ,
10851125 instructions = "You are a secondary agent." ,
1086- model = "gpt-4o-mini" ,
1126+ model = model ,
10871127 )
10881128
10891129 primary_agent = agents .Agent (
10901130 name = "primary_agent" ,
10911131 instructions = "You are a primary agent that hands off to secondary agent." ,
1092- model = "gpt-4o-mini" ,
1132+ model = model ,
10931133 handoffs = [secondary_agent ],
10941134 )
10951135
1096- with patch .dict (os .environ , {"OPENAI_API_KEY" : "test-key" }):
1097- with patch (
1098- "agents.models.openai_responses.OpenAIResponsesModel.get_response"
1099- ) as mock_get_response :
1100- # Mock two responses:
1101- # 1. Primary agent calls handoff tool
1102- # 2. Secondary agent provides final response
1103- handoff_response = ModelResponse (
1104- output = [
1105- ResponseFunctionToolCall (
1106- id = "call_handoff_123" ,
1107- call_id = "call_handoff_123" ,
1108- name = "transfer_to_secondary_agent" ,
1109- type = "function_call" ,
1110- arguments = "{}" ,
1111- )
1112- ],
1113- usage = mock_usage ,
1114- response_id = "resp_handoff_123" ,
1115- )
1116-
1117- final_response = ModelResponse (
1118- output = [
1119- ResponseOutputMessage (
1120- id = "msg_final" ,
1121- type = "message" ,
1122- status = "completed" ,
1123- content = [
1124- ResponseOutputText (
1125- text = "I'm the specialist and I can help with that!" ,
1126- type = "output_text" ,
1127- annotations = [],
1128- )
1129- ],
1130- role = "assistant" ,
1131- )
1132- ],
1133- usage = mock_usage ,
1134- response_id = "resp_final_123" ,
1135- )
1136+ handoff_response = get_model_response (
1137+ Response (
1138+ id = "resp_tool_123" ,
1139+ output = [
1140+ ResponseFunctionToolCall (
1141+ id = "call_handoff_123" ,
1142+ call_id = "call_handoff_123" ,
1143+ name = "transfer_to_secondary_agent" ,
1144+ type = "function_call" ,
1145+ arguments = "{}" ,
1146+ )
1147+ ],
1148+ parallel_tool_calls = False ,
1149+ tool_choice = "none" ,
1150+ tools = [],
1151+ created_at = 10000000 ,
1152+ model = "gpt-4" ,
1153+ object = "response" ,
1154+ usage = ResponseUsage (
1155+ input_tokens = 10 ,
1156+ input_tokens_details = InputTokensDetails (
1157+ cached_tokens = 0 ,
1158+ ),
1159+ output_tokens = 20 ,
1160+ output_tokens_details = OutputTokensDetails (
1161+ reasoning_tokens = 5 ,
1162+ ),
1163+ total_tokens = 30 ,
1164+ ),
1165+ )
1166+ )
11361167
1137- mock_get_response .side_effect = [handoff_response , final_response ]
1168+ final_response = get_model_response (
1169+ Response (
1170+ id = "resp_final_123" ,
1171+ output = [
1172+ ResponseOutputMessage (
1173+ id = "msg_final" ,
1174+ type = "message" ,
1175+ status = "completed" ,
1176+ content = [
1177+ ResponseOutputText (
1178+ text = "I'm the specialist and I can help with that!" ,
1179+ type = "output_text" ,
1180+ annotations = [],
1181+ )
1182+ ],
1183+ role = "assistant" ,
1184+ )
1185+ ],
1186+ parallel_tool_calls = False ,
1187+ tool_choice = "none" ,
1188+ tools = [],
1189+ created_at = 10000000 ,
1190+ model = "gpt-4" ,
1191+ object = "response" ,
1192+ usage = ResponseUsage (
1193+ input_tokens = 10 ,
1194+ input_tokens_details = InputTokensDetails (
1195+ cached_tokens = 0 ,
1196+ ),
1197+ output_tokens = 20 ,
1198+ output_tokens_details = OutputTokensDetails (
1199+ reasoning_tokens = 5 ,
1200+ ),
1201+ total_tokens = 30 ,
1202+ ),
1203+ )
1204+ )
11381205
1139- sentry_init (
1140- integrations = [OpenAIAgentsIntegration ()],
1141- traces_sample_rate = 1.0 ,
1142- )
1206+ with patch .object (
1207+ primary_agent .model ._client ._client ,
1208+ "send" ,
1209+ side_effect = [handoff_response , final_response ],
1210+ ) as _ :
1211+ sentry_init (
1212+ integrations = [OpenAIAgentsIntegration ()],
1213+ traces_sample_rate = 1.0 ,
1214+ )
11431215
1144- events = capture_events ()
1216+ events = capture_events ()
11451217
1146- with pytest .raises (MaxTurnsExceeded ):
1147- await agents .Runner .run (
1148- primary_agent ,
1149- "Please hand off to secondary agent" ,
1150- run_config = test_run_config ,
1151- max_turns = 1 ,
1152- )
1218+ with pytest .raises (MaxTurnsExceeded ):
1219+ await agents .Runner .run (
1220+ primary_agent ,
1221+ "Please hand off to secondary agent" ,
1222+ run_config = test_run_config ,
1223+ max_turns = 1 ,
1224+ )
11531225
11541226 (error , transaction ) = events
11551227 spans = transaction ["spans" ]
1156- handoff_span = spans [ 2 ]
1228+ handoff_span = next ( span for span in spans if span . get ( "op" ) == OP . GEN_AI_HANDOFF )
11571229
11581230 # Verify handoff span was created
11591231 assert handoff_span is not None
0 commit comments