DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

F-YXL /

F-YXL/dsh-mimo-speak

Verified

Read assistant replies aloud in DeepSeek Harness — Xiaomi MiMo TTS with 9 preset voices, voice cloning, and an optional Edge TTS fallback

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@6a2193db

dsh-mimo-speak

CI License: MIT dsh node

Reads assistant replies out loud in DeepSeek Harness, using Xiaomi MiMo TTS.

It adds a speaker button to every finalized reply and a settings card on the Plugins page. Click the button and the reply plays through an inline player. The MiMo API key never leaves the host: the browser sends text, the host sends back WAV.

Install

Desktop: open Plugins → Add plugin (添加插件), enter github:F-YXL/dsh-mimo-speak, restart. Or from a terminal:

dsh plugin --profile desktop add github:F-YXL/dsh-mimo-speak

Web: install into the web profile and refresh the page:

dsh plugin --profile web add github:F-YXL/dsh-mimo-speak

Pin a release instead of following main:

dsh plugin --profile desktop add 'github:F-YXL/dsh-mimo-speak#v0.1.0'

No build permission to grant: the package ships a prebuilt lib/ and has no install scripts. It also shows up in the in-app plugin market (Plugins → Discover), because the repo carries the dsh-plugin topic.

After installing, open Plugins → dsh-mimo-speak, paste the MiMo API key and press Audition. You should hear the sample voice. A speaker button appears under each finalized reply from then on.

What it does

The settings card holds the API key (write-only, stored in the host credential store), endpoint, model, one of nine preset voices or a voice-clone sample, default style, character budget and timeout. Audition speaks a sample using the card's current, unsaved edits.

Each finalized reply gets a speaker button. Before reading, the plugin strips code blocks, URLs and formatting marks, then plays what is left. The progress bar is off by default; turn it on for a draggable, keyboard-seekable track (←/→, Home/End). Stop means stop: in-flight requests are aborted, queued audio is dropped, and Esc does the same.

Read scopes: brief (the conversation model summarizes the reply into a sentence or two first), text (prose only), smart (prose plus short code), all.

There is also an agent tool, mimo-tts-say. It synthesizes speech into <session workspace>/tts/speech-<timestamp>-<random>.wav and returns the path. It writes a file; sending it as a voice message is up to the chat channel.

If MiMo fails and the edge-tts CLI is installed, the plugin can retry with a Microsoft Edge neural voice.

Requirements

  • DeepSeek Harness 0.2.2-rc.1 (the package declares engines.dsh: ">=0.2.2-rc.1 <0.3.0")
  • Node.js >= 22.19
  • A MiMo API key from https://api.xiaomimimo.com, needed at runtime only

Install from source or tarball

From a checkout. lib/ is committed prebuilt, so build is only needed after you edit src/:

git clone https://github.com/F-YXL/dsh-mimo-speak.git
cd dsh-mimo-speak
dsh plugin --profile <profile> add ./dsh-mimo-speak
dsh --profile <profile> --dump-config | grep mimo-speak   # expect a "# == dsh-mimo-speak" layer

From a packed tarball:

npm pack
dsh plugin --profile <profile> add ./dsh-mimo-speak-0.1.0.tgz

Configure

Field Default Notes
apiKeyEnv MIMO_API_KEY credential reference, not the key itself
baseUrl https://api.xiaomimimo.com/v1 /chat/completions is appended
model mimo-v2.5-tts switches to mimo-v2.5-tts-voiceclone automatically when a clone sample is set
voice mimo_default preset: mimo_default, 冰糖, 茉莉, 苏打, 白桦, Mia, Chloe, Milo, Dean
voiceSamplePath — host-local WAV, 24 kHz / 16-bit / mono, 5–15 s
style — e.g. 开心, 语速慢, 东北话; empty lets the model decide
maxChars 2000 longer text is clamped at a sentence boundary
chunkChars 120 size of one playback chunk
timeoutMs 60000
autoRead false read each newly finalized reply automatically
readScope smart brief / text / smart / all
fallbackEngine none none or edge
edgeVoice zh-CN-XiaoxiaoNeural (Xiaoxiao) any edge-tts voice id

To keep the key out of the profile config, override the row in your profile's cordis.patch.yml:

- id: mimo-speak
  config:
    apiKey: !!js process.env.MIMO_API_KEY

Security

The API key is written through the host credential store (credentials/set) and never ends up in a repo file, a log line or the browser. Every /api/mimo-speak/* route passes ctx.connection.requestRejection first, so a request from outside the app's trusted origin is refused before any key is used. Request bodies are capped at 1 MiB; overlong text is cut at a sentence boundary.

npm run check:privacy scans every tracked file for credentials, home paths and email addresses, plus names or hostnames you list in the git-ignored .privacy-markers. npm run check:privacy:history also walks every blob and commit identity in the history, because git log -p publishes the past too. CI runs the file scan on every push.

If you find a vulnerability, use SECURITY.md, not a public issue.

Development

npm install
npm run build            # tsc -> lib/node, tsdown -> lib/client.js
npm test                 # 65 unit tests (node --test)
node scripts/stub-e2e.mjs        # full HTTP round-trip against a local stub, no key needed
MIMO_API_KEY=... node scripts/live-e2e.mjs   # one real request against the MiMo platform

lib/ is committed on purpose, so dsh plugin add needs no build step. After editing src/, run npm run build and commit lib/ in the same commit; CI fails when the committed build output is stale.

See CONTRIBUTING.md for commit conventions and the version-pinning workflow, MAINTAINING.md for release steps.

Pinning to a dsh release

The package pins one set of @deepseek-ai/dsh-* dependencies; npm will not pick a matching build for the host. Two declarations do the gating:

  • dsh.compatibility.dshReleases lists the releases this build supports ({ "0.2.2-rc.1": "compatible" }). The installer refuses or warns when the host version is not on the list.
  • engines.dsh is an npm-style range (>=0.2.2-rc.1 <0.3.0).

Two things to watch:

  1. All @deepseek-ai/dsh-* packages share one release version (0.2.2-rc.1), but @deepseek-ai/cordis and @deepseek-ai/schemastery have their own (~4.1.0 / ~3.19.0 here). Bumping everything together 404s.
  2. Since 0.2.0, dsh-scope is a peer of dsh-session / dsh-api-remotes and must be in both peerDependencies and devDependencies, or npm install fails with ERESOLVE.

Use the script instead of editing version numbers by hand:

node scripts/pin-dsh-version.mjs <release>            # rewrite package.json, print next steps
node scripts/pin-dsh-version.mjs <release> --install  # ... and run npm install
node scripts/pin-dsh-version.mjs <release> --self     # ... and bump this plugin's version too
node scripts/pin-dsh-version.mjs <release> --dry-run  # preview only

How it maps to the plugin spec

Spec Where it lands here
Packaging & installing plugins (bundle) package.json declares dsh.bundle.patch + cordis.patch.yml inserts the id: mimo-speak row; the same package carries the dsh.client browser half
Plugin configuration (no hardcoded knobs) every parameter is a Schemastery Config field with .volatile(); the key is role('secret')
Instant config forms ctx.configForms (mimo-speak namespace) + the standard SettingsFormModel form
Web client slots two seats: plugins.bundle.config and conversation.chat.assistant-actions
Web server five routes under /api/mimo-speak/*, each behind ctx.connection.requestRejection (Host/Origin + browser auth), so a bare LAN request cannot spend your API quota
Client module system lib/client.js is a lazy-CJS factory (window.__ModuleLoader__.load); the build-time purity gate keeps cross-plugin imports type-only

MiMo wire format

POST {baseUrl}/chat/completions, header api-key: <key>, body:

{
  "model": "mimo-v2.5-tts",
  "audio": { "format": "wav", "voice": "mimo_default" },
  "messages": [{ "role": "assistant", "content": "<style>开心</style>要说的话" }]
}

Voice cloning switches to mimo-v2.5-tts-voiceclone and puts the reference audio in audio.voice as a data URI:

{
  "model": "mimo-v2.5-tts-voiceclone",
  "audio": { "format": "wav", "voice": "data:audio/wav;base64,<base64 wav>" },
  "messages": [{ "role": "assistant", "content": "要说的话" }]
}

The response carries base64 audio in choices[0].message.audio.data — either a RIFF container (passed through) or raw PCM (wrapped into a 24 kHz / 16-bit / mono WAV by the plugin).

Known limits

  • Reading is per message: the action row belongs to one finalized reply, so reasoning blocks and tool calls are not read. Code blocks become "代码略".
  • Text longer than maxChars is read from the start and cut at a sentence boundary, with a hint next to the action row.
  • Desktop (Electron) goes through the IPC fetch bridge; the route paths are unchanged.
  • Voice input is the official @deepseek-ai/dsh-experimental-voice-input-bundle, not this plugin. This one stops speaking as soon as the microphone opens, so the two do not feed back into each other.

License

MIT © F-YXL. Commercial use is permitted — that is what MIT means.


中文说明(完整)

这是什么

把助手回复「读出来」的 DSH 插件:「插件」页里的配置卡片 + 每条已定稿助手回复动作行里的音量按钮 + 带可拖动进度条的内联播放器。语音由小米 MiMo TTS(mimo-v2.5-tts)合成,API Key 只存在于宿主侧,浏览器只发送文本、收到 WAV。

面向 deepseek-harness 0.2.2-rc.1(engines.dsh: ">=0.2.2-rc.1 <0.3.0")。

功能

  1. 「插件」页 → dsh-mimo-speak 详情页的配置卡片
    • API Key(只写输入框:粘贴即经官方 credentials/set 通道存入宿主凭据存储,页面不回显;显示「已配置/未配置」,清空保存不改动已存密钥)
    • 接口地址(默认 https://api.xiaomimimo.com/v1)、模型(默认 mimo-v2.5-tts)
    • 预置音色(官方 9 音色下拉:mimo_default、冰糖、茉莉、苏打、白桦、Mia、Chloe、Milo、Dean)/ 音色克隆样本(宿主本地 WAV 路径,24 kHz / 16 bit / 单声道,5–15 s)
    • 默认风格(如「开心」「语速慢」「东北话」;留空由模型自行判断,推荐)、单次最大字符数、请求超时
    • 试听:用卡片当前(含未保存修改)的风格/音色合成一句示例,内联播放
    • 标准表单行为:脏值预览、「已覆盖/恢复默认」徽标、保存/放弃、只读部署提示
  2. 每条已定稿助手回复底部的音量按钮
    • 点击 → 宿主合成(Markdown 归一化:不朗读代码块/URL/标记符号)→ 自动播放
    • 进度条可关(默认关):关掉只留播放/停止按钮;打开才有可拖动进度条(指针拖拽、单击定位、键盘 ←→/Home/End)
    • 停止是真取消:中断在途请求、作废本轮、清空已排程音频;Esc 同样生效
    • 同时只有一条在播;播放中动作行不再悬停淡出
    • 失败就地提示(未配置/超时/上游拒绝/无文本等稳定错误码 → 本地化文案)
  3. 朗读范围四档:简要播报(先摘要再念:用当前对话模型把回复浓缩成 1–2 句口播摘要,约 80 字;模型不可用时退回规则摘要)/ 只读正文 / 智能(正文+短代码)/ 全部
  4. Agent 工具 mimo-tts-say
    • 参数:text(必填)、voice、style(可选覆盖)
    • 行为:合成 → 写 <会话工作区>/tts/speech-<时间戳>-<8位随机>.wav → 返回绝对路径 + 字节数 + 时长
    • 只产出文件,不发消息:要真的发出去,把返回的 path 交给当前聊天渠道自己的发语音工具
    • 为什么写进工作区:渠道侧的发送工具通常只接受工作区内的路径
  5. 安全边界:密钥只在宿主;/api/mimo-speak/* 路由先过 Connection 信任栅栏;请求体 1 MiB 上限;文本超预算在句末截断朗读开头。工具和网页朗读共用同一套 settings/凭据,不额外存密钥。

安装

桌面端:打开「插件 → 添加插件」,填入 github:F-YXL/dsh-mimo-speak,然后重启 DSH;也可以在终端执行:

dsh plugin --profile desktop add github:F-YXL/dsh-mimo-speak

Web:装到 web profile,重启后刷新页面:

dsh plugin --profile web add github:F-YXL/dsh-mimo-speak

仓库公开后,它也会出现在应用内的「插件市场 / Discover」里(仓库带了 dsh-plugin topic,市场会自动收录)。从源码构建请见英文「Install from source or tarball」一节;本包自带预构建的 lib/ 且没有 install 脚本,无需 pnpm 构建授权。

不想把密钥写进配置文件时,在 profile 的 cordis.patch.yml 里覆写该行:

- id: mimo-speak
  config:
    apiKey: !!js process.env.MIMO_API_KEY

与官方语音输入插件配合

语音指令的识别交给官方组合包,本插件只负责播报,两者互补:

# 版本要与宿主对齐:DSH 0.2.2-rc.1 → 钉到同版本
dsh plugin --profile <profile> add @deepseek-ai/dsh-experimental-voice-input-bundle@0.2.2-rc.1

它是本地 SenseVoice 转写(首次使用下载运行时),识别结果插入输入框草稿,不会自动发送。本插件检测到麦克风被打开时会立即停止朗读,所以「说完 → 发送 → 自动朗读回复」这条链路不会把播报录进去。

目录

dsh-mimo-speak/
├── package.json            # dsh.bundle + dsh.client manifest,engines.dsh 锁 >=0.2.2-rc.1 <0.3.0
├── cordis.patch.yml        # bundle 层:插入 id: mimo-speak 行(两侧共用)
├── tsconfig.json           # tsc → lib/node(Node 半侧 + 类型)
├── tsdown.config.ts        # 官方 clientBundle 的树外复刻 → lib/client.js
├── icon.svg  locale/{zh,en}.json
├── src/
│   ├── index.ts            # Host 插件:Config schema + Web 路由
│   ├── tts.ts              # MiMo TTS 客户端(/chat/completions + audio 输出块)
│   ├── wav.ts  normalize.ts# PCM→WAV 封装;Markdown→可朗读文本
│   ├── settings.ts         # 两侧共享:命名空间/路由/默认值/线格式类型
│   └── client/             # 浏览器半侧:两个 slot 席位、播放器、配置卡片
├── tests/                  # 65 项单测(node --test)
└── scripts/
    ├── stub-e2e.mjs        # 本地桩服务端到端(无需真实密钥)
    ├── live-e2e.mjs        # 真实 MiMo API 端到端(需 MIMO_API_KEY)
    └── check-privacy.mjs   # 推送前的隐私扫描

构建与测试

npm install
npm test                          # 65 项单测
npm run build                     # tsc + tsdown → lib/
node scripts/stub-e2e.mjs         # 真实 HTTP 桩端到端(96,044 字节 WAV)
npm run check:privacy             # 隐私扫描,必须 0 命中

适配版本与「按版本选适配」

分发不会按宿主版本自动下载对应适配版本。 本插件打包时把 @deepseek-ai/dsh-* 依赖锁死在某一个 release(当前 0.2.2-rc.1),别人拉到的就是同一份 package.json。真正的版本闸门是两处兼容性声明:

  • dsh.compatibility.dshReleases:官方字段,精确列出本包适配的 release。安装器/市场在对方 dsh 版本不在白名单时拒绝或警告。
  • engines.dsh:npm 风格的范围闸门(当前 >=0.2.2-rc.1 <0.3.0)。

运行时实际用的 @deepseek-ai/dsh-* 实例由宿主 dsh 自带,插件代码只是复用宿主环境里的版本。想让旧版 dsh 也能用,得单独发一份 pin 回旧版本的包(scripts/pin-dsh-version.mjs 一键生成),即「多版本并行发布」。

三个容易踩的坑:

  1. @deepseek-ai/dsh-* 整条线共用同一个 release 版本号。
  2. @deepseek-ai/cordis 与 @deepseek-ai/schemastery 是独立版本号,不跟随 release。
  3. 0.2.0 起 dsh-scope 成为 peer 依赖,需同时写在 peerDependencies 与 devDependencies 里,否则 npm install 报 ERESOLVE。

MiMo 线格式

POST {baseUrl}/chat/completions
头:api-key: <key>
体:{ "model": "mimo-v2.5-tts",
      "audio": { "format": "wav", "voice": "mimo_default" },
      "messages": [{ "role": "assistant", "content": "<style>开心</style>要说的话" }] }

音色克隆时切到 mimo-v2.5-tts-voiceclone 模型,参考音频以 Data URI 放进 audio.voice。响应 choices[0].message.audio.data 为 base64 音频(RIFF 或裸 PCM,裸 PCM 由插件包成 24 kHz / 16 bit / mono WAV)。

已知边界

  • 按消息朗读:动作行属于该条已定稿回复,朗读其正文(不读思考块与工具调用);代码块以「代码略」代替。
  • 文本超过「单次最大字符数」时朗读开头并在句末截断。
  • Desktop(Electron)走 IPC 桥接 fetch,路由路径不变。
  • 语音输入(麦克风)是官方 @deepseek-ai/dsh-experimental-voice-input-bundle 的职责,本插件不重复实现。

许可证

MIT © F-YXL。MIT 允许商用——这是它和「禁止商用」许可证的关键区别;如果你需要禁止商用,请自行更换为 PolyForm Noncommercial 等非开源许可证。

—/ 5

No ratings yet

Verified DSH bundle

Commit 6a2193db1413

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout