Spaces:
Running on Zero
Running on Zero
| """One shared, task-switchable UI instead of five duplicated forms.""" | |
| from __future__ import annotations | |
| import gradio as gr | |
| from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES | |
| from core.execution_plan import RUN_MODE_CHOICES | |
| from core.runtime_config import CONFIG | |
| from core.settings import ( | |
| ARCHITECTURES_CONFIG, | |
| MODEL_DEFAULTS_CONFIG, | |
| MODEL_MAP_CHECKPOINT, | |
| MODEL_TYPE_MAP, | |
| RESOLUTION_MAP, | |
| ) | |
| from ui.guidance import QUICK_PRESETS, model_hint, task_help | |
| from .ui_components import ( | |
| create_anima_controlnet_lllite_ui, | |
| create_boogu_edit_ui, | |
| create_category_filter_ui, | |
| create_conditioning_ui, | |
| create_controlnet_ui, | |
| create_diffsynth_controlnet_ui, | |
| create_embedding_ui, | |
| create_flux1_ipadapter_ui, | |
| create_hidream_o1_reference_ui, | |
| create_ipadapter_ui, | |
| create_joyai_reference_ui, | |
| create_krea2_controlnet_ui, | |
| create_krea2_identity_edit_ui, | |
| create_krea2_reference_edit_ui, | |
| create_lora_settings_ui, | |
| create_model_architecture_filter_ui, | |
| create_pid_ui, | |
| create_qwen_image_edit_ui, | |
| create_reference_image_ui, | |
| create_reference_latent_ui, | |
| create_sd3_ipadapter_ui, | |
| create_style_ui, | |
| create_vae_override_ui, | |
| ) | |
| TASK_CHOICES = [ | |
| ("✨ 文生图", "txt2img"), | |
| ("🖼️ 图生图", "img2img"), | |
| ("🖌️ 局部重绘", "inpaint"), | |
| ("↔️ 扩图", "outpaint"), | |
| ("🔎 高清修复", "hires_fix"), | |
| ] | |
| PROMPT_EXAMPLES = [ | |
| ["一位银发少女站在雨夜车站,半身构图,电影感侧光,细腻插画,高质量"], | |
| ["现代东方客厅,浅木色与米白配色,午后自然光,广角室内摄影"], | |
| ["未来城市夜景,湿润街道倒影,霓虹灯,低机位,强烈纵深,电影概念设计"], | |
| ] | |
| def create_ui() -> dict: | |
| prefix = "studio" | |
| components: dict = {"_task_prefixes": [(prefix, None)]} | |
| default_values = MODEL_DEFAULTS_CONFIG.get("Default", {}) | |
| default_model = next(iter(MODEL_MAP_CHECKPOINT), None) | |
| default_architecture = MODEL_TYPE_MAP.get(default_model, "SDXL") | |
| default_architecture_info = ARCHITECTURES_CONFIG.get("architectures", {}).get( | |
| default_architecture, {} | |
| ) | |
| default_resolution_key = default_architecture_info.get("model_type", "sdxl") | |
| default_resolution_map = RESOLUTION_MAP.get( | |
| default_resolution_key, RESOLUTION_MAP.get("sdxl", {}) | |
| ) | |
| default_aspect = next(iter(default_resolution_map), "1:1 (Square)") | |
| default_width, default_height = default_resolution_map.get(default_aspect, (1024, 1024)) | |
| components[f"task_type_{prefix}"] = gr.Radio( | |
| choices=TASK_CHOICES, | |
| value="txt2img", | |
| label="1. 选择任务", | |
| elem_id="task-switcher", | |
| ) | |
| components[f"task_help_{prefix}"] = gr.Markdown( | |
| task_help("txt2img"), elem_classes="task-help" | |
| ) | |
| with gr.Row(equal_height=False, elem_id="workspace-row"): | |
| with gr.Column(scale=7, min_width=360): | |
| with gr.Accordion("2. 选择模型", open=True): | |
| components[f"quick_preset_{prefix}"] = gr.Dropdown( | |
| choices=QUICK_PRESETS, | |
| value="__manual__", | |
| label="快速方案", | |
| info="只是帮你定位模型;仍可继续调整所有参数。", | |
| interactive=True, | |
| ) | |
| components.update(create_model_architecture_filter_ui(prefix)) | |
| with gr.Row(): | |
| components.update(create_category_filter_ui(prefix)) | |
| components[f"base_model_{prefix}"] = gr.Dropdown( | |
| label="具体模型", | |
| choices=list(MODEL_MAP_CHECKPOINT.keys()), | |
| value=default_model, | |
| filterable=True, | |
| allow_custom_value=False, | |
| scale=3, | |
| interactive=True, | |
| ) | |
| components[f"model_hint_{prefix}"] = gr.Markdown( | |
| model_hint(default_model) if default_model else "暂无可用模型。", | |
| elem_classes="model-hint", | |
| ) | |
| with gr.Accordion("批量、多图与模型 PK(可选)", open=False): | |
| components[f"run_mode_{prefix}"] = gr.Dropdown( | |
| choices=RUN_MODE_CHOICES, | |
| value="single", | |
| label="运行方式", | |
| info="所有模式都顺序执行,不会同时把多个大模型塞进显存。", | |
| ) | |
| components[f"pk_models_{prefix}"] = gr.Dropdown( | |
| choices=list(MODEL_MAP_CHECKPOINT.keys()), | |
| value=[], | |
| multiselect=True, | |
| max_choices=max(1, CONFIG.max_pk_models - 1), | |
| label="额外对比模型", | |
| info=f"当前模型会自动加入;最多合计 {CONFIG.max_pk_models} 个。", | |
| filterable=True, | |
| visible=False, | |
| ) | |
| components[f"batch_images_{prefix}"] = gr.File( | |
| file_count="multiple", | |
| file_types=["image"], | |
| type="filepath", | |
| allow_reordering=True, | |
| label="批量输入 / 多图参考", | |
| visible=False, | |
| ) | |
| with gr.Row(): | |
| components[f"reference_role_{prefix}"] = gr.Dropdown( | |
| choices=[ | |
| ("自动匹配模型能力", "auto"), | |
| ("人物 / 身份参考", "identity"), | |
| ("风格参考", "style"), | |
| ], | |
| value="auto", | |
| label="多图融合用途", | |
| visible=False, | |
| ) | |
| components[f"pk_model_defaults_{prefix}"] = gr.Checkbox( | |
| value=True, | |
| label="PK 使用各模型推荐采样参数", | |
| info="Prompt、尺寸与种子保持一致;步数和 CFG 按模型推荐值。", | |
| visible=False, | |
| ) | |
| gr.Markdown( | |
| "**怎么选:** 多图独立是 A→A′、B→B′;多图×多模型会执行图片和模型的组合;" | |
| "多图融合是把整组图片同时交给 Qwen/Mage-Flow/JoyAI/Boogu/Krea/Flux 等兼容模型。" | |
| ) | |
| with gr.Group(elem_classes="prompt-card"): | |
| components[f"prompt_{prefix}"] = gr.Textbox( | |
| label="3. 描述你想要的画面", | |
| placeholder="主体 + 动作 + 场景 + 构图 + 风格 + 光线", | |
| lines=4, | |
| max_lines=8, | |
| value=default_values.get("positive_prompt", ""), | |
| ) | |
| with gr.Accordion("负面提示词(可选)", open=False): | |
| components[f"neg_prompt_{prefix}"] = gr.Textbox( | |
| label="不希望出现的内容", | |
| placeholder="例如:低质量、模糊、多余手指、水印", | |
| lines=2, | |
| value=default_values.get("negative_prompt", ""), | |
| ) | |
| gr.Examples( | |
| examples=PROMPT_EXAMPLES, | |
| inputs=[components[f"prompt_{prefix}"]], | |
| label="点一下填入示例", | |
| ) | |
| with gr.Group(visible=False) as source_panel: | |
| components[f"source_image_{prefix}"] = gr.Image( | |
| type="pil", | |
| label="源图片", | |
| sources=["upload", "clipboard"], | |
| height=300, | |
| ) | |
| components[f"source_panel_{prefix}"] = source_panel | |
| with gr.Group(visible=False) as inpaint_panel: | |
| gr.Markdown("涂抹需要替换的区域;未涂抹部分会尽量保留。") | |
| components[f"inpaint_image_dict_{prefix}"] = gr.ImageEditor( | |
| type="pil", | |
| label="图片与蒙版", | |
| sources=["upload", "clipboard"], | |
| height=420, | |
| ) | |
| with gr.Row(): | |
| components[f"inpaint_denoise_{prefix}"] = gr.Slider( | |
| 0.0, 1.0, value=1.0, step=0.05, label="局部重绘幅度" | |
| ) | |
| components[f"grow_mask_by_{prefix}"] = gr.Slider( | |
| 0, 64, value=6, step=1, label="蒙版外扩像素" | |
| ) | |
| components[f"inpaint_panel_{prefix}"] = inpaint_panel | |
| with gr.Group(visible=False) as img2img_panel: | |
| components[f"img2img_denoise_{prefix}"] = gr.Slider( | |
| 0.0, | |
| 1.0, | |
| value=0.7, | |
| step=0.01, | |
| label="重绘幅度", | |
| info="0 更接近原图,1 改动最大。", | |
| ) | |
| components[f"img2img_panel_{prefix}"] = img2img_panel | |
| with gr.Group(visible=False) as outpaint_panel: | |
| with gr.Row(): | |
| components[f"left_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向左扩展") | |
| components[f"right_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向右扩展") | |
| with gr.Row(): | |
| components[f"top_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向上扩展") | |
| components[f"bottom_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向下扩展") | |
| components[f"feathering_{prefix}"] = gr.Slider( | |
| 0, 100, 10, step=1, label="接缝羽化 / 蒙版外扩" | |
| ) | |
| components[f"outpaint_panel_{prefix}"] = outpaint_panel | |
| with gr.Group(visible=False) as hires_panel: | |
| with gr.Row(): | |
| components[f"hires_upscaler_{prefix}"] = gr.Dropdown( | |
| choices=["nearest-exact", "bilinear", "area", "bicubic", "bislerp"], | |
| value="nearest-exact", | |
| label="放大算法", | |
| ) | |
| components[f"hires_scale_by_{prefix}"] = gr.Slider( | |
| 1.0, 4.0, 1.5, step=0.1, label="放大倍数" | |
| ) | |
| components[f"hires_denoise_{prefix}"] = gr.Slider( | |
| 0.0, 1.0, 0.55, step=0.01, label="细节重绘幅度" | |
| ) | |
| components[f"hires_panel_{prefix}"] = hires_panel | |
| with gr.Group() as size_panel: | |
| with gr.Row(): | |
| components[f"aspect_ratio_{prefix}"] = gr.Dropdown( | |
| label="画幅", | |
| choices=list(default_resolution_map) or [default_aspect], | |
| value=default_aspect, | |
| interactive=True, | |
| scale=2, | |
| ) | |
| components[f"width_{prefix}"] = gr.Number( | |
| label="宽度", value=default_width, precision=0, interactive=True | |
| ) | |
| components[f"height_{prefix}"] = gr.Number( | |
| label="高度", value=default_height, precision=0, interactive=True | |
| ) | |
| components[f"size_panel_{prefix}"] = size_panel | |
| with gr.Accordion("高级采样与运行参数", open=False): | |
| with gr.Row(): | |
| components[f"sampler_{prefix}"] = gr.Dropdown( | |
| choices=SAMPLER_CHOICES, | |
| value=default_values.get("sampler_name", "euler"), | |
| label="采样器", | |
| ) | |
| components[f"scheduler_{prefix}"] = gr.Dropdown( | |
| choices=SCHEDULER_CHOICES, | |
| value=default_values.get("scheduler", "simple"), | |
| label="调度器", | |
| ) | |
| with gr.Row(): | |
| components[f"steps_{prefix}"] = gr.Slider( | |
| 1, 100, default_values.get("steps", 8), step=1, label="采样步数" | |
| ) | |
| components[f"cfg_{prefix}"] = gr.Slider( | |
| 1.0, 20.0, default_values.get("cfg", 1.0), step=0.1, label="CFG" | |
| ) | |
| with gr.Row(): | |
| components[f"seed_{prefix}"] = gr.Number( | |
| label="随机种子(-1 为随机)", value=-1, precision=0 | |
| ) | |
| components[f"batch_size_{prefix}"] = gr.Slider( | |
| 1, | |
| CONFIG.max_batch_size, | |
| 1, | |
| step=1, | |
| label="单次生成数量", | |
| info="数量越大,显存与 ZeroGPU 配额消耗越高。", | |
| ) | |
| with gr.Row(): | |
| components[f"clip_skip_{prefix}"] = gr.Slider( | |
| 1, 2, 1, step=1, label="Clip Skip", visible=False | |
| ) | |
| components[f"guidance_{prefix}"] = gr.Slider( | |
| 1.0, 10.0, 3.5, step=0.1, label="FLUX Guidance", visible=False | |
| ) | |
| components[f"zero_gpu_{prefix}"] = gr.Dropdown( | |
| choices=[("自动估算", 0), ("60 秒", 60), ("90 秒", 90), ("120 秒", 120)], | |
| value=0, | |
| label="ZeroGPU 运行时长", | |
| info="通常保持自动;复杂高分辨率任务可手动提高。", | |
| ) | |
| with gr.Row(): | |
| components[f"auto_model_params_{prefix}"] = gr.Checkbox( | |
| value=True, | |
| label="切模型时自动应用推荐采样参数", | |
| info="关闭后会保留你手调的步数、CFG、采样器和调度器。", | |
| scale=3, | |
| ) | |
| components[f"reset_model_params_{prefix}"] = gr.Button( | |
| "恢复当前模型推荐值", size="sm", scale=1 | |
| ) | |
| with gr.Column(scale=5, min_width=340, elem_id="result-column"): | |
| with gr.Row(): | |
| components[f"run_{prefix}"] = gr.Button( | |
| "开始生成", variant="primary", size="lg", scale=3 | |
| ) | |
| components[f"cancel_{prefix}"] = gr.Button( | |
| "取消排队", variant="stop", size="lg", scale=1 | |
| ) | |
| components[f"result_{prefix}"] = gr.Gallery( | |
| label="生成结果", | |
| show_label=True, | |
| columns=2, | |
| object_fit="contain", | |
| height=720, | |
| ) | |
| components[f"run_summary_{prefix}"] = gr.Markdown( | |
| "普通模式会在这里显示执行结果;PK/批量模式会逐项列出成功与失败。" | |
| ) | |
| components[f"clear_result_{prefix}"] = gr.Button("清空结果", size="sm") | |
| gr.Markdown( | |
| "生成结果包含 Prompt、参数和完整 ComfyUI 工作流元数据,下载 PNG 后可继续复现。", | |
| elem_classes="result-note", | |
| ) | |
| gr.Markdown("## 当前模型可用的扩展能力", elem_classes="advanced-title") | |
| gr.Markdown("下方只显示当前模型兼容的能力;切换模型后会自动更新。") | |
| components.update(create_lora_settings_ui(prefix)) | |
| components.update(create_controlnet_ui(prefix)) | |
| components.update(create_anima_controlnet_lllite_ui(prefix)) | |
| components.update(create_diffsynth_controlnet_ui(prefix)) | |
| components.update(create_krea2_controlnet_ui(prefix)) | |
| components.update(create_ipadapter_ui(prefix)) | |
| components.update(create_flux1_ipadapter_ui(prefix)) | |
| components.update(create_sd3_ipadapter_ui(prefix)) | |
| components.update(create_embedding_ui(prefix)) | |
| components.update(create_style_ui(prefix)) | |
| components.update(create_conditioning_ui(prefix)) | |
| components.update(create_reference_latent_ui(prefix)) | |
| components.update(create_hidream_o1_reference_ui(prefix)) | |
| components.update(create_joyai_reference_ui(prefix)) | |
| components.update(create_krea2_identity_edit_ui(prefix)) | |
| components.update(create_krea2_reference_edit_ui(prefix)) | |
| components.update(create_qwen_image_edit_ui(prefix)) | |
| components.update(create_boogu_edit_ui(prefix)) | |
| components.update(create_reference_image_ui(prefix)) | |
| components.update(create_vae_override_ui(prefix)) | |
| components.update(create_pid_ui(prefix)) | |
| return components | |