feat(接口2): 支持动态切换Flux模型+分辨率 + 模型对比测试脚本
代码改动: - comfyui.py: run() 新增 unet_name 参数,提交前自动改写模型节点 (.gguf→UnetLoaderGGUF, .safetensors→UNETLoader),并按模型自动同步 文本编码器(4b→qwen_3_4b, 9b→qwen_3_8b),避免切换时维度不匹配 - redraw.py: run_redraw() 透传 unet_name - service.py: generate_grow_results_swap/generate_grow_results 支持 redraw_max_side(分辨率参数化) 和 unet_name 透传 - app.py: 接口2 新增 flux_model/redraw_max_side 两个 Form 参数(男女路径都加) - test_interface2.html: 新增 Flux模型/压图长边 下拉选择器 - add_hair.json/0716add-hair-api.json: 工作流默认模型改为 9b 测试脚本: - benchmark_matrix.py: 4模型×3分辨率×3图×3次 矩阵测试 - benchmark_hairstyle.py: 3图×5发型×10组合 发型对比测试 - benchmark_report.py/benchmark_hairstyle_report.py: HTML报告生成 清理: - .gitignore: 排除 benchmark_out/、报告HTML、gateway.log、*.bak.* - 移除 gateway.log 的 git 跟踪
This commit is contained in:
+34
-1
@@ -30,6 +30,19 @@ _REPO = os.path.dirname(os.path.dirname(__file__))
|
||||
_INPUT_NODE = "26" # LoadImage:外部输入图(含 alpha 遮罩)
|
||||
_SEED_NODE = "6" # RandomNoise
|
||||
_PROMPT_NODE = "60" # JjkText:提示词
|
||||
_UNET_NODE = "16" # UNETLoader / UnetLoaderGGUF:Flux 模型加载
|
||||
_CLIP_NODE = "61" # CLIPLoader:qwen 文本编码器
|
||||
|
||||
# Flux 模型 → 配套文本编码器映射。切换 unet 时自动同步编码器,避免维度不匹配。
|
||||
# 规则:4b 系列配 qwen_3_4b,9b 系列(fp8/GGUF)配 qwen_3_8b_fp8mixed。
|
||||
def _clip_for_unet(unet_name: str) -> str | None:
|
||||
"""根据 unet 文件名推断配套的文本编码器文件名;无法推断返回 None。"""
|
||||
low = unet_name.lower()
|
||||
if "4b" in low and "9b" not in low:
|
||||
return "qwen_3_4b.safetensors"
|
||||
if "9b" in low:
|
||||
return "qwen_3_8b_fp8mixed.safetensors"
|
||||
return None
|
||||
|
||||
_wf_cache: dict[str, dict] = {} # path → workflow JSON
|
||||
_wf_output_node: dict[str, str] = {} # path → SaveImage 节点 ID
|
||||
@@ -86,13 +99,18 @@ def _get_output_node(workflow_path: str | None = None) -> str:
|
||||
|
||||
|
||||
def run(rgba_png_bytes: bytes, timeout: float = COMFY_TIMEOUT, prompt: str = None,
|
||||
workflow_path: str | None = None, front: bool = False) -> bytes:
|
||||
workflow_path: str | None = None, front: bool = False,
|
||||
unet_name: str | None = None) -> bytes:
|
||||
"""提交一次生发任务,返回输出 PNG 字节。失败抛异常。
|
||||
|
||||
prompt:非 None 时替换工作流节点60(JjkText)的文本;None 时用工作流内置默认提示词。
|
||||
workflow_path:工作流 JSON 路径,None 则用默认 add_hair.json。
|
||||
front:True 时任务插到 ComfyUI 队列最前(server 端 "front" 字段,队列号取负)。
|
||||
接口2 对时延敏感用 True,避免排在接口3/5 的批量任务后面;其余接口保持 False。
|
||||
unet_name:非 None 时改写工作流里的模型加载节点(节点16),动态切换 Flux 模型。
|
||||
.safetensors → 保持 UNETLoader 节点类型不变,只替换 unet_name;
|
||||
.gguf → 自动把节点类型改成 UnetLoaderGGUF(需装 ComfyUI-GGUF 插件)。
|
||||
None 时用工作流内置默认模型。
|
||||
"""
|
||||
path = workflow_path or _WORKFLOW_DEFAULT
|
||||
output_node = _get_output_node(path)
|
||||
@@ -120,6 +138,21 @@ def run(rgba_png_bytes: bytes, timeout: float = COMFY_TIMEOUT, prompt: str = Non
|
||||
wf[_SEED_NODE]["inputs"]["noise_seed"] = random.randint(0, 2**63 - 1)
|
||||
if prompt is not None:
|
||||
wf[_PROMPT_NODE]["inputs"]["text"] = prompt
|
||||
if unet_name is not None:
|
||||
node = wf.get(_UNET_NODE)
|
||||
if node is not None:
|
||||
# .gguf 需切换到 ComfyUI-GGUF 插件的 UnetLoaderGGUF 节点;
|
||||
# .safetensors/.ckpt 保持原 UNETLoader 节点类型不变
|
||||
if unet_name.lower().endswith(".gguf"):
|
||||
node["class_type"] = "UnetLoaderGGUF"
|
||||
else:
|
||||
node["class_type"] = "UNETLoader"
|
||||
node["inputs"]["unet_name"] = unet_name
|
||||
# 同步切换配套文本编码器(4b→qwen_3_4b, 9b→qwen_3_8b),避免维度不匹配
|
||||
clip_node = wf.get(_CLIP_NODE)
|
||||
clip_name = _clip_for_unet(unet_name)
|
||||
if clip_node is not None and clip_name is not None:
|
||||
clip_node["inputs"]["clip_name"] = clip_name
|
||||
|
||||
# 诊断:落盘实际提交的工作流 + 输入图,便于和手动 ComfyUI 跑的对比
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user