From 3a394210ffae0358100e9d14cf1639f764d7eab7 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:22:39 -0700 Subject: [PATCH] fix(stt/tts): widen null-subsection guards to all provider config reads Sibling sites of the salvaged #47334 fix: xai/openai/elevenlabs/gemini/ mistral/piper/neutts subsection reads in transcription_tools.py and tts_tool.py used .get(key, {}) which passes a present-but-null value through as None. All provider-subsection reads now use .get(key) or {}. Providers without a DEFAULT_CONFIG entry (e.g. stt.xai) were still receiving None even after the load_config() deep-merge fix, since the merge can only fill sections that have defaults. --- tools/transcription_tools.py | 22 +++++++++++----------- tools/tts_tool.py | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index 02380fbc433..959afc9e353 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -122,7 +122,7 @@ def _load_stt_config() -> dict: """Load the ``stt`` section from user config, falling back to defaults.""" try: from hermes_cli.config import load_config - return load_config().get("stt", {}) + return load_config().get("stt") or {} except Exception: return {} @@ -1130,7 +1130,7 @@ def _transcribe_local(file_path: str, model_name: str) -> Dict[str, Any]: # Language: config.yaml (stt.local.language) > env var > auto-detect. _forced_lang = ( - (_load_stt_config().get("local", {}) or {}).get("language") + (_load_stt_config().get("local") or {}).get("language") or os.getenv(LOCAL_STT_LANGUAGE_ENV) or None ) @@ -1213,7 +1213,7 @@ def _transcribe_local_command(file_path: str, model_name: str) -> Dict[str, Any] # Language: config.yaml (stt.local.language) > env var > "en" default. language = ( - (_load_stt_config().get("local", {}) or {}).get("language") + (_load_stt_config().get("local") or {}).get("language") or os.getenv(LOCAL_STT_LANGUAGE_ENV) or DEFAULT_LOCAL_STT_LANGUAGE ) @@ -1447,7 +1447,7 @@ def _transcribe_xai(file_path: str, model_name: str) -> Dict[str, Any]: } stt_config = _load_stt_config() - xai_config = stt_config.get("xai", {}) + xai_config = stt_config.get("xai") or {} base_url = str( xai_config.get("base_url") or get_env_value("XAI_STT_BASE_URL") @@ -1542,7 +1542,7 @@ def _transcribe_elevenlabs(file_path: str, model_name: str) -> Dict[str, Any]: return {"success": False, "transcript": "", "error": "ELEVENLABS_API_KEY not set"} stt_config = _load_stt_config() - elevenlabs_config = stt_config.get("elevenlabs", {}) or {} + elevenlabs_config = stt_config.get("elevenlabs") or {} base_url = str( elevenlabs_config.get("base_url") or get_env_value("ELEVENLABS_STT_BASE_URL") @@ -1657,14 +1657,14 @@ def transcribe_audio(file_path: str, model: Optional[str] = None) -> Dict[str, A provider = _get_provider(stt_config) if provider == "local": - local_cfg = stt_config.get("local", {}) or {} + local_cfg = stt_config.get("local") or {} model_name = _normalize_local_model( model or local_cfg.get("model", DEFAULT_LOCAL_MODEL) ) return _transcribe_local(file_path, model_name) if provider == "local_command": - local_cfg = stt_config.get("local", {}) or {} + local_cfg = stt_config.get("local") or {} model_name = _normalize_local_command_model( model or local_cfg.get("model", DEFAULT_LOCAL_MODEL) ) @@ -1675,12 +1675,12 @@ def transcribe_audio(file_path: str, model: Optional[str] = None) -> Dict[str, A return _transcribe_groq(file_path, model_name) if provider == "openai": - openai_cfg = stt_config.get("openai", {}) or {} + openai_cfg = stt_config.get("openai") or {} model_name = model or openai_cfg.get("model", DEFAULT_STT_MODEL) return _transcribe_openai(file_path, model_name) if provider == "mistral": - mistral_cfg = stt_config.get("mistral", {}) or {} + mistral_cfg = stt_config.get("mistral") or {} model_name = model or mistral_cfg.get("model", DEFAULT_MISTRAL_STT_MODEL) return _transcribe_mistral(file_path, model_name) @@ -1690,7 +1690,7 @@ def transcribe_audio(file_path: str, model: Optional[str] = None) -> Dict[str, A return _transcribe_xai(file_path, model_name) if provider == "elevenlabs": - elevenlabs_cfg = stt_config.get("elevenlabs", {}) or {} + elevenlabs_cfg = stt_config.get("elevenlabs") or {} model_name = model or elevenlabs_cfg.get("model_id", DEFAULT_ELEVENLABS_STT_MODEL) return _transcribe_elevenlabs(file_path, model_name) @@ -1754,7 +1754,7 @@ def transcribe_audio(file_path: str, model: Optional[str] = None) -> Dict[str, A def _resolve_openai_audio_client_config() -> tuple[str, str]: """Return direct OpenAI audio config or a managed gateway fallback.""" stt_config = _load_stt_config() - openai_cfg = stt_config.get("openai", {}) or {} + openai_cfg = stt_config.get("openai") or {} cfg_api_key = openai_cfg.get("api_key", "") cfg_base_url = openai_cfg.get("base_url", "") if cfg_api_key: diff --git a/tools/tts_tool.py b/tools/tts_tool.py index 758eace2472..ec7b361b8e1 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -341,7 +341,7 @@ def _load_tts_config() -> Dict[str, Any]: try: from hermes_cli.config import load_config config = load_config() - return config.get("tts", {}) + return config.get("tts") or {} except ImportError: logger.debug("hermes_cli.config not available, using default TTS config") return {} @@ -949,7 +949,7 @@ async def _generate_edge_tts(text: str, output_path: str, tts_config: Dict[str, Path to the saved audio file. """ _edge_tts = _import_edge_tts() - edge_config = tts_config.get("edge", {}) or {} + edge_config = tts_config.get("edge") or {} voice = edge_config.get("voice", DEFAULT_EDGE_VOICE) speed = float(edge_config.get("speed", tts_config.get("speed", 1.0))) @@ -982,7 +982,7 @@ def _generate_elevenlabs(text: str, output_path: str, tts_config: Dict[str, Any] if not api_key: raise ValueError("ELEVENLABS_API_KEY not set. Get one at https://elevenlabs.io/") - el_config = tts_config.get("elevenlabs", {}) + el_config = tts_config.get("elevenlabs") or {} voice_id = el_config.get("voice_id", DEFAULT_ELEVENLABS_VOICE_ID) model_id = el_config.get("model_id", DEFAULT_ELEVENLABS_MODEL_ID) @@ -1026,7 +1026,7 @@ def _generate_openai_tts(text: str, output_path: str, tts_config: Dict[str, Any] """ api_key, base_url, is_managed = _resolve_openai_audio_client_config() - oai_config = tts_config.get("openai", {}) + oai_config = tts_config.get("openai") or {} model = oai_config.get("model", DEFAULT_OPENAI_MODEL) voice = oai_config.get("voice", DEFAULT_OPENAI_VOICE) custom_base_url = oai_config.get("base_url") @@ -1204,7 +1204,7 @@ def _generate_xai_tts(text: str, output_path: str, tts_config: Dict[str, Any]) - if not api_key: raise ValueError("No xAI credentials found. Configure xAI OAuth in `hermes model` or set XAI_API_KEY.") - xai_config = tts_config.get("xai", {}) + xai_config = tts_config.get("xai") or {} voice_id = str(xai_config.get("voice_id", DEFAULT_XAI_VOICE_ID)).strip() or DEFAULT_XAI_VOICE_ID language = str(xai_config.get("language", DEFAULT_XAI_LANGUAGE)).strip() or DEFAULT_XAI_LANGUAGE sample_rate = int(xai_config.get("sample_rate", DEFAULT_XAI_SAMPLE_RATE)) @@ -1443,7 +1443,7 @@ def _generate_mistral_tts(text: str, output_path: str, tts_config: Dict[str, Any if not api_key: raise ValueError("MISTRAL_API_KEY not set. Get one at https://console.mistral.ai/") - mi_config = tts_config.get("mistral", {}) + mi_config = tts_config.get("mistral") or {} model = mi_config.get("model", DEFAULT_MISTRAL_TTS_MODEL) voice_id = mi_config.get("voice_id") or DEFAULT_MISTRAL_TTS_VOICE_ID @@ -1694,7 +1694,7 @@ def _generate_gemini_tts(text: str, output_path: str, tts_config: Dict[str, Any] "GEMINI_API_KEY not set. Get one at https://aistudio.google.com/app/apikey" ) - raw_gemini_config = tts_config.get("gemini", {}) + raw_gemini_config = tts_config.get("gemini") or {} gemini_config = raw_gemini_config if isinstance(raw_gemini_config, dict) else {} model = str(gemini_config.get("model", DEFAULT_GEMINI_TTS_MODEL)).strip() or DEFAULT_GEMINI_TTS_MODEL voice = str(gemini_config.get("voice", DEFAULT_GEMINI_TTS_VOICE)).strip() or DEFAULT_GEMINI_TTS_VOICE @@ -1858,7 +1858,7 @@ def _generate_neutts(text: str, output_path: str, tts_config: Dict[str, Any]) -> """ import sys - neutts_config = tts_config.get("neutts", {}) + neutts_config = tts_config.get("neutts") or {} ref_audio = neutts_config.get("ref_audio", "") or _default_neutts_ref_audio() ref_text = neutts_config.get("ref_text", "") or _default_neutts_ref_text() model = neutts_config.get("model", "neuphonic/neutts-air-q4-gguf") @@ -1996,7 +1996,7 @@ def _generate_piper_tts(text: str, output_path: str, tts_config: Dict[str, Any]) PiperVoice = _import_piper() import wave - piper_config = tts_config.get("piper", {}) if isinstance(tts_config, dict) else {} + piper_config = tts_config.get("piper") or {} if isinstance(tts_config, dict) else {} voice_name = piper_config.get("voice") or DEFAULT_PIPER_VOICE download_dir = Path(piper_config.get("voices_dir") or _get_piper_voices_dir()).expanduser() download_dir.mkdir(parents=True, exist_ok=True) @@ -2619,7 +2619,7 @@ def stream_tts_to_speaker( model_id = DEFAULT_ELEVENLABS_STREAMING_MODEL_ID tts_config = _load_tts_config() - el_config = tts_config.get("elevenlabs", {}) + el_config = tts_config.get("elevenlabs") or {} voice_id = el_config.get("voice_id", voice_id) model_id = el_config.get("streaming_model_id", el_config.get("model_id", model_id))