Spaces:
Sleeping
Sleeping
| """ | |
| API Key Rotation Manager for avoiding rate limits. | |
| Cycles through multiple Google API keys. | |
| """ | |
| import os | |
| from typing import List | |
| import threading | |
| class APIKeyRotator: | |
| """Manages rotation of multiple API keys to avoid rate limits.""" | |
| def __init__(self): | |
| self._lock = threading.Lock() | |
| self._current_index = 0 | |
| self._api_keys = self._load_api_keys() | |
| self._exhausted_keys = set() # Track rate-limited keys | |
| self._all_exhausted = False # Flag when all keys are exhausted | |
| if not self._api_keys: | |
| raise ValueError("No Google API keys found in environment") | |
| print(f"[API_ROTATOR] Loaded {len(self._api_keys)} API key(s)") | |
| def _load_api_keys(self) -> List[str]: | |
| """Load all Google API keys from environment.""" | |
| keys = [] | |
| # Primary key | |
| primary_key = os.getenv("GOOGLE_API_KEY") | |
| if primary_key: | |
| keys.append(primary_key) | |
| # Additional keys: GOOGLE_API_KEY_2, GOOGLE_API_KEY_3, etc. | |
| i = 2 | |
| while True: | |
| key = os.getenv(f"GOOGLE_API_KEY_{i}") | |
| if not key: | |
| break | |
| keys.append(key) | |
| i += 1 | |
| return keys | |
| def get_next_key(self) -> str: | |
| """Get the next API key in rotation.""" | |
| with self._lock: | |
| key = self._api_keys[self._current_index] | |
| self._current_index = (self._current_index + 1) % len(self._api_keys) | |
| # Log which key we're using (show only last 4 chars for security) | |
| key_preview = f"...{key[-4:]}" if len(key) > 4 else "****" | |
| print(f"[API_ROTATOR] Using key {self._current_index}/{len(self._api_keys)}: {key_preview}") | |
| return key | |
| def get_current_key(self) -> str: | |
| """Get the current API key without rotating.""" | |
| with self._lock: | |
| return self._api_keys[self._current_index] | |
| def mark_key_exhausted(self, key_index: int): | |
| """Mark a specific key as rate-limited/exhausted.""" | |
| with self._lock: | |
| self._exhausted_keys.add(key_index) | |
| print(f"[API_ROTATOR] ⚠️ Key {key_index + 1}/{len(self._api_keys)} marked as exhausted") | |
| # Check if all keys are now exhausted | |
| if len(self._exhausted_keys) >= len(self._api_keys): | |
| self._all_exhausted = True | |
| print(f"[API_ROTATOR] 🚨 ALL {len(self._api_keys)} Gemini keys exhausted! Switching to OpenAI.") | |
| def are_all_keys_exhausted(self) -> bool: | |
| """Check if all API keys have been exhausted.""" | |
| with self._lock: | |
| return self._all_exhausted | |
| def reset_exhaustion(self): | |
| """Reset exhaustion tracking (useful for new sessions or after cooldown).""" | |
| with self._lock: | |
| self._exhausted_keys.clear() | |
| self._all_exhausted = False | |
| print(f"[API_ROTATOR] ✓ Exhaustion tracking reset") | |
| def key_count(self) -> int: | |
| """Number of available API keys.""" | |
| return len(self._api_keys) | |
| # Global instance | |
| _rotator = None | |
| def get_api_key_rotator() -> APIKeyRotator: | |
| """Get or create the global API key rotator.""" | |
| global _rotator | |
| if _rotator is None: | |
| _rotator = APIKeyRotator() | |
| return _rotator | |
| def get_next_google_api_key() -> str: | |
| """Get the next Google API key in rotation.""" | |
| return get_api_key_rotator().get_next_key() | |