From 5b57bf3dd048f66f3b83e83d6044d9255b9fea7b Mon Sep 17 00:00:00 2001 From: hermes Date: Sun, 15 Mar 2026 12:08:30 -0400 Subject: [PATCH] [loop-cycle-50] fix: agent retry uses exponential backoff instead of fixed 1s delay (#174) (#181) --- src/timmy/agents/base.py | 2 +- tests/timmy/test_agent_retry.py | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/timmy/agents/base.py b/src/timmy/agents/base.py index a4d64dac..0ea9ea87 100644 --- a/src/timmy/agents/base.py +++ b/src/timmy/agents/base.py @@ -145,7 +145,7 @@ class BaseAgent(ABC): max_retries, exc, ) - await asyncio.sleep(1) + await asyncio.sleep(min(2 ** (attempt - 1), 8)) else: logger.error( "Agent run failed after %d attempts: %s", diff --git a/tests/timmy/test_agent_retry.py b/tests/timmy/test_agent_retry.py index c2511f3c..dfa5d1e1 100644 --- a/tests/timmy/test_agent_retry.py +++ b/tests/timmy/test_agent_retry.py @@ -1,6 +1,6 @@ """Tests for agent retry logic on transient errors.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import httpx import pytest @@ -42,11 +42,16 @@ async def test_run_retries_on_transient_error(sub_agent): sub_agent.agent.run = mock_run # Act - result = await sub_agent.run("test message") + with patch("timmy.agents.base.asyncio.sleep") as mock_sleep: + result = await sub_agent.run("test message") # Assert assert result == "Success after retries" assert call_count == 3 # 2 failures + 1 success + # Verify exponential backoff: attempt 1 = 1s, attempt 2 = 2s + assert mock_sleep.call_count == 2 + mock_sleep.assert_any_call(1) + mock_sleep.assert_any_call(2) @pytest.mark.asyncio @@ -56,11 +61,16 @@ async def test_run_exhausts_retries(sub_agent): sub_agent.agent.run.side_effect = Exception("Ollama 500 error: XML parse error") # Act & Assert - with pytest.raises(Exception, match="Ollama 500 error: XML parse error"): - await sub_agent.run("test message") + with patch("timmy.agents.base.asyncio.sleep") as mock_sleep: + with pytest.raises(Exception, match="Ollama 500 error: XML parse error"): + await sub_agent.run("test message") # Should have been called 3 times (max retries) assert sub_agent.agent.run.call_count == 3 + # Verify exponential backoff: attempt 1 = 1s, attempt 2 = 2s + assert mock_sleep.call_count == 2 + mock_sleep.assert_any_call(1) + mock_sleep.assert_any_call(2) @pytest.mark.asyncio @@ -114,10 +124,13 @@ async def test_run_logs_retry_attempts(sub_agent, caplog): sub_agent.agent.run = mock_run # Act - result = await sub_agent.run("test message") + with patch("timmy.agents.base.asyncio.sleep") as mock_sleep: + result = await sub_agent.run("test message") # Assert assert result == "Success" assert call_count == 2 # 1 failure + 1 success assert "Agent run failed on attempt 1/3" in caplog.text assert "Retrying..." in caplog.text + # Verify sleep was called with 1s for first attempt + mock_sleep.assert_called_once_with(1)