fix(tools): resolve TTS/STT provider keys through credential pool

TTS/STT providers (Mistral, ElevenLabs) only checked env vars and
.env files via get_env_value(), ignoring keys stored via
'hermes auth add mistral' / 'hermes auth add elevenlabs'.

Add _resolve_provider_key() helper that falls back to the credential
pool (agent.credential_pool.load_pool) when the env var is unset.

Affected:
- tools/transcription_tools.py: 6 sites (provider selection + auto-detect)
- tools/tts_tool.py: 6 sites (synthesis + availability check)

Fixes #68003
This commit is contained in:
sg-architect 2026-07-21 16:50:13 +08:00 committed by Teknium
parent ed591a5664
commit 79bcfc23ab
2 changed files with 59 additions and 11 deletions

View file

@ -62,6 +62,30 @@ def get_env_value(name, default=None):
value = _get_env_value(name)
return default if value is None else value
def _resolve_provider_key(env_var: str, provider_id: str) -> str:
"""Resolve an API key from env, .env, or the credential pool.
Used by TTS/STT providers (Mistral, ElevenLabs) that store keys
via ``hermes auth add <provider_id>``.
"""
key = get_env_value(env_var)
if key:
return key
try:
from agent.credential_pool import load_pool
pool = load_pool(provider_id)
if pool and pool.has_credentials():
entry = pool.peek()
if entry:
key = getattr(entry, "access_token", "") or getattr(entry, "runtime_api_key", "")
key = str(key).strip()
if key:
return key
except Exception:
pass
return ""
# ---------------------------------------------------------------------------
# Optional imports — graceful degradation
# ---------------------------------------------------------------------------
@ -843,7 +867,7 @@ def _get_provider(stt_config: dict) -> str:
return "none"
if provider == "mistral":
if _HAS_MISTRAL and get_env_value("MISTRAL_API_KEY"):
if _HAS_MISTRAL and _resolve_provider_key("MISTRAL_API_KEY", "mistral"):
return "mistral"
logger.warning(
"STT provider 'mistral' configured but mistralai package "
@ -862,7 +886,7 @@ def _get_provider(stt_config: dict) -> str:
return "none"
if provider == "elevenlabs":
if get_env_value("ELEVENLABS_API_KEY"):
if _resolve_provider_key("ELEVENLABS_API_KEY", "elevenlabs"):
return "elevenlabs"
logger.warning(
"STT provider 'elevenlabs' configured but ELEVENLABS_API_KEY not set"
@ -904,7 +928,7 @@ def _get_provider(stt_config: dict) -> str:
# Only auto-select Mistral if the SDK is already present — don't trigger a
# lazy-install during passive auto-detection. Explicit `provider: mistral`
# (above) does lazy-install on first transcription call.
if _HAS_MISTRAL and get_env_value("MISTRAL_API_KEY"):
if _HAS_MISTRAL and _resolve_provider_key("MISTRAL_API_KEY", "mistral"):
logger.info("No local STT available, using Mistral Voxtral Transcribe API")
return "mistral"
try:
@ -915,7 +939,7 @@ def _get_provider(stt_config: dict) -> str:
return "xai"
except Exception:
pass
if get_env_value("ELEVENLABS_API_KEY"):
if _resolve_provider_key("ELEVENLABS_API_KEY", "elevenlabs"):
logger.info("No local STT available, using ElevenLabs Scribe STT API")
return "elevenlabs"
if _HAS_OPENAI and (get_env_value("DEEPINFRA_API_KEY") or "").strip():
@ -1483,7 +1507,7 @@ def _transcribe_mistral(file_path: str, model_name: str) -> Dict[str, Any]:
Uses the ``mistralai`` Python SDK to call ``/v1/audio/transcriptions``.
Requires ``MISTRAL_API_KEY`` environment variable.
"""
api_key = get_env_value("MISTRAL_API_KEY")
api_key = _resolve_provider_key("MISTRAL_API_KEY", "mistral")
if not api_key:
return {"success": False, "transcript": "", "error": "MISTRAL_API_KEY not set"}
@ -1632,7 +1656,7 @@ def _transcribe_xai(file_path: str, model_name: str) -> Dict[str, Any]:
def _transcribe_elevenlabs(file_path: str, model_name: str) -> Dict[str, Any]:
"""Transcribe using ElevenLabs Scribe STT API."""
api_key = get_env_value("ELEVENLABS_API_KEY")
api_key = _resolve_provider_key("ELEVENLABS_API_KEY", "elevenlabs")
if not api_key:
return {"success": False, "transcript": "", "error": "ELEVENLABS_API_KEY not set"}

View file

@ -69,6 +69,30 @@ def get_env_value(name, default=None):
return os.getenv(name, default)
value = _get_env_value(name)
return default if value is None else value
def _resolve_provider_key(env_var: str, provider_id: str) -> str:
"""Resolve an API key from env, .env, or the credential pool.
Used by TTS providers (Mistral, ElevenLabs) that store keys
via ``hermes auth add <provider_id>``.
"""
key = get_env_value(env_var)
if key:
return key
try:
from agent.credential_pool import load_pool
pool = load_pool(provider_id)
if pool and pool.has_credentials():
entry = pool.peek()
if entry:
key = getattr(entry, "access_token", "") or getattr(entry, "runtime_api_key", "")
key = str(key).strip()
if key:
return key
except Exception:
pass
return ""
from tools.managed_tool_gateway import resolve_managed_tool_gateway
from tools.tool_backend_helpers import (
managed_nous_tools_enabled,
@ -1092,7 +1116,7 @@ def _generate_elevenlabs(text: str, output_path: str, tts_config: Dict[str, Any]
Returns:
Path to the saved audio file.
"""
api_key = (get_env_value("ELEVENLABS_API_KEY") or "")
api_key = (_resolve_provider_key("ELEVENLABS_API_KEY", "elevenlabs") or "")
if not api_key:
raise ValueError("ELEVENLABS_API_KEY not set. Get one at https://elevenlabs.io/")
@ -1663,7 +1687,7 @@ def _generate_mistral_tts(text: str, output_path: str, tts_config: Dict[str, Any
and writes the raw bytes to *output_path*.
Supports native Opus output for Telegram voice bubbles.
"""
api_key = (get_env_value("MISTRAL_API_KEY") or "")
api_key = (_resolve_provider_key("MISTRAL_API_KEY", "mistral") or "")
if not api_key:
raise ValueError("MISTRAL_API_KEY not set. Get one at https://console.mistral.ai/")
@ -2746,7 +2770,7 @@ def check_tts_requirements() -> bool:
_import_elevenlabs()
except ImportError:
return False
return bool(get_env_value("ELEVENLABS_API_KEY"))
return bool(_resolve_provider_key("ELEVENLABS_API_KEY", "elevenlabs"))
if provider == "openai":
try:
_import_openai_client()
@ -2775,7 +2799,7 @@ def check_tts_requirements() -> bool:
_import_mistral_client()
except ImportError:
return False
return bool(get_env_value("MISTRAL_API_KEY"))
return bool(_resolve_provider_key("MISTRAL_API_KEY", "mistral"))
if provider == "neutts":
return _check_neutts_available()
if provider == "kittentts":
@ -3074,7 +3098,7 @@ if __name__ == "__main__":
print("\nProvider availability:")
print(f" Edge TTS: {'installed' if _check(_import_edge_tts, 'edge') else 'not installed (pip install edge-tts)'}")
print(f" ElevenLabs: {'installed' if _check(_import_elevenlabs, 'el') else 'not installed (pip install elevenlabs)'}")
print(f" API Key: {'set' if get_env_value('ELEVENLABS_API_KEY') else 'not set'}")
print(f" API Key: {'set' if _resolve_provider_key('ELEVENLABS_API_KEY', 'elevenlabs') else 'not set'}")
print(f" OpenAI: {'installed' if _check(_import_openai_client, 'oai') else 'not installed'}")
print(
" API Key: "