Skip to content
dsh.fish
Bundle

dsh-live2d-voice

Live2D session view with realtime voice: continuous voice input, streaming TTS, subtitle translation, multi-model catalog, standalone entry / Live2D 实时语音会话视图:连续语音输入、流式 TTS、字幕翻译、多模型目录、独立入口

Source
john-walks-slow
stars
1 stars
License
MIT
Updated
Updated yesterday

Readme

# dsh-live2d-voice

DSH 插件:在会话视图里新增 **Live2D** tab——Live2D 角色随对话实时开口说话(口型同步 + 表情切换 + 字幕),AI 回复经 Fish Audio TTS 流式合成语音推送到浏览器播放;🎙 连续语音输入(本地 VAD 分句 + 火山引擎 ASR)让你全程免键盘对话。

能力总览:

- 🎭 **Live2D 角色舞台**:Cubism 4 模型(pixi.js v7 渲染),说话时 `ParamMouthOpenY` 随音频 RMS 包络驱动口型,句级情绪标签切换表情
- 🔊 **流式语音**:LLM 回复边生成边按句合成(Fish Audio,44100Hz PCM),SSE 推送、浏览器端队列播放;断句 / abort 与模型流同步
- 🎙 **连续语音输入**(v0.2.0 → 流式化):浏览器本地 VAD 分句 → **实时推流**(火山 `bigmodel_async` 双向流式 + nostream 二遍,边说边出字幕,说完 ≈0.6s 定稿)→ 自动提交对话;支持说话打断(barge-in)与扬声器回声防护;`asrMode: nostream` 可切回一次性整句识别(25 语种含日语)
- 💬 **双语字幕**:用户与 AI 台词均显示(14s 淡出),可一键隐藏;角色台词自动翻译成目标语言(v0.3.0,走会话同款 LLM,一行原文一行译文)
- 🎭 **多模型目录**(v0.3.0):`modelPath` 指向多个角色子目录时 ⚙ 出现模型切换器,即选即换
- 🗂 **per-workspace 覆盖**(v0.3.0):按工作区路径覆盖音色/模型/语言等表现层配置,同一插件多工作区多角色
- ⛶ **沉浸全屏**(v1.1.0):一键全屏成为角色终端;全屏+语音监听时自动保持屏幕常亮(Wake Lock)——把旧手机放在桌上当角色挂机
- 👁 **视线追踪**(v1.1.0 实验性):前置摄像头注视追踪,角色会看着你的脸(MediaPipe FaceLandmarker,经插件路由懒加载,手机无需科学上网;⚙ 可开关)
- 📸 **看向你**(v1.1.0 实验性):模型可调用 `look_at_user` 工具从前置摄像头拍一张照片并真正"看到"你(仅在该会话的 Live2D 视图打开时存在此工具)
- 📱 **陀螺仪视差**(v1.2.0 实验性):倾斜手机,角色的头部/身体/视线与位置随之偏移——"角色在屏幕玻璃后面"的立体错觉(开启时以当前握持姿势为正中;⚙ 可开关)
- 🚪 **独立入口**(v1.3.0,v1.4.1 起 GUI 的 Live2D 界面有"独立入口"按钮直达):`/live2d-voice/app?session=<id>`——无 GUI 界面的单会话角色页,自带全部能力(语音/字幕/翻译/设置/全屏/实验特性);冷会话自动经会话控制器恢复(带完整 preset),支持 steer 插话
- 🗂 **会话选择页**(v1.6.0):`/live2d-voice`——列全部会话(标题/目录/运行状态/相对时间),一键新建直达角色页;配套无头 API:`POST /live2d-voice/session`(幂等创建/领养会话)、`GET /live2d-voice/sessions`(会话列表),独立启动器与 exe 打包的基石
- 🎨 **deepseek娘**(v1.3.0 内容):AI 生成贴图重皮的角色模型(基座 haru,gemini 整图重绘 + alpha 回贴 + UV 覆盖校验),参数驱动与基座逐位一致;见模型库 CATALOG.md
- 🎭 **第三人称模式**(v1.5.0):开启后玩家也有自己的角色与音色——双角色同台(左玩家、右 AI);输入先经润色/翻译层(可选)变成玩家角色的台词,由玩家皮套先念出(TTS + 口型 + 字幕「你」徽章),AI 皮套再回应
- 📹 **视频通话模式**(v1.6.0):`liveMode: "call"`——AI 角色居中大画面,你的化身以可拖动小窗(PiP)同屏:口型实时跟随麦克风、头/眼跟随前置摄像头视线追踪;输入原样直达 AI(不润色、不代播),像一场真的视频通话
- ⌨ **键盘输入**:在 Live2D 页直接对话(走 GUI 会话通道,冷会话自动创建/resume agent)
- ⚙ **音色快切**:HUD 内置 5 个预设音色,即选即生效
- 🔇 **静音开关**:只看口型不听声

