mirror of
https://github.com/Aider-AI/aider.git
synced 2025-12-23 08:48:18 +00:00
converted line endings from windows to unix
This commit is contained in:
parent
fc7bffaf4d
commit
bee371eae1
4 changed files with 1582 additions and 1582 deletions
74
.gitignore
vendored
74
.gitignore
vendored
|
|
@ -1,37 +1,37 @@
|
|||
# Ignore everything
|
||||
*
|
||||
|
||||
# But descend into directories
|
||||
!*/
|
||||
|
||||
# Recursively allow files under subtree
|
||||
!/.github/**
|
||||
!/aider/**
|
||||
!/benchmark/**
|
||||
!/docker/**
|
||||
!/requirements/**
|
||||
!/scripts/**
|
||||
!/tests/**
|
||||
|
||||
# Specific Files
|
||||
!/.dockerignore
|
||||
!/.flake8
|
||||
!/.gitignore
|
||||
!/.pre-commit-config.yaml
|
||||
!/CHANGELOG.md
|
||||
!/CNAME
|
||||
!/CONTRIBUTING.metadata
|
||||
!/HISTORY.md
|
||||
!/LICENSE.txt
|
||||
!/MANIFEST.in
|
||||
!/pyproject.toml
|
||||
!/pytest.ini
|
||||
!/README.md
|
||||
!/requirements.txt
|
||||
|
||||
# Ignore specific files
|
||||
aider/__version__.py
|
||||
aider/_version.py
|
||||
*.pyc
|
||||
.aider*
|
||||
env/
|
||||
# Ignore everything
|
||||
*
|
||||
|
||||
# But descend into directories
|
||||
!*/
|
||||
|
||||
# Recursively allow files under subtree
|
||||
!/.github/**
|
||||
!/aider/**
|
||||
!/benchmark/**
|
||||
!/docker/**
|
||||
!/requirements/**
|
||||
!/scripts/**
|
||||
!/tests/**
|
||||
|
||||
# Specific Files
|
||||
!/.dockerignore
|
||||
!/.flake8
|
||||
!/.gitignore
|
||||
!/.pre-commit-config.yaml
|
||||
!/CHANGELOG.md
|
||||
!/CNAME
|
||||
!/CONTRIBUTING.metadata
|
||||
!/HISTORY.md
|
||||
!/LICENSE.txt
|
||||
!/MANIFEST.in
|
||||
!/pyproject.toml
|
||||
!/pytest.ini
|
||||
!/README.md
|
||||
!/requirements.txt
|
||||
|
||||
# Ignore specific files
|
||||
aider/__version__.py
|
||||
aider/_version.py
|
||||
*.pyc
|
||||
.aider*
|
||||
env/
|
||||
|
|
|
|||
|
|
@ -1,115 +1,115 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from aider.dump import dump # noqa: F401
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExInfo:
|
||||
name: str
|
||||
retry: bool
|
||||
description: str
|
||||
|
||||
|
||||
EXCEPTIONS = [
|
||||
ExInfo("APIConnectionError", True, None),
|
||||
ExInfo("APIError", True, None),
|
||||
ExInfo("APIResponseValidationError", True, None),
|
||||
ExInfo(
|
||||
"AuthenticationError",
|
||||
False,
|
||||
"The API provider is not able to authenticate you. Check your API key.",
|
||||
),
|
||||
ExInfo("AzureOpenAIError", True, None),
|
||||
ExInfo("BadGatewayError", False, None),
|
||||
ExInfo("BadRequestError", False, None),
|
||||
ExInfo("BudgetExceededError", True, None),
|
||||
ExInfo(
|
||||
"ContentPolicyViolationError",
|
||||
True,
|
||||
"The API provider has refused the request due to a safety policy about the content.",
|
||||
),
|
||||
ExInfo("ContextWindowExceededError", False, None), # special case handled in base_coder
|
||||
ExInfo("ErrorEventError", True, None),
|
||||
ExInfo("ImageFetchError", True, "The API cannot fetch an image"),
|
||||
ExInfo("InternalServerError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("InvalidRequestError", True, None),
|
||||
ExInfo("JSONSchemaValidationError", True, None),
|
||||
ExInfo("NotFoundError", False, None),
|
||||
ExInfo("OpenAIError", True, None),
|
||||
ExInfo(
|
||||
"RateLimitError",
|
||||
True,
|
||||
"The API provider has rate limited you. Try again later or check your quotas.",
|
||||
),
|
||||
ExInfo("RouterRateLimitError", True, None),
|
||||
ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("UnprocessableEntityError", True, None),
|
||||
ExInfo("UnsupportedParamsError", True, None),
|
||||
ExInfo(
|
||||
"Timeout",
|
||||
True,
|
||||
"The API provider timed out without returning a response. They may be down or overloaded.",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class LiteLLMExceptions:
|
||||
exceptions = dict()
|
||||
exception_info = {exi.name: exi for exi in EXCEPTIONS}
|
||||
|
||||
def __init__(self):
|
||||
self._load()
|
||||
|
||||
def _load(self, strict=False):
|
||||
import litellm
|
||||
|
||||
for var in dir(litellm):
|
||||
if var.endswith("Error"):
|
||||
if var not in self.exception_info:
|
||||
raise ValueError(f"{var} is in litellm but not in aider's exceptions list")
|
||||
|
||||
for var in self.exception_info:
|
||||
ex = getattr(litellm, var, "default")
|
||||
|
||||
if ex != "default":
|
||||
if not issubclass(ex, BaseException):
|
||||
continue
|
||||
|
||||
self.exceptions[ex] = self.exception_info[var]
|
||||
|
||||
def exceptions_tuple(self):
|
||||
return tuple(self.exceptions)
|
||||
|
||||
def get_ex_info(self, ex):
|
||||
"""Return the ExInfo for a given exception instance"""
|
||||
import litellm
|
||||
|
||||
if ex.__class__ is litellm.APIConnectionError:
|
||||
if "google.auth" in str(ex):
|
||||
return ExInfo(
|
||||
"APIConnectionError", False, "You need to: pip install google-generativeai"
|
||||
)
|
||||
if "boto3" in str(ex):
|
||||
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
||||
if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
|
||||
return ExInfo(
|
||||
"APIConnectionError",
|
||||
True,
|
||||
(
|
||||
"OpenRouter or the upstream API provider is down, overloaded or rate"
|
||||
" limiting your requests."
|
||||
),
|
||||
)
|
||||
|
||||
# Check for specific non-retryable APIError cases like insufficient credits
|
||||
if ex.__class__ is litellm.APIError:
|
||||
err_str = str(ex).lower()
|
||||
if "insufficient credits" in err_str and '"code":402' in err_str:
|
||||
return ExInfo(
|
||||
"APIError",
|
||||
False,
|
||||
"Insufficient credits with the API provider. Please add credits.",
|
||||
)
|
||||
# Fall through to default APIError handling if not the specific credits error
|
||||
|
||||
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
||||
from dataclasses import dataclass
|
||||
|
||||
from aider.dump import dump # noqa: F401
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExInfo:
|
||||
name: str
|
||||
retry: bool
|
||||
description: str
|
||||
|
||||
|
||||
EXCEPTIONS = [
|
||||
ExInfo("APIConnectionError", True, None),
|
||||
ExInfo("APIError", True, None),
|
||||
ExInfo("APIResponseValidationError", True, None),
|
||||
ExInfo(
|
||||
"AuthenticationError",
|
||||
False,
|
||||
"The API provider is not able to authenticate you. Check your API key.",
|
||||
),
|
||||
ExInfo("AzureOpenAIError", True, None),
|
||||
ExInfo("BadGatewayError", False, None),
|
||||
ExInfo("BadRequestError", False, None),
|
||||
ExInfo("BudgetExceededError", True, None),
|
||||
ExInfo(
|
||||
"ContentPolicyViolationError",
|
||||
True,
|
||||
"The API provider has refused the request due to a safety policy about the content.",
|
||||
),
|
||||
ExInfo("ContextWindowExceededError", False, None), # special case handled in base_coder
|
||||
ExInfo("ErrorEventError", True, None),
|
||||
ExInfo("ImageFetchError", True, "The API cannot fetch an image"),
|
||||
ExInfo("InternalServerError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("InvalidRequestError", True, None),
|
||||
ExInfo("JSONSchemaValidationError", True, None),
|
||||
ExInfo("NotFoundError", False, None),
|
||||
ExInfo("OpenAIError", True, None),
|
||||
ExInfo(
|
||||
"RateLimitError",
|
||||
True,
|
||||
"The API provider has rate limited you. Try again later or check your quotas.",
|
||||
),
|
||||
ExInfo("RouterRateLimitError", True, None),
|
||||
ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("UnprocessableEntityError", True, None),
|
||||
ExInfo("UnsupportedParamsError", True, None),
|
||||
ExInfo(
|
||||
"Timeout",
|
||||
True,
|
||||
"The API provider timed out without returning a response. They may be down or overloaded.",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class LiteLLMExceptions:
|
||||
exceptions = dict()
|
||||
exception_info = {exi.name: exi for exi in EXCEPTIONS}
|
||||
|
||||
def __init__(self):
|
||||
self._load()
|
||||
|
||||
def _load(self, strict=False):
|
||||
import litellm
|
||||
|
||||
for var in dir(litellm):
|
||||
if var.endswith("Error"):
|
||||
if var not in self.exception_info:
|
||||
raise ValueError(f"{var} is in litellm but not in aider's exceptions list")
|
||||
|
||||
for var in self.exception_info:
|
||||
ex = getattr(litellm, var, "default")
|
||||
|
||||
if ex != "default":
|
||||
if not issubclass(ex, BaseException):
|
||||
continue
|
||||
|
||||
self.exceptions[ex] = self.exception_info[var]
|
||||
|
||||
def exceptions_tuple(self):
|
||||
return tuple(self.exceptions)
|
||||
|
||||
def get_ex_info(self, ex):
|
||||
"""Return the ExInfo for a given exception instance"""
|
||||
import litellm
|
||||
|
||||
if ex.__class__ is litellm.APIConnectionError:
|
||||
if "google.auth" in str(ex):
|
||||
return ExInfo(
|
||||
"APIConnectionError", False, "You need to: pip install google-generativeai"
|
||||
)
|
||||
if "boto3" in str(ex):
|
||||
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
||||
if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
|
||||
return ExInfo(
|
||||
"APIConnectionError",
|
||||
True,
|
||||
(
|
||||
"OpenRouter or the upstream API provider is down, overloaded or rate"
|
||||
" limiting your requests."
|
||||
),
|
||||
)
|
||||
|
||||
# Check for specific non-retryable APIError cases like insufficient credits
|
||||
if ex.__class__ is litellm.APIError:
|
||||
err_str = str(ex).lower()
|
||||
if "insufficient credits" in err_str and '"code":402' in err_str:
|
||||
return ExInfo(
|
||||
"APIError",
|
||||
False,
|
||||
"Insufficient credits with the API provider. Please add credits.",
|
||||
)
|
||||
# Fall through to default APIError handling if not the specific credits error
|
||||
|
||||
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
||||
|
|
|
|||
2666
aider/models.py
2666
aider/models.py
File diff suppressed because it is too large
Load diff
|
|
@ -1,97 +1,97 @@
|
|||
from aider.exceptions import ExInfo, LiteLLMExceptions
|
||||
|
||||
|
||||
def test_litellm_exceptions_load():
|
||||
"""Test that LiteLLMExceptions loads without errors"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert len(ex.exceptions) > 0
|
||||
|
||||
|
||||
def test_exceptions_tuple():
|
||||
"""Test that exceptions_tuple returns a non-empty tuple"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert isinstance(ex.exceptions_tuple(), tuple)
|
||||
assert len(ex.exceptions_tuple()) > 0
|
||||
|
||||
|
||||
def test_get_ex_info():
|
||||
"""Test get_ex_info returns correct ExInfo"""
|
||||
ex = LiteLLMExceptions()
|
||||
|
||||
# Test with a known exception type
|
||||
from litellm import AuthenticationError
|
||||
|
||||
auth_error = AuthenticationError(
|
||||
message="Invalid API key", llm_provider="openai", model="gpt-4"
|
||||
)
|
||||
ex_info = ex.get_ex_info(auth_error)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name == "AuthenticationError"
|
||||
assert ex_info.retry is False
|
||||
assert "API key" in ex_info.description
|
||||
|
||||
# Test with unknown exception type
|
||||
class UnknownError(Exception):
|
||||
pass
|
||||
|
||||
unknown = UnknownError()
|
||||
ex_info = ex.get_ex_info(unknown)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name is None
|
||||
assert ex_info.retry is None
|
||||
assert ex_info.description is None
|
||||
|
||||
|
||||
def test_rate_limit_error():
|
||||
"""Test specific handling of RateLimitError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import RateLimitError
|
||||
|
||||
rate_error = RateLimitError(message="Rate limit exceeded", llm_provider="openai", model="gpt-4")
|
||||
ex_info = ex.get_ex_info(rate_error)
|
||||
assert ex_info.retry is True
|
||||
assert "rate limited" in ex_info.description.lower()
|
||||
|
||||
|
||||
def test_bad_gateway_error():
|
||||
"""Test specific handling of BadGatewayError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import BadGatewayError
|
||||
|
||||
bad_gateway_error = BadGatewayError(
|
||||
message="Bad Gateway", llm_provider="openai", model="gpt-4"
|
||||
)
|
||||
ex_info = ex.get_ex_info(bad_gateway_error)
|
||||
assert ex_info.retry is True
|
||||
assert ex_info.name == "BadGatewayError"
|
||||
|
||||
|
||||
def test_context_window_error():
|
||||
"""Test specific handling of ContextWindowExceededError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import ContextWindowExceededError
|
||||
|
||||
ctx_error = ContextWindowExceededError(
|
||||
message="Context length exceeded", model="gpt-4", llm_provider="openai"
|
||||
)
|
||||
ex_info = ex.get_ex_info(ctx_error)
|
||||
assert ex_info.retry is False
|
||||
|
||||
|
||||
def test_openrouter_error():
|
||||
"""Test specific handling of OpenRouter API errors"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import APIConnectionError
|
||||
|
||||
# Create an APIConnectionError with OpenrouterException message
|
||||
openrouter_error = APIConnectionError(
|
||||
message="APIConnectionError: OpenrouterException - 'choices'",
|
||||
model="openrouter/model",
|
||||
llm_provider="openrouter",
|
||||
)
|
||||
|
||||
ex_info = ex.get_ex_info(openrouter_error)
|
||||
assert ex_info.retry is True
|
||||
assert "OpenRouter" in ex_info.description
|
||||
assert "overloaded" in ex_info.description
|
||||
assert "rate" in ex_info.description
|
||||
from aider.exceptions import ExInfo, LiteLLMExceptions
|
||||
|
||||
|
||||
def test_litellm_exceptions_load():
|
||||
"""Test that LiteLLMExceptions loads without errors"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert len(ex.exceptions) > 0
|
||||
|
||||
|
||||
def test_exceptions_tuple():
|
||||
"""Test that exceptions_tuple returns a non-empty tuple"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert isinstance(ex.exceptions_tuple(), tuple)
|
||||
assert len(ex.exceptions_tuple()) > 0
|
||||
|
||||
|
||||
def test_get_ex_info():
|
||||
"""Test get_ex_info returns correct ExInfo"""
|
||||
ex = LiteLLMExceptions()
|
||||
|
||||
# Test with a known exception type
|
||||
from litellm import AuthenticationError
|
||||
|
||||
auth_error = AuthenticationError(
|
||||
message="Invalid API key", llm_provider="openai", model="gpt-4"
|
||||
)
|
||||
ex_info = ex.get_ex_info(auth_error)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name == "AuthenticationError"
|
||||
assert ex_info.retry is False
|
||||
assert "API key" in ex_info.description
|
||||
|
||||
# Test with unknown exception type
|
||||
class UnknownError(Exception):
|
||||
pass
|
||||
|
||||
unknown = UnknownError()
|
||||
ex_info = ex.get_ex_info(unknown)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name is None
|
||||
assert ex_info.retry is None
|
||||
assert ex_info.description is None
|
||||
|
||||
|
||||
def test_rate_limit_error():
|
||||
"""Test specific handling of RateLimitError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import RateLimitError
|
||||
|
||||
rate_error = RateLimitError(message="Rate limit exceeded", llm_provider="openai", model="gpt-4")
|
||||
ex_info = ex.get_ex_info(rate_error)
|
||||
assert ex_info.retry is True
|
||||
assert "rate limited" in ex_info.description.lower()
|
||||
|
||||
|
||||
def test_bad_gateway_error():
|
||||
"""Test specific handling of BadGatewayError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import BadGatewayError
|
||||
|
||||
bad_gateway_error = BadGatewayError(
|
||||
message="Bad Gateway", llm_provider="openai", model="gpt-4"
|
||||
)
|
||||
ex_info = ex.get_ex_info(bad_gateway_error)
|
||||
assert ex_info.retry is True
|
||||
assert ex_info.name == "BadGatewayError"
|
||||
|
||||
|
||||
def test_context_window_error():
|
||||
"""Test specific handling of ContextWindowExceededError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import ContextWindowExceededError
|
||||
|
||||
ctx_error = ContextWindowExceededError(
|
||||
message="Context length exceeded", model="gpt-4", llm_provider="openai"
|
||||
)
|
||||
ex_info = ex.get_ex_info(ctx_error)
|
||||
assert ex_info.retry is False
|
||||
|
||||
|
||||
def test_openrouter_error():
|
||||
"""Test specific handling of OpenRouter API errors"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import APIConnectionError
|
||||
|
||||
# Create an APIConnectionError with OpenrouterException message
|
||||
openrouter_error = APIConnectionError(
|
||||
message="APIConnectionError: OpenrouterException - 'choices'",
|
||||
model="openrouter/model",
|
||||
llm_provider="openrouter",
|
||||
)
|
||||
|
||||
ex_info = ex.get_ex_info(openrouter_error)
|
||||
assert ex_info.retry is True
|
||||
assert "OpenRouter" in ex_info.description
|
||||
assert "overloaded" in ex_info.description
|
||||
assert "rate" in ex_info.description
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue