← 返回列表
未验证
@achasoft/dsh-voice
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/16 · 已提供中文文档
向 DeepSeek Harness Web Client 中口述提示词——在编辑器中放置一个麦克风,并支持可切换的转录方式:托管 Whisper API、自托管服务器,或完全离线的 whisper.cpp 二进制文件。
综合分
29.6
GitHub 分
29.6
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add navid-kianfar/dsh-voice该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-api-remotes@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-settings-plugins@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-credentials@deepseek-ai/dsh-llm用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
@achasoft/dsh-voice
为 DeepSeek Harness(dsh)Web 客户端提供语音输入。编辑器中的麦克风按钮会录制你说的话,并将音频片段发送到 dsh 主机进行转写。如果你开启润色,部署的默认模型随后会整理文本。结果会添加到你的草稿末尾,你可以在发送前进行编辑。该插件可以使用本地的 whisper.cpp 二进制文件进行转写,因此音频永远不会离开本机,也可以使用任何支持 OpenAI /audio/transcriptions API 的端点。
编辑器输入工具栏左侧带有麦克风按钮
功能
麦克风控制
该按钮位于编辑器输入工具栏的左侧。仅当启用了转写提供方行时才会显示。如果没有提供方,该位置不会渲染任何内容。
| 手势(interactionMode) | 开始 | 停止 |
|---|---|---|
| toggle(默认) | 点击 | 再次点击 |
| hold | 按住 | 松开 |
在 hold 模式下,如果在麦克风打开之前松开按钮,则会取消开始。这通常发生在浏览器的权限提示仍显示时。此时控件会显示“说话时按住按钮”。获得访问权限后再次按下即可。
录制期间,按钮旁边会出现一个三格音量指示器和已用秒数计时器。音量指示器表明麦克风确实在拾取声音。停止后,在音频片段转写期间按钮会被禁用。如果开启润色,则在模型清理文本期间会显示“polishing…”。
录制中的编辑器:高亮的麦克风按钮、音量指示器、已用计时器
自动停止
- 静音(silenceStopMs,默认 2500 毫秒):在持续安静达到该时长后结束录制,但仅在已经听到语音后才会生效,因此缓慢的开头不会被截断。不会显示提示。将其设为 0 可关闭静音停止。
- 时长上限(maxClipSeconds,默认 120 秒):录制会结束,并像你手动停止一样进行转写。控件会显示“已在 120 秒限制处停止”。
实时预览
当 liveIntervalMs 设置为大于 0 时,到目前为止录制的音频会按该间隔再次转写。临时文本会显示在计时器旁边。它永远不会写入草稿。草稿仅在听写结束时更改一次。每一轮都从音频片段的开头开始,因此对于托管端点来说,每一轮都是一次计费请求。这就是预览默认关闭的原因。
转写文本的去向
- append(默认)会将转写文本添加到草稿末尾,必要时会添加一个分隔空格。
- replace 会用转录文本替换整个草稿,但前提是自录制开始以来草稿未被修改。如果你在听写时输入了内容,转录文本则会改为追加。
文本通过会话的 slash/input-insert-text 编辑器命令插入,而不是重写草稿。草稿中已有的引用芯片(@path 引用)保持不变,一次撤销即可移除听写内容。如果编辑器拒绝插入超过 500 毫秒,控件会显示 transcript not inserted: ,因此听写内容不会丢失。
润色
开启 polish 后(默认关闭),原始转录文本会发送到部署的默认模型(即 agentDefaultModel 选择,而非特定会话中选择的模型),并附带一条保守的清理指令:移除填充词和错误开头,恢复标点,修正明显误识别的技术术语,并将口头枚举转换为 Markdown 列表。该指令禁止回答、总结或翻译。polishPrompt 会替换该指令。如果模型请求失败,或未配置模型,则使用原始转录文本。润色不需要额外凭据,但确实会将转录文本发送到该模型;请参阅隐私与安全。
非语音过滤
Whisper 模型会对静音音频输出诸如 you 之类的词或 (beep) 之类的标注,而不是什么都不输出。插件分三步过滤:
1. 最响时刻(峰值短窗口 RMS)低于 0.005 的片段根本不会发送。控件会显示“没有听到任何内容”。whisper-cpp 提供程序会在启动二进制文件之前,对收到的 WAV 应用相同的检查。
2. 仅由标注([BLANK_AUDIO]、(wind blowing)、music、音符)组成的输出始终会被丢弃。
3. 固定短语(you、thank you、thanks、thanks for watching、thank you for watching、bye)仅在片段的峰值低于 0.02 时才会被丢弃。以正常音量说出的真实单词听写仍会通过。
消息
| 显示 | 原因 |
|---|---|
| no transcription provider | 提供程序在片段发往主机的途中被卸载 |
| 提供程序的就绪详情,或 transcription is not configured | 提供程序已挂载但未就绪 |
| microphone access was denied | 权限被拒绝,或被页面上下文阻止 |
| no microphone is available | 没有输入设备,或所选设备已不存在 |
| this browser cannot record audio | 没有 MediaRecorder,或缺少 navigator.mediaDevices(不安全来源) |
| the transcription provider accepts no format this browser can record | 提供程序的格式与浏览器的格式没有重叠,且提供程序不接受 WAV |
| recording too long | 片段超过 maxClipBytes,或端点返回 HTTP 413 |
| transcription is unreachable | 主机繁忙(请参阅限制) |
| transcription timed out / unsupported audio format / nothing was recorded / transcription failed | 其他已分类的失败 |
设置卡片
打开 Settings > Plugins > Plugin configuration,然后展开 Voice input。该卡片会显示提供商和模型,并带有 Ready 或 Not ready 徽章(未就绪时还会显示原因,例如 no model configured 或 model file not found at …;麦克风按钮的工具提示也带有相同的原因)。你可以在此编辑录音手势、转写文本放置位置、最长录音时长、AI 润色及其提示词、静音停止、实时预览间隔、麦克风和语言。在静音停止和实时预览字段中,0 会关闭该功能,而空白字段会恢复部署时的值。编辑会被暂存:Save 会写入这些编辑,Discard 会丢弃它们。麦克风选择会立即保存,并且仅保留在此浏览器中(localStorage 键 achasoft.dsh-voice.deviceId)。只有在浏览器已授予过一次麦克风访问权限后,设备名称才会出现。
Voice input settings card expanded, showing provider status and the editable fields
要求
- 带有 Web Client 的 dsh。此版本已针对 dsh 0.1.5-rc.2 测试。
- Node.js ^22.19 或 >=24,并且 pnpm 位于 PATH 中,以供 dsh plugin 使用。
- 安全的浏览器上下文。 浏览器仅在 HTTPS 或 localhost / 127.0.0.1 上公开麦克风。如果你通过局域网地址或主机名以纯 HTTP 打开 Web Client,按钮仍会出现,但按下它会显示“this browser cannot record audio”。
- Web Client 源的麦克风权限。嵌入在另一个应用或 iframe 中的页面需要该宿主允许麦克风访问。否则启动会失败并显示“microphone access was denied”。
- 一个转写提供商,按如下方式设置。
whisper.cpp(本地)
安装该二进制文件。在 macOS 上使用 Homebrew 时,该 formula 现在名为 whisper.cpp,而 whisper-cpp 仍会解析到它:
brew install whisper-cpp
command -v whisper-cli # e.g. /opt/homebrew/bin/whisper-cli
将 GGML 模型下载到你选择的任意路径:
mkdir -p ~/.dsh/models
curl -L -o ~/.dsh/models/ggml-base.en.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin
.en 模型仅支持英语。对于其他语言,请使用多语言模型,例如 ggml-base.bin。当 language 为空白时,提供商会传入 -l auto,因此该二进制文件会检测所说的语言(若任其自行处理,whisper-cli 会假定 -l en)。将 language 设置为诸如 de 之类的代码可跳过检测。
这是该提供商运行的确切命令,可用于在 16 kHz 单声道 WAV 上手动检查你的二进制文件和模型:
whisper-cli -m ~/.dsh/models/ggml-base.en.bin -f clip.wav --no-timestamps --no-prints -l auto
OpenAI 兼容端点
任何接受 POST /audio/transcriptions 作为 multipart 表单数据(model、file、可选的 language)并返回带有 text 字段的 JSON 的服务器。对于托管服务,API 密钥必须能够由 harness 凭据接缝按照 apiKeyEnv 中的名称解析:启动环境、存储的凭据文件 $DSH_HOME/.credentials.yaml、工作目录的 .env,或 $DSH_HOME/.env,按此顺序。
安装
dsh plugin --profile web add @achasoft/dsh-voice
dsh plugin 会在 $DSH_HOME/profiles/web 中运行 pnpm($DSH_HOME 默认为 ~/.dsh)。然后它会将该包添加到该配置文件的 dsh.profile.bundles 中,因为该包声明了一个 dsh.bundle 补丁。重启 dsh web 以加载它。本地检出(dsh plugin --profile web add ./dsh-voice,从你的当前目录解析并链接)必须先使用 npm run build 构建。
确认 voice、voice-ui、voice-openai-compatible 和 voice-whisper-cpp 这些行存在:
dsh --profile web --dump-config
配置如何分层
后面的层优先:按 bundle 顺序,每个 bundle 的 cordis.patch.yml(包括本包的),然后是 $DSH_HOME/profiles/web/cordis.patch.yml,然后是 $DSH_HOME/cordis.patch.yml,然后是 --patch 覆盖层。通过 id 定位某一行的补丁条目会替换该行的整个 config,因此请重新声明你保留的每一个键。设置卡片的编辑会作为用户覆盖存储在 $DSH_HOME/settings.yaml 的 voice: 部分中,并应用在组合后的行之上。
启用一个提供方
两个提供方行默认都处于禁用状态,且只能启用其中一个:两者都声明 ctx.transcription,如果挂载了两个,加载会失败。将以下内容之一添加到 $DSH_HOME/profiles/web/cordis.patch.yml。
whisper.cpp:
- id: voice-whisper-cpp
disabled: false
config:
binaryPath: /opt/homebrew/bin/whisper-cli
modelPath: /Users/you/.dsh/models/ggml-base.en.bin
timeoutMs: 300000
maxOutputBytes: 262144
graceMs: 5000
OpenAI 兼容(对于不需要授权的本地服务器,省略 apiKeyEnv):
- id: voice-openai-compatible
disabled: false
config:
baseUrl: https://api.openai.com/v1
model: whisper-1
apiKeyEnv: OPENAI_API_KEY
timeoutMs: 120000
卸载
dsh plugin --profile web remove @achasoft/dsh-voice
然后从你的配置文件的 cordis.patch.yml 中移除任何 voice 行。
配置
voice(偏好设置)
| 键 | cordis.patch.yml 中的默认值 | Schema | 设置卡片 |
|---|---|---|---|
| interactionMode | toggle | 必填,toggle 或 hold | 是 |
| insertMode | append | 必填,append 或 replace | 是 |
| maxClipSeconds | 120 | 必填整数,>= 1 | 是 |
| maxClipBytes | 26214400(25 MiB) | 必填整数,>= 1 | 否 |
| language | 未设置 | 可选字符串,传递给提供方 | 是 |
| polish | false | 可选布尔值,默认为 false | 是 |
| polishPrompt | 未设置(内置指令) | 可选字符串 | 是,当润色开启时 |
| silenceStopMs | 2500 | 可选整数,>= 0;0 或未设置则禁用 | 是,见注释 |
| liveIntervalMs | 未设置(注释掉的 2000) | 可选整数,>= 0;0 或未设置则禁用 | 是,见注释 |
- maxClipSeconds 由浏览器强制执行。maxClipBytes 由宿主强制执行,宿主在解码前测量 base64 载荷。
- 在卡片中清除可选字段会移除你的覆盖值,因此组合后的值会再次生效。对于 silenceStopMs,该值为 2500,所以清除它不会关闭静音停止。请改为输入 0:它会被存储为你的覆盖值并禁用该功能。当你的配置文件补丁设置了 liveIntervalMs 时,情况同样如此。
- polish 是可选的,默认为 false。已经声明 polish: true 的配置文件和设置会保持开启。
voice-whisper-cpp
| 键 | 默认值 | 模式 |
|---|---|---|
| binaryPath | whisper-cli | 必填;建议使用绝对路径。裸名称会在宿主经过清理的 PATH 中查找,该 PATH 不一定是你 shell 的 PATH |
| modelPath | '' | 必填;GGML 模型文件的绝对路径,或以 ~/ 开头的路径。为空时报告 Not ready 并显示 no model configured;相对路径、文件缺失或不可读也会报告 Not ready 并附带原因 |
| threads | 未设置(whisper.cpp 自身的默认值) | 可选整数,>= 1;作为 -t 传入 |
| timeoutMs | 300000 | 必填整数,>= 1 |
| maxOutputBytes | 262144 | 必填整数;捕获的 stdout 和 stderr 的上限 |
| graceMs | 5000 | 必填整数;取消或超时时 SIGTERM 与 SIGKILL 之间的延迟 |
仅接受 audio/wav。浏览器以其原生容器录制,并在上传前将片段转换为 16 kHz 单声道 16 位 WAV。临时文件写入操作系统临时目录下的 dsh-voice- 目录,并在每次调用后删除。
voice-openai-compatible
| 键 | 默认值 | 模式 |
|---|---|---|
| baseUrl | https://api.openai.com/v1 | 必填;不带 /audio/transcriptions 的前缀;会移除一个尾部斜杠 |
| model | whisper-1 | 必填 |
| apiKeyEnv | OPENAI_API_KEY | 可选凭据引用(键的名称,绝不是其值) |
| timeoutMs | 120000 | 必填整数,>= 1 |
接受的格式为 flac、m4a、mp4、mpeg、mpga、ogg、oga、wav 和 webm,因此浏览器上传其原生录制内容(例如 WebM/Opus)。HTTP 401 和 403 会报告为“transcription is not configured”。
宿主限制
每个宿主最多同时运行 2 个转录,并再排队 4 个。超出后,请求会立即收到“transcription is unreachable”的响应(provider-unavailable 代码)。实时预览传递会在你停止时取消,因此它们不会占用最终传递所需的槽位。
RPC 与面向模型的接口
浏览器在 voice 命名空间上使用三个宿主方法:
| 方法 | 用途 |
|---|---|
| describe() | 提供程序是否已挂载并准备就绪、其接受的格式以及当前偏好设置 |
| transcribe({ audioBase64, mimeType }) | 将一段音频转为文本。失败时以 { ok: false, code, message } 值返回 |
| polish(text) | 通过部署的默认模型进行清理 |
该插件不注册任何工具、提示或会话事件。只有当您将草稿作为自己的消息发送时,转录文本才会进入对话。
隐私与安全
- 音频通过 RPC 请求从浏览器传输到 dsh 主机。使用 whisper-cpp 时,它在主机上处理,并在调用后删除。使用 openai-compatible 时,它被上传到 baseUrl。两个提供程序都不存储音频或转录文本。
- 当 polish 开启时(默认关闭),转录文本会被发送到部署的默认模型提供程序。使用 whisper-cpp 和远程模型时,音频留在本地,但文本不会。若要完全本地听写,请关闭 polish。
- API 密钥在每次调用开始时在主机上解析,绝不会发送到浏览器。describe() 仅报告是否配置了密钥。
- 麦克风选择存储在浏览器的 localStorage 中,绝不会写入设置文档。
已知限制
- 转录文本会追加到草稿末尾,而不是插入光标处。 该控件位于编辑器之外,无法读取其选区。
- 手势更改会在下一次按下后生效。 已打开的编辑器会保留其加载时的 interactionMode,直到按下按钮(此时会重新读取设置)或重新加载页面。更改后的第一次按下仍按之前的手势处理。
- 嵌入式或不安全上下文会阻止麦克风。 请参阅要求。
- 无流式传输。 主机方法是一元的,因此文本在您停止后才会到达。实时预览在每一轮都会重新转录整个音频片段。
- 提供程序检查不会加载模型。 whisper-cpp 在二进制文件解析成功且 modelPath 指向可读文件后即报告 Ready,但不会检查该文件是否为有效模型。损坏或错误的文件会在第一次听写时以二进制文件的错误失败。
- whisper-cli 在音频不可读时退出码为 0。 提供程序改为监视 stderr 中的 error:、failed to read audio 和 failed to initialize whisper context 行,并将 stderr 的末尾报告为原因。
- 仅限 Web 客户端。 终端 UI 没有捕获路径。
开发
开发时链接到上两级目录中的 deepseek-harness 检出(../../deepseek-harness,由 link: devDependencies 设置):
workspace/
├── deepseek-harness/
└── dsh-plugins/
└── dsh-voice/ <- this repository
pnpm install
npm test # Typert drift check, then vitest
npm run build # tsc emit, then tsdown bundle into lib/
npm run check:typert # only the Typert drift check
npm run typecheck # tsc --noEmit;需要与目标 harness 匹配的 harness 检出
generated/ 存放 Typert RPC 契约。只有 harness 生成器才能生成它,因此它被提交到仓库中,并且当它与 src/host/ 不一致时,npm test 会失败。请从干净的 harness 检出中重新生成它,一次一个插件:
node scripts/regen-typert.mjs ../../deepseek-harness
许可证
MIT。参见 LICENSE。同作者(navid-kianfar)的其他插件
扫码进群