## 安装

```bash
# 在 DSH profile(如 ~/.dsh/profiles/web)里
pnpm add dsh-live2d-voice        # 或 link: 本地开发
# package.json 的 dsh.profile.bundles 追加 "dsh-live2d-voice"
pnpm install && sv restart dsh   # 重启 dsh 生效
```

要求:DSH ≥ 对应 0.1.5-rc.3 host 包系列;浏览器需 WebGL。

## 配置

配置文件:`<DSH_HOME>/live2d-voice.json`(如 `~/.dsh/live2d-voice.json`)。保存后刷新页面即生效(音色也可在 HUD 里直接切换,会写回该文件)。

```jsonc
{
  // 角色模型目录:包含 .model3.json 的文件夹(Cubism 4)
  "modelPath": "/root/.dsh/live2d-voice-models/haru",

  // Fish Audio 音色 id(见下方预设表)
  "voiceId": "0c7771ca5910484e8a4933068017fcee",

  // TTS 模型名
  "ttsModel": "s2.1-pro-free",

  // Fish Audio API key:内联数组,或指向 key 文件(每行一个,或 JSON 数组)
  // 多 key 自动轮询:401/402/429 时切换下一个
  "apiKeys": [],
  "apiKeyFile": "/root/.config/fish-audio/keys.json",

  // 情绪标签 → 模型表情(Haru 的表情索引;值也可以是表情名字符串)
  "emotionMap": {
    "neutral": 0, "joy": 1, "sappiness": 1, "sadness": 2,
    "anger": 3, "surprise": 4, "fear": 5, "disgust": 6, "shy": 7
  },

  // 语音输入:火山引擎 ASR 凭证文件(JSON:apikey,或 appid+accessToken)
  "asrCredentialsFile": "~/.config/volc-asr/credentials.json",

  // 语音输入识别语言:auto(自动检测中/日/英等)| zh | ja | en
  "sttLanguage": "auto",

  // 角色说话语言:ja(默认,角色始终用日语交流)| zh | en —— 注入 live 模式提示词
  "speechLanguage": "ja",

  // 角色台词的翻译目标语言:zh / ja / en / ko…(languageLabel 支持即有效);off 关闭
  "subtitleLanguage": "zh",

  // 多模型目录下的当前选中角色(⚙ 里切换会写回)
  "modelSelection": "",

  // 自定义提示词:仅在 Live2D 语音模式下随情绪标签协议一起注入
  // 例:"无论用户说什么语言,总是用日语自然交流。用户会看到字幕翻译。"
  "speechPrompt": "",

  // ── 舞台模式(v1.6.0)──
  // first = 单角色(默认)| third = 双角色舞台剧(玩家化身先念台词,见下方 player* 字段)
  // call = 视频通话:输入直达 AI,玩家化身小窗实时跟随你的口型与视线
  // 旧配置 thirdPerson: true 加载时自动迁移为 "third"
  "liveMode": "first",
  // 玩家化身角色(third 的左侧角色 / call 的小窗;多模型目录中的角色名;空 = third 仅语音"画外音"、call 无小窗)
  "playerModelSelection": "",
  // 玩家音色(Fish Audio 预设任选;与 AI 同音色时 ⚙ 会提示换一个)
  "playerVoiceId": "ed3a1c523b524870a85a5a76cb1e0c3d",
  // 润色/翻译层:把输入改写成玩家角色的台词(失败自动回退原文)
  "playerPolish": true,
  // 玩家角色说话语言(润色目标语言;auto = 不限)
  "playerSpeechLanguage": "zh",
  // 玩家人设:润色时的角色设定(语气/口癖/自称等)
  "playerPrompt": "",
  // 玩家情绪标签 → 玩家模型表情(同 emotionMap 格式)
  "playerEmotionMap": {},

  // per-workspace 覆盖(可选):按会话工作区的绝对路径覆盖表现层配置
  // "workspaces": {
  //   "/root/projects/xxx": { "voiceId": "abf4fa2e25634b41aadc4e0ef9ddaea5", "speechLanguage": "zh" }
  // }
  "workspaces": {}
}
```

| 字段 | 默认 | 说明 |
| --- | --- | --- |
| `modelPath` | `""` | 必填。两种形态:目录直接放一个或多个 `.model3.json`(多文件时同样出现 ⚙ 切换器);或放多个角色子目录(每个一级子目录一个 `.model3.json`)。为空时页面显示配置引导,也不会注入任何提示词;可覆盖于 workspaces(资产路由多根解析) |
| `modelSelection` | `""` | 多模型目录下当前选中的角色名(空 = 第一个) |
| `voiceId` | rem | Fish Audio 参考音色 id |
| `ttsModel` | `s2.1-pro-free` | Fish Audio TTS 模型 |
| `apiKeys` | `[]` | 内联 key 列表(优先于 `apiKeyFile`) |
| `apiKeyFile` | `""` | key 文件路径(每行一个 key 或 JSON 数组) |
| `asrCredentialsFile` | `~/.config/volc-asr/credentials.json` | 火山 ASR 凭证(JSON 含 `apikey` 或 `appid`+`accessToken`);未配置时 🎙 点击给出引导提示 |
| `sttLanguage` | `"auto"` | 语音识别语言;auto 用火山 `enable_auto_lang` 自动检测(仅 `nostream` 模式生效;流式模式自动识别中/英+方言) |
| `asrMode` | `"stream"` | 语音识别传输:`stream`(默认)实时推流(边说边出字幕、说完 ≈0.6s 定稿;不支持日语/韩语输入);`nostream` 一次性整句识别(延迟 ≈1.3s;25 语种含日语) |
| `speechLanguage` | `"ja"` | 角色说话语言,注入"始终用 X 语言交流"指令;`auto` 跟随用户语言(不注入语言指令)。日语指令中的"(用户会看到字幕翻译)"承诺仅在 `subtitleLanguage` 实际生效(非 `off` 且不同于角色语言)时出现 |
| `subtitleLanguage` | `"zh"` | 角色台词的翻译目标语言(`off` 关闭;与会话生效的 `speechLanguage` 相同或 `speechLanguage=auto` 时可能整句透传,按需配置;可覆盖于 workspaces) |
| `sentenceSubtitles` | `true` | 逐句字幕:开启(默认)时每句独立 TTS、字幕逐句跟随发音;关闭时句子攒成段落块,一次 TTS 合成、字幕按段显示(语音更连贯、首音稍晚) |
| `eyeTracking` | `false` | 实验性:前置摄像头视线追踪(⚙ 面板可切换;首次开启经插件路由下载视线模型) |
| `gyroParallax` | `false` | 实验性:陀螺仪视差(DeviceOrientation → 头部/身体/眼球角度 + 位置偏移;开启时校准正中姿势) |
| `emotionMap` | 9 情绪默认表 | 标签 → 表情索引/名称(neutral/joy/sappiness/sadness/anger/surprise/fear/disgust/shy) |
| `speechPrompt` | `""` | 自定义指令,仅语音模式生效(HUD ⚙ 里也能编辑) |
| `liveMode` | `"first"` | 舞台模式(v1.6.0,⚙ 面板与系统设置的三段选择器可切,写回配置):`first` 单角色(默认)/ `third` 双角色舞台剧(玩家化身先念)/ `call` 视频通话(输入直达 AI,化身小窗跟随);旧配置 `thirdPerson: true` 自动迁移为 `"third"` |
| `playerModelSelection` | `""` | 玩家化身角色(`third` 的左侧角色 / `call` 的小窗化身;多模型目录中的角色名);空 = third 无形象"画外音"模式、call 无小窗,未选模型也可开模式 |
| `playerVoiceId` | 元气少年音 | 玩家音色(Fish Audio 参考音色 id,预设任选;与 AI 音色相同时 ⚙ 提示换一个) |
| `playerPolish` | `true` | 润色/翻译层:把输入改写成玩家角色的台词(按 `playerPrompt` 人设与 `playerSpeechLanguage`);关闭 = 原文直念 |
| `playerSpeechLanguage` | `"zh"` | 玩家角色说话语言(润色目标语言;`auto` = 不限) |
| `playerPrompt` | `""` | 玩家人设,润色提示词的一部分(语气/口癖/自称等) |
| `playerEmotionMap` | 9 情绪默认表 | 玩家情绪标签 → 玩家模型表情索引/名称(同 `emotionMap` 格式) |
| `workspaces` | `{}` | per-workspace 覆盖:`{ "<工作区绝对路径>": { voiceId, modelPath, modelSelection, speechLanguage, sttLanguage, subtitleLanguage, speechPrompt, emotionMap 任选 } }`;凭证类字段只在全局层 |

