diff --git a/core/genius_client.py b/core/genius_client.py index 2da1cbca..9bbfccc6 100644 --- a/core/genius_client.py +++ b/core/genius_client.py @@ -11,32 +11,45 @@ logger = get_logger("genius_client") # Global rate limiting variables _last_api_call_time = 0 _api_call_lock = threading.Lock() -MIN_API_INTERVAL = 0.5 # 500ms between calls (Genius doesn't publish limits, be conservative) +MIN_API_INTERVAL = 1.5 # 1.5s between calls — Genius 429s at 500ms +_rate_limit_backoff = 0 # Extra backoff seconds after 429 +_rate_limit_until = 0 # Timestamp until which all calls should wait def rate_limited(func): - """Decorator to enforce rate limiting on Genius API calls""" + """Decorator to enforce rate limiting on Genius API calls with exponential backoff on 429""" @wraps(func) def wrapper(*args, **kwargs): - global _last_api_call_time + global _last_api_call_time, _rate_limit_backoff, _rate_limit_until with _api_call_lock: current_time = time.time() - time_since_last_call = current_time - _last_api_call_time + # If in backoff period from a previous 429, wait it out + if current_time < _rate_limit_until: + wait = _rate_limit_until - current_time + logger.debug(f"Genius rate limit backoff: waiting {wait:.1f}s") + time.sleep(wait) + + time_since_last_call = time.time() - _last_api_call_time if time_since_last_call < MIN_API_INTERVAL: - sleep_time = MIN_API_INTERVAL - time_since_last_call - time.sleep(sleep_time) + time.sleep(MIN_API_INTERVAL - time_since_last_call) _last_api_call_time = time.time() try: result = func(*args, **kwargs) + # Success — gradually reduce backoff + if _rate_limit_backoff > 0: + _rate_limit_backoff = max(0, _rate_limit_backoff - 5) return result except Exception as e: - if "rate limit" in str(e).lower() or "429" in str(e): - logger.warning(f"Genius rate limit hit, implementing backoff: {e}") - time.sleep(10.0) + if "429" in str(e) or "rate limit" in str(e).lower(): + # Exponential backoff: 30s → 60s → 120s (cap at 120s) + _rate_limit_backoff = min(120, max(30, _rate_limit_backoff * 2) if _rate_limit_backoff else 30) + _rate_limit_until = time.time() + _rate_limit_backoff + logger.warning(f"Genius 429 rate limit — backing off {_rate_limit_backoff}s") + time.sleep(_rate_limit_backoff) raise e return wrapper diff --git a/webui/index.html b/webui/index.html index 2cc91205..9487bf65 100644 --- a/webui/index.html +++ b/webui/index.html @@ -127,6 +127,10 @@ Settings +