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