Skip to content

Commit 1016142

Browse files
test(openai-agents): Replace mocks with httpx in handoff tests (#5604)
Replace mocks with `httpx` types to avoid test failures when library internals change.
1 parent 1d4adf0 commit 1016142

File tree

1 file changed

+185
-113
lines changed

1 file changed

+185
-113
lines changed

tests/integrations/openai_agents/test_openai_agents.py

Lines changed: 185 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)