dsh-voice
goodandready/dsh-voice
Voice input for DeepSeek Harness: dictation chunked by pauses and voice messages, each with its own provider fallback chain (Deepgram, Groq, HuggingFace, local whisper.cpp).
Instalar
dsh plugin --profile web add github:goodandready/dsh-voiceREADME
dsh-voice
Voice input for the DeepSeek Harness Web GUI, in two modes, each with its own provider fallback chain.
Dictation — press the mic, talk, and the text lands in the composer as you go: speech is cut into phrases on silence and each phrase is transcribed on its own. Press again to stop; sending stays in your hands.
Voice message — press the wave button, record, press again. The transcript is inserted and sent to the agent after a short cancel window.
Both modes fall back across providers, so one outage or rate limit does not lose your recording. API keys never reach the browser: audio is posted to the plugin's own route and the host talks to the providers.
Install
# From npm:
dsh plugin --profile web add @goodandready/dsh-voice
# Locally from a checkout:
dsh plugin --profile web add file:/path/to/dsh-voice
Restart the Web UI afterwards, then hard-refresh the browser.
Providers
| Key | Service | Default model | Credential |
|---|---|---|---|
deepgram | Deepgram | nova-2 | DEEPGRAM_API_KEY |
groq | Groq | whisper-large-v3-turbo | GROQ_API_KEY |
hf | HuggingFace Inference | openai/whisper-large-v3 | HF_TOKEN |
local-whisper | local whisper.cpp server | model given at server start | none, fully offline |
Keys are read through the DSH credentials service (Settings → Credentials, or
$DSH_HOME/.credentials.yaml), falling back to the process environment. A
provider without a key is skipped, not fatal.
Configure (Web GUI)
Settings → Голос (Voice) has three blocks:
- Dictation — fallback chain (provider + optional model per row, order is
the order of attempts), language, and the silence threshold that ends a
phrase (
vadSilenceMs, default 700 ms). - Voice message — its own independent chain, language, and the cancel
window before the message is sent (
autoSendMs, default 4000 ms). - General — local whisper endpoint, binary, model, autostart.
Speed matters for dictation and accuracy for messages, which is why the chains are separate: a sensible pair is Deepgram → Groq → local for dictation and Groq → HuggingFace → local for messages.
Local whisper.cpp
The local provider needs a running whisper.cpp server:
whisper-server -m /path/to/ggml-medium-q8_0.bin --host 127.0.0.1 --port 8001
Set whisperModel (and whisperBin if it is not in PATH) and the plugin
launches the server itself when autoStart is on. While whisperModel is
empty, autostart stays off.
ffmpeg is required for this provider. whisper.cpp accepts WAV only and
rejects the webm/opus the browser records, so the host converts each recording
to 16 kHz mono WAV before forwarding it. Point ffmpegBin at your binary if it
is not in PATH.
Tool
The plugin also registers transcribe_audio(file_path, language?) for the
agent, using the voice-message chain. Useful for recordings and interviews that
are already files on disk.
Routes
| Route | Purpose |
|---|---|
POST /dsh-voice/transcribe | {dataBase64, mimeType, mode} → {ok, text, provider, tookMs} |
GET /dsh-voice/status | whisper server state and the effective chains |
Structure
lib/index.js host: config, routes, transcribe_audio, whisper autostart
lib/providers.js the four providers, pure functions (network injected)
lib/chain.js fallback walk over a chain
lib/wav.js webm/opus → WAV 16 kHz mono via ffmpeg
lib/client.js browser: composer buttons, recording, settings page
test/ node --test units for the chain and the providers
Run the tests with npm test (no dependencies, Node's built-in runner).
Requirements
- DeepSeek Harness with the Web GUI
- Node 20+
- ffmpeg, for the local whisper provider
- a microphone reachable from the browser (HTTPS or localhost)
License
MIT