Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |