From 2bfe9fabbc248f038a680b0ea8c2dd758da403b9 Mon Sep 17 00:00:00 2001 From: Tranquil-Flow Date: Mon, 13 Jul 2026 15:23:05 +0000 Subject: [PATCH] fix(image_gen/codex): remove unsupported tool_choice from request payload (#19505) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chatgpt.com/backend-api/codex backend 400s on every tool_choice shape for the hosted image_generation tool — it looks up tool_choice as a function name and never recognizes hosted-tool entries. Removing the field from _build_responses_payload() lets the host model decide; the instructions field nudges it toward the tool. Salvaged from PR #19979 (originally targeted the old client.responses.stream call, which no longer exists on upstream/main; the live request now flows through _build_responses_payload + httpx in _collect_image_b64). --- plugins/image_gen/openai-codex/__init__.py | 14 +++++++++----- .../image_gen/test_openai_codex_provider.py | 7 ++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/plugins/image_gen/openai-codex/__init__.py b/plugins/image_gen/openai-codex/__init__.py index d8d5ea80d97..e4e7cd0fc09 100644 --- a/plugins/image_gen/openai-codex/__init__.py +++ b/plugins/image_gen/openai-codex/__init__.py @@ -307,11 +307,15 @@ def _build_responses_payload( "background": "opaque", "partial_images": 1, }], - "tool_choice": { - "type": "allowed_tools", - "mode": "required", - "tools": [{"type": "image_generation"}], - }, + # No ``tool_choice`` is sent: the chatgpt.com/backend-api/codex backend + # rejects every shape we have for forcing the hosted ``image_generation`` + # tool. ``{"type": "allowed_tools", "mode": "required", "tools": [{"type": + # "image_generation"}]}`` (and the simpler ``{"type": "image_generation"}`` + # form) both 400 with ``Tool choice 'image_generation' not found in 'tools' + # parameter`` — the backend looks up tool_choice as a *function* name and + # never recognizes hosted-tool entries. Letting the host model decide is + # the only shape Codex currently accepts; the ``instructions`` above are + # what nudge it toward the tool. See issue #19505. "stream": True, } diff --git a/tests/plugins/image_gen/test_openai_codex_provider.py b/tests/plugins/image_gen/test_openai_codex_provider.py index 338ea97418a..e0d1f2a6e6a 100644 --- a/tests/plugins/image_gen/test_openai_codex_provider.py +++ b/tests/plugins/image_gen/test_openai_codex_provider.py @@ -149,9 +149,10 @@ class TestGenerate: assert captured["input"][0]["type"] == "message" assert captured["input"][0]["role"] == "user" assert captured["input"][0]["content"][0]["type"] == "input_text" - assert captured["tool_choice"]["type"] == "allowed_tools" - assert captured["tool_choice"]["mode"] == "required" - assert captured["tool_choice"]["tools"] == [{"type": "image_generation"}] + # Regression for #19505: the Codex backend 400s on every tool_choice + # shape we have for the hosted ``image_generation`` tool, so the + # provider must omit tool_choice entirely and rely on instructions. + assert "tool_choice" not in captured tool = captured["tools"][0] assert tool["type"] == "image_generation"