Skip to content

Commit d9008a2

Browse files
committed
fix: correct retry_on_exceptions logic to properly handle custom exceptions
The previous logic required _is_transient_error() to be true in all cases, effectively ignoring the retry_on_exceptions parameter. Now the plugin will retry if the error is either in retry_on_exceptions OR is a transient error. Added test case to verify custom exception types trigger retry correctly.
1 parent 5c22eb5 commit d9008a2

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

src/google/adk_community/plugins/llm_resilience_plugin.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,14 @@ async def on_model_error_callback(
168168
error: Exception,
169169
) -> Optional[LlmResponse]:
170170
"""Handle model errors with retry and fallback logic."""
171-
# Decide whether to handle this error
172-
if self.retry_on_exceptions is not None and not isinstance(
173-
error, self.retry_on_exceptions
174-
):
175-
# If user provided an explicit exception tuple and it doesn't match,
176-
# optionally still retry on transient HTTP-ish errors.
177-
if not _is_transient_error(error):
178-
return None
179-
else:
180-
# If user did not provide explicit list, rely on our transient heuristic
181-
if not _is_transient_error(error):
182-
# Non-transient error → don't handle
183-
return None
171+
# Decide whether to handle this error:
172+
# Retry if error is in retry_on_exceptions OR is a transient error
173+
if self.retry_on_exceptions and isinstance(error, self.retry_on_exceptions):
174+
# User explicitly wants to retry on this exception type.
175+
pass
176+
elif not _is_transient_error(error):
177+
# Not an explicit exception and not a transient error, so don't handle.
178+
return None
184179

185180
# Attempt retries on the same model
186181
response = await self._retry_same_model(

tests/unittests/plugins/test_llm_resilience_plugin.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,38 @@ class NonTransientError(RuntimeError):
183183
error=NonTransientError("boom"),
184184
)
185185
self.assertIsNone(result)
186+
187+
async def test_custom_retry_on_exceptions(self):
188+
"""Test that custom exception types in retry_on_exceptions trigger retry."""
189+
agent = LlmAgent(name="agent", model=SimpleSuccessModel())
190+
invocation_context = await create_invocation_context(agent)
191+
192+
class CustomError(Exception):
193+
pass
194+
195+
# Plugin configured to retry on CustomError (which is NOT a transient error)
196+
plugin = LlmResiliencePlugin(
197+
max_retries=2,
198+
retry_on_exceptions=(CustomError,),
199+
)
200+
201+
llm_request = LlmRequest(
202+
contents=[
203+
types.Content(
204+
role="user", parts=[types.Part.from_text(text="hello")]
205+
)
206+
]
207+
)
208+
209+
# CustomError should trigger retry even though it's not transient
210+
result = await plugin.on_model_error_callback(
211+
callback_context=invocation_context,
212+
llm_request=llm_request,
213+
error=CustomError("custom failure"),
214+
)
215+
216+
self.assertIsNotNone(result)
217+
self.assertIsInstance(result, LlmResponse)
218+
self.assertEqual(
219+
result.content.parts[0].text.strip(), "final response from mock"
220+
)

0 commit comments

Comments
 (0)