fix(agent): trim usage-less empty fix to guard + observability
Drop the loop-side '(empty)' rewrite (the turn-completion explainer already owns that at delivery, and gateway/desktop match on the sentinel) and the extra token-count persistence. Keeps: usage-absent empty streaks arm the deterministic fast-fail after two attempts with no content or reasoning, and every completed API call logs even when the provider omits usage (#101898).
This commit is contained in:
@@ -8761,12 +8761,7 @@ def run_conversation(
|
||||
"answer:\n\n" + reasoning_preview
|
||||
)
|
||||
else:
|
||||
final_response = agent._format_turn_completion_explanation(
|
||||
_turn_exit_reason
|
||||
) or (
|
||||
"⚠️ No reply: the model returned empty content after "
|
||||
"all retries and fallback attempts."
|
||||
)
|
||||
final_response = "(empty)"
|
||||
break
|
||||
|
||||
# Reset retry counter/signature on successful content
|
||||
|
||||
@@ -11,7 +11,7 @@ Invariants pinned here:
|
||||
``_empty_terminal_sentinel`` marker (replay semantics unchanged).
|
||||
- Raw reasoning is NEVER promoted earlier in the ladder — a reasoning-only
|
||||
response still goes through prefill continuation first.
|
||||
- A truly empty exhaustion returns an actionable no-reply explanation.
|
||||
- A truly empty exhaustion (no reasoning either) still returns "(empty)".
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -109,11 +109,11 @@ def test_exhausted_reasoning_only_delivers_labeled_excerpt(tmp_path, monkeypatch
|
||||
)
|
||||
|
||||
|
||||
def test_exhausted_truly_empty_delivers_no_reply_explanation(tmp_path, monkeypatch):
|
||||
"""No reasoning anywhere produces an honest terminal explanation even
|
||||
when the downstream completion explainer is disabled."""
|
||||
def test_exhausted_truly_empty_keeps_existing_behavior(tmp_path, monkeypatch):
|
||||
"""No reasoning anywhere → behavior unchanged from main: the '(empty)'
|
||||
terminal (possibly rewritten by the downstream turn-completion explainer)
|
||||
is delivered, and no reasoning excerpt appears."""
|
||||
agent = _build_agent(tmp_path, monkeypatch)
|
||||
monkeypatch.setattr(agent, "_turn_completion_explainer_enabled", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
agent, "_interruptible_api_call",
|
||||
lambda api_kwargs: _truly_empty_response(),
|
||||
@@ -122,7 +122,9 @@ def test_exhausted_truly_empty_delivers_no_reply_explanation(tmp_path, monkeypat
|
||||
result = agent.run_conversation("hello?")
|
||||
|
||||
final = result["final_response"]
|
||||
assert final.startswith("⚠️ No reply:")
|
||||
# Either the raw sentinel (explainer off) or the explainer's rewrite —
|
||||
# never the reasoning-excerpt frame, which requires reasoning to exist.
|
||||
assert final == "(empty)" or final.startswith("⚠️ No reply:")
|
||||
assert "only internal reasoning" not in final
|
||||
|
||||
|
||||
|
||||
@@ -3601,16 +3601,12 @@ class TestRunConversation:
|
||||
patch.object(agent, "_persist_session"),
|
||||
patch.object(agent, "_save_trajectory"),
|
||||
patch.object(agent, "_cleanup_task_resources"),
|
||||
patch.object(
|
||||
agent, "_turn_completion_explainer_enabled", return_value=False
|
||||
),
|
||||
caplog.at_level(logging.INFO, logger="agent.conversation_loop"),
|
||||
):
|
||||
result = agent.run_conversation("answer me")
|
||||
assert result["completed"] is True
|
||||
assert result["api_calls"] == 2
|
||||
assert agent.session_api_calls == 2
|
||||
assert result["final_response"].startswith("⚠️ No reply:")
|
||||
assert caplog.text.count("usage=unavailable") == 2
|
||||
|
||||
def test_truly_empty_response_succeeds_on_nudge(self, agent):
|
||||
|
||||
Reference in New Issue
Block a user