From 16695bfa07e51ff60c85e4baec894730b331549a Mon Sep 17 00:00:00 2001 From: Julien Talbot <61507425+Julientalbot@users.noreply.github.com> Date: Tue, 26 May 2026 22:05:25 +0400 Subject: [PATCH] fix(codex): strip tool controls from summary calls (cherry picked from commit b058c25740b4a4bfa40ffbe2ce7bcddb9fe930dd) Co-authored-by: Matthieu Talbot <1246794+MartyLake@users.noreply.github.com> --- agent/chat_completion_helpers.py | 2 ++ tests/run_agent/test_run_agent.py | 42 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/agent/chat_completion_helpers.py b/agent/chat_completion_helpers.py index 5485af6ce5..f2e98cdb3b 100644 --- a/agent/chat_completion_helpers.py +++ b/agent/chat_completion_helpers.py @@ -2075,6 +2075,8 @@ def _codex_summary_attempt(agent, api_messages: list, api_request_id: str): def _attempt(retry_count: int) -> str: codex_kwargs = agent._build_api_kwargs(api_messages) codex_kwargs.pop("tools", None) + codex_kwargs.pop("tool_choice", None) + codex_kwargs.pop("parallel_tool_calls", None) return _summary_text(agent, agent._run_codex_stream(codex_kwargs)) return _attempt diff --git a/tests/run_agent/test_run_agent.py b/tests/run_agent/test_run_agent.py index a807fb7a6a..a470bf990b 100644 --- a/tests/run_agent/test_run_agent.py +++ b/tests/run_agent/test_run_agent.py @@ -2905,6 +2905,48 @@ class TestHandleMaxIterations: for item in input_items ) + def test_codex_summary_strips_tool_controls_when_tools_removed(self, agent): + 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." + captured = {} + + monkey_kwargs = { + "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): + captured.update(kwargs) + return SimpleNamespace( + status="completed", + output=[ + SimpleNamespace( + type="message", + status="completed", + content=[SimpleNamespace(type="output_text", text="Summary")], + ) + ], + ) + + with ( + patch.object(agent, "_build_api_kwargs", return_value=monkey_kwargs.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 "tools" not in captured + assert "tool_choice" not in captured + assert "parallel_tool_calls" not in captured + def test_api_sanitizer_matches_responses_call_id_when_id_differs(self, agent): messages = [ {