Skip to content

Commit 240d05d

Browse files
Openai error handling for ratelimit, timeout and try again errors(fix #1255) (#1361)
* Adds error handling for openai's rate limit error in llms/openai module and its tests - Adds test for rate limit error handling in the llms/openai module - Adds error handling for rate limit error in the llms/openai module - Refactors code in llms/openai module to be readable and modular * Adds error handling for openai's timeout error in llms/openai module and its test - Adds test for timeout error handling in chat_completion in llms/openai module - Adds error handling for openai's timeout error in chat_completion in llms/openai module * Adds error handling for openai's try again error in llms/openai module and its test - Adds test for openai's try again error handling in chat_completion in llms/openai module - Adds error handling for openai's try again error in chat_completion in llms/openai module * Refactors llms/openai module and its tests to return error after retry attempts are exausted * Increases wait time for retry of chat_completion in llms/openai module * Removes unused import
1 parent ea2a0b6 commit 240d05d

File tree

2 files changed

+105
-5
lines changed

2 files changed

+105
-5
lines changed

superagi/llms/openai.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import openai
22
from openai import APIError, InvalidRequestError
3-
from openai.error import RateLimitError, AuthenticationError
3+
from openai.error import RateLimitError, AuthenticationError, Timeout, TryAgain
4+
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_random_exponential
45

56
from superagi.config.config import get_config
67
from superagi.lib.logger import logger
78
from superagi.llms.base_llm import BaseLlm
89

10+
MAX_RETRY_ATTEMPTS = 5
11+
MIN_WAIT = 30 # Seconds
12+
MAX_WAIT = 300 # Seconds
13+
14+
def custom_retry_error_callback(retry_state):
15+
logger.info("OpenAi Exception:", retry_state.outcome.exception())
16+
return {"error": "ERROR_OPENAI", "message": "Open ai exception: "+str(retry_state.outcome.exception())}
17+
918

1019
class OpenAi(BaseLlm):
1120
def __init__(self, api_key, model="gpt-4", temperature=0.6, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT"), top_p=1,
@@ -50,6 +59,17 @@ def get_model(self):
5059
"""
5160
return self.model
5261

62+
@retry(
63+
retry=(
64+
retry_if_exception_type(RateLimitError) |
65+
retry_if_exception_type(Timeout) |
66+
retry_if_exception_type(TryAgain)
67+
),
68+
stop=stop_after_attempt(MAX_RETRY_ATTEMPTS), # Maximum number of retry attempts
69+
wait=wait_random_exponential(min=MIN_WAIT, max=MAX_WAIT),
70+
before_sleep=lambda retry_state: logger.info(f"{retry_state.outcome.exception()} (attempt {retry_state.attempt_number})"),
71+
retry_error_callback=custom_retry_error_callback
72+
)
5373
def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT")):
5474
"""
5575
Call the OpenAI chat completion API.
@@ -75,12 +95,18 @@ def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT
7595
)
7696
content = response.choices[0].message["content"]
7797
return {"response": response, "content": content}
98+
except RateLimitError as api_error:
99+
logger.info("OpenAi RateLimitError:", api_error)
100+
raise RateLimitError(str(api_error))
101+
except Timeout as timeout_error:
102+
logger.info("OpenAi Timeout:", timeout_error)
103+
raise Timeout(str(timeout_error))
104+
except TryAgain as try_again_error:
105+
logger.info("OpenAi TryAgain:", try_again_error)
106+
raise TryAgain(str(try_again_error))
78107
except AuthenticationError as auth_error:
79108
logger.info("OpenAi AuthenticationError:", auth_error)
80109
return {"error": "ERROR_AUTHENTICATION", "message": "Authentication error please check the api keys: "+str(auth_error)}
81-
except RateLimitError as api_error:
82-
logger.info("OpenAi RateLimitError:", api_error)
83-
return {"error": "ERROR_RATE_LIMIT", "message": "Openai rate limit exceeded: "+str(api_error)}
84110
except InvalidRequestError as invalid_request_error:
85111
logger.info("OpenAi InvalidRequestError:", invalid_request_error)
86112
return {"error": "ERROR_INVALID_REQUEST", "message": "Openai invalid request error: "+str(invalid_request_error)}

tests/unit_tests/llms/test_open_ai.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import openai
12
import pytest
23
from unittest.mock import MagicMock, patch
3-
from superagi.llms.openai import OpenAi
4+
5+
from superagi.llms.openai import OpenAi, MAX_RETRY_ATTEMPTS
46

57

68
@patch('superagi.llms.openai.openai')
@@ -33,6 +35,78 @@ def test_chat_completion(mock_openai):
3335
)
3436

