From a355db1ef5d3d0e2b94e519ccd9e6da81110c665 Mon Sep 17 00:00:00 2001 From: Mate Remias Date: Sun, 10 May 2026 14:12:18 +0200 Subject: [PATCH] feat(tools): pass language hint to Groq STT Read stt.groq.language from config.yaml (with HERMES_LOCAL_STT_LANGUAGE env fallback) and forward it to the Groq Whisper API to skip auto-detection on known-language audio. Omit when unset so Groq auto-detects, preserving today's behavior. Bonus: swap xAI's hardcoded env literal for the LOCAL_STT_LANGUAGE_ENV constant for consistency. Co-Authored-By: Claude Opus 4.7 (1M context) --- cli-config.yaml.example | 2 ++ tools/transcription_tools.py | 25 ++++++++++++++----- .../docs/reference/environment-variables.md | 2 +- website/docs/user-guide/configuration.md | 5 +++- website/docs/user-guide/features/tts.md | 6 ++++- .../docs/user-guide/features/voice-mode.md | 3 +++ 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/cli-config.yaml.example b/cli-config.yaml.example index 3ee047cd5d84..9ad8788a96f3 100644 --- a/cli-config.yaml.example +++ b/cli-config.yaml.example @@ -1127,6 +1127,8 @@ stt: local: model: "base" # tiny | base | small | medium | large-v3 | turbo # language: "" # auto-detect; set to "en", "es", "fr", etc. to force + # groq: + # language: "" # auto-detect; set to "en", "es", "fr", etc. to force openai: model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe language: "" # auto-detect; set to "en", "es", "fr", etc. to force diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index 410943496130..bef4b3b63025 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -1302,7 +1302,12 @@ def _transcribe_local_command(file_path: str, model_name: str) -> Dict[str, Any] def _transcribe_groq(file_path: str, model_name: str) -> Dict[str, Any]: - """Transcribe using Groq Whisper API (free tier available).""" + """Transcribe using Groq Whisper API (free tier available). + + Honours an optional ISO-639-1 language hint resolved from + ``stt.groq.language`` (config.yaml) or ``HERMES_LOCAL_STT_LANGUAGE`` + (env). When neither is set, Groq Whisper auto-detects. + """ api_key = get_env_value("GROQ_API_KEY") if not api_key: return {"success": False, "transcript": "", "error": "GROQ_API_KEY not set"} @@ -1315,20 +1320,28 @@ def _transcribe_groq(file_path: str, model_name: str) -> Dict[str, Any]: logger.info("Model %s not available on Groq, using %s", model_name, DEFAULT_GROQ_STT_MODEL) model_name = DEFAULT_GROQ_STT_MODEL + groq_cfg = _load_stt_config().get("groq", {}) + language = groq_cfg.get("language") or os.getenv(LOCAL_STT_LANGUAGE_ENV) + try: from openai import OpenAI, APIError, APIConnectionError, APITimeoutError client = OpenAI(api_key=api_key, base_url=GROQ_BASE_URL, timeout=30, max_retries=0) try: + create_kwargs = { + "model": model_name, + "response_format": "text", + } + if language: + create_kwargs["language"] = language with open(file_path, "rb") as audio_file: transcription = client.audio.transcriptions.create( - model=model_name, file=audio_file, - response_format="text", + **create_kwargs, ) transcript_text = str(transcription).strip() - logger.info("Transcribed %s via Groq API (%s, %d chars)", - Path(file_path).name, model_name, len(transcript_text)) + logger.info("Transcribed %s via Groq API (%s, lang=%s, %d chars)", + Path(file_path).name, model_name, language or "auto", len(transcript_text)) return {"success": True, "transcript": transcript_text, "provider": "groq"} finally: @@ -1508,7 +1521,7 @@ def _transcribe_xai(file_path: str, model_name: str) -> Dict[str, Any]: ).strip().rstrip("/") language = str( xai_config.get("language") - or os.getenv("HERMES_LOCAL_STT_LANGUAGE") + or os.getenv(LOCAL_STT_LANGUAGE_ENV) or DEFAULT_LOCAL_STT_LANGUAGE ).strip() # .get("format", True) already defaults to True when the key is absent; diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index 362652974035..ac73691ea468 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -102,7 +102,7 @@ Hermes reads environment variables from the process environment and, for user-ma | `HERMES_MODEL` | Override model name at process level (used by cron scheduler; prefer `config.yaml` for normal use) | | `VOICE_TOOLS_OPENAI_KEY` | Preferred OpenAI key for OpenAI speech-to-text and text-to-speech providers | | `HERMES_LOCAL_STT_COMMAND` | Optional local speech-to-text command template. Supports `{input_path}`, `{output_dir}`, `{language}`, and `{model}` placeholders | -| `HERMES_LOCAL_STT_LANGUAGE` | Default language passed to `HERMES_LOCAL_STT_COMMAND` or auto-detected local `whisper` CLI fallback (default: `en`) | +| `HERMES_LOCAL_STT_LANGUAGE` | Default language hint for STT. Used by `HERMES_LOCAL_STT_COMMAND`, the local `whisper` CLI fallback (default: `en`), Groq, and xAI when no per-provider `language` is set in `config.yaml` | | `HERMES_HOME` | Override Hermes config directory (default: `~/.hermes`). Also scopes the gateway PID file and systemd service name, so multiple installations can run concurrently | | `HERMES_GIT_BASH_PATH` | **Windows only.** Override `bash.exe` discovery for the terminal tool. Points at any bash — full Git-for-Windows install, WSL bash via symlink, MSYS2, Cygwin. The installer sets this automatically to the PortableGit it provisioned. See the [Windows (Native) Guide](../user-guide/windows-native.md#how-hermes-runs-shell-commands-on-windows) | | `HERMES_DISABLE_WINDOWS_UTF8` | **Windows only.** Set to `1` to disable the UTF-8 stdio shim (`configure_windows_stdio()`) and fall back to the console's locale code page. Useful for bisecting encoding bugs; rarely the right setting in normal operation | diff --git a/website/docs/user-guide/configuration.md b/website/docs/user-guide/configuration.md index eb6d19ba1adb..f55bd139db4b 100644 --- a/website/docs/user-guide/configuration.md +++ b/website/docs/user-guide/configuration.md @@ -1715,6 +1715,9 @@ stt: provider: "local" # "local" | "groq" | "openai" | "mistral" local: model: "base" # tiny, base, small, medium, large-v3 + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect + groq: + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect openai: model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe language: "" # auto-detect; set to "en", "es", "fr", etc. to force @@ -1726,7 +1729,7 @@ Set `stt.echo_transcripts: false` when the gateway should transcribe voice notes Provider behavior: - `local` uses `faster-whisper` running on your machine. Install it separately with `pip install faster-whisper`. -- `groq` uses Groq's Whisper-compatible endpoint and reads `GROQ_API_KEY`. +- `groq` uses Groq's Whisper-compatible endpoint and reads `GROQ_API_KEY`. Pass `stt.groq.language` (or the global `HERMES_LOCAL_STT_LANGUAGE` env var) to skip auto-detection and reduce latency. - `openai` uses the OpenAI speech API and reads `VOICE_TOOLS_OPENAI_KEY`. If the requested provider is unavailable, Hermes falls back automatically in this order: `local` → `groq` → `openai`. diff --git a/website/docs/user-guide/features/tts.md b/website/docs/user-guide/features/tts.md index 726a43c7724e..2d8ceb228d75 100644 --- a/website/docs/user-guide/features/tts.md +++ b/website/docs/user-guide/features/tts.md @@ -429,12 +429,16 @@ stt: provider: "local" # "local" | "groq" | "openai" | "mistral" | "xai" local: model: "base" # tiny, base, small, medium, large-v3 + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect + groq: + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect openai: model: "whisper-1" # whisper-1, gpt-4o-mini-transcribe, gpt-4o-transcribe mistral: model: "voxtral-mini-latest" # voxtral-mini-latest, voxtral-mini-2602 xai: model: "grok-stt" # xAI Grok STT + language: "" # optional ISO-639-1 hint ``` ### Provider Details @@ -449,7 +453,7 @@ stt: | `medium` | ~1.5 GB | Slower | Great | | `large-v3` | ~3 GB | Slowest | Best | -**Groq API** — Requires `GROQ_API_KEY`. Good cloud fallback when you want a free hosted STT option. +**Groq API** — Requires `GROQ_API_KEY`. Good cloud fallback when you want a free hosted STT option. Set `stt.groq.language` (or the global `HERMES_LOCAL_STT_LANGUAGE` env var) to skip Whisper's auto-detect and reduce latency on known-language audio. **OpenAI API** — Accepts `VOICE_TOOLS_OPENAI_KEY` first and falls back to `OPENAI_API_KEY`. Supports `whisper-1`, `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. diff --git a/website/docs/user-guide/features/voice-mode.md b/website/docs/user-guide/features/voice-mode.md index 7c3222b6438a..b10ef0dff7c3 100644 --- a/website/docs/user-guide/features/voice-mode.md +++ b/website/docs/user-guide/features/voice-mode.md @@ -414,6 +414,9 @@ stt: provider: "local" # "local" (free) | "groq" | "openai" | "mistral" | "xai" local: model: "base" # tiny, base, small, medium, large-v3 + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect + groq: + language: "" # optional ISO-639-1 hint (e.g. "en"); blank = auto-detect # model: "whisper-1" # Legacy: used when provider is not set # Text-to-Speech