zsenpro/creating

plugin

把本机 GPT-SoVITS 音色接进 DSH:voice_speak 工具与 /voice 命令朗读,每条助手回复下方有喇叭按钮,设置页可切音色、调合成参数、管本地推理服务,并支持一键安装音色包。仅 Windows;需本机已有 GPT-SoVITS 仓库、训练好的权重与它的 Python 环境。

语音与音频

dsh-yourself-voice · 你的语音

把你的 GPT-SoVITS 音色接进 DSH(DeepSeek Harness):

  • 🔊 一键朗读:每条助手回复下方的操作行里多一个喇叭图标,点一下用你的音色念出来,播放中再点一下立刻停。
  • 🎛 可视化设置页:DSH「设置 → 插件 → 你的语音」,左侧竖排 tab:音色与模型 / 合成参数 / 参考音频 / 朗读与按钮 / 服务与诊断。卡片式音色库、试听、预设、参数微调全在界面里点点就行。
  • 🧩 工具与命令:voice_speak / voice_settings 两个工具,以及输入框里的 /voice 命令,给智能体和你自己都能用。
  • 🔒 全在本机:推理服务(api_v2.py)跑在 127.0.0.1,音频不出机器。

设置 → 插件 → 你的语音:音色与模型

回复下方的喇叭(待机 / 播放中,再点一次停)朗读与按钮
待机 播放中朗读与按钮

它需要什么

依赖说明
DSHWeb / Desktop 均可,插件装进 profile(默认 web)
Node.js≥ 20(用 DSH 自带的运行时也行)
GPT-SoVITS本机的 GPT-SoVITS 仓库(含 api_v2.py)+ 它自己的 Python 环境
训练好的权重GPT_weights_v4/*.ckpt + SoVITS_weights_v4/*.pth(版本按你的模型 v1/v2/v3/v4 对应)
参考音频ref/ 目录里一段 3~10 秒干净人声 + 逐字一致的文本

插件本身不训练模型:训练在 GPT-SoVITS 那边做完,这里负责「切换、调参、试听、朗读」。


安装

方式一:插件市场 / CLI(推荐)

dsh plugin --profile web add dsh-yourself-voice

方式二:本地目录(开发或不想发布时)

在 DSH 的 profile 补丁(~/.dsh/profiles/<profile>/cordis.patch.yml)里加一条:

- insert:
    - id: yourself-voice
      name: "file:///绝对路径/plugin/index.mjs?v=1"
      config:
        dataDir: "绝对路径/你的数据目录"

config.dataDir 省略时默认 ~/.dsh/yourself-voice。改完重启 DSH(或用下面的 ?v=N 热重载)。

用 dsh plugin add 装的话,profile 里的 node_modules/dsh-yourself-voice 会指向这个包, 入口名写包名即可;本仓库的 install/install.ps1 是等价的手工安装脚本。


第一次配置(5 步)

  1. 打开 设置 → 插件 → 你的语音。
  2. 服务与诊断 tab:把「GPT-SoVITS 根目录」填成你的 GPT-SoVITS 仓库路径(里面有 api_v2.py)。
  3. 音色与模型 tab:点「⟳ 刷新」,扫出来的音色会变成卡片;点卡片即切换(切换是热切换,不用重启服务)。想先听效果就点卡片右下角的 ▶。
  4. 参考音频 tab:选中 ref/ 里的音频,确认「参考文本」与音频逐字一致(同名的 .txt 会被自动填进来)。
  5. 朗读与按钮 tab:点「▶ 试听一句」确认能出声。之后每条助手回复下面就有喇叭了。

第一次朗读会拉起推理服务,冷启动大约 1 分钟(看显卡和模型大小),之后常驻、单条合成通常 3~8 秒。


界面里能改什么

tab内容
音色与模型当前音色、搜索、卡片列表(点卡片切换、▶ 试听)、高级:模型架构 / GPT 权重 / SoVITS 权重 / 模型目录、最近试听(可重播、可清空)
合成参数预设下拉(应用 / 另存为 / 删除)+ 语速、合成语言在明面上,温度、top-k、top-p、随机种子、切句方式收在「高级参数」折叠区;每项都能单项「恢复默认」
参考音频参考音频卡片、参考文本多行框、参考文本语言,可试听参考音频本身
朗读与按钮显示喇叭按钮、按钮提示文字、每轮自动朗读、最短/最长朗读字数、含代码块跳过、朗读前去 Markdown、让 DSH 出声播放
服务与诊断服务状态、地址、设备、参考音频就绪检查、启动/停止/重启、服务日志、复制诊断信息
音色包别人训练好的整套音色(权重 + 参考音频 + 参考原文),点「下载安装」一键装好并自动切过去;也能装本地 .tar.gz

改动即时保存:普通输入失焦/防抖 300ms 写盘;下拉、开关、点卡片立刻生效。 涉及模型架构 / 设备 / 端口 / 模型目录的改动会重启推理服务(界面会提示)。


音色包:把整套音色打包搬走

主包故意不带权重(一个音色动辄 200 MB,塞进 npm 包每次升级都要重下)。权重走「音色包」:一个 .tar.gz,里面是 manifest.json + GPT 权重 + SoVITS 权重 + 参考音频 + 参考原文。

装的人只需要 设置 → 插件 → 你的语音 → 音色包 → 下载安装(也可以「选本地 .tar.gz」装别人传给你的文件)。装完会自动切到新音色、重启一次推理服务;同一份包重复装会识别出「文件都在、大小一致」,不重写、不重启。

  • 装之前会逐文件校验 sha256,有一个字节不对就整包拒收,不会留下半套权重。
  • 只认 manifest 里列出的文件(路径必须落在 GPT-SoVITS 目录或数据目录内),包里有别的东西直接拒收。
  • 装过什么记在 <数据目录>/voicepack/installed.json,界面据此显示「已安装 / 更新可装」。

做包(发布者)

# 在 plugin/ 目录,Node 用 DSH 自带的那份即可
node scripts/build-voicepack.mjs --write
# 产出 dist/aemeath3-1.0.0.tar.gz,并把 size / sha256 / 文件清单写回 voicepacks.json

常用参数:--id、--label、--version、--out、--url、--gpt、--sovits、--ref、--prompt、--data-dir。 默认取 settings.json 里当前正在用的那套音色,所以「我现在听的这个」就是被打包的那个。

托管与发布

  1. 一条命令搞定(本机没有 Node 也能跑,只要系统自带 curl.exe):

    .\release-voicepack.cmd -Repo <你的GitHub用户名>/dsh-yourself-voice -Token <GitHub token>
    

    它会核对包的 size/sha256 → 建/复用 Release(tag voicepack-<id>-<version>)→ 流式上传 203 MB → 回读核对 → 只改 voicepacks.json 里 url 那一行。仓库不存在时加 -CreateRepo 自动建(必须 public,否则别人下不到附件);-DryRun 只核对不上传。

  2. 想手传也行:把 dist/*.tar.gz 传到 GitHub Releases(或任意直链对象存储),把地址写进 voicepacks.json 的 url(size、sha256、files 由 build-voicepack.mjs 填好),再发 npm 包。

  3. 界面会给 size 做进度条、装之前核对整包 sha256;url 空着时按钮是灰的,并提示发布者还没配地址。

dist/ 不进 npm 包(见 .gitignore 与 files 白名单),发布前记得单独上传。音色包里的权重能不能再分发,取决于训练者授权——自己训练的才 freely 分发。


工具与命令

voice_speak   { text, play?, speed?, textLang?, maxChars?, raw? }
voice_settings{ action: show|help|status|models|voices|set|preset|applyVoice|preview|reload|start|stop|restartServer, changes? }

/voice on | off | say <文本> | last | speed <n> | lang <代码> | voices | use <音色名> | button on|off

宿主 HTTP 接口

浏览器半边只跟这些路由说话(前缀 /yourself-voice,仅接受本机同源请求):

方法路径作用
GET/ping探活 + 服务状态 + 按钮配置
GET/POST/settings读设置 / 改设置({changes, presets?, restart?})
POST/settings/reset恢复默认(可只传 {keys:[...]})
GET/models音色卡片、权重文件、参考音频列表
POST/models/apply切换音色 / 权重 / 参考音频({voiceId} 或 {gptWeights,...})
GET/POST/engine状态 / {action: start|stop|restart}
GET/log?lines=160服务日志尾部
POST/preview试听:{text, overrides?} → wav 字节,并记进历史
GET/POST/history最近试听列表 / {action:'clear'|'delete', id?}
GET/audio?id=回放某条试听
GET/ref-audio?file=回放参考音频(只允许 ref/ 里的文件)
POST/speak助手回复正文 → wav({sessionId, messageId, text?, dry?})
GET/voicepack音色包清单(含安装状态)+ 当前安装进度
GET/voicepack/status只取安装进度
POST/voicepack/install按清单里的 id(或直接给 url)下载并安装,202 后轮询进度
POST/voicepack/install-file装本地 .tar.gz({path})
POST/voicepack/cancel取消正在进行的安装
GET/messages诊断:会话里带正文的助手消息
GET/debug诊断:客户端模块图、引擎状态、装配问题

目录结构

plugin/                  ← npm 包(要发布的就这个目录)
  index.mjs              宿主半边门面:工具、命令、自动朗读、路由装配
  api.mjs                全部 HTTP 路由(设置 / 模型 / 试听 / 朗读)
  settings.mjs           设置读写、默认值、校验、字段元数据、生成 tts_config.yaml
  models.mjs             扫权重目录、配「音色」、扫参考音频
  engine.mjs             拉起推理服务、POST /tts、本机播放
  text.mjs               Markdown → 口语文本
  voicepack.mjs          音色包:下载 / 解包 / 逐文件校验 / 落地 / 记账
  voicepacks.json        音色包清单(id、label、version、size、sha256、url)
  scripts/               build-voicepack.mjs:把当前音色打成 .tar.gz 并回填清单
  client.js              浏览器半边(手写 bundle):设置页 + 喇叭按钮
  tool.mjs               defineTool 的本地等价实现(官方包不在插件解析范围内)
  assets/                首次运行铺进数据目录的模板(start_server.ps1 / speak.ps1 / ref 说明)
  test/                  自测脚本(宿主接口 / 浏览器半边 / 音色包 / 引擎,见下)
  cordis.patch.yml       npm 装载用的补丁片段

数据目录(默认 ~/.dsh/yourself-voice):

settings.json            设置
tts_config.yaml          按设置生成,给 api_v2.py 用
ref/                     参考音频 + 文本
out/                     最近输出、服务日志、试听历史(out/previews/)
start_server.ps1         拉起推理服务(从 assets/ 铺开)

疑难排查

喇叭点了没反应 / 一直是「合成中…」 看 服务与诊断 → 看服务日志;/yourself-voice/debug 也能看到引擎状态与装配问题。 冷启动第一次要等约 1 分钟。

切了模型没生效 GPT-SoVITS 的模型架构(v1/v2/v3/v4)必须和权重所在目录对应,放错目录就会加载失败。 设置页「音色与模型 → 高级」里的「模型架构」要和权重目录一致;换架构会重启服务。

报「参考音频不存在」/ 读出来不像 ref/ 里的音频要 3~10 秒、干净、无伴奏;「参考文本」必须和音频逐字一致(差一个字都会跑调)。

端口被占用 默认 127.0.0.1:9880,和别的 GPT-SoVITS 实例撞了就在「服务与诊断」里改端口(会重启)。

音色包下载慢 / 卡在某个百分比 进度条走的是 content-length,断流会一直停在原地——点「取消」再来一次即可(已下完的部分用临时文件,重试从 0 开始)。 加速:把 .tar.gz 放本机/局域网,用「选本地 .tar.gz」装,跳过下载。

音色包装完,念出来还是原来那个声音 看「服务与诊断 → 服务状态」:装完会自动切音色并重启一次,重启要十几秒到一分钟;重启失败时界面会在音色包 tab 下面写原因(已装好,但自动切换音色失败:…),到「音色与模型」手动点一下新卡片就行。

装完看不到设置页 profile 补丁里的 id / 入口名要对应;用 dsh plugin list --profile web 确认插件是 active, 再刷新页面。宿主代码改了按下面的热重载走一遍。


开发:改完不用重启 DSH

浏览器半边(client.js)和宿主半边(*.mjs)都可以热重载:

  1. 改 plugin/*.mjs 或 plugin/client.js;
  2. 把 profile 补丁里入口名的 ?v=N 加一(例如 index.mjs?v=3 → ?v=4);
  3. 同时把插件内部所有相对 import 的 ?v= 也加一(./api.mjs?v=3 → ./api.mjs?v=4,涉及的 文件:index.mjs、api.mjs、engine.mjs、text.mjs……)。漏掉这一步会出现 The requested module './settings.mjs' does not provide an export named '…' 这类怪错: 入口被重新 import 了,但它 import 的依赖还是缓存里的旧模块(相对 import 解析出的 URL 不带 query,命中旧缓存)。工作区里有个小工具可以直接批量改: .scratch\bump-plugin-imports.mjs <N>;
  4. 用插件管理器把 yourself-voice 关一次、开一次(GUI 里点,或 plugin_manager set_plugin);
  5. 验证:out/plugin-module-loaded.json 里的 url 变成新的 ?v=N,且
curl http://127.0.0.1:19387/yourself-voice/ping

为什么要这样:Node 的 ESM 模块缓存按 URL 字符串命中,?v=N 变了才会真正重新 import; 浏览器帮手的 bundle 也是在插件激活时快照的,同样需要重新扫描一次。


自测(发布前跑一遍)

npm install                 # 装 devDependencies(jsdom / react / react-dom)
npm test                    # = test:voicepack + test:api + test:ui
npm run test:voicepack      # 只跑音色包(不用开 DSH、不联网)
npm run test:engine         # 只看引擎(不用开 DSH)

这台开发机上没有装 Node.js,所以上面几行要走插件目录里的发布脚本(它用 DSH 自带的 node+pnpm): .\publish.cmd -DryRun(= 自测 + 打包干跑 + 发布干跑)。装上 Node.js 之后 npm test 一样能用。

脚本它验证什么
test/voicepack.mjs音色包全链路(纯模块、不依赖 DSH):做一个小包 → HTTP 下载(含 302 重定向)→ 逐文件 sha256 校验 → 落地 → 记账 → 重复装识别为「一个都不重写」;被篡改的包整包拒收且不落地;不是音色包的文件被拒;--data-dir 里就地装本地包;装完 scanLibrary 能扫出新音色。
test/api.mjs宿主全部 HTTP 接口:设置读写与校验(超范围/未知键会被拒、磁盘不被写坏)、预设往返、模型扫描、参考音频与路径穿越防护、日志/引擎状态、会话正文 → /speak 干跑与 404、试听合成 → 历史回放 → 删除、音色包路由(清单/进度/不存在的 id 或文件/空闲取消)、信任围栏(sec-fetch-site/跨源 Origin/伪造 Host 都 403)。连不上 DSH 时自动跳过。
test/ui.mjs浏览器半边:按加载器的方式执行 client.js,确认注册了两个槽位、六个 tab 都能渲染、卡片与试听按钮齐、没有 <button> 嵌 <button> 这种非法结构;连不上 DSH 时退回内置样例数据,组件照样被渲染点击。
test/engine.mjs引擎本身:设置读取、生成 tts_config.yaml、拉起服务、合成一段 wav(--text 只看 Markdown → 口语转换,--play 顺便播出来)。

test/api.mjs 会真的合成一次(几秒),并且跑完把 settings.json 按字节还原。指定目标:

node test/api.mjs --host http://127.0.0.1:8080 --data-dir "C:\\...\\aemeath-voice"
node test/ui.mjs --host http://127.0.0.1:8080

上架到插件市场

这个包按市场要求准备好了(dsh.manifestVersion / dsh.client / dsh.compatibility / engines / files 白名单 / 中英 README / MIT 许可证 / 截图目录),发布步骤、精选列表 awesome-dsh-plugin 的 PR 条目模板、 以及上架前 checklist 都在 PUBLISH.md。


English

dsh-yourself-voice brings your locally trained GPT-SoVITS voice into DSH:

  • a speaker icon in every assistant message's action row — click to speak, click again to stop instantly;
  • a full settings UI under Settings → Plugins → 你的语音 (voice cards, synthesis parameters, presets, reference audio, service control, logs);
  • voice_speak / voice_settings tools and the /voice command for agents and humans alike;
  • voice packs: prebuilt voice .tar.gz bundles (weights + reference clip + transcript) installed with one click from the settings page, sha256-verified per file, auto-switched after install;
  • everything local: inference runs on 127.0.0.1, audio never leaves the machine.

Requirements: DSH, Node ≥ 20, a local GPT-SoVITS checkout (api_v2.py), trained weights, and a 3–10 s clean reference clip with its exact transcript. Install: dsh plugin --profile web add dsh-yourself-voice, then set the GPT-SoVITS root in the settings tab. MIT licensed.


许可

MIT,见 LICENSE。