From 3b73dbc0b05162180b3bb737c26fda555c9a398f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:14:43 +0000 Subject: [PATCH] fix(utils): stop clobbering litellm.num_retries global after a completion exception --- litellm/utils.py | 28 ++-------------------------- tests/test_litellm/test_utils.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index 18b89ee0d13..1a98b6ef96f 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -1501,18 +1501,7 @@ def client(original_function): except Exception as e: call_type = original_function.__name__ if call_type == CallTypes.completion.value: - num_retries = kwargs.get("num_retries", None) or litellm.num_retries or None - if kwargs.get("retry_policy", None): - get_num_retries_from_retry_policy = getattr( - sys.modules[__name__], "get_num_retries_from_retry_policy" - ) - reset_retry_policy = getattr(sys.modules[__name__], "reset_retry_policy") - num_retries = get_num_retries_from_retry_policy( - exception=e, - retry_policy=kwargs.get("retry_policy"), - ) - kwargs["retry_policy"] = reset_retry_policy() # prevent infinite loops - litellm.num_retries = None # set retries to None to prevent infinite loops + num_retries, kwargs = _get_wrapper_num_retries(kwargs=kwargs, exception=e) context_window_fallback_dict = kwargs.get("context_window_fallback_dict", {}) _is_litellm_router_call = "model_group" in ( @@ -1540,18 +1529,7 @@ def client(original_function): kwargs["model"] = context_window_fallback_dict[model] return original_function(*args, **kwargs) elif call_type == CallTypes.responses.value: - num_retries = kwargs.get("num_retries", None) or litellm.num_retries or None - if kwargs.get("retry_policy", None): - get_num_retries_from_retry_policy = getattr( - sys.modules[__name__], "get_num_retries_from_retry_policy" - ) - reset_retry_policy = getattr(sys.modules[__name__], "reset_retry_policy") - num_retries = get_num_retries_from_retry_policy( - exception=e, - retry_policy=kwargs.get("retry_policy"), - ) - kwargs["retry_policy"] = reset_retry_policy() # prevent infinite loops - litellm.num_retries = None # set retries to None to prevent infinite loops + num_retries, kwargs = _get_wrapper_num_retries(kwargs=kwargs, exception=e) _is_litellm_router_call = "model_group" in ( kwargs.get("metadata") or {} @@ -1822,7 +1800,6 @@ def client(original_function): num_retries and not _is_litellm_router_call ): # only enter this if call is not from litellm router/proxy. router has it's own logic for retrying try: - litellm.num_retries = None # set retries to None to prevent infinite loops kwargs["num_retries"] = num_retries kwargs["original_function"] = original_function if isinstance(e, openai.RateLimitError): # rate limiting specific error @@ -1852,7 +1829,6 @@ def client(original_function): num_retries and not _is_litellm_router_call ): # only enter this if call is not from litellm router/proxy. router has it's own logic for retrying try: - litellm.num_retries = None # set retries to None to prevent infinite loops kwargs["num_retries"] = num_retries kwargs["original_function"] = original_function if isinstance(e, openai.RateLimitError): # rate limiting specific error diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 1a5c419754b..6432eade0ef 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -4737,3 +4737,26 @@ def test_gemini_image_models_do_not_support_reasoning( f"{model} incorrectly classified as reasoning-capable. " "Add 'supports_reasoning: false' to its model_cost entry." ) + + +def test_completion_exception_preserves_global_num_retries(monkeypatch): + monkeypatch.setattr(litellm, "num_retries", 2) + with pytest.raises(litellm.RateLimitError): + litellm.completion( + model="gpt-4o-mini", + messages=[{"role": "user", "content": "hi"}], + mock_response="litellm.RateLimitError", + ) + assert litellm.num_retries == 2 + + +@pytest.mark.asyncio +async def test_acompletion_exception_preserves_global_num_retries(monkeypatch): + monkeypatch.setattr(litellm, "num_retries", 2) + with pytest.raises(litellm.RateLimitError): + await litellm.acompletion( + model="gpt-4o-mini", + messages=[{"role": "user", "content": "hi"}], + mock_response="litellm.RateLimitError", + ) + assert litellm.num_retries == 2