Spaces:
Running
Running
import asyncio | |
import logging | |
from contextlib import asynccontextmanager | |
from google import genai | |
import threading | |
from config import settings | |
logger = logging.getLogger(__name__) | |
class ApiKeyPool: | |
"""Manage Google API keys with round-robin selection.""" | |
def __init__(self) -> None: | |
self._keys: list[str] | None = None | |
self._index = 0 | |
self._lock = asyncio.Lock() | |
self._sync_lock = threading.Lock() | |
def _load_keys(self) -> None: | |
keys_raw = settings.gemini_api_keys | |
keys_str = keys_raw.get_secret_value() | |
keys = [k.strip() for k in keys_str.split(',') if k.strip()] if keys_str else [] | |
if not keys: | |
msg = "Google API keys are not configured or invalid" | |
logger.error(msg) | |
raise ValueError(msg) | |
self._keys = keys | |
async def get_key(self) -> str: | |
async with self._lock: | |
if self._keys is None: | |
self._load_keys() | |
key = self._keys[self._index] | |
self._index = (self._index + 1) % len(self._keys) | |
logger.debug("Using Google API key index %s", self._index) | |
return key | |
def get_key_sync(self) -> str: | |
"""Synchronous helper for environments without an event loop.""" | |
with self._sync_lock: | |
if self._keys is None: | |
self._load_keys() | |
key = self._keys[self._index] | |
self._index = (self._index + 1) % len(self._keys) | |
logger.debug("Using Google API key index %s", self._index) | |
return key | |
class GoogleClientFactory: | |
"""Factory for thread-safe creation of Google GenAI clients.""" | |
_pool = ApiKeyPool() | |
async def image(cls): | |
key = await cls._pool.get_key() | |
client = genai.Client(api_key=key) | |
try: | |
yield client.aio | |
finally: | |
pass | |
async def audio(cls): | |
key = await cls._pool.get_key() | |
client = genai.Client(api_key=key, http_options={"api_version": "v1alpha"}) | |
try: | |
yield client.aio | |
finally: | |
pass | |