**提示词注入是会话级、按需生效的**:只有当前会话打开了 Live2D 视图(SSE 在连)时,才会注入"语音输出格式 + 情绪标签"提示词(含 `speechPrompt` 自定义指令);普通 Chat 会话完全不受影响。从 Live2D 切回普通对话后的首轮回复会自动附上一段"已退出语音模式"的提醒,模型随即恢复正常 Markdown/代码块输出,对话可以无缝续接。

> **预设兼容**:语音模式指令以**用户消息注入**(`agent/pre-step` 向组装消息追加一条 role:user 的插件消息,参考 dsh-mnemon 的注入方式)而非 systemPrompt section——不受任何 preset 的 `complete: true` 语义影响,`chat` 等极简预设下同样生效。注入消息带 `source: {kind:"plugin", plugin:"dsh-live2d-voice"}` 标识,会话日志可见。

内置音色预设(HUD ⚙ 里可直接切换):

| 预设 | voiceId |
| --- | --- |
| Rem · 温柔女仆 | `0c7771ca5910484e8a4933068017fcee` |
| 元气女仆 | `abf4fa2e25634b41aadc4e0ef9ddaea5` |
| 芙莉莲 · 知性 | `c174516c799a42e7be88b96c86cfbd3e` |
| 芙宁娜 · 娇俏 | `3fd70bbcdb6342df8c0c4143b958944b` |
| Cute Girl · 甜美 | `0c54c26032024142bf6339dc4d4aca1b` |

### 角色模型

任意 Cubism 4 模型(`.model3.json`)均可,放入一个目录并把 `modelPath` 指向它。例如官方示例 Haru:

```bash
mkdir -p ~/.dsh/live2d-voice-models
cd ~/.dsh/live2d-voice-models
# 从 live2d 官方示例仓库下载 haru 的全部文件(jsdelivr 镜像 guansss/pixi-live2d-display 仓库)
# haru.model3.json + textures + motions + expressions,共 19 个文件
```

`emotionMap` 的值对应模型的 expressions 列表索引(或名称),不同模型的表情数量与顺序不同,按需调整。

## 使用

1. 打开任一会话,顶部视图 tab 切到 **Live2D**
2. 点 HUD 的 ⌨ 打开输入框直接对话;或点 🎙 开启连续语音输入——说话自然停顿后一句自动识别、自动发送(说完即可继续说下一句)
3. AI 说话时直接开口即可**打断**(barge-in 立即静音角色并转向你的新输入);正在生成回复时的新语音会以 steer 模式插队
4. 表情/口型/字幕随回复自动驱动;🔇 静音、💬 字幕开关、⛶ 全屏、⚙ 音色/模型/语言/视线追踪
5. 全屏 + 语音监听时屏幕保持常亮——适合把一台旧手机常驻 Live2D 页当角色终端;长时间静默挂机无内存增长,说话即响应

角色与字幕(v0.3.0):

- `modelPath` 放多个角色子目录时,⚙ 面板出现「角色模型」切换器,点击即换(模型立即重新加载)
- 角色台词若与 `subtitleLanguage` 不同语言,会自动翻译并以小字附在原句下方(用当前会话同款模型,逐句异步,不影响语音节奏)
- `workspaces` 按工作区路径覆盖配置:不同工作区的会话可以各有各的音色/角色/语言

第三人称模式(v1.5.0):

- ⚙ 面板或系统设置开启「第三人称」后,舞台变双角色同台:左侧玩家角色(`playerModelSelection`)、右侧 AI 角色;开关切换即时重排、不重载模型
- 键盘/语音输入不再直接交给 AI:先(可选,`playerPolish`)润色成玩家角色的台词 → 玩家音色念出(字幕行带「你」徽章)→ AI 角色回应;**润色后的台词才是进入会话日志的消息**(Chat tab 所见即玩家皮套所说)
- 提交后字幕区先出一行置灰的「酝酿中…」瞬态提示,玩家台词到达即替换;关闭润色则原文直念
- 未选玩家模型也可开启:无形象的"画外音"模式(润色/语音/字幕照常,仅无口型与形象)
- 玩家台词播放中再次开口(键盘或语音)会打断当前播放,barge-in 语义与第一人称一致;AI 回应中插话同样切断 AI 语音

视频通话模式(v1.6.0):

- ⚙ 面板或系统设置把舞台模式切到「视频通话」后:AI 角色回到居中大画面(取景与第一人称一致),你的化身出现在一个可拖动的小窗(PiP)里
- 小窗化身实时"演你":🎙 监听中口型随麦克风电平开合;开启视线追踪时头/眼/身体朝你人脸在摄像头画面中的方向转动
- 小窗整窗可拖动,松手自动吸附到最近的角落;不选玩家模型则没有小窗(纯 AI 大画面通话)
- 输入原样直达 AI——不润色、不由化身代播(这是与第三人称的核心区别),AI 回复照常 TTS 朗读并驱动大画面角色
- 旧配置 `thirdPerson: true` 加载时自动迁移为 `liveMode: "third"`,行为不变

语音输入细节:

- 识别延迟(流式模式,默认):**边说边出字**(开口 ≈1s 出首字、实时跟进),说完 ≈0.6s 出定稿并自动提交;`asrMode: nostream` 时为 说完话 0.5s(VAD 判停)+ 识别约 1.3s
- 流式模式走火山双向流式优化版(`bigmodel_async` + `enable_nonstream` 二遍识别):实时字幕与 nostream 级准确率兼得;**不支持日语/韩语等小语种输入**(官方语言参数仅 nostream 端点支持,日语实测空文本)——需要日语输入时把 `asrMode` 切回 `nostream`
- 角色的语音可能被麦克风拾到(外放场景):识别结果与角色最近台词高度重合时会被当作回声丢弃
- 浏览器要求 HTTPS 或 localhost(getUserMedia 限制);手机浏览器同样可用
- `sttLanguage: auto` 实测覆盖中文/日语/英语自动检测(nostream);流式模式下中/英+方言自动识别,无需指定语言;`ja`/`zh`/`en` 锁定仅在 nostream 模式生效

注意:

- 情绪标签(如 `[joy]`)会随消息写入会话日志——切回 Chat tab 可见完整对话记录
- Live2D 页不显示历史消息;要看历史请切回 Chat tab
- TTS 只对**当前打开了 Live2D 视图的会话**生效,其他会话不受影响

## 工作原理(简)

```
                        ┌── 🎙 语音输入 ──────────────────────────────────────┐
                        │ 浏览器 AudioWorklet:48k→16k 降采样 + 能量 VAD     │
                        │ 分句(250ms 预滚 / 20s 上限)                      │
                        │   │ 实时 PCM 帧(流式,默认)                      │
                        │   ▼                                               │
                        │ WS /live2d-voice/asr/ws  × 每句一个连接            │
                        │   │ 火山 bigmodel_async + 二遍识别(200ms 攒帧)   │
                        │   ▼ interim 实时字幕(SSE asr-interim)            │
                        │ 定稿文本(SSE asr-final)── 自动提交 ──► 用户输入   │
                        │ (asrMode: nostream 时走 POST /asr/recognize 整句)│
                        └───────────────────────────────────────────────────┘
用户输入 ──► GUI 会话通道 (session/prompt) ──► agent 回复流
                                                  │ llm/stream tap(仅 Live 监听的会话)
                                                  ▼
                              句子切分 + 情绪标签提取([joy] 等)
                                                  │
                        ┌─────────────────────────┼─────────────────┐
                        ▼                         ▼                 ▼
                  expression 事件          subtitle 事件      Fish TTS (流式 PCM)
                        │                         │                 │
                        └────────────► SSE /live2d-voice/stream ◄───┘
                                                  │
                                    浏览器:表情切换 / 字幕 / 播放队列
                                                  │ RMS 包络
                                                  ▼
                                             ParamMouthOpenY
```

- 情绪协议(会话级按需注入):仅当该会话的 Live2D 视图打开时,每轮以**用户消息注入**(`agent/pre-step` 追加 role:user 插件消息,`source.plugin="dsh-live2d-voice"`)注入"语音输出格式"指令(含 `speechLanguage` 指令与自定义 `speechPrompt`),要求模型句首输出 `[neutral|joy|sadness|anger|surprise|fear|disgust|shy]` 标签;句子层提取后映射为表情,标签本身不进字幕。切回 Chat 视图后下一轮自动注入"已退出"提醒
- TTS 与 Turn 解耦:LLM 流结束即放行 Agent Turn 结算,剩余句子在后台继续合成;新一轮流开始或视图关闭时自动中止旧合成
- 多 key 轮询:单 key 401/402/429 自动切下一个,全部失败才报错(SSE `error` 事件)
- 语音识别(流式,默认)用火山 `bigmodel_async`(双向流式优化版 + `enable_nonstream` 二遍,v3 sauc 二进制帧协议,帧不带 seq):边说边出实数 interim(SSE `asr-interim`),句末 `{"t":"finish"}` 控制帧触发定稿(SSE `asr-final`);实测 4.7s 中文句说完 ≈0.6s 出最终文本;**语言参数仅 nostream 端点支持**,双向流式只覆盖中英+方言(日语实测空文本),故 `asrMode: nostream` 保留旧的 `bigmodel_nostream` 一次性整句识别(25 语种 `enable_auto_lang`,含 ja-JP)
- 防重复提交:每次上行带随机 `up` 标识,`asr-interim`/`asr-final` 事件回传该标识,仅发起上传的视图处理(GUI tab 缓存未卸载、GUI + 独立入口双开、双设备都不再重复提交同一句)
- 浏览器→host 上行走 WebSocket(每句一连接):Chromium 的 `ReadableStream` fetch body 只支持 HTTP/2(HTTP/1.1 下 `ERR_ALPN_NEGOTIATION_FAILED`),而 harness webserver 是 HTTP/1.1
- 字幕翻译是 host 侧一次性 `ctx.llm.stream` 调用(复用会话的 provider/model,无 sessionId/purpose 故不会被本插件 tap 回环);每会话串行队列、最多积压 2 句,超出丢最旧——字幕时效优先;译文经 SSE `subtitle-translation` 按 `lineId` 回贴
- 第三人称管线(v1.5.0):输入 POST `/live2d-voice/player-line` → host 侧每会话串行队列(润色 = 无 sessionId 的一次性 `ctx.llm.stream` → 玩家表情 → Fish TTS(playerVoiceId))→ SSE 事件带 `speaker:"player"`(事件名全部复用,缺省 = assistant,向后兼容)→ **玩家台词 TTS 合成完成后才向 agent 提交**,音频事件严格先玩家后 AI;双模型共享单个 Pixi Application(Cubism WebGLManager 是全局单例持 gl 指针,双 WebGL context 会互相抢占致先挂载模型渲染空白)
- 视频通话模式(v1.6.0):输入路径与第一人称完全一致(无润色/代播,`submitText` 仅 `third` 走 player-line);玩家模型挂共享 Pixi Application 的 `StageLayout.window` 小窗(Graphics 圆角矩形 mask 裁剪 + DOM 覆盖层窗框/名牌/拖拽),口型由 mic RMS 包络驱动(攻快收慢),视线追踪开启时头/眼朝向人脸方向;look 位移通道对小窗归零防滑出 mask

## 开发

```bash
npm install        # .npmrc 已设 legacy-peer-deps(client 包 0.1.1-rc.2 peer 与 host 0.1.5-rc.3 冲突)
npm run typecheck
npm run build      # lib/index.js (host ESM) + lib/client.js (浏览器 bundle)
node e2e/verify-live.mjs     # Phase 1 端到端回归 22 项(需 e2e 实例跑在 4188,见 dsh-e2e skill)
node e2e/verify-voice.mjs    # Phase 2 语音闭环 17 项(同上;需 /tmp/t-zh-16k.pcm 与 /tmp/t-ja-16k.pcm 测试音频;断言 nostream 传输路径)
node e2e/verify-stream.mjs   # 流式 ASR 全链路:实时推流 → asr-interim/asr-final → 自动提交(同上环境)
node e2e/verify-phase3.mjs   # Phase 3 翻译/多模型/workspace 19 项(需 /root/.dsh-e2e-test-models 多模型夹具)
node e2e/verify-v11.mjs      # v1.1.0 全屏/视线追踪资产/MediaPipe 加载/摄像头工具 10 项(需 --use-fake-device-for-media-stream)
node e2e/verify-soak.mjs     # 长时闲置 soak(默认 6 分钟,SOAK_MINUTES 可调)
node e2e/verify-standalone.mjs # 独立入口 12 项(无参引导/挂载/键盘提交/冷会话恢复/语音回路)
node e2e/verify-third-person.mjs # 第三人称模式 35 项(配置面/双模型同台/全链路 SSE/日志/中→日润色/关闭兜底)
node e2e/verify-video-call.mjs   # 视频通话模式 29 项(三态切换/PiP 小窗/拖拽吸附/口型跟随/输入直达/提示词注入)

> 语音/Phase 3 脚本默认硬编码本机 playwright-core(/root/projects/camoufox-mcp/node_modules)与 chromium 路径;`E2E_URL`/`E2E_CFG` 环境变量可覆盖实例地址与配置文件。
```

源码结构:`src/`(host:config/events/tts/sentence/speech/asr/system-prompt/routes)+ `src/client/`(view/engine/model/hud/subtitle/mic/api)。构建产物 `lib/client.js` 是浏览器 bundle(react/pixi/live2d 打包进去,`@deepseek-ai/*` external 由宿主提供)。

## License

MIT

Install

dsh plugin --profile web add github:john-walks-slow/dsh-live2d-voice

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source