Files
hermes-agent/tests/agent/test_turn_api_call_interrupt.py

77 lines
3.1 KiB
Python

"""Tests for ``agent/turn_api_call.py::handle_api_interrupt`` — the plain (non-redirect)
interrupt that lands mid provider call and records the streamed partial as the interrupted
assistant row."""
from __future__ import annotations
import threading
import time
from agent.agent_runtime_helpers import _INTERRUPTED_PLACEHOLDER
from agent.repetition_guard import REPETITION_LOOP_INTERRUPTED
from agent.turn_api_call import handle_api_interrupt
from agent.turn_retry_state import TurnRetryState
from run_agent import AIAgent
def _bare_agent(streamed: str) -> AIAgent:
agent = object.__new__(AIAgent)
agent._pending_redirect = None
agent._pending_redirect_lock = threading.Lock()
agent._interrupt_requested = False
agent._interrupt_message = None
agent._current_streamed_assistant_text = streamed
agent._strip_think_blocks = lambda content: content
agent.quiet_mode = True
agent.log_prefix = ""
agent.thinking_callback = None
agent._print_fn = lambda *args, **kwargs: None
agent._persist_session = lambda *args, **kwargs: None
return agent
def _interrupt(streamed: str):
messages = [{"role": "user", "content": "start"}]
verdict = handle_api_interrupt(
_bare_agent(streamed), _retry=TurnRetryState(), thinking_spinner=None, messages=messages,
conversation_history=[], api_start_time=time.time(), interrupted=False, final_response=None,
)
return messages, verdict
def test_repetition_dominated_partial_is_not_kept_as_the_interrupted_row():
"""A looped partial replayed as the interrupted assistant row re-seeds the loop on the next
turn (#112764): the row keeps the neutral placeholder and the user is told what happened."""
looped = "I. " * 1941
messages, verdict = _interrupt(looped)
# Same hidden shape as the redirect placeholder: no visible bubble in transcript replays.
assert messages[-1]["role"] == "assistant"
assert messages[-1]["content"] == ""
assert messages[-1]["display_kind"] == "hidden"
assert messages[-1]["api_content"] == _INTERRUPTED_PLACEHOLDER
assert verdict.final_response == REPETITION_LOOP_INTERRUPTED
assert "I. I. I." not in verdict.final_response
def test_distinct_batch_rows_are_not_mistaken_for_a_loop():
"""Legitimately repetitive output (distinct INSERT rows sharing a long prefix) trips the
window scan but is not a runaway loop: the partial must stay the interrupted row and must
not be relabelled as a degenerate reply."""
rows = "\n".join(
f"INSERT INTO users (id, name, email, created_at) VALUES ({i}, 'user{i}', 'user{i}@example.com', NOW());"
for i in range(12)
)
messages, verdict = _interrupt(rows)
assert (messages[-1]["role"], messages[-1]["content"]) == ("assistant", rows)
assert verdict.final_response == rows
def test_ordinary_partial_is_kept_as_the_interrupted_row():
messages, verdict = _interrupt("Visible draft.")
assert (messages[-1]["role"], messages[-1]["content"]) == ("assistant", "Visible draft.")
assert messages[-1]["display_metadata"] == {"interrupted": True}
assert verdict.final_response == "Visible draft."