From 9e893d16d1f4bdb3c1623a547450f8b8bdcc5bee Mon Sep 17 00:00:00 2001 From: briandevans <252620095+briandevans@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:18:49 -0700 Subject: [PATCH] fix(aux): default Codex reasoning effort to medium when extra_body.reasoning.effort is falsy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit auxiliary..extra_body.reasoning, but the new translation path in _CodexCompletionsAdapter.create() reads the effort with ``reasoning_cfg.get("effort", "medium")``. That returns the configured value verbatim when the key is present, so ``effort: null`` / ``effort: ""`` (both common YAML shapes) flow through as ``{"effort": null, "summary": "auto"}`` and Codex rejects the request with "Invalid value for parameter ``reasoning.effort``". agent/transports/codex.py::build_kwargs() — which the new adapter is documented to mirror — uses a truthy check (``elif reasoning_config.get("effort"):``) so the same falsy values keep the "medium" default. Switch the auxiliary adapter to the same ``or "medium"`` truthy form so identical config produces identical requests on both paths. - [x] Two new regression tests cover ``effort: None`` and ``effort: ""`` and assert the request goes out as ``{"effort": "medium", "summary": "auto"}``. - [x] Old behaviour fails the new tests (``{'effort': None} != {'effort': 'medium'}``); fixed behaviour passes all 11 tests in the ``TestCodexAdapterReasoningTranslation`` class. - [x] Adjacent suites green: ``tests/agent/test_auxiliary_client.py`` (108 passed) and ``tests/agent/transports/test_codex_transport.py + test_chat_completions.py`` (73 passed). Co-Authored-By: Claude Opus 4.7 (1M context) --- agent/auxiliary_client.py | 7 ++++++- tests/agent/test_auxiliary_client.py | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 4c706748a0..62695af83a 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -567,7 +567,12 @@ class _CodexCompletionsAdapter: # API allows it. pass else: - effort = reasoning_cfg.get("effort", "medium") + # Truthy-only check mirrors agent/transports/codex.py + # build_kwargs(): falsy values (None, "", 0) fall back + # to the default rather than being forwarded to the + # Codex backend, which rejects e.g. {"effort": null} + # with a 400. + effort = reasoning_cfg.get("effort") or "medium" # Codex backend rejects "minimal"; clamp to "low" to # match the main-agent Codex transport behavior. if effort == "minimal": diff --git a/tests/agent/test_auxiliary_client.py b/tests/agent/test_auxiliary_client.py index 43125554df..a42b6fc877 100644 --- a/tests/agent/test_auxiliary_client.py +++ b/tests/agent/test_auxiliary_client.py @@ -1650,6 +1650,30 @@ class TestCodexAdapterReasoningTranslation: ) assert "reasoning" not in captured + def test_reasoning_effort_null_falls_back_to_medium(self): + """Parity with agent/transports/codex.py::build_kwargs() — falsy + ``effort`` (None / empty / 0) keeps the default ``medium`` instead + of being forwarded to Codex. Codex rejects ``{"effort": null}`` + with HTTP 400 (Invalid value for parameter `reasoning.effort`).""" + adapter, captured = self._build_adapter() + adapter.create( + messages=[{"role": "user", "content": "hi"}], + extra_body={"reasoning": {"effort": None}}, + ) + assert captured.get("reasoning") == {"effort": "medium", "summary": "auto"} + assert captured.get("include") == ["reasoning.encrypted_content"] + + def test_reasoning_effort_empty_string_falls_back_to_medium(self): + """Empty-string effort (e.g. ``effort: ""`` in YAML) is falsy in + the main-agent path's truthy check; mirror that here so the same + config produces the same result.""" + adapter, captured = self._build_adapter() + adapter.create( + messages=[{"role": "user", "content": "hi"}], + extra_body={"reasoning": {"effort": ""}}, + ) + assert captured.get("reasoning") == {"effort": "medium", "summary": "auto"} + assert captured.get("include") == ["reasoning.encrypted_content"] class TestVisionAutoSkipsKimiCoding: