@goodandready/dsh-tts 0.3.15 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +192 -87
- package/README.ru.md +195 -0
- package/README.zh.md +190 -0
- package/lib/client/worklet.js +68 -1
- package/lib/client.js +382 -8
- package/lib/engines/f5.js +43 -2
- package/lib/engines/kokoro.js +29 -2
- package/lib/engines/manager.js +235 -0
- package/lib/engines/types.js +47 -2
- package/lib/index.js +211 -13
- package/lib/text.js +31 -2
- package/package.json +2 -2
package/README.zh.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# 📦 @goodandready/dsh-tts
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
<h3>DeepSeek Harness 多引擎语音合成:本地离线神经网络引擎、低延迟流式音频(<300ms)、IT术语词典与即时通讯集成</h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-tts"><img src="https://img.shields.io/npm/v/@goodandready/dsh-tts.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-10b981.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
|
|
10
|
+
<a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
|
|
11
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/作者全部项目-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="作者全部项目"></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
20
|
+
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
21
|
+
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ⚡ 插件概览
|
|
29
|
+
|
|
30
|
+
**`dsh-tts`** 为 **DeepSeek Harness** Web 界面提供高保真智能体回复语音朗读服务。开启 **朗读智能体回复** 后,每条生成的助手回复或实时流式片段均由服务端合成并即时推流至浏览器播放。
|
|
31
|
+
|
|
32
|
+
API 密钥绝不暴露给前端:音频合成全程在服务端通过**多服务商独立备用链**执行,包括完全离线的本地神经网络模型(Kokoro-82M 和 F5-TTS)。
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
graph LR
|
|
36
|
+
subgraph Input [助手回复文本]
|
|
37
|
+
Reply[💬 智能体生成文本] --> Scrub[智能过滤 & IT术语词典]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subgraph Stream [低延迟流式音频 < 300ms]
|
|
41
|
+
Scrub --> SSE[SSE /dsh-tts/stream]
|
|
42
|
+
SSE --> Worklet[AudioWorklet PCM 处理器]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
subgraph Cache [性能缓存层]
|
|
46
|
+
Scrub --> LRU{磁盘 LRU 缓存}
|
|
47
|
+
LRU -->|命中缓存| Play[即刻推流播放]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
subgraph Fallback [TTS 引擎备用链]
|
|
51
|
+
LRU -->|未命中| Chain{生效备用链}
|
|
52
|
+
Chain -->|首选| P1[Kokoro / F5-TTS 本地离线]
|
|
53
|
+
Chain -.->|云端神经网络| P2[ElevenLabs / OpenAI / CosyVoice]
|
|
54
|
+
Chain -.->|免费云端| P3[EdgeTTS / SiliconFlow]
|
|
55
|
+
Chain -.->|系统兜底| P4[本地 Piper / eSpeak NG]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
subgraph Output [输出与集成]
|
|
59
|
+
P1 --> Store[写入磁盘缓存]
|
|
60
|
+
P2 --> Store
|
|
61
|
+
P3 --> Store
|
|
62
|
+
P4 --> Store
|
|
63
|
+
Store --> Play
|
|
64
|
+
Store --> Msg[Telegram / Discord via dsh-messenger-gateway]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
style Input fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
|
|
68
|
+
style Stream fill:#181825,stroke:#89dceb,stroke-width:2px,color:#cdd6f4
|
|
69
|
+
style Cache fill:#181825,stroke:#cba6f7,stroke-width:2px,color:#cdd6f4
|
|
70
|
+
style Fallback fill:#11111b,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
|
|
71
|
+
style Output fill:#181825,stroke:#f38ba8,stroke-width:2px,color:#cdd6f4
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🚀 核心功能
|
|
77
|
+
|
|
78
|
+
### 1. 📴 完全离线本地神经网络引擎(Kokoro CPU 与 F5-TTS GPU)
|
|
79
|
+
* **Kokoro-82M (CPU)**:8200万参数轻量级神经网络模型,通过 ONNX Runtime 在 CPU 上本地运行,零云端依赖。
|
|
80
|
+
* **F5-TTS (GPU)**:零样本扩散 Transformer 语音合成,通过本地推理守护进程运行于 NVIDIA GPU。
|
|
81
|
+
* **ModelManager 管理界面**:在设置中手动安装模型,实时显示下载进度条、SHA-256 校验和一键删除。无任何静默或自动下载。
|
|
82
|
+
|
|
83
|
+
### 2. ⚡ 实时流式音频播放(延迟 < 300ms)
|
|
84
|
+
* **AudioWorklet (`TTSWorklet`)**:高性能 Web Audio Worklet 处理器,以 24kHz 采样率无缝播放 Float32Array PCM 数据块,无可感知的爆音或缓冲区欠载。
|
|
85
|
+
* **Server-Sent Events (SSE)**:专用 `/dsh-tts/stream` 路由将合成音频片段即时推送至浏览器。
|
|
86
|
+
|
|
87
|
+
### 3. 🎙️ 语音双工对话与 VAD 打断(配合 `@goodandready/dsh-voice`)
|
|
88
|
+
* **全双工对话**:语音听写完成后自动启动回复语音合成。
|
|
89
|
+
* **VAD 打断**:检测到用户开始说话时立即静音助手语音播放。
|
|
90
|
+
* **安装保护**:若未安装 `@goodandready/dsh-voice`,相关控件将禁用并显示安装指引。
|
|
91
|
+
|
|
92
|
+
### 4. 📚 内置 IT 术语发音词典
|
|
93
|
+
* **预配置词汇表**:为常见技术缩写和开发者术语提供正确语音替换(SQL、Nginx、Kubernetes/K8s、Docker、API、JSON、YAML、GUI、CLI、CI/CD 等 25 个术语)。
|
|
94
|
+
* **交互式编辑器**:编辑规则,逐条点击 **▶ 试听** 按钮预听效果,一键加载全部 IT 术语。
|
|
95
|
+
|
|
96
|
+
### 5. 👥 多智能体角色个性化语音
|
|
97
|
+
* 为不同子智能体(`coder`、`reviewer`、`planner`、`tester` 等)分配独立的语音、服务商、模型和提示音。
|
|
98
|
+
* 自动从会话事件中识别智能体名称。
|
|
99
|
+
|
|
100
|
+
### 6. 💬 即时通讯语音消息集成(配合 `@goodandready/dsh-messenger-gateway`)
|
|
101
|
+
* 通过 `POST /dsh-tts/speak` 为 Telegram 和 Discord 机器人回复生成语音消息。
|
|
102
|
+
* 缺少网关插件时自动显示安装提示,防护性依赖检查。
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🛠️ 支持的 18 大服务商矩阵
|
|
107
|
+
|
|
108
|
+
| 服务商 Key | 对应引擎 | 默认模型 | 默认发音人 | 凭证变量名 | 说明与亮点 |
|
|
109
|
+
|---|---|---|---|---|---|
|
|
110
|
+
| `kokoro` | 本地 Kokoro-82M ONNX | `hexgrad/Kokoro-82M` | `af_bella` | *无需密钥* | **100% 离线 CPU 神经网络合成** |
|
|
111
|
+
| `f5` | 本地 F5-TTS GPU 守护进程 | `F5-TTS` | 默认 | *无需密钥* | **高保真 GPU 零样本语音合成** |
|
|
112
|
+
| `elevenlabs` | ElevenLabs API | `eleven_multilingual_v2` | `Rachel` | `ELEVENLABS_API_KEY` | 极致拟人情感音色 |
|
|
113
|
+
| `openai` | OpenAI Audio | `gpt-4o-mini-tts` / `tts-1` | `alloy` | `OPENAI_API_KEY` | 经典高清发音 |
|
|
114
|
+
| `edge` | 微软 Edge 在线 | `zh-CN-XiaoxiaoNeural` | `zh-CN-XiaoxiaoNeural` | *无需密钥* | **免费免 Key 高保真神经网络语音** |
|
|
115
|
+
| `siliconflow` | 硅基流动 CosyVoice | `FunAudioLLM/CosyVoice2-0.5B` | 默认 | `SILICONFLOW_API_KEY` | SOTA CosyVoice2 语音大模型 |
|
|
116
|
+
| `deepinfra` | DeepInfra Kokoro | `hexgrad/Kokoro-82M` | 默认 | `DEEPINFRA_API_KEY` | 极速轻量 Kokoro 开源引擎 |
|
|
117
|
+
| `fireworks` | Fireworks AI | `kokoro` | 默认 | `FIREWORKS_API_KEY` | 毫秒级 Kokoro 推理 |
|
|
118
|
+
| `minimax` | MiniMax Speech | `speech-01-turbo` | 默认 | `MINIMAX_API_KEY` | 高表现力中文旗舰发音 |
|
|
119
|
+
| `mimo` | 小米 MiMo Audio | `mimo-v2.5-tts` | 默认 | `MIMO_API_KEY` | 低延迟流式语音 |
|
|
120
|
+
| `google` | Google Cloud TTS | `gemini-2.5-flash-preview-tts` | 语种默认 | `GEMINI_API_KEY` | 谷歌 Gemini 多语种神经网络合成 |
|
|
121
|
+
| `azure` | Azure 认知语音 | `en-US-JennyNeural` | 区域默认 | `AZURE_SPEECH_KEY` | 企业级神经网络发音人 |
|
|
122
|
+
| `deepgram` | Deepgram Aura | `aura-asteria-en` | `asteria` | `DEEPGRAM_API_KEY` | 极低延迟英文输出 |
|
|
123
|
+
| `groq` | Groq TTS | `playai-tts` | `default` | `GROQ_API_KEY` | 极速推理 |
|
|
124
|
+
| `openrouter` | OpenRouter Audio | `openai/gpt-4o-mini-tts` | `alloy` | `OPENROUTER_API_KEY` | 统一路由器通道 |
|
|
125
|
+
| `custom` | 自定义 OpenAI 规范 | 可配置 | 可配置 | `CUSTOM_TTS_API_KEY` | 任意兼容 `/v1/audio/speech` 接口 |
|
|
126
|
+
| `piper` | 本地 Piper ONNX | 本地权重 | 模型默认 | *无需密钥* | 100% 离线神经网络引擎 |
|
|
127
|
+
| `espeak` | 本地 eSpeak NG | 系统合成器 | `zh` / `en` | *无需密钥* | 100% 离线轻量兜底 |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📦 安装指南
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
dsh plugin --profile web add @goodandready/dsh-tts
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
> [!IMPORTANT]
|
|
138
|
+
> 安装后请重启 Web UI(`systemctl --user restart dsh-web`)并刷新浏览器标签页。
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## ⚙️ 配置示例(`settings.yaml`)
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
dsh-tts:
|
|
146
|
+
speakReplies: true
|
|
147
|
+
enableLocalEngines: true
|
|
148
|
+
kokoroEnabled: true
|
|
149
|
+
streamingEnabled: true
|
|
150
|
+
enableItDictionary: true
|
|
151
|
+
voiceDuplexEnabled: true
|
|
152
|
+
vadBargeIn: true
|
|
153
|
+
messengerTtsEnabled: true
|
|
154
|
+
cache: true
|
|
155
|
+
cacheMaxMb: 150
|
|
156
|
+
autoDetect: true
|
|
157
|
+
chain:
|
|
158
|
+
- provider: kokoro
|
|
159
|
+
- provider: edge
|
|
160
|
+
voice: zh-CN-XiaoxiaoNeural
|
|
161
|
+
- provider: openai
|
|
162
|
+
model: tts-1
|
|
163
|
+
voice: alloy
|
|
164
|
+
roles:
|
|
165
|
+
coder:
|
|
166
|
+
provider: openai
|
|
167
|
+
voice: onyx
|
|
168
|
+
reviewer:
|
|
169
|
+
provider: edge
|
|
170
|
+
voice: zh-CN-YunxiNeural
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🤖 HTTP API 路由
|
|
176
|
+
|
|
177
|
+
* `GET /dsh-tts/stream` — 实时 SSE 音频流推送。
|
|
178
|
+
* `POST /dsh-tts/speak` — `{ text, voice?, model? }` → 返回合成音频。
|
|
179
|
+
* `POST /dsh-tts/preview` — `{ provider, model, voice, text? }` → UI 中试听语音。
|
|
180
|
+
* `GET /dsh-tts/models/status` — 查询本地模型安装状态(Kokoro、F5-TTS)。
|
|
181
|
+
* `POST /dsh-tts/models/install` — `{ engine: 'kokoro' | 'f5' }` → 启动模型下载。
|
|
182
|
+
* `DELETE /dsh-tts/models/delete` — `{ engine: 'kokoro' | 'f5' }` → 删除本地模型。
|
|
183
|
+
* `GET /dsh-tts/integrations` — 查询关联插件状态(`dsh-voice`、`dsh-messenger-gateway`)。
|
|
184
|
+
* `GET /dsh-tts/status` — 返回引擎链状态、缓存统计与引擎就绪信息。
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 📄 开源协议
|
|
189
|
+
|
|
190
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
package/lib/client/worklet.js
CHANGED
|
@@ -1,4 +1,71 @@
|
|
|
1
1
|
class TTSWorklet extends AudioWorkletProcessor {
|
|
2
|
-
|
|
2
|
+
constructor() {
|
|
3
|
+
super()
|
|
4
|
+
this.bufferQueue = []
|
|
5
|
+
this.currentChunk = null
|
|
6
|
+
this.chunkOffset = 0
|
|
7
|
+
this.playing = true
|
|
8
|
+
|
|
9
|
+
this.port.onmessage = (event) => {
|
|
10
|
+
const data = event.data
|
|
11
|
+
if (!data) return
|
|
12
|
+
if (data.type === 'chunk' && data.pcm instanceof Float32Array) {
|
|
13
|
+
this.bufferQueue.push(data.pcm)
|
|
14
|
+
} else if (data.type === 'clear' || data.type === 'stop') {
|
|
15
|
+
this.bufferQueue = []
|
|
16
|
+
this.currentChunk = null
|
|
17
|
+
this.chunkOffset = 0
|
|
18
|
+
} else if (data.type === 'pause') {
|
|
19
|
+
this.playing = false
|
|
20
|
+
} else if (data.type === 'resume') {
|
|
21
|
+
this.playing = true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
process(inputs, outputs, parameters) {
|
|
27
|
+
const output = outputs[0]
|
|
28
|
+
if (!output || !output[0]) return true
|
|
29
|
+
const channel = output[0]
|
|
30
|
+
const bufferLength = channel.length
|
|
31
|
+
|
|
32
|
+
if (!this.playing) {
|
|
33
|
+
channel.fill(0)
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let written = 0
|
|
38
|
+
while (written < bufferLength) {
|
|
39
|
+
if (!this.currentChunk || this.chunkOffset >= this.currentChunk.length) {
|
|
40
|
+
if (this.bufferQueue.length === 0) {
|
|
41
|
+
this.currentChunk = null
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
this.currentChunk = this.bufferQueue.shift()
|
|
45
|
+
this.chunkOffset = 0
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const available = this.currentChunk.length - this.chunkOffset
|
|
49
|
+
const needed = bufferLength - written
|
|
50
|
+
const toCopy = Math.min(available, needed)
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < toCopy; i++) {
|
|
53
|
+
channel[written + i] = this.currentChunk[this.chunkOffset + i]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.chunkOffset += toCopy
|
|
57
|
+
written += toCopy
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Fill remaining output buffer with silence if queue is drained
|
|
61
|
+
if (written < bufferLength) {
|
|
62
|
+
for (let i = written; i < bufferLength; i++) {
|
|
63
|
+
channel[i] = 0
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
3
69
|
}
|
|
70
|
+
|
|
4
71
|
registerProcessor('tts-worklet', TTSWorklet)
|