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:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user