mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-01 02:05:00 +00:00
129 lines
3.3 KiB
Python
129 lines
3.3 KiB
Python
import hashlib
|
|
import json
|
|
import time
|
|
|
|
import backoff
|
|
|
|
from aider.dump import dump # noqa: F401
|
|
from aider.llm import litellm
|
|
|
|
# from diskcache import Cache
|
|
|
|
|
|
CACHE_PATH = "~/.aider.send.cache.v1"
|
|
CACHE = None
|
|
# CACHE = Cache(CACHE_PATH)
|
|
|
|
RETRY_TIMEOUT = 60
|
|
|
|
|
|
def retry_exceptions():
|
|
import httpx
|
|
import openai
|
|
|
|
return (
|
|
# httpx
|
|
httpx.ConnectError,
|
|
httpx.RemoteProtocolError,
|
|
httpx.ReadTimeout,
|
|
#
|
|
# litellm exceptions inherit from openai exceptions
|
|
# https://docs.litellm.ai/docs/exception_mapping
|
|
#
|
|
# openai.BadRequestError,
|
|
# litellm.ContextWindowExceededError,
|
|
# litellm.ContentPolicyViolationError,
|
|
#
|
|
# openai.AuthenticationError,
|
|
# openai.PermissionDeniedError,
|
|
# openai.NotFoundError,
|
|
#
|
|
openai.APITimeoutError,
|
|
openai.UnprocessableEntityError,
|
|
openai.RateLimitError,
|
|
openai.APIConnectionError,
|
|
openai.APIError,
|
|
openai.APIStatusError,
|
|
openai.InternalServerError,
|
|
)
|
|
|
|
|
|
def lazy_litellm_retry_decorator(func):
|
|
def wrapper(*args, **kwargs):
|
|
decorated_func = backoff.on_exception(
|
|
backoff.expo,
|
|
retry_exceptions(),
|
|
max_time=RETRY_TIMEOUT,
|
|
on_backoff=lambda details: print(
|
|
f"{details.get('exception', 'Exception')}\nRetry in {details['wait']:.1f} seconds."
|
|
),
|
|
)(func)
|
|
return decorated_func(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
|
|
def send_completion(
|
|
model_name,
|
|
messages,
|
|
functions,
|
|
stream,
|
|
temperature=0,
|
|
extra_params=None,
|
|
):
|
|
kwargs = dict(
|
|
model=model_name,
|
|
messages=messages,
|
|
stream=stream,
|
|
)
|
|
if temperature is not None:
|
|
kwargs["temperature"] = temperature
|
|
|
|
if functions is not None:
|
|
function = functions[0]
|
|
kwargs["tools"] = [dict(type="function", function=function)]
|
|
kwargs["tool_choice"] = {"type": "function", "function": {"name": function["name"]}}
|
|
|
|
if extra_params is not None:
|
|
kwargs.update(extra_params)
|
|
|
|
key = json.dumps(kwargs, sort_keys=True).encode()
|
|
|
|
# Generate SHA1 hash of kwargs and append it to chat_completion_call_hashes
|
|
hash_object = hashlib.sha1(key)
|
|
|
|
if not stream and CACHE is not None and key in CACHE:
|
|
return hash_object, CACHE[key]
|
|
|
|
res = litellm.completion(**kwargs)
|
|
|
|
if not stream and CACHE is not None:
|
|
CACHE[key] = res
|
|
|
|
return hash_object, res
|
|
|
|
|
|
def simple_send_with_retries(model_name, messages, extra_params=None):
|
|
retry_delay = 0.125
|
|
while True:
|
|
try:
|
|
kwargs = {
|
|
"model_name": model_name,
|
|
"messages": messages,
|
|
"functions": None,
|
|
"stream": False,
|
|
"extra_params": extra_params,
|
|
}
|
|
|
|
_hash, response = send_completion(**kwargs)
|
|
return response.choices[0].message.content
|
|
except retry_exceptions() as err:
|
|
print(str(err))
|
|
retry_delay *= 2
|
|
if retry_delay > RETRY_TIMEOUT:
|
|
break
|
|
print(f"Retrying in {retry_delay:.1f} seconds...")
|
|
time.sleep(retry_delay)
|
|
continue
|
|
except AttributeError:
|
|
return
|