From 658e6c885d40a2b27a5c3f35bb6276fa5801daf8 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 24 Sep 2026 23:19:53 -0500 Subject: [PATCH] revert(agent): drop the no-op side-channel length-stop change The reasoning half only touched a docstring, and its tests pinned main's existing continuation (reasoning-off retry, then the 'No visible answer' ceiling). Nothing changed, so the PR stays on the Windows close/stop fix. --- agent/turn_truncation.py | 4 +- ...length_continuation_thinking_exhaustion.py | 54 ------------------- 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/agent/turn_truncation.py b/agent/turn_truncation.py index 6405bd97cc..61c44e56fc 100644 --- a/agent/turn_truncation.py +++ b/agent/turn_truncation.py @@ -246,9 +246,7 @@ def _abort_reason(agent: Any, content: Any, has_tool_calls: bool) -> Optional[tu """``(vprint, user response, error)`` when continuation must NOT be attempted: thinking exhausted the budget (reasoning blocks with no visible text after them — ``content=None`` from non- models is normal truncation), or a repetition loop - burned the budget on one fragment (reasoning stripped first). A length stop whose - only text is a reasoning side channel is unfinished thought: continuation owns it. - """ + burned the budget on one fragment (reasoning stripped first).""" if has_tool_calls: return None if content and _THINK_TAG_RE.search(content) and not agent._has_content_after_think_block(content): diff --git a/tests/agent/test_length_continuation_thinking_exhaustion.py b/tests/agent/test_length_continuation_thinking_exhaustion.py index 90d33d0493..2c987b3573 100644 --- a/tests/agent/test_length_continuation_thinking_exhaustion.py +++ b/tests/agent/test_length_continuation_thinking_exhaustion.py @@ -142,27 +142,6 @@ def _thinking_only_length_response(): ) -def _side_channel_length_response(*, reasoning=None, reasoning_content=None, content=""): - """finish_reason=length, empty visible content, reasoning parked off to - the side. Model id is irrelevant — the field is the signal.""" - from tests.agent.test_run_agent import _mock_assistant_msg - - return SimpleNamespace( - id="chatcmpl-reasoning-side-channel", - model="test/model", - choices=[SimpleNamespace( - index=0, - message=_mock_assistant_msg( - content=content, - reasoning=reasoning, - reasoning_content=reasoning_content, - ), - finish_reason=FINISH_REASON_LENGTH, - )], - usage=None, - ) - - def _full_response(content): from tests.agent.test_run_agent import _mock_response @@ -341,36 +320,3 @@ class TestReasoningOffReachesTheWire: calls = loop_agent.client.chat.completions.create.call_args_list first = (calls[0].kwargs.get("extra_body") or {}).get("reasoning") assert first == {"enabled": True, "effort": "high"}, first - - -class TestReasoningSideChannelLengthContinues: - """finish_reason=length with nothing visible and a reasoning side channel is - unfinished thought. Continuation owns it; the side channel is not the answer. - """ - - @pytest.mark.parametrize("field", ["reasoning", "reasoning_content"]) - def test_empty_visible_content_continues(self, loop_agent, field): - kwargs = {field: "the model spent the whole budget here"} - loop_agent.client.chat.completions.create.side_effect = [ - _side_channel_length_response(**kwargs), - _full_response("the visible answer"), - ] - result = _run(loop_agent, "write me a long report") - - assert result["completed"] is True - assert "the visible answer" in (result["final_response"] or "") - assert "the model spent the whole budget here" not in (result["final_response"] or "") - assert loop_agent.client.chat.completions.create.call_count == 2 - - def test_visible_answer_with_reasoning_still_continues(self, loop_agent): - loop_agent.client.chat.completions.create.side_effect = [ - _side_channel_length_response( - content="partial answer ", - reasoning="some leftover reasoning", - ), - _full_response("and the rest."), - ] - result = _run(loop_agent, "write me a long report") - - assert result["completed"] is True - assert "partial answer" in (result["final_response"] or "")