fix: update /voice status to show correct STT provider

Voice status was hardcoded to check API keys only. Now uses the actual
provider resolution (local/groq/openai) so it correctly shows
"local faster-whisper" when installed instead of "Groq" or "MISSING".
This commit is contained in:
0xbyt4 2026-03-13 23:48:45 +03:00
parent eb052b1b42
commit 69cb373864
3 changed files with 30 additions and 23 deletions

10
cli.py
View file

@ -3555,12 +3555,12 @@ class HermesCLI:
"Install with: pip install sounddevice numpy\n" "Install with: pip install sounddevice numpy\n"
"Or: pip install hermes-agent[voice]" "Or: pip install hermes-agent[voice]"
) )
if not reqs["stt_key_set"]: if not reqs.get("stt_available", reqs.get("stt_key_set")):
raise RuntimeError( raise RuntimeError(
"Voice mode requires an STT API key for transcription.\n" "Voice mode requires an STT provider for transcription.\n"
"Set GROQ_API_KEY (free) or VOICE_TOOLS_OPENAI_KEY.\n" "Option 1: pip install faster-whisper (free, local)\n"
"Groq: https://console.groq.com/keys\n" "Option 2: Set GROQ_API_KEY (free tier)\n"
"OpenAI: https://platform.openai.com/api-keys" "Option 3: Set VOICE_TOOLS_OPENAI_KEY (paid)"
) )
# Prevent double-start from concurrent threads (atomic check-and-set) # Prevent double-start from concurrent threads (atomic check-and-set)

View file

@ -65,14 +65,14 @@ class TestCheckVoiceRequirements:
monkeypatch.setattr("tools.voice_mode._audio_available", lambda: True) monkeypatch.setattr("tools.voice_mode._audio_available", lambda: True)
monkeypatch.setattr("tools.voice_mode.detect_audio_environment", monkeypatch.setattr("tools.voice_mode.detect_audio_environment",
lambda: {"available": True, "warnings": []}) lambda: {"available": True, "warnings": []})
monkeypatch.setenv("VOICE_TOOLS_OPENAI_KEY", "sk-test-key") monkeypatch.setattr("tools.transcription_tools._get_provider", lambda cfg: "openai")
from tools.voice_mode import check_voice_requirements from tools.voice_mode import check_voice_requirements
result = check_voice_requirements() result = check_voice_requirements()
assert result["available"] is True assert result["available"] is True
assert result["audio_available"] is True assert result["audio_available"] is True
assert result["stt_key_set"] is True assert result["stt_available"] is True
assert result["missing_packages"] == [] assert result["missing_packages"] == []
def test_missing_audio_packages(self, monkeypatch): def test_missing_audio_packages(self, monkeypatch):
@ -89,19 +89,18 @@ class TestCheckVoiceRequirements:
assert "sounddevice" in result["missing_packages"] assert "sounddevice" in result["missing_packages"]
assert "numpy" in result["missing_packages"] assert "numpy" in result["missing_packages"]
def test_missing_stt_key(self, monkeypatch): def test_missing_stt_provider(self, monkeypatch):
monkeypatch.setattr("tools.voice_mode._audio_available", lambda: True) monkeypatch.setattr("tools.voice_mode._audio_available", lambda: True)
monkeypatch.setattr("tools.voice_mode.detect_audio_environment", monkeypatch.setattr("tools.voice_mode.detect_audio_environment",
lambda: {"available": True, "warnings": []}) lambda: {"available": True, "warnings": []})
monkeypatch.delenv("VOICE_TOOLS_OPENAI_KEY", raising=False) monkeypatch.setattr("tools.transcription_tools._get_provider", lambda cfg: "none")
monkeypatch.delenv("GROQ_API_KEY", raising=False)
from tools.voice_mode import check_voice_requirements from tools.voice_mode import check_voice_requirements
result = check_voice_requirements() result = check_voice_requirements()
assert result["available"] is False assert result["available"] is False
assert result["stt_key_set"] is False assert result["stt_available"] is False
assert "STT API key: MISSING" in result["details"] assert "STT provider: MISSING" in result["details"]
# ============================================================================ # ============================================================================

View file

@ -676,12 +676,15 @@ def check_voice_requirements() -> Dict[str, Any]:
"""Check if all voice mode requirements are met. """Check if all voice mode requirements are met.
Returns: Returns:
Dict with ``available``, ``audio_available``, ``stt_key_set``, Dict with ``available``, ``audio_available``, ``stt_available``,
``missing_packages``, and ``details``. ``missing_packages``, and ``details``.
""" """
openai_key = bool(os.getenv("VOICE_TOOLS_OPENAI_KEY")) # Determine STT provider availability
groq_key = bool(os.getenv("GROQ_API_KEY")) from tools.transcription_tools import _get_provider, _load_stt_config, _HAS_FASTER_WHISPER
stt_key_set = openai_key or groq_key stt_config = _load_stt_config()
stt_provider = _get_provider(stt_config)
stt_available = stt_provider != "none"
missing: List[str] = [] missing: List[str] = []
has_audio = _audio_available() has_audio = _audio_available()
@ -691,7 +694,7 @@ def check_voice_requirements() -> Dict[str, Any]:
# Environment detection # Environment detection
env_check = detect_audio_environment() env_check = detect_audio_environment()
available = has_audio and stt_key_set and env_check["available"] available = has_audio and stt_available and env_check["available"]
details_parts = [] details_parts = []
if has_audio: if has_audio:
@ -699,12 +702,17 @@ def check_voice_requirements() -> Dict[str, Any]:
else: else:
details_parts.append("Audio capture: MISSING (pip install sounddevice numpy)") details_parts.append("Audio capture: MISSING (pip install sounddevice numpy)")
if openai_key: if stt_provider == "local":
details_parts.append("STT API key: OK (OpenAI)") details_parts.append("STT provider: OK (local faster-whisper)")
elif groq_key: elif stt_provider == "groq":
details_parts.append("STT API key: OK (Groq)") details_parts.append("STT provider: OK (Groq)")
elif stt_provider == "openai":
details_parts.append("STT provider: OK (OpenAI)")
else: else:
details_parts.append("STT API key: MISSING (set GROQ_API_KEY or VOICE_TOOLS_OPENAI_KEY)") details_parts.append(
"STT provider: MISSING (pip install faster-whisper, "
"or set GROQ_API_KEY / VOICE_TOOLS_OPENAI_KEY)"
)
for warning in env_check["warnings"]: for warning in env_check["warnings"]:
details_parts.append(f"Environment: {warning}") details_parts.append(f"Environment: {warning}")
@ -712,7 +720,7 @@ def check_voice_requirements() -> Dict[str, Any]:
return { return {
"available": available, "available": available,
"audio_available": has_audio, "audio_available": has_audio,
"stt_key_set": stt_key_set, "stt_available": stt_available,
"missing_packages": missing, "missing_packages": missing,
"details": "\n".join(details_parts), "details": "\n".join(details_parts),
"environment": env_check, "environment": env_check,