Connect Custom TTS and STT Command Providers to Hermes
Hermes can connect to a speech engine that exposes a command-line interface without requiring a Python plugin. A TTS command provider receives text in a temporary UTF-8 file and must write an audio file. An STT command provider receives an audio path and must return or write a transcript. Named providers in config.yaml can coexist with built-ins.
Use a command provider for a trusted CLI or short pipeline. Use a Python provider plugin when the integration needs an SDK with no CLI, OAuth refresh, native streaming, or provider-specific discovery.
Choose the correct configuration surface
Custom TTS engines live under tts.providers.<name> and are selected by tts.provider. Custom STT engines live under stt.providers.<name> and are selected by stt.provider.
Choose a unique name. Built-in provider names always win, so defining tts.providers.openai or stt.providers.openai cannot replace the native OpenAI implementation. Command providers also take precedence over a plugin registered with the same non-built-in name.
Find and edit the active configuration with:
hermes config path
hermes config editConnect a TTS command provider
The official TTS registry shape is:
tts:
provider: voxcpm
providers:
voxcpm:
type: command
command: "voxcpm --ref ~/voice.wav --text-file {input_path} --out {output_path}"
output_format: mp3
timeout: 180
voice_compatible: trueHermes writes the requested text to {input_path}, renders the command, and expects audio at {output_path}. type: command is recommended for clarity, although a non-empty command makes Hermes infer the same type.
TTS templates can use:
{input_path}or{text_path}for the temporary text file.{output_path}for the audio file the command must create.{format}for the configured output format.{voice},{model}, and{speed}for provider or global settings.
Supported declared output formats are mp3, wav, ogg, flac, m4a, aac, amr, and opus. The command must really produce the declared format; Hermes validates the name and file location but does not transcode merely because the extension says MP3. An unknown format falls back to mp3.
Command TTS output is delivered as a regular audio attachment by default. Set voice_compatible: true to request voice-bubble delivery. Hermes can convert MP3 or WAV to Opus/OGG with ffmpeg for platforms that need it. The default timeout is 120 idle seconds; output on stdout or stderr resets that idle deadline.
Pass only the environment a provider needs
Hermes scrubs gateway tokens, LLM keys, and internal relay credentials from TTS and STT subprocess environments. Normal values such as PATH, HOME, and locale remain. If the speech CLI needs its own environment variable, list only that variable under env_passthrough:
tts:
providers:
voxcpm:
type: command
command: "voxcpm --text-file {input_path} --out {output_path}"
output_format: mp3
env_passthrough: [VOICE_BACKEND_TOKEN]Keep the secret out of the command template, YAML, screenshots, and logs. The child process receives only the explicitly passed variable in addition to the normal scrubbed environment.
Connect an STT command provider
The STT registry mirrors TTS but changes the data direction:
stt:
provider: parakeet
language: "en"
providers:
parakeet:
type: command
command: "parakeet-asr --model nvidia/parakeet-tdt-0.6b-v2 --in {input_path} --out {output_path}"
format: txt
language: en
timeout: 300STT templates can use:
{input_path}for the original, read-only audio file.{output_path}for the transcript file.{output_dir}for that file's parent directory.{format}fortxt,json,srt, orvtt.{language}and{model}for the resolved provider settings.
After a successful exit, Hermes first reads a non-empty {output_path}. If no file exists but the command wrote stdout, Hermes uses stdout. If both are empty, the call fails with an explicit no-output error. For json, srt, and vtt, Hermes returns the raw content; the runner does not automatically extract a JSON .text field.
The STT timeout defaults to 300 seconds and kills the whole process tree on expiry. Language defaults to stt.language, then en, while model comes from the provider block unless the transcription call supplies an override.
Understand shell behavior
Registry command providers are shell-driven. Hermes substitutes and quotes placeholders according to whether they occur bare, inside single quotes, or inside double quotes. Do not pre-quote dynamic placeholder values. Use {{ and }} when the command needs literal braces.
The legacy HERMES_LOCAL_STT_COMMAND path is different. It supports one local command through the built-in local_command provider, tokenizes the rendered template into an argument vector, and does not interpret |, >, &&, or ; unless you explicitly invoke a shell. Prefer stt.providers.<name> when you need multiple named engines, per-provider models or languages, or a shell pipeline.
Activate and test end to end
Install and test the speech CLI independently first. It must be on the PATH visible to the Hermes process and able to read or write the required files. Then check the Hermes configuration and restart the active surface:
hermes config check
hermes gateway restartFor TTS, enable /voice tts and send a short sentence. Confirm the result is non-empty audio in the declared format and that attachment versus voice-bubble delivery matches the provider setting. For STT, use /voice on in the CLI or send a short voice note through a configured gateway. Command failures, empty output, and timeouts surface to the agent with diagnostic output, so correct the CLI invocation before raising limits.
Pitfalls
- Do not reuse a built-in provider name; the built-in implementation will win.
- Do not declare one format while producing another. A matching extension is not proof of valid audio or transcript content.
- Do not embed credentials in
command; use a narrowly scopedenv_passthroughentry when needed. - Do not pre-quote placeholders. Hermes already applies context-aware shell quoting.
- Do not expect JSON STT output to be parsed into plain text automatically.
- Do not treat a command template as untrusted data. It runs with the same user and filesystem access as Hermes.
- Do not use a command provider for an SDK-only, OAuth-refreshing, or streaming integration that needs the Python plugin interface.
Verification checklist
- Verify the chosen provider name does not collide with a built-in.
- Verify the speech CLI runs under the same environment and
PATHas Hermes. - Verify every placeholder required by the CLI maps to the documented TTS or STT placeholder set.
- Verify the command creates a non-empty output file or, for STT, produces usable stdout.
- Verify timeout and process-tree behavior are appropriate for the model's startup time.
- Verify only required provider-specific variables are listed in
env_passthrough. - Verify a real TTS request and a real STT voice sample succeed after the gateway restart or CLI relaunch.
