Spaces:
Build error
Build error
from typing import Any, Callable | |
from tenacity import ( | |
retry, | |
retry_if_exception_type, | |
stop_after_attempt, | |
wait_exponential, | |
) | |
from openhands.core.exceptions import LLMNoResponseError | |
from openhands.core.logger import openhands_logger as logger | |
from openhands.utils.tenacity_stop import stop_if_should_exit | |
class RetryMixin: | |
"""Mixin class for retry logic.""" | |
def retry_decorator(self, **kwargs: Any) -> Callable: | |
""" | |
Create a LLM retry decorator with customizable parameters. This is used for 429 errors, and a few other exceptions in LLM classes. | |
Args: | |
**kwargs: Keyword arguments to override default retry behavior. | |
Keys: num_retries, retry_exceptions, retry_min_wait, retry_max_wait, retry_multiplier | |
Returns: | |
A retry decorator with the parameters customizable in configuration. | |
""" | |
num_retries = kwargs.get('num_retries') | |
retry_exceptions: tuple = kwargs.get('retry_exceptions', ()) | |
retry_min_wait = kwargs.get('retry_min_wait') | |
retry_max_wait = kwargs.get('retry_max_wait') | |
retry_multiplier = kwargs.get('retry_multiplier') | |
retry_listener = kwargs.get('retry_listener') | |
def before_sleep(retry_state: Any) -> None: | |
self.log_retry_attempt(retry_state) | |
if retry_listener: | |
retry_listener(retry_state.attempt_number, num_retries) | |
# Check if the exception is LLMNoResponseError | |
exception = retry_state.outcome.exception() | |
if isinstance(exception, LLMNoResponseError): | |
if hasattr(retry_state, 'kwargs'): | |
# Only change temperature if it's zero or not set | |
current_temp = retry_state.kwargs.get('temperature', 0) | |
if current_temp == 0: | |
retry_state.kwargs['temperature'] = 1.0 | |
logger.warning( | |
'LLMNoResponseError detected with temperature=0, setting temperature to 1.0 for next attempt.' | |
) | |
else: | |
logger.warning( | |
f'LLMNoResponseError detected with temperature={current_temp}, keeping original temperature' | |
) | |
retry_decorator: Callable = retry( | |
before_sleep=before_sleep, | |
stop=stop_after_attempt(num_retries) | stop_if_should_exit(), | |
reraise=True, | |
retry=( | |
retry_if_exception_type(retry_exceptions) | |
), # retry only for these types | |
wait=wait_exponential( | |
multiplier=retry_multiplier, | |
min=retry_min_wait, | |
max=retry_max_wait, | |
), | |
) | |
return retry_decorator | |
def log_retry_attempt(self, retry_state: Any) -> None: | |
"""Log retry attempts.""" | |
exception = retry_state.outcome.exception() | |
logger.error( | |
f'{exception}. Attempt #{retry_state.attempt_number} | You can customize retry values in the configuration.', | |
) | |