3537

38+
@patch('superagi.llms.openai.wait_random_exponential.__call__')
39+
@patch('superagi.llms.openai.openai')
40+
def test_chat_completion_retry_rate_limit_error(mock_openai, mock_wait_random_exponential):
41+
# Arrange
42+
model = 'gpt-4'
43+
api_key = 'test_key'
44+
openai_instance = OpenAi(api_key, model=model)
45+
46+
messages = [{"role": "system", "content": "You are a helpful assistant."}]
47+
max_tokens = 100
48+
49+
mock_openai.ChatCompletion.create.side_effect = openai.error.RateLimitError("Rate limit exceeded")
50+
51+
# Mock sleep time
52+
mock_wait_random_exponential.return_value = 0.1
53+
54+
# Act
55+
result = openai_instance.chat_completion(messages, max_tokens)
56+
57+
# Assert
58+
assert result == {"error": "ERROR_OPENAI", "message": "Open ai exception: Rate limit exceeded"}
59+
assert mock_openai.ChatCompletion.create.call_count == MAX_RETRY_ATTEMPTS
60+
61+
62+
@patch('superagi.llms.openai.wait_random_exponential.__call__')
63+
@patch('superagi.llms.openai.openai')
64+
def test_chat_completion_retry_timeout_error(mock_openai, mock_wait_random_exponential):
65+
# Arrange
66+
model = 'gpt-4'
67+
api_key = 'test_key'
68+
openai_instance = OpenAi(api_key, model=model)
69+
70+
messages = [{"role": "system", "content": "You are a helpful assistant."}]
71+
max_tokens = 100
72+
73+
mock_openai.ChatCompletion.create.side_effect = openai.error.Timeout("Timeout occured")
74+
75+
# Mock sleep time
76+
mock_wait_random_exponential.return_value = 0.1
77+
78+
# Act
79+
result = openai_instance.chat_completion(messages, max_tokens)
80+
81+
# Assert
82+
assert result == {"error": "ERROR_OPENAI", "message": "Open ai exception: Timeout occured"}
83+
assert mock_openai.ChatCompletion.create.call_count == MAX_RETRY_ATTEMPTS
84+
85+
86+
@patch('superagi.llms.openai.wait_random_exponential.__call__')
87+
@patch('superagi.llms.openai.openai')
88+
def test_chat_completion_retry_try_again_error(mock_openai, mock_wait_random_exponential):
89+
# Arrange
90+
model = 'gpt-4'
91+
api_key = 'test_key'
92+
openai_instance = OpenAi(api_key, model=model)
93+
94+
messages = [{"role": "system", "content": "You are a helpful assistant."}]
95+
max_tokens = 100
96+
97+
mock_openai.ChatCompletion.create.side_effect = openai.error.TryAgain("Try Again")
98+
99+
# Mock sleep time
100+
mock_wait_random_exponential.return_value = 0.1
101+
102+
# Act
103+
result = openai_instance.chat_completion(messages, max_tokens)
104+
105+
# Assert
106+
assert result == {"error": "ERROR_OPENAI", "message": "Open ai exception: Try Again"}
107+
assert mock_openai.ChatCompletion.create.call_count == MAX_RETRY_ATTEMPTS
108+
109+
36110
def test_verify_access_key():
37111
model = 'gpt-4'
38112
api_key = 'test_key'

0 commit comments

Comments
 (0)