dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
English · Releases · IndexTTS 集成 · GPU 兼容性 · 和 llama-swap / berth 的区别
为本地 AI 情感陪伴(AstrBot / DeepSeek Harness)原生接入的 GPU 调度器 —— 让人设对话、IndexTTS 语音和自拍/改图在同一张 12 GB 卡上轮流用卡。
它不是通用 GPU 租约系统:不换客户端、不加代理、不读 nvidia-smi,只在宿主的事件边界上停/起你明确列出的进程。
在 12 GB 卡上,它把本地 LLM、IndexTTS 和 ComfyUI 从「互相换页」变成「轮流独占」——实测 s2mel 从 30 s/步回到 0.12 s/步,预填从 0.2 tok/s 回到 ~1300 tok/s。
LLM 常驻,显存 4.9 GB / 12 GB
│
├── "/aimg 一只猫" ──► 停 LLM ──► 起 ComfyUI ──► 出图 ──► 等队列跑空
│ (腾出 ~6 GB) 停 ComfyUI
│ 拉起 LLM
└── 语音请求 ──────► 停 LLM ──► 确保 TTS 在线 ──► 合成 ──► 拉起 LLM它只负责这些「停/起」的顺序。不是模型、不是路由、也不是要你重写的启动器:所有机器相关的参数都在一个 JSON profile 里。
12 GB 卡上通常是这样:
服务 · 常驻显存 llama-server(Qwen3.6-35B-A3B MoE,256K 上下文) · ~4.9 GB IndexTTS webui · ~2.3–5.3 GB ComfyUI + SD/Flux · ~6–7 GB 桌面 + 宿主程序 · ~2 GB
前三个里任意两个加桌面就会溢出。Windows 不会当场报错,而是开始把显存换页到内存——这不会崩,只会慢 100 倍。在写下它的那台机器上实测:s2mel 扩散从 0.12 s/步 变成 30 s/步,一张图跑了 543 秒,预填从 ~1300 tok/s 掉到 0.2 tok/s。进程一直是活的。
这套东西最初就是给一个本地 AI 陪伴机器人写的:她要有人设连续性(本地 LLM)、要能发语音(IndexTTS)、要能自拍和改图(ComfyUI),而且这些经常发生在同一个回合里。通用 GPU 调度器不知道一段语音什么时候真正合成完;对陪伴场景,那个边界恰恰是最重要的。
解法不是换更大的卡,而是确保关键时刻只有一个在占显存。
/queue 跑空再停服务。coordinator.lock,语音事件和生图任务不会同时切服务。显存协调已经不缺项目:llama-swap 6.1k★,berth 2026-10-11 才公开、已 200★,vhotseat、OneGPU4All 也在做类似的事。这个项目不假装是通用调度器,它只解决一个具体问题:
你在 AstrBot 或 DSH 上跑一个本地 AI 陪伴:人设对话(本地 LLM)+ IndexTTS 语音 + 自拍/改图(ComfyUI),全在同一张 12 GB 卡上。
一次回合里可能同时发生:
用户消息
├─ 她回复文字(LLM 占卡)
├─ 回复带 [语音] → 停 LLM → IndexTTS 合成 → 恢复 LLM
└─ 调 generate_selfie → 停 LLM/TTS → ComfyUI 出图 → 恢复 LLM你的情况 · 更合适的选择 只是 llama.cpp / vLLM 换模型,所有客户端都能改 base URL · llama-swap 要通用 GPU 租约、优先级、游戏/VM/多卡仲裁 · berth(alpha,Windows 实验性) Linux 上 LLM ↔ ComfyUI 轮流 · vhotseat 在 AstrBot / DSH 上跑本地 AI 陪伴,不想改端口、不加代理,语音合成结束才切 · 本项目
相对通用调度器,它多做这些:
vram_status / vram_acquire / vram_releasetts_busy.flag / tts_ready.flag 协议(合成边界,不是 HTTP 边界)也少做这些:多卡、游戏/VM、优先级队列、网关、nvidia-smi 轮询、provider 自动切换。需要这些就选上面的通用项目。
不跑 AstrBot / DSH 也可以:核心包 vram_switcher 不依赖宿主,VramService 提供 start / acquire / release / status,可以直接在你的 AI 陪伴服务里用:
from vram_switcher import load_config
from vram_switcher.service import VramService
cfg = load_config({"profile_path": "profile.local.json"}, plugin_dir=".", data_dir="./data")
service = VramService(cfg)
await service.start()
await service.acquire("image", reason="selfie") # 停 LLM、起 ComfyUI
# ... 出图 ...
await service.release("selfie") # 停 ComfyUI、恢复 LLM语音侧仍建议走 tts_busy.flag / tts_ready.flag,因为只有 TTS 插件知道合成什么时候真正结束。
调度器本身不挑卡:没有 CUDA / ROCm / nvidia-smi 依赖,只看进程、命令行、端口和 flag。挑卡的是它调度的三个服务。
组件 · N 卡 Windows · A 卡 Windows · A 卡 Linux 调度器 · ✅ 生产验证 · ⚠️ 机制可用,未实测 · ⚠️ 基础支持 llama.cpp · ✅ CUDA · ⚠️ Vulkan(社区常用) · ⚠️ ROCm / Vulkan ComfyUI · ✅ CUDA · ⚠️ DirectML / ZLUDA · ⚠️ ROCm IndexTTS · ✅ CUDA · ⚠️ 最易卡住 · ⚠️ ROCm 需实测
一句话:N 卡是已验证路径;A 卡的调度层可用,但 IndexTTS 在 Windows A 卡上最容易卡住(PyTorch 的 ROCm 轮子覆盖有限)。llama.cpp 用 Vulkan 基本没问题;ComfyUI 走 DirectML/ZLUDA 能跑,但有节点兼容性和性能代价。
A 卡 profile 的关键调整:ready.type 用 port、超时给足、recovery_wait_seconds 加大、process_names / cmdline_any 按实际构建改。完整矩阵、逐组件说明和检查清单见 docs/gpu-support.md。
git clone https://github.com/xiaoxiao150/vram-switcher
cd vram-switcher
# 自动探测正在运行的 llama-server / TTS webui / ComfyUI,生成 profile。
# 这一步不会启动或停止任何进程。
python configure.py --root D:/AI # 只看不写加 --dry-runconfigure.py 会写出 profile.local.json,并打印它找到了什么:
[detected] llm: running process
[detected] tts: running process
[missing ] image: fill this in by hand
[port ] 8080 (llm) listening
[port ] 7860 (tts) listening
[port ] 8188 (image) free把宿主指向这个文件并重启即可。没探测到的服务会写成 enabled: false,插件仍然合法,只是少做一件事。
LLM 常驻
├─ TTS 请求 → 停 LLM(可停辅助服务)→ 确保 TTS 在线 → 完成后恢复 LLM
└─ 生图请求 → 停 LLM(可停 TTS/辅助服务)→ 启动图像服务 → 等就绪
→ 出图完成 → 等队列跑空 → 停图像服务 → 恢复 LLMprofile.local.jsonllm / tts / image,外加任意数量的只停不启 aux_servicesmoe、dense27b)、多个 TTS 版本(2.5 / 2.0,支持 auto)tts_busy.flag 表示合成中,tts_ready.flag 表示显存已就绪/queue,不打断一批多张图_prepare_switch、_recover_after_tts、事件标记、flag 路径全部保留default_provider_id 打架git clone https://github.com/xiaoxiao150/vram-switcher AstrBot/data/plugins/astrbot_plugin_vram_switcher也可以直接从插件市场安装:搜 VRAM Switcher(cloud.astrbot.app)。
然后在插件配置里确认:
profile_path = 你生成的 profile.local.json(建议放AstrBot/data/plugin_data/astrbot_plugin_vram_switcher/,填绝对路径)
local_llm_engine = profile 里的引擎键(用 configure.py 生成时是 detected)index_tts_version = auto 或 2.5 / 2.0没跑过 configure.py 的话,也可以从 profile.example.json 手动复制一份改。 如果 profile 找不到或 JSON 非法,插件会记录错误并保持禁用,不会崩溃或乱杀进程。
IndexTTS webui 没有“卸载模型”的 HTTP API。它常驻时占 2.3–5.3 GB;本地 LLM 占 4.9 GB;ComfyUI 出图要 6–7 GB。12 GB 卡上三者不能共存,只能让 TTS 在合成前把卡让出来,合成后再还回去。
这里用的是进程级互斥 + 两个文件:
AstrBot 决定发语音
│
├─ TTS 插件写 tts_busy.flag(并删掉上一轮的 tts_ready.flag)
│
▼
VRAM Switcher(每 2 s 轮询)
├─ 停本地 LLM(以及 profile 里的 aux_services)
├─ 确保 IndexTTS webui 在线(没起就按 versions 启动,等端口 listen)
└─ 写 tts_ready.flag
│
▼
TTS 插件等到 ready + webui 在线,才提交 /gen_single
│
▼
合成结束,TTS 插件删掉 tts_busy.flag
│
▼
VRAM Switcher 恢复:等 ComfyUI 队列跑空 → 停图像服务 → 拉起本地 LLM关键点:
tts_ready.flag 必须在真正停完 LLM 之后写。TTS 抢跑会让 s2mel 权重被 Windows 换页:实测 0.12 s/步 → 30 s/步,25 步要 731 秒。version_selector: auto 跟随正在运行的 2.5 / 2.0;两个都没起时用 default_version。最小 profile:
{
"services": {
"tts": {
"enabled": true,
"port": 7860,
"process_names": ["python.exe"],
"cmdline_contains": ["webui.py"],
"cmdline_any": ["IndexTTS", "Index-TTS"],
"stop": {"timeout_seconds": 20, "tree": false},
"busy_flag": "${DATA_DIR}/tts_busy.flag",
"ready_flag": "${DATA_DIR}/tts_ready.flag",
"flag_stale_seconds": 900,
"start_cooldown_seconds": 150,
"version_selector": "auto",
"default_version": "2.5",
"versions": {
"2.5": {
"args": ["D:/AI/IndexTTS2.5/.venv/Scripts/python.exe",
"D:/AI/IndexTTS2.5/webui.py", "--version", "2.5", "--fp16"],
"cwd": "D:/AI/IndexTTS2.5"
}
}
}
}
}完整的适配器代码、AstrBot 钩子优先级和失败排查见 docs/indextts.md。注意:这个仓库只提供调度侧;你的 TTS 插件必须按上面的协议写/删 flag,否则联动不会生效。
完整示例见 profile.example.json。
profile 字符串里支持两个内置变量,避免把绝对路径写死:
${PLUGIN_DIR}:插件源码目录${DATA_DIR}:AstrBot 的 plugin_data/astrbot_plugin_vram_switcher 目录${ENV_NAME}:读取同名环境变量{
"services": {
"llm": {
"enabled": true,
"port": 8080,
"process_names": ["llama-server.exe"],
"stop": {"timeout_seconds": 15, "tree": true},
"start": {
"engine": "moe",
"profiles": {
"moe": {
"args": ["D:/AI/llama/llama-server.exe", "-m", "D:/AI/models/model.gguf", "-c", "32768"],
"cwd": "D:/AI/llama",
"log_file": "${DATA_DIR}/llm.log"
}
}
},
"ready": {"type": "process", "timeout_seconds": 50},
"recovery_wait_seconds": 45,
"night_quiet": {"enabled": true, "start": "23:00", "end": "07:00"}
}
}
}process_names 是停止/检查用的进程名白名单。start.profiles 可以有多个引擎;AstrBot 配置里的 local_llm_engine 选其中一个。ready.type = process 表示“看到进程存在”即算恢复;如果你的启动器会先起一个壳进程,建议改成 port。recovery_wait_seconds 是启动后等待时间;0 表示立即检查(适合测试)。{
"services": {
"tts": {
"enabled": true,
"port": 7860,
"process_names": ["python.exe"],
"cmdline_contains": ["webui.py"],
"cmdline_any": ["IndexTTS", "Index-TTS"],
"stop_extra": [{"process_names": ["cmd.exe"], "cmdline_any": ["IndexTTS"]}],
"stop": {"timeout_seconds": 20, "tree": false},
"busy_flag": "${DATA_DIR}/tts_busy.flag",
"ready_flag": "${DATA_DIR}/tts_ready.flag",
"flag_stale_seconds": 900,
"start_cooldown_seconds": 150,
"version_selector": "auto",
"default_version": "2.5",
"versions": {
"2.5": {"args": ["python.exe", "webui.py", "--version", "2.5", "--fp16"], "cwd": "D:/AI/IndexTTS2.5"}
}
}
}
}TTS 插件需要配合两个文件:
busy_flag,结束时删除;ready_flag,TTS 插件应在提交合成前等它出现。flag_stale_seconds 用于清理崩溃后残留的 busy 文件,避免永远卡在“合成中”。
{
"services": {
"image": {
"enabled": true,
"port": 8188,
"process_names": ["python.exe"],
"cmdline_contains": ["ComfyUI"],
"stop_by_port": true,
"start": {
"args": ["D:/AI/ComfyUI/python/python.exe", "-u", "-s", "D:/AI/ComfyUI/ComfyUI/main.py", "--windows-standalone-build"],
"cwd": "D:/AI/ComfyUI/ComfyUI"
},
"ready": {"type": "port", "timeout_seconds": 300},
"queue": {"timeout_seconds": 420, "interval_seconds": 6}
}
}
}stop_by_port: true 会额外杀掉监听该端口的进程。只在你确认端口属于该服务时使用。
{
"aux_services": [
{
"key": "prompt_optimizer",
"process_names": ["pe-server.exe"],
"stop": {"timeout_seconds": 10, "tree": true}
}
]
}辅助服务只停不启。适合提示词优化器、临时缓存服务等“下次使用时会自己拉起”的进程。
{
"triggers": {
"tts_keywords": ["语音"],
"image_prefixes": ["aimg", "img2img", "改图", "/改图"],
"image_tool_names": ["generate_selfie", "generate_image", "comfyui_txt2img"],
"tts_reply_marker_pattern": "[\\[【]\\s*语音\\s*[\\]】]"
}
}tts_keywords:用户消息包含关键词 → TTS 预切换。image_prefixes:消息首词匹配 → 生图预切换。image_tool_names:LLM 决定调用这些工具前 → 生图预切换。tts_reply_marker_pattern:LLM 回复里出现语音标记 → TTS 预切换。AstrBot 插件配置里的 extra_tts_keywords / extra_comfyui_prefixes 会追加到 profile 对应列表。