diff --git a/ai-hub/app/api/routes/stt.py b/ai-hub/app/api/routes/stt.py index a41390a..dc27526 100644 --- a/ai-hub/app/api/routes/stt.py +++ b/ai-hub/app/api/routes/stt.py @@ -48,15 +48,25 @@ # Resolve provider: User Prefs > Global Settings prefs = {} + system_prefs = services.user_service.get_system_settings(db) if user_id: user = services.user_service.get_user_by_id(db=db, user_id=user_id) if user and user.preferences: prefs = user.preferences.get("stt", {}) from app.config import settings - active_provider = provider_name or prefs.get("active_provider") or settings.STT_PROVIDER + active_provider = provider_name or prefs.get("active_provider") or system_prefs.get("stt", {}).get("active_provider") or settings.STT_PROVIDER active_prefs = prefs.get("providers", {}).get(active_provider, {}) + # --- Fallback to System Settings if personal key is missing --- + if not active_prefs or not active_prefs.get("api_key") or "*" in str(active_prefs.get("api_key")): + system_provider_prefs = system_prefs.get("stt", {}).get("providers", {}).get(active_provider, {}) + if system_provider_prefs: + # Merge but prioritize system key if personal is masked/empty + merged = system_provider_prefs.copy() + if active_prefs: merged.update({k: v for k, v in active_prefs.items() if v}) + active_prefs = merged + logger.info(f"Resolving STT. user_id={user_id}, provider={active_provider}") from app.core.providers.factory import get_stt_provider diff --git a/ai-hub/app/api/routes/tts.py b/ai-hub/app/api/routes/tts.py index c6272f7..ac34ebb 100644 --- a/ai-hub/app/api/routes/tts.py +++ b/ai-hub/app/api/routes/tts.py @@ -37,15 +37,25 @@ try: # Resolve provider: User Prefs > Global Settings prefs = {} + system_prefs = services.user_service.get_system_settings(db) if user_id: user = services.user_service.get_user_by_id(db=db, user_id=user_id) if user and user.preferences: prefs = user.preferences.get("tts", {}) from app.config import settings - active_provider = provider_name or prefs.get("active_provider") or settings.TTS_PROVIDER + active_provider = provider_name or prefs.get("active_provider") or system_prefs.get("tts", {}).get("active_provider") or settings.TTS_PROVIDER active_prefs = prefs.get("providers", {}).get(active_provider, {}) + # --- Fallback to System Settings if personal key is missing --- + if not active_prefs or not active_prefs.get("api_key") or "*" in str(active_prefs.get("api_key")): + system_provider_prefs = system_prefs.get("tts", {}).get("providers", {}).get(active_provider, {}) + if system_provider_prefs: + # Merge but prioritize system key if personal is masked/empty + merged = system_provider_prefs.copy() + if active_prefs: merged.update({k: v for k, v in active_prefs.items() if v}) + active_prefs = merged + from app.core.providers.factory import get_tts_provider # kwargs extract non-key/model/voice settings kwargs = {k: v for k, v in active_prefs.items() if k not in ["api_key", "model", "voice"]} diff --git a/ai-hub/app/core/providers/factory.py b/ai-hub/app/core/providers/factory.py index 303cdc2..e0ded0d 100644 --- a/ai-hub/app/core/providers/factory.py +++ b/ai-hub/app/core/providers/factory.py @@ -112,6 +112,12 @@ actual_key = settings.GEMINI_API_KEY elif provider_name in settings.LLM_PROVIDERS and not is_masked(settings.LLM_PROVIDERS[provider_name].get("api_key")): actual_key = settings.LLM_PROVIDERS[provider_name].get("api_key") + elif "google" in provider_name or "gemini" in provider_name: + # Final desperate search for ANY gemini key in LLM_PROVIDERS + for p, p_d in settings.LLM_PROVIDERS.items(): + if ("gemini" in p.lower() or "google" in p.lower()) and not is_masked(p_d.get("api_key")): + actual_key = p_d.get("api_key") + break # Resolve base technology type base_type = kwargs.get("provider_type") or resolve_provider_info(provider_name, "tts", _tts_registry) @@ -141,6 +147,12 @@ actual_key = settings.GEMINI_API_KEY elif provider_name in settings.LLM_PROVIDERS and not is_masked(settings.LLM_PROVIDERS[provider_name].get("api_key")): actual_key = settings.LLM_PROVIDERS[provider_name].get("api_key") + elif "google" in provider_name or "gemini" in provider_name: + # Final desperate search for ANY gemini key in LLM_PROVIDERS + for p, p_d in settings.LLM_PROVIDERS.items(): + if ("gemini" in p.lower() or "google" in p.lower()) and not is_masked(p_d.get("api_key")): + actual_key = p_d.get("api_key") + break base_type = kwargs.get("provider_type") or resolve_provider_info(provider_name, "stt", _stt_registry) diff --git a/ai-hub/app/core/providers/stt/gemini.py b/ai-hub/app/core/providers/stt/gemini.py index 9ca1bc7..db15e6e 100644 --- a/ai-hub/app/core/providers/stt/gemini.py +++ b/ai-hub/app/core/providers/stt/gemini.py @@ -15,18 +15,18 @@ def __init__(self, api_key: Optional[str] = None, model_name: str = 'gemini-1.5-flash', **kwargs): self.api_key = api_key or os.getenv('GEMINI_API_KEY') - if not self.api_key: - raise ValueError('GEMINI_API_KEY environment variable not set or provided.') - + clean_model = model_name or 'gemini-1.5-flash' model_id = clean_model.split('/')[-1] self.model_name = model_id - # Use v1beta — the only endpoint that supports audio inline_data with Gemini 2.x - self.api_url = ( - f'https://generativelanguage.googleapis.com/v1beta/models/' - f'{model_id}:generateContent?key={self.api_key}' - ) + # We construct the URL here if key exists, but we'll also check it in transcribe_audio + self.api_url = "" + if self.api_key: + self.api_url = ( + f'https://generativelanguage.googleapis.com/v1beta/models/' + f'{model_id}:generateContent?key={self.api_key}' + ) logger.debug(f"Initialized GoogleSTTProvider: model={self.model_name}") @@ -49,6 +49,9 @@ async def transcribe_audio(self, audio_data: bytes) -> str: """Transcribes audio using Gemini's inline_data approach (no Files API needed).""" + if not self.api_key: + raise HTTPException(status_code=400, detail="Gemini STT API key is not configured in settings.") + logger.debug("Starting transcription process.") mime_type = self._detect_mime(audio_data)