This commit is contained in:
commit
55ec1d663c
@ -50,6 +50,8 @@ def load_config():
|
||||
os.getenv("CLOVER_MAX_HISTORY_MESSAGES", "20")
|
||||
),
|
||||
"max_history_tokens": int(os.getenv("CLOVER_MAX_HISTORY_TOKENS", "8000")),
|
||||
"max_retries": int(os.getenv("CLOVER_MAX_RETRIES", "3")),
|
||||
"retry_base_delay": float(os.getenv("CLOVER_RETRY_BASE_DELAY", "2")),
|
||||
}
|
||||
|
||||
# Debug output if enabled
|
||||
|
||||
@ -5,6 +5,7 @@ Handles communication with the OpenAI-compatible LLM server
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import requests
|
||||
@ -55,7 +56,10 @@ class APIClient:
|
||||
self, endpoint: str, method: str = "GET", data: Optional[Dict] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Make a request to the LLM API server
|
||||
Make a request to the LLM API server with retry and exponential backoff.
|
||||
|
||||
Retries up to 3 times with exponential backoff (2s base delay) for
|
||||
transient failures (5xx errors, connection errors, timeouts).
|
||||
|
||||
Uses requests.Session with explicit SSL verification control.
|
||||
|
||||
@ -67,33 +71,74 @@ class APIClient:
|
||||
Returns:
|
||||
dict: Response from the API
|
||||
"""
|
||||
try:
|
||||
# Ensure base_url doesn't have trailing slash and endpoint has leading slash
|
||||
base = self.base_url.rstrip("/")
|
||||
endpoint = endpoint if endpoint.startswith("/") else f"/{endpoint}"
|
||||
url = f"{base}{endpoint}"
|
||||
max_retries = self.config.get("max_retries", 3)
|
||||
base_delay = self.config.get("retry_base_delay", 2)
|
||||
|
||||
kwargs = {
|
||||
"timeout": self.config.get("timeout", 300),
|
||||
"verify": self.ssl_verify,
|
||||
last_exception = None
|
||||
|
||||
for attempt in range(max_retries + 1):
|
||||
try:
|
||||
kwargs = {
|
||||
"timeout": self.config.get("timeout", 300),
|
||||
"verify": self.ssl_verify,
|
||||
}
|
||||
|
||||
if method == "GET":
|
||||
response = self.session.get(url, **kwargs)
|
||||
elif method == "POST":
|
||||
response = self.session.post(url, json=data, **kwargs)
|
||||
else:
|
||||
raise ValueError(f"Unsupported HTTP method: {method}")
|
||||
|
||||
# Retry on 5xx server errors
|
||||
if response.status_code >= 500 and attempt < max_retries:
|
||||
delay = base_delay * (2**attempt)
|
||||
print(
|
||||
f"Server error {response.status_code}, "
|
||||
f"retrying in {delay}s (attempt {attempt + 1}/{max_retries})"
|
||||
)
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
response.raise_for_status()
|
||||
return {"success": True, "data": response.json()}
|
||||
|
||||
except requests.exceptions.Timeout as e:
|
||||
last_exception = e
|
||||
if attempt < max_retries:
|
||||
delay = base_delay * (2**attempt)
|
||||
print(
|
||||
f"Timeout, retrying in {delay}s "
|
||||
f"(attempt {attempt + 1}/{max_retries})"
|
||||
)
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
last_exception = e
|
||||
if attempt < max_retries:
|
||||
delay = base_delay * (2**attempt)
|
||||
print(
|
||||
f"Connection error, retrying in {delay}s "
|
||||
f"(attempt {attempt + 1}/{max_retries})"
|
||||
)
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return {"success": False, "error": f"HTTP Request failed: {str(e)}"}
|
||||
except json.JSONDecodeError as e:
|
||||
return {"success": False, "error": f"Invalid JSON response: {str(e)}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unexpected error: {str(e)}"}
|
||||
|
||||
# All retries exhausted
|
||||
if last_exception:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Request failed after {max_retries} retries: {str(last_exception)}",
|
||||
}
|
||||
|
||||
if method == "GET":
|
||||
response = self.session.get(url, **kwargs)
|
||||
elif method == "POST":
|
||||
response = self.session.post(url, json=data, **kwargs)
|
||||
else:
|
||||
raise ValueError(f"Unsupported HTTP method: {method}")
|
||||
|
||||
response.raise_for_status()
|
||||
return {"success": True, "data": response.json()}
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return {"success": False, "error": f"HTTP Request failed: {str(e)}"}
|
||||
except json.JSONDecodeError as e:
|
||||
return {"success": False, "error": f"Invalid JSON response: {str(e)}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Unexpected error: {str(e)}"}
|
||||
return {"success": False, "error": "Request failed after maximum retries"}
|
||||
|
||||
def list_models(self) -> Dict[str, Any]:
|
||||
"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user