Spaces:
Running
Running
File size: 4,492 Bytes
ce0ec3b 7539685 ce0ec3b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
"""
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
|