From 1882cf55db78be1ea52581be886badb7c5224562 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:16:33 -0700 Subject: [PATCH 1/6] Handle ConnectionError --- aider/coder.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/aider/coder.py b/aider/coder.py index c8007f33b..69ba19a14 100755 --- a/aider/coder.py +++ b/aider/coder.py @@ -8,6 +8,7 @@ from pathlib import Path import git import openai +import requests from openai.error import RateLimitError from rich.console import Console from rich.live import Live @@ -407,10 +408,13 @@ class Coder: ) break except RateLimitError as err: - retry_after = 1 self.io.tool_error(f"RateLimitError: {err}") - self.io.tool_error(f"Retry in {retry_after} seconds.") - time.sleep(retry_after) + except requests.exceptions.ConnectionError as err: + self.io.tool_error(f"ConnectionError: {err}") + + retry_after = 1 + self.io.tool_error(f"Retry in {retry_after} seconds.") + time.sleep(retry_after) self.show_send_output(completion, silent) except KeyboardInterrupt: From c27483193be9a5dfa0eda2b5d83669ce91067be5 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:18:45 -0700 Subject: [PATCH 2/6] refac --- aider/coder.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/aider/coder.py b/aider/coder.py index 69ba19a14..2252ba669 100755 --- a/aider/coder.py +++ b/aider/coder.py @@ -391,6 +391,24 @@ class Coder: return prompts.added_files.format(fnames=", ".join(mentioned_rel_fnames)) + def send_with_retries(self, model, messages): + while True: + try: + return openai.ChatCompletion.create( + model=model, + messages=messages, + temperature=0, + stream=True, + ) + except RateLimitError as err: + self.io.tool_error(f"RateLimitError: {err}") + except requests.exceptions.ConnectionError as err: + self.io.tool_error(f"ConnectionError: {err}") + + retry_after = 1 + self.io.tool_error(f"Retry in {retry_after} seconds.") + time.sleep(retry_after) + def send(self, messages, model=None, silent=False): if not model: model = self.main_model @@ -398,24 +416,7 @@ class Coder: self.resp = "" interrupted = False try: - while True: - try: - completion = openai.ChatCompletion.create( - model=model, - messages=messages, - temperature=0, - stream=True, - ) - break - except RateLimitError as err: - self.io.tool_error(f"RateLimitError: {err}") - except requests.exceptions.ConnectionError as err: - self.io.tool_error(f"ConnectionError: {err}") - - retry_after = 1 - self.io.tool_error(f"Retry in {retry_after} seconds.") - time.sleep(retry_after) - + completion = self.send_with_retries(model, messages) self.show_send_output(completion, silent) except KeyboardInterrupt: interrupted = True From 2a8e5a727c20d165e2066717a2879ea1894fcb07 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:20:56 -0700 Subject: [PATCH 3/6] aider: Added a test case for `coder.send_with_retries(model, messages)` to handle `RateLimitError` and call `time.sleep` once. # Context: USER: add a test case for coder.send_with_retries(model, messages). it should patch openai.ChatCompletion.create so that on the first call it raises openai.error import RateLimitError. and on the 2nd call it returns None. the test should confirm that time.sleep is called 1 time. ASSISTANT: To add the test case for `coder.send_with_retries(model, messages)`, we will modify the `tests/test_coder.py` file. We will create a new test method called `test_send_with_retries_rate_limit_error` and use `patch` to mock the `openai.ChatCompletion.create` and `time.sleep` methods. Here's the code change for the new test case: ```python tests/test_coder.py <<<<<<< ORIGINAL if __name__ == "__main__": unittest.main() ======= @patch("aider.coder.openai.ChatCompletion.create") @patch("aider.coder.time.sleep") def test_send_with_retries_rate_limit_error(self, mock_sleep, mock_chat_completion_create): # Mock the IO object mock_io = MagicMock() # Initialize the Coder object with the mocked IO and mocked repo coder = Coder(io=mock_io, openai_api_key="fake_key") # Set up the mock to raise RateLimitError on the first call and return None on the second call mock_chat_completion_create.side_effect = [openai.error.RateLimitError("Rate limit exceeded"), None] # Call the send_with_retries method coder.send_with_retries("model", ["message"]) # Assert that time.sleep was called once mock_sleep.assert_called_once() if __name__ == "__main__": unittest.main() >>>>>>> UPDATED ``` This test case will ensure that `send_with_retries` handles the `RateLimitError` correctly and calls `time.sleep` once before retrying. --- tests/test_coder.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_coder.py b/tests/test_coder.py index f703969e3..037049022 100644 --- a/tests/test_coder.py +++ b/tests/test_coder.py @@ -118,5 +118,23 @@ class TestCoder(unittest.TestCase): self.assertEqual(result, 'a good "commit message"') + @patch("aider.coder.openai.ChatCompletion.create") + @patch("aider.coder.time.sleep") + def test_send_with_retries_rate_limit_error(self, mock_sleep, mock_chat_completion_create): + # Mock the IO object + mock_io = MagicMock() + + # Initialize the Coder object with the mocked IO and mocked repo + coder = Coder(io=mock_io, openai_api_key="fake_key") + + # Set up the mock to raise RateLimitError on the first call and return None on the second call + mock_chat_completion_create.side_effect = [openai.error.RateLimitError("Rate limit exceeded"), None] + + # Call the send_with_retries method + coder.send_with_retries("model", ["message"]) + + # Assert that time.sleep was called once + mock_sleep.assert_called_once() + if __name__ == "__main__": unittest.main() From 3bdddf4b362d983eb78baf38174f85ab1dbe4427 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:21:33 -0700 Subject: [PATCH 4/6] added import --- tests/test_coder.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_coder.py b/tests/test_coder.py index 037049022..61b60cc79 100644 --- a/tests/test_coder.py +++ b/tests/test_coder.py @@ -2,6 +2,8 @@ import os import unittest from unittest.mock import MagicMock, patch +import openai + from aider.coder import Coder @@ -117,7 +119,6 @@ class TestCoder(unittest.TestCase): # Assert that the returned message is the expected one self.assertEqual(result, 'a good "commit message"') - @patch("aider.coder.openai.ChatCompletion.create") @patch("aider.coder.time.sleep") def test_send_with_retries_rate_limit_error(self, mock_sleep, mock_chat_completion_create): @@ -127,8 +128,12 @@ class TestCoder(unittest.TestCase): # Initialize the Coder object with the mocked IO and mocked repo coder = Coder(io=mock_io, openai_api_key="fake_key") - # Set up the mock to raise RateLimitError on the first call and return None on the second call - mock_chat_completion_create.side_effect = [openai.error.RateLimitError("Rate limit exceeded"), None] + # Set up the mock to raise RateLimitError on + # the first call and return None on the second call + mock_chat_completion_create.side_effect = [ + openai.error.RateLimitError("Rate limit exceeded"), + None, + ] # Call the send_with_retries method coder.send_with_retries("model", ["message"]) @@ -136,5 +141,6 @@ class TestCoder(unittest.TestCase): # Assert that time.sleep was called once mock_sleep.assert_called_once() + if __name__ == "__main__": unittest.main() From 2d1fc9f6da180f76472017eab6a0c93b93fc2953 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:22:33 -0700 Subject: [PATCH 5/6] aider: Added a new test case `test_send_with_retries_connection_error` to ensure that `send_with_retries` handles the `ConnectionError` correctly and calls `time.sleep` once before retrying. # Context: USER: make another test like that, but raise requests.exceptions.ConnectionError ASSISTANT: To add another test case for `coder.send_with_retries(model, messages)` that raises `requests.exceptions.ConnectionError`, we will modify the `tests/test_coder.py` file. We will create a new test method called `test_send_with_retries_connection_error` and use `patch` to mock the `openai.ChatCompletion.create` and `time.sleep` methods. Here's the code change for the new test case: ```python tests/test_coder.py <<<<<<< ORIGINAL if __name__ == "__main__": unittest.main() ======= @patch("aider.coder.openai.ChatCompletion.create") @patch("aider.coder.time.sleep") def test_send_with_retries_connection_error(self, mock_sleep, mock_chat_completion_create): # Mock the IO object mock_io = MagicMock() # Initialize the Coder object with the mocked IO and mocked repo coder = Coder(io=mock_io, openai_api_key="fake_key") # Set up the mock to raise ConnectionError on the first call and return None on the second call mock_chat_completion_create.side_effect = [requests.exceptions.ConnectionError("Connection error"), None] # Call the send_with_retries method coder.send_with_retries("model", ["message"]) # Assert that time.sleep was called once mock_sleep.assert_called_once() if __name__ == "__main__": unittest.main() >>>>>>> UPDATED ``` This test case will ensure that `send_with_retries` handles the `ConnectionError` correctly and calls `time.sleep` once before retrying. --- tests/test_coder.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_coder.py b/tests/test_coder.py index 61b60cc79..e50ac756b 100644 --- a/tests/test_coder.py +++ b/tests/test_coder.py @@ -142,5 +142,23 @@ class TestCoder(unittest.TestCase): mock_sleep.assert_called_once() + @patch("aider.coder.openai.ChatCompletion.create") + @patch("aider.coder.time.sleep") + def test_send_with_retries_connection_error(self, mock_sleep, mock_chat_completion_create): + # Mock the IO object + mock_io = MagicMock() + + # Initialize the Coder object with the mocked IO and mocked repo + coder = Coder(io=mock_io, openai_api_key="fake_key") + + # Set up the mock to raise ConnectionError on the first call and return None on the second call + mock_chat_completion_create.side_effect = [requests.exceptions.ConnectionError("Connection error"), None] + + # Call the send_with_retries method + coder.send_with_retries("model", ["message"]) + + # Assert that time.sleep was called once + mock_sleep.assert_called_once() + if __name__ == "__main__": unittest.main() From 5263367af0c17b18bbab1dff29c18e9283720f0c Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 3 Jun 2023 06:23:07 -0700 Subject: [PATCH 6/6] added import --- tests/test_coder.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_coder.py b/tests/test_coder.py index e50ac756b..eee43897e 100644 --- a/tests/test_coder.py +++ b/tests/test_coder.py @@ -3,6 +3,7 @@ import unittest from unittest.mock import MagicMock, patch import openai +import requests from aider.coder import Coder @@ -141,7 +142,6 @@ class TestCoder(unittest.TestCase): # Assert that time.sleep was called once mock_sleep.assert_called_once() - @patch("aider.coder.openai.ChatCompletion.create") @patch("aider.coder.time.sleep") def test_send_with_retries_connection_error(self, mock_sleep, mock_chat_completion_create): @@ -151,8 +151,12 @@ class TestCoder(unittest.TestCase): # Initialize the Coder object with the mocked IO and mocked repo coder = Coder(io=mock_io, openai_api_key="fake_key") - # Set up the mock to raise ConnectionError on the first call and return None on the second call - mock_chat_completion_create.side_effect = [requests.exceptions.ConnectionError("Connection error"), None] + # Set up the mock to raise ConnectionError on the first call + # and return None on the second call + mock_chat_completion_create.side_effect = [ + requests.exceptions.ConnectionError("Connection error"), + None, + ] # Call the send_with_retries method coder.send_with_retries("model", ["message"]) @@ -160,5 +164,6 @@ class TestCoder(unittest.TestCase): # Assert that time.sleep was called once mock_sleep.assert_called_once() + if __name__ == "__main__": unittest.main()