test(agent): cover the codex iteration-summary retry path

Iteration-limit summaries retry once when the first attempt comes back empty.
Both attempts share one builder closure, so assert the retry body is as
tool-free as the first: forces an empty summary, then checks every captured
codex request for tools / tool_choice / parallel_tool_calls.

Addresses the retry-coverage request on #32777.
This commit is contained in:
trnorga
2026-09-11 19:20:40 -07:00
committed by kshitij
parent 16695bfa07
commit e76ce0b67f
+52
View File
@@ -2947,6 +2947,58 @@ class TestHandleMaxIterations:
assert "tool_choice" not in captured
assert "parallel_tool_calls" not in captured
def test_codex_summary_retry_also_strips_tool_controls(self, agent):
"""The retry attempt must be as toolless as the first one.
Iteration-limit summaries retry once when the first summary comes back empty; both
attempts build their body through the same codex path, so both must drop the tool-control
block the transport emits alongside ``tools`` (agent/transports/codex.py).
"""
agent.api_mode = "codex_responses"
agent.provider = "openai-codex"
agent.base_url = "https://chatgpt.com/backend-api/codex"
agent._base_url_lower = agent.base_url.lower()
agent._base_url_hostname = "chatgpt.com"
agent.model = "gpt-5.5"
agent._cached_system_prompt = "You are helpful."
bodies = []
tool_laden = {
"model": "gpt-5.5",
"input": [{"role": "user", "content": "do stuff"}],
"tools": [{"type": "function", "name": "web_search"}],
"tool_choice": "auto",
"parallel_tool_calls": True,
}
def fake_run_codex_stream(kwargs):
bodies.append(dict(kwargs))
# First attempt returns an empty summary so the caller takes the retry path.
text = "" if len(bodies) == 1 else "Summary"
return SimpleNamespace(
status="completed",
output=[
SimpleNamespace(
type="message",
status="completed",
content=[SimpleNamespace(type="output_text", text=text)],
)
],
)
with (
patch.object(agent, "_build_api_kwargs", return_value=tool_laden.copy()),
patch.object(agent, "_run_codex_stream", side_effect=fake_run_codex_stream),
):
result = agent._handle_max_iterations([{"role": "user", "content": "do stuff"}], 90)
assert result == "Summary"
assert len(bodies) == 2, f"expected one retry after the empty summary, got {len(bodies)} attempts"
for attempt_index, sent in enumerate(bodies):
assert "tools" not in sent, f"attempt {attempt_index}: tools leaked into the summary call"
assert "tool_choice" not in sent, f"attempt {attempt_index}: tool_choice leaked"
assert "parallel_tool_calls" not in sent, f"attempt {attempt_index}: parallel_tool_calls leaked"
def test_api_sanitizer_matches_responses_call_id_when_id_differs(self, agent):
messages = [
{