dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Voice copilot for DeepSeek Harness: names and attaches the push-to-talk session, adds composer microphones, and runs a real-time spoken conversation loop with Silero VAD, speculative transcription, streaming TTS and speaker verification.
Voice copilot for DeepSeek Harness. It gives the push-to-talk session a name and a place in the sidebar, adds dictation microphones to the composer, and can run a real-time spoken conversation: the microphone stays open, a Silero VAD decides when you speak, transcription starts while you are still silent, and the answer is spoken back sentence by sentence while the model is still writing.
The plugin is the glue between the harness and a speech stack running on localhost. It does not ship a speech-to-text engine or a text-to-speech voice; those are external services (see Requirements).
npm install -g pnpm
npx @deepseek-ai/dsh plugin --profile web add github:songjongdevsecops/dsh-plugin-voz-copilot --allow-build=onnxruntime-node
--allow-build=onnxruntime-node é obrigatório no pnpm 10+: sem ele o pnpm bloqueia o download do binário do ONNX Runtime e a instalação falha. O pacote em si não tem passo de build.
dsh web and start it again)npx @deepseek-ai/dsh web
The package declares `dsh.bundle.patch`, so `dsh plugin add` installs it **and**
adds its line to the profile bundle list. The bundle layer is read at boot, so a
restart is what activates it.
If `dsh` is installed globally, use `dsh` instead of `npx @deepseek-ai/dsh`.
### Remove
```sh
npx @deepseek-ai/dsh plugin --profile web remove dsh-plugin-voz-copilot
# restart the harness
Removing the package removes its bundle line. Conversations already named keep their title in the session log; nothing is deleted.
^22.19.0 || >=24.0.0 (the same range DeepSeek Harness supports).PATH (the harness manages profile plugins with it).web profile for the interface. The title
mode is used by the acp profile.whisper-server, at whisperUrl (default http://127.0.0.1:8178):
POST /inference?response_format=json with the audio in the multipart field
file, answering {"text": "..."}. The server must decode WebM/Opus (an
ffmpeg-enabled build), WAV and MP3. This plugin does not install it.whisperFastUrl (for example http://127.0.0.1:8179)
for short conversation segments. When it is not set, the main endpoint is used.edge-tts CLI, or a warm HTTP server. See
Configuration. Without tts, the real-time conversation
button stays disabled with the reason in its tooltip; dictation still works.sherpa-onnx and numpy
(requirements.txt). The ERes2Net model (38 MB, Apache-2.0) and the Silero
VAD model (2.3 MB, MIT) are bundled. The speech detector uses
onnxruntime-node, installed by npm.The plugin is additive: it does not replace any harness control.
Push-to-talk session. With a companion push-to-talk bridge that binds the
Copilot key (F23 in the reference setup) and opens the session with
[conversa_copilot] as the first line of the turn, this plugin pins the
session title to conversa_copilot and attaches the session to the configured
workspace, so it appears in the sidebar instead of staying invisible. The
bridge and the key binding are not part of this package.
Header microphone. Binds the conversation that is open in the GUI as the
target of the voice. Lit means this is the voice conversation; a second click
unbinds it. Binding writes the session id to statePath (the same file the
bridge reads) and attaches the session to the workspace.
Sidebar footer. Shows which conversation is receiving the voice without opening it.
Composer microphones. Two buttons next to Send:
Click the same button again to stop. While recording, the button becomes a red
stop square; Esc or the cancel control in the floating pill aborts. The
transcription is appended after whatever was already in the draft, never
replacing it. An empty transcription is an error, never an empty turn.
Real-time conversation. The third button opens the spoken loop on the open
conversation: speak normally, the answer is spoken while it is written, and
speaking over it interrupts (barge-in). The panel has pause, stop-response and
close controls; Esc closes it and always releases the microphone.
Only your voice. After enrolling your voice in the conversation panel, speaker embeddings decide whether a segment belongs to you. Audio from other voices is ignored before transcription. The check is fail-open: no enrollment, no local server, or too-short audio lets the speech through.
Two halves, one package:
| Half | File | What it does |
|---|---|---|
| host | index.js | title mode pins the session title; attach mode links and attaches the session, and serves /voz-copilot/api/* |
| page | client.js | header button, sidebar indicator, composer microphones, recording pill and the conversation panel |
| host | vad.js | Silero VAD on 16 kHz PCM: speech probability with LSTM state between calls |
| host | speaker.js + speaker-servidor.py | speaker embeddings, voice profile and the cosine decision |
| host | fala.js | deterministic text cleanup (markdown, code, links, emoji) and sentence splitting |
| host | tts.js | text to MP3 through edge-tts or the warm server |
| host | falar.js | waits for the turn to finish and reads its text |
The browser never talks to the speech servers directly: the host process owns
the loopback route and enforces a local-request boundary (loopback Host,
same-origin Origin, POST for state changes, 256 KB for JSON and 16 MB for
audio). Audio and transcripts stay on localhost unless you point a configured
service at a remote address.
The defaults come from the package bundle patch and boot on any machine. To use
your own workspace and voice, edit the profile patch
(~/.dsh/profiles/web/cordis.patch.yml). A patch under the same id replaces
the whole config of the line, so restate every key you want to keep.
- id: voz-copilot
config:
mode: 'attach'
workspace: '/path/to/your/workspace'
title: 'conversa_copilot'
statePath: '~/.dsh/voz-copilot.json'
pollMs: 3000
whisperUrl: 'http://127.0.0.1:8178'
whisperFastUrl: 'http://127.0.0.1:8179'
voz:
persona: 'Answer in short, plain sentences that sound natural when spoken aloud.'
maxChars: 220
tts:
bin: 'edge-tts'
voice: 'en-US-AriaNeural'
rate: '+0%'
volume: '+0%'
lexicon: '/path/to/tts_lexicon.py'
python: 'python3'
servidorQuente: 'http://127.0.0.1:8180'
servidorQuenteScript: '/path/to/tts-servidor.py'
servidorQuentePython: 'python3'
orador:
limiar: 0.5
perfilPath: '~/.dsh/voz-perfil.json'
~ is expanded in workspace, statePath and the other paths below.
| Key | Default | Purpose |
|---|---|---|
mode | attach (bundle line); code default is title | attach for the web profile; title for the acp profile |
workspace | . (the process working directory) | workspace that owns the voice session; set it to your workspace path |
title | conversa_copilot | title pinned on the push-to-talk session |
marker | none, required in title mode | first-line marker the bridge sends, e.g. [conversa_copilot] |
statePath | ~/.dsh/voz-copilot.json | durable link between the bridge and the session id |
pollMs | 3000 | how often attach mode checks the link |
whisperUrl | http://127.0.0.1:8178 | whisper-server used by dictation and by the slow path |
whisperFastUrl | same as whisperUrl | faster whisper-server for conversation segments |
voz.persona | a short spoken-answer instruction | prefix prepended to each spoken turn |
voz.maxChars | 220 | cap per spoken phrase (60 to 600) |
tts.bin | required with tts | edge-tts executable, or a compatible wrapper |
tts.voice | required with tts | voice name passed to edge-tts |
tts.rate, tts.volume, tts.pitch | unset | passed through when set |
tts.lexicon | unset | pronunciation lexicon script (text in, text out) |
tts.python | python3 | interpreter for the lexicon |
tts.servidorQuente | unset | warm TTS HTTP server base URL |
tts.servidorQuenteScript | unset | script the plugin starts for the warm server |
tts.servidorQuentePython | python3 | interpreter for that script |
orador.limiar | 0.5 | cosine threshold for "this is my voice" (0.1 to 0.95) |
orador.perfilPath | ~/.dsh/voz-perfil.json | voice profile, mode 0600, vectors only |
orador.url | http://127.0.0.1:8181 | speaker embedding server |
orador.python | venv/bin/python in the package (venv\Scripts\python.exe on Windows) | interpreter for the speaker server |
orador.script | speaker-servidor.py in the package | speaker server script |
orador.modelo | models/orador_eres2net.onnx in the package | speaker embedding model |
tts.bin is spawned once per phrase with --voice, --text, and the optional
--rate=, --volume=, --pitch=, and must write MP3 to stdout. With
tts.servidorQuente set, the plugin first tries
GET /fala?texto=...&voz=... on that server and starts
tts.servidorQuenteScript when it is down, falling back to the CLI. The warm
server is what makes the first byte fast; the CLI works without it.
The default paths assume a checkout of this repository with a venv inside it:
git clone https://github.com/songjongdevsecops/dsh-plugin-voz-copilot.git
cd dsh-plugin-voz-copilot
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Then install the plugin from the checkout (dsh plugin --profile web add /path/to/dsh-plugin-voz-copilot) so the bundled paths resolve, or point
orador.python, orador.script and orador.modelo at that checkout. The
Python server is optional: without it the conversation mode still works, just
without speaker filtering.
# the host half is mounted and answering
curl -s http://127.0.0.1:3080/voz-copilot/api/estado
# expected: {"ok":true,"sessionId":null,"conversa":false,...}
# "conversa" is true only when tts is configured
# the browser half is being served (revision of client.js)
curl -sN http://127.0.0.1:3080/plugins/events | grep voz-copilot
In the GUI: open a conversation, click the plain microphone, speak, and click it again. The text should appear in the composer. If the speech server is not reachable, the pill shows the error instead of silently doing nothing.
whisperUrl,
dictation and conversation transcription fail with a clear error; the
session naming/attach side still works if a bridge writes the marker and the
state file.edge-tts (which sends the text to
Microsoft's service) or your own warm server. Without tts, spoken replies
and the conversation button are unavailable.onnxruntime-node
ships a native binary per platform; the Python venv path is platform-aware,
but the external services are assumed to be POSIX-friendly 127.0.0.1
processes. The browser half is tested with Chromium (MediaRecorder,
audio/webm;codecs=opus, AudioContext).orador.limiar for your microphone and room. It is fail-open by
design: a failure never drops your speech, so a missing server means every
voice in the room can become a turn.statePath is shared with the bridge. If
you run both, let the bridge own the voice link; binding from the GUI is for
setups without the bridge.title mode needs the acp profile. The web profile does not have
the session title service used by that mode.