Files
hermes-agent/tests/agent/test_empty_response_fallback_refund.py
Axl Ibiza, MBA bc6808fbf5 fix(agent): refund the API call when an empty response activates fallback (#77305)
Fallback activation after empty-response retries re-entered the outer loop
without refunding the provisional call, so a provider hop consumed an
iteration. Refund via _refund_api_call and carry the count back through
EmptyResponseVerdict -> FinalResponseVerdict -> s.api_call_count.

Salvaged from #88867.

Co-authored-by: RelaxJonh <92573950+RelaxJonh@users.noreply.github.com>
2026-09-24 22:27:18 +05:30

49 lines
1.8 KiB
Python

"""Empty-response fallback hop refunds its API call (#77305)."""
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from agent import turn_empty_response as ter
def _agent():
a = MagicMock()
a._current_streamed_assistant_text = ""
a._has_content_after_think_block.return_value = False
a._strip_think_blocks.side_effect = lambda t: t or ""
a._last_content_with_tools = None
a._thinking_prefill_retries = 2
a._fallback_chain = ["fallback"]
a._api_call_count = 5
return a
def _recover(agent):
msg = SimpleNamespace(reasoning=None, reasoning_content=None, reasoning_details=None)
with patch.object(ter, "_retry_empty", return_value=(None, None, False)), \
patch("agent.conversation_loop._sync_failover_system_message", return_value="sys"):
return ter.recover_empty_response(
agent, msg, None, "stop", final_response="", messages=[{"role": "user", "content": "hi"}],
api_messages=[], conversation_history=[], active_system_prompt="sys", api_call_count=5,
turn_exit_reason=None, preflight_compression_blocked=False,
)
def test_fallback_activation_refunds_call_and_budget():
agent = _agent()
agent._try_activate_fallback.return_value = True
v = _recover(agent)
assert v.action == "continue"
assert v.api_call_count == 4
assert agent._api_call_count == 4
agent.iteration_budget.refund.assert_called_once()
def test_exhausted_without_fallback_keeps_count():
agent = _agent()
agent._try_activate_fallback.return_value = False
with patch.object(ter, "_terminal_empty", return_value="(empty)"):
v = _recover(agent)
assert v.action == "break"
assert v.api_call_count == 5
agent.iteration_budget.refund.assert_not_called()