LyricsAnalyzerAgent / api_utils.py
tonko22's picture
Update rich tool: bugifx,
7539685
raw
history blame
4.49 kB
"""
Utilities for making API calls with retry logic and error handling.
"""
import time
import random
import traceback
from loguru import logger
from litellm import completion
def make_api_call_with_retry(model: str, prompt: str) -> str:
"""
Makes an API call with a retry mechanism for error handling.
Args:
model: The model identifier to use.
prompt: The prompt text to send to the model.
Returns:
The response from the model as a string.
"""
max_attempts = 20
base_delay = 10
max_delay = 60
attempt = 0
last_exception = None
while attempt < max_attempts:
try:
# Add a small random delay to prevent simultaneous requests
jitter = random.uniform(0.1, 1.0)
time.sleep(jitter)
# If this is a retry attempt, add exponential backoff delay
if attempt > 0:
delay = min(base_delay * (2 ** (attempt - 1)), max_delay)
time.sleep(delay)
response = completion(
model=model,
messages=[{"role": "user", "content": prompt}],
num_retries=2, # Built-in retry mechanism of LiteLLM
response_format={
"type": "json_object",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string", "description": "Overall analysis of the song vibes, meaning and mood"},
"main_themes": {"type": "array", "items": {"type": "string"}, "description": "Main themes identified in the song"},
"mood": {"type": "string", "description": "The overall mood/emotion of the song"},
"sections_analysis": {
"type": "array",
"items": {
"type": "object",
"properties": {
"section_type": {"type": "string", "description": "verse/chorus/bridge/etc."},
"section_number": {"type": "integer", "description": "Sequential number of this section type"},
"lines": {"type": "array", "items": {"type": "string"}, "description": "Lyrics of this section"},
"analysis": {"type": "string", "description": "Analysis of this section with respect to the overall theme"}
},
"required": ["section_type", "section_number", "lines", "analysis"]
}
},
"conclusion": {"type": "string", "description": "The song vibes and concepts of the underlying meaning, including ideas author may have intended to express"}
},
"required": ["summary", "main_themes", "mood", "sections_analysis", "conclusion"]
}
}
)
# Try to extract the content from the response
try:
analysis_result = response.choices[0].message.content.strip()
return analysis_result
except (AttributeError, KeyError, IndexError):
try:
analysis_result = response["choices"][0]["message"]["content"].strip()
return analysis_result
except (AttributeError, KeyError, IndexError):
# If we couldn't extract the content, return an error
raise ValueError("Failed to extract content from response")
except (ConnectionError, TimeoutError) as e:
last_exception = e
logger.warning("API call failed (attempt {}/{}) for model {}: {}. Retrying...", attempt+1, max_attempts, model, str(e))
attempt += 1
continue
except Exception as e:
logger.error("Unexpected error: {}", str(e))
logger.error(traceback.format_exc())
raise # For other exceptions, we don't retry
# If all attempts failed, re-raise the last exception
if last_exception:
logger.error("All {} attempts failed. Last error: {}", max_attempts, str(last_exception))
raise last_exception