fix(reasoning): retry after a mandatory-reasoning 400 resends the user's own effort

The retry must land on the same provider cache key as every prior request
in the session. Discard only the one-shot continuation disable and send
agent.reasoning_config verbatim; a config that is itself a disable is
omitted (that session never sent anything else, so nothing warm is lost).

Live: user effort=high, ephemeral disable → 400 → retry carries
{enabled: true, effort: high}.
This commit is contained in:
Teknium
2026-09-03 04:14:15 -07:00
parent f6bd1633f7
commit 63279301bc
2 changed files with 22 additions and 16 deletions

View File

@@ -1978,14 +1978,20 @@ def _reasoning_config_for_wire(agent):
applies its own default.
"""
cfg = agent.reasoning_config
if _consume_ephemeral_reasoning_off(agent):
ephemeral_off = _consume_ephemeral_reasoning_off(agent)
if getattr(agent, "_reasoning_disable_rejected", False):
# The route rejects disables. Resend exactly what the session has
# been sending — the user's own config — so the retry lands on the
# same provider cache key as every prior request. Only a config that
# is itself a disable is dropped (omitted → route default), and that
# session has never sent anything else, so nothing warm is lost.
if isinstance(cfg, dict) and (
cfg.get("enabled") is False or cfg.get("effort") == "none"
):
return None
return cfg
if ephemeral_off:
cfg = {**(cfg or {}), "enabled": False, "effort": "none"}
if (
getattr(agent, "_reasoning_disable_rejected", False)
and isinstance(cfg, dict)
and (cfg.get("enabled") is False or cfg.get("effort") == "none")
):
return None
return cfg

View File

@@ -68,21 +68,21 @@ class TestReasoningOffOneShotOverride:
cfg = _reasoning_config_for_wire(agent)
assert cfg == {"enabled": False, "effort": "none"}
def test_rejected_disable_drops_every_disable_but_keeps_enabled(self):
"""After a 'reasoning is mandatory' 400, no disable reaches the wire —
neither the configured one nor the one-shot continuation override —
while an enabled config still passes through."""
def test_rejected_disable_resends_users_config_verbatim(self):
"""After a 'reasoning is mandatory' 400 the retry must land on the
SAME provider cache key as every prior request: the ephemeral
continuation override is discarded and the user's own config goes
out unchanged. A config that is itself a disable is omitted."""
from agent.chat_completion_helpers import _reasoning_config_for_wire
agent = _AgentStandIn({"enabled": False})
agent = _AgentStandIn({"enabled": True, "effort": "high"})
agent._reasoning_disable_rejected = True
assert _reasoning_config_for_wire(agent) is None
agent._ephemeral_reasoning_off = True
assert _reasoning_config_for_wire(agent) is None
assert _reasoning_config_for_wire(agent) == {"enabled": True, "effort": "high"}
assert agent._ephemeral_reasoning_off is False
agent.reasoning_config = {"enabled": True, "effort": "high"}
assert _reasoning_config_for_wire(agent) == {"enabled": True, "effort": "high"}
agent.reasoning_config = {"enabled": False}
assert _reasoning_config_for_wire(agent) is None
@pytest.fixture()