dsh-qwen-paint · (绘图)千问(优化版)
面向 DeepSeek Harness 的本地图像生成插件。经由本机 ComfyUI 调用 Qwen-Image 2.1 模型,
在对话中直接完成文生图与图生图;生成的图像落盘保存,并在会话界面内直接呈现。
插件的全部网络流量限于本机回环地址(127.0.0.1),不依赖外部网络,无需任何 API 密钥,亦不产生调用费用。
原作者:@奇迹与你(https://github.com/wonderandyou/dsh-qwen-paint)
优化版作者:@yanfei0725(https://github.com/yanfei0725/dsh-qwen-paint) | 许可协议:MIT | 当前版本:0.16.0
0.16.0 新增说明
- OOM 自动回退:生图遇到显存不足时,自动将 VAE 解码切换为分块模式并重试一次;二次失败会保留错误原因并给出排查建议。需当前 ComfyUI 提供内置
VAEDecodeTiled节点。 - SADA 探索性接入(默认关闭):先探测可选节点,只有节点存在且用户显式开启才加入工作流;本项目尚未安装该第三方节点,节点类型/输入及画质和速度收益未做真机验证,不自动下载。
- 本机扩展 API:新增
/api/qwen-paint/health、/capabilities、/presets、/memory/recent、/memory/preference,供本地工具集成;端点仅接受本机请求。 - 记忆联动基础:每次成功出图追加一条 JSONL 到输出目录
.qwen-paint-memory.jsonl,dsh-auto-memory可从 recent 端点读取;偏好 GET/POST 目前只持久化,不自动改变生图参数。 - ComfyUI Registry 元数据:加入
pyproject.toml以供 Registry 索引发现。此仓库仍是 DSH 插件而非 ComfyUI Python custom node;元数据不代表已发布或已获 Registry 收录。
优化版说明
本版在不改变输出画质的前提下,围绕推理性能、显卡适配与平台兼容三个方向对插件进行了系统性增强。
一、推理链路性能
- 按输出尺寸自动选择加速方案。
三个加速组件(TE-Speed、Speedup、BlockCache T8)需独占同一组模型包装器,同一时刻仅能启用其一。
本版依据本次生成的实际尺寸自动切换:小尺寸(≤1024²)采用 TE-Speed 与 Speedup(实测 +25.4%);
大尺寸(如 1408²)改用 BlockCache T8(实测 +22.7%,约节省 27.4 秒)。
该策略基于实测结论——在 1408² 下 TE-Speed 命中率为 0%、Speedup 仅 12.5%,却占用互斥资源。 - 编排阶段并发化。
提交任务前的队列查询、参考图上传与后端能力探测三项操作改为并发执行;
结果轮询间隔由 1200 ms 缩短至 400 ms;后端就绪后立即预热能力探测。 - 显存驻留策略自动化。
依据显存总量自动确定启动参数:≥24 GB 全量驻留;16–24 GB 采用--highvram并保留少量余量;
<16 GB 不启用驻留参数以规避显存溢出。此举显著减少约 13.5 GB 模型权重的反复装卸开销。 - 接入 RDNA3 FlashAttention。
将已安装但未被引用的 RDNA3-Flash-Attention 节点接入模型链末端。该实现为精确注意力,不改变计算语义。 - 支持单次批量生成。
新增count参数(1–4),一次提交可生成多张图像,模型权重仅加载一次;默认值仍为 1,原有行为不变。
二、显卡厂商适配
插件以加权评分方式识别显卡型号与代系(明确型号优先于泛化厂商名,独立显卡优先于核显),
并为每一类硬件指定相应的注意力后端与可选加速组件:
| 厂商 | 识别依据 | 注意力后端 | 可选加速组件 |
|---|---|---|---|
| NVIDIA | GeForce / RTX / A100 等 |
--use-pytorch-cross-attention |
Triton for Windows、SageAttention、Nunchaku |
| Intel Arc | Arc.*B\d{3} / Arc.*A\d{3} |
--use-pytorch-cross-attention |
IPEX |
| AMD RDNA3(RX 7000) | Radeon RX 7xxx |
--use-quad-cross-attention |
A 卡专属内核(可选)、异步卸载(可选) |
| AMD RDNA4(RX 9000) | Radeon RX 9xxx |
--use-pytorch-cross-attention |
A 卡专属内核(可选)、异步卸载(可选) |
| AMD RDNA2(RX 6000) | Radeon RX 6xxx |
--use-pytorch-cross-attention |
A 卡专属内核(可选)、异步卸载(可选) |
| AMD RDNA1(RX 5000)★ | Radeon RX 5xxx |
--use-pytorch-cross-attention |
A 卡专属内核(可选)、异步卸载(可选) |
| Apple Silicon ★ | Apple M1–M5 |
--use-pytorch-cross-attention |
MPS 算子回退(默认开)、取消显存水位上限(可选) |
| Intel Mac + AMD 独显 ★ | Radeon Pro 5xxxM 等 |
--use-pytorch-cross-attention |
同上(macOS 上 AMD 显卡走 Metal,不走 ROCm) |
| Intel Mac(核显)★ | Intel Iris / UHD Graphics |
不指定 | MPS 算子回退(默认开) |
各类硬件的参数与加速组件相互独立,不会交叉套用。
★ 平台闸(2026-10-12 新增):显卡判定在平台这一维上再收一次口。 同一个型号名在不同平台能用的计算栈完全不同 —— Windows/Linux 上的
Radeon RX 7900 XT走 ROCm(quad / CK 那套), 而 macOS 上的 AMD 显卡只有 Metal/MPS 一条路(Apple 不提供 ROCm); Windows/Linux 上的Intel Arc走 XPU + IPEX,而 macOS 上根本没有 XPU 后端。 不收口就会出现"Intel Mac 被建议装 IPEX"这类照着做必然失败的建议。
A 卡专属优化(可选,默认关)★ 2026-10-11 新增
与 N 卡/I 卡的「厂商专属优化」同一套思路:只在适用的显卡上、在优化面板里出现,
默认关闭,由你自己决定要不要开。两项都是质量中性的 —— 换的是后端 / 加载时机,
不改任何计算结果,不牺牲原生画质。
| 开关 | 作用 | 按代系给什么 |
|---|---|---|
| A 卡专属内核 | 换用本代的替代注意力内核 | 5000 / 6000 → --use-quad-cross-attention;7000 / 9000 → --use-ck-attention |
| A 卡异步卸载 | --async-offload |
ComfyUI 的 help 原文写着它 "Enabled by default on Nvidia" —— 即 N 卡默认开、A 卡默认关,这里给 A 卡补上(只改权重加载的时机,与计算重叠) |
⚠ 两个都是启动参数 ⇒ 改完必须重启 ComfyUI 才生效(面板提示里也写了)。
⚠ 5000 / 6000 / 9000 我们没有硬件实测,所以一律默认关 —— 属可选档,不合适就关回去。
⚠ 也不会因为默认关而把面板显示成"优化部分启用":它们被标记为可选,
单独计入总账的「N 项可选(默认关)」。
想永久生效:把amdKernel/amdAsyncOffload写进cordis.patch.yml(面板开关是内存态)。
三、操作系统兼容
启动、停止、进程存活查询与显卡探测均按平台分别实现:
- Windows:经 WMI 派生进程(宿主重启不会连带终止 ComfyUI),显卡信息取自 PowerShell;
- Linux:以
setsid/nohup脱离会话,显卡信息取自nvidia-smi/rocm-smi/lspci及/sys; - macOS(Apple Silicon / Intel Mac)★ 2026-10-11 新增,2026-10-12 补上真正的优化:
显卡信息取自
system_profiler/sysctl, 进程脱离会话先探测setsid是否存在(macOS 没有这个命令),没有就退回nohup。
macOS 怎么用
ComfyUI 官方对 Apple Silicon(M1–M5)只提供手动安装(没有便携包),所以:
- 装 pytorch nightly —— 按 Apple 的 Metal 加速指南:https://developer.apple.com/metal/pytorch/
- 按 ComfyUI 官方「Manual Installation」装依赖
- 启动:
python main.py(插件会自动补上下面那条参数,你不需要手写)
然后 node scripts/setup.mjs --comfy <你的 ComfyUI 目录> 就能继续「下模型 + 挂插件」两步。
(插件不会替 macOS 用户去下 Windows 便携包。)
macOS 上插件到底做了什么(★ 2026-10-12)
先说清为什么之前是"零优化":ComfyUI 在 MPS 上不会自动启用 PyTorch SDPA ——
它的 ENABLE_PYTORCH_ATTENTION 自动开启条件里只有 NVIDIA / Intel XPU / Ascend / MLU
和一批 AMD gfx 代号,没有任何一条与 MPS 相关。于是 Mac 上会落到
attention_sub_quad(那个给 CPU / 低显存场景准备的次二次实现),
根本没走 Metal 的融合内核。
现在插件会做两件事:
| 做什么 | 怎么做的 | 依据 |
|---|---|---|
| 切到 Metal 融合 SDPA | 启动参数加 --use-pytorch-cross-attention |
ComfyUI 源码 comfy/model_management.py 里 ENABLE_PYTORCH_ATTENTION 无 MPS 分支(可自行复核) |
| 兜住未实现的算子 | 注入环境变量 PYTORCH_ENABLE_MPS_FALLBACK=1 |
PyTorch 官方 "MPS Environment Variables" 文档 |
另有一个可选档(默认关):PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 ——
取消 MPS 分配器的硬上限(官方默认 1.7),让大模型在小内存 Mac 上可能跑得起来。
默认关是因为官方明确写了:取消硬上限后若发生系统级 OOM,"may cause system failure" ——
那是"用稳定性换可用性"的取舍,得你自己权衡。
⚠ 插件在 Mac 上不加任何 CUDA / ROCm 专用内核参数 (
--use-sage-attention/--use-quad-cross-attention/--use-ck-attention在 MPS 上都不适用)。 ⚠ 统一内存没有独立显存 ⇒--highvram/--reserve-vram这套驻留参数显式跳过 (ComfyUI 源码里 MPS 的vram_state被强制成SHARED,这组参数根本不参与), 面板会如实说明"为什么没有这一项"。 ⚠--force-fp16/--force-non-blocking也不加 —— ComfyUI 对 MPS 本来就是 fp16、 且源码里device_supports_non_blocking()对 MPS 恒为 False(原注释:"mps doesn't support non blocking")。 ⚠ Intel Mac:没有 CUDA / ROCm / XPU 任何一条厂商计算栈。 带 AMD 独显的机型走 MPS(识别为「Intel Mac + AMD 独显」); 只有 Intel 核显的机型实际上只能跑 CPU,出图会非常慢 —— 这是硬件事实,不粉饰。 ⚠ 上述 Mac 优化没有真机实测(开发机是 Windows + ROCm)。 依据是 ComfyUI 与 PyTorch 的源码/官方文档(都可复核),但"能快多少"给不出数字。 想回到"一个参数都不加":把comfyAttentionBackend设为default。
四、可运维性
- 面板与工具如实报告每一项优化的真实状态(已启用 / 可启用 / 不可启用 / 按尺寸让位)。
- 随附 22 套回归测试,覆盖四厂商判定、三平台分支(Windows / Linux / macOS)、
工作流接线、取消与超时、可移植性及打包完整性。
其中
_mac_env_test.mjs会真起一个sh跑一遍启动命令, 验证 macOS 的环境变量确实注入到了子进程(而不是"看起来像")。
它是什么
一个 DSH 插件(host + client 两半),注册一个工具:
| 工具 | 作用 |
|---|---|
draw_image |
按提示词调本机 ComfyUI 出图,成品落盘到「千问1生图」,并把绝对路径回给助手用于内联显示 |
list_available_models |
列出 ComfyUI 磁盘上真实存在的模型文件(UNET / CLIP / VAE / LoRA / Checkpoint) |
check_acceleration |
检查加速环境:识别显卡厂商/代系、当前注意力后端、通用优化与厂商专用包的就位情况(install 动作可按白名单安装) |
- host 半边(
lib/index.js):注册工具、空闲 5 分钟自动关停 ComfyUI、挂只读状态端点。 - client 半边(
lib/client.js):给draw_image一张自绘卡片(图直接显示在工具行里)、
在 composer 下方显示 ComfyUI 在线状态。
不改 DSH 安装目录、不改内核文件。
自动适配(大概率你什么都不用配)
- ComfyUI 装在哪 —— 自动探测,三级回落:
① 配置里填了就用;② 从正在运行的 ComfyUI 进程反查;
③ 扫常见位置 + 各盘根目录下名字含comfy的目录(覆盖ComfyUI-rocm这类改名安装)。 - 显卡厂商 / 代系 —— 自动识别并套用对应优化。用的是打分制:
明确型号 > 泛化厂商名 > 核显,所以"核显 + 独显"混装也能选对独显。识别结果 判定依据 注意力后端 AMD RDNA3(RX 7000 系 / gfx11xx) Radeon RX 7xxx--use-quad-cross-attention(实测 +15%)AMD RDNA4(RX 9000 系 / gfx120x) Radeon RX 9xxx--use-pytorch-cross-attention(CK SDPA 专用内核)AMD RDNA2(RX 6000 系 / gfx103x) Radeon RX 6xxx--use-pytorch-cross-attention(该代无融合内核,走保守路径)NVIDIA(CUDA,通用) GeForce/RTX等--use-pytorch-cross-attentionIntel Arc B 系(B580 / B570) Arc.*B\d{3}--use-pytorch-cross-attention(XPU + IPEX)Intel Arc A 系(A770 / A750 / A580) Arc.*A\d{3}--use-pytorch-cross-attention(XPU + IPEX)认不出 — 不加任何参数(宁可少点性能,也要保证能起来) - 有哪些节点 / 模型文件 —— 出图前探测一次,缺哪个就跳过哪个(结果缓存 10 分钟)。
- 模型文件名 —— 模糊匹配:int8 / fp8 / bf16 等不同量化版都能认出来。
想看当前判定结果:对助手说「检查一下加速环境」,会调 check_acceleration 返回
识别到的显卡、采用的参数、通用优化与厂商专用包的就位情况。
界面上的优化状态
装完之后,ComfyUI 状态点右边会有一个小徽标,一眼看出优化到底有没有生效:
| 徽标 | 含义 |
|---|---|
优化已启用 |
全部生效(8 步 / CFG 1.0 全栈链路) |
优化部分启用 |
有 turbo8 但缺某一环 —— 点开看缺了什么 |
未启用优化 |
没 turbo8,回退 25 步原生链路(慢,但一定能出图)—— 点开看该怎么办 |
优化未确认 |
探测失败(这不代表缺件,出图时会重新探测) |
插件已停用 |
enabled=false —— 点开看怎么恢复 |
橙色/灰色的徽标可以点,会弹出逐项明细和能直接照做的原因,比如:
· 未启用:没在 ComfyUI 的
loras目录里找到 turbo8 LoRA(当前回落的文件名是
「turbo8_lora_step2500.safetensors」,看起来并不存在)。把它放进models\loras即可。
· 未启用:ComfyUI 里没有QwenImage21Cache节点 —— 通常是 ComfyUI 版本偏旧,升级即可。
面板里还有三类信息,都值得留意:
- 关键缺口提醒(橙色块,置顶)—— 例如「配了 GGUF 主模型但没装 ComfyUI-GGUF 节点」。 这类问题会让出图直接被 ComfyUI 拒收,所以任何时候都会显示(不像"未启用"只在缺件时出现)。
- 总账:
共 N 项:… · M 项可选(默认关 / 已开 K 项)—— 可选档单独一档, 不会因为"默认关"而被算成"还差几项没配好"。 - 按显卡隐藏:识别到你的显卡后,不适用的优化会直接不显示, 并在面板上说明「已按显卡识别隐藏 N 项(显卡名):…」—— 比如 N 卡上不会出现 AMD RDNA3 的项。
想了解更完整的细节,可以直接让助手调用 check_acceleration 工具。
生图意图判定与启用开关
drawTrigger —— 模型多主动地判断你想不想画图
| 档位 | 行为 | 适合 |
|---|---|---|
off |
不做主动引导,只在直接点名要画图时才画 | 想要个安静工具的人 |
strict |
只在明确要求且描述具体时才画;模糊的先问一句 | 怕误触发的人 |
auto(默认) |
判断是生图请求就画;但排除流程图/表格这类场景,那种先确认 | 大多数人 |
eager |
只要沾边就画(最积极) | 确定只用它画图的人 |
四档都内置了边界规则:像「画个流程图 / 思维导图 / 表格 / 架构图」这种
看着像生图、实际该用文字或其它工具的请求,会先确认一句而不是直接画。
判断不准时宁可先问一句,也不要画错。
无论哪一档,都不会在你没有生图意图时去调工具 —— 最松的
eager也只是"沾边就画"。
enabled —— 插件总开关
设为 false 时,插件对 DSH 相当于"不存在":不注册 draw_image、不注入任何提示词、
不预热 ComfyUI、不做空闲关停。
但保留 check_acceleration / list_available_models 两个诊断类工具(只读、不画图)
—— 否则关掉之后真出了问题,连查都没法查。
跨平台与下载兜底
支持 Windows 与 Linux。显卡识别、ComfyUI 路径探测、进程查找都各有两套实现,
按平台自动选用:
| Windows | Linux | |
|---|---|---|
| 执行命令 | PowerShell | sh -c(POSIX,不假设有 bash) |
| 取显卡信息 | WMI Win32_VideoController |
nvidia-smi → rocm-smi → lspci → /sys/class/drm(四级回落) |
| ComfyUI 解释器 | venv\Scripts\python.exe |
venv/bin/python |
厂商专用加速包是多源下载的 —— 每个包 5 套候选,一套不通自动顶上下一套:
PyPI 官方源 → 清华 TUNA 镜像 → 阿里云镜像 → 腾讯云镜像 → GitHub 源码直装
(为防卡住,pip 统一带 --timeout 15 --retries 1,让"连不上"快速失败并切源;
装了哪一套会记在返回值里,方便排查。)
包里只带这套"下载器",不预置任何已下载的文件 —— 所以本包只有 ~500 KB。
内置兜底:下载全失败也能画图
加速件全是锦上添花,缺了只会慢,不会不可用。把加速全部拿掉后,
插件产出的工作流会退化成只用 ComfyUI 原生节点:
UNETLoader → CLIPLoader → VAELoader → TextEncodeQwenImage21
→ EmptyLatentImage → KSampler(25 步 / CFG 2.5) → VAEDecode → SaveImage
只依赖 ComfyUI 本体 + 千问模型本身,一个下载来的组件都不需要。
依赖
- ComfyUI 跑在
127.0.0.1:8188(自己手动启动 or 让插件按需后台拉起;安装位置自动探测) - 三个千问模型文件(共约 13.3 GB):
qwen_image_2.1_int8_convrot.safetensors(UNET,放models\diffusion_models\)qwen3vl_8b_w4a8.safetensors(CLIP、type=qwen_image,放models\text_encoders\)qwen_image_2.1_vae_bf16.safetensors(VAE,放models\vae\)
- 不需要任何 Python 环境、不装任何第三方包 —— 插件本体是纯 Node ESM,零外部依赖。
默认加速链(★ 2026-10-09 更新:四层叠加,额外三个依赖)
出图默认走「turbo8 八步 + TE-Speed 跳步缓存」这条快链,需要额外部件:
| 部件 | 放哪 | 说明 |
|---|---|---|
turbo8_lora_step2500.safetensors |
models\loras\(约 1.36 GB) |
八步蒸馏 LoRA,chriswritescode/Turbo8-LoRA-Qwen-Image-2.1 |
TE-Speed-QwenImage21 |
custom_nodes\ |
跳步缓存节点,tl2012tl/TE-Speed-QwenImage21(闭源 nodes.pyd,见下) |
ComfyUI-Qwen-Image-2.1-Speedup |
custom_nodes\ |
★ 2026-10-09 新增:步级残差缓存,frakd/ComfyUI-Qwen-Image-2.1-Speedup(MIT,纯 Python,无闭源件)。与 TE-Speed 实测可叠 |
另外启动时默认带一个参数:--use-quad-cross-attention(注意力后端)。
关掉它见下面「可调项」的 comfyAttentionBackend。
效果(本机 RX 7900 XT / ComfyUI 0.39.0,1024 档,每遍换 seed 的稳态值):
| 档位 | 耗时 | vs 原默认 | 说明 |
|---|---|---|---|
| 原默认:30 步 CFG 2.5 | 91.6 s | — | 基准 |
| ① turbo8 八步 | 12.6 s | −86% | 大头在这里(采样轴,替换) |
| ② ① + TE-Speed | 10.74 s | −88% | 采样层,算子级省 23% |
| ③ ② + QwenImage21Speedup | 9.71 s | −89.4% | 采样层第二层,与 TE 实测可叠 |
④ 现默认:③ + --use-quad-cross-attention |
8.22 s | −91% | 注意力后端层,再省 15~17% |
第 ④ 档是 2026-10-09 端到端实测(改动后的默认链路,3 轮丢首轮):稳态 8.22 s。QwenImage21Speedup 自己的日志会打 1 cached of 8 model forwards (12.5% skipped)。
⚠
--use-quad-cross-attention的结论是在本机 RX 7900 XT / gfx1100 / 原生 ROCm 7.2.1 上测的:
10.739 s → 9.328 s(+15.1%,两轮独立复现),满叠栈上 +17.3%,画质无损
(同 seed PSNR 39.5/41.4 dB,清晰度 166.3 vs 基线 164.6)。
对照:--use-pytorch-cross-attention是 −0.3%(无效)——它本来就是默认路径。
换到 N 卡或别的后端请自己重测,别照抄。
⚠ 首张图还要额外 ~12 s 加载模型(20 GB),那是一次性的,不算在这条链的开销里。
⚠ 测量方法提醒:上表每遍都换了 seed。若两遍用完全相同的 prompt + seed,
ComfyUI 会直接回缓存(实测第二遍只花 0.40 s),拿那个数字算"加速比"会严重失真
—— 我们一度就因此把 TE 的收益误报成 −43%,实际是 −23%。⚠ TE-Speed 的整层跳过在 8 步下其实是 0 次(8 步的 t 序列前 3 步被它内置的
window 0.18-1.00挡掉,后 5 步的 change 又都超过 0.06 阈值)。它仍然有效是靠
每次调用复用部分 KV/attention 的算子级省时。想让它真正"跳步",得把步数调到
12 步以上(未验证)。
✅ 缺件会自动降级,不再"整单硬失败"(2026-10-09 通用化改造)。
插件出图前会先探测 ComfyUI 有哪些节点、磁盘上有哪些模型文件,据此自动选一档
可用的最快链路:LoRA 不在 → 回退 25 步 / CFG 2.5 原生链;TE-Speed / Speedup 没装
→ 逐个跳过,链子自动接上。不会再让 ComfyUI 用node type not found拒收整个任务。
只有连千问主模型都匹配不到时,才会沿用配置里的文件名去试(那时才看得到 ComfyUI 的原始报错)。
手动退回慢档见「可调项」里的turbo8Enabled/teSpeedEnabled/speedupEnabled。另外,ComfyUI 装在哪、显卡是哪家、该用哪个注意力后端,都不需要你配置 —— 见下节。
prompt 精简(2026-10-09 装上,但先把预期说清楚)
默认会做一次无损清理:折叠多余空白/换行、合并连续重复标点、去掉末尾悬挂分隔符。
语义完全不变,干净 prompt 跑一遍也不变。
实测上限只有约 4.8%,而且只对超长 prompt 才有意义(同一条栈只变 prompt):
| prompt 长度 | 耗时 |
|---|---|
| 38 字 | 7.821 s |
| 329 字 | 8.088 s |
| 1974 字 | 8.194 s |
即 prompt 从 38 字加到 1974 字(52 倍)也只多花 0.373 s。
这也顺带证明了:文本 token 没有被填充到固定长度(否则长短 prompt 会一样快)。
交叉复测(clean / dirty 交替各 3 轮)显示差异完全淹没在噪声里 —— 不要指望靠精简 prompt 提速。
因此 maxPromptChars(按字数硬截断)默认是 0 = 关闭:它会改变出图,而换来的最多也就那 4.8%。
真要省时间,去调上面的加速链,别动 prompt。
关于闭源
nodes.pyd:TE-Speed 只发 Windows 编译好的.pyd,没有源码。装之前做过静态审查
(无网络/exec/子进程/eval/base64 调用,导入期不改任何 ComfyUI 方法;运行时只 monkey-patchPoseBranchCache.select以修 CFG>1 的报错)。介意闭源就别装它 —— 不装的话 turbo8 单跑
仍有 12.6 s(−86%),只比带 TE 慢 2.9 s。装好之后想临时停用它,见下面teSpeedEnabled。
超分放大 VOSR2(可选,画质/预览层,默认关,不进时间账)
VOSR2 是在出图之后再插一级 1.4B 单步超分(LightningDiT + 千问 2D VAE + DINOv2-L),
与上面四层加速链正交、可叠加。权重 6.97 GB 已下齐并字节校验通过,部署在ComfyUI\models\vosr2\。
2026-10-09 端到端实测(默认加速链 + quad):
| 档位 | 做法 | 实测(末轮) | vs 原生 |
|---|---|---|---|
| 1024² | 原生直出 | 7.74 s | 基准 |
| 1024² | 512² + VOSR2×2 | 45.16 s | 慢 5.84× |
| 2048² | 原生直出 | 53.27 s | 基准 |
| 2048² | 512² + VOSR2×4 | 155.05 s | 慢 2.91× |
结论:超分不省时间,反而慢 2.9×–5.8×。它的价值是「用低分辨率成图成本换更高名义分辨率 / 细节」,
属于画质 / 预览增强,不计入成图时间,因此保持默认关、按需手动开启。
⚠ 打通它必须先过
custom_nodes/zz_rgba2rgb这个节点:Qwen-Image VAE 解码出的是 4 通道 RGBA,
而VOSR2Upscale只要 3 通道 RGB,直接连会ValueError: expects 3-channel RGB, got 4 channels。
该节点做image[..., :3]丢 alpha。画质级 A/B 未做(不同分辨率同 seed 构图不同,无法严格比对)。
ROCm / 系统开关审计(2026-10-09,速览)
在真实生产启动(仅 --use-quad-cross-attention,无 --reserve-vram,基线 7.1s)上实测:PYTORCH_HIP_ALLOC_CONF=expandable_segments、HIP_FORCE_DEV_KERNARG=1、MIOPEN_FIND_MODE=FAST、--fast fp16_accumulation、--fp16-vae 五项波动均落在轮间噪声内(−0.4%~−1.4%),无可复现收益;--highvram 强压全模型进显存反而慢 67%,明确禁用。hipBLASLt 运行时已加载(GEMM 调优天花板是开的),下一步可做带持久化缓存的正式 TunableOp 实测(~25 分钟,待确认)。
Windows 系统层(VBS/Defender 排除/进程优先级/Adrenalin 钩子)需你在宿主机自查——沙箱里 reg.exe 被禁读不到。
详细证据表见两份报告 §11.9 / §M。
安装
一键部署(推荐):连 ComfyUI 和模型一起搞定
node scripts/setup.mjs # 检测 + 缺什么补什么
node scripts/setup.mjs --check # 只看差什么,绝不动任何东西
node scripts/setup.mjs --verify # 已存在的模型也重算 SHA256(最稳)
node scripts/setup.mjs --comfy D:\ComfyUI # 指定 ComfyUI 目录
node scripts/setup.mjs --tier full|lite # 直接指定主模型档位(见下)
它会:① 自动找 ComfyUI;② 把三个模型下到正确目录并逐个 SHA256 校验(校验不过的一律删除);
③ 调 install.mjs 把插件挂进 DSH profile。
主模型两个档位(小显存适配)
装的时候会问一句,两个档位:
| 档位 | 主模型 | 体积 | 适合 |
|---|---|---|---|
| 完整档(默认) | qwen_image_2.1_int8_convrot.safetensors |
6.8 GB | 显存 ≥ 8 GB,画质最好 |
| 轻量档 | qwen-image-2.1-UC-Q4_K_M.gguf(GGUF Q4_K_M) |
4.3 GB | 8 GB 及以下(如 RTX 5060) |
CLIP 与 VAE 两档共用,只有主模型不同。
轻量档有两件事必须知道:
- 要另装 ComfyUI-GGUF 节点(
city96/ComfyUI-GGUF),否则出图会被 ComfyUI 直接拒收:git clone https://github.com/city96/ComfyUI-GGUF "<你的ComfyUI>/custom_nodes/ComfyUI-GGUF" - 该 GGUF 是 Uncensored 系的 —— 经核实,目前能在 ComfyUI-GGUF 下跑起来的
Qwen-Image GGUF 只有这一类。介意的话装完自行替换
models/unet里的文件即可, 插件会自动认你换的那个。
⚠ 重要限制(2026-10-12 排查):Qwen-Image 2.1 的 GGUF 目前只有
abenzerps一家提供,且只在 HuggingFace。脚本会依次试hf-mirror.com→huggingface.co,但 hf-mirror 对 LFS 大文件只返回 308 跳转、等于没镜像,所以国内网络下很可能下不动。city96(ComfyUI-GGUF 作者)确实有一份在 ModelScope 上,但他转的是 初代 Qwen-Image(20B,Q4_K_M 12.17 GB),与 2.1(7B)不能混用。 ⇒ 下不动是预期内的:自己弄一个可用的.gguf放进models/unet即可,插件会按文件名自动挑最优的那个。
⚠ 曾经的坑:这里原先是
unsloth/Qwen-Image-2.1-GGUF的 Q4_K_M, 那个是 stable-diffusion.cpp 格式的 GGUF(元数据为空、张量名model.diffusion_model.*),ComfyUI-GGUF 加载不了。2026-10-12 已换成 实测能用的abenzerps系,并保留 ComfyUI-GGUF 的安装提示。
显存还是紧张? 降 megapixels(如 0.5)立竿见影;出图目录也可以设到固态盘上(见「配置」的 outputDir)。
显卡:N 卡开箱即用,A 卡走 ROCm(0.1.1 新增)
脚本先看显卡再决定怎么装:N 卡用 ComfyUI 官方便携包;AMD Radeon 走 ROCm 路线
(AMD 官方 Windows wheels,装完还是 ComfyUI,插件本身不用换,模型也是同一套);
Intel 核显基本跑不动。
AMD 用户先 node scripts/setup.mjs --check 看计划,确认后 --yes 执行。
⚠ 三条必读:A 卡可能静默出错(跑完必须人眼看图)、别升 ROCm 10.0
(HIP 7.15 会破坏权重,脚本锁 7.2.1)、模型禁止商用。
★ RX 5000 系列(RDNA1 / gfx101x)单独说一句:这一代不在 ROCm 官方支持矩阵内 —— PyTorch 官方 ROCm 轮子没有编译 gfx101x,通常需要
HSA_OVERRIDE_GFX_VERSION(或自行按 gfx1010 / gfx1012 重新编译 PyTorch)才能让 PyTorch 认卡。 这属于环境搭建层面的事,插件不改也不该改(改驱动/编译轮子的风险远大于收益), 只在识别到这一代时把这件事写进面板提示,免得你"装完发现认不到卡"。 注意力后端上它与 RDNA2 同样走保守的--use-pytorch-cross-attention(ROCm 的 AOTriton 融合内核只覆盖 RDNA3 / 3.5 / 4,CK SDPA 也不覆盖 gfx101x)。
完整说明见 INSTALL.md 的「AMD Radeon 用户」一节。
只装插件本体
node scripts/install.mjs # 装进 desktop profile(默认)
node scripts/install.mjs --profile web
node scripts/install.mjs --uninstall
node scripts/install.mjs --dry-run
脚本做的事(幂等、改前备份、验证不过自动回滚):
- 先 import 一次插件入口,ESM 语法错或依赖解析失败会当场暴露;
- 备份 profile 的
package.json到profiles\<profile>\_backup-dsh-qwen-paint-<时间戳>\; - 写
dependencies["dsh-qwen-paint"] = "link:<本目录>"; - 把
dsh-qwen-paint加进dsh.profile.bundles; - 建 Junction
profiles\<profile>\node_modules\dsh-qwen-paint → 本目录; - 复核三处都落了,没落就回滚。
官方首选路径是 DSH 自己的 Plugin Manager(
plugin_manager的install_bundle,target = 本包绝对目录),
或等价 CLIdsh plugin --profile desktop add link:<本目录>(desktop profile 必须先完全退出 DSH)。
本脚本是兜底路径,做的是等价的两件事。⚠ 若官方 Plugin Manager 因网络装不上 —— 典型报错
ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION,
或访问registry.npmmirror.org超时 / 证书不被信任 —— 那不是插件的问题,
是 pnpm 的供应链校验连不上源。直接用上面的scripts/install.mjs即可:
与官方等价,且不依赖任何包管理器网络请求(本插件零运行时依赖)。
装完必须彻底退出 DSH 再重新打开 —— 宿主侧工具清单只在启动时装配一次。
(DSH 是桌面端 Electron 应用,没有刷新页面这回事。)
用法
装好并重启后,在聊天框直接说:
画一只在雪地里的柴犬,逆光,胶片颗粒,电影感
给我画一张 16:9 的赛博朋克雨夜街景,霓虹反射在积水上
用同样的种子再画一遍刚才那张
助手会调 draw_image。可传的参数:
| 参数 | 说明 |
|---|---|
prompt |
必填,画面描述(中文可以)。建议写完整一段:主体 + 细节 + 环境 + 光线 + 风格 |
size |
画幅比例:1:1(默认)、3:4、4:3、2:3、3:2、16:9、9:16 |
megapixels |
总像素(百万),默认 1.0。要更清楚就调大 |
steps |
采样步数。⚠ 默认被强制钉在 8 步(turbo8 的工作点),手填别的数会被忽略 —— 详见下面「加速链」 |
seed |
随机种子。同提示词 + 同种子 = 同一张图;不填则每次不同 |
negative |
负面提示词。⚠ 千问模型 cfg=1,负面词基本不生效,一般别填 |
尺寸按 32 的倍数对齐(r32(v)=max(32, round(v/32)*32)),与 app.py 的算法一致:1:1@1MP → 992×992;16:9@1MP → 1344×736;2:3@1MP → 832×1216。
出图落在哪
C:\Users\<你的用户名>\Desktop\千问1生图\(遵循工作区约定;也在会话工作区「桌面」之内,
所以交付卡片和行内图片都能取到这个文件)。
命名:<提示词前若干字>_Qwen2.1_<宽>x<高>_<序号>.png,重名自动加 -2、-3。
图片是怎么显示在聊天框里的(原理,改之前务必看)
这一节是 2026-10-07 逐个查内核源码核过的,不要凭直觉改:
| 路径 | 能用吗 | 原因(都有内核源码依据) |
|---|---|---|
ImageBlock(工具结果里返 {type:"image", attachment}) |
✗ | 客户端 imageCardModel() 开头就是 if (call?.name !== "read_image") return null —— 只认 read_image;其他工具的 image block 会被 generic 卡片用 JSON.stringify 当文本打印出来 |
presentCall / presentResult |
✗ | ui-tool README 原文:"Host presentCall and presentResult values never enter the Client." |
presentationMeta 让图显示 |
✗ | 它确实会落到 tool/result 的 data.meta,但客户端只有 read_image 那张卡片读它、且只当路径标签用 |
present 的交付卡片显示图 |
✗ | dsh-client-ui-deliverables 客户端全文没有任何 <img> / loadImage;交付卡片只给「用默认程序打开」的入口 |
| 助手消息直接带图片 | ✗ | 附件子系统文档明说:"当前生产适配器声明只输出文本,因此只有用户内容携带图片" |
① 正文 Markdown 图片  |
✓ | 官方设计:客户端 fileMediaUrl() 把本地绝对路径重写成 <base>/api/file?path=…,打到内核已认证的 /api/file 路由取字节,渲染成真 <img>。内核自带的 FILE_REFERENCE_PROMPT 就是这么教模型的 |
| ② 客户端自定义 toolview | ✓ 100% 确定 | tool.call.toolview 是 keyed 槽,文档原文"Any name is allowed, including tools registered by your package";draw_image 这个 key 未被占用,注册它不遮蔽任何自带 UI |
本插件两条都做,互不依赖:
- ① host 侧用
ctx.systemPrompt.section({ name: 'qwen-paint:inline-generated-image', order: 9100, text: … })
下一条指令,要求模型出图后在正文里内联展示(与内核DELIVERABLE_FILE_REFERENCES同一机制)。 - ②
lib/client.js注册tool.call.toolview的draw_image格子:运行中显示「正在用本机千问画图…」,
完成后自己<img src="api/file?path=…">渲染,失败时显示错误原文。
另外 host 侧仍会 append deliverables/presented(与官方 present 同一载荷)——
这不是为了显示图,而是顺带给一个可点开/用默认程序打开的正式交付入口,且它能让记录可回放。
空闲自动关停(省显存)
要求:"不要让 comfyUI 一直跑着,只要五分钟不生图 comfyUI 后端自动关闭。"
- 计时只在插件出过图之后才开始(
lastActivityAt初始为null)。
→ 所以绝不会误关你自己打开的、正在「小鲸鱼生图」里用的 ComfyUI。 - 到点后还要过两关才动手:ComfyUI 仍在线,且
/queue为空(有任务在跑就续期,绝不打断)。 - 只杀"监听该端口 且 命令行确实含
comfy与main.py"的进程;认不出来就跳过并记日志——
绝不按端口盲杀。 - 关掉后计时复位;下次出图需要重新启动 ComfyUI(
draw_image会明确提示怎么启动)。
配置:idleShutdownMs(默认 300000 = 5 分钟)、autoShutdown(默认 true,设 false 就永不自动关)。
ComfyUI 在线状态提示(composer 下方)
在 composer 卡片下方(conversation.composer.dock 槽)显示一个小指示器:
| 圆点 | 含义 |
|---|---|
| 🟢 绿 | ComfyUI 在线;若正在空闲倒计时,会写「空闲 N 分后自动关闭」 |
| 🟡 黄 | ComfyUI 正在出图(/queue 非空) |
| ⚪ 灰 | ComfyUI 未启动 / 状态未知 |
它每 5 秒读一次 host 的只读端点 GET /api/qwen-paint/status.json
(回退 /qwen-paint/status.json)。该端点:
- 只放行本机请求(Host 与远端地址都必须是回环),其余一律 403;
- 只回事实(
online/busy/idleShutdownInMs/url),不含任何本地路径或密钥; - 任何意外都回 200 + 可读原因,前端显示"未知"而不是报错。
该槽是 list 槽、replaceRisk: none,插件用自己的 id(qwen-paint-status)注册,
与自带的 stats pill 并存,不替换任何自带 UI。
故障排查
| 现象 | 原因 / 处理 |
|---|---|
| 说"画个图",助手说没有这个工具 | 插件没生效 → 确认 profile 的 dsh.profile.bundles 里有 dsh-qwen-paint,然后彻底退出 DSH 重开 |
| 报"连不上本机 ComfyUI" | ComfyUI 没启动。双击 D:\ComfyUI\启动ComfyUI.bat 或先开「小鲸鱼生图」(它会自动拉起引擎),等它起来再画 |
| 报"等 ComfyUI 出图超时" | 首次出图要加载 7.26 GB UNET + 6.31 GB 文本编码器,第一次特别慢;本机实测 25 步 1MP 约 62–124 秒,首次更久 |
报 ComfyUI 报错:... + 节点校验详情 |
模型文件名对不上或节点不存在 → 对照上面「依赖」里的三个文件名,以及 ComfyUI 版本需支持 TextEncodeQwenImage21 / QwenImage21Cache(本机 0.37.0 内置) |
| 图出来了但聊天框只有文字 | 交付卡片依赖会话工作区能取到该文件;把 outputDir 设在会话工作区内(默认就在桌面「千问1生图」) |
出图报 value not in list / 被直接拒收 |
选了轻量档(GGUF)但没装 ComfyUI-GGUF 节点 —— 克隆 ComfyUI-GGUF 到 custom_nodes 后重启 ComfyUI。插件会在优化面板置顶用橙色块提醒你 |
| 显存不够 / 很慢 | 三个模型共约 14 GB,8 GB 卡必然换入换出。两个办法:① 装插件时选「轻量档」(GGUF Q4_K_M,约 4.3 GB,见上面「主模型两个档位」);② 降低 megapixels(如 0.5) |
| 优化面板里点了开关,像"没反应" | 面板里那几个开关(A 卡专属内核 / 异步卸载 / SageAttention)是启动参数 ⇒ 改完要重启 ComfyUI 才生效(面板提示里写了)。另外它们是内存态,重载 DSH 会回到 cordis.patch.yml 里的值 —— 想永久生效就写进配置 |
| macOS 上找不到 ComfyUI | 官方对 Apple Silicon 只提供手动安装(没有便携包)。装好后用 node scripts/setup.mjs --comfy <你的 ComfyUI 目录> 指定位置即可(详见 INSTALL.md) |
自测
node scripts/selftest.mjs
纯 Node,不联网、不需要真 ComfyUI、不出图、不占显存:起一个假 ComfyUI HTTP 服务器,
把插件真跑一遍(探活 → 提交 → 轮询 → 取图 → 落盘 → 交付事件),另加尺寸/文件名/workflow
结构/失败分支/空闲关停边界/状态端点鉴权/客户端卡片渲染的断言(287 项,以脚本实际打印为准)。
配置
改 cordis.patch.yml 里那一行的 config(这一层升级插件也不会被覆盖):
| 字段 | 默认 | 说明 |
|---|---|---|
comfyUrl |
http://127.0.0.1:8188 |
ComfyUI 地址 |
outputDir |
C:\Users\<你的用户名>\Desktop\千问1生图 |
成品落盘目录 |
comfyOutputDir |
D:\ComfyUI\ComfyUI\output |
先在本地取文件,取不到才走 /view |
unet / clip / vae |
三个千问文件名 | 换模型时改这里 |
steps / resolution |
8 / 1024 |
默认步数(见下 turbo8Steps)与编码分辨率 |
timeoutMs |
600000 |
单任务超时(10 分钟) |
idleShutdownMs |
300000 |
★ 出图后多久没再出图就自动关掉 ComfyUI(5 分钟) |
autoShutdown |
true |
设 false 就永不自动关 |
idleCheckMs |
30000 |
空闲检查间隔 |
statusCacheMs |
3000 |
状态探测缓存(客户端每 5 秒轮询一次) |
⚠
unet除了"用哪个模型",还决定用哪个加载器:文件名以.gguf结尾 → 走UnetLoaderGGUF(需 ComfyUI-GGUF);否则走原生UNETLoader。
厂商专属优化(可选,默认关)★ 2026-10-11 新增
与 N 卡/I 卡的「厂商专属优化」同一套思路:只在适用的显卡上生效、默认关闭。 也可以在优化面板里点开关(内存态,重载 DSH 会回到这里的值)。
| 字段 | 默认 | 适用 | 说明 |
|---|---|---|---|
amdKernel |
false |
AMD | 换用本代的替代注意力内核:5000 / 6000 → --use-quad-cross-attention,7000 / 9000 → --use-ck-attention |
amdAsyncOffload |
false |
AMD | --async-offload(ComfyUI 在 N 卡上默认开、A 卡上默认关,这里给 A 卡补上) |
nvidiaSageAttention |
'auto' |
NVIDIA | 'auto' = 自检确认 sageattention 已装才挂 --use-sage-attention;true 强制挂(确定装好了再设,没装会让 ComfyUI 起不来);false 永不挂 |
appleMpsFallback |
true |
macOS | 注入 PYTORCH_ENABLE_MPS_FALLBACK=1:MPS 未实现的算子自动回退 CPU,而不是让整次出图抛错(质量中性,只影响"本来就会失败"的路径) |
appleMpsUnlimitedMem |
false |
macOS | 注入 PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0:取消 MPS 分配器硬上限(官方默认 1.7),大模型在小内存 Mac 上可能就能跑。⚠ 系统级 OOM 会连系统一起拖垮,官方原文 "may cause system failure" |
comfyAttentionBackend |
'auto' |
全部 | 手动接管注意力后端:quad / pytorch / ck / default(auto = 按显卡自动选) |
⚠ 前三个都是启动参数 ⇒ 改完必须重启 ComfyUI 才生效。 ⚠ 两个
appleMps*是注入给 ComfyUI 进程的环境变量(不是命令行参数)⇒ 同样需重启 ComfyUI; 且只在 macOS 生效 —— 其它平台改它们会被服务端直接拒绝(没有 MPS 后端,改了也不会发生什么)。 ⚠ Mac 上不会挂--use-sage-attention/--use-quad-cross-attention/--use-ck-attention(MPS 没有这些内核),也不会给统一内存加--highvram那套(MPS 的vram_state恒为SHARED,加了不参与)。
加速链(★ 2026-10-08 新增,默认全开)
| 字段 | 默认 | 说明 |
|---|---|---|
turbo8Lora |
turbo8_lora_step2500.safetensors |
LoRA 文件名;留空 = 用这个内置默认名 |
turbo8Strength |
1.0 |
LoRA 强度。发布方按 1.0 训;调到 0.65 更"稳"但会丢风格 |
turbo8MaxShift / turbo8BaseShift |
0.6935 / 0.5 |
ModelSamplingFlux 的位移 —— turbo8 的工作点,别乱改 |
turbo8Steps |
0 |
0 = 跟随 8 步;写 1–60 用那个数(⚠ 要生效得先关 forceTurbo8Steps) |
forceTurbo8Steps |
true |
是否把步数强制钉在 8(连用户手填的 steps 也覆盖) |
qwenCacheDevice / qwenCacheDtype |
auto / int8 |
官方 QwenImage21Cache:int8 折半缓存、约等于 bf16 精度 |
teSpeedEnabled |
true |
★ TE-Speed 总开关。设 false = 整个 TESpeed 节点摘掉(适合介意闭源 .pyd 的人);turbo8 单跑仍有 12.6 s |
teSpeedAttention |
kitchen_int8 |
TE-Speed 的注意力实现 |
teSpeedStepCache |
te_predictor |
每跳最多 1 步连续跳过;speed 是 2 步(更激进、更糊) |
teSpeedThreshold |
0.06 |
reuse_threshold:实测过的档位 |
teSpeedStartPercent / teSpeedEndPercent |
0.0 / 1.0 |
生效窗口 |
teSpeedErrorLimit |
0.08 |
predictor_error_limit |
teSpeedVerbose |
false |
每步刷日志(排查用) |
想完全退回改动前的老链路(25 步、无 LoRA、无缓存):这不是一个开关能切回去的 ——
LoRA 与缓存节点现在无条件接在模型链上。最小可行回退是teSpeedEnabled: false(摘掉闭源节点)+ forceTurbo8Steps: false + turbo8Steps: 25;
要连 LoRA 一起去掉,建议直接恢复 lib/index.js 的备份(lib/index.js.bak-before-turbo8-*)。
设计取舍(为什么这么写)
- 零外部依赖:不
import @deepseek-ai/dsh-tools。tools.register()只强校验output.schema是标准 JSON Schema,而parameters的 spec→JSON Schema 转换是defineTool()做的、register()不校验 —— 既然自己写成标准 JSON Schema 就够,
就不引入"装进 profile 后解析不到包"的风险(插件加载失败会让 DSH 起不来)。 - 用局部
root.inject等服务,不用对象级inject:对象级会把整个apply
推迟到服务就绪之后,某个服务在老宿主里不存在时插件会永远不 apply 且不报错。 - workflow 照抄主人机器上已验证的那份(
D:\ComfyUI\小鲸鱼生图\app.py的build_workflow),节点与连线一个字不改。 - 启动预热:插件加载时后台探一次后端,不在线就隐藏窗口拉起
(不 await、不阻塞 DSH 启动);出图时还有一次兜底,所以预热失败也不影响出图。 - 空闲关停的计时起点有两个:预热确认后端在线时、以及你在界面里改档位时。
(⚠ 早先只有"出图成功"才算起点,结果重启 DSH 后一次图都没画就永远不关 —— 已修。)
到点还要确认它在线、/queue为空(有任务就续期,绝不打断),
并且只杀命令行确实像 ComfyUI 的进程 —— 宁可留着占显存,也不误杀别人的进程。 - 状态端点只放行本机、只回事实:
{ok, online, busy, idleShutdownInMs, url},
不含任何本地路径或模型名(自测里有专门一条断言守着这条)。 - 客户端状态指示器用 React 座位 + 纯 DOM(照
dsh-reasoning-glow的做法):
React 只渲染一个<span>座位,圆点与文字都在useEffect里用原生 DOM 挂,
并且注册前检查React.useRef/useEffect存在 —— 种子被裁剪过就安静跳过,绝不抛。
许可
MIT —— 见 LICENSE。你可以自由使用、修改、再分发(包括商用),
只需保留版权声明与许可声明。
致谢与来源
- 15 种流光的配色抄自「月匠」 —— 原样照搬、一个色标没改,仅作致敬与复用,
相关权利归原作者;原作者若有异议,联系即删。 - 模型权重来自 Comfy Org 官方在 ModelScope 的仓库
(https://modelscope.cn/models/Comfy-Org/Qwen-Image-2.1)。
本仓库只记录官方 SHA256,不转存、不镜像任何权重文件。 - ComfyUI 是 comfyanonymous/ComfyUI 的作品,
本插件只是通过它的 HTTP API 下任务。
作者
原作者:@奇迹与你 | https://github.com/wonderandyou/dsh-qwen-paint
优化版:@yanfei0725 | https://github.com/yanfei0725/dsh-qwen-paint
No comments yet. Be the first to write one.