Spaces:
Running on Zero
Running on Zero
alx lord commited on
Sync with upstream ImageGen + keep custom models and cloud saving
Browse files- README.md +17 -1
- chain_injectors/controlnet_injector.py +13 -8
- chain_injectors/flux3_prompt_upsampler_injector.py +304 -0
- chain_injectors/moss_image_control_injector.py +162 -0
- chain_injectors/moss_image_edit_injector.py +228 -0
- chain_injectors/pid_injector.py +3 -3
- chain_injectors/reference_image_injector.py +1 -1
- comfy_integration/setup.py +32 -6
- core/pipelines/pipeline_input_processor.py +79 -0
- core/pipelines/sd_image_pipeline.py +293 -287
- core/pipelines/workflow_recipes/_partials/conditioning/fibo-2.0.yaml +77 -0
- core/pipelines/workflow_recipes/_partials/conditioning/flux3.yaml +104 -0
- core/pipelines/workflow_recipes/_partials/conditioning/moss-image.yaml +97 -0
- core/pipelines/workflow_recipes/_partials/conditioning/qwen-image-2.1.yaml +1 -1
- core/settings.py +2 -1
- mcp_tools/common.py +113 -0
- requirements.txt +9 -8
- ui/events/chain_handlers.py +36 -1
- ui/events/change_handlers.py +8 -2
- ui/events/main.py +24 -3
- ui/events/run_handlers.py +22 -1
- ui/imagegen_ui.py +7 -2
- ui/layout.py +43 -42
- ui/shared/ui_components.py +947 -838
- utils/app_utils.py +1 -1
- yaml/chain_features.yaml +93 -0
- yaml/constants.yaml +27 -0
- yaml/file_list.yaml +121 -50
- yaml/model_architecture_features.yaml +20 -0
- yaml/model_architectures.yaml +9 -0
- yaml/model_defaults.yaml +78 -2
- yaml/model_list.yaml +74 -34
- yaml/pid.yaml +1 -1
- yaml/task_features.yaml +15 -0
README.md
CHANGED
|
@@ -24,6 +24,7 @@ models:
|
|
| 24 |
- black-forest-labs/FLUX.2-klein-9b-kv-fp8
|
| 25 |
- black-forest-labs/FLUX.2-klein-base-4b-nvfp4
|
| 26 |
- black-forest-labs/FLUX.2-klein-base-9b-nvfp4
|
|
|
|
| 27 |
- bluepen5805/4nima_pencil-XL
|
| 28 |
- bluepen5805/anima-models
|
| 29 |
- bluepen5805/anima_pencil-XL
|
|
@@ -43,6 +44,7 @@ models:
|
|
| 43 |
- Comfy-Org/Anima-LLLite
|
| 44 |
- Comfy-Org/Boogu-Image
|
| 45 |
- Comfy-Org/ERNIE-Image
|
|
|
|
| 46 |
- Comfy-Org/FLUX.1-Krea-dev_ComfyUI
|
| 47 |
- Comfy-Org/flux2-dev
|
| 48 |
- Comfy-Org/HiDream-I1_ComfyUI
|
|
@@ -55,6 +57,7 @@ models:
|
|
| 55 |
- Comfy-Org/Lumina_Image_2.0_Repackaged
|
| 56 |
- Comfy-Org/Mage-Flow
|
| 57 |
- Comfy-Org/Ming-Image
|
|
|
|
| 58 |
- Comfy-Org/NewBie-image-Exp0.1_repackaged
|
| 59 |
- Comfy-Org/Omnigen2_ComfyUI_repackaged
|
| 60 |
- Comfy-Org/Ovis-Image
|
|
@@ -100,7 +103,6 @@ models:
|
|
| 100 |
- jdopensource/JoyAI-Image-Edit-Plus-ComfyUI
|
| 101 |
- kandinskylab/Kandinsky-5.0-T2I-Lite
|
| 102 |
- Kijai/flux-fp8
|
| 103 |
-
- Kijai/QwenImage_experimental
|
| 104 |
- Laxhar/noob_openpose
|
| 105 |
- Laxhar/noobai-XL-1.1
|
| 106 |
- Laxhar/noobai-XL-Vpred-1.0
|
|
@@ -113,6 +115,10 @@ models:
|
|
| 113 |
- MIC-Lab/illustriousXLv1.1_controlnet
|
| 114 |
- misri/hassakuXLIllustrious_v30
|
| 115 |
- motimalu/kirazuri-anima
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
- nvidia/Cosmos-Predict2-2B-Text2Image
|
| 117 |
- nvidia/Cosmos-Predict2-14B-Text2Image
|
| 118 |
- OnomaAIResearch/Illustrious-XL-v1.0
|
|
@@ -149,10 +155,17 @@ models:
|
|
| 149 |
- black-forest-labs/FLUX.1-dev
|
| 150 |
- black-forest-labs/FLUX.1-Krea-dev
|
| 151 |
- black-forest-labs/FLUX.1-schnell
|
|
|
|
|
|
|
|
|
|
| 152 |
- Boogu/Boogu-Image-0.1-Turbo
|
| 153 |
- Boogu/Boogu-Image-0.1-Base
|
| 154 |
- Boogu/Boogu-Image-0.1-Edit
|
| 155 |
- Boogu/Boogu-Image-0.1-Edit-Turbo
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
- HiDream-ai/HiDream-I1-Dev
|
| 157 |
- HiDream-ai/HiDream-I1-Fast
|
| 158 |
- HiDream-ai/HiDream-I1-Full
|
|
@@ -178,10 +191,13 @@ models:
|
|
| 178 |
- nvidia/PiD
|
| 179 |
- nvidia/PixelDiT-1300M-1024px
|
| 180 |
- OmniGen2/OmniGen2
|
|
|
|
|
|
|
| 181 |
- Qwen/Qwen3.8-27B
|
| 182 |
- Qwen/Qwen-Image
|
| 183 |
- Qwen/Qwen-Image-2512
|
| 184 |
- Qwen/Qwen-Image-2.1
|
|
|
|
| 185 |
- Qwen/Qwen-Image-2.1-PE-I2I
|
| 186 |
- Qwen/Qwen-Image-2.1-PE-T2I
|
| 187 |
- Qwen/Qwen-Image-Edit
|
|
|
|
| 24 |
- black-forest-labs/FLUX.2-klein-9b-kv-fp8
|
| 25 |
- black-forest-labs/FLUX.2-klein-base-4b-nvfp4
|
| 26 |
- black-forest-labs/FLUX.2-klein-base-9b-nvfp4
|
| 27 |
+
- black-forest-labs/flux-3-image-klein-comfy
|
| 28 |
- bluepen5805/4nima_pencil-XL
|
| 29 |
- bluepen5805/anima-models
|
| 30 |
- bluepen5805/anima_pencil-XL
|
|
|
|
| 44 |
- Comfy-Org/Anima-LLLite
|
| 45 |
- Comfy-Org/Boogu-Image
|
| 46 |
- Comfy-Org/ERNIE-Image
|
| 47 |
+
- Comfy-Org/Fibo-2.0
|
| 48 |
- Comfy-Org/FLUX.1-Krea-dev_ComfyUI
|
| 49 |
- Comfy-Org/flux2-dev
|
| 50 |
- Comfy-Org/HiDream-I1_ComfyUI
|
|
|
|
| 57 |
- Comfy-Org/Lumina_Image_2.0_Repackaged
|
| 58 |
- Comfy-Org/Mage-Flow
|
| 59 |
- Comfy-Org/Ming-Image
|
| 60 |
+
- Comfy-Org/MOSS-Image
|
| 61 |
- Comfy-Org/NewBie-image-Exp0.1_repackaged
|
| 62 |
- Comfy-Org/Omnigen2_ComfyUI_repackaged
|
| 63 |
- Comfy-Org/Ovis-Image
|
|
|
|
| 103 |
- jdopensource/JoyAI-Image-Edit-Plus-ComfyUI
|
| 104 |
- kandinskylab/Kandinsky-5.0-T2I-Lite
|
| 105 |
- Kijai/flux-fp8
|
|
|
|
| 106 |
- Laxhar/noob_openpose
|
| 107 |
- Laxhar/noobai-XL-1.1
|
| 108 |
- Laxhar/noobai-XL-Vpred-1.0
|
|
|
|
| 115 |
- MIC-Lab/illustriousXLv1.1_controlnet
|
| 116 |
- misri/hassakuXLIllustrious_v30
|
| 117 |
- motimalu/kirazuri-anima
|
| 118 |
+
- Noctaluna/Anima-Noct-Anime-Mix
|
| 119 |
+
- Noctaluna/Krea2-Noct-Uncensored
|
| 120 |
+
- Noctaluna/Noct-Q-Anime-Uncensored-Qwen-Image-2.1
|
| 121 |
+
- Noctaluna/Noct-Q-Uncensored-Qwen-Image-2.1
|
| 122 |
- nvidia/Cosmos-Predict2-2B-Text2Image
|
| 123 |
- nvidia/Cosmos-Predict2-14B-Text2Image
|
| 124 |
- OnomaAIResearch/Illustrious-XL-v1.0
|
|
|
|
| 155 |
- black-forest-labs/FLUX.1-dev
|
| 156 |
- black-forest-labs/FLUX.1-Krea-dev
|
| 157 |
- black-forest-labs/FLUX.1-schnell
|
| 158 |
+
- black-forest-labs/flux-3-image-klein
|
| 159 |
+
- black-forest-labs/flux-3-image-klein-base
|
| 160 |
+
- black-forest-labs/flux-3-image-klein-pup
|
| 161 |
- Boogu/Boogu-Image-0.1-Turbo
|
| 162 |
- Boogu/Boogu-Image-0.1-Base
|
| 163 |
- Boogu/Boogu-Image-0.1-Edit
|
| 164 |
- Boogu/Boogu-Image-0.1-Edit-Turbo
|
| 165 |
+
- briaai/Fibo-2.0-dev
|
| 166 |
+
- briaai/Fibo-2.0-edit-turbo
|
| 167 |
+
- chinoll/Moss-Image-Edit
|
| 168 |
+
- darask0/MOSS-Image-color-depth-control
|
| 169 |
- HiDream-ai/HiDream-I1-Dev
|
| 170 |
- HiDream-ai/HiDream-I1-Fast
|
| 171 |
- HiDream-ai/HiDream-I1-Full
|
|
|
|
| 191 |
- nvidia/PiD
|
| 192 |
- nvidia/PixelDiT-1300M-1024px
|
| 193 |
- OmniGen2/OmniGen2
|
| 194 |
+
- OpenMOSS-Team/MOSS-Image-Base
|
| 195 |
+
- OpenMOSS-Team/MOSS-Image-SFT
|
| 196 |
- Qwen/Qwen3.8-27B
|
| 197 |
- Qwen/Qwen-Image
|
| 198 |
- Qwen/Qwen-Image-2512
|
| 199 |
- Qwen/Qwen-Image-2.1
|
| 200 |
+
- Qwen/Qwen-Image-2.1-Turbo
|
| 201 |
- Qwen/Qwen-Image-2.1-PE-I2I
|
| 202 |
- Qwen/Qwen-Image-2.1-PE-T2I
|
| 203 |
- Qwen/Qwen-Image-Edit
|
chain_injectors/controlnet_injector.py
CHANGED
|
@@ -28,10 +28,10 @@ def inject(assembler, chain_definition, chain_items):
|
|
| 28 |
default_megapixels = 1.0
|
| 29 |
megapixels = float(chain_definition.get('megapixels', default_megapixels))
|
| 30 |
|
| 31 |
-
# 1. Qwen-Image-2.1
|
| 32 |
-
if template_name
|
| 33 |
if 'model' not in assembler.workflow[ksampler_id]['inputs']:
|
| 34 |
-
print(f"Warning: KSampler node '{ksampler_name}' is missing 'model' input. Skipping
|
| 35 |
return
|
| 36 |
|
| 37 |
current_model_connection = assembler.workflow[ksampler_id]['inputs']['model']
|
|
@@ -57,19 +57,24 @@ def inject(assembler, chain_definition, chain_items):
|
|
| 57 |
apply_cn_id = assembler._get_unique_id()
|
| 58 |
apply_cn_node = assembler._get_node_template(template_name)
|
| 59 |
apply_cn_node['inputs']['strength'] = item_data.get('strength', 1.0)
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
| 62 |
apply_cn_node['inputs']['model'] = current_model_connection
|
| 63 |
apply_cn_node['inputs']['model_patch'] = [patch_loader_id, 0]
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
| 66 |
apply_cn_node['inputs']['vae'] = vae_connection
|
| 67 |
|
| 68 |
assembler.workflow[apply_cn_id] = apply_cn_node
|
| 69 |
current_model_connection = [apply_cn_id, 0]
|
| 70 |
|
| 71 |
assembler.workflow[ksampler_id]['inputs']['model'] = current_model_connection
|
| 72 |
-
print(f"
|
| 73 |
return
|
| 74 |
|
| 75 |
# 2. Anima LLLite ControlNet
|
|
|
|
| 28 |
default_megapixels = 1.0
|
| 29 |
megapixels = float(chain_definition.get('megapixels', default_megapixels))
|
| 30 |
|
| 31 |
+
# 1. Fun ControlNet (Qwen-Image-2.1)
|
| 32 |
+
if template_name in ("ZImageFunControlnet", "QwenImage21FunControlNetApply"):
|
| 33 |
if 'model' not in assembler.workflow[ksampler_id]['inputs']:
|
| 34 |
+
print(f"Warning: KSampler node '{ksampler_name}' is missing 'model' input. Skipping Fun ControlNet.")
|
| 35 |
return
|
| 36 |
|
| 37 |
current_model_connection = assembler.workflow[ksampler_id]['inputs']['model']
|
|
|
|
| 57 |
apply_cn_id = assembler._get_unique_id()
|
| 58 |
apply_cn_node = assembler._get_node_template(template_name)
|
| 59 |
apply_cn_node['inputs']['strength'] = item_data.get('strength', 1.0)
|
| 60 |
+
if 'start_percent' in apply_cn_node.get('inputs', {}):
|
| 61 |
+
apply_cn_node['inputs']['start_percent'] = item_data.get('start_percent', 0.0)
|
| 62 |
+
if 'end_percent' in apply_cn_node.get('inputs', {}):
|
| 63 |
+
apply_cn_node['inputs']['end_percent'] = item_data.get('end_percent', 1.0)
|
| 64 |
apply_cn_node['inputs']['model'] = current_model_connection
|
| 65 |
apply_cn_node['inputs']['model_patch'] = [patch_loader_id, 0]
|
| 66 |
+
if 'image' in apply_cn_node.get('inputs', {}):
|
| 67 |
+
apply_cn_node['inputs']['image'] = [image_scaler_id, 0]
|
| 68 |
+
elif 'control_image' in apply_cn_node.get('inputs', {}):
|
| 69 |
+
apply_cn_node['inputs']['control_image'] = [image_scaler_id, 0]
|
| 70 |
+
if vae_connection and 'vae' in apply_cn_node.get('inputs', {}):
|
| 71 |
apply_cn_node['inputs']['vae'] = vae_connection
|
| 72 |
|
| 73 |
assembler.workflow[apply_cn_id] = apply_cn_node
|
| 74 |
current_model_connection = [apply_cn_id, 0]
|
| 75 |
|
| 76 |
assembler.workflow[ksampler_id]['inputs']['model'] = current_model_connection
|
| 77 |
+
print(f"Fun ControlNet injector applied. KSampler model input redirected through {len(chain_items)} node(s).")
|
| 78 |
return
|
| 79 |
|
| 80 |
# 2. Anima LLLite ControlNet
|
chain_injectors/flux3_prompt_upsampler_injector.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FLUX.3 Prompt Upsampler Chain Injector.
|
| 2 |
+
|
| 3 |
+
This injector integrates the FLUX.3 prompt upsampler model into the generation workflow:
|
| 4 |
+
- Uses qwen3.5_9b_flux3_prompt_upsampler_w4a8.safetensors (type: flux3).
|
| 5 |
+
- Uses PrimitiveStringMultiline for system prompt and input prompt.
|
| 6 |
+
- Expands user prompt into detailed generative captions using TextGenerate.
|
| 7 |
+
- Supports reasoning mode (thinking=True) via native ComfyUI TextGenerate reasoning separation.
|
| 8 |
+
- Supports single or multi-reference image inputs: multiple reference images are stitched
|
| 9 |
+
into a structured grid layout via ComfyUI native ImageStitch nodes and scaled via ImageScaleToTotalPixels,
|
| 10 |
+
then connected to the TextGenerate 'image' input.
|
| 11 |
+
- Connects enhanced prompt directly to downstream CLIPTextEncode positive prompt.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
from utils.app_utils import ensure_file_downloaded
|
| 16 |
+
except ImportError:
|
| 17 |
+
ensure_file_downloaded = None
|
| 18 |
+
|
| 19 |
+
SYSTEM_PROMPTS = {
|
| 20 |
+
"t2i": (
|
| 21 |
+
"You are the prompt upsampler of a text-to-image model. Expand the user's short request into ONE detailed, self-contained caption: the exact text the image model receives. Output only the caption, with no greetings, explanations, refusals, questions, Markdown, headings, JSON, coordinates, ids or angle-bracket tokens, and no quotes around it. If the input is not clearly an image request, caption a visualization of its concept. The request may begin with a \"TARGET CANVAS: W:H\" line: compose for that canvas (wide spreads content sideways, tall stacks it) and never mention the ratio or the canvas.\n\n"
|
| 22 |
+
"Fidelity and planning: keep every explicit subject, attribute, count, spatial constraint, style cue and piece of wording; never contradict, substitute, drop or narrow the request, and keep an abstract concept abstract. Decide everything left open as ONE concrete, consistent plan; never offer alternatives or hedge. Never re-express a stated constraint through another mechanism; add camera optics (depth of field, bokeh, blur) only when asked for or implied. Without a stated style, plan an actual photograph and open with the medium convention (\"Full-color outdoor photograph, ...\", \"Full-color indoor photograph, ...\", \"Studio photograph, ...\", \"Close-up photograph, ...\"); keep \"Photorealistic\" for renders, and turn \"photorealistic\" or \"realistic\" requests for photos into the photograph convention. Give a photograph a few ordinary, non-ideal specifics drawn from the scene's own objects instead of the stock-photo ideal. Do not invent names, brands, logos or celebrities; keep user-named ones. A long request keeps its content but not its wording: drop appraisal and sales vocabulary, turn moods into observable physical facts, and add only new concrete content with positions. Time of day is lighting, not rendered text.\n\n"
|
| 23 |
+
"Caption format: one continuous prose block in the generative voice, never \"this image shows\", \"we can see\" or \"is visible\". Lead with medium and style, then scene, lighting and camera, then the subjects with concrete colors, materials and state. Every person in focus gets an observable profile: ethnicity, gender, age range, skin tone (\"fair skin\", \"brown skin\"), hair and clothing. Make the layout reconstructable from words: frame-relative positions (frame-left, frame-right, upper, lower, foreground, midground, background), sentence-initial anchors (\"On the left, ...\", \"In the background, ...\"), relations (holds, faces, behind) and a consistent order; describe up to about ten instances one by one, beyond that give the exact count and arrangement. Out-of-focus or distant planes are shapes, colors and silhouettes, never identities or readable text, and the background takes about a sixth of the caption. Be definite: no hedging adverbs, no quality or emotion judgments, no closing sentence about mood; state negations as what is present. Rendered text: transcribe every wording verbatim inside double quotes in its original language and script, keep the line breaks of multi-line text, and give its typography, color and placement; author concrete, domain-correct wording when the request needs text it does not supply, and name canonical structures (calendar, keyboard, periodic table) exactly with their full layout. All prose is English; only quoted text keeps its language. Typical length is 100 to 350 words, more for complex scenes.\n\n"
|
| 24 |
+
"Output only the caption."
|
| 25 |
+
),
|
| 26 |
+
"i2i": (
|
| 27 |
+
"You are the prompt upsampler of an image-editing model. You see the reference image(s), attached in order (the first image, the second image, ...), and the user's short request. Expand it into ONE detailed, self-contained instruction: the caption the editing model receives together with the same images. Output only the instruction as one continuous English prose block, with no greetings, explanations, Markdown, JSON, coordinates, ids or angle-bracket tokens. Read a vague or conversational request as the most plausible edit of the supplied images.\n\n"
|
| 28 |
+
"Pick the voice by task shape. When one reference stays and gets bounded changes, write an imperative editing instruction on it (\"Remove the ...\", \"Change the ... to ...\", \"Zoom in on ...\"); a whole-image transformation opens with its scope (\"Convert the entire image into ...\", \"Apply a ... filter over the entire scene\", \"Horizontally flip the entire image so that ...\", \"Shift the camera viewpoint to ...\", then where the major elements should now be). When the references are drawn into a newly composed frame (combining subjects, a new scene, a garment, object or style donor), write a generation request (\"Create a photograph of ...\", \"Create an anime-style illustration of ...\", \"Generate a new image featuring ...\") that says what to take from each image, by ordinal, and what is newly generated.\n\n"
|
| 29 |
+
"Keep every explicit edit, constraint, count, position, style cue and piece of wording, and decide what is left open as one concrete plan. Ground everything in what is actually visible: identify each referent unambiguously by its real appearance, never mention content that is not there, and use every image the user attached. State every end state concretely (which color, how large, which pose, which material) and give new content enough appearance to be generated. Cover the knock-on effects of the change (shadows, reflections, occlusion, perspective, scale, lighting) and say what fills a removed region. A zoom-in names what stays in frame and which neighbors are cropped out; an outpainting continues the scene in the same medium. Describe the change, not the whole scene, and close an editing instruction with one compact clause naming what stays unchanged; transferred people keep their identity, face and accessories unless asked otherwise. Transcribe on-image text verbatim in its original language inside double quotes, with line breaks as \\n, and match the original font, size and color when replacing it. Name recognizable public figures, brands and landmarks you can see. Directions are frame-relative; use no evaluative or quality words. Typical length is 50 to 150 words.\n\n"
|
| 30 |
+
"Output only the instruction."
|
| 31 |
+
),
|
| 32 |
+
"t2i_bbox": (
|
| 33 |
+
"You are the prompt upsampler of a text-to-image model. Plan the full image the user's short request implies and output ONE structured caption on a single line: the global caption paragraph, one space, then ONE JSON array, the element table that places every cited element with a bounding box in the image to be generated. Output nothing else (no Markdown fences, commentary or refusals); visualize the concept of a non-image input. The request may begin with a \"TARGET CANVAS: W:H\" line: compose for that canvas and never mention it. Boxes live on a 0-1000 grid stretched onto the canvas: an element of physical width-to-height ratio a gets an x-span of about y-span x a x H/W.\n\n"
|
| 34 |
+
"Planning: keep every explicit subject, attribute, count, spatial constraint, style cue and piece of wording, and decide everything left open as ONE concrete composition that the caption and the table both describe. Without a stated style plan an actual photograph and open with the medium convention (\"Full-color outdoor photograph, ...\", \"Studio photograph, ...\"); add camera optics only when asked for; do not invent names, brands, logos or celebrities; drop appraisal and sales vocabulary.\n\nThe table: one row per distinct object, person, animal, building or salient region the caption mentions and one per coherent text block; no rows for style, lighting or atmosphere. Structured layouts (tables, grids, calendars, keyboards, charts, maps, posters, pages) decompose into their regions, each row carrying that region's complete verbatim content; more than about ten interchangeable items become one collection row giving the exact count and arrangement, while items whose positions matter keep a row each. Row keys, in order: \"id\", \"bbox\", \"desc\". id: the bare class noun plus an index numbered left to right, then top to bottom, from 1 (person_1, dog_2); a text block is its two-letter language code plus _Text_ plus an index (En_Text_1); one entity is one row and ids are unique. bbox: [y_min, x_min, y_max, x_max] as integers from 0 to 1000, top-left origin, clamped at the borders, with plausible relative sizes and a small margin around text. desc: one self-contained paragraph in the generative voice that opens with the element's identity (never \"the X\"): shape, material, colors, state and markings, but no position. People in focus get the richest desc: ethnicity, gender, age range, skin tone, build, hair, face, each garment, accessories, pose and expression. A text row carries its complete wording verbatim in double quotes, with line breaks as \\n, plus its typography.\n\nThe global caption: one flowing paragraph in the generative voice (medium first, then scene, lighting, camera and frame-relative layout; no quality judgments) that names each table element at identity level at its first mention and cites it with its id in its own angle brackets: \"a wicker table <table_1> holding a tiered stand <stand_1>\". Cite every table id and no other; appearance detail and text content live in the descs; no coordinates. Text-dense documents are laid out region by region with every visible text transcribed verbatim at a readable size (cut content rather than shrink type), and a grid quotes every visible cell. All prose is English; only quoted text keeps its language.\n\nOutput only the caption and the table."
|
| 35 |
+
),
|
| 36 |
+
"i2i_bbox": (
|
| 37 |
+
"You are the annotation system an image-editing model was trained on. You see the source image(s), attached in order as ref_image_0, ref_image_1, ..., and the user's editing instruction; the edited target image does not exist yet. Determine the edit precisely, imagine the target consistently with the source, and write the record its annotator would have written, on one line: the instruction paragraph, one space, then ONE JSON array. Output nothing else.\n\nThe paragraph: English, instruction voice (\"Change the ...\", \"Remove the ...\", or \"Create a photograph of ...\" when the references are drawn into a new frame), never observer phrasing and never a comparison with the target. Cite every involved object, person or text block at its first mention with its table id in its own angle brackets (\"remove the delivery van <van_1>\"), every table id exactly once and no other id; cite a reference image by its token when content is taken from it (\"the cup <cup_1> from <ref_image_1>\"). End states (new color, new text in quotes, pose, degree) live in the paragraph, static appearance in the descs; no coordinates. An editing instruction closes with one compact clause that everything else stays identical to the source.\n\nThe table, rows {\"id\", \"from\", \"src_bbox\", \"tgt_bbox\", \"desc\"}: one row per edited, removed, added, moved or anchoring item, plus the scene or background when it is itself changed or preserved; no rows for untouched scenery. id: a lowercase class noun plus an index (person_1, cup_2) numbered left to right, then top to bottom; text blocks are En_Text_1 and so on; ids are unique. from: \"ref_image_N\" for an item visible in reference N, \"target\" for newly created content. src_bbox is the item's box in its from-image and null for target rows; tgt_bbox is its box in the imagined result and null for removed items; moved or resized items carry both; new items are sized like similar objects in the frame. Boxes are [y_min, x_min, y_max, x_max] integers from 0 to 1000, top-left origin, tight around objects, with 5 to 15 units of margin around text. A row may also carry src_bbox_1stage and tgt_bbox_1stage, a first-pass estimate of the same boxes. desc: one English paragraph of the item's static appearance in its from-image (for target rows, its decided appearance), opening with its identity; people get the richest descs (ethnicity, gender, age range, skin tone, hair, face, garments, accessories, pose, expression); text rows quote their text verbatim.\n\nState only decided facts: visible in the source, demanded by the instruction, or a concrete default needed to realize it. Settle whether the edit targets one instance, all of a class or several; land every magnitude on a concrete value; include visible knock-on effects; added objects are new objects at real-world size; a zoom-in names the neighbors it excludes; an outpainting keeps the source whole and continues it in the same medium. Everything the instruction does not involve stays identical to the source.\n\nOutput only the paragraph and the table."
|
| 38 |
+
)
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
DEFAULT_FLUX3_SYSTEM_PROMPT = SYSTEM_PROMPTS["t2i"]
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def create_node(assembler, class_type, title):
|
| 45 |
+
"""Create a workflow node dictionary with fallback template generation."""
|
| 46 |
+
try:
|
| 47 |
+
node = assembler._get_node_template(class_type)
|
| 48 |
+
except Exception:
|
| 49 |
+
node = {
|
| 50 |
+
"inputs": {},
|
| 51 |
+
"class_type": class_type,
|
| 52 |
+
"_meta": {"title": title}
|
| 53 |
+
}
|
| 54 |
+
if "_meta" not in node:
|
| 55 |
+
node["_meta"] = {}
|
| 56 |
+
node['_meta']['title'] = title
|
| 57 |
+
return node
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def inject(assembler, chain_definition, chain_items):
|
| 61 |
+
"""Inject FLUX.3 prompt upsampler nodes into the target generation workflow."""
|
| 62 |
+
if not chain_items:
|
| 63 |
+
return
|
| 64 |
+
|
| 65 |
+
is_enabled = False
|
| 66 |
+
thinking_enabled = False
|
| 67 |
+
bbox_mode = False
|
| 68 |
+
max_length = 3584
|
| 69 |
+
custom_system_prompt = None
|
| 70 |
+
|
| 71 |
+
for item in chain_items:
|
| 72 |
+
if isinstance(item, dict):
|
| 73 |
+
if item.get("enable", False) or item.get("enabled", False) or item.get("value", False):
|
| 74 |
+
is_enabled = True
|
| 75 |
+
if item.get("thinking", False) in (True, "true", "on", "yes", "1", 1):
|
| 76 |
+
thinking_enabled = True
|
| 77 |
+
if item.get("bbox_mode", False) in (True, "true", "on", "yes", "1", 1):
|
| 78 |
+
bbox_mode = True
|
| 79 |
+
if "max_length" in item and item["max_length"]:
|
| 80 |
+
try:
|
| 81 |
+
max_length = int(item["max_length"])
|
| 82 |
+
except (ValueError, TypeError):
|
| 83 |
+
pass
|
| 84 |
+
if "system_prompt" in item and item["system_prompt"]:
|
| 85 |
+
custom_system_prompt = str(item["system_prompt"])
|
| 86 |
+
elif item is True or str(item).lower() in ("on", "true", "yes", "1"):
|
| 87 |
+
is_enabled = True
|
| 88 |
+
break
|
| 89 |
+
|
| 90 |
+
if not is_enabled:
|
| 91 |
+
return
|
| 92 |
+
|
| 93 |
+
target_node_name = chain_definition.get('target_node', 'pos_prompt')
|
| 94 |
+
target_node_id = assembler.node_map.get(target_node_name)
|
| 95 |
+
|
| 96 |
+
if not target_node_id or target_node_id not in assembler.workflow:
|
| 97 |
+
for node_id, node in assembler.workflow.items():
|
| 98 |
+
if isinstance(node, dict) and node.get('class_type') == 'CLIPTextEncode':
|
| 99 |
+
title = node.get('_meta', {}).get('title', '').lower()
|
| 100 |
+
if 'pos' in title or 'positive' in title:
|
| 101 |
+
target_node_id = node_id
|
| 102 |
+
break
|
| 103 |
+
|
| 104 |
+
if not target_node_id or target_node_id not in assembler.workflow:
|
| 105 |
+
for node_id, node in assembler.workflow.items():
|
| 106 |
+
if isinstance(node, dict) and node.get('class_type') in ('CLIPTextEncode', 'TextEncodeQwenImage21', 'TextEncodeMingImageEdit'):
|
| 107 |
+
target_node_id = node_id
|
| 108 |
+
break
|
| 109 |
+
|
| 110 |
+
if not target_node_id or target_node_id not in assembler.workflow:
|
| 111 |
+
print(f"Warning: Target node '{target_node_name}' not found for FLUX.3 Prompt Upsampler. Skipping.")
|
| 112 |
+
return
|
| 113 |
+
|
| 114 |
+
target_node = assembler.workflow[target_node_id]
|
| 115 |
+
original_prompt = target_node.get('inputs', {}).get('text')
|
| 116 |
+
if original_prompt is None:
|
| 117 |
+
original_prompt = target_node.get('inputs', {}).get('prompt', '')
|
| 118 |
+
|
| 119 |
+
# 1. Collect image connections for multi-image reference support
|
| 120 |
+
load_image_nodes = [
|
| 121 |
+
node_id for node_id, node in assembler.workflow.items()
|
| 122 |
+
if isinstance(node, dict) and node.get('class_type') == 'LoadImage'
|
| 123 |
+
]
|
| 124 |
+
has_load_image = len(load_image_nodes) > 0
|
| 125 |
+
target_image_conn = None
|
| 126 |
+
|
| 127 |
+
# Auto-select official BFL system prompt template:
|
| 128 |
+
# If loadimage is detected: i2i / i2i_bbox; otherwise t2i / t2i_bbox
|
| 129 |
+
if custom_system_prompt:
|
| 130 |
+
system_prompt = custom_system_prompt
|
| 131 |
+
else:
|
| 132 |
+
if has_load_image:
|
| 133 |
+
template_key = "i2i_bbox" if bbox_mode else "i2i"
|
| 134 |
+
else:
|
| 135 |
+
template_key = "t2i_bbox" if bbox_mode else "t2i"
|
| 136 |
+
system_prompt = SYSTEM_PROMPTS.get(template_key, DEFAULT_FLUX3_SYSTEM_PROMPT)
|
| 137 |
+
|
| 138 |
+
if has_load_image:
|
| 139 |
+
indexed_slots = []
|
| 140 |
+
target_inputs = target_node.get('inputs', {})
|
| 141 |
+
for slot_key, slot_val in target_inputs.items():
|
| 142 |
+
if slot_key.startswith('images.image_') and isinstance(slot_val, (list, tuple)):
|
| 143 |
+
try:
|
| 144 |
+
idx = int(slot_key.split('_')[-1])
|
| 145 |
+
except Exception:
|
| 146 |
+
idx = 999
|
| 147 |
+
indexed_slots.append((idx, slot_val))
|
| 148 |
+
|
| 149 |
+
indexed_slots.sort(key=lambda x: x[0])
|
| 150 |
+
ref_image_conns = [s[1] for s in indexed_slots]
|
| 151 |
+
|
| 152 |
+
if not ref_image_conns:
|
| 153 |
+
for lid in load_image_nodes:
|
| 154 |
+
scaled_conn = None
|
| 155 |
+
for nid, node in assembler.workflow.items():
|
| 156 |
+
if isinstance(node, dict) and node.get('class_type') == 'ImageScaleToTotalPixels':
|
| 157 |
+
img_input = node.get('inputs', {}).get('image')
|
| 158 |
+
if isinstance(img_input, (list, tuple)) and str(img_input[0]) == str(lid):
|
| 159 |
+
scaled_conn = [nid, 0]
|
| 160 |
+
break
|
| 161 |
+
if scaled_conn:
|
| 162 |
+
ref_image_conns.append(scaled_conn)
|
| 163 |
+
else:
|
| 164 |
+
scale_id = assembler._get_unique_id()
|
| 165 |
+
scale_node = create_node(assembler, "ImageScaleToTotalPixels", "Scale Input Image")
|
| 166 |
+
scale_node['inputs'] = {
|
| 167 |
+
"upscale_method": "nearest-exact",
|
| 168 |
+
"megapixels": 1,
|
| 169 |
+
"resolution_steps": 1,
|
| 170 |
+
"image": [lid, 0]
|
| 171 |
+
}
|
| 172 |
+
assembler.workflow[scale_id] = scale_node
|
| 173 |
+
ref_image_conns.append([scale_id, 0])
|
| 174 |
+
|
| 175 |
+
if len(ref_image_conns) == 1:
|
| 176 |
+
target_image_conn = ref_image_conns[0]
|
| 177 |
+
elif len(ref_image_conns) >= 2:
|
| 178 |
+
num_cols = 2 if len(ref_image_conns) <= 4 else 3
|
| 179 |
+
rows = []
|
| 180 |
+
for i in range(0, len(ref_image_conns), num_cols):
|
| 181 |
+
rows.append(ref_image_conns[i:i + num_cols])
|
| 182 |
+
|
| 183 |
+
stitched_rows = []
|
| 184 |
+
for r_idx, row_imgs in enumerate(rows):
|
| 185 |
+
if len(row_imgs) == 1:
|
| 186 |
+
stitched_rows.append(row_imgs[0])
|
| 187 |
+
else:
|
| 188 |
+
curr_stitch = row_imgs[0]
|
| 189 |
+
for c_idx in range(1, len(row_imgs)):
|
| 190 |
+
st_id = assembler._get_unique_id()
|
| 191 |
+
st_node = create_node(assembler, "ImageStitch", f"Stitch Images (Row {r_idx+1}-{c_idx})")
|
| 192 |
+
st_node['inputs'] = {
|
| 193 |
+
"direction": "right",
|
| 194 |
+
"match_image_size": True,
|
| 195 |
+
"spacing_width": 0,
|
| 196 |
+
"spacing_color": "white",
|
| 197 |
+
"image1": curr_stitch,
|
| 198 |
+
"image2": row_imgs[c_idx]
|
| 199 |
+
}
|
| 200 |
+
assembler.workflow[st_id] = st_node
|
| 201 |
+
curr_stitch = [st_id, 0]
|
| 202 |
+
stitched_rows.append(curr_stitch)
|
| 203 |
+
|
| 204 |
+
if len(stitched_rows) == 1:
|
| 205 |
+
final_stitched = stitched_rows[0]
|
| 206 |
+
else:
|
| 207 |
+
curr_vertical = stitched_rows[0]
|
| 208 |
+
for r_idx in range(1, len(stitched_rows)):
|
| 209 |
+
st_id = assembler._get_unique_id()
|
| 210 |
+
st_node = create_node(assembler, "ImageStitch", f"Stitch Images (Vertical {r_idx})")
|
| 211 |
+
st_node['inputs'] = {
|
| 212 |
+
"direction": "down",
|
| 213 |
+
"match_image_size": True,
|
| 214 |
+
"spacing_width": 0,
|
| 215 |
+
"spacing_color": "white",
|
| 216 |
+
"image1": curr_vertical,
|
| 217 |
+
"image2": stitched_rows[r_idx]
|
| 218 |
+
}
|
| 219 |
+
assembler.workflow[st_id] = st_node
|
| 220 |
+
curr_vertical = [st_id, 0]
|
| 221 |
+
final_stitched = curr_vertical
|
| 222 |
+
|
| 223 |
+
final_scale_id = assembler._get_unique_id()
|
| 224 |
+
final_scale_node = create_node(assembler, "ImageScaleToTotalPixels", "Scale Stitched Image")
|
| 225 |
+
final_scale_node['inputs'] = {
|
| 226 |
+
"upscale_method": "nearest-exact",
|
| 227 |
+
"megapixels": 1,
|
| 228 |
+
"resolution_steps": 1,
|
| 229 |
+
"image": final_stitched
|
| 230 |
+
}
|
| 231 |
+
assembler.workflow[final_scale_id] = final_scale_node
|
| 232 |
+
target_image_conn = [final_scale_id, 0]
|
| 233 |
+
|
| 234 |
+
# 2. Ensure model is downloaded
|
| 235 |
+
clip_model_name = "qwen3.5_9b_flux3_prompt_upsampler_w4a8.safetensors"
|
| 236 |
+
if ensure_file_downloaded:
|
| 237 |
+
try:
|
| 238 |
+
ensure_file_downloaded(clip_model_name)
|
| 239 |
+
except Exception as e:
|
| 240 |
+
print(f"Warning: Failed to ensure '{clip_model_name}' downloaded: {e}")
|
| 241 |
+
|
| 242 |
+
# 3. Create CLIPLoader node (type: flux3)
|
| 243 |
+
clip_node_id = assembler._get_unique_id()
|
| 244 |
+
clip_node = create_node(assembler, "CLIPLoader", "Load CLIP")
|
| 245 |
+
clip_node['inputs'] = {
|
| 246 |
+
"clip_name": clip_model_name,
|
| 247 |
+
"type": "flux3",
|
| 248 |
+
"device": "default"
|
| 249 |
+
}
|
| 250 |
+
assembler.workflow[clip_node_id] = clip_node
|
| 251 |
+
|
| 252 |
+
# 4. Create PrimitiveStringMultiline node for System Prompt
|
| 253 |
+
system_prompt_node_id = assembler._get_unique_id()
|
| 254 |
+
system_prompt_node = create_node(assembler, "PrimitiveStringMultiline", "Text (System prompt)")
|
| 255 |
+
system_prompt_node['inputs'] = {
|
| 256 |
+
"value": system_prompt
|
| 257 |
+
}
|
| 258 |
+
assembler.workflow[system_prompt_node_id] = system_prompt_node
|
| 259 |
+
|
| 260 |
+
# 5. Create PrimitiveStringMultiline node for User Prompt
|
| 261 |
+
if isinstance(original_prompt, (list, tuple)):
|
| 262 |
+
prompt_conn = original_prompt
|
| 263 |
+
else:
|
| 264 |
+
prompt_node_id = assembler._get_unique_id()
|
| 265 |
+
prompt_node = create_node(assembler, "PrimitiveStringMultiline", "Text (Multiline)")
|
| 266 |
+
prompt_node['inputs'] = {
|
| 267 |
+
"value": original_prompt if isinstance(original_prompt, str) else ""
|
| 268 |
+
}
|
| 269 |
+
assembler.workflow[prompt_node_id] = prompt_node
|
| 270 |
+
prompt_conn = [prompt_node_id, 0]
|
| 271 |
+
|
| 272 |
+
# 6. Create TextGenerate node
|
| 273 |
+
text_gen_node_id = assembler._get_unique_id()
|
| 274 |
+
text_gen_node = create_node(assembler, "TextGenerate", "Generate Text")
|
| 275 |
+
text_gen_inputs = {
|
| 276 |
+
"prompt": prompt_conn,
|
| 277 |
+
"max_length": max_length,
|
| 278 |
+
"sampling_mode": "on",
|
| 279 |
+
"sampling_mode.temperature": 1.0,
|
| 280 |
+
"sampling_mode.top_k": 1,
|
| 281 |
+
"sampling_mode.top_p": 0.95,
|
| 282 |
+
"sampling_mode.min_p": 0.0,
|
| 283 |
+
"sampling_mode.repetition_penalty": 1.05,
|
| 284 |
+
"sampling_mode.seed": 0,
|
| 285 |
+
"sampling_mode.presence_penalty": 1.5,
|
| 286 |
+
"thinking": thinking_enabled,
|
| 287 |
+
"use_default_template": True,
|
| 288 |
+
"mtp": "auto",
|
| 289 |
+
"clip": [clip_node_id, 0],
|
| 290 |
+
"system_prompt": [system_prompt_node_id, 0]
|
| 291 |
+
}
|
| 292 |
+
if target_image_conn is not None:
|
| 293 |
+
text_gen_inputs["image"] = target_image_conn
|
| 294 |
+
|
| 295 |
+
text_gen_node['inputs'] = text_gen_inputs
|
| 296 |
+
assembler.workflow[text_gen_node_id] = text_gen_node
|
| 297 |
+
|
| 298 |
+
# 7. Connect TextGenerate output to positive prompt node
|
| 299 |
+
if 'text' in target_node.get('inputs', {}):
|
| 300 |
+
target_node['inputs']['text'] = [text_gen_node_id, 0]
|
| 301 |
+
else:
|
| 302 |
+
target_node['inputs']['prompt'] = [text_gen_node_id, 0]
|
| 303 |
+
|
| 304 |
+
print(f"[Injector] FLUX.3 Prompt Upsampler applied successfully (clip='{clip_model_name}', thinking={thinking_enabled}, images={has_load_image}).")
|
chain_injectors/moss_image_control_injector.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
try:
|
| 4 |
+
from utils.app_utils import ensure_file_downloaded
|
| 5 |
+
except ImportError:
|
| 6 |
+
ensure_file_downloaded = None
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def create_node(assembler, class_type, title=None):
|
| 10 |
+
try:
|
| 11 |
+
node = assembler._get_node_template_from_api(class_type)
|
| 12 |
+
except Exception:
|
| 13 |
+
try:
|
| 14 |
+
node = assembler._get_node_template(class_type)
|
| 15 |
+
except Exception:
|
| 16 |
+
node = {
|
| 17 |
+
"inputs": {},
|
| 18 |
+
"class_type": class_type,
|
| 19 |
+
"_meta": {"title": title or class_type}
|
| 20 |
+
}
|
| 21 |
+
if title:
|
| 22 |
+
node.setdefault('_meta', {})['title'] = title
|
| 23 |
+
return node
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def inject(assembler, chain_definition, chain_items):
|
| 27 |
+
"""
|
| 28 |
+
Injects MOSS-Image Color/Depth Control into the workflow:
|
| 29 |
+
1. Loads the darask0/MOSS-Image-color-depth-control LoRA adapter (moss-control.safetensors)
|
| 30 |
+
which widens the video_embedder from 128 to 384 channels.
|
| 31 |
+
2. Loads and scales the color map and/or depth map.
|
| 32 |
+
3. Adds the MOSSImageControl node to encode maps into latents, concatenate them into
|
| 33 |
+
concat_latent_image conditioning, and set timestep schedule (start/end percent).
|
| 34 |
+
4. Connects the patched model and conditioned positive/negative outputs to KSampler.
|
| 35 |
+
"""
|
| 36 |
+
if not chain_items:
|
| 37 |
+
return
|
| 38 |
+
|
| 39 |
+
# Normalize chain_items: may be a single dict or list of dicts
|
| 40 |
+
if isinstance(chain_items, dict):
|
| 41 |
+
item_data = chain_items
|
| 42 |
+
elif isinstance(chain_items, list) and len(chain_items) > 0:
|
| 43 |
+
item_data = chain_items[0]
|
| 44 |
+
if not isinstance(item_data, dict):
|
| 45 |
+
return
|
| 46 |
+
else:
|
| 47 |
+
return
|
| 48 |
+
|
| 49 |
+
color_map = item_data.get('color_map')
|
| 50 |
+
depth_map = item_data.get('depth_map')
|
| 51 |
+
|
| 52 |
+
if not color_map and not depth_map:
|
| 53 |
+
return
|
| 54 |
+
|
| 55 |
+
lora_filename = "moss-control.safetensors"
|
| 56 |
+
if ensure_file_downloaded:
|
| 57 |
+
try:
|
| 58 |
+
ensure_file_downloaded(lora_filename)
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f"Warning: Failed to ensure '{lora_filename}' downloaded: {e}")
|
| 61 |
+
|
| 62 |
+
ksampler_name = chain_definition.get('ksampler_node', 'ksampler')
|
| 63 |
+
vae_loader_name = chain_definition.get('vae_loader_node', 'vae_loader')
|
| 64 |
+
|
| 65 |
+
if ksampler_name not in assembler.node_map:
|
| 66 |
+
print(f"Warning: Target node '{ksampler_name}' for MOSS-Image Control chain not found. Skipping.")
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
ksampler_id = assembler.node_map[ksampler_name]
|
| 70 |
+
|
| 71 |
+
if 'model' not in assembler.workflow[ksampler_id]['inputs']:
|
| 72 |
+
print(f"Warning: KSampler node '{ksampler_name}' is missing 'model' input. Skipping.")
|
| 73 |
+
return
|
| 74 |
+
|
| 75 |
+
if 'positive' not in assembler.workflow[ksampler_id]['inputs'] or \
|
| 76 |
+
'negative' not in assembler.workflow[ksampler_id]['inputs']:
|
| 77 |
+
print(f"Warning: KSampler node '{ksampler_name}' is missing 'positive' or 'negative' inputs. Skipping.")
|
| 78 |
+
return
|
| 79 |
+
|
| 80 |
+
current_model_connection = assembler.workflow[ksampler_id]['inputs']['model']
|
| 81 |
+
current_pos_connection = assembler.workflow[ksampler_id]['inputs']['positive']
|
| 82 |
+
current_neg_connection = assembler.workflow[ksampler_id]['inputs']['negative']
|
| 83 |
+
|
| 84 |
+
vae_connection = None
|
| 85 |
+
if vae_loader_name in assembler.node_map:
|
| 86 |
+
vae_connection = [assembler.node_map[vae_loader_name], 0]
|
| 87 |
+
else:
|
| 88 |
+
for node_id, node in assembler.workflow.items():
|
| 89 |
+
if isinstance(node, dict) and node.get('class_type') == 'VAELoader':
|
| 90 |
+
vae_connection = [node_id, 0]
|
| 91 |
+
break
|
| 92 |
+
|
| 93 |
+
if not vae_connection:
|
| 94 |
+
print("Warning: VAE connection not found for MOSS-Image Control. Skipping.")
|
| 95 |
+
return
|
| 96 |
+
|
| 97 |
+
megapixels = float(chain_definition.get('megapixels', 1.0))
|
| 98 |
+
|
| 99 |
+
# 1. Inject LoRA loader onto the model path
|
| 100 |
+
lora_loader_id = assembler._get_unique_id()
|
| 101 |
+
lora_loader_node = create_node(assembler, "LoraLoaderModelOnly", "Load LoRA (MOSS-Image Control)")
|
| 102 |
+
lora_loader_node['inputs']['lora_name'] = lora_filename
|
| 103 |
+
lora_loader_node['inputs']['strength_model'] = 1.0
|
| 104 |
+
lora_loader_node['inputs']['model'] = current_model_connection
|
| 105 |
+
assembler.workflow[lora_loader_id] = lora_loader_node
|
| 106 |
+
current_model_connection = [lora_loader_id, 0]
|
| 107 |
+
|
| 108 |
+
# 2. Process Color Map if provided
|
| 109 |
+
scale_color_id = None
|
| 110 |
+
if color_map:
|
| 111 |
+
load_color_id = assembler._get_unique_id()
|
| 112 |
+
load_color_node = create_node(assembler, "LoadImage", "Load MOSS Color Map")
|
| 113 |
+
load_color_node['inputs']['image'] = color_map
|
| 114 |
+
assembler.workflow[load_color_id] = load_color_node
|
| 115 |
+
|
| 116 |
+
scale_color_id = assembler._get_unique_id()
|
| 117 |
+
scale_color_node = create_node(assembler, "ImageScaleToTotalPixels", "Scale MOSS Color Map")
|
| 118 |
+
scale_color_node['inputs']['upscale_method'] = "nearest-exact"
|
| 119 |
+
scale_color_node['inputs']['megapixels'] = megapixels
|
| 120 |
+
scale_color_node['inputs']['resolution_steps'] = 1
|
| 121 |
+
scale_color_node['inputs']['image'] = [load_color_id, 0]
|
| 122 |
+
assembler.workflow[scale_color_id] = scale_color_node
|
| 123 |
+
|
| 124 |
+
# 3. Process Depth Map if provided
|
| 125 |
+
scale_depth_id = None
|
| 126 |
+
if depth_map:
|
| 127 |
+
load_depth_id = assembler._get_unique_id()
|
| 128 |
+
load_depth_node = create_node(assembler, "LoadImage", "Load MOSS Depth Map")
|
| 129 |
+
load_depth_node['inputs']['image'] = depth_map
|
| 130 |
+
assembler.workflow[load_depth_id] = load_depth_node
|
| 131 |
+
|
| 132 |
+
scale_depth_id = assembler._get_unique_id()
|
| 133 |
+
scale_depth_node = create_node(assembler, "ImageScaleToTotalPixels", "Scale MOSS Depth Map")
|
| 134 |
+
scale_depth_node['inputs']['upscale_method'] = "nearest-exact"
|
| 135 |
+
scale_depth_node['inputs']['megapixels'] = megapixels
|
| 136 |
+
scale_depth_node['inputs']['resolution_steps'] = 1
|
| 137 |
+
scale_depth_node['inputs']['image'] = [load_depth_id, 0]
|
| 138 |
+
assembler.workflow[scale_depth_id] = scale_depth_node
|
| 139 |
+
|
| 140 |
+
# 4. Create MOSSImageControl node
|
| 141 |
+
moss_control_id = assembler._get_unique_id()
|
| 142 |
+
moss_control_node = create_node(assembler, "MOSSImageControl", "MOSS-Image Color/Depth Control")
|
| 143 |
+
moss_control_node['inputs']['positive'] = current_pos_connection
|
| 144 |
+
moss_control_node['inputs']['negative'] = current_neg_connection
|
| 145 |
+
moss_control_node['inputs']['vae'] = vae_connection
|
| 146 |
+
moss_control_node['inputs']['strength'] = float(item_data.get('strength', 1.0))
|
| 147 |
+
moss_control_node['inputs']['start_percent'] = float(item_data.get('start_percent', 0.0))
|
| 148 |
+
moss_control_node['inputs']['end_percent'] = float(item_data.get('end_percent', 0.5))
|
| 149 |
+
|
| 150 |
+
if scale_color_id:
|
| 151 |
+
moss_control_node['inputs']['color_map'] = [scale_color_id, 0]
|
| 152 |
+
if scale_depth_id:
|
| 153 |
+
moss_control_node['inputs']['depth_map'] = [scale_depth_id, 0]
|
| 154 |
+
|
| 155 |
+
assembler.workflow[moss_control_id] = moss_control_node
|
| 156 |
+
|
| 157 |
+
# 5. Connect back to KSampler
|
| 158 |
+
assembler.workflow[ksampler_id]['inputs']['model'] = current_model_connection
|
| 159 |
+
assembler.workflow[ksampler_id]['inputs']['positive'] = [moss_control_id, 0]
|
| 160 |
+
assembler.workflow[ksampler_id]['inputs']['negative'] = [moss_control_id, 1]
|
| 161 |
+
|
| 162 |
+
print(f"MOSS-Image Color/Depth Control injector applied (color_map: {bool(color_map)}, depth_map: {bool(depth_map)}).")
|
chain_injectors/moss_image_edit_injector.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
try:
|
| 4 |
+
from utils.app_utils import ensure_file_downloaded
|
| 5 |
+
except ImportError:
|
| 6 |
+
ensure_file_downloaded = None
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def create_node(assembler, class_type, title=None):
|
| 10 |
+
try:
|
| 11 |
+
node = assembler._get_node_template_from_api(class_type)
|
| 12 |
+
except Exception:
|
| 13 |
+
try:
|
| 14 |
+
node = assembler._get_node_template(class_type)
|
| 15 |
+
except Exception:
|
| 16 |
+
node = {
|
| 17 |
+
"inputs": {},
|
| 18 |
+
"class_type": class_type,
|
| 19 |
+
"_meta": {"title": title or class_type}
|
| 20 |
+
}
|
| 21 |
+
if title:
|
| 22 |
+
node.setdefault('_meta', {})['title'] = title
|
| 23 |
+
return node
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def inject(assembler, chain_definition, chain_items):
|
| 27 |
+
"""
|
| 28 |
+
Injects MOSS-Image Edit (Reference Image Edit) into the workflow:
|
| 29 |
+
1. Loads the officially packaged Edit LoRA (moss_image_edit_lora_rank_64_bf16.safetensors)
|
| 30 |
+
onto the model path.
|
| 31 |
+
2. Loads and scales up to 10 reference images with ImageScaleToTotalPixels.
|
| 32 |
+
3. Replaces positive/negative prompt encoding with native TextEncodeMOSSImageEdit nodes,
|
| 33 |
+
which attach reference image latents and text tokens.
|
| 34 |
+
4. Connects patched model and conditioned positive/negative outputs to KSampler.
|
| 35 |
+
"""
|
| 36 |
+
if not chain_items:
|
| 37 |
+
return
|
| 38 |
+
|
| 39 |
+
# Normalize chain_items: dict or list of dicts/strings
|
| 40 |
+
edit_format = "Moss-Image-Edit"
|
| 41 |
+
strength = 1.0
|
| 42 |
+
valid_images = []
|
| 43 |
+
|
| 44 |
+
if isinstance(chain_items, dict):
|
| 45 |
+
chain_items = [chain_items]
|
| 46 |
+
|
| 47 |
+
if isinstance(chain_items, list):
|
| 48 |
+
for item in chain_items:
|
| 49 |
+
if not item:
|
| 50 |
+
continue
|
| 51 |
+
if isinstance(item, dict):
|
| 52 |
+
raw_imgs = item.get('images')
|
| 53 |
+
if isinstance(raw_imgs, list):
|
| 54 |
+
for img in raw_imgs:
|
| 55 |
+
p = img.get('image') or img.get('filename') or img.get('path') if isinstance(img, dict) else img
|
| 56 |
+
if p and isinstance(p, str) and p.strip():
|
| 57 |
+
valid_images.append(p.strip())
|
| 58 |
+
elif isinstance(raw_imgs, str) and raw_imgs.strip():
|
| 59 |
+
valid_images.append(raw_imgs.strip())
|
| 60 |
+
|
| 61 |
+
p = item.get('image') or item.get('filename') or item.get('path')
|
| 62 |
+
if p and isinstance(p, str) and p.strip():
|
| 63 |
+
valid_images.append(p.strip())
|
| 64 |
+
|
| 65 |
+
if 'edit_format' in item:
|
| 66 |
+
edit_format = item['edit_format']
|
| 67 |
+
if 'strength' in item:
|
| 68 |
+
try:
|
| 69 |
+
strength = float(item['strength'])
|
| 70 |
+
except (ValueError, TypeError):
|
| 71 |
+
pass
|
| 72 |
+
elif isinstance(item, str) and item.strip():
|
| 73 |
+
valid_images.append(item.strip())
|
| 74 |
+
|
| 75 |
+
if not valid_images:
|
| 76 |
+
return
|
| 77 |
+
|
| 78 |
+
valid_images = valid_images[:10]
|
| 79 |
+
|
| 80 |
+
lora_filename = "moss_image_edit_lora_rank_64_bf16.safetensors"
|
| 81 |
+
if ensure_file_downloaded:
|
| 82 |
+
try:
|
| 83 |
+
ensure_file_downloaded(lora_filename)
|
| 84 |
+
except Exception as e:
|
| 85 |
+
print(f"Warning: Failed to ensure '{lora_filename}' downloaded: {e}")
|
| 86 |
+
|
| 87 |
+
ksampler_name = chain_definition.get('ksampler_node', 'ksampler')
|
| 88 |
+
pos_prompt_name = chain_definition.get('pos_prompt_node', 'pos_prompt')
|
| 89 |
+
neg_prompt_name = chain_definition.get('neg_prompt_node', 'neg_prompt')
|
| 90 |
+
clip_loader_name = chain_definition.get('clip_loader_node', 'clip_loader')
|
| 91 |
+
vae_loader_name = chain_definition.get('vae_loader_node', 'vae_loader')
|
| 92 |
+
|
| 93 |
+
if ksampler_name not in assembler.node_map:
|
| 94 |
+
print(f"Warning: Target node '{ksampler_name}' for MOSS-Image Edit chain not found. Skipping.")
|
| 95 |
+
return
|
| 96 |
+
|
| 97 |
+
ksampler_id = assembler.node_map[ksampler_name]
|
| 98 |
+
|
| 99 |
+
if 'model' not in assembler.workflow[ksampler_id]['inputs']:
|
| 100 |
+
print(f"Warning: KSampler node '{ksampler_name}' is missing 'model' input. Skipping.")
|
| 101 |
+
return
|
| 102 |
+
|
| 103 |
+
current_model_connection = assembler.workflow[ksampler_id]['inputs']['model']
|
| 104 |
+
|
| 105 |
+
vae_connection = None
|
| 106 |
+
if vae_loader_name in assembler.node_map:
|
| 107 |
+
vae_connection = [assembler.node_map[vae_loader_name], 0]
|
| 108 |
+
else:
|
| 109 |
+
for node_id, node in assembler.workflow.items():
|
| 110 |
+
if isinstance(node, dict) and node.get('class_type') == 'VAELoader':
|
| 111 |
+
vae_connection = [node_id, 0]
|
| 112 |
+
break
|
| 113 |
+
|
| 114 |
+
if not vae_connection:
|
| 115 |
+
print("Warning: VAE connection not found for MOSS-Image Edit. Skipping.")
|
| 116 |
+
return
|
| 117 |
+
|
| 118 |
+
pos_prompt_id = assembler.node_map.get(pos_prompt_name)
|
| 119 |
+
neg_prompt_id = assembler.node_map.get(neg_prompt_name)
|
| 120 |
+
|
| 121 |
+
clip_connection = None
|
| 122 |
+
if clip_loader_name in assembler.node_map:
|
| 123 |
+
clip_connection = [assembler.node_map[clip_loader_name], 0]
|
| 124 |
+
elif pos_prompt_id and 'clip' in assembler.workflow.get(pos_prompt_id, {}).get('inputs', {}):
|
| 125 |
+
clip_connection = assembler.workflow[pos_prompt_id]['inputs']['clip']
|
| 126 |
+
else:
|
| 127 |
+
for node_id, node in assembler.workflow.items():
|
| 128 |
+
if isinstance(node, dict) and node.get('class_type') == 'CLIPLoader':
|
| 129 |
+
clip_connection = [node_id, 0]
|
| 130 |
+
break
|
| 131 |
+
|
| 132 |
+
if not clip_connection:
|
| 133 |
+
print("Warning: CLIP connection not found for MOSS-Image Edit. Skipping.")
|
| 134 |
+
return
|
| 135 |
+
|
| 136 |
+
# 1. Inject LoRA loader on model path if strength > 0
|
| 137 |
+
if strength > 0.0:
|
| 138 |
+
lora_loader_id = assembler._get_unique_id()
|
| 139 |
+
lora_loader_node = create_node(assembler, "LoraLoaderModelOnly", "Load LoRA (MOSS-Image Edit)")
|
| 140 |
+
lora_loader_node['inputs']['lora_name'] = lora_filename
|
| 141 |
+
lora_loader_node['inputs']['strength_model'] = strength
|
| 142 |
+
lora_loader_node['inputs']['model'] = current_model_connection
|
| 143 |
+
assembler.workflow[lora_loader_id] = lora_loader_node
|
| 144 |
+
current_model_connection = [lora_loader_id, 0]
|
| 145 |
+
|
| 146 |
+
megapixels = float(chain_definition.get('megapixels', 1.0))
|
| 147 |
+
|
| 148 |
+
# 2. Process Reference Images
|
| 149 |
+
scale_image_ids = []
|
| 150 |
+
for i, img_filename in enumerate(valid_images):
|
| 151 |
+
load_id = assembler._get_unique_id()
|
| 152 |
+
load_node = create_node(assembler, "LoadImage", f"Load Reference Image {i+1} (MOSS Edit)")
|
| 153 |
+
load_node['inputs']['image'] = img_filename
|
| 154 |
+
assembler.workflow[load_id] = load_node
|
| 155 |
+
|
| 156 |
+
scale_id = assembler._get_unique_id()
|
| 157 |
+
scale_node = create_node(assembler, "ImageScaleToTotalPixels", f"Scale Reference {i+1} (MOSS Edit)")
|
| 158 |
+
scale_node['inputs']['upscale_method'] = "lanczos"
|
| 159 |
+
scale_node['inputs']['megapixels'] = megapixels
|
| 160 |
+
scale_node['inputs']['resolution_steps'] = 1
|
| 161 |
+
scale_node['inputs']['image'] = [load_id, 0]
|
| 162 |
+
assembler.workflow[scale_id] = scale_node
|
| 163 |
+
scale_image_ids.append(scale_id)
|
| 164 |
+
|
| 165 |
+
# 3. Extract Prompt Texts
|
| 166 |
+
pos_text = ""
|
| 167 |
+
if pos_prompt_id and pos_prompt_id in assembler.workflow:
|
| 168 |
+
pos_text = assembler.workflow[pos_prompt_id]['inputs'].get('text', '') or assembler.workflow[pos_prompt_id]['inputs'].get('prompt', '')
|
| 169 |
+
elif hasattr(assembler, 'ui_values') and isinstance(assembler.ui_values, dict):
|
| 170 |
+
pos_text = assembler.ui_values.get('positive_prompt') or assembler.ui_values.get('prompt') or ''
|
| 171 |
+
|
| 172 |
+
neg_text = ""
|
| 173 |
+
if neg_prompt_id and neg_prompt_id in assembler.workflow:
|
| 174 |
+
neg_text = assembler.workflow[neg_prompt_id]['inputs'].get('text', '') or assembler.workflow[neg_prompt_id]['inputs'].get('prompt', '')
|
| 175 |
+
elif hasattr(assembler, 'ui_values') and isinstance(assembler.ui_values, dict):
|
| 176 |
+
neg_text = assembler.ui_values.get('negative_prompt') or assembler.ui_values.get('neg_prompt') or ''
|
| 177 |
+
|
| 178 |
+
# 4. Positive TextEncodeMOSSImageEdit
|
| 179 |
+
pos_edit_id = assembler._get_unique_id()
|
| 180 |
+
pos_edit_node = create_node(assembler, "TextEncodeMOSSImageEdit", "Text Encode MOSS-Image Edit")
|
| 181 |
+
pos_edit_node['inputs']['clip'] = clip_connection
|
| 182 |
+
pos_edit_node['inputs']['prompt'] = pos_text
|
| 183 |
+
pos_edit_node['inputs']['vae'] = vae_connection
|
| 184 |
+
pos_edit_node['inputs']['edit_format'] = edit_format
|
| 185 |
+
pos_edit_node['inputs']['image_strength'] = 1.0
|
| 186 |
+
for i, sc_id in enumerate(scale_image_ids):
|
| 187 |
+
pos_edit_node['inputs'][f"images.image_{i}"] = [sc_id, 0]
|
| 188 |
+
pos_edit_node['inputs'] = {k: v for k, v in pos_edit_node['inputs'].items() if v is not None}
|
| 189 |
+
assembler.workflow[pos_edit_id] = pos_edit_node
|
| 190 |
+
|
| 191 |
+
# 5. Negative TextEncodeMOSSImageEdit
|
| 192 |
+
neg_edit_id = assembler._get_unique_id()
|
| 193 |
+
neg_edit_node = create_node(assembler, "TextEncodeMOSSImageEdit", "Text Encode MOSS-Image Edit")
|
| 194 |
+
neg_edit_node['inputs']['clip'] = clip_connection
|
| 195 |
+
neg_edit_node['inputs']['prompt'] = neg_text
|
| 196 |
+
neg_edit_node['inputs']['vae'] = vae_connection
|
| 197 |
+
neg_edit_node['inputs']['edit_format'] = edit_format
|
| 198 |
+
neg_edit_node['inputs']['image_strength'] = 1.0
|
| 199 |
+
for i, sc_id in enumerate(scale_image_ids):
|
| 200 |
+
neg_edit_node['inputs'][f"images.image_{i}"] = [sc_id, 0]
|
| 201 |
+
neg_edit_node['inputs'] = {k: v for k, v in neg_edit_node['inputs'].items() if v is not None}
|
| 202 |
+
assembler.workflow[neg_edit_id] = neg_edit_node
|
| 203 |
+
|
| 204 |
+
# 6. Reconnect model and redirect conditioning references in workflow
|
| 205 |
+
assembler.workflow[ksampler_id]['inputs']['model'] = current_model_connection
|
| 206 |
+
|
| 207 |
+
# Redirect all existing connections that referenced pos_prompt_id / neg_prompt_id
|
| 208 |
+
if pos_prompt_id:
|
| 209 |
+
for node in assembler.workflow.values():
|
| 210 |
+
if isinstance(node, dict) and 'inputs' in node:
|
| 211 |
+
for k, v in node['inputs'].items():
|
| 212 |
+
if isinstance(v, list) and len(v) == 2 and v[0] == pos_prompt_id:
|
| 213 |
+
node['inputs'][k] = [pos_edit_id, 0]
|
| 214 |
+
|
| 215 |
+
if neg_prompt_id:
|
| 216 |
+
for node in assembler.workflow.values():
|
| 217 |
+
if isinstance(node, dict) and 'inputs' in node:
|
| 218 |
+
for k, v in node['inputs'].items():
|
| 219 |
+
if isinstance(v, list) and len(v) == 2 and v[0] == neg_prompt_id:
|
| 220 |
+
node['inputs'][k] = [neg_edit_id, 0]
|
| 221 |
+
|
| 222 |
+
# Clean up original CLIPTextEncode nodes if they are in workflow
|
| 223 |
+
if pos_prompt_id and pos_prompt_id in assembler.workflow:
|
| 224 |
+
del assembler.workflow[pos_prompt_id]
|
| 225 |
+
if neg_prompt_id and neg_prompt_id in assembler.workflow:
|
| 226 |
+
del assembler.workflow[neg_prompt_id]
|
| 227 |
+
|
| 228 |
+
print(f"MOSS-Image Edit injector applied with {len(valid_images)} reference image(s) (format: {edit_format}, strength: {strength}).")
|
chain_injectors/pid_injector.py
CHANGED
|
@@ -290,6 +290,9 @@ def inject(assembler, chain_definition, chain_items):
|
|
| 290 |
if input_val[0] == original_vae_decode_id:
|
| 291 |
node_data['inputs'][input_name] = [pid_vae_decode_id, 0]
|
| 292 |
|
|
|
|
|
|
|
|
|
|
| 293 |
is_vae_loader_referenced = False
|
| 294 |
if original_vae_loader_id:
|
| 295 |
for node_id, node_data in assembler.workflow.items():
|
|
@@ -306,7 +309,4 @@ def inject(assembler, chain_definition, chain_items):
|
|
| 306 |
if original_vae_loader_id in assembler.workflow and not is_vae_loader_referenced:
|
| 307 |
del assembler.workflow[original_vae_loader_id]
|
| 308 |
|
| 309 |
-
if original_vae_decode_id in assembler.workflow:
|
| 310 |
-
del assembler.workflow[original_vae_decode_id]
|
| 311 |
-
|
| 312 |
print("[PiD Injector] Successfully injected PiD pipeline and replaced VAE decode/loader.")
|
|
|
|
| 290 |
if input_val[0] == original_vae_decode_id:
|
| 291 |
node_data['inputs'][input_name] = [pid_vae_decode_id, 0]
|
| 292 |
|
| 293 |
+
if original_vae_decode_id in assembler.workflow:
|
| 294 |
+
del assembler.workflow[original_vae_decode_id]
|
| 295 |
+
|
| 296 |
is_vae_loader_referenced = False
|
| 297 |
if original_vae_loader_id:
|
| 298 |
for node_id, node_data in assembler.workflow.items():
|
|
|
|
| 309 |
if original_vae_loader_id in assembler.workflow and not is_vae_loader_referenced:
|
| 310 |
del assembler.workflow[original_vae_loader_id]
|
| 311 |
|
|
|
|
|
|
|
|
|
|
| 312 |
print("[PiD Injector] Successfully injected PiD pipeline and replaced VAE decode/loader.")
|
chain_injectors/reference_image_injector.py
CHANGED
|
@@ -43,7 +43,7 @@ def inject(assembler, chain_definition, chain_items):
|
|
| 43 |
target_node_id = assembler.node_map.get(target_node_name)
|
| 44 |
|
| 45 |
if not target_node_id or target_node_id not in assembler.workflow:
|
| 46 |
-
supported_types = ('TextEncodeMageFlowEdit', 'TextEncodeQwenImage21', 'TextEncodeBooguEdit')
|
| 47 |
for node_id, node in assembler.workflow.items():
|
| 48 |
if isinstance(node, dict) and node.get('class_type') in supported_types:
|
| 49 |
target_node_id = node_id
|
|
|
|
| 43 |
target_node_id = assembler.node_map.get(target_node_name)
|
| 44 |
|
| 45 |
if not target_node_id or target_node_id not in assembler.workflow:
|
| 46 |
+
supported_types = ('TextEncodeMageFlowEdit', 'TextEncodeQwenImage21', 'TextEncodeBooguEdit', 'TextEncodeMingImageEdit', 'TextEncodeFibo2')
|
| 47 |
for node_id, node in assembler.workflow.items():
|
| 48 |
if isinstance(node, dict) and node.get('class_type') in supported_types:
|
| 49 |
target_node_id = node_id
|
comfy_integration/setup.py
CHANGED
|
@@ -25,12 +25,6 @@ def initialize_comfyui():
|
|
| 25 |
else:
|
| 26 |
print("✅ ComfyUI repository already exists.")
|
| 27 |
|
| 28 |
-
try:
|
| 29 |
-
print("--- [Setup] Applying PR #16519 for ComfyUI (Qwen-Image 2.1 Fun ControlNet) ---")
|
| 30 |
-
os.system(f"git -C {COMFYUI_TEMP_DIR} fetch --depth 1 origin pull/16519/head && git -c user.name=dev -c user.email=dev@example.com -C {COMFYUI_TEMP_DIR} merge --no-edit FETCH_HEAD || git -C {COMFYUI_TEMP_DIR} checkout -f FETCH_HEAD")
|
| 31 |
-
print("✅ Successfully applied PR #16519.")
|
| 32 |
-
except Exception as e:
|
| 33 |
-
print(f"⚠️ Warning: Could not apply PR #16519 for ComfyUI: {e}")
|
| 34 |
|
| 35 |
print(f"--- Merging ComfyUI from '{COMFYUI_TEMP_DIR}' to '{APP_DIR}' ---")
|
| 36 |
for item in os.listdir(COMFYUI_TEMP_DIR):
|
|
@@ -127,6 +121,38 @@ def initialize_comfyui():
|
|
| 127 |
|
| 128 |
print(f"✅ Current working directory is: {os.getcwd()}")
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
import comfy.model_management
|
| 131 |
print("--- Environment Ready ---")
|
| 132 |
|
|
|
|
| 25 |
else:
|
| 26 |
print("✅ ComfyUI repository already exists.")
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
print(f"--- Merging ComfyUI from '{COMFYUI_TEMP_DIR}' to '{APP_DIR}' ---")
|
| 30 |
for item in os.listdir(COMFYUI_TEMP_DIR):
|
|
|
|
| 121 |
|
| 122 |
print(f"✅ Current working directory is: {os.getcwd()}")
|
| 123 |
|
| 124 |
+
# [ZeroGPU / Upstream ComfyUI Compatibility]
|
| 125 |
+
# Guard against ComfyUI PR #16746 is_integrated attribute check crash under ZeroGPU SimpleNamespace
|
| 126 |
+
try:
|
| 127 |
+
import torch
|
| 128 |
+
if hasattr(torch, "cuda") and hasattr(torch.cuda, "get_device_properties"):
|
| 129 |
+
_orig_gdp = torch.cuda.get_device_properties
|
| 130 |
+
def _safe_gdp(*args, **kwargs):
|
| 131 |
+
p = _orig_gdp(*args, **kwargs)
|
| 132 |
+
if not hasattr(p, "is_integrated"):
|
| 133 |
+
try:
|
| 134 |
+
p.is_integrated = False
|
| 135 |
+
except Exception:
|
| 136 |
+
pass
|
| 137 |
+
return p
|
| 138 |
+
torch.cuda.get_device_properties = _safe_gdp
|
| 139 |
+
except Exception:
|
| 140 |
+
pass
|
| 141 |
+
|
| 142 |
+
mm_path = os.path.join(APP_DIR, "comfy", "model_management.py")
|
| 143 |
+
if os.path.exists(mm_path):
|
| 144 |
+
try:
|
| 145 |
+
with open(mm_path, "r", encoding="utf-8") as f:
|
| 146 |
+
mm_code = f.read()
|
| 147 |
+
unsafe_check = "bool(torch.cuda.get_device_properties(device).is_integrated)"
|
| 148 |
+
safe_check = "bool(getattr(torch.cuda.get_device_properties(device), 'is_integrated', False))"
|
| 149 |
+
if unsafe_check in mm_code:
|
| 150 |
+
with open(mm_path, "w", encoding="utf-8") as f:
|
| 151 |
+
f.write(mm_code.replace(unsafe_check, safe_check))
|
| 152 |
+
print("🛡️ [Setup] Patched comfy/model_management.py for safe is_integrated attribute access.")
|
| 153 |
+
except Exception as e:
|
| 154 |
+
print(f"⚠️ [Setup] Failed to patch comfy/model_management.py: {e}")
|
| 155 |
+
|
| 156 |
import comfy.model_management
|
| 157 |
print("--- Environment Ready ---")
|
| 158 |
|
core/pipelines/pipeline_input_processor.py
CHANGED
|
@@ -430,6 +430,82 @@ def process_pipeline_inputs(ui_inputs: Dict[str, Any], progress: gr.Progress, wo
|
|
| 430 |
item["system_prompt"] = str(sys_p)
|
| 431 |
active_ming_image_prompt_enhancer.append(item)
|
| 432 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
return {
|
| 434 |
"active_loras_for_gpu": active_loras_for_gpu,
|
| 435 |
"active_loras_for_meta": active_loras_for_meta,
|
|
@@ -449,5 +525,8 @@ def process_pipeline_inputs(ui_inputs: Dict[str, Any], progress: gr.Progress, wo
|
|
| 449 |
"active_conditioning": active_conditioning,
|
| 450 |
"active_qwen_image_2_1_prompt_enhancer": active_qwen_image_2_1_prompt_enhancer,
|
| 451 |
"active_ming_image_prompt_enhancer": active_ming_image_prompt_enhancer,
|
|
|
|
|
|
|
|
|
|
| 452 |
"temp_files_to_clean": temp_files_to_clean
|
| 453 |
}
|
|
|
|
| 430 |
item["system_prompt"] = str(sys_p)
|
| 431 |
active_ming_image_prompt_enhancer.append(item)
|
| 432 |
|
| 433 |
+
flux3_pe_enable = ui_inputs.get('flux3_prompt_upsampler_enable', False)
|
| 434 |
+
active_flux3_prompt_upsampler = []
|
| 435 |
+
if 'flux3_prompt_upsampler' in enabled_chains and flux3_pe_enable:
|
| 436 |
+
item = {
|
| 437 |
+
"enable": True,
|
| 438 |
+
"thinking": bool(ui_inputs.get('flux3_prompt_upsampler_thinking', False)),
|
| 439 |
+
"bbox_mode": bool(ui_inputs.get('flux3_prompt_upsampler_bbox_mode', False)),
|
| 440 |
+
"max_length": int(ui_inputs.get('flux3_prompt_upsampler_max_length', 3584))
|
| 441 |
+
}
|
| 442 |
+
sys_p = ui_inputs.get('flux3_prompt_upsampler_system_prompt')
|
| 443 |
+
if sys_p:
|
| 444 |
+
item["system_prompt"] = str(sys_p)
|
| 445 |
+
active_flux3_prompt_upsampler.append(item)
|
| 446 |
+
|
| 447 |
+
color_map_val = ui_inputs.get('moss_image_control_color_map')
|
| 448 |
+
depth_map_val = ui_inputs.get('moss_image_control_depth_map')
|
| 449 |
+
active_moss_image_control = []
|
| 450 |
+
if 'moss_image_control' in enabled_chains and (color_map_val or depth_map_val):
|
| 451 |
+
color_filename = None
|
| 452 |
+
if color_map_val:
|
| 453 |
+
if isinstance(color_map_val, str):
|
| 454 |
+
color_filename = color_map_val
|
| 455 |
+
elif hasattr(color_map_val, 'save'):
|
| 456 |
+
if not os.path.exists(INPUT_DIR): os.makedirs(INPUT_DIR)
|
| 457 |
+
temp_path = os.path.join(INPUT_DIR, f"temp_moss_color_{random.randint(1000, 9999)}.png")
|
| 458 |
+
color_map_val.save(temp_path, "PNG")
|
| 459 |
+
temp_files_to_clean.append(temp_path)
|
| 460 |
+
color_filename = os.path.basename(temp_path)
|
| 461 |
+
|
| 462 |
+
depth_filename = None
|
| 463 |
+
if depth_map_val:
|
| 464 |
+
if isinstance(depth_map_val, str):
|
| 465 |
+
depth_filename = depth_map_val
|
| 466 |
+
elif hasattr(depth_map_val, 'save'):
|
| 467 |
+
if not os.path.exists(INPUT_DIR): os.makedirs(INPUT_DIR)
|
| 468 |
+
temp_path = os.path.join(INPUT_DIR, f"temp_moss_depth_{random.randint(1000, 9999)}.png")
|
| 469 |
+
depth_map_val.save(temp_path, "PNG")
|
| 470 |
+
temp_files_to_clean.append(temp_path)
|
| 471 |
+
depth_filename = os.path.basename(temp_path)
|
| 472 |
+
|
| 473 |
+
if color_filename or depth_filename:
|
| 474 |
+
try:
|
| 475 |
+
strength = float(ui_inputs.get('moss_image_control_strength', 1.0))
|
| 476 |
+
except (ValueError, TypeError):
|
| 477 |
+
strength = 1.0
|
| 478 |
+
try:
|
| 479 |
+
start_percent = float(ui_inputs.get('moss_image_control_start_percent', 0.0))
|
| 480 |
+
except (ValueError, TypeError):
|
| 481 |
+
start_percent = 0.0
|
| 482 |
+
try:
|
| 483 |
+
end_percent = float(ui_inputs.get('moss_image_control_end_percent', 0.5))
|
| 484 |
+
except (ValueError, TypeError):
|
| 485 |
+
end_percent = 0.5
|
| 486 |
+
|
| 487 |
+
active_moss_image_control.append({
|
| 488 |
+
'color_map': color_filename,
|
| 489 |
+
'depth_map': depth_filename,
|
| 490 |
+
'strength': strength,
|
| 491 |
+
'start_percent': start_percent,
|
| 492 |
+
'end_percent': end_percent
|
| 493 |
+
})
|
| 494 |
+
|
| 495 |
+
moss_image_edit_data = ui_inputs.get('moss_image_edit_data', [])
|
| 496 |
+
active_moss_image_edit = []
|
| 497 |
+
if 'moss_image_edit' in enabled_chains and moss_image_edit_data:
|
| 498 |
+
for img in moss_image_edit_data:
|
| 499 |
+
if img:
|
| 500 |
+
if isinstance(img, str):
|
| 501 |
+
active_moss_image_edit.append(img)
|
| 502 |
+
elif hasattr(img, 'save'):
|
| 503 |
+
if not os.path.exists(INPUT_DIR): os.makedirs(INPUT_DIR)
|
| 504 |
+
temp_path = os.path.join(INPUT_DIR, f"temp_moss_edit_{random.randint(1000, 9999)}.png")
|
| 505 |
+
img.save(temp_path, "PNG")
|
| 506 |
+
temp_files_to_clean.append(temp_path)
|
| 507 |
+
active_moss_image_edit.append(os.path.basename(temp_path))
|
| 508 |
+
|
| 509 |
return {
|
| 510 |
"active_loras_for_gpu": active_loras_for_gpu,
|
| 511 |
"active_loras_for_meta": active_loras_for_meta,
|
|
|
|
| 525 |
"active_conditioning": active_conditioning,
|
| 526 |
"active_qwen_image_2_1_prompt_enhancer": active_qwen_image_2_1_prompt_enhancer,
|
| 527 |
"active_ming_image_prompt_enhancer": active_ming_image_prompt_enhancer,
|
| 528 |
+
"active_flux3_prompt_upsampler": active_flux3_prompt_upsampler,
|
| 529 |
+
"active_moss_image_control": active_moss_image_control,
|
| 530 |
+
"active_moss_image_edit": active_moss_image_edit,
|
| 531 |
"temp_files_to_clean": temp_files_to_clean
|
| 532 |
}
|
core/pipelines/sd_image_pipeline.py
CHANGED
|
@@ -1,288 +1,294 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import random
|
| 3 |
-
import shutil
|
| 4 |
-
import torch
|
| 5 |
-
import gradio as gr
|
| 6 |
-
from PIL import Image
|
| 7 |
-
from typing import List, Dict, Any
|
| 8 |
-
|
| 9 |
-
from .base_pipeline import BasePipeline
|
| 10 |
-
from core.settings import *
|
| 11 |
-
from utils.app_utils import sanitize_prompt
|
| 12 |
-
from core.workflow_assembler import WorkflowAssembler
|
| 13 |
-
from .workflow_executor import WorkflowExecutor
|
| 14 |
-
from .pipeline_input_processor import process_pipeline_inputs
|
| 15 |
-
|
| 16 |
-
class SdImagePipeline(BasePipeline):
|
| 17 |
-
def get_required_models(self, model_display_name: str, **kwargs) -> List[str]:
|
| 18 |
-
model_info = ALL_MODEL_MAP.get(model_display_name)
|
| 19 |
-
if not model_info:
|
| 20 |
-
return [model_display_name]
|
| 21 |
-
|
| 22 |
-
path_or_components = model_info[1]
|
| 23 |
-
if isinstance(path_or_components, dict):
|
| 24 |
-
return [v for v in path_or_components.values() if v and v != "pixel_space"]
|
| 25 |
-
else:
|
| 26 |
-
return [model_display_name]
|
| 27 |
-
|
| 28 |
-
def _gpu_logic(self, ui_inputs: Dict, loras_string: str, workflow: Dict[str, Any], assembler: WorkflowAssembler, progress=gr.Progress(track_tqdm=True)):
|
| 29 |
-
model_display_name = ui_inputs['model_display_name']
|
| 30 |
-
|
| 31 |
-
progress(0.4, desc="Executing workflow...")
|
| 32 |
-
|
| 33 |
-
initial_objects = {}
|
| 34 |
-
|
| 35 |
-
decoded_images_tensor = WorkflowExecutor.execute_workflow(workflow, initial_objects=initial_objects)
|
| 36 |
-
|
| 37 |
-
output_images = []
|
| 38 |
-
raw_seed = ui_inputs.get('seed')
|
| 39 |
-
start_seed = int(raw_seed) if (raw_seed is not None and raw_seed != -1) else random.randint(0, 2**64 - 1)
|
| 40 |
-
for i in range(decoded_images_tensor.shape[0]):
|
| 41 |
-
img_tensor = decoded_images_tensor[i]
|
| 42 |
-
pil_image = Image.fromarray((img_tensor.cpu().numpy() * 255.0).astype("uint8"))
|
| 43 |
-
current_seed = start_seed + i
|
| 44 |
-
|
| 45 |
-
width_for_meta = ui_inputs.get('width', 'N/A')
|
| 46 |
-
height_for_meta = ui_inputs.get('height', 'N/A')
|
| 47 |
-
|
| 48 |
-
params_string = f"{ui_inputs['positive_prompt']}\nNegative prompt: {ui_inputs['negative_prompt']}\n"
|
| 49 |
-
params_string += f"Steps: {ui_inputs['num_inference_steps']}, Sampler: {ui_inputs['sampler']}, Scheduler: {ui_inputs['scheduler']}, CFG scale: {ui_inputs['guidance_scale']}, Seed: {current_seed}, Size: {width_for_meta}x{height_for_meta}, Base Model: {model_display_name}"
|
| 50 |
-
if ui_inputs['task_type'] != 'txt2img': params_string += f", Denoise: {ui_inputs['denoise']}"
|
| 51 |
-
if loras_string: params_string += f", {loras_string}"
|
| 52 |
-
|
| 53 |
-
pil_image.info = {'parameters': params_string.strip()}
|
| 54 |
-
output_images.append(pil_image)
|
| 55 |
-
|
| 56 |
-
return output_images
|
| 57 |
-
|
| 58 |
-
def run(self, ui_inputs: Dict, progress):
|
| 59 |
-
progress(0, desc="Preparing models...")
|
| 60 |
-
|
| 61 |
-
task_type = ui_inputs['task_type']
|
| 62 |
-
model_display_name = ui_inputs['model_display_name']
|
| 63 |
-
model_type = MODEL_TYPE_MAP.get(model_display_name, 'sdxl')
|
| 64 |
-
|
| 65 |
-
architectures_dict = ARCHITECTURES_CONFIG.get('architectures', {})
|
| 66 |
-
workflow_model_type = architectures_dict.get(model_type, {}).get("model_type", model_type.lower().replace(" ", "").replace(".", ""))
|
| 67 |
-
|
| 68 |
-
ui_inputs['positive_prompt'] = sanitize_prompt(ui_inputs.get('positive_prompt', ''))
|
| 69 |
-
ui_inputs['negative_prompt'] = sanitize_prompt(ui_inputs.get('negative_prompt', ''))
|
| 70 |
-
|
| 71 |
-
required_models = self.get_required_models(model_display_name=model_display_name)
|
| 72 |
-
|
| 73 |
-
arch_enabled_chains = FEATURES_CONFIG.get(workflow_model_type, {}).get('enabled_chains', [])
|
| 74 |
-
task_enabled_chains = TASK_FEATURES_CONFIG.get(task_type, {}).get('enabled_chains', [])
|
| 75 |
-
enabled_chains = set([c for c in arch_enabled_chains if c in task_enabled_chains])
|
| 76 |
-
|
| 77 |
-
is_pid_enabled = (ui_inputs.get('pid_settings', 'OFF') == 'ON' and 'pid' in enabled_chains and task_type == 'txt2img')
|
| 78 |
-
if is_pid_enabled:
|
| 79 |
-
import yaml
|
| 80 |
-
pid_config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'yaml', 'pid.yaml')
|
| 81 |
-
pid_unet_name = "pid_1.5_flux1_1024_to_4096_4step_int8_convrot.safetensors"
|
| 82 |
-
try:
|
| 83 |
-
with open(pid_config_path, 'r', encoding='utf-8') as f:
|
| 84 |
-
pid_config = yaml.safe_load(f) or {}
|
| 85 |
-
pid_items = pid_config.get("PiD", [])
|
| 86 |
-
for item in pid_items:
|
| 87 |
-
archs = item.get("architectures", [])
|
| 88 |
-
if workflow_model_type in archs:
|
| 89 |
-
pid_unet_name = item.get("filepath")
|
| 90 |
-
break
|
| 91 |
-
except Exception as e:
|
| 92 |
-
print(f"Error loading PiD config for download: {e}")
|
| 93 |
-
|
| 94 |
-
if pid_unet_name not in required_models:
|
| 95 |
-
required_models.append(pid_unet_name)
|
| 96 |
-
if "gemma_2_2b_it_elm_fp8_scaled.safetensors" not in required_models:
|
| 97 |
-
required_models.append("gemma_2_2b_it_elm_fp8_scaled.safetensors")
|
| 98 |
-
|
| 99 |
-
self.model_manager.ensure_models_downloaded(required_models, progress=progress)
|
| 100 |
-
|
| 101 |
-
temp_files_to_clean = []
|
| 102 |
-
try:
|
| 103 |
-
processed = process_pipeline_inputs(ui_inputs, progress, workflow_model_type)
|
| 104 |
-
temp_files_to_clean.extend(processed["temp_files_to_clean"])
|
| 105 |
-
|
| 106 |
-
active_loras_for_gpu = processed["active_loras_for_gpu"]
|
| 107 |
-
active_loras_for_meta = processed["active_loras_for_meta"]
|
| 108 |
-
active_controlnets = processed["active_controlnets"]
|
| 109 |
-
active_ipadapters = processed["active_ipadapters"]
|
| 110 |
-
active_flux1_ipadapters = processed["active_flux1_ipadapters"]
|
| 111 |
-
active_sd3_ipadapters = processed["active_sd3_ipadapters"]
|
| 112 |
-
active_styles = processed["active_styles"]
|
| 113 |
-
active_reference_latents = processed["active_reference_latents"]
|
| 114 |
-
active_hidream_o1_reference = processed["active_hidream_o1_reference"]
|
| 115 |
-
active_sensenova_reference = processed.get("active_sensenova_reference", [])
|
| 116 |
-
active_joyai_reference = processed.get("active_joyai_reference", [])
|
| 117 |
-
active_krea2_identity_edit = processed.get("active_krea2_identity_edit", [])
|
| 118 |
-
active_krea2_reference_edit = processed.get("active_krea2_reference_edit", [])
|
| 119 |
-
active_qwen_image_edit = processed.get("active_qwen_image_edit", [])
|
| 120 |
-
active_reference_images = processed.get("active_reference_images", [])
|
| 121 |
-
active_conditioning = processed["active_conditioning"]
|
| 122 |
-
active_qwen_image_2_1_prompt_enhancer = processed.get("active_qwen_image_2_1_prompt_enhancer", [])
|
| 123 |
-
active_ming_image_prompt_enhancer = processed.get("active_ming_image_prompt_enhancer", [])
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
'
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
"
|
| 164 |
-
"
|
| 165 |
-
"
|
| 166 |
-
"
|
| 167 |
-
"
|
| 168 |
-
"
|
| 169 |
-
"
|
| 170 |
-
"
|
| 171 |
-
"
|
| 172 |
-
"
|
| 173 |
-
"
|
| 174 |
-
"
|
| 175 |
-
"
|
| 176 |
-
"
|
| 177 |
-
"
|
| 178 |
-
"
|
| 179 |
-
"
|
| 180 |
-
"
|
| 181 |
-
"
|
| 182 |
-
"
|
| 183 |
-
"
|
| 184 |
-
"
|
| 185 |
-
"
|
| 186 |
-
"
|
| 187 |
-
"
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
'
|
| 200 |
-
'
|
| 201 |
-
'
|
| 202 |
-
'
|
| 203 |
-
'
|
| 204 |
-
'
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
if
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
return results
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import random
|
| 3 |
+
import shutil
|
| 4 |
+
import torch
|
| 5 |
+
import gradio as gr
|
| 6 |
+
from PIL import Image
|
| 7 |
+
from typing import List, Dict, Any
|
| 8 |
+
|
| 9 |
+
from .base_pipeline import BasePipeline
|
| 10 |
+
from core.settings import *
|
| 11 |
+
from utils.app_utils import sanitize_prompt
|
| 12 |
+
from core.workflow_assembler import WorkflowAssembler
|
| 13 |
+
from .workflow_executor import WorkflowExecutor
|
| 14 |
+
from .pipeline_input_processor import process_pipeline_inputs
|
| 15 |
+
|
| 16 |
+
class SdImagePipeline(BasePipeline):
|
| 17 |
+
def get_required_models(self, model_display_name: str, **kwargs) -> List[str]:
|
| 18 |
+
model_info = ALL_MODEL_MAP.get(model_display_name)
|
| 19 |
+
if not model_info:
|
| 20 |
+
return [model_display_name]
|
| 21 |
+
|
| 22 |
+
path_or_components = model_info[1]
|
| 23 |
+
if isinstance(path_or_components, dict):
|
| 24 |
+
return [v for v in path_or_components.values() if v and v != "pixel_space"]
|
| 25 |
+
else:
|
| 26 |
+
return [model_display_name]
|
| 27 |
+
|
| 28 |
+
def _gpu_logic(self, ui_inputs: Dict, loras_string: str, workflow: Dict[str, Any], assembler: WorkflowAssembler, progress=gr.Progress(track_tqdm=True)):
|
| 29 |
+
model_display_name = ui_inputs['model_display_name']
|
| 30 |
+
|
| 31 |
+
progress(0.4, desc="Executing workflow...")
|
| 32 |
+
|
| 33 |
+
initial_objects = {}
|
| 34 |
+
|
| 35 |
+
decoded_images_tensor = WorkflowExecutor.execute_workflow(workflow, initial_objects=initial_objects)
|
| 36 |
+
|
| 37 |
+
output_images = []
|
| 38 |
+
raw_seed = ui_inputs.get('seed')
|
| 39 |
+
start_seed = int(raw_seed) if (raw_seed is not None and raw_seed != -1) else random.randint(0, 2**64 - 1)
|
| 40 |
+
for i in range(decoded_images_tensor.shape[0]):
|
| 41 |
+
img_tensor = decoded_images_tensor[i]
|
| 42 |
+
pil_image = Image.fromarray((img_tensor.cpu().numpy() * 255.0).astype("uint8"))
|
| 43 |
+
current_seed = start_seed + i
|
| 44 |
+
|
| 45 |
+
width_for_meta = ui_inputs.get('width', 'N/A')
|
| 46 |
+
height_for_meta = ui_inputs.get('height', 'N/A')
|
| 47 |
+
|
| 48 |
+
params_string = f"{ui_inputs['positive_prompt']}\nNegative prompt: {ui_inputs['negative_prompt']}\n"
|
| 49 |
+
params_string += f"Steps: {ui_inputs['num_inference_steps']}, Sampler: {ui_inputs['sampler']}, Scheduler: {ui_inputs['scheduler']}, CFG scale: {ui_inputs['guidance_scale']}, Seed: {current_seed}, Size: {width_for_meta}x{height_for_meta}, Base Model: {model_display_name}"
|
| 50 |
+
if ui_inputs['task_type'] != 'txt2img': params_string += f", Denoise: {ui_inputs['denoise']}"
|
| 51 |
+
if loras_string: params_string += f", {loras_string}"
|
| 52 |
+
|
| 53 |
+
pil_image.info = {'parameters': params_string.strip()}
|
| 54 |
+
output_images.append(pil_image)
|
| 55 |
+
|
| 56 |
+
return output_images
|
| 57 |
+
|
| 58 |
+
def run(self, ui_inputs: Dict, progress):
|
| 59 |
+
progress(0, desc="Preparing models...")
|
| 60 |
+
|
| 61 |
+
task_type = ui_inputs['task_type']
|
| 62 |
+
model_display_name = ui_inputs['model_display_name']
|
| 63 |
+
model_type = MODEL_TYPE_MAP.get(model_display_name, 'sdxl')
|
| 64 |
+
|
| 65 |
+
architectures_dict = ARCHITECTURES_CONFIG.get('architectures', {})
|
| 66 |
+
workflow_model_type = architectures_dict.get(model_type, {}).get("model_type", model_type.lower().replace(" ", "").replace(".", ""))
|
| 67 |
+
|
| 68 |
+
ui_inputs['positive_prompt'] = sanitize_prompt(ui_inputs.get('positive_prompt', ''))
|
| 69 |
+
ui_inputs['negative_prompt'] = sanitize_prompt(ui_inputs.get('negative_prompt', ''))
|
| 70 |
+
|
| 71 |
+
required_models = self.get_required_models(model_display_name=model_display_name)
|
| 72 |
+
|
| 73 |
+
arch_enabled_chains = FEATURES_CONFIG.get(workflow_model_type, {}).get('enabled_chains', [])
|
| 74 |
+
task_enabled_chains = TASK_FEATURES_CONFIG.get(task_type, {}).get('enabled_chains', [])
|
| 75 |
+
enabled_chains = set([c for c in arch_enabled_chains if c in task_enabled_chains])
|
| 76 |
+
|
| 77 |
+
is_pid_enabled = (ui_inputs.get('pid_settings', 'OFF') == 'ON' and 'pid' in enabled_chains and task_type == 'txt2img')
|
| 78 |
+
if is_pid_enabled:
|
| 79 |
+
import yaml
|
| 80 |
+
pid_config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'yaml', 'pid.yaml')
|
| 81 |
+
pid_unet_name = "pid_1.5_flux1_1024_to_4096_4step_int8_convrot.safetensors"
|
| 82 |
+
try:
|
| 83 |
+
with open(pid_config_path, 'r', encoding='utf-8') as f:
|
| 84 |
+
pid_config = yaml.safe_load(f) or {}
|
| 85 |
+
pid_items = pid_config.get("PiD", [])
|
| 86 |
+
for item in pid_items:
|
| 87 |
+
archs = item.get("architectures", [])
|
| 88 |
+
if workflow_model_type in archs:
|
| 89 |
+
pid_unet_name = item.get("filepath")
|
| 90 |
+
break
|
| 91 |
+
except Exception as e:
|
| 92 |
+
print(f"Error loading PiD config for download: {e}")
|
| 93 |
+
|
| 94 |
+
if pid_unet_name not in required_models:
|
| 95 |
+
required_models.append(pid_unet_name)
|
| 96 |
+
if "gemma_2_2b_it_elm_fp8_scaled.safetensors" not in required_models:
|
| 97 |
+
required_models.append("gemma_2_2b_it_elm_fp8_scaled.safetensors")
|
| 98 |
+
|
| 99 |
+
self.model_manager.ensure_models_downloaded(required_models, progress=progress)
|
| 100 |
+
|
| 101 |
+
temp_files_to_clean = []
|
| 102 |
+
try:
|
| 103 |
+
processed = process_pipeline_inputs(ui_inputs, progress, workflow_model_type)
|
| 104 |
+
temp_files_to_clean.extend(processed["temp_files_to_clean"])
|
| 105 |
+
|
| 106 |
+
active_loras_for_gpu = processed["active_loras_for_gpu"]
|
| 107 |
+
active_loras_for_meta = processed["active_loras_for_meta"]
|
| 108 |
+
active_controlnets = processed["active_controlnets"]
|
| 109 |
+
active_ipadapters = processed["active_ipadapters"]
|
| 110 |
+
active_flux1_ipadapters = processed["active_flux1_ipadapters"]
|
| 111 |
+
active_sd3_ipadapters = processed["active_sd3_ipadapters"]
|
| 112 |
+
active_styles = processed["active_styles"]
|
| 113 |
+
active_reference_latents = processed["active_reference_latents"]
|
| 114 |
+
active_hidream_o1_reference = processed["active_hidream_o1_reference"]
|
| 115 |
+
active_sensenova_reference = processed.get("active_sensenova_reference", [])
|
| 116 |
+
active_joyai_reference = processed.get("active_joyai_reference", [])
|
| 117 |
+
active_krea2_identity_edit = processed.get("active_krea2_identity_edit", [])
|
| 118 |
+
active_krea2_reference_edit = processed.get("active_krea2_reference_edit", [])
|
| 119 |
+
active_qwen_image_edit = processed.get("active_qwen_image_edit", [])
|
| 120 |
+
active_reference_images = processed.get("active_reference_images", [])
|
| 121 |
+
active_conditioning = processed["active_conditioning"]
|
| 122 |
+
active_qwen_image_2_1_prompt_enhancer = processed.get("active_qwen_image_2_1_prompt_enhancer", [])
|
| 123 |
+
active_ming_image_prompt_enhancer = processed.get("active_ming_image_prompt_enhancer", [])
|
| 124 |
+
active_flux3_prompt_upsampler = processed.get("active_flux3_prompt_upsampler", [])
|
| 125 |
+
active_moss_image_control = processed.get("active_moss_image_control", [])
|
| 126 |
+
active_moss_image_edit = processed.get("active_moss_image_edit", [])
|
| 127 |
+
|
| 128 |
+
loras_string = f"LoRAs: [{', '.join(active_loras_for_meta)}]" if active_loras_for_meta else ""
|
| 129 |
+
|
| 130 |
+
progress(0.8, desc="Assembling workflow...")
|
| 131 |
+
|
| 132 |
+
seed_val = ui_inputs.get('seed')
|
| 133 |
+
if seed_val is None or seed_val == -1:
|
| 134 |
+
ui_inputs['seed'] = random.randint(0, 2**32 - 1)
|
| 135 |
+
|
| 136 |
+
model_info = ALL_MODEL_MAP[model_display_name]
|
| 137 |
+
path_or_components = model_info[1]
|
| 138 |
+
latent_type = model_info[3] if len(model_info) > 3 and model_info[3] else 'latent'
|
| 139 |
+
latent_generator_template = "EmptyLatentImage"
|
| 140 |
+
if latent_type == 'sd3_latent':
|
| 141 |
+
latent_generator_template = "EmptySD3LatentImage"
|
| 142 |
+
elif latent_type == 'chroma_radiance_latent':
|
| 143 |
+
latent_generator_template = "EmptyChromaRadianceLatentImage"
|
| 144 |
+
elif latent_type == 'hunyuan_latent':
|
| 145 |
+
latent_generator_template = "EmptyHunyuanImageLatent"
|
| 146 |
+
|
| 147 |
+
dynamic_values = {
|
| 148 |
+
'task_type': ui_inputs['task_type'],
|
| 149 |
+
'model_type': workflow_model_type,
|
| 150 |
+
'latent_type': latent_type,
|
| 151 |
+
'latent_generator_template': latent_generator_template
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
recipe_path = os.path.join(os.path.dirname(__file__), "workflow_recipes", "unified_recipe.yaml")
|
| 155 |
+
assembler = WorkflowAssembler(recipe_path, dynamic_values=dynamic_values)
|
| 156 |
+
|
| 157 |
+
hidream_o1_smoothing_data = []
|
| 158 |
+
if 'hidream_o1_smoothing' in enabled_chains and model_display_name == "HiDream-O1-Image":
|
| 159 |
+
hidream_o1_smoothing_data.append({})
|
| 160 |
+
|
| 161 |
+
workflow_inputs = {
|
| 162 |
+
**ui_inputs,
|
| 163 |
+
"positive_prompt": ui_inputs['positive_prompt'], "negative_prompt": ui_inputs['negative_prompt'],
|
| 164 |
+
"seed": ui_inputs['seed'], "steps": ui_inputs['num_inference_steps'], "cfg": ui_inputs['guidance_scale'],
|
| 165 |
+
"sampler_name": ui_inputs['sampler'], "scheduler": ui_inputs['scheduler'],
|
| 166 |
+
"batch_size": ui_inputs['batch_size'],
|
| 167 |
+
"denoise": ui_inputs['denoise'],
|
| 168 |
+
"vae_name": ui_inputs.get('vae_name') if 'vae' in enabled_chains else None,
|
| 169 |
+
"lora_chain": active_loras_for_gpu,
|
| 170 |
+
"controlnet_chain": active_controlnets,
|
| 171 |
+
"ipadapter_chain": active_ipadapters,
|
| 172 |
+
"flux1_ipadapter_chain": active_flux1_ipadapters,
|
| 173 |
+
"sd3_ipadapter_chain": active_sd3_ipadapters,
|
| 174 |
+
"style_chain": active_styles,
|
| 175 |
+
"conditioning_chain": active_conditioning,
|
| 176 |
+
"reference_latent_chain": active_reference_latents,
|
| 177 |
+
"hidream_o1_reference_chain": active_hidream_o1_reference,
|
| 178 |
+
"sensenova_reference_chain": active_sensenova_reference,
|
| 179 |
+
"joyai_image_chain": active_joyai_reference,
|
| 180 |
+
"krea2_identity_edit_chain": active_krea2_identity_edit,
|
| 181 |
+
"krea2_style_reference_chain": active_krea2_reference_edit,
|
| 182 |
+
"qwen_image_edit_chain": active_qwen_image_edit,
|
| 183 |
+
"reference_image_chain": active_reference_images,
|
| 184 |
+
"qwen_image_2_1_prompt_enhancer_chain": active_qwen_image_2_1_prompt_enhancer,
|
| 185 |
+
"ming_image_prompt_enhancer_chain": active_ming_image_prompt_enhancer,
|
| 186 |
+
"flux3_prompt_upsampler_chain": active_flux3_prompt_upsampler,
|
| 187 |
+
"moss_image_control_chain": active_moss_image_control,
|
| 188 |
+
"moss_image_edit_chain": active_moss_image_edit,
|
| 189 |
+
"vae_chain": [ui_inputs.get('vae_name')] if (ui_inputs.get('vae_name') and 'vae' in enabled_chains) else [],
|
| 190 |
+
"hidream_o1_smoothing_chain": hidream_o1_smoothing_data,
|
| 191 |
+
"pid_chain": [ui_inputs.get('pid_settings', 'OFF')] if is_pid_enabled else [],
|
| 192 |
+
"scheduler_width": ui_inputs.get('width', 1024),
|
| 193 |
+
"scheduler_height": ui_inputs.get('height', 1024),
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
if isinstance(path_or_components, dict):
|
| 197 |
+
workflow_inputs.update(path_or_components)
|
| 198 |
+
workflow_inputs.update({
|
| 199 |
+
'unet_name': path_or_components.get('unet'),
|
| 200 |
+
'unet_uncond_name': path_or_components.get('unet_uncond'),
|
| 201 |
+
'vae_name': ui_inputs.get('vae_name') or path_or_components.get('vae'),
|
| 202 |
+
'clip_name': path_or_components.get('clip'),
|
| 203 |
+
'clip1_name': path_or_components.get('clip1'),
|
| 204 |
+
'clip2_name': path_or_components.get('clip2'),
|
| 205 |
+
'clip3_name': path_or_components.get('clip3'),
|
| 206 |
+
'clip4_name': path_or_components.get('clip4'),
|
| 207 |
+
'lora_name': path_or_components.get('lora'),
|
| 208 |
+
'qwen35_model': path_or_components.get('qwen35_model'),
|
| 209 |
+
'adapter_name': path_or_components.get('adapter_name'),
|
| 210 |
+
'adapter_strength': path_or_components.get('adapter_strength', 1.0),
|
| 211 |
+
})
|
| 212 |
+
else:
|
| 213 |
+
workflow_inputs['model_name'] = path_or_components
|
| 214 |
+
|
| 215 |
+
if task_type == 'txt2img':
|
| 216 |
+
workflow_inputs['width'] = ui_inputs['width']
|
| 217 |
+
workflow_inputs['height'] = ui_inputs['height']
|
| 218 |
+
|
| 219 |
+
workflow = assembler.assemble(workflow_inputs)
|
| 220 |
+
|
| 221 |
+
post_assembled_models = []
|
| 222 |
+
for node_id, node in workflow.items():
|
| 223 |
+
if not isinstance(node, dict):
|
| 224 |
+
continue
|
| 225 |
+
inputs = node.get('inputs', {})
|
| 226 |
+
for k, v in inputs.items():
|
| 227 |
+
if isinstance(v, str) and v in ALL_FILE_DOWNLOAD_MAP:
|
| 228 |
+
if v not in required_models and v not in post_assembled_models:
|
| 229 |
+
post_assembled_models.append(v)
|
| 230 |
+
if post_assembled_models:
|
| 231 |
+
self.model_manager.ensure_models_downloaded(post_assembled_models, progress=progress)
|
| 232 |
+
|
| 233 |
+
progress(1.0, desc="All models ready. Requesting GPU for generation...")
|
| 234 |
+
|
| 235 |
+
results = self._execute_gpu_logic(
|
| 236 |
+
self._gpu_logic,
|
| 237 |
+
duration=ui_inputs['zero_gpu_duration'],
|
| 238 |
+
default_duration=60,
|
| 239 |
+
task_name=f"ImageGen ({task_type})",
|
| 240 |
+
ui_inputs=ui_inputs,
|
| 241 |
+
loras_string=loras_string,
|
| 242 |
+
workflow=workflow,
|
| 243 |
+
assembler=assembler,
|
| 244 |
+
progress=progress
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
import json
|
| 248 |
+
import glob
|
| 249 |
+
from PIL import PngImagePlugin
|
| 250 |
+
|
| 251 |
+
prompt_json = json.dumps(workflow)
|
| 252 |
+
|
| 253 |
+
out_dir = os.path.abspath(OUTPUT_DIR)
|
| 254 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 255 |
+
|
| 256 |
+
try:
|
| 257 |
+
existing_files = glob.glob(os.path.join(out_dir, "gen_*.png"))
|
| 258 |
+
existing_files.sort(key=os.path.getmtime)
|
| 259 |
+
while len(existing_files) > 50:
|
| 260 |
+
os.remove(existing_files.pop(0))
|
| 261 |
+
except Exception as e:
|
| 262 |
+
print(f"Warning: Failed to cleanup output dir: {e}")
|
| 263 |
+
|
| 264 |
+
final_results = []
|
| 265 |
+
for img in results:
|
| 266 |
+
if not isinstance(img, Image.Image):
|
| 267 |
+
final_results.append(img)
|
| 268 |
+
continue
|
| 269 |
+
|
| 270 |
+
metadata = PngImagePlugin.PngInfo()
|
| 271 |
+
params_string = img.info.get("parameters", "")
|
| 272 |
+
if params_string:
|
| 273 |
+
metadata.add_text("parameters", params_string)
|
| 274 |
+
metadata.add_text("prompt", prompt_json)
|
| 275 |
+
|
| 276 |
+
filename = f"gen_{random.randint(1000000, 9999999)}.png"
|
| 277 |
+
filepath = os.path.join(out_dir, filename)
|
| 278 |
+
img.save(filepath, "PNG", pnginfo=metadata)
|
| 279 |
+
try:
|
| 280 |
+
from core.cloud_storage import queue_image
|
| 281 |
+
queue_image(model_type, model_display_name, filepath, params_string)
|
| 282 |
+
except Exception as _e:
|
| 283 |
+
print(f"Cloud storage skipped: {_e}")
|
| 284 |
+
final_results.append(filepath)
|
| 285 |
+
|
| 286 |
+
results = final_results
|
| 287 |
+
|
| 288 |
+
finally:
|
| 289 |
+
for temp_file in temp_files_to_clean:
|
| 290 |
+
if temp_file and os.path.exists(temp_file):
|
| 291 |
+
os.remove(temp_file)
|
| 292 |
+
print(f"✅ Cleaned up temp file: {temp_file}")
|
| 293 |
+
|
| 294 |
return results
|
core/pipelines/workflow_recipes/_partials/conditioning/fibo-2.0.yaml
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
nodes:
|
| 2 |
+
unet_loader:
|
| 3 |
+
class_type: UNETLoader
|
| 4 |
+
title: "Load Diffusion Model"
|
| 5 |
+
params:
|
| 6 |
+
weight_dtype: "default"
|
| 7 |
+
vae_loader:
|
| 8 |
+
class_type: VAELoader
|
| 9 |
+
title: "Load VAE"
|
| 10 |
+
clip_loader:
|
| 11 |
+
class_type: CLIPLoader
|
| 12 |
+
title: "Load CLIP"
|
| 13 |
+
params:
|
| 14 |
+
type: "fibo2"
|
| 15 |
+
device: "default"
|
| 16 |
+
text_encode_latent:
|
| 17 |
+
class_type: TextEncodeFibo2
|
| 18 |
+
title: "TextEncodeFibo2"
|
| 19 |
+
|
| 20 |
+
connections:
|
| 21 |
+
- from: "unet_loader:0"
|
| 22 |
+
to: "ksampler:model"
|
| 23 |
+
- from: "clip_loader:0"
|
| 24 |
+
to: "text_encode_latent:clip"
|
| 25 |
+
- from: "vae_loader:0"
|
| 26 |
+
to: "vae_decode:vae"
|
| 27 |
+
- from: "vae_loader:0"
|
| 28 |
+
to: "vae_encode:vae"
|
| 29 |
+
- from: "text_encode_latent:0"
|
| 30 |
+
to: "ksampler:positive"
|
| 31 |
+
- from: "text_encode_latent:1"
|
| 32 |
+
to: "ksampler:negative"
|
| 33 |
+
- from: "text_encode_latent:2"
|
| 34 |
+
to: "ksampler:latent_image"
|
| 35 |
+
|
| 36 |
+
dynamic_lora_chains:
|
| 37 |
+
lora_chain:
|
| 38 |
+
template: "LoraLoader"
|
| 39 |
+
output_map:
|
| 40 |
+
"unet_loader:0": "model"
|
| 41 |
+
"clip_loader:0": "clip"
|
| 42 |
+
input_map:
|
| 43 |
+
"model": "model"
|
| 44 |
+
"clip": "clip"
|
| 45 |
+
end_input_map:
|
| 46 |
+
"model": ["ksampler:model"]
|
| 47 |
+
"clip": ["text_encode_latent:clip"]
|
| 48 |
+
|
| 49 |
+
dynamic_controlnet_chains:
|
| 50 |
+
controlnet_chain:
|
| 51 |
+
template: "ControlNetApplyAdvanced"
|
| 52 |
+
ksampler_node: "ksampler"
|
| 53 |
+
vae_source: "vae_loader:0"
|
| 54 |
+
|
| 55 |
+
dynamic_conditioning_chains:
|
| 56 |
+
conditioning_chain:
|
| 57 |
+
ksampler_node: "ksampler"
|
| 58 |
+
clip_source: "clip_loader:0"
|
| 59 |
+
|
| 60 |
+
dynamic_pid_chains:
|
| 61 |
+
pid_chain:
|
| 62 |
+
ksampler_node: "ksampler"
|
| 63 |
+
|
| 64 |
+
dynamic_reference_image_chains:
|
| 65 |
+
reference_image_chain:
|
| 66 |
+
text_encode_node: "text_encode_latent"
|
| 67 |
+
vae_node: "vae_loader"
|
| 68 |
+
|
| 69 |
+
ui_map:
|
| 70 |
+
unet_name: "unet_loader:unet_name"
|
| 71 |
+
vae_name: "vae_loader:vae_name"
|
| 72 |
+
clip_name: "clip_loader:clip_name"
|
| 73 |
+
positive_prompt: "text_encode_latent:prompt"
|
| 74 |
+
negative_prompt: "text_encode_latent:negative_prompt"
|
| 75 |
+
width: "text_encode_latent:width"
|
| 76 |
+
height: "text_encode_latent:height"
|
| 77 |
+
batch_size: "text_encode_latent:batch_size"
|
core/pipelines/workflow_recipes/_partials/conditioning/flux3.yaml
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
nodes:
|
| 2 |
+
unet_loader:
|
| 3 |
+
class_type: UNETLoader
|
| 4 |
+
title: "Load Diffusion Model"
|
| 5 |
+
params:
|
| 6 |
+
weight_dtype: "default"
|
| 7 |
+
clip_loader:
|
| 8 |
+
class_type: CLIPLoader
|
| 9 |
+
title: "Load CLIP"
|
| 10 |
+
params:
|
| 11 |
+
type: "flux3"
|
| 12 |
+
device: "default"
|
| 13 |
+
vae_loader:
|
| 14 |
+
class_type: VAELoader
|
| 15 |
+
title: "Load VAE"
|
| 16 |
+
|
| 17 |
+
pos_prompt:
|
| 18 |
+
class_type: CLIPTextEncode
|
| 19 |
+
title: "CLIP Text Encode (Positive)"
|
| 20 |
+
neg_prompt:
|
| 21 |
+
class_type: CLIPTextEncode
|
| 22 |
+
title: "CLIP Text Encode (Negative)"
|
| 23 |
+
|
| 24 |
+
ksampler:
|
| 25 |
+
class_type: KSampler
|
| 26 |
+
title: "KSampler"
|
| 27 |
+
params:
|
| 28 |
+
denoise: 1.0
|
| 29 |
+
|
| 30 |
+
vae_decode:
|
| 31 |
+
class_type: VAEDecode
|
| 32 |
+
title: "VAE Decode"
|
| 33 |
+
|
| 34 |
+
save_image:
|
| 35 |
+
class_type: SaveImage
|
| 36 |
+
title: "Save Image"
|
| 37 |
+
|
| 38 |
+
connections:
|
| 39 |
+
- from: "unet_loader:0"
|
| 40 |
+
to: "ksampler:model"
|
| 41 |
+
- from: "clip_loader:0"
|
| 42 |
+
to: "pos_prompt:clip"
|
| 43 |
+
- from: "clip_loader:0"
|
| 44 |
+
to: "neg_prompt:clip"
|
| 45 |
+
- from: "vae_loader:0"
|
| 46 |
+
to: "vae_decode:vae"
|
| 47 |
+
- from: "vae_loader:0"
|
| 48 |
+
to: "vae_encode:vae"
|
| 49 |
+
|
| 50 |
+
- from: "pos_prompt:0"
|
| 51 |
+
to: "ksampler:positive"
|
| 52 |
+
- from: "neg_prompt:0"
|
| 53 |
+
to: "ksampler:negative"
|
| 54 |
+
|
| 55 |
+
- from: "latent_source:0"
|
| 56 |
+
to: "ksampler:latent_image"
|
| 57 |
+
|
| 58 |
+
- from: "ksampler:0"
|
| 59 |
+
to: "vae_decode:samples"
|
| 60 |
+
- from: "vae_decode:0"
|
| 61 |
+
to: "save_image:images"
|
| 62 |
+
|
| 63 |
+
dynamic_lora_chains:
|
| 64 |
+
lora_chain:
|
| 65 |
+
template: "LoraLoader"
|
| 66 |
+
output_map:
|
| 67 |
+
"unet_loader:0": "model"
|
| 68 |
+
"clip_loader:0": "clip"
|
| 69 |
+
input_map:
|
| 70 |
+
"model": "model"
|
| 71 |
+
"clip": "clip"
|
| 72 |
+
end_input_map:
|
| 73 |
+
"model": ["ksampler:model"]
|
| 74 |
+
"clip": ["pos_prompt:clip", "neg_prompt:clip"]
|
| 75 |
+
|
| 76 |
+
dynamic_reference_latent_chains:
|
| 77 |
+
reference_latent_chain:
|
| 78 |
+
ksampler_node: "ksampler"
|
| 79 |
+
vae_node: "vae_loader"
|
| 80 |
+
|
| 81 |
+
dynamic_flux3_prompt_upsampler_chains:
|
| 82 |
+
flux3_prompt_upsampler_chain:
|
| 83 |
+
target_node: "pos_prompt"
|
| 84 |
+
|
| 85 |
+
dynamic_pid_chains:
|
| 86 |
+
pid_chain:
|
| 87 |
+
ksampler_node: "ksampler"
|
| 88 |
+
|
| 89 |
+
ui_map:
|
| 90 |
+
unet_name: "unet_loader:unet_name"
|
| 91 |
+
clip_name: "clip_loader:clip_name"
|
| 92 |
+
vae_name: "vae_loader:vae_name"
|
| 93 |
+
|
| 94 |
+
positive_prompt: "pos_prompt:text"
|
| 95 |
+
negative_prompt: "neg_prompt:text"
|
| 96 |
+
|
| 97 |
+
seed: "ksampler:seed"
|
| 98 |
+
steps: "ksampler:steps"
|
| 99 |
+
cfg: "ksampler:cfg"
|
| 100 |
+
sampler_name: "ksampler:sampler_name"
|
| 101 |
+
scheduler: "ksampler:scheduler"
|
| 102 |
+
denoise: "ksampler:denoise"
|
| 103 |
+
|
| 104 |
+
filename_prefix: "save_image:filename_prefix"
|
core/pipelines/workflow_recipes/_partials/conditioning/moss-image.yaml
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
nodes:
|
| 2 |
+
pos_prompt:
|
| 3 |
+
class_type: CLIPTextEncode
|
| 4 |
+
title: "moss-image Positive"
|
| 5 |
+
neg_prompt:
|
| 6 |
+
class_type: CLIPTextEncode
|
| 7 |
+
title: "moss-image Negative"
|
| 8 |
+
unet_loader:
|
| 9 |
+
class_type: UNETLoader
|
| 10 |
+
title: "Load Diffusion Model"
|
| 11 |
+
params:
|
| 12 |
+
weight_dtype: "default"
|
| 13 |
+
slg:
|
| 14 |
+
class_type: SkipLayerGuidanceDiTSimple
|
| 15 |
+
title: "SkipLayerGuidanceDiTSimple"
|
| 16 |
+
params:
|
| 17 |
+
double_layers: ""
|
| 18 |
+
single_layers: "22"
|
| 19 |
+
start_percent: 0.0
|
| 20 |
+
end_percent: 0.5
|
| 21 |
+
clip_loader:
|
| 22 |
+
class_type: CLIPLoader
|
| 23 |
+
title: "Load CLIP"
|
| 24 |
+
params:
|
| 25 |
+
type: "moss_image"
|
| 26 |
+
device: "default"
|
| 27 |
+
vae_loader:
|
| 28 |
+
class_type: VAELoader
|
| 29 |
+
title: "Load VAE"
|
| 30 |
+
|
| 31 |
+
connections:
|
| 32 |
+
- from: "unet_loader:0"
|
| 33 |
+
to: "slg:model"
|
| 34 |
+
- from: "slg:0"
|
| 35 |
+
to: "ksampler:model"
|
| 36 |
+
- from: "clip_loader:0"
|
| 37 |
+
to: "pos_prompt:clip"
|
| 38 |
+
- from: "clip_loader:0"
|
| 39 |
+
to: "neg_prompt:clip"
|
| 40 |
+
- from: "pos_prompt:0"
|
| 41 |
+
to: "ksampler:positive"
|
| 42 |
+
- from: "neg_prompt:0"
|
| 43 |
+
to: "ksampler:negative"
|
| 44 |
+
- from: "vae_loader:0"
|
| 45 |
+
to: "vae_decode:vae"
|
| 46 |
+
- from: "vae_loader:0"
|
| 47 |
+
to: "vae_encode:vae"
|
| 48 |
+
|
| 49 |
+
dynamic_lora_chains:
|
| 50 |
+
lora_chain:
|
| 51 |
+
template: "LoraLoader"
|
| 52 |
+
output_map:
|
| 53 |
+
"unet_loader:0": "model"
|
| 54 |
+
"clip_loader:0": "clip"
|
| 55 |
+
input_map:
|
| 56 |
+
"model": "model"
|
| 57 |
+
"clip": "clip"
|
| 58 |
+
end_input_map:
|
| 59 |
+
"model": ["slg:model"]
|
| 60 |
+
"clip": ["pos_prompt:clip", "neg_prompt:clip"]
|
| 61 |
+
|
| 62 |
+
dynamic_conditioning_chains:
|
| 63 |
+
conditioning_chain:
|
| 64 |
+
ksampler_node: "ksampler"
|
| 65 |
+
clip_source: "clip_loader:0"
|
| 66 |
+
|
| 67 |
+
dynamic_vae_chains:
|
| 68 |
+
vae_chain:
|
| 69 |
+
targets:
|
| 70 |
+
- "vae_decode:vae"
|
| 71 |
+
- "vae_encode:vae"
|
| 72 |
+
|
| 73 |
+
dynamic_moss_image_control_chains:
|
| 74 |
+
moss_image_control_chain:
|
| 75 |
+
ksampler_node: "ksampler"
|
| 76 |
+
vae_loader_node: "vae_loader"
|
| 77 |
+
megapixels: 1.0
|
| 78 |
+
|
| 79 |
+
dynamic_moss_image_edit_chains:
|
| 80 |
+
moss_image_edit_chain:
|
| 81 |
+
ksampler_node: "ksampler"
|
| 82 |
+
clip_loader_node: "clip_loader"
|
| 83 |
+
vae_loader_node: "vae_loader"
|
| 84 |
+
pos_prompt_node: "pos_prompt"
|
| 85 |
+
neg_prompt_node: "neg_prompt"
|
| 86 |
+
megapixels: 1.0
|
| 87 |
+
|
| 88 |
+
dynamic_pid_chains:
|
| 89 |
+
pid_chain:
|
| 90 |
+
ksampler_node: "ksampler"
|
| 91 |
+
|
| 92 |
+
ui_map:
|
| 93 |
+
positive_prompt: "pos_prompt:text"
|
| 94 |
+
negative_prompt: "neg_prompt:text"
|
| 95 |
+
unet_name: "unet_loader:unet_name"
|
| 96 |
+
clip_name: "clip_loader:clip_name"
|
| 97 |
+
vae_name: "vae_loader:vae_name"
|
core/pipelines/workflow_recipes/_partials/conditioning/qwen-image-2.1.yaml
CHANGED
|
@@ -51,7 +51,7 @@ dynamic_lora_chains:
|
|
| 51 |
|
| 52 |
dynamic_controlnet_chains:
|
| 53 |
controlnet_chain:
|
| 54 |
-
template: "
|
| 55 |
ksampler_node: "ksampler"
|
| 56 |
vae_source: "vae_loader:0"
|
| 57 |
|
|
|
|
| 51 |
|
| 52 |
dynamic_controlnet_chains:
|
| 53 |
controlnet_chain:
|
| 54 |
+
template: "ZImageFunControlnet"
|
| 55 |
ksampler_node: "ksampler"
|
| 56 |
vae_source: "vae_loader:0"
|
| 57 |
|
core/settings.py
CHANGED
|
@@ -198,6 +198,7 @@ try:
|
|
| 198 |
MAX_CONDITIONINGS = _constants.get('MAX_CONDITIONINGS', 10)
|
| 199 |
MAX_CONTROLNETS = _constants.get('MAX_CONTROLNETS', 5)
|
| 200 |
MAX_IPADAPTERS = _constants.get('MAX_IPADAPTERS', 5)
|
|
|
|
| 201 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "Hugging Face", "Upload File"])
|
| 202 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
| 203 |
MULTIPLIERS_MAP = _constants.get('MULTIPLIERS_MAP', {})
|
|
@@ -207,7 +208,7 @@ try:
|
|
| 207 |
MODEL_DEFAULTS_CONFIG = load_model_defaults()
|
| 208 |
except Exception as e:
|
| 209 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 210 |
-
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS, MAX_IPADAPTERS = 10, 5, 10, 5, 5
|
| 211 |
LORA_SOURCE_CHOICES = ["Civitai", "Hugging Face", "Upload File"]
|
| 212 |
RESOLUTION_MAP = {}
|
| 213 |
MULTIPLIERS_MAP = {}
|
|
|
|
| 198 |
MAX_CONDITIONINGS = _constants.get('MAX_CONDITIONINGS', 10)
|
| 199 |
MAX_CONTROLNETS = _constants.get('MAX_CONTROLNETS', 5)
|
| 200 |
MAX_IPADAPTERS = _constants.get('MAX_IPADAPTERS', 5)
|
| 201 |
+
MAX_MOSS_IMAGE_EDITS = _constants.get('MAX_MOSS_IMAGE_EDITS', 10)
|
| 202 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "Hugging Face", "Upload File"])
|
| 203 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
| 204 |
MULTIPLIERS_MAP = _constants.get('MULTIPLIERS_MAP', {})
|
|
|
|
| 208 |
MODEL_DEFAULTS_CONFIG = load_model_defaults()
|
| 209 |
except Exception as e:
|
| 210 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 211 |
+
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS, MAX_IPADAPTERS, MAX_MOSS_IMAGE_EDITS = 10, 5, 10, 5, 5, 10
|
| 212 |
LORA_SOURCE_CHOICES = ["Civitai", "Hugging Face", "Upload File"]
|
| 213 |
RESOLUTION_MAP = {}
|
| 214 |
MULTIPLIERS_MAP = {}
|
mcp_tools/common.py
CHANGED
|
@@ -447,6 +447,7 @@ def _execute_imagegen_pipeline(task_id: str, params: dict):
|
|
| 447 |
qwen_image_edit_data = []
|
| 448 |
hidream_o1_reference_data = []
|
| 449 |
sensenova_reference_data = []
|
|
|
|
| 450 |
cond_prompts = []
|
| 451 |
cond_widths = []
|
| 452 |
cond_heights = []
|
|
@@ -628,6 +629,46 @@ def _execute_imagegen_pipeline(task_id: str, params: dict):
|
|
| 628 |
ui_inputs["ming_image_prompt_enhancer_max_length"] = int(item.get("max_length", 4096))
|
| 629 |
if "system_prompt" in item:
|
| 630 |
ui_inputs["ming_image_prompt_enhancer_system_prompt"] = str(item["system_prompt"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 631 |
elif itype == "krea2_identity_edit":
|
| 632 |
img = _parse_image_param(item.get("image"))
|
| 633 |
if img:
|
|
@@ -660,6 +701,16 @@ def _execute_imagegen_pipeline(task_id: str, params: dict):
|
|
| 660 |
img = _parse_image_param(item.get("image"))
|
| 661 |
if img:
|
| 662 |
sensenova_reference_data.append(img)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 663 |
elif itype == "vae":
|
| 664 |
v_source = item.get("source", item.get("vae_source", "Civitai"))
|
| 665 |
v_val = item.get("vae_value", item.get("value", item.get("vae_id", item.get("vae_name", ""))))
|
|
@@ -670,6 +721,7 @@ def _execute_imagegen_pipeline(task_id: str, params: dict):
|
|
| 670 |
if lora_data: ui_inputs["lora_data"] = lora_data
|
| 671 |
if embedding_data: ui_inputs["embedding_data"] = embedding_data
|
| 672 |
if controlnet_data: ui_inputs["controlnet_data"] = controlnet_data
|
|
|
|
| 673 |
|
| 674 |
if ipadapter_images:
|
| 675 |
preset = ipadapter_global_preset or "STANDARD (medium strength)"
|
|
@@ -757,6 +809,67 @@ def _execute_imagegen_pipeline(task_id: str, params: dict):
|
|
| 757 |
else:
|
| 758 |
ui_inputs["ming_image_prompt_enhancer_enable"] = False
|
| 759 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 760 |
_TASKS_DB[task_id]["progress"] = 50
|
| 761 |
|
| 762 |
# Execute Pipeline
|
|
|
|
| 447 |
qwen_image_edit_data = []
|
| 448 |
hidream_o1_reference_data = []
|
| 449 |
sensenova_reference_data = []
|
| 450 |
+
moss_image_edit_data = []
|
| 451 |
cond_prompts = []
|
| 452 |
cond_widths = []
|
| 453 |
cond_heights = []
|
|
|
|
| 629 |
ui_inputs["ming_image_prompt_enhancer_max_length"] = int(item.get("max_length", 4096))
|
| 630 |
if "system_prompt" in item:
|
| 631 |
ui_inputs["ming_image_prompt_enhancer_system_prompt"] = str(item["system_prompt"])
|
| 632 |
+
elif itype == "flux3_prompt_upsampler":
|
| 633 |
+
is_enabled = item.get("enable", item.get("enabled", True))
|
| 634 |
+
if isinstance(is_enabled, str):
|
| 635 |
+
is_enabled = is_enabled.upper() in ("ON", "TRUE", "1")
|
| 636 |
+
ui_inputs["flux3_prompt_upsampler_enable"] = bool(is_enabled)
|
| 637 |
+
is_thinking = item.get("thinking", False)
|
| 638 |
+
if isinstance(is_thinking, str):
|
| 639 |
+
is_thinking = is_thinking.upper() in ("ON", "TRUE", "1")
|
| 640 |
+
ui_inputs["flux3_prompt_upsampler_thinking"] = bool(is_thinking)
|
| 641 |
+
is_bbox_mode = item.get("bbox_mode", False)
|
| 642 |
+
if isinstance(is_bbox_mode, str):
|
| 643 |
+
is_bbox_mode = is_bbox_mode.upper() in ("ON", "TRUE", "1")
|
| 644 |
+
ui_inputs["flux3_prompt_upsampler_bbox_mode"] = bool(is_bbox_mode)
|
| 645 |
+
ui_inputs["flux3_prompt_upsampler_max_length"] = int(item.get("max_length", 3584))
|
| 646 |
+
if "system_prompt" in item:
|
| 647 |
+
ui_inputs["flux3_prompt_upsampler_system_prompt"] = str(item["system_prompt"])
|
| 648 |
+
elif itype == "moss_image_control":
|
| 649 |
+
if "color_map" in item:
|
| 650 |
+
parsed_color_map = _parse_image_param(item["color_map"])
|
| 651 |
+
if parsed_color_map:
|
| 652 |
+
ui_inputs["moss_image_control_color_map"] = parsed_color_map
|
| 653 |
+
if "depth_map" in item:
|
| 654 |
+
parsed_depth_map = _parse_image_param(item["depth_map"])
|
| 655 |
+
if parsed_depth_map:
|
| 656 |
+
ui_inputs["moss_image_control_depth_map"] = parsed_depth_map
|
| 657 |
+
if "strength" in item:
|
| 658 |
+
try:
|
| 659 |
+
ui_inputs["moss_image_control_strength"] = float(item["strength"])
|
| 660 |
+
except (ValueError, TypeError):
|
| 661 |
+
pass
|
| 662 |
+
if "start_percent" in item:
|
| 663 |
+
try:
|
| 664 |
+
ui_inputs["moss_image_control_start_percent"] = float(item["start_percent"])
|
| 665 |
+
except (ValueError, TypeError):
|
| 666 |
+
pass
|
| 667 |
+
if "end_percent" in item:
|
| 668 |
+
try:
|
| 669 |
+
ui_inputs["moss_image_control_end_percent"] = float(item["end_percent"])
|
| 670 |
+
except (ValueError, TypeError):
|
| 671 |
+
pass
|
| 672 |
elif itype == "krea2_identity_edit":
|
| 673 |
img = _parse_image_param(item.get("image"))
|
| 674 |
if img:
|
|
|
|
| 701 |
img = _parse_image_param(item.get("image"))
|
| 702 |
if img:
|
| 703 |
sensenova_reference_data.append(img)
|
| 704 |
+
elif itype == "moss_image_edit":
|
| 705 |
+
if "images" in item and isinstance(item["images"], list):
|
| 706 |
+
for img_item in item["images"]:
|
| 707 |
+
p_img = _parse_image_param(img_item)
|
| 708 |
+
if p_img:
|
| 709 |
+
moss_image_edit_data.append(p_img)
|
| 710 |
+
elif "image" in item:
|
| 711 |
+
p_img = _parse_image_param(item["image"])
|
| 712 |
+
if p_img:
|
| 713 |
+
moss_image_edit_data.append(p_img)
|
| 714 |
elif itype == "vae":
|
| 715 |
v_source = item.get("source", item.get("vae_source", "Civitai"))
|
| 716 |
v_val = item.get("vae_value", item.get("value", item.get("vae_id", item.get("vae_name", ""))))
|
|
|
|
| 721 |
if lora_data: ui_inputs["lora_data"] = lora_data
|
| 722 |
if embedding_data: ui_inputs["embedding_data"] = embedding_data
|
| 723 |
if controlnet_data: ui_inputs["controlnet_data"] = controlnet_data
|
| 724 |
+
if moss_image_edit_data: ui_inputs["moss_image_edit_data"] = moss_image_edit_data
|
| 725 |
|
| 726 |
if ipadapter_images:
|
| 727 |
preset = ipadapter_global_preset or "STANDARD (medium strength)"
|
|
|
|
| 809 |
else:
|
| 810 |
ui_inputs["ming_image_prompt_enhancer_enable"] = False
|
| 811 |
|
| 812 |
+
flux3_pe_val = params.get("flux3_prompt_upsampler") or params.get("flux3_prompt_upsampler_enable")
|
| 813 |
+
if flux3_pe_val is not None:
|
| 814 |
+
if isinstance(flux3_pe_val, bool):
|
| 815 |
+
ui_inputs["flux3_prompt_upsampler_enable"] = flux3_pe_val
|
| 816 |
+
elif isinstance(flux3_pe_val, dict):
|
| 817 |
+
ui_inputs["flux3_prompt_upsampler_enable"] = bool(flux3_pe_val.get("enable", flux3_pe_val.get("enabled", True)))
|
| 818 |
+
ui_inputs["flux3_prompt_upsampler_thinking"] = bool(flux3_pe_val.get("thinking", False))
|
| 819 |
+
ui_inputs["flux3_prompt_upsampler_bbox_mode"] = bool(flux3_pe_val.get("bbox_mode", False))
|
| 820 |
+
ui_inputs["flux3_prompt_upsampler_max_length"] = int(flux3_pe_val.get("max_length", 3584))
|
| 821 |
+
if "system_prompt" in flux3_pe_val:
|
| 822 |
+
ui_inputs["flux3_prompt_upsampler_system_prompt"] = str(flux3_pe_val["system_prompt"])
|
| 823 |
+
elif str(flux3_pe_val).upper() in ("ON", "TRUE", "1"):
|
| 824 |
+
ui_inputs["flux3_prompt_upsampler_enable"] = True
|
| 825 |
+
else:
|
| 826 |
+
ui_inputs["flux3_prompt_upsampler_enable"] = False
|
| 827 |
+
|
| 828 |
+
moss_control_val = params.get("moss_image_control")
|
| 829 |
+
if moss_control_val and isinstance(moss_control_val, dict):
|
| 830 |
+
if "color_map" in moss_control_val:
|
| 831 |
+
parsed_color_map = _parse_image_param(moss_control_val["color_map"])
|
| 832 |
+
if parsed_color_map:
|
| 833 |
+
ui_inputs["moss_image_control_color_map"] = parsed_color_map
|
| 834 |
+
if "depth_map" in moss_control_val:
|
| 835 |
+
parsed_depth_map = _parse_image_param(moss_control_val["depth_map"])
|
| 836 |
+
if parsed_depth_map:
|
| 837 |
+
ui_inputs["moss_image_control_depth_map"] = parsed_depth_map
|
| 838 |
+
if "strength" in moss_control_val:
|
| 839 |
+
try:
|
| 840 |
+
ui_inputs["moss_image_control_strength"] = float(moss_control_val["strength"])
|
| 841 |
+
except (ValueError, TypeError):
|
| 842 |
+
pass
|
| 843 |
+
if "start_percent" in moss_control_val:
|
| 844 |
+
try:
|
| 845 |
+
ui_inputs["moss_image_control_start_percent"] = float(moss_control_val["start_percent"])
|
| 846 |
+
except (ValueError, TypeError):
|
| 847 |
+
pass
|
| 848 |
+
if "end_percent" in moss_control_val:
|
| 849 |
+
try:
|
| 850 |
+
ui_inputs["moss_image_control_end_percent"] = float(moss_control_val["end_percent"])
|
| 851 |
+
except (ValueError, TypeError):
|
| 852 |
+
pass
|
| 853 |
+
|
| 854 |
+
moss_edit_val = params.get("moss_image_edit")
|
| 855 |
+
if moss_edit_val:
|
| 856 |
+
m_imgs = []
|
| 857 |
+
if isinstance(moss_edit_val, dict):
|
| 858 |
+
raw_imgs = moss_edit_val.get("images", [])
|
| 859 |
+
if isinstance(raw_imgs, list):
|
| 860 |
+
for img_item in raw_imgs:
|
| 861 |
+
p_img = _parse_image_param(img_item)
|
| 862 |
+
if p_img: m_imgs.append(p_img)
|
| 863 |
+
elif "image" in moss_edit_val:
|
| 864 |
+
p_img = _parse_image_param(moss_edit_val["image"])
|
| 865 |
+
if p_img: m_imgs.append(p_img)
|
| 866 |
+
elif isinstance(moss_edit_val, list):
|
| 867 |
+
for img_item in moss_edit_val:
|
| 868 |
+
p_img = _parse_image_param(img_item)
|
| 869 |
+
if p_img: m_imgs.append(p_img)
|
| 870 |
+
if m_imgs:
|
| 871 |
+
ui_inputs["moss_image_edit_data"] = m_imgs
|
| 872 |
+
|
| 873 |
_TASKS_DB[task_id]["progress"] = 50
|
| 874 |
|
| 875 |
# Execute Pipeline
|
requirements.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
comfyui-frontend-package==1.
|
| 2 |
-
comfyui-workflow-templates==0.11.
|
| 3 |
-
comfyui-embedded-docs==0.5.
|
| 4 |
torch
|
| 5 |
torchsde
|
| 6 |
torchvision
|
|
@@ -13,7 +13,7 @@ safetensors>=0.4.2
|
|
| 13 |
aiohttp>=3.11.8
|
| 14 |
yarl>=1.18.0
|
| 15 |
pyyaml
|
| 16 |
-
Pillow
|
| 17 |
scipy
|
| 18 |
tqdm
|
| 19 |
psutil
|
|
@@ -21,8 +21,8 @@ alembic
|
|
| 21 |
SQLAlchemy>=2.0.0
|
| 22 |
filelock
|
| 23 |
av>=17.0.0
|
| 24 |
-
comfy-
|
| 25 |
-
comfy-
|
| 26 |
requests
|
| 27 |
simpleeval>=1.0.0
|
| 28 |
blake3
|
|
@@ -34,7 +34,7 @@ pydantic~=2.0
|
|
| 34 |
pydantic-settings~=2.0
|
| 35 |
PyOpenGL>=3.1.8
|
| 36 |
comfy-angle
|
| 37 |
-
|
| 38 |
|
| 39 |
diffusers
|
| 40 |
protobuf
|
|
@@ -42,4 +42,5 @@ insightface
|
|
| 42 |
huggingface-hub
|
| 43 |
imageio
|
| 44 |
spaces
|
| 45 |
-
sageattention @ https://huggingface.co/RioShiina/Sage-Attention-ZeroGPU-Space-Build/resolve/main/sageattention-2.2.0-cp312-cp312-linux_x86_64.whl
|
|
|
|
|
|
| 1 |
+
comfyui-frontend-package==1.57.1
|
| 2 |
+
comfyui-workflow-templates==0.11.96
|
| 3 |
+
comfyui-embedded-docs==0.5.14
|
| 4 |
torch
|
| 5 |
torchsde
|
| 6 |
torchvision
|
|
|
|
| 13 |
aiohttp>=3.11.8
|
| 14 |
yarl>=1.18.0
|
| 15 |
pyyaml
|
| 16 |
+
Pillow>=10.1.0
|
| 17 |
scipy
|
| 18 |
tqdm
|
| 19 |
psutil
|
|
|
|
| 21 |
SQLAlchemy>=2.0.0
|
| 22 |
filelock
|
| 23 |
av>=17.0.0
|
| 24 |
+
comfy-aimdo==0.6.0
|
| 25 |
+
comfy-kitchen==0.2.40
|
| 26 |
requests
|
| 27 |
simpleeval>=1.0.0
|
| 28 |
blake3
|
|
|
|
| 34 |
pydantic-settings~=2.0
|
| 35 |
PyOpenGL>=3.1.8
|
| 36 |
comfy-angle
|
| 37 |
+
|
| 38 |
|
| 39 |
diffusers
|
| 40 |
protobuf
|
|
|
|
| 42 |
huggingface-hub
|
| 43 |
imageio
|
| 44 |
spaces
|
| 45 |
+
sageattention @ https://huggingface.co/RioShiina/Sage-Attention-ZeroGPU-Space-Build/resolve/main/sageattention-2.2.0-cp312-cp312-linux_x86_64.whl
|
| 46 |
+
boto3
|
ui/events/chain_handlers.py
CHANGED
|
@@ -689,4 +689,39 @@ def create_krea2_reference_edit_event_handlers(prefix, ui_components):
|
|
| 689 |
add_outputs = [count_state, add_button, del_button] + ref_rows
|
| 690 |
del_outputs = [count_state, add_button, del_button] + ref_rows + images
|
| 691 |
add_button.click(fn=add_ref_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 692 |
-
del_button.click(fn=del_ref_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
add_outputs = [count_state, add_button, del_button] + ref_rows
|
| 690 |
del_outputs = [count_state, add_button, del_button] + ref_rows + images
|
| 691 |
add_button.click(fn=add_ref_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 692 |
+
del_button.click(fn=del_ref_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
| 693 |
+
|
| 694 |
+
|
| 695 |
+
def create_moss_image_edit_event_handlers(prefix, ui_components):
|
| 696 |
+
ref_rows = ui_components.get(f'moss_image_edit_rows_{prefix}')
|
| 697 |
+
if not ref_rows: return
|
| 698 |
+
count_state = ui_components[f'moss_image_edit_count_state_{prefix}']
|
| 699 |
+
add_button = ui_components[f'add_moss_image_edit_button_{prefix}']
|
| 700 |
+
del_button = ui_components[f'delete_moss_image_edit_button_{prefix}']
|
| 701 |
+
images = ui_components[f'moss_image_edit_images_{prefix}']
|
| 702 |
+
max_count = len(ref_rows)
|
| 703 |
+
|
| 704 |
+
def add_ref_row(c):
|
| 705 |
+
c += 1
|
| 706 |
+
return {
|
| 707 |
+
count_state: c,
|
| 708 |
+
ref_rows[c - 1]: gr.update(visible=True),
|
| 709 |
+
add_button: gr.update(visible=c < max_count),
|
| 710 |
+
del_button: gr.update(visible=True),
|
| 711 |
+
}
|
| 712 |
+
|
| 713 |
+
def del_ref_row(c):
|
| 714 |
+
c -= 1
|
| 715 |
+
return {
|
| 716 |
+
count_state: c,
|
| 717 |
+
ref_rows[c]: gr.update(visible=False),
|
| 718 |
+
images[c]: None,
|
| 719 |
+
add_button: gr.update(visible=True),
|
| 720 |
+
del_button: gr.update(visible=c > 1),
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
add_outputs = [count_state, add_button, del_button] + ref_rows
|
| 724 |
+
del_outputs = [count_state, add_button, del_button] + ref_rows + images
|
| 725 |
+
add_button.click(fn=add_ref_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 726 |
+
del_button.click(fn=del_ref_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
| 727 |
+
|
ui/events/change_handlers.py
CHANGED
|
@@ -15,7 +15,7 @@ from .config_loaders import (
|
|
| 15 |
load_ipadapter_config
|
| 16 |
)
|
| 17 |
|
| 18 |
-
def make_update_fn(m_comp, cat_comp, ar_comp, width_comp, height_comp, cn_types, cn_series, cn_filepaths, ipa_preset, lora_acc, cn_acc, ipa_acc, sd3_ipa_acc, flux1_ipa_acc, style_acc, embed_acc, cond_acc, ref_latent_acc, hidream_o1_ref_acc, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp, pid_acc=None, vae_acc=None, joyai_ref_acc=None, krea2_identity_edit_acc=None, krea2_reference_edit_acc=None, qwen_image_edit_acc=None, ref_img_acc=None, sensenova_ref_acc=None, task_type=None, type_comp=None, qwen_pe_acc=None, ming_pe_acc=None):
|
| 19 |
def update_fn(*args):
|
| 20 |
arch = args[0]
|
| 21 |
category = args[1]
|
|
@@ -86,6 +86,9 @@ def make_update_fn(m_comp, cat_comp, ar_comp, width_comp, height_comp, cn_types,
|
|
| 86 |
if vae_acc: updates[vae_acc] = gr.update(visible=('vae' in enabled_chains))
|
| 87 |
if qwen_pe_acc: updates[qwen_pe_acc] = gr.update(visible=('qwen_image_2_1_prompt_enhancer' in enabled_chains))
|
| 88 |
if ming_pe_acc: updates[ming_pe_acc] = gr.update(visible=('ming_image_prompt_enhancer' in enabled_chains))
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
if ar_comp:
|
| 91 |
res_key = arch_model_type
|
|
@@ -130,7 +133,7 @@ def make_update_fn(m_comp, cat_comp, ar_comp, width_comp, height_comp, cn_types,
|
|
| 130 |
return update_fn
|
| 131 |
|
| 132 |
|
| 133 |
-
def make_model_change_fn(cat_comp_ref, ar_comp, width_comp, height_comp, cn_types, cn_series, cn_filepaths, arch_comp_ref, ipa_preset, lora_acc, cn_acc, ipa_acc, sd3_ipa_acc, flux1_ipa_acc, style_acc, embed_acc, cond_acc, ref_latent_acc, hidream_o1_ref_acc, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp, pid_acc=None, vae_acc=None, joyai_ref_acc=None, krea2_identity_edit_acc=None, krea2_reference_edit_acc=None, qwen_image_edit_acc=None, ref_img_acc=None, sensenova_ref_acc=None, task_type=None, type_comp=None, qwen_pe_acc=None, ming_pe_acc=None):
|
| 134 |
def change_fn(*args):
|
| 135 |
model_name = args[0]
|
| 136 |
idx = 1
|
|
@@ -209,6 +212,9 @@ def make_model_change_fn(cat_comp_ref, ar_comp, width_comp, height_comp, cn_type
|
|
| 209 |
if vae_acc: updates[vae_acc] = gr.update(visible=('vae' in enabled_chains))
|
| 210 |
if qwen_pe_acc: updates[qwen_pe_acc] = gr.update(visible=('qwen_image_2_1_prompt_enhancer' in enabled_chains))
|
| 211 |
if ming_pe_acc: updates[ming_pe_acc] = gr.update(visible=('ming_image_prompt_enhancer' in enabled_chains))
|
|
|
|
|
|
|
|
|
|
| 212 |
|
| 213 |
if ar_comp:
|
| 214 |
res_key = arch_model_type
|
|
|
|
| 15 |
load_ipadapter_config
|
| 16 |
)
|
| 17 |
|
| 18 |
+
def make_update_fn(m_comp, cat_comp, ar_comp, width_comp, height_comp, cn_types, cn_series, cn_filepaths, ipa_preset, lora_acc, cn_acc, ipa_acc, sd3_ipa_acc, flux1_ipa_acc, style_acc, embed_acc, cond_acc, ref_latent_acc, hidream_o1_ref_acc, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp, pid_acc=None, vae_acc=None, joyai_ref_acc=None, krea2_identity_edit_acc=None, krea2_reference_edit_acc=None, qwen_image_edit_acc=None, ref_img_acc=None, sensenova_ref_acc=None, task_type=None, type_comp=None, qwen_pe_acc=None, ming_pe_acc=None, flux3_pe_acc=None, moss_image_control_acc=None, moss_image_edit_acc=None):
|
| 19 |
def update_fn(*args):
|
| 20 |
arch = args[0]
|
| 21 |
category = args[1]
|
|
|
|
| 86 |
if vae_acc: updates[vae_acc] = gr.update(visible=('vae' in enabled_chains))
|
| 87 |
if qwen_pe_acc: updates[qwen_pe_acc] = gr.update(visible=('qwen_image_2_1_prompt_enhancer' in enabled_chains))
|
| 88 |
if ming_pe_acc: updates[ming_pe_acc] = gr.update(visible=('ming_image_prompt_enhancer' in enabled_chains))
|
| 89 |
+
if flux3_pe_acc: updates[flux3_pe_acc] = gr.update(visible=('flux3_prompt_upsampler' in enabled_chains))
|
| 90 |
+
if moss_image_control_acc: updates[moss_image_control_acc] = gr.update(visible=('moss_image_control' in enabled_chains))
|
| 91 |
+
if moss_image_edit_acc: updates[moss_image_edit_acc] = gr.update(visible=('moss_image_edit' in enabled_chains))
|
| 92 |
|
| 93 |
if ar_comp:
|
| 94 |
res_key = arch_model_type
|
|
|
|
| 133 |
return update_fn
|
| 134 |
|
| 135 |
|
| 136 |
+
def make_model_change_fn(cat_comp_ref, ar_comp, width_comp, height_comp, cn_types, cn_series, cn_filepaths, arch_comp_ref, ipa_preset, lora_acc, cn_acc, ipa_acc, sd3_ipa_acc, flux1_ipa_acc, style_acc, embed_acc, cond_acc, ref_latent_acc, hidream_o1_ref_acc, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp, pid_acc=None, vae_acc=None, joyai_ref_acc=None, krea2_identity_edit_acc=None, krea2_reference_edit_acc=None, qwen_image_edit_acc=None, ref_img_acc=None, sensenova_ref_acc=None, task_type=None, type_comp=None, qwen_pe_acc=None, ming_pe_acc=None, flux3_pe_acc=None, moss_image_control_acc=None, moss_image_edit_acc=None):
|
| 137 |
def change_fn(*args):
|
| 138 |
model_name = args[0]
|
| 139 |
idx = 1
|
|
|
|
| 212 |
if vae_acc: updates[vae_acc] = gr.update(visible=('vae' in enabled_chains))
|
| 213 |
if qwen_pe_acc: updates[qwen_pe_acc] = gr.update(visible=('qwen_image_2_1_prompt_enhancer' in enabled_chains))
|
| 214 |
if ming_pe_acc: updates[ming_pe_acc] = gr.update(visible=('ming_image_prompt_enhancer' in enabled_chains))
|
| 215 |
+
if flux3_pe_acc: updates[flux3_pe_acc] = gr.update(visible=('flux3_prompt_upsampler' in enabled_chains))
|
| 216 |
+
if moss_image_control_acc: updates[moss_image_control_acc] = gr.update(visible=('moss_image_control' in enabled_chains))
|
| 217 |
+
if moss_image_edit_acc: updates[moss_image_edit_acc] = gr.update(visible=('moss_image_edit' in enabled_chains))
|
| 218 |
|
| 219 |
if ar_comp:
|
| 220 |
res_key = arch_model_type
|
ui/events/main.py
CHANGED
|
@@ -15,7 +15,8 @@ from .chain_handlers import (
|
|
| 15 |
create_krea2_identity_edit_event_handlers,
|
| 16 |
create_krea2_reference_edit_event_handlers,
|
| 17 |
create_qwen_image_edit_event_handlers,
|
| 18 |
-
create_reference_image_event_handlers
|
|
|
|
| 19 |
)
|
| 20 |
from .change_handlers import (
|
| 21 |
make_update_fn,
|
|
@@ -64,6 +65,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 64 |
vae_accordion = ui_components.get(f'vae_accordion_{prefix}')
|
| 65 |
qwen_pe_accordion = ui_components.get(f'qwen_image_2_1_prompt_enhancer_accordion_{prefix}')
|
| 66 |
ming_pe_accordion = ui_components.get(f'ming_image_prompt_enhancer_accordion_{prefix}')
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
ipa_preset_list = ui_components.get(f'ipadapter_final_preset_{prefix}')
|
| 69 |
|
|
@@ -101,6 +105,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 101 |
if vae_accordion: outputs.append(vae_accordion)
|
| 102 |
if qwen_pe_accordion: outputs.append(qwen_pe_accordion)
|
| 103 |
if ming_pe_accordion: outputs.append(ming_pe_accordion)
|
|
|
|
|
|
|
|
|
|
| 104 |
if ipa_preset_list: outputs.append(ipa_preset_list)
|
| 105 |
|
| 106 |
outputs.extend(valid_extra_comps)
|
|
@@ -111,7 +118,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 111 |
ipa_preset_list, lora_accordion, cn_accordion, ipa_accordion, sd3_ipa_accordion, flux1_ipa_accordion, style_accordion, embedding_accordion, conditioning_accordion,
|
| 112 |
ref_latent_accordion, hidream_o1_ref_accordion, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp,
|
| 113 |
pid_acc=pid_accordion, vae_acc=vae_accordion, joyai_ref_acc=joyai_ref_accordion, krea2_identity_edit_acc=krea2_identity_edit_accordion, krea2_reference_edit_acc=krea2_reference_edit_accordion, qwen_image_edit_acc=qwen_image_edit_accordion, ref_img_acc=ref_img_accordion, sensenova_ref_acc=sensenova_ref_accordion,
|
| 114 |
-
task_type=task_type, type_comp=type_comp, qwen_pe_acc=qwen_pe_accordion, ming_pe_acc=ming_pe_accordion
|
|
|
|
|
|
|
| 115 |
)
|
| 116 |
inputs = [arch_comp, cat_comp]
|
| 117 |
if aspect_ratio_comp:
|
|
@@ -147,6 +156,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 147 |
if vae_accordion: outputs2.append(vae_accordion)
|
| 148 |
if qwen_pe_accordion: outputs2.append(qwen_pe_accordion)
|
| 149 |
if ming_pe_accordion: outputs2.append(ming_pe_accordion)
|
|
|
|
|
|
|
|
|
|
| 150 |
if ipa_preset_list: outputs2.append(ipa_preset_list)
|
| 151 |
|
| 152 |
outputs2.extend(valid_extra_comps)
|
|
@@ -162,7 +174,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 162 |
arch_comp, ipa_preset_list, lora_accordion, cn_accordion, ipa_accordion, sd3_ipa_accordion, flux1_ipa_accordion, style_accordion, embedding_accordion, conditioning_accordion,
|
| 163 |
ref_latent_accordion, hidream_o1_ref_accordion, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp,
|
| 164 |
pid_acc=pid_accordion, vae_acc=vae_accordion, joyai_ref_acc=joyai_ref_accordion, krea2_identity_edit_acc=krea2_identity_edit_accordion, krea2_reference_edit_acc=krea2_reference_edit_accordion, qwen_image_edit_acc=qwen_image_edit_accordion, ref_img_acc=ref_img_accordion, sensenova_ref_acc=sensenova_ref_accordion,
|
| 165 |
-
task_type=task_type, type_comp=type_comp, qwen_pe_acc=qwen_pe_accordion, ming_pe_acc=ming_pe_accordion
|
|
|
|
|
|
|
| 166 |
)
|
| 167 |
if type_comp:
|
| 168 |
inputs2.append(type_comp)
|
|
@@ -184,6 +198,7 @@ def attach_event_handlers(ui_components, demo):
|
|
| 184 |
create_krea2_reference_edit_event_handlers(prefix, ui_components)
|
| 185 |
create_qwen_image_edit_event_handlers(prefix, ui_components)
|
| 186 |
create_reference_image_event_handlers(prefix, ui_components)
|
|
|
|
| 187 |
create_run_event(prefix, task_type, ui_components)
|
| 188 |
|
| 189 |
# Dynamic Type toggle event for unified imagegen UI
|
|
@@ -252,6 +267,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 252 |
f'reference_image_accordion_{prefix}': 'reference_image',
|
| 253 |
f'qwen_image_2_1_prompt_enhancer_accordion_{prefix}': 'qwen_image_2_1_prompt_enhancer',
|
| 254 |
f'ming_image_prompt_enhancer_accordion_{prefix}': 'ming_image_prompt_enhancer',
|
|
|
|
|
|
|
|
|
|
| 255 |
f'pid_accordion_{prefix}': 'pid',
|
| 256 |
f'vae_accordion_{prefix}': 'vae',
|
| 257 |
}
|
|
@@ -291,6 +309,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 291 |
'reference_image_accordion_imagegen',
|
| 292 |
'qwen_image_2_1_prompt_enhancer_accordion_imagegen',
|
| 293 |
'ming_image_prompt_enhancer_accordion_imagegen',
|
|
|
|
|
|
|
|
|
|
| 294 |
'pid_accordion_imagegen', 'vae_accordion_imagegen',
|
| 295 |
]
|
| 296 |
for key in accordion_keys:
|
|
|
|
| 15 |
create_krea2_identity_edit_event_handlers,
|
| 16 |
create_krea2_reference_edit_event_handlers,
|
| 17 |
create_qwen_image_edit_event_handlers,
|
| 18 |
+
create_reference_image_event_handlers,
|
| 19 |
+
create_moss_image_edit_event_handlers
|
| 20 |
)
|
| 21 |
from .change_handlers import (
|
| 22 |
make_update_fn,
|
|
|
|
| 65 |
vae_accordion = ui_components.get(f'vae_accordion_{prefix}')
|
| 66 |
qwen_pe_accordion = ui_components.get(f'qwen_image_2_1_prompt_enhancer_accordion_{prefix}')
|
| 67 |
ming_pe_accordion = ui_components.get(f'ming_image_prompt_enhancer_accordion_{prefix}')
|
| 68 |
+
flux3_pe_accordion = ui_components.get(f'flux3_prompt_upsampler_accordion_{prefix}')
|
| 69 |
+
moss_control_accordion = ui_components.get(f'moss_image_control_accordion_{prefix}')
|
| 70 |
+
moss_edit_accordion = ui_components.get(f'moss_image_edit_accordion_{prefix}')
|
| 71 |
|
| 72 |
ipa_preset_list = ui_components.get(f'ipadapter_final_preset_{prefix}')
|
| 73 |
|
|
|
|
| 105 |
if vae_accordion: outputs.append(vae_accordion)
|
| 106 |
if qwen_pe_accordion: outputs.append(qwen_pe_accordion)
|
| 107 |
if ming_pe_accordion: outputs.append(ming_pe_accordion)
|
| 108 |
+
if flux3_pe_accordion: outputs.append(flux3_pe_accordion)
|
| 109 |
+
if moss_control_accordion: outputs.append(moss_control_accordion)
|
| 110 |
+
if moss_edit_accordion: outputs.append(moss_edit_accordion)
|
| 111 |
if ipa_preset_list: outputs.append(ipa_preset_list)
|
| 112 |
|
| 113 |
outputs.extend(valid_extra_comps)
|
|
|
|
| 118 |
ipa_preset_list, lora_accordion, cn_accordion, ipa_accordion, sd3_ipa_accordion, flux1_ipa_accordion, style_accordion, embedding_accordion, conditioning_accordion,
|
| 119 |
ref_latent_accordion, hidream_o1_ref_accordion, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp,
|
| 120 |
pid_acc=pid_accordion, vae_acc=vae_accordion, joyai_ref_acc=joyai_ref_accordion, krea2_identity_edit_acc=krea2_identity_edit_accordion, krea2_reference_edit_acc=krea2_reference_edit_accordion, qwen_image_edit_acc=qwen_image_edit_accordion, ref_img_acc=ref_img_accordion, sensenova_ref_acc=sensenova_ref_accordion,
|
| 121 |
+
task_type=task_type, type_comp=type_comp, qwen_pe_acc=qwen_pe_accordion, ming_pe_acc=ming_pe_accordion, flux3_pe_acc=flux3_pe_accordion,
|
| 122 |
+
moss_image_control_acc=moss_control_accordion,
|
| 123 |
+
moss_image_edit_acc=moss_edit_accordion
|
| 124 |
)
|
| 125 |
inputs = [arch_comp, cat_comp]
|
| 126 |
if aspect_ratio_comp:
|
|
|
|
| 156 |
if vae_accordion: outputs2.append(vae_accordion)
|
| 157 |
if qwen_pe_accordion: outputs2.append(qwen_pe_accordion)
|
| 158 |
if ming_pe_accordion: outputs2.append(ming_pe_accordion)
|
| 159 |
+
if flux3_pe_accordion: outputs2.append(flux3_pe_accordion)
|
| 160 |
+
if moss_control_accordion: outputs2.append(moss_control_accordion)
|
| 161 |
+
if moss_edit_accordion: outputs2.append(moss_edit_accordion)
|
| 162 |
if ipa_preset_list: outputs2.append(ipa_preset_list)
|
| 163 |
|
| 164 |
outputs2.extend(valid_extra_comps)
|
|
|
|
| 174 |
arch_comp, ipa_preset_list, lora_accordion, cn_accordion, ipa_accordion, sd3_ipa_accordion, flux1_ipa_accordion, style_accordion, embedding_accordion, conditioning_accordion,
|
| 175 |
ref_latent_accordion, hidream_o1_ref_accordion, prompt_comp, neg_prompt_comp, steps_comp, cfg_comp, sampler_comp, scheduler_comp,
|
| 176 |
pid_acc=pid_accordion, vae_acc=vae_accordion, joyai_ref_acc=joyai_ref_accordion, krea2_identity_edit_acc=krea2_identity_edit_accordion, krea2_reference_edit_acc=krea2_reference_edit_accordion, qwen_image_edit_acc=qwen_image_edit_accordion, ref_img_acc=ref_img_accordion, sensenova_ref_acc=sensenova_ref_accordion,
|
| 177 |
+
task_type=task_type, type_comp=type_comp, qwen_pe_acc=qwen_pe_accordion, ming_pe_acc=ming_pe_accordion, flux3_pe_acc=flux3_pe_accordion,
|
| 178 |
+
moss_image_control_acc=moss_control_accordion,
|
| 179 |
+
moss_image_edit_acc=moss_edit_accordion
|
| 180 |
)
|
| 181 |
if type_comp:
|
| 182 |
inputs2.append(type_comp)
|
|
|
|
| 198 |
create_krea2_reference_edit_event_handlers(prefix, ui_components)
|
| 199 |
create_qwen_image_edit_event_handlers(prefix, ui_components)
|
| 200 |
create_reference_image_event_handlers(prefix, ui_components)
|
| 201 |
+
create_moss_image_edit_event_handlers(prefix, ui_components)
|
| 202 |
create_run_event(prefix, task_type, ui_components)
|
| 203 |
|
| 204 |
# Dynamic Type toggle event for unified imagegen UI
|
|
|
|
| 267 |
f'reference_image_accordion_{prefix}': 'reference_image',
|
| 268 |
f'qwen_image_2_1_prompt_enhancer_accordion_{prefix}': 'qwen_image_2_1_prompt_enhancer',
|
| 269 |
f'ming_image_prompt_enhancer_accordion_{prefix}': 'ming_image_prompt_enhancer',
|
| 270 |
+
f'flux3_prompt_upsampler_accordion_{prefix}': 'flux3_prompt_upsampler',
|
| 271 |
+
f'moss_image_control_accordion_{prefix}': 'moss_image_control',
|
| 272 |
+
f'moss_image_edit_accordion_{prefix}': 'moss_image_edit',
|
| 273 |
f'pid_accordion_{prefix}': 'pid',
|
| 274 |
f'vae_accordion_{prefix}': 'vae',
|
| 275 |
}
|
|
|
|
| 309 |
'reference_image_accordion_imagegen',
|
| 310 |
'qwen_image_2_1_prompt_enhancer_accordion_imagegen',
|
| 311 |
'ming_image_prompt_enhancer_accordion_imagegen',
|
| 312 |
+
'flux3_prompt_upsampler_accordion_imagegen',
|
| 313 |
+
'moss_image_control_accordion_imagegen',
|
| 314 |
+
'moss_image_edit_accordion_imagegen',
|
| 315 |
'pid_accordion_imagegen', 'vae_accordion_imagegen',
|
| 316 |
]
|
| 317 |
for key in accordion_keys:
|
ui/events/run_handlers.py
CHANGED
|
@@ -53,6 +53,24 @@ def create_run_event(prefix: str, task_type: str = None, ui_components: dict = N
|
|
| 53 |
if ui_components.get(f'ming_image_prompt_enhancer_system_prompt_{prefix}'):
|
| 54 |
run_inputs_map['ming_image_prompt_enhancer_system_prompt'] = ui_components[f'ming_image_prompt_enhancer_system_prompt_{prefix}']
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
# Generic / Txt2Img inputs
|
| 57 |
if ui_components.get(f'width_{prefix}') or ui_components.get(f'{prefix}_width'):
|
| 58 |
run_inputs_map['width'] = ui_components.get(f'width_{prefix}') or ui_components.get(f'{prefix}_width')
|
|
@@ -101,6 +119,7 @@ def create_run_event(prefix: str, task_type: str = None, ui_components: dict = N
|
|
| 101 |
krea2_reference_edit_data_components = ui_components.get(f'all_krea2_reference_edit_components_flat_{prefix}', [])
|
| 102 |
qwen_image_edit_data_components = ui_components.get(f'all_qwen_image_edit_components_flat_{prefix}', [])
|
| 103 |
reference_image_data_components = ui_components.get(f'all_reference_image_components_flat_{prefix}', [])
|
|
|
|
| 104 |
|
| 105 |
run_inputs_map['vae_source'] = ui_components.get(f'vae_source_{prefix}')
|
| 106 |
run_inputs_map['vae_id'] = ui_components.get(f'vae_id_{prefix}')
|
|
@@ -111,7 +130,8 @@ def create_run_event(prefix: str, task_type: str = None, ui_components: dict = N
|
|
| 111 |
all_chains = [
|
| 112 |
lora_data_components, controlnet_data_components, ipadapter_data_components,
|
| 113 |
sd3_ipadapter_data_components, flux1_ipadapter_data_components, style_data_components,
|
| 114 |
-
embedding_data_components, conditioning_data_components, reference_latent_data_components, hidream_o1_reference_data_components, sensenova_reference_data_components, joyai_reference_data_components, krea2_identity_edit_data_components, krea2_reference_edit_data_components, qwen_image_edit_data_components, reference_image_data_components
|
|
|
|
| 115 |
]
|
| 116 |
for chain in all_chains:
|
| 117 |
if chain:
|
|
@@ -144,6 +164,7 @@ def create_run_event(prefix: str, task_type: str = None, ui_components: dict = N
|
|
| 144 |
assign_chain_data('krea2_reference_edit_data', krea2_reference_edit_data_components)
|
| 145 |
assign_chain_data('qwen_image_edit_data', qwen_image_edit_data_components)
|
| 146 |
assign_chain_data('reference_image_data', reference_image_data_components)
|
|
|
|
| 147 |
|
| 148 |
if 'ui_type' in ui_dict:
|
| 149 |
ui_dict['task_type'] = TYPE_TASK_MAP.get(ui_dict['ui_type'], 'txt2img')
|
|
|
|
| 53 |
if ui_components.get(f'ming_image_prompt_enhancer_system_prompt_{prefix}'):
|
| 54 |
run_inputs_map['ming_image_prompt_enhancer_system_prompt'] = ui_components[f'ming_image_prompt_enhancer_system_prompt_{prefix}']
|
| 55 |
|
| 56 |
+
if ui_components.get(f'flux3_prompt_upsampler_enable_{prefix}'):
|
| 57 |
+
run_inputs_map['flux3_prompt_upsampler_enable'] = ui_components[f'flux3_prompt_upsampler_enable_{prefix}']
|
| 58 |
+
if ui_components.get(f'flux3_prompt_upsampler_thinking_{prefix}'):
|
| 59 |
+
run_inputs_map['flux3_prompt_upsampler_thinking'] = ui_components[f'flux3_prompt_upsampler_thinking_{prefix}']
|
| 60 |
+
if ui_components.get(f'flux3_prompt_upsampler_bbox_mode_{prefix}'):
|
| 61 |
+
run_inputs_map['flux3_prompt_upsampler_bbox_mode'] = ui_components[f'flux3_prompt_upsampler_bbox_mode_{prefix}']
|
| 62 |
+
if ui_components.get(f'flux3_prompt_upsampler_max_length_{prefix}'):
|
| 63 |
+
run_inputs_map['flux3_prompt_upsampler_max_length'] = ui_components[f'flux3_prompt_upsampler_max_length_{prefix}']
|
| 64 |
+
|
| 65 |
+
if ui_components.get(f'moss_image_control_color_map_{prefix}'):
|
| 66 |
+
run_inputs_map['moss_image_control_color_map'] = ui_components[f'moss_image_control_color_map_{prefix}']
|
| 67 |
+
if ui_components.get(f'moss_image_control_depth_map_{prefix}'):
|
| 68 |
+
run_inputs_map['moss_image_control_depth_map'] = ui_components[f'moss_image_control_depth_map_{prefix}']
|
| 69 |
+
if ui_components.get(f'moss_image_control_strength_{prefix}'):
|
| 70 |
+
run_inputs_map['moss_image_control_strength'] = ui_components[f'moss_image_control_strength_{prefix}']
|
| 71 |
+
if ui_components.get(f'moss_image_control_end_percent_{prefix}'):
|
| 72 |
+
run_inputs_map['moss_image_control_end_percent'] = ui_components[f'moss_image_control_end_percent_{prefix}']
|
| 73 |
+
|
| 74 |
# Generic / Txt2Img inputs
|
| 75 |
if ui_components.get(f'width_{prefix}') or ui_components.get(f'{prefix}_width'):
|
| 76 |
run_inputs_map['width'] = ui_components.get(f'width_{prefix}') or ui_components.get(f'{prefix}_width')
|
|
|
|
| 119 |
krea2_reference_edit_data_components = ui_components.get(f'all_krea2_reference_edit_components_flat_{prefix}', [])
|
| 120 |
qwen_image_edit_data_components = ui_components.get(f'all_qwen_image_edit_components_flat_{prefix}', [])
|
| 121 |
reference_image_data_components = ui_components.get(f'all_reference_image_components_flat_{prefix}', [])
|
| 122 |
+
moss_image_edit_data_components = ui_components.get(f'all_moss_image_edit_components_flat_{prefix}', [])
|
| 123 |
|
| 124 |
run_inputs_map['vae_source'] = ui_components.get(f'vae_source_{prefix}')
|
| 125 |
run_inputs_map['vae_id'] = ui_components.get(f'vae_id_{prefix}')
|
|
|
|
| 130 |
all_chains = [
|
| 131 |
lora_data_components, controlnet_data_components, ipadapter_data_components,
|
| 132 |
sd3_ipadapter_data_components, flux1_ipadapter_data_components, style_data_components,
|
| 133 |
+
embedding_data_components, conditioning_data_components, reference_latent_data_components, hidream_o1_reference_data_components, sensenova_reference_data_components, joyai_reference_data_components, krea2_identity_edit_data_components, krea2_reference_edit_data_components, qwen_image_edit_data_components, reference_image_data_components,
|
| 134 |
+
moss_image_edit_data_components
|
| 135 |
]
|
| 136 |
for chain in all_chains:
|
| 137 |
if chain:
|
|
|
|
| 164 |
assign_chain_data('krea2_reference_edit_data', krea2_reference_edit_data_components)
|
| 165 |
assign_chain_data('qwen_image_edit_data', qwen_image_edit_data_components)
|
| 166 |
assign_chain_data('reference_image_data', reference_image_data_components)
|
| 167 |
+
assign_chain_data('moss_image_edit_data', moss_image_edit_data_components)
|
| 168 |
|
| 169 |
if 'ui_type' in ui_dict:
|
| 170 |
ui_dict['task_type'] = TYPE_TASK_MAP.get(ui_dict['ui_type'], 'txt2img')
|
ui/imagegen_ui.py
CHANGED
|
@@ -9,7 +9,9 @@ from .shared.ui_components import (
|
|
| 9 |
create_model_architecture_filter_ui, create_category_filter_ui,
|
| 10 |
create_sd3_ipadapter_ui, create_flux1_ipadapter_ui, create_style_ui,
|
| 11 |
create_reference_latent_ui, create_hidream_o1_reference_ui, create_sensenova_reference_ui, create_joyai_reference_ui, create_reference_image_ui, create_krea2_identity_edit_ui, create_krea2_reference_edit_ui, create_qwen_image_edit_ui,
|
| 12 |
-
create_pid_ui, create_qwen_image_2_1_prompt_enhancer_ui, create_ming_image_prompt_enhancer_ui
|
|
|
|
|
|
|
| 13 |
)
|
| 14 |
|
| 15 |
default_vals = MODEL_DEFAULTS_CONFIG.get('Default', {})
|
|
@@ -162,7 +164,7 @@ def create_ui():
|
|
| 162 |
components[f'cfg_{prefix}'] = gr.Slider(label="CFG Scale", minimum=1.0, maximum=20.0, step=0.1, value=DEFAULT_CFG)
|
| 163 |
with gr.Row():
|
| 164 |
components[f'seed_{prefix}'] = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
|
| 165 |
-
components[f'batch_size_{prefix}'] = gr.Slider(label="Batch Size", minimum=1, maximum=
|
| 166 |
with gr.Row():
|
| 167 |
components[f'zero_gpu_{prefix}'] = gr.Number(label="ZeroGPU Duration (s)", value=60, placeholder="Default: 60s", info="Optional: Set how long to reserve the GPU. Longer jobs may need more time.")
|
| 168 |
|
|
@@ -189,7 +191,10 @@ def create_ui():
|
|
| 189 |
components.update(create_reference_image_ui(prefix))
|
| 190 |
components.update(create_qwen_image_2_1_prompt_enhancer_ui(prefix))
|
| 191 |
components.update(create_ming_image_prompt_enhancer_ui(prefix))
|
|
|
|
| 192 |
components.update(create_vae_override_ui(prefix))
|
|
|
|
|
|
|
| 193 |
components.update(create_pid_ui(prefix))
|
| 194 |
components[f'accordion_wrapper_{prefix}'] = accordion_wrapper
|
| 195 |
|
|
|
|
| 9 |
create_model_architecture_filter_ui, create_category_filter_ui,
|
| 10 |
create_sd3_ipadapter_ui, create_flux1_ipadapter_ui, create_style_ui,
|
| 11 |
create_reference_latent_ui, create_hidream_o1_reference_ui, create_sensenova_reference_ui, create_joyai_reference_ui, create_reference_image_ui, create_krea2_identity_edit_ui, create_krea2_reference_edit_ui, create_qwen_image_edit_ui,
|
| 12 |
+
create_pid_ui, create_qwen_image_2_1_prompt_enhancer_ui, create_ming_image_prompt_enhancer_ui,
|
| 13 |
+
create_flux3_prompt_upsampler_ui, create_moss_image_control_ui,
|
| 14 |
+
create_moss_image_edit_ui
|
| 15 |
)
|
| 16 |
|
| 17 |
default_vals = MODEL_DEFAULTS_CONFIG.get('Default', {})
|
|
|
|
| 164 |
components[f'cfg_{prefix}'] = gr.Slider(label="CFG Scale", minimum=1.0, maximum=20.0, step=0.1, value=DEFAULT_CFG)
|
| 165 |
with gr.Row():
|
| 166 |
components[f'seed_{prefix}'] = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
|
| 167 |
+
components[f'batch_size_{prefix}'] = gr.Slider(label="Batch Size", minimum=1, maximum=16, step=1, value=1)
|
| 168 |
with gr.Row():
|
| 169 |
components[f'zero_gpu_{prefix}'] = gr.Number(label="ZeroGPU Duration (s)", value=60, placeholder="Default: 60s", info="Optional: Set how long to reserve the GPU. Longer jobs may need more time.")
|
| 170 |
|
|
|
|
| 191 |
components.update(create_reference_image_ui(prefix))
|
| 192 |
components.update(create_qwen_image_2_1_prompt_enhancer_ui(prefix))
|
| 193 |
components.update(create_ming_image_prompt_enhancer_ui(prefix))
|
| 194 |
+
components.update(create_flux3_prompt_upsampler_ui(prefix))
|
| 195 |
components.update(create_vae_override_ui(prefix))
|
| 196 |
+
components.update(create_moss_image_control_ui(prefix))
|
| 197 |
+
components.update(create_moss_image_edit_ui(prefix))
|
| 198 |
components.update(create_pid_ui(prefix))
|
| 199 |
components[f'accordion_wrapper_{prefix}'] = accordion_wrapper
|
| 200 |
|
ui/layout.py
CHANGED
|
@@ -1,43 +1,44 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import gradio as gr
|
| 3 |
-
from core.settings import *
|
| 4 |
-
|
| 5 |
-
from .imagegen_ui import create_ui as create_imagegen_ui
|
| 6 |
-
|
| 7 |
-
MAX_DYNAMIC_CONTROLS = 10
|
| 8 |
-
|
| 9 |
-
def build_ui(event_handler_function):
|
| 10 |
-
ui_components = {}
|
| 11 |
-
|
| 12 |
-
with gr.Blocks() as demo:
|
| 13 |
-
gr.Markdown("# ImageGen")
|
| 14 |
-
gr.Markdown(
|
| 15 |
-
"This demo is a streamlined version of the [Comfy web UI](https://github.com/RioShiina47/comfy-webui)'s [ImageGen](https://huggingface.co/spaces/RioShiina/ImageGen) functionality. Support [High-Level MCP](https://rioshiina-imagegen.hf.space/gradio_api/mcp/)."
|
| 16 |
-
)
|
| 17 |
-
ui_components.update(create_imagegen_ui())
|
| 18 |
-
|
| 19 |
-
gr.Markdown("<div style='text-align: left; margin-top: 20px;'>Made by RioShiina with ❤️<br><a href='https://github.com/RioShiina47' target='_blank'>GitHub</a> | <a href='https://huggingface.co/RioShiina' target='_blank'>Hugging Face</a> | <a href='https://civitai.com/user/RioShiina' target='_blank'>Civitai</a></div>")
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
mcp.
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
fn.
|
| 42 |
-
|
|
|
|
| 43 |
return demo
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
from core.settings import *
|
| 4 |
+
|
| 5 |
+
from .imagegen_ui import create_ui as create_imagegen_ui
|
| 6 |
+
|
| 7 |
+
MAX_DYNAMIC_CONTROLS = 10
|
| 8 |
+
|
| 9 |
+
def build_ui(event_handler_function):
|
| 10 |
+
ui_components = {}
|
| 11 |
+
|
| 12 |
+
with gr.Blocks() as demo:
|
| 13 |
+
gr.Markdown("# ImageGen")
|
| 14 |
+
gr.Markdown(
|
| 15 |
+
"This demo is a streamlined version of the [Comfy web UI](https://github.com/RioShiina47/comfy-webui)'s [ImageGen](https://huggingface.co/spaces/RioShiina/ImageGen) functionality. Support [High-Level MCP](https://rioshiina-imagegen.hf.space/gradio_api/mcp/)."
|
| 16 |
+
)
|
| 17 |
+
ui_components.update(create_imagegen_ui())
|
| 18 |
+
|
| 19 |
+
gr.Markdown("<div style='text-align: left; margin-top: 20px;'>Made by RioShiina with ❤️<br><a href='https://github.com/RioShiina47' target='_blank'>GitHub</a> | <a href='https://huggingface.co/RioShiina' target='_blank'>Hugging Face</a> | <a href='https://civitai.com/user/RioShiina' target='_blank'>Civitai</a></div>")
|
| 20 |
+
|
| 21 |
+
gr.Markdown("<div style='font-size:12px; opacity:0.7; margin-top:12px;'>Generated images, prompts and settings are saved for research and model-training purposes.</div>")
|
| 22 |
+
|
| 23 |
+
event_handler_function(ui_components, demo)
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
import mcp_tools as mcp
|
| 27 |
+
if hasattr(mcp, "register_high_level_mcp_apis"):
|
| 28 |
+
mcp.register_high_level_mcp_apis(demo)
|
| 29 |
+
mcp.cleanup_dependencies_api_names(demo)
|
| 30 |
+
elif hasattr(mcp, 'MCP_FUNCTIONS') and isinstance(mcp.MCP_FUNCTIONS, list):
|
| 31 |
+
for func in mcp.MCP_FUNCTIONS:
|
| 32 |
+
gr.api(func)
|
| 33 |
+
print(f"✅ Registered MCP API endpoint: '{func.__name__}'")
|
| 34 |
+
except Exception as e:
|
| 35 |
+
print(f"⚠️ Warning registering MCP functions: {e}")
|
| 36 |
+
|
| 37 |
+
# Disable API exposure for all atomic UI event handlers
|
| 38 |
+
high_level_names = getattr(mcp, "HIGH_LEVEL_MCP_API_NAMES", set())
|
| 39 |
+
for fn in demo.fns.values():
|
| 40 |
+
if getattr(fn, "api_name", None) not in high_level_names:
|
| 41 |
+
fn.show_api = False
|
| 42 |
+
fn.api_name = False
|
| 43 |
+
|
| 44 |
return demo
|
ui/shared/ui_components.py
CHANGED
|
@@ -1,838 +1,947 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
| 3 |
-
from core.settings import (
|
| 4 |
-
MAX_LORAS, LORA_SOURCE_CHOICES, MAX_EMBEDDINGS, MAX_CONDITIONINGS,
|
| 5 |
-
MAX_CONTROLNETS, MAX_IPADAPTERS, RESOLUTION_MAP, ARCHITECTURES_CONFIG,
|
| 6 |
-
MODEL_MAP_CHECKPOINT, MODEL_TYPE_MAP, FEATURES_CONFIG, ARCH_CATEGORIES_MAP,
|
| 7 |
-
VAE_DIR, MODEL_DEFAULTS_CONFIG
|
| 8 |
-
)
|
| 9 |
-
import yaml
|
| 10 |
-
import os
|
| 11 |
-
from functools import lru_cache
|
| 12 |
-
from utils.app_utils import save_uploaded_file
|
| 13 |
-
|
| 14 |
-
default_model_name = list(MODEL_MAP_CHECKPOINT.keys())[0] if MODEL_MAP_CHECKPOINT else None
|
| 15 |
-
default_m_type = MODEL_TYPE_MAP.get(default_model_name, "SDXL") if default_model_name else "SDXL"
|
| 16 |
-
default_architectures_dict = ARCHITECTURES_CONFIG.get('architectures', {})
|
| 17 |
-
default_arch_model_type = default_architectures_dict.get(default_m_type, {}).get("model_type", default_m_type.lower().replace(" ", "").replace(".", ""))
|
| 18 |
-
default_arch_features = FEATURES_CONFIG.get(default_arch_model_type, {})
|
| 19 |
-
default_enabled_chains = default_arch_features.get('enabled_chains', [])
|
| 20 |
-
|
| 21 |
-
default_vals = MODEL_DEFAULTS_CONFIG.get('Default', {})
|
| 22 |
-
DEFAULT_STEPS = default_vals.get('steps', 20)
|
| 23 |
-
DEFAULT_CFG = default_vals.get('cfg', 5.0)
|
| 24 |
-
DEFAULT_SAMPLER = default_vals.get('sampler_name', 'euler')
|
| 25 |
-
DEFAULT_SCHEDULER = default_vals.get('scheduler', 'simple')
|
| 26 |
-
DEFAULT_POS_PROMPT = default_vals.get('positive_prompt', '')
|
| 27 |
-
DEFAULT_NEG_PROMPT = default_vals.get('negative_prompt', '')
|
| 28 |
-
|
| 29 |
-
@lru_cache(maxsize=1)
|
| 30 |
-
def get_ipadapter_config_from_yaml():
|
| 31 |
-
try:
|
| 32 |
-
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 33 |
-
_IPADAPTER_LIST_PATH = os.path.join(_PROJECT_ROOT, 'yaml', 'ipadapter.yaml')
|
| 34 |
-
with open(_IPADAPTER_LIST_PATH, 'r', encoding='utf-8') as f:
|
| 35 |
-
config = yaml.safe_load(f)
|
| 36 |
-
return config
|
| 37 |
-
except Exception as e:
|
| 38 |
-
print(f"Warning: Could not load ipadapter.yaml for UI components: {e}")
|
| 39 |
-
return {}
|
| 40 |
-
|
| 41 |
-
def get_ipadapter_presets(arch="SDXL"):
|
| 42 |
-
config = get_ipadapter_config_from_yaml()
|
| 43 |
-
presets = []
|
| 44 |
-
if config:
|
| 45 |
-
std_presets = config.get("IPAdapter_presets", {}).get(arch, [])
|
| 46 |
-
face_presets = config.get("IPAdapter_FaceID_presets", {}).get(arch, [])
|
| 47 |
-
if std_presets:
|
| 48 |
-
presets.extend(std_presets)
|
| 49 |
-
if face_presets:
|
| 50 |
-
presets.extend(face_presets)
|
| 51 |
-
return presets if presets else ["STANDARD (medium strength)"]
|
| 52 |
-
|
| 53 |
-
def create_model_architecture_filter_ui(prefix):
|
| 54 |
-
components = {}
|
| 55 |
-
ordered_architectures = ARCHITECTURES_CONFIG.get("architecture_order", [])
|
| 56 |
-
choices = ["ALL"] + ordered_architectures
|
| 57 |
-
|
| 58 |
-
components[f'model_arch_{prefix}'] = gr.Radio(
|
| 59 |
-
label="Model Architecture",
|
| 60 |
-
choices=choices,
|
| 61 |
-
value="ALL",
|
| 62 |
-
interactive=True,
|
| 63 |
-
visible=True
|
| 64 |
-
)
|
| 65 |
-
return components
|
| 66 |
-
|
| 67 |
-
def create_category_filter_ui(prefix):
|
| 68 |
-
valid_cats = list(set(cat for cats in ARCH_CATEGORIES_MAP.values() for cat in cats))
|
| 69 |
-
cat_choices = ["ALL"] + sorted(valid_cats)
|
| 70 |
-
|
| 71 |
-
components = {}
|
| 72 |
-
components[f'model_cat_{prefix}'] = gr.Dropdown(
|
| 73 |
-
label="Filter Models",
|
| 74 |
-
choices=cat_choices,
|
| 75 |
-
value="ALL",
|
| 76 |
-
interactive=True,
|
| 77 |
-
scale=1,
|
| 78 |
-
allow_custom_value=True
|
| 79 |
-
)
|
| 80 |
-
return components
|
| 81 |
-
|
| 82 |
-
def create_base_parameter_ui(prefix, defaults=None):
|
| 83 |
-
if defaults is None:
|
| 84 |
-
defaults = {}
|
| 85 |
-
|
| 86 |
-
components = {}
|
| 87 |
-
|
| 88 |
-
with gr.Row():
|
| 89 |
-
components[f'aspect_ratio_{prefix}'] = gr.Dropdown(
|
| 90 |
-
label="Aspect Ratio",
|
| 91 |
-
choices=list(RESOLUTION_MAP.get('sdxl', {}).keys()),
|
| 92 |
-
value="1:1 (Square)",
|
| 93 |
-
interactive=True,
|
| 94 |
-
allow_custom_value=True
|
| 95 |
-
)
|
| 96 |
-
with gr.Row():
|
| 97 |
-
components[f'width_{prefix}'] = gr.Number(label="Width", value=defaults.get('w', 1024), interactive=True)
|
| 98 |
-
components[f'height_{prefix}'] = gr.Number(label="Height", value=defaults.get('h', 1024), interactive=True)
|
| 99 |
-
with gr.Row():
|
| 100 |
-
components[f'sampler_{prefix}'] = gr.Dropdown(
|
| 101 |
-
label="Sampler",
|
| 102 |
-
choices=SAMPLER_CHOICES,
|
| 103 |
-
value=DEFAULT_SAMPLER if DEFAULT_SAMPLER in SAMPLER_CHOICES else (SAMPLER_CHOICES[0] if SAMPLER_CHOICES else 'euler')
|
| 104 |
-
)
|
| 105 |
-
components[f'scheduler_{prefix}'] = gr.Dropdown(
|
| 106 |
-
label="Scheduler",
|
| 107 |
-
choices=SCHEDULER_CHOICES,
|
| 108 |
-
value=DEFAULT_SCHEDULER if DEFAULT_SCHEDULER in SCHEDULER_CHOICES else (SCHEDULER_CHOICES[0] if SCHEDULER_CHOICES else 'simple')
|
| 109 |
-
)
|
| 110 |
-
with gr.Row():
|
| 111 |
-
components[f'steps_{prefix}'] = gr.Slider(label="Steps", minimum=1, maximum=100, step=1, value=DEFAULT_STEPS)
|
| 112 |
-
components[f'cfg_{prefix}'] = gr.Slider(label="CFG Scale", minimum=1.0, maximum=20.0, step=0.1, value=DEFAULT_CFG)
|
| 113 |
-
with gr.Row():
|
| 114 |
-
components[f'seed_{prefix}'] = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
|
| 115 |
-
components[f'batch_size_{prefix}'] = gr.Slider(label="Batch Size", minimum=1, maximum=
|
| 116 |
-
with gr.Row():
|
| 117 |
-
components[f'zero_gpu_{prefix}'] = gr.Number(label="ZeroGPU Duration (s)", value=60, placeholder="Default: 60s", info="Optional: Set how long to reserve the GPU.")
|
| 118 |
-
|
| 119 |
-
return components
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
def create_lora_settings_ui(prefix: str):
|
| 123 |
-
components = {}
|
| 124 |
-
|
| 125 |
-
lora_rows, lora_sources, lora_ids, lora_scales, lora_uploads = [], [], [], [], []
|
| 126 |
-
|
| 127 |
-
with gr.Accordion("LoRA Settings", open=False, visible=('lora' in default_enabled_chains)) as lora_accordion:
|
| 128 |
-
components[f'lora_accordion_{prefix}'] = lora_accordion
|
| 129 |
-
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `lightx2v/Qwen-Image-Lightning/Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors`).")
|
| 130 |
-
components[f'lora_count_state_{prefix}'] = gr.State(1)
|
| 131 |
-
|
| 132 |
-
for i in range(MAX_LORAS):
|
| 133 |
-
with gr.Row(visible=i==0) as row:
|
| 134 |
-
source = gr.Dropdown(label=f"LoRA Source {i+1}", choices=LORA_SOURCE_CHOICES, value=LORA_SOURCE_CHOICES[0], scale=1)
|
| 135 |
-
lora_id = gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=2, type="text")
|
| 136 |
-
scale = gr.Slider(label=f"Scale", minimum=0.0, maximum=2.0, step=0.05, value=1.0, scale=1)
|
| 137 |
-
upload = gr.UploadButton(label="Upload", file_types=[".safetensors"], scale=1)
|
| 138 |
-
|
| 139 |
-
lora_rows.append(row)
|
| 140 |
-
lora_sources.append(source)
|
| 141 |
-
lora_ids.append(lora_id)
|
| 142 |
-
lora_scales.append(scale)
|
| 143 |
-
lora_uploads.append(upload)
|
| 144 |
-
|
| 145 |
-
with gr.Row():
|
| 146 |
-
components[f'add_lora_button_{prefix}'] = gr.Button("Add LoRA", variant="secondary")
|
| 147 |
-
components[f'delete_lora_button_{prefix}'] = gr.Button("Remove LoRA", variant="secondary", visible=False)
|
| 148 |
-
|
| 149 |
-
components[f'lora_rows_{prefix}'] = lora_rows
|
| 150 |
-
components[f'lora_sources_{prefix}'] = lora_sources
|
| 151 |
-
components[f'lora_ids_{prefix}'] = lora_ids
|
| 152 |
-
components[f'lora_scales_{prefix}'] = lora_scales
|
| 153 |
-
components[f'lora_uploads_{prefix}'] = lora_uploads
|
| 154 |
-
|
| 155 |
-
all_lora_components_flat = []
|
| 156 |
-
for i in range(MAX_LORAS):
|
| 157 |
-
all_lora_components_flat.extend([lora_sources[i], lora_ids[i], lora_scales[i], lora_uploads[i]])
|
| 158 |
-
components[f'all_lora_components_flat_{prefix}'] = all_lora_components_flat
|
| 159 |
-
|
| 160 |
-
return components
|
| 161 |
-
|
| 162 |
-
def create_controlnet_ui(prefix: str, max_units=MAX_CONTROLNETS):
|
| 163 |
-
components = {}
|
| 164 |
-
key = lambda name: f"{name}_{prefix}"
|
| 165 |
-
|
| 166 |
-
with gr.Accordion("ControlNet Settings", open=False, visible=('controlnet' in default_enabled_chains)) as accordion:
|
| 167 |
-
components[key('controlnet_accordion')] = accordion
|
| 168 |
-
|
| 169 |
-
cn_rows, images, series, types, strengths, filepaths = [], [], [], [], [], []
|
| 170 |
-
components.update({
|
| 171 |
-
key('controlnet_rows'): cn_rows,
|
| 172 |
-
key('controlnet_images'): images,
|
| 173 |
-
key('controlnet_series'): series,
|
| 174 |
-
key('controlnet_types'): types,
|
| 175 |
-
key('controlnet_strengths'): strengths,
|
| 176 |
-
key('controlnet_filepaths'): filepaths
|
| 177 |
-
})
|
| 178 |
-
|
| 179 |
-
for i in range(max_units):
|
| 180 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 181 |
-
with gr.Column(scale=1):
|
| 182 |
-
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 183 |
-
with gr.Column(scale=2):
|
| 184 |
-
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
|
| 185 |
-
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
|
| 186 |
-
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 187 |
-
filepaths.append(gr.State(None))
|
| 188 |
-
cn_rows.append(row)
|
| 189 |
-
|
| 190 |
-
with gr.Row():
|
| 191 |
-
components[key('add_controlnet_button')] = gr.Button("✚ Add ControlNet")
|
| 192 |
-
components[key('delete_controlnet_button')] = gr.Button("➖ Delete ControlNet", visible=False)
|
| 193 |
-
components[key('controlnet_count_state')] = gr.State(1)
|
| 194 |
-
|
| 195 |
-
all_cn_components_flat = []
|
| 196 |
-
for i in range(max_units):
|
| 197 |
-
all_cn_components_flat.extend([
|
| 198 |
-
images[i], types[i], series[i], strengths[i], filepaths[i]
|
| 199 |
-
])
|
| 200 |
-
components[key('all_controlnet_components_flat')] = all_cn_components_flat
|
| 201 |
-
|
| 202 |
-
return components
|
| 203 |
-
|
| 204 |
-
def create_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 205 |
-
components = {}
|
| 206 |
-
key = lambda name: f"{name}_{prefix}"
|
| 207 |
-
|
| 208 |
-
sdxl_presets = get_ipadapter_presets("SDXL")
|
| 209 |
-
default_preset = sdxl_presets[0] if sdxl_presets else None
|
| 210 |
-
|
| 211 |
-
with gr.Accordion("IPAdapter Settings", open=False, visible=('ipadapter' in default_enabled_chains)) as accordion:
|
| 212 |
-
components[key('ipadapter_accordion')] = accordion
|
| 213 |
-
gr.Markdown("💡 **Tip:** Processed using the [cubiq/ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) node.")
|
| 214 |
-
|
| 215 |
-
with gr.Row():
|
| 216 |
-
components[key('ipadapter_final_preset')] = gr.Dropdown(
|
| 217 |
-
label="Preset (for all images)",
|
| 218 |
-
choices=sdxl_presets,
|
| 219 |
-
value=default_preset,
|
| 220 |
-
interactive=True,
|
| 221 |
-
allow_custom_value=True
|
| 222 |
-
)
|
| 223 |
-
components[key('ipadapter_embeds_scaling')] = gr.Dropdown(
|
| 224 |
-
label="Embeds Scaling",
|
| 225 |
-
choices=['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],
|
| 226 |
-
value='V only',
|
| 227 |
-
interactive=True
|
| 228 |
-
)
|
| 229 |
-
|
| 230 |
-
with gr.Row():
|
| 231 |
-
components[key('ipadapter_combine_method')] = gr.Dropdown(
|
| 232 |
-
label="Combine Method",
|
| 233 |
-
choices=["concat", "add", "subtract", "average", "norm average", "max", "min"],
|
| 234 |
-
value="concat",
|
| 235 |
-
interactive=True
|
| 236 |
-
)
|
| 237 |
-
components[key('ipadapter_final_weight')] = gr.Slider(label="Final Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True)
|
| 238 |
-
components[key('ipadapter_final_lora_strength')] = gr.Slider(label="Final LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False)
|
| 239 |
-
|
| 240 |
-
gr.Markdown("---")
|
| 241 |
-
|
| 242 |
-
ipa_rows, images, weights, lora_strengths = [], [], [], []
|
| 243 |
-
components.update({
|
| 244 |
-
key('ipadapter_rows'): ipa_rows,
|
| 245 |
-
key('ipadapter_images'): images,
|
| 246 |
-
key('ipadapter_weights'): weights,
|
| 247 |
-
key('ipadapter_lora_strengths'): lora_strengths
|
| 248 |
-
})
|
| 249 |
-
|
| 250 |
-
for i in range(max_units):
|
| 251 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 252 |
-
with gr.Column(scale=1):
|
| 253 |
-
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 254 |
-
with gr.Column(scale=2):
|
| 255 |
-
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 256 |
-
lora_strengths.append(gr.Slider(label="LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False))
|
| 257 |
-
ipa_rows.append(row)
|
| 258 |
-
|
| 259 |
-
with gr.Row():
|
| 260 |
-
components[key('add_ipadapter_button')] = gr.Button("✚ Add IPAdapter")
|
| 261 |
-
components[key('delete_ipadapter_button')] = gr.Button("➖ Delete IPAdapter", visible=False)
|
| 262 |
-
components[key('ipadapter_count_state')] = gr.State(1)
|
| 263 |
-
|
| 264 |
-
all_ipa_components_flat = images + weights + lora_strengths
|
| 265 |
-
all_ipa_components_flat += [
|
| 266 |
-
components[key('ipadapter_final_preset')],
|
| 267 |
-
components[key('ipadapter_final_weight')],
|
| 268 |
-
components[key('ipadapter_final_lora_strength')],
|
| 269 |
-
components[key('ipadapter_embeds_scaling')],
|
| 270 |
-
components[key('ipadapter_combine_method')],
|
| 271 |
-
]
|
| 272 |
-
components[key('all_ipadapter_components_flat')] = all_ipa_components_flat
|
| 273 |
-
|
| 274 |
-
return components
|
| 275 |
-
|
| 276 |
-
def create_flux1_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 277 |
-
components = {}
|
| 278 |
-
key = lambda name: f"{name}_{prefix}"
|
| 279 |
-
|
| 280 |
-
with gr.Accordion("IPAdapter Settings (FLUX.1)", open=False, visible=('flux1_ipadapter' in default_enabled_chains)) as accordion:
|
| 281 |
-
components[key('flux1_ipadapter_accordion')] = accordion
|
| 282 |
-
gr.Markdown("💡 **Tip:** Processed using the [Shakker-Labs/ComfyUI-IPAdapter-Flux](https://github.com/Shakker-Labs/ComfyUI-IPAdapter-Flux) node.")
|
| 283 |
-
|
| 284 |
-
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
|
| 285 |
-
components.update({
|
| 286 |
-
key('flux1_ipadapter_rows'): ipa_rows,
|
| 287 |
-
key('flux1_ipadapter_images'): images,
|
| 288 |
-
key('flux1_ipadapter_weights'): weights,
|
| 289 |
-
key('flux1_ipadapter_start_percents'): start_percents,
|
| 290 |
-
key('flux1_ipadapter_end_percents'): end_percents,
|
| 291 |
-
})
|
| 292 |
-
|
| 293 |
-
for i in range(max_units):
|
| 294 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 295 |
-
with gr.Column(scale=1):
|
| 296 |
-
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 297 |
-
with gr.Column(scale=2):
|
| 298 |
-
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True))
|
| 299 |
-
with gr.Row():
|
| 300 |
-
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
|
| 301 |
-
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=0.6, interactive=True))
|
| 302 |
-
ipa_rows.append(row)
|
| 303 |
-
|
| 304 |
-
with gr.Row():
|
| 305 |
-
components[key('add_flux1_ipadapter_button')] = gr.Button("✚ Add IPAdapter (FLUX)")
|
| 306 |
-
components[key('delete_flux1_ipadapter_button')] = gr.Button("➖ Delete IPAdapter (FLUX)", visible=False)
|
| 307 |
-
components[key('flux1_ipadapter_count_state')] = gr.State(1)
|
| 308 |
-
|
| 309 |
-
all_flux1_ipa_components_flat = images + weights + start_percents + end_percents
|
| 310 |
-
components[key('all_flux1_ipadapter_components_flat')] = all_flux1_ipa_components_flat
|
| 311 |
-
|
| 312 |
-
return components
|
| 313 |
-
|
| 314 |
-
def create_sd3_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 315 |
-
components = {}
|
| 316 |
-
key = lambda name: f"{name}_{prefix}"
|
| 317 |
-
|
| 318 |
-
with gr.Accordion("IPAdapter Settings (SD3)", open=False, visible=('sd3_ipadapter' in default_enabled_chains)) as accordion:
|
| 319 |
-
components[key('sd3_ipadapter_accordion')] = accordion
|
| 320 |
-
gr.Markdown("💡 **Tip:** Processed using the [Slickytail/ComfyUI-InstantX-IPAdapter-SD3](https://github.com/Slickytail/ComfyUI-InstantX-IPAdapter-SD3) node.")
|
| 321 |
-
|
| 322 |
-
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
|
| 323 |
-
components.update({
|
| 324 |
-
key('sd3_ipadapter_rows'): ipa_rows,
|
| 325 |
-
key('sd3_ipadapter_images'): images,
|
| 326 |
-
key('sd3_ipadapter_weights'): weights,
|
| 327 |
-
key('sd3_ipadapter_start_percents'): start_percents,
|
| 328 |
-
key('sd3_ipadapter_end_percents'): end_percents,
|
| 329 |
-
})
|
| 330 |
-
|
| 331 |
-
for i in range(max_units):
|
| 332 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 333 |
-
with gr.Column(scale=1):
|
| 334 |
-
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 335 |
-
with gr.Column(scale=2):
|
| 336 |
-
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.5, interactive=True))
|
| 337 |
-
with gr.Row():
|
| 338 |
-
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
|
| 339 |
-
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=1.0, interactive=True))
|
| 340 |
-
ipa_rows.append(row)
|
| 341 |
-
|
| 342 |
-
with gr.Row():
|
| 343 |
-
components[key('add_sd3_ipadapter_button')] = gr.Button("✚ Add IPAdapter (SD3)")
|
| 344 |
-
components[key('delete_sd3_ipadapter_button')] = gr.Button("➖ Delete IPAdapter (SD3)", visible=False)
|
| 345 |
-
components[key('sd3_ipadapter_count_state')] = gr.State(1)
|
| 346 |
-
|
| 347 |
-
all_sd3_ipa_components_flat = images + weights + start_percents + end_percents
|
| 348 |
-
components[key('all_sd3_ipadapter_components_flat')] = all_sd3_ipa_components_flat
|
| 349 |
-
|
| 350 |
-
return components
|
| 351 |
-
|
| 352 |
-
def create_style_ui(prefix: str):
|
| 353 |
-
components = {}
|
| 354 |
-
key = lambda name: f"{name}_{prefix}"
|
| 355 |
-
|
| 356 |
-
with gr.Accordion("Style Settings (FLUX.1)", open=False, visible=('style' in default_enabled_chains)) as accordion:
|
| 357 |
-
components[key('style_accordion')] = accordion
|
| 358 |
-
|
| 359 |
-
style_rows, images, strengths = [], [], []
|
| 360 |
-
components.update({
|
| 361 |
-
key('style_rows'): style_rows,
|
| 362 |
-
key('style_images'): images,
|
| 363 |
-
key('style_strengths'): strengths
|
| 364 |
-
})
|
| 365 |
-
|
| 366 |
-
for i in range(5):
|
| 367 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 368 |
-
with gr.Column(scale=1):
|
| 369 |
-
images.append(gr.Image(label=f"Style Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 370 |
-
with gr.Column(scale=2):
|
| 371 |
-
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 372 |
-
style_rows.append(row)
|
| 373 |
-
|
| 374 |
-
with gr.Row():
|
| 375 |
-
components[key('add_style_button')] = gr.Button("✚ Add Style (FLUX)")
|
| 376 |
-
components[key('delete_style_button')] = gr.Button("➖ Delete Style (FLUX)", visible=False)
|
| 377 |
-
components[key('style_count_state')] = gr.State(1)
|
| 378 |
-
|
| 379 |
-
all_style_components_flat = images + strengths
|
| 380 |
-
components[key('all_style_components_flat')] = all_style_components_flat
|
| 381 |
-
|
| 382 |
-
return components
|
| 383 |
-
|
| 384 |
-
def create_embedding_ui(prefix: str):
|
| 385 |
-
components = {}
|
| 386 |
-
key = lambda name: f"{name}_{prefix}"
|
| 387 |
-
|
| 388 |
-
with gr.Accordion("Embedding Settings", open=False, visible=('embedding' in default_enabled_chains)) as accordion:
|
| 389 |
-
components[key('embedding_accordion')] = accordion
|
| 390 |
-
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. For example, entering the Version ID 456 will automatically save the file as \"civitai/456.safetensors\", and you will need to manually enter `embedding:civitai/456` in either your prompt or negative prompt to activate it. When downloading from Hugging Face, please use the format: repo_id/filename.extension or repo_id/folder_path/filename.extension (e.g., ilikebigturtles/lazypos/lazypos.safetensors or ilikebigturtles/lazyneg/lazyneg.safetensors). For Hugging Face files, you will need to enter embedding:huggingface/filename (e.g., entering embedding:huggingface/lazypos in your positive prompt, or embedding:huggingface/lazyneg in your negative prompt) to activate it. For uploaded files, enter `embedding:upload_file/filename` in either your positive prompt or negative prompt to activate it.")
|
| 391 |
-
|
| 392 |
-
embedding_rows, sources, ids, files, upload_buttons = [], [], [], [], []
|
| 393 |
-
components.update({
|
| 394 |
-
key('embedding_rows'): embedding_rows,
|
| 395 |
-
key('embeddings_sources'): sources,
|
| 396 |
-
key('embeddings_ids'): ids,
|
| 397 |
-
key('embeddings_files'): files,
|
| 398 |
-
key('embeddings_uploads'): upload_buttons
|
| 399 |
-
})
|
| 400 |
-
|
| 401 |
-
for i in range(MAX_EMBEDDINGS):
|
| 402 |
-
with gr.Row(visible=(i < 1)) as row:
|
| 403 |
-
sources.append(gr.Dropdown(label=f"Embedding Source {i+1}", choices=LORA_SOURCE_CHOICES, value="Civitai", scale=1, interactive=True))
|
| 404 |
-
ids.append(gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=3, interactive=True, type="text"))
|
| 405 |
-
upload_btn = gr.UploadButton("Upload", file_types=[".safetensors"], scale=1)
|
| 406 |
-
files.append(gr.State(None))
|
| 407 |
-
upload_buttons.append(upload_btn)
|
| 408 |
-
embedding_rows.append(row)
|
| 409 |
-
|
| 410 |
-
with gr.Row():
|
| 411 |
-
components[key('add_embedding_button')] = gr.Button("✚ Add Embedding")
|
| 412 |
-
components[key('delete_embedding_button')] = gr.Button("➖ Delete Embedding", visible=False)
|
| 413 |
-
components[key('embedding_count_state')] = gr.State(1)
|
| 414 |
-
|
| 415 |
-
all_embedding_components_flat = []
|
| 416 |
-
for i in range(MAX_EMBEDDINGS):
|
| 417 |
-
all_embedding_components_flat.extend([sources[i], ids[i], files[i]])
|
| 418 |
-
components[key('all_embedding_components_flat')] = all_embedding_components_flat
|
| 419 |
-
|
| 420 |
-
return components
|
| 421 |
-
|
| 422 |
-
def create_conditioning_ui(prefix: str):
|
| 423 |
-
components = {}
|
| 424 |
-
key = lambda name: f"{name}_{prefix}"
|
| 425 |
-
|
| 426 |
-
with gr.Accordion("Conditioning Settings", open=False, visible=('conditioning' in default_enabled_chains)) as accordion:
|
| 427 |
-
components[key('conditioning_accordion')] = accordion
|
| 428 |
-
gr.Markdown("💡 **Tip:** Define rectangular areas and assign specific prompts to them. Coordinates (X, Y) start from the top-left corner.")
|
| 429 |
-
|
| 430 |
-
cond_rows, prompts, widths, heights, xs, ys, strengths = [], [], [], [], [], [], []
|
| 431 |
-
components.update({
|
| 432 |
-
key('conditioning_rows'): cond_rows,
|
| 433 |
-
key('conditioning_prompts'): prompts,
|
| 434 |
-
key('conditioning_widths'): widths,
|
| 435 |
-
key('conditioning_heights'): heights,
|
| 436 |
-
key('conditioning_xs'): xs,
|
| 437 |
-
key('conditioning_ys'): ys,
|
| 438 |
-
key('conditioning_strengths'): strengths
|
| 439 |
-
})
|
| 440 |
-
|
| 441 |
-
for i in range(MAX_CONDITIONINGS):
|
| 442 |
-
with gr.Column(visible=(i < 1)) as row_wrapper:
|
| 443 |
-
prompts.append(gr.Textbox(label=f"Area Prompt {i+1}", lines=2, interactive=True))
|
| 444 |
-
with gr.Row():
|
| 445 |
-
xs.append(gr.Number(label="X", value=0, interactive=True, step=8, scale=1))
|
| 446 |
-
ys.append(gr.Number(label="Y", value=0, interactive=True, step=8, scale=1))
|
| 447 |
-
widths.append(gr.Number(label="Width", value=512, interactive=True, step=8, scale=1))
|
| 448 |
-
heights.append(gr.Number(label="Height", value=512, interactive=True, step=8, scale=1))
|
| 449 |
-
strengths.append(gr.Slider(label="Strength", minimum=0.1, maximum=2.0, step=0.05, value=1.0, interactive=True, scale=2))
|
| 450 |
-
cond_rows.append(row_wrapper)
|
| 451 |
-
|
| 452 |
-
with gr.Row():
|
| 453 |
-
components[key('add_conditioning_button')] = gr.Button("✚ Add Area")
|
| 454 |
-
components[key('delete_conditioning_button')] = gr.Button("➖ Delete Area", visible=False)
|
| 455 |
-
components[key('conditioning_count_state')] = gr.State(1)
|
| 456 |
-
|
| 457 |
-
all_cond_components_flat = prompts + widths + heights + xs + ys + strengths
|
| 458 |
-
components[key('all_conditioning_components_flat')] = all_cond_components_flat
|
| 459 |
-
|
| 460 |
-
return components
|
| 461 |
-
|
| 462 |
-
def on_vae_upload(file_obj):
|
| 463 |
-
if not file_obj:
|
| 464 |
-
return gr.update(), gr.update(), None
|
| 465 |
-
|
| 466 |
-
uploaded_filename = save_uploaded_file(file_obj, VAE_DIR)
|
| 467 |
-
return uploaded_filename, "Upload File", file_obj
|
| 468 |
-
|
| 469 |
-
def create_vae_override_ui(prefix: str):
|
| 470 |
-
components = {}
|
| 471 |
-
key = lambda name: f"{name}_{prefix}"
|
| 472 |
-
|
| 473 |
-
with gr.Accordion("VAE Settings (Override)", open=False, visible=('vae' in default_enabled_chains)) as vae_accordion:
|
| 474 |
-
components[key('vae_accordion')] = vae_accordion
|
| 475 |
-
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `madebyollin/sdxl-vae-fp16-fix/sdxl_vae.safetensors`).")
|
| 476 |
-
with gr.Row():
|
| 477 |
-
components[key('vae_source')] = gr.Dropdown(
|
| 478 |
-
label="VAE Source",
|
| 479 |
-
choices=LORA_SOURCE_CHOICES,
|
| 480 |
-
value="Civitai",
|
| 481 |
-
scale=1,
|
| 482 |
-
interactive=True
|
| 483 |
-
)
|
| 484 |
-
components[key('vae_id')] = gr.Textbox(
|
| 485 |
-
label="Civitai Version ID / HF file / Upload File",
|
| 486 |
-
scale=3,
|
| 487 |
-
interactive=True,
|
| 488 |
-
type="text"
|
| 489 |
-
)
|
| 490 |
-
upload_btn = gr.UploadButton(
|
| 491 |
-
"Upload",
|
| 492 |
-
file_types=[".safetensors"],
|
| 493 |
-
scale=1
|
| 494 |
-
)
|
| 495 |
-
components[key('vae_upload_button')] = upload_btn
|
| 496 |
-
components[key('vae_file')] = gr.State(None)
|
| 497 |
-
|
| 498 |
-
upload_btn.upload(
|
| 499 |
-
fn=on_vae_upload,
|
| 500 |
-
inputs=[upload_btn],
|
| 501 |
-
outputs=[components[key('vae_id')], components[key('vae_source')], components[key('vae_file')]]
|
| 502 |
-
)
|
| 503 |
-
|
| 504 |
-
return components
|
| 505 |
-
|
| 506 |
-
def create_reference_latent_ui(prefix: str, max_units=10):
|
| 507 |
-
components = {}
|
| 508 |
-
key = lambda name: f"{name}_{prefix}"
|
| 509 |
-
|
| 510 |
-
with gr.Accordion("Reference Edit Settings", open=False, visible=('reference_latent' in default_enabled_chains)) as ref_accordion:
|
| 511 |
-
components[key('reference_latent_accordion')] = ref_accordion
|
| 512 |
-
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 513 |
-
|
| 514 |
-
ref_image_groups = []
|
| 515 |
-
ref_image_inputs = []
|
| 516 |
-
with gr.Row():
|
| 517 |
-
for i in range(max_units):
|
| 518 |
-
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 519 |
-
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 520 |
-
ref_image_groups.append(img_col)
|
| 521 |
-
ref_image_inputs.append(img_comp)
|
| 522 |
-
|
| 523 |
-
components[key('reference_latent_rows')] = ref_image_groups
|
| 524 |
-
components[key('reference_latent_images')] = ref_image_inputs
|
| 525 |
-
|
| 526 |
-
with gr.Row():
|
| 527 |
-
components[key('add_reference_latent_button')] = gr.Button("✚ Add Reference Image")
|
| 528 |
-
components[key('delete_reference_latent_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 529 |
-
components[key('reference_latent_count_state')] = gr.State(1)
|
| 530 |
-
|
| 531 |
-
components[key('all_reference_latent_components_flat')] = ref_image_inputs
|
| 532 |
-
|
| 533 |
-
return components
|
| 534 |
-
|
| 535 |
-
def create_hidream_o1_reference_ui(prefix: str, max_units=10):
|
| 536 |
-
components = {}
|
| 537 |
-
key = lambda name: f"{name}_{prefix}"
|
| 538 |
-
|
| 539 |
-
with gr.Accordion("HiDream-O1 Reference Edit Settings", open=False, visible=('hidream_o1_reference' in default_enabled_chains)) as ref_accordion:
|
| 540 |
-
components[key('hidream_o1_reference_accordion')] = ref_accordion
|
| 541 |
-
gr.Markdown("💡 **Tip:** Please use **HiDream-O1-Image-Dev** (HiDream-O1-Image will time out), and set the resolution to **4.0MP** (e.g., 2048x2048). In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 542 |
-
|
| 543 |
-
ref_image_groups = []
|
| 544 |
-
ref_image_inputs = []
|
| 545 |
-
with gr.Row():
|
| 546 |
-
for i in range(max_units):
|
| 547 |
-
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 548 |
-
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 549 |
-
ref_image_groups.append(img_col)
|
| 550 |
-
ref_image_inputs.append(img_comp)
|
| 551 |
-
|
| 552 |
-
components[key('hidream_o1_reference_rows')] = ref_image_groups
|
| 553 |
-
components[key('hidream_o1_reference_images')] = ref_image_inputs
|
| 554 |
-
|
| 555 |
-
with gr.Row():
|
| 556 |
-
components[key('add_hidream_o1_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 557 |
-
components[key('delete_hidream_o1_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 558 |
-
components[key('hidream_o1_reference_count_state')] = gr.State(1)
|
| 559 |
-
|
| 560 |
-
components[key('all_hidream_o1_reference_components_flat')] = ref_image_inputs
|
| 561 |
-
|
| 562 |
-
return components
|
| 563 |
-
|
| 564 |
-
def create_sensenova_reference_ui(prefix: str, max_units=10):
|
| 565 |
-
components = {}
|
| 566 |
-
key = lambda name: f"{name}_{prefix}"
|
| 567 |
-
|
| 568 |
-
with gr.Accordion("SenseNova Reference Edit Settings", open=False, visible=('sensenova_reference' in default_enabled_chains)) as ref_accordion:
|
| 569 |
-
components[key('sensenova_reference_accordion')] = ref_accordion
|
| 570 |
-
gr.Markdown("💡 **Tip:** For SenseNova models, this feature enables reference image editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 571 |
-
|
| 572 |
-
ref_image_groups = []
|
| 573 |
-
ref_image_inputs = []
|
| 574 |
-
with gr.Row():
|
| 575 |
-
for i in range(max_units):
|
| 576 |
-
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 577 |
-
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 578 |
-
ref_image_groups.append(img_col)
|
| 579 |
-
ref_image_inputs.append(img_comp)
|
| 580 |
-
|
| 581 |
-
components[key('sensenova_reference_rows')] = ref_image_groups
|
| 582 |
-
components[key('sensenova_reference_images')] = ref_image_inputs
|
| 583 |
-
|
| 584 |
-
with gr.Row():
|
| 585 |
-
components[key('add_sensenova_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 586 |
-
components[key('delete_sensenova_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 587 |
-
components[key('sensenova_reference_count_state')] = gr.State(1)
|
| 588 |
-
|
| 589 |
-
components[key('all_sensenova_reference_components_flat')] = ref_image_inputs
|
| 590 |
-
|
| 591 |
-
return components
|
| 592 |
-
|
| 593 |
-
def create_joyai_reference_ui(prefix: str, max_units=2):
|
| 594 |
-
components = {}
|
| 595 |
-
key = lambda name: f"{name}_{prefix}"
|
| 596 |
-
|
| 597 |
-
with gr.Accordion("JoyAI Reference Edit Settings", open=False, visible=('joyai_image' in default_enabled_chains)) as ref_accordion:
|
| 598 |
-
components[key('joyai_reference_accordion')] = ref_accordion
|
| 599 |
-
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit** (JoyAI-Image-Edit recommended), while adding multiple images performs an **Image Combine** (JoyAI-Image-Edit-Plus recommended with ZeroGPU Duration (s) set to 120).")
|
| 600 |
-
|
| 601 |
-
ref_image_groups = []
|
| 602 |
-
ref_image_inputs = []
|
| 603 |
-
with gr.Row():
|
| 604 |
-
for i in range(max_units):
|
| 605 |
-
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 606 |
-
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 607 |
-
ref_image_groups.append(img_col)
|
| 608 |
-
ref_image_inputs.append(img_comp)
|
| 609 |
-
|
| 610 |
-
components[key('joyai_reference_rows')] = ref_image_groups
|
| 611 |
-
components[key('joyai_reference_images')] = ref_image_inputs
|
| 612 |
-
|
| 613 |
-
with gr.Row():
|
| 614 |
-
components[key('add_joyai_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 615 |
-
components[key('delete_joyai_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 616 |
-
components[key('joyai_reference_count_state')] = gr.State(1)
|
| 617 |
-
|
| 618 |
-
components[key('all_joyai_reference_components_flat')] = ref_image_inputs
|
| 619 |
-
|
| 620 |
-
return components
|
| 621 |
-
|
| 622 |
-
def create_reference_image_ui(prefix: str, max_units=10):
|
| 623 |
-
components = {}
|
| 624 |
-
key = lambda name: f"{name}_{prefix}"
|
| 625 |
-
|
| 626 |
-
with gr.Accordion("Reference Image Edit Settings", open=False, visible=('reference_image' in default_enabled_chains)) as ref_accordion:
|
| 627 |
-
components[key('reference_image_accordion')] = ref_accordion
|
| 628 |
-
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 629 |
-
|
| 630 |
-
ref_image_groups = []
|
| 631 |
-
ref_image_inputs = []
|
| 632 |
-
with gr.Row():
|
| 633 |
-
for i in range(max_units):
|
| 634 |
-
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 635 |
-
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 636 |
-
ref_image_groups.append(img_col)
|
| 637 |
-
ref_image_inputs.append(img_comp)
|
| 638 |
-
|
| 639 |
-
components[key('reference_image_rows')] = ref_image_groups
|
| 640 |
-
components[key('reference_image_images')] = ref_image_inputs
|
| 641 |
-
|
| 642 |
-
with gr.Row():
|
| 643 |
-
components[key('add_reference_image_button')] = gr.Button("✚ Add Reference Image")
|
| 644 |
-
components[key('delete_reference_image_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 645 |
-
components[key('reference_image_count_state')] = gr.State(1)
|
| 646 |
-
|
| 647 |
-
components[key('all_reference_image_components_flat')] = ref_image_inputs
|
| 648 |
-
|
| 649 |
-
return components
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
gr.
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
components[key('
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
components[key('
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
components[key('
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
)
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
| 835 |
-
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
| 3 |
+
from core.settings import (
|
| 4 |
+
MAX_LORAS, LORA_SOURCE_CHOICES, MAX_EMBEDDINGS, MAX_CONDITIONINGS,
|
| 5 |
+
MAX_CONTROLNETS, MAX_IPADAPTERS, MAX_MOSS_IMAGE_EDITS, RESOLUTION_MAP, ARCHITECTURES_CONFIG,
|
| 6 |
+
MODEL_MAP_CHECKPOINT, MODEL_TYPE_MAP, FEATURES_CONFIG, ARCH_CATEGORIES_MAP,
|
| 7 |
+
VAE_DIR, MODEL_DEFAULTS_CONFIG
|
| 8 |
+
)
|
| 9 |
+
import yaml
|
| 10 |
+
import os
|
| 11 |
+
from functools import lru_cache
|
| 12 |
+
from utils.app_utils import save_uploaded_file
|
| 13 |
+
|
| 14 |
+
default_model_name = list(MODEL_MAP_CHECKPOINT.keys())[0] if MODEL_MAP_CHECKPOINT else None
|
| 15 |
+
default_m_type = MODEL_TYPE_MAP.get(default_model_name, "SDXL") if default_model_name else "SDXL"
|
| 16 |
+
default_architectures_dict = ARCHITECTURES_CONFIG.get('architectures', {})
|
| 17 |
+
default_arch_model_type = default_architectures_dict.get(default_m_type, {}).get("model_type", default_m_type.lower().replace(" ", "").replace(".", ""))
|
| 18 |
+
default_arch_features = FEATURES_CONFIG.get(default_arch_model_type, {})
|
| 19 |
+
default_enabled_chains = default_arch_features.get('enabled_chains', [])
|
| 20 |
+
|
| 21 |
+
default_vals = MODEL_DEFAULTS_CONFIG.get('Default', {})
|
| 22 |
+
DEFAULT_STEPS = default_vals.get('steps', 20)
|
| 23 |
+
DEFAULT_CFG = default_vals.get('cfg', 5.0)
|
| 24 |
+
DEFAULT_SAMPLER = default_vals.get('sampler_name', 'euler')
|
| 25 |
+
DEFAULT_SCHEDULER = default_vals.get('scheduler', 'simple')
|
| 26 |
+
DEFAULT_POS_PROMPT = default_vals.get('positive_prompt', '')
|
| 27 |
+
DEFAULT_NEG_PROMPT = default_vals.get('negative_prompt', '')
|
| 28 |
+
|
| 29 |
+
@lru_cache(maxsize=1)
|
| 30 |
+
def get_ipadapter_config_from_yaml():
|
| 31 |
+
try:
|
| 32 |
+
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 33 |
+
_IPADAPTER_LIST_PATH = os.path.join(_PROJECT_ROOT, 'yaml', 'ipadapter.yaml')
|
| 34 |
+
with open(_IPADAPTER_LIST_PATH, 'r', encoding='utf-8') as f:
|
| 35 |
+
config = yaml.safe_load(f)
|
| 36 |
+
return config
|
| 37 |
+
except Exception as e:
|
| 38 |
+
print(f"Warning: Could not load ipadapter.yaml for UI components: {e}")
|
| 39 |
+
return {}
|
| 40 |
+
|
| 41 |
+
def get_ipadapter_presets(arch="SDXL"):
|
| 42 |
+
config = get_ipadapter_config_from_yaml()
|
| 43 |
+
presets = []
|
| 44 |
+
if config:
|
| 45 |
+
std_presets = config.get("IPAdapter_presets", {}).get(arch, [])
|
| 46 |
+
face_presets = config.get("IPAdapter_FaceID_presets", {}).get(arch, [])
|
| 47 |
+
if std_presets:
|
| 48 |
+
presets.extend(std_presets)
|
| 49 |
+
if face_presets:
|
| 50 |
+
presets.extend(face_presets)
|
| 51 |
+
return presets if presets else ["STANDARD (medium strength)"]
|
| 52 |
+
|
| 53 |
+
def create_model_architecture_filter_ui(prefix):
|
| 54 |
+
components = {}
|
| 55 |
+
ordered_architectures = ARCHITECTURES_CONFIG.get("architecture_order", [])
|
| 56 |
+
choices = ["ALL"] + ordered_architectures
|
| 57 |
+
|
| 58 |
+
components[f'model_arch_{prefix}'] = gr.Radio(
|
| 59 |
+
label="Model Architecture",
|
| 60 |
+
choices=choices,
|
| 61 |
+
value="ALL",
|
| 62 |
+
interactive=True,
|
| 63 |
+
visible=True
|
| 64 |
+
)
|
| 65 |
+
return components
|
| 66 |
+
|
| 67 |
+
def create_category_filter_ui(prefix):
|
| 68 |
+
valid_cats = list(set(cat for cats in ARCH_CATEGORIES_MAP.values() for cat in cats))
|
| 69 |
+
cat_choices = ["ALL"] + sorted(valid_cats)
|
| 70 |
+
|
| 71 |
+
components = {}
|
| 72 |
+
components[f'model_cat_{prefix}'] = gr.Dropdown(
|
| 73 |
+
label="Filter Models",
|
| 74 |
+
choices=cat_choices,
|
| 75 |
+
value="ALL",
|
| 76 |
+
interactive=True,
|
| 77 |
+
scale=1,
|
| 78 |
+
allow_custom_value=True
|
| 79 |
+
)
|
| 80 |
+
return components
|
| 81 |
+
|
| 82 |
+
def create_base_parameter_ui(prefix, defaults=None):
|
| 83 |
+
if defaults is None:
|
| 84 |
+
defaults = {}
|
| 85 |
+
|
| 86 |
+
components = {}
|
| 87 |
+
|
| 88 |
+
with gr.Row():
|
| 89 |
+
components[f'aspect_ratio_{prefix}'] = gr.Dropdown(
|
| 90 |
+
label="Aspect Ratio",
|
| 91 |
+
choices=list(RESOLUTION_MAP.get('sdxl', {}).keys()),
|
| 92 |
+
value="1:1 (Square)",
|
| 93 |
+
interactive=True,
|
| 94 |
+
allow_custom_value=True
|
| 95 |
+
)
|
| 96 |
+
with gr.Row():
|
| 97 |
+
components[f'width_{prefix}'] = gr.Number(label="Width", value=defaults.get('w', 1024), interactive=True)
|
| 98 |
+
components[f'height_{prefix}'] = gr.Number(label="Height", value=defaults.get('h', 1024), interactive=True)
|
| 99 |
+
with gr.Row():
|
| 100 |
+
components[f'sampler_{prefix}'] = gr.Dropdown(
|
| 101 |
+
label="Sampler",
|
| 102 |
+
choices=SAMPLER_CHOICES,
|
| 103 |
+
value=DEFAULT_SAMPLER if DEFAULT_SAMPLER in SAMPLER_CHOICES else (SAMPLER_CHOICES[0] if SAMPLER_CHOICES else 'euler')
|
| 104 |
+
)
|
| 105 |
+
components[f'scheduler_{prefix}'] = gr.Dropdown(
|
| 106 |
+
label="Scheduler",
|
| 107 |
+
choices=SCHEDULER_CHOICES,
|
| 108 |
+
value=DEFAULT_SCHEDULER if DEFAULT_SCHEDULER in SCHEDULER_CHOICES else (SCHEDULER_CHOICES[0] if SCHEDULER_CHOICES else 'simple')
|
| 109 |
+
)
|
| 110 |
+
with gr.Row():
|
| 111 |
+
components[f'steps_{prefix}'] = gr.Slider(label="Steps", minimum=1, maximum=100, step=1, value=DEFAULT_STEPS)
|
| 112 |
+
components[f'cfg_{prefix}'] = gr.Slider(label="CFG Scale", minimum=1.0, maximum=20.0, step=0.1, value=DEFAULT_CFG)
|
| 113 |
+
with gr.Row():
|
| 114 |
+
components[f'seed_{prefix}'] = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
|
| 115 |
+
components[f'batch_size_{prefix}'] = gr.Slider(label="Batch Size", minimum=1, maximum=16, step=1, value=1)
|
| 116 |
+
with gr.Row():
|
| 117 |
+
components[f'zero_gpu_{prefix}'] = gr.Number(label="ZeroGPU Duration (s)", value=60, placeholder="Default: 60s", info="Optional: Set how long to reserve the GPU.")
|
| 118 |
+
|
| 119 |
+
return components
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def create_lora_settings_ui(prefix: str):
|
| 123 |
+
components = {}
|
| 124 |
+
|
| 125 |
+
lora_rows, lora_sources, lora_ids, lora_scales, lora_uploads = [], [], [], [], []
|
| 126 |
+
|
| 127 |
+
with gr.Accordion("LoRA Settings", open=False, visible=('lora' in default_enabled_chains)) as lora_accordion:
|
| 128 |
+
components[f'lora_accordion_{prefix}'] = lora_accordion
|
| 129 |
+
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `lightx2v/Qwen-Image-Lightning/Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors`).")
|
| 130 |
+
components[f'lora_count_state_{prefix}'] = gr.State(1)
|
| 131 |
+
|
| 132 |
+
for i in range(MAX_LORAS):
|
| 133 |
+
with gr.Row(visible=i==0) as row:
|
| 134 |
+
source = gr.Dropdown(label=f"LoRA Source {i+1}", choices=LORA_SOURCE_CHOICES, value=LORA_SOURCE_CHOICES[0], scale=1)
|
| 135 |
+
lora_id = gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=2, type="text")
|
| 136 |
+
scale = gr.Slider(label=f"Scale", minimum=0.0, maximum=2.0, step=0.05, value=1.0, scale=1)
|
| 137 |
+
upload = gr.UploadButton(label="Upload", file_types=[".safetensors"], scale=1)
|
| 138 |
+
|
| 139 |
+
lora_rows.append(row)
|
| 140 |
+
lora_sources.append(source)
|
| 141 |
+
lora_ids.append(lora_id)
|
| 142 |
+
lora_scales.append(scale)
|
| 143 |
+
lora_uploads.append(upload)
|
| 144 |
+
|
| 145 |
+
with gr.Row():
|
| 146 |
+
components[f'add_lora_button_{prefix}'] = gr.Button("Add LoRA", variant="secondary")
|
| 147 |
+
components[f'delete_lora_button_{prefix}'] = gr.Button("Remove LoRA", variant="secondary", visible=False)
|
| 148 |
+
|
| 149 |
+
components[f'lora_rows_{prefix}'] = lora_rows
|
| 150 |
+
components[f'lora_sources_{prefix}'] = lora_sources
|
| 151 |
+
components[f'lora_ids_{prefix}'] = lora_ids
|
| 152 |
+
components[f'lora_scales_{prefix}'] = lora_scales
|
| 153 |
+
components[f'lora_uploads_{prefix}'] = lora_uploads
|
| 154 |
+
|
| 155 |
+
all_lora_components_flat = []
|
| 156 |
+
for i in range(MAX_LORAS):
|
| 157 |
+
all_lora_components_flat.extend([lora_sources[i], lora_ids[i], lora_scales[i], lora_uploads[i]])
|
| 158 |
+
components[f'all_lora_components_flat_{prefix}'] = all_lora_components_flat
|
| 159 |
+
|
| 160 |
+
return components
|
| 161 |
+
|
| 162 |
+
def create_controlnet_ui(prefix: str, max_units=MAX_CONTROLNETS):
|
| 163 |
+
components = {}
|
| 164 |
+
key = lambda name: f"{name}_{prefix}"
|
| 165 |
+
|
| 166 |
+
with gr.Accordion("ControlNet Settings", open=False, visible=('controlnet' in default_enabled_chains)) as accordion:
|
| 167 |
+
components[key('controlnet_accordion')] = accordion
|
| 168 |
+
|
| 169 |
+
cn_rows, images, series, types, strengths, filepaths = [], [], [], [], [], []
|
| 170 |
+
components.update({
|
| 171 |
+
key('controlnet_rows'): cn_rows,
|
| 172 |
+
key('controlnet_images'): images,
|
| 173 |
+
key('controlnet_series'): series,
|
| 174 |
+
key('controlnet_types'): types,
|
| 175 |
+
key('controlnet_strengths'): strengths,
|
| 176 |
+
key('controlnet_filepaths'): filepaths
|
| 177 |
+
})
|
| 178 |
+
|
| 179 |
+
for i in range(max_units):
|
| 180 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 181 |
+
with gr.Column(scale=1):
|
| 182 |
+
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 183 |
+
with gr.Column(scale=2):
|
| 184 |
+
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
|
| 185 |
+
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
|
| 186 |
+
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 187 |
+
filepaths.append(gr.State(None))
|
| 188 |
+
cn_rows.append(row)
|
| 189 |
+
|
| 190 |
+
with gr.Row():
|
| 191 |
+
components[key('add_controlnet_button')] = gr.Button("✚ Add ControlNet")
|
| 192 |
+
components[key('delete_controlnet_button')] = gr.Button("➖ Delete ControlNet", visible=False)
|
| 193 |
+
components[key('controlnet_count_state')] = gr.State(1)
|
| 194 |
+
|
| 195 |
+
all_cn_components_flat = []
|
| 196 |
+
for i in range(max_units):
|
| 197 |
+
all_cn_components_flat.extend([
|
| 198 |
+
images[i], types[i], series[i], strengths[i], filepaths[i]
|
| 199 |
+
])
|
| 200 |
+
components[key('all_controlnet_components_flat')] = all_cn_components_flat
|
| 201 |
+
|
| 202 |
+
return components
|
| 203 |
+
|
| 204 |
+
def create_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 205 |
+
components = {}
|
| 206 |
+
key = lambda name: f"{name}_{prefix}"
|
| 207 |
+
|
| 208 |
+
sdxl_presets = get_ipadapter_presets("SDXL")
|
| 209 |
+
default_preset = sdxl_presets[0] if sdxl_presets else None
|
| 210 |
+
|
| 211 |
+
with gr.Accordion("IPAdapter Settings", open=False, visible=('ipadapter' in default_enabled_chains)) as accordion:
|
| 212 |
+
components[key('ipadapter_accordion')] = accordion
|
| 213 |
+
gr.Markdown("💡 **Tip:** Processed using the [cubiq/ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) node.")
|
| 214 |
+
|
| 215 |
+
with gr.Row():
|
| 216 |
+
components[key('ipadapter_final_preset')] = gr.Dropdown(
|
| 217 |
+
label="Preset (for all images)",
|
| 218 |
+
choices=sdxl_presets,
|
| 219 |
+
value=default_preset,
|
| 220 |
+
interactive=True,
|
| 221 |
+
allow_custom_value=True
|
| 222 |
+
)
|
| 223 |
+
components[key('ipadapter_embeds_scaling')] = gr.Dropdown(
|
| 224 |
+
label="Embeds Scaling",
|
| 225 |
+
choices=['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],
|
| 226 |
+
value='V only',
|
| 227 |
+
interactive=True
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
with gr.Row():
|
| 231 |
+
components[key('ipadapter_combine_method')] = gr.Dropdown(
|
| 232 |
+
label="Combine Method",
|
| 233 |
+
choices=["concat", "add", "subtract", "average", "norm average", "max", "min"],
|
| 234 |
+
value="concat",
|
| 235 |
+
interactive=True
|
| 236 |
+
)
|
| 237 |
+
components[key('ipadapter_final_weight')] = gr.Slider(label="Final Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True)
|
| 238 |
+
components[key('ipadapter_final_lora_strength')] = gr.Slider(label="Final LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False)
|
| 239 |
+
|
| 240 |
+
gr.Markdown("---")
|
| 241 |
+
|
| 242 |
+
ipa_rows, images, weights, lora_strengths = [], [], [], []
|
| 243 |
+
components.update({
|
| 244 |
+
key('ipadapter_rows'): ipa_rows,
|
| 245 |
+
key('ipadapter_images'): images,
|
| 246 |
+
key('ipadapter_weights'): weights,
|
| 247 |
+
key('ipadapter_lora_strengths'): lora_strengths
|
| 248 |
+
})
|
| 249 |
+
|
| 250 |
+
for i in range(max_units):
|
| 251 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 252 |
+
with gr.Column(scale=1):
|
| 253 |
+
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 254 |
+
with gr.Column(scale=2):
|
| 255 |
+
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 256 |
+
lora_strengths.append(gr.Slider(label="LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False))
|
| 257 |
+
ipa_rows.append(row)
|
| 258 |
+
|
| 259 |
+
with gr.Row():
|
| 260 |
+
components[key('add_ipadapter_button')] = gr.Button("✚ Add IPAdapter")
|
| 261 |
+
components[key('delete_ipadapter_button')] = gr.Button("➖ Delete IPAdapter", visible=False)
|
| 262 |
+
components[key('ipadapter_count_state')] = gr.State(1)
|
| 263 |
+
|
| 264 |
+
all_ipa_components_flat = images + weights + lora_strengths
|
| 265 |
+
all_ipa_components_flat += [
|
| 266 |
+
components[key('ipadapter_final_preset')],
|
| 267 |
+
components[key('ipadapter_final_weight')],
|
| 268 |
+
components[key('ipadapter_final_lora_strength')],
|
| 269 |
+
components[key('ipadapter_embeds_scaling')],
|
| 270 |
+
components[key('ipadapter_combine_method')],
|
| 271 |
+
]
|
| 272 |
+
components[key('all_ipadapter_components_flat')] = all_ipa_components_flat
|
| 273 |
+
|
| 274 |
+
return components
|
| 275 |
+
|
| 276 |
+
def create_flux1_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 277 |
+
components = {}
|
| 278 |
+
key = lambda name: f"{name}_{prefix}"
|
| 279 |
+
|
| 280 |
+
with gr.Accordion("IPAdapter Settings (FLUX.1)", open=False, visible=('flux1_ipadapter' in default_enabled_chains)) as accordion:
|
| 281 |
+
components[key('flux1_ipadapter_accordion')] = accordion
|
| 282 |
+
gr.Markdown("💡 **Tip:** Processed using the [Shakker-Labs/ComfyUI-IPAdapter-Flux](https://github.com/Shakker-Labs/ComfyUI-IPAdapter-Flux) node.")
|
| 283 |
+
|
| 284 |
+
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
|
| 285 |
+
components.update({
|
| 286 |
+
key('flux1_ipadapter_rows'): ipa_rows,
|
| 287 |
+
key('flux1_ipadapter_images'): images,
|
| 288 |
+
key('flux1_ipadapter_weights'): weights,
|
| 289 |
+
key('flux1_ipadapter_start_percents'): start_percents,
|
| 290 |
+
key('flux1_ipadapter_end_percents'): end_percents,
|
| 291 |
+
})
|
| 292 |
+
|
| 293 |
+
for i in range(max_units):
|
| 294 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 295 |
+
with gr.Column(scale=1):
|
| 296 |
+
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 297 |
+
with gr.Column(scale=2):
|
| 298 |
+
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True))
|
| 299 |
+
with gr.Row():
|
| 300 |
+
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
|
| 301 |
+
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=0.6, interactive=True))
|
| 302 |
+
ipa_rows.append(row)
|
| 303 |
+
|
| 304 |
+
with gr.Row():
|
| 305 |
+
components[key('add_flux1_ipadapter_button')] = gr.Button("✚ Add IPAdapter (FLUX)")
|
| 306 |
+
components[key('delete_flux1_ipadapter_button')] = gr.Button("➖ Delete IPAdapter (FLUX)", visible=False)
|
| 307 |
+
components[key('flux1_ipadapter_count_state')] = gr.State(1)
|
| 308 |
+
|
| 309 |
+
all_flux1_ipa_components_flat = images + weights + start_percents + end_percents
|
| 310 |
+
components[key('all_flux1_ipadapter_components_flat')] = all_flux1_ipa_components_flat
|
| 311 |
+
|
| 312 |
+
return components
|
| 313 |
+
|
| 314 |
+
def create_sd3_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
|
| 315 |
+
components = {}
|
| 316 |
+
key = lambda name: f"{name}_{prefix}"
|
| 317 |
+
|
| 318 |
+
with gr.Accordion("IPAdapter Settings (SD3)", open=False, visible=('sd3_ipadapter' in default_enabled_chains)) as accordion:
|
| 319 |
+
components[key('sd3_ipadapter_accordion')] = accordion
|
| 320 |
+
gr.Markdown("💡 **Tip:** Processed using the [Slickytail/ComfyUI-InstantX-IPAdapter-SD3](https://github.com/Slickytail/ComfyUI-InstantX-IPAdapter-SD3) node.")
|
| 321 |
+
|
| 322 |
+
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
|
| 323 |
+
components.update({
|
| 324 |
+
key('sd3_ipadapter_rows'): ipa_rows,
|
| 325 |
+
key('sd3_ipadapter_images'): images,
|
| 326 |
+
key('sd3_ipadapter_weights'): weights,
|
| 327 |
+
key('sd3_ipadapter_start_percents'): start_percents,
|
| 328 |
+
key('sd3_ipadapter_end_percents'): end_percents,
|
| 329 |
+
})
|
| 330 |
+
|
| 331 |
+
for i in range(max_units):
|
| 332 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 333 |
+
with gr.Column(scale=1):
|
| 334 |
+
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 335 |
+
with gr.Column(scale=2):
|
| 336 |
+
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.5, interactive=True))
|
| 337 |
+
with gr.Row():
|
| 338 |
+
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
|
| 339 |
+
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=1.0, interactive=True))
|
| 340 |
+
ipa_rows.append(row)
|
| 341 |
+
|
| 342 |
+
with gr.Row():
|
| 343 |
+
components[key('add_sd3_ipadapter_button')] = gr.Button("✚ Add IPAdapter (SD3)")
|
| 344 |
+
components[key('delete_sd3_ipadapter_button')] = gr.Button("➖ Delete IPAdapter (SD3)", visible=False)
|
| 345 |
+
components[key('sd3_ipadapter_count_state')] = gr.State(1)
|
| 346 |
+
|
| 347 |
+
all_sd3_ipa_components_flat = images + weights + start_percents + end_percents
|
| 348 |
+
components[key('all_sd3_ipadapter_components_flat')] = all_sd3_ipa_components_flat
|
| 349 |
+
|
| 350 |
+
return components
|
| 351 |
+
|
| 352 |
+
def create_style_ui(prefix: str):
|
| 353 |
+
components = {}
|
| 354 |
+
key = lambda name: f"{name}_{prefix}"
|
| 355 |
+
|
| 356 |
+
with gr.Accordion("Style Settings (FLUX.1)", open=False, visible=('style' in default_enabled_chains)) as accordion:
|
| 357 |
+
components[key('style_accordion')] = accordion
|
| 358 |
+
|
| 359 |
+
style_rows, images, strengths = [], [], []
|
| 360 |
+
components.update({
|
| 361 |
+
key('style_rows'): style_rows,
|
| 362 |
+
key('style_images'): images,
|
| 363 |
+
key('style_strengths'): strengths
|
| 364 |
+
})
|
| 365 |
+
|
| 366 |
+
for i in range(5):
|
| 367 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 368 |
+
with gr.Column(scale=1):
|
| 369 |
+
images.append(gr.Image(label=f"Style Image {i+1}", type="pil", sources=["upload"], height=256))
|
| 370 |
+
with gr.Column(scale=2):
|
| 371 |
+
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
|
| 372 |
+
style_rows.append(row)
|
| 373 |
+
|
| 374 |
+
with gr.Row():
|
| 375 |
+
components[key('add_style_button')] = gr.Button("✚ Add Style (FLUX)")
|
| 376 |
+
components[key('delete_style_button')] = gr.Button("➖ Delete Style (FLUX)", visible=False)
|
| 377 |
+
components[key('style_count_state')] = gr.State(1)
|
| 378 |
+
|
| 379 |
+
all_style_components_flat = images + strengths
|
| 380 |
+
components[key('all_style_components_flat')] = all_style_components_flat
|
| 381 |
+
|
| 382 |
+
return components
|
| 383 |
+
|
| 384 |
+
def create_embedding_ui(prefix: str):
|
| 385 |
+
components = {}
|
| 386 |
+
key = lambda name: f"{name}_{prefix}"
|
| 387 |
+
|
| 388 |
+
with gr.Accordion("Embedding Settings", open=False, visible=('embedding' in default_enabled_chains)) as accordion:
|
| 389 |
+
components[key('embedding_accordion')] = accordion
|
| 390 |
+
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. For example, entering the Version ID 456 will automatically save the file as \"civitai/456.safetensors\", and you will need to manually enter `embedding:civitai/456` in either your prompt or negative prompt to activate it. When downloading from Hugging Face, please use the format: repo_id/filename.extension or repo_id/folder_path/filename.extension (e.g., ilikebigturtles/lazypos/lazypos.safetensors or ilikebigturtles/lazyneg/lazyneg.safetensors). For Hugging Face files, you will need to enter embedding:huggingface/filename (e.g., entering embedding:huggingface/lazypos in your positive prompt, or embedding:huggingface/lazyneg in your negative prompt) to activate it. For uploaded files, enter `embedding:upload_file/filename` in either your positive prompt or negative prompt to activate it.")
|
| 391 |
+
|
| 392 |
+
embedding_rows, sources, ids, files, upload_buttons = [], [], [], [], []
|
| 393 |
+
components.update({
|
| 394 |
+
key('embedding_rows'): embedding_rows,
|
| 395 |
+
key('embeddings_sources'): sources,
|
| 396 |
+
key('embeddings_ids'): ids,
|
| 397 |
+
key('embeddings_files'): files,
|
| 398 |
+
key('embeddings_uploads'): upload_buttons
|
| 399 |
+
})
|
| 400 |
+
|
| 401 |
+
for i in range(MAX_EMBEDDINGS):
|
| 402 |
+
with gr.Row(visible=(i < 1)) as row:
|
| 403 |
+
sources.append(gr.Dropdown(label=f"Embedding Source {i+1}", choices=LORA_SOURCE_CHOICES, value="Civitai", scale=1, interactive=True))
|
| 404 |
+
ids.append(gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=3, interactive=True, type="text"))
|
| 405 |
+
upload_btn = gr.UploadButton("Upload", file_types=[".safetensors"], scale=1)
|
| 406 |
+
files.append(gr.State(None))
|
| 407 |
+
upload_buttons.append(upload_btn)
|
| 408 |
+
embedding_rows.append(row)
|
| 409 |
+
|
| 410 |
+
with gr.Row():
|
| 411 |
+
components[key('add_embedding_button')] = gr.Button("✚ Add Embedding")
|
| 412 |
+
components[key('delete_embedding_button')] = gr.Button("➖ Delete Embedding", visible=False)
|
| 413 |
+
components[key('embedding_count_state')] = gr.State(1)
|
| 414 |
+
|
| 415 |
+
all_embedding_components_flat = []
|
| 416 |
+
for i in range(MAX_EMBEDDINGS):
|
| 417 |
+
all_embedding_components_flat.extend([sources[i], ids[i], files[i]])
|
| 418 |
+
components[key('all_embedding_components_flat')] = all_embedding_components_flat
|
| 419 |
+
|
| 420 |
+
return components
|
| 421 |
+
|
| 422 |
+
def create_conditioning_ui(prefix: str):
|
| 423 |
+
components = {}
|
| 424 |
+
key = lambda name: f"{name}_{prefix}"
|
| 425 |
+
|
| 426 |
+
with gr.Accordion("Conditioning Settings", open=False, visible=('conditioning' in default_enabled_chains)) as accordion:
|
| 427 |
+
components[key('conditioning_accordion')] = accordion
|
| 428 |
+
gr.Markdown("💡 **Tip:** Define rectangular areas and assign specific prompts to them. Coordinates (X, Y) start from the top-left corner.")
|
| 429 |
+
|
| 430 |
+
cond_rows, prompts, widths, heights, xs, ys, strengths = [], [], [], [], [], [], []
|
| 431 |
+
components.update({
|
| 432 |
+
key('conditioning_rows'): cond_rows,
|
| 433 |
+
key('conditioning_prompts'): prompts,
|
| 434 |
+
key('conditioning_widths'): widths,
|
| 435 |
+
key('conditioning_heights'): heights,
|
| 436 |
+
key('conditioning_xs'): xs,
|
| 437 |
+
key('conditioning_ys'): ys,
|
| 438 |
+
key('conditioning_strengths'): strengths
|
| 439 |
+
})
|
| 440 |
+
|
| 441 |
+
for i in range(MAX_CONDITIONINGS):
|
| 442 |
+
with gr.Column(visible=(i < 1)) as row_wrapper:
|
| 443 |
+
prompts.append(gr.Textbox(label=f"Area Prompt {i+1}", lines=2, interactive=True))
|
| 444 |
+
with gr.Row():
|
| 445 |
+
xs.append(gr.Number(label="X", value=0, interactive=True, step=8, scale=1))
|
| 446 |
+
ys.append(gr.Number(label="Y", value=0, interactive=True, step=8, scale=1))
|
| 447 |
+
widths.append(gr.Number(label="Width", value=512, interactive=True, step=8, scale=1))
|
| 448 |
+
heights.append(gr.Number(label="Height", value=512, interactive=True, step=8, scale=1))
|
| 449 |
+
strengths.append(gr.Slider(label="Strength", minimum=0.1, maximum=2.0, step=0.05, value=1.0, interactive=True, scale=2))
|
| 450 |
+
cond_rows.append(row_wrapper)
|
| 451 |
+
|
| 452 |
+
with gr.Row():
|
| 453 |
+
components[key('add_conditioning_button')] = gr.Button("✚ Add Area")
|
| 454 |
+
components[key('delete_conditioning_button')] = gr.Button("➖ Delete Area", visible=False)
|
| 455 |
+
components[key('conditioning_count_state')] = gr.State(1)
|
| 456 |
+
|
| 457 |
+
all_cond_components_flat = prompts + widths + heights + xs + ys + strengths
|
| 458 |
+
components[key('all_conditioning_components_flat')] = all_cond_components_flat
|
| 459 |
+
|
| 460 |
+
return components
|
| 461 |
+
|
| 462 |
+
def on_vae_upload(file_obj):
|
| 463 |
+
if not file_obj:
|
| 464 |
+
return gr.update(), gr.update(), None
|
| 465 |
+
|
| 466 |
+
uploaded_filename = save_uploaded_file(file_obj, VAE_DIR)
|
| 467 |
+
return uploaded_filename, "Upload File", file_obj
|
| 468 |
+
|
| 469 |
+
def create_vae_override_ui(prefix: str):
|
| 470 |
+
components = {}
|
| 471 |
+
key = lambda name: f"{name}_{prefix}"
|
| 472 |
+
|
| 473 |
+
with gr.Accordion("VAE Settings (Override)", open=False, visible=('vae' in default_enabled_chains)) as vae_accordion:
|
| 474 |
+
components[key('vae_accordion')] = vae_accordion
|
| 475 |
+
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `madebyollin/sdxl-vae-fp16-fix/sdxl_vae.safetensors`).")
|
| 476 |
+
with gr.Row():
|
| 477 |
+
components[key('vae_source')] = gr.Dropdown(
|
| 478 |
+
label="VAE Source",
|
| 479 |
+
choices=LORA_SOURCE_CHOICES,
|
| 480 |
+
value="Civitai",
|
| 481 |
+
scale=1,
|
| 482 |
+
interactive=True
|
| 483 |
+
)
|
| 484 |
+
components[key('vae_id')] = gr.Textbox(
|
| 485 |
+
label="Civitai Version ID / HF file / Upload File",
|
| 486 |
+
scale=3,
|
| 487 |
+
interactive=True,
|
| 488 |
+
type="text"
|
| 489 |
+
)
|
| 490 |
+
upload_btn = gr.UploadButton(
|
| 491 |
+
"Upload",
|
| 492 |
+
file_types=[".safetensors"],
|
| 493 |
+
scale=1
|
| 494 |
+
)
|
| 495 |
+
components[key('vae_upload_button')] = upload_btn
|
| 496 |
+
components[key('vae_file')] = gr.State(None)
|
| 497 |
+
|
| 498 |
+
upload_btn.upload(
|
| 499 |
+
fn=on_vae_upload,
|
| 500 |
+
inputs=[upload_btn],
|
| 501 |
+
outputs=[components[key('vae_id')], components[key('vae_source')], components[key('vae_file')]]
|
| 502 |
+
)
|
| 503 |
+
|
| 504 |
+
return components
|
| 505 |
+
|
| 506 |
+
def create_reference_latent_ui(prefix: str, max_units=10):
|
| 507 |
+
components = {}
|
| 508 |
+
key = lambda name: f"{name}_{prefix}"
|
| 509 |
+
|
| 510 |
+
with gr.Accordion("Reference Edit Settings", open=False, visible=('reference_latent' in default_enabled_chains)) as ref_accordion:
|
| 511 |
+
components[key('reference_latent_accordion')] = ref_accordion
|
| 512 |
+
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 513 |
+
|
| 514 |
+
ref_image_groups = []
|
| 515 |
+
ref_image_inputs = []
|
| 516 |
+
with gr.Row():
|
| 517 |
+
for i in range(max_units):
|
| 518 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 519 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 520 |
+
ref_image_groups.append(img_col)
|
| 521 |
+
ref_image_inputs.append(img_comp)
|
| 522 |
+
|
| 523 |
+
components[key('reference_latent_rows')] = ref_image_groups
|
| 524 |
+
components[key('reference_latent_images')] = ref_image_inputs
|
| 525 |
+
|
| 526 |
+
with gr.Row():
|
| 527 |
+
components[key('add_reference_latent_button')] = gr.Button("✚ Add Reference Image")
|
| 528 |
+
components[key('delete_reference_latent_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 529 |
+
components[key('reference_latent_count_state')] = gr.State(1)
|
| 530 |
+
|
| 531 |
+
components[key('all_reference_latent_components_flat')] = ref_image_inputs
|
| 532 |
+
|
| 533 |
+
return components
|
| 534 |
+
|
| 535 |
+
def create_hidream_o1_reference_ui(prefix: str, max_units=10):
|
| 536 |
+
components = {}
|
| 537 |
+
key = lambda name: f"{name}_{prefix}"
|
| 538 |
+
|
| 539 |
+
with gr.Accordion("HiDream-O1 Reference Edit Settings", open=False, visible=('hidream_o1_reference' in default_enabled_chains)) as ref_accordion:
|
| 540 |
+
components[key('hidream_o1_reference_accordion')] = ref_accordion
|
| 541 |
+
gr.Markdown("💡 **Tip:** Please use **HiDream-O1-Image-Dev** (HiDream-O1-Image will time out), and set the resolution to **4.0MP** (e.g., 2048x2048). In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 542 |
+
|
| 543 |
+
ref_image_groups = []
|
| 544 |
+
ref_image_inputs = []
|
| 545 |
+
with gr.Row():
|
| 546 |
+
for i in range(max_units):
|
| 547 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 548 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 549 |
+
ref_image_groups.append(img_col)
|
| 550 |
+
ref_image_inputs.append(img_comp)
|
| 551 |
+
|
| 552 |
+
components[key('hidream_o1_reference_rows')] = ref_image_groups
|
| 553 |
+
components[key('hidream_o1_reference_images')] = ref_image_inputs
|
| 554 |
+
|
| 555 |
+
with gr.Row():
|
| 556 |
+
components[key('add_hidream_o1_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 557 |
+
components[key('delete_hidream_o1_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 558 |
+
components[key('hidream_o1_reference_count_state')] = gr.State(1)
|
| 559 |
+
|
| 560 |
+
components[key('all_hidream_o1_reference_components_flat')] = ref_image_inputs
|
| 561 |
+
|
| 562 |
+
return components
|
| 563 |
+
|
| 564 |
+
def create_sensenova_reference_ui(prefix: str, max_units=10):
|
| 565 |
+
components = {}
|
| 566 |
+
key = lambda name: f"{name}_{prefix}"
|
| 567 |
+
|
| 568 |
+
with gr.Accordion("SenseNova Reference Edit Settings", open=False, visible=('sensenova_reference' in default_enabled_chains)) as ref_accordion:
|
| 569 |
+
components[key('sensenova_reference_accordion')] = ref_accordion
|
| 570 |
+
gr.Markdown("💡 **Tip:** For SenseNova models, this feature enables reference image editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 571 |
+
|
| 572 |
+
ref_image_groups = []
|
| 573 |
+
ref_image_inputs = []
|
| 574 |
+
with gr.Row():
|
| 575 |
+
for i in range(max_units):
|
| 576 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 577 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 578 |
+
ref_image_groups.append(img_col)
|
| 579 |
+
ref_image_inputs.append(img_comp)
|
| 580 |
+
|
| 581 |
+
components[key('sensenova_reference_rows')] = ref_image_groups
|
| 582 |
+
components[key('sensenova_reference_images')] = ref_image_inputs
|
| 583 |
+
|
| 584 |
+
with gr.Row():
|
| 585 |
+
components[key('add_sensenova_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 586 |
+
components[key('delete_sensenova_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 587 |
+
components[key('sensenova_reference_count_state')] = gr.State(1)
|
| 588 |
+
|
| 589 |
+
components[key('all_sensenova_reference_components_flat')] = ref_image_inputs
|
| 590 |
+
|
| 591 |
+
return components
|
| 592 |
+
|
| 593 |
+
def create_joyai_reference_ui(prefix: str, max_units=2):
|
| 594 |
+
components = {}
|
| 595 |
+
key = lambda name: f"{name}_{prefix}"
|
| 596 |
+
|
| 597 |
+
with gr.Accordion("JoyAI Reference Edit Settings", open=False, visible=('joyai_image' in default_enabled_chains)) as ref_accordion:
|
| 598 |
+
components[key('joyai_reference_accordion')] = ref_accordion
|
| 599 |
+
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit** (JoyAI-Image-Edit recommended), while adding multiple images performs an **Image Combine** (JoyAI-Image-Edit-Plus recommended with ZeroGPU Duration (s) set to 120).")
|
| 600 |
+
|
| 601 |
+
ref_image_groups = []
|
| 602 |
+
ref_image_inputs = []
|
| 603 |
+
with gr.Row():
|
| 604 |
+
for i in range(max_units):
|
| 605 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 606 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 607 |
+
ref_image_groups.append(img_col)
|
| 608 |
+
ref_image_inputs.append(img_comp)
|
| 609 |
+
|
| 610 |
+
components[key('joyai_reference_rows')] = ref_image_groups
|
| 611 |
+
components[key('joyai_reference_images')] = ref_image_inputs
|
| 612 |
+
|
| 613 |
+
with gr.Row():
|
| 614 |
+
components[key('add_joyai_reference_button')] = gr.Button("✚ Add Reference Image")
|
| 615 |
+
components[key('delete_joyai_reference_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 616 |
+
components[key('joyai_reference_count_state')] = gr.State(1)
|
| 617 |
+
|
| 618 |
+
components[key('all_joyai_reference_components_flat')] = ref_image_inputs
|
| 619 |
+
|
| 620 |
+
return components
|
| 621 |
+
|
| 622 |
+
def create_reference_image_ui(prefix: str, max_units=10):
|
| 623 |
+
components = {}
|
| 624 |
+
key = lambda name: f"{name}_{prefix}"
|
| 625 |
+
|
| 626 |
+
with gr.Accordion("Reference Image Edit Settings", open=False, visible=('reference_image' in default_enabled_chains)) as ref_accordion:
|
| 627 |
+
components[key('reference_image_accordion')] = ref_accordion
|
| 628 |
+
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 629 |
+
|
| 630 |
+
ref_image_groups = []
|
| 631 |
+
ref_image_inputs = []
|
| 632 |
+
with gr.Row():
|
| 633 |
+
for i in range(max_units):
|
| 634 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 635 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 636 |
+
ref_image_groups.append(img_col)
|
| 637 |
+
ref_image_inputs.append(img_comp)
|
| 638 |
+
|
| 639 |
+
components[key('reference_image_rows')] = ref_image_groups
|
| 640 |
+
components[key('reference_image_images')] = ref_image_inputs
|
| 641 |
+
|
| 642 |
+
with gr.Row():
|
| 643 |
+
components[key('add_reference_image_button')] = gr.Button("✚ Add Reference Image")
|
| 644 |
+
components[key('delete_reference_image_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 645 |
+
components[key('reference_image_count_state')] = gr.State(1)
|
| 646 |
+
|
| 647 |
+
components[key('all_reference_image_components_flat')] = ref_image_inputs
|
| 648 |
+
|
| 649 |
+
return components
|
| 650 |
+
def create_moss_image_control_ui(prefix: str):
|
| 651 |
+
components = {}
|
| 652 |
+
key = lambda name: f"{name}_{prefix}"
|
| 653 |
+
|
| 654 |
+
with gr.Accordion("MOSS-Image Color/Depth Control Settings", open=False, visible=('moss_image_control' in default_enabled_chains)) as control_accordion:
|
| 655 |
+
components[key('moss_image_control_accordion')] = control_accordion
|
| 656 |
+
gr.Markdown("💡 **Tip:** Uses the [darask0/MOSS-Image-color-depth-control](https://huggingface.co/darask0/MOSS-Image-color-depth-control) adapter.")
|
| 657 |
+
with gr.Row():
|
| 658 |
+
components[key('moss_image_control_color_map')] = gr.Image(
|
| 659 |
+
type="pil", label="Color Region Map (Optional)", sources=["upload"], height=200
|
| 660 |
+
)
|
| 661 |
+
components[key('moss_image_control_depth_map')] = gr.Image(
|
| 662 |
+
type="pil", label="Depth Map (Optional, near=white)", sources=["upload"], height=200
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
with gr.Row():
|
| 666 |
+
components[key('moss_image_control_strength')] = gr.Slider(
|
| 667 |
+
label="Control Strength",
|
| 668 |
+
value=1.0,
|
| 669 |
+
minimum=0.0,
|
| 670 |
+
maximum=2.0,
|
| 671 |
+
step=0.05,
|
| 672 |
+
interactive=True
|
| 673 |
+
)
|
| 674 |
+
components[key('moss_image_control_end_percent')] = gr.Slider(
|
| 675 |
+
label="End Percent (0.5 for 3D mannequin)",
|
| 676 |
+
value=0.5,
|
| 677 |
+
minimum=0.0,
|
| 678 |
+
maximum=1.0,
|
| 679 |
+
step=0.01,
|
| 680 |
+
interactive=True
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
return components
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
def create_pid_ui(prefix: str):
|
| 687 |
+
components = {}
|
| 688 |
+
key = lambda name: f"{name}_{prefix}"
|
| 689 |
+
|
| 690 |
+
with gr.Accordion("PiD Settings", open=False, visible=('pid' in default_enabled_chains)) as pid_accordion:
|
| 691 |
+
components[key('pid_accordion')] = pid_accordion
|
| 692 |
+
gr.Markdown("💡 **Tip:** Use PiD (Pixel Diffusion Decoder) instead of the VAE Decoder for 4x decoding.")
|
| 693 |
+
with gr.Row():
|
| 694 |
+
components[key('pid_settings')] = gr.Dropdown(
|
| 695 |
+
label="PiD Mode",
|
| 696 |
+
choices=["OFF", "ON"],
|
| 697 |
+
value="OFF",
|
| 698 |
+
interactive=True
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
return components
|
| 702 |
+
|
| 703 |
+
def create_krea2_identity_edit_ui(prefix: str, max_units=2):
|
| 704 |
+
components = {}
|
| 705 |
+
key = lambda name: f"{name}_{prefix}"
|
| 706 |
+
|
| 707 |
+
with gr.Accordion("Krea2 Identity Edit Settings", open=False, visible=('krea2_identity_edit' in default_enabled_chains)) as ref_accordion:
|
| 708 |
+
components[key('krea2_identity_edit_accordion')] = ref_accordion
|
| 709 |
+
gr.Markdown("💡 **Tip:** Processed using the [lbouaraba/comfyui-krea2edit](https://github.com/lbouaraba/comfyui-krea2edit) node. (Krea-2-Turbo recommended, Krea-2-Raw need set ZeroGPU Duration (s) to 120 ) In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 710 |
+
|
| 711 |
+
ref_image_groups = []
|
| 712 |
+
ref_image_inputs = []
|
| 713 |
+
with gr.Row():
|
| 714 |
+
for i in range(max_units):
|
| 715 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 716 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 717 |
+
ref_image_groups.append(img_col)
|
| 718 |
+
ref_image_inputs.append(img_comp)
|
| 719 |
+
|
| 720 |
+
components[key('krea2_identity_edit_rows')] = ref_image_groups
|
| 721 |
+
components[key('krea2_identity_edit_images')] = ref_image_inputs
|
| 722 |
+
|
| 723 |
+
with gr.Row():
|
| 724 |
+
components[key('add_krea2_identity_edit_button')] = gr.Button("✚ Add Reference Image")
|
| 725 |
+
components[key('delete_krea2_identity_edit_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 726 |
+
components[key('krea2_identity_edit_count_state')] = gr.State(1)
|
| 727 |
+
|
| 728 |
+
components[key('all_krea2_identity_edit_components_flat')] = ref_image_inputs
|
| 729 |
+
|
| 730 |
+
return components
|
| 731 |
+
|
| 732 |
+
def create_qwen_image_edit_ui(prefix: str, max_units=3):
|
| 733 |
+
components = {}
|
| 734 |
+
key = lambda name: f"{name}_{prefix}"
|
| 735 |
+
|
| 736 |
+
with gr.Accordion("Qwen-Image Edit Settings", open=False, visible=('qwen_image_edit' in default_enabled_chains)) as ref_accordion:
|
| 737 |
+
components[key('qwen_image_edit_accordion')] = ref_accordion
|
| 738 |
+
gr.Markdown("💡 **Tip:** (lightx2v/Qwen-Image-Edit-2511-Lightning recommended) In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
|
| 739 |
+
|
| 740 |
+
ref_image_groups = []
|
| 741 |
+
ref_image_inputs = []
|
| 742 |
+
with gr.Row():
|
| 743 |
+
for i in range(max_units):
|
| 744 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 745 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 746 |
+
ref_image_groups.append(img_col)
|
| 747 |
+
ref_image_inputs.append(img_comp)
|
| 748 |
+
|
| 749 |
+
components[key('qwen_image_edit_rows')] = ref_image_groups
|
| 750 |
+
components[key('qwen_image_edit_images')] = ref_image_inputs
|
| 751 |
+
|
| 752 |
+
with gr.Row():
|
| 753 |
+
components[key('add_qwen_image_edit_button')] = gr.Button("✚ Add Reference Image")
|
| 754 |
+
components[key('delete_qwen_image_edit_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 755 |
+
components[key('qwen_image_edit_count_state')] = gr.State(1)
|
| 756 |
+
|
| 757 |
+
components[key('all_qwen_image_edit_components_flat')] = ref_image_inputs
|
| 758 |
+
|
| 759 |
+
return components
|
| 760 |
+
|
| 761 |
+
def create_krea2_reference_edit_ui(prefix: str, max_units=3):
|
| 762 |
+
components = {}
|
| 763 |
+
key = lambda name: f"{name}_{prefix}"
|
| 764 |
+
|
| 765 |
+
with gr.Accordion("Krea2 Style Reference Edit Settings", open=False, visible=('krea2_style_reference' in default_enabled_chains)) as ref_accordion:
|
| 766 |
+
components[key('krea2_reference_edit_accordion')] = ref_accordion
|
| 767 |
+
gr.Markdown("💡 **Tip:** (Krea-2-Turbo recommended) Add style reference images to perform style reference editing.")
|
| 768 |
+
|
| 769 |
+
ref_image_groups = []
|
| 770 |
+
ref_image_inputs = []
|
| 771 |
+
with gr.Row():
|
| 772 |
+
for i in range(max_units):
|
| 773 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 774 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 775 |
+
ref_image_groups.append(img_col)
|
| 776 |
+
ref_image_inputs.append(img_comp)
|
| 777 |
+
|
| 778 |
+
components[key('krea2_reference_edit_rows')] = ref_image_groups
|
| 779 |
+
components[key('krea2_reference_edit_images')] = ref_image_inputs
|
| 780 |
+
|
| 781 |
+
with gr.Row():
|
| 782 |
+
components[key('add_krea2_reference_edit_button')] = gr.Button("✚ Add Reference Image")
|
| 783 |
+
components[key('delete_krea2_reference_edit_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 784 |
+
components[key('krea2_reference_edit_count_state')] = gr.State(1)
|
| 785 |
+
|
| 786 |
+
components[key('all_krea2_reference_edit_components_flat')] = ref_image_inputs
|
| 787 |
+
|
| 788 |
+
return components
|
| 789 |
+
|
| 790 |
+
|
| 791 |
+
def create_qwen_image_2_1_prompt_enhancer_ui(prefix: str):
|
| 792 |
+
components = {}
|
| 793 |
+
key = lambda name: f"{name}_{prefix}"
|
| 794 |
+
|
| 795 |
+
with gr.Accordion("Qwen-Image-2.1 Prompt Enhancer Settings", open=False, visible=('qwen_image_2_1_prompt_enhancer' in default_enabled_chains)) as enhancer_accordion:
|
| 796 |
+
components[key('qwen_image_2_1_prompt_enhancer_accordion')] = enhancer_accordion
|
| 797 |
+
gr.Markdown("💡 **Tip:** Automatically selects between [Qwen-Image-2.1-PE-T2I](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-T2I) and [Qwen-Image-2.1-PE-I2I](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-I2I) for prompt enhancement based on input context.")
|
| 798 |
+
with gr.Row():
|
| 799 |
+
components[key('qwen_image_2_1_prompt_enhancer_enable')] = gr.Checkbox(
|
| 800 |
+
label="Enable Qwen-Image-2.1 Prompt Enhancer",
|
| 801 |
+
value=False,
|
| 802 |
+
interactive=True,
|
| 803 |
+
scale=2
|
| 804 |
+
)
|
| 805 |
+
components[key('qwen_image_2_1_prompt_enhancer_thinking')] = gr.Checkbox(
|
| 806 |
+
label="Enable Thinking (Requires longer ZeroGPU Duration)",
|
| 807 |
+
value=False,
|
| 808 |
+
interactive=True,
|
| 809 |
+
scale=2
|
| 810 |
+
)
|
| 811 |
+
components[key('qwen_image_2_1_prompt_enhancer_max_length')] = gr.Number(
|
| 812 |
+
label="Max Length",
|
| 813 |
+
value=4096,
|
| 814 |
+
precision=0,
|
| 815 |
+
minimum=16,
|
| 816 |
+
step=16,
|
| 817 |
+
interactive=True,
|
| 818 |
+
scale=1
|
| 819 |
+
)
|
| 820 |
+
|
| 821 |
+
return components
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
DEFAULT_MING_IMAGE_SYSTEM_PROMPT = (
|
| 825 |
+
"You are a senior visual designer and image-prompt engineer. Expand the user's request into one precise, high-resolution Figma-style caption. Return only one JSON object. "
|
| 826 |
+
"Use exactly two top-level keys. `canvas_settings` contains exactly `aspect_ratio`, `ambient_lighting`, and `image_style`. `layers` lists visible groups from background to topmost overlay. Every layer contains exactly `description`, `coordinates`, `hierarchy_and_relation`, and `color_specs`; `color_specs` is an array of hex colors. "
|
| 827 |
+
'`coordinates` MUST be one string, never an object or array, in exactly this form: `"cx: 0.500, cy: 0.500, w: 1.000, h: 1.000"`. Values are normalized; each bbox encloses its complete owned object and stays inside the canvas. '
|
| 828 |
+
"A layer is one selectable visible semantic group: background, full person, coherent object, panel, card, row, or text block. Prefer the fewest groups that preserve the layout. Keep people and objects intact. Never create invisible parents, guides, placeholders, empty layers, duplicate summaries, or multiple owners for one element. "
|
| 829 |
+
"Preserve every user-supplied rendered string character-for-character and as one contiguous string. Unless multiple visible copies are requested, it must occur exactly once across all `description` fields and zero times in `hierarchy_and_relation`. Quote it only where describing its visible rendering; refer to the related subject elsewhere with unquoted semantic wording. Enumerate intended copy, invent extra copy sparingly, and never hide content behind \"other text\", \"remaining labels\", or \"etc.\" "
|
| 830 |
+
"Describe concrete composition, typography, materials, texture, lighting, pose, and camera treatment without literary filler. Use `hierarchy_and_relation` only for ownership, alignment, containment, stacking, and occlusion. "
|
| 831 |
+
"Infer structured layouts first. Use one complete layer per card and state its row and column. A compact secondary table may be one layer only if every header and cell is listed; otherwise use a visible shared frame when present, one complete header, and one complete layer per body row, binding values to columns and stating blanks. Enumerate sequences, schedules, spans, gaps, and vacant tracks in visual order. Do not mistake ordinary alignment for a table. "
|
| 832 |
+
"Silently verify schema, string coordinates, Z-order, exact-text counts, geometry, bbox validity, and completeness."
|
| 833 |
+
)
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
def create_ming_image_prompt_enhancer_ui(prefix: str):
|
| 837 |
+
components = {}
|
| 838 |
+
key = lambda name: f"{name}_{prefix}"
|
| 839 |
+
|
| 840 |
+
with gr.Accordion("Ming-Image Prompt Enhancer Settings", open=False, visible=('ming_image_prompt_enhancer' in default_enabled_chains)) as enhancer_accordion:
|
| 841 |
+
components[key('ming_image_prompt_enhancer_accordion')] = enhancer_accordion
|
| 842 |
+
gr.Markdown("💡 **Tip:** Leverages [Qwen3.8-27B](https://huggingface.co/Qwen/Qwen3.8-27B) guided by the official [System Prompt](https://github.com/inclusionAI/Ming-Image#text-to-image-prompt-rewriting) to automatically enhance prompts into structured layouts based on input context.")
|
| 843 |
+
with gr.Row():
|
| 844 |
+
components[key('ming_image_prompt_enhancer_enable')] = gr.Checkbox(
|
| 845 |
+
label="Enable Ming-Image Prompt Enhancer",
|
| 846 |
+
value=False,
|
| 847 |
+
interactive=True,
|
| 848 |
+
scale=2
|
| 849 |
+
)
|
| 850 |
+
components[key('ming_image_prompt_enhancer_thinking')] = gr.Checkbox(
|
| 851 |
+
label="Enable Thinking (Requires longer ZeroGPU Duration)",
|
| 852 |
+
value=False,
|
| 853 |
+
interactive=True,
|
| 854 |
+
scale=2
|
| 855 |
+
)
|
| 856 |
+
components[key('ming_image_prompt_enhancer_max_length')] = gr.Number(
|
| 857 |
+
label="Max Length",
|
| 858 |
+
value=4096,
|
| 859 |
+
precision=0,
|
| 860 |
+
minimum=16,
|
| 861 |
+
step=16,
|
| 862 |
+
interactive=True,
|
| 863 |
+
scale=1
|
| 864 |
+
)
|
| 865 |
+
components[key('ming_image_prompt_enhancer_system_prompt')] = gr.Textbox(
|
| 866 |
+
label="System Prompt",
|
| 867 |
+
value=DEFAULT_MING_IMAGE_SYSTEM_PROMPT,
|
| 868 |
+
lines=4,
|
| 869 |
+
max_lines=12,
|
| 870 |
+
interactive=True
|
| 871 |
+
)
|
| 872 |
+
|
| 873 |
+
return components
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
def create_flux3_prompt_upsampler_ui(prefix: str):
|
| 877 |
+
components = {}
|
| 878 |
+
key = lambda name: f"{name}_{prefix}"
|
| 879 |
+
|
| 880 |
+
with gr.Accordion("FLUX.3 Prompt Upsampler Settings", open=False, visible=('flux3_prompt_upsampler' in default_enabled_chains)) as enhancer_accordion:
|
| 881 |
+
components[key('flux3_prompt_upsampler_accordion')] = enhancer_accordion
|
| 882 |
+
gr.Markdown(
|
| 883 |
+
"💡 **Tip:** System prompt automatically matches the official [black-forest-labs/flux-3-image-klein-pup](https://huggingface.co/black-forest-labs/flux-3-image-klein-pup/blob/main/system_prompts.json) templates: uses `t2i` when no image is provided, and automatically switches to `i2i` when image input is detected; enabling **BBox Mode** switches respectively to `t2i_bbox` and `i2i_bbox` to output target bounding box annotations."
|
| 884 |
+
)
|
| 885 |
+
with gr.Row():
|
| 886 |
+
components[key('flux3_prompt_upsampler_enable')] = gr.Checkbox(
|
| 887 |
+
label="Enable FLUX.3 Prompt Upsampler",
|
| 888 |
+
value=False,
|
| 889 |
+
interactive=True,
|
| 890 |
+
scale=2
|
| 891 |
+
)
|
| 892 |
+
components[key('flux3_prompt_upsampler_thinking')] = gr.Checkbox(
|
| 893 |
+
label="Enable Thinking (Requires longer ZeroGPU Duration)",
|
| 894 |
+
value=False,
|
| 895 |
+
interactive=True,
|
| 896 |
+
scale=2
|
| 897 |
+
)
|
| 898 |
+
components[key('flux3_prompt_upsampler_bbox_mode')] = gr.Checkbox(
|
| 899 |
+
label="BBox Mode",
|
| 900 |
+
value=False,
|
| 901 |
+
interactive=True,
|
| 902 |
+
scale=1
|
| 903 |
+
)
|
| 904 |
+
components[key('flux3_prompt_upsampler_max_length')] = gr.Number(
|
| 905 |
+
label="Max Length",
|
| 906 |
+
value=3584,
|
| 907 |
+
precision=0,
|
| 908 |
+
minimum=16,
|
| 909 |
+
step=16,
|
| 910 |
+
interactive=True,
|
| 911 |
+
scale=1
|
| 912 |
+
)
|
| 913 |
+
|
| 914 |
+
return components
|
| 915 |
+
|
| 916 |
+
|
| 917 |
+
def create_moss_image_edit_ui(prefix: str, max_units=None):
|
| 918 |
+
if max_units is None:
|
| 919 |
+
max_units = MAX_MOSS_IMAGE_EDITS
|
| 920 |
+
components = {}
|
| 921 |
+
key = lambda name: f"{name}_{prefix}"
|
| 922 |
+
|
| 923 |
+
with gr.Accordion("MOSS-Image Edit Settings", open=False, visible=('moss_image_edit' in default_enabled_chains)) as ref_accordion:
|
| 924 |
+
components[key('moss_image_edit_accordion')] = ref_accordion
|
| 925 |
+
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an Image Edit, while adding multiple images performs an Image Combine. Uses [chinoll/Moss-Image-Edit](https://huggingface.co/chinoll/Moss-Image-Edit).")
|
| 926 |
+
|
| 927 |
+
ref_image_groups = []
|
| 928 |
+
ref_image_inputs = []
|
| 929 |
+
with gr.Row():
|
| 930 |
+
for i in range(max_units):
|
| 931 |
+
with gr.Column(visible=(i < 1), min_width=160) as img_col:
|
| 932 |
+
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
|
| 933 |
+
ref_image_groups.append(img_col)
|
| 934 |
+
ref_image_inputs.append(img_comp)
|
| 935 |
+
|
| 936 |
+
components[key('moss_image_edit_rows')] = ref_image_groups
|
| 937 |
+
components[key('moss_image_edit_images')] = ref_image_inputs
|
| 938 |
+
|
| 939 |
+
with gr.Row():
|
| 940 |
+
components[key('add_moss_image_edit_button')] = gr.Button("✚ Add Reference Image")
|
| 941 |
+
components[key('delete_moss_image_edit_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 942 |
+
components[key('moss_image_edit_count_state')] = gr.State(1)
|
| 943 |
+
|
| 944 |
+
components[key('all_moss_image_edit_components_flat')] = ref_image_inputs
|
| 945 |
+
|
| 946 |
+
return components
|
| 947 |
+
|
utils/app_utils.py
CHANGED
|
@@ -196,7 +196,7 @@ def get_lora_path(source: str, id_or_url: str, civitai_key: str, progress) -> tu
|
|
| 196 |
filename = sanitize_filename(f"{version_id}{file_ext}")
|
| 197 |
save_dir = os.path.join(LORA_DIR, "civitai")
|
| 198 |
local_path = os.path.join(save_dir, filename)
|
| 199 |
-
api_key_to_use = civitai_key
|
| 200 |
source_name = f"Civitai ID {version_id}"
|
| 201 |
elif source == "Hugging Face":
|
| 202 |
parts = id_or_url.strip().split('/')
|
|
|
|
| 196 |
filename = sanitize_filename(f"{version_id}{file_ext}")
|
| 197 |
save_dir = os.path.join(LORA_DIR, "civitai")
|
| 198 |
local_path = os.path.join(save_dir, filename)
|
| 199 |
+
api_key_to_use = civitai_key
|
| 200 |
source_name = f"Civitai ID {version_id}"
|
| 201 |
elif source == "Hugging Face":
|
| 202 |
parts = id_or_url.strip().split('/')
|
yaml/chain_features.yaml
CHANGED
|
@@ -236,6 +236,21 @@ mage_flow_reference_edit:
|
|
| 236 |
required:
|
| 237 |
- image
|
| 238 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
krea2_identity_edit:
|
| 240 |
chains: krea2_identity_edit
|
| 241 |
display_name: "KREA2 Identity Edit"
|
|
@@ -479,3 +494,81 @@ ming_image_prompt_enhancer:
|
|
| 479 |
system_prompt:
|
| 480 |
type: string
|
| 481 |
description: "System prompt for Qwen3.8-27B prompt enhancer (defaults to official Ming-Image Figma-style rewriter prompt)."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
required:
|
| 237 |
- image
|
| 238 |
|
| 239 |
+
fibo2_reference_edit:
|
| 240 |
+
chains: reference_image
|
| 241 |
+
display_name: "Fibo-2.0 Reference Edit"
|
| 242 |
+
description: "For Fibo 2.0 models, this feature enables reference image editing and combining capabilities. In txt2img mode, adding a single reference image performs an Image Edit, while adding multiple images performs an Image Combine."
|
| 243 |
+
max_count: 10
|
| 244 |
+
usage_guideline: "Supply reference image as Base64 Data URI or HTTP/HTTPS URL."
|
| 245 |
+
parameters_schema:
|
| 246 |
+
type: object
|
| 247 |
+
properties:
|
| 248 |
+
image:
|
| 249 |
+
type: string
|
| 250 |
+
description: "Reference image encoded as Base64 Data URI or HTTP/HTTPS URL."
|
| 251 |
+
required:
|
| 252 |
+
- image
|
| 253 |
+
|
| 254 |
krea2_identity_edit:
|
| 255 |
chains: krea2_identity_edit
|
| 256 |
display_name: "KREA2 Identity Edit"
|
|
|
|
| 494 |
system_prompt:
|
| 495 |
type: string
|
| 496 |
description: "System prompt for Qwen3.8-27B prompt enhancer (defaults to official Ming-Image Figma-style rewriter prompt)."
|
| 497 |
+
|
| 498 |
+
flux3_prompt_upsampler:
|
| 499 |
+
chains: flux3_prompt_upsampler
|
| 500 |
+
display_name: "FLUX.3 Prompt Upsampler Settings"
|
| 501 |
+
description: "Expand short prompts into detailed generative captions using FLUX.3 Prompt Upsampler (qwen3.5_9b_flux3_prompt_upsampler_w4a8) with automatic official template selection (t2i, i2i, t2i_bbox, i2i_bbox), multi-reference image stitching, and reasoning CoT thinking mode."
|
| 502 |
+
max_count: 1
|
| 503 |
+
usage_guideline: "Set enable to true to activate FLUX.3 prompt upsampler. Automatically selects official BFL t2i or i2i template based on input image detection. Supports bbox_mode, thinking mode, and multi-reference image stitching."
|
| 504 |
+
parameters_schema:
|
| 505 |
+
type: object
|
| 506 |
+
properties:
|
| 507 |
+
enable:
|
| 508 |
+
type: boolean
|
| 509 |
+
default: false
|
| 510 |
+
description: "Enable FLUX.3 Prompt Upsampler."
|
| 511 |
+
thinking:
|
| 512 |
+
type: boolean
|
| 513 |
+
default: false
|
| 514 |
+
description: "Enable thinking (reasoning CoT)."
|
| 515 |
+
bbox_mode:
|
| 516 |
+
type: boolean
|
| 517 |
+
default: false
|
| 518 |
+
description: "Enable BBox mode (automatically selects t2i_bbox or i2i_bbox official system prompt templates to output bounding boxes)."
|
| 519 |
+
max_length:
|
| 520 |
+
type: integer
|
| 521 |
+
default: 3584
|
| 522 |
+
description: "Maximum generation length for prompt upsampler (default 3584)."
|
| 523 |
+
system_prompt:
|
| 524 |
+
type: string
|
| 525 |
+
description: "Optional custom system prompt override (defaults to official t2i/i2i or t2i_bbox/i2i_bbox templates)."
|
| 526 |
+
|
| 527 |
+
moss_image_control:
|
| 528 |
+
chains: moss_image_control
|
| 529 |
+
display_name: "MOSS-Image Color/Depth Control"
|
| 530 |
+
description: "Controls MOSS-Image generation using a color region map (object/region spatial layout) and/or a depth map (3D spatial depth, near is white) via darask0/MOSS-Image-color-depth-control adapter and MOSSImageControl node."
|
| 531 |
+
max_count: 1
|
| 532 |
+
usage_guideline: "Supply color_map (RGB region ID map) and/or depth_map (near=white grayscale map) as Base64 Data URI or HTTP/HTTPS URL. Set strength (default 1.0) and timestep range (start_percent 0.0, end_percent 0.5)."
|
| 533 |
+
parameters_schema:
|
| 534 |
+
type: object
|
| 535 |
+
properties:
|
| 536 |
+
color_map:
|
| 537 |
+
type: string
|
| 538 |
+
description: "Color region map image encoded as Base64 Data URI or HTTP/HTTPS URL. Each region uses a flat RGB color as a region ID."
|
| 539 |
+
depth_map:
|
| 540 |
+
type: string
|
| 541 |
+
description: "Depth map image encoded as Base64 Data URI or HTTP/HTTPS URL (near is white, far is dark)."
|
| 542 |
+
strength:
|
| 543 |
+
type: number
|
| 544 |
+
default: 1.0
|
| 545 |
+
minimum: 0.0
|
| 546 |
+
maximum: 2.0
|
| 547 |
+
description: "Control influence strength multiplier (default 1.0)."
|
| 548 |
+
start_percent:
|
| 549 |
+
type: number
|
| 550 |
+
default: 0.0
|
| 551 |
+
minimum: 0.0
|
| 552 |
+
maximum: 1.0
|
| 553 |
+
description: "Sampling percentage at which control begins (default 0.0)."
|
| 554 |
+
end_percent:
|
| 555 |
+
type: number
|
| 556 |
+
default: 0.5
|
| 557 |
+
minimum: 0.0
|
| 558 |
+
maximum: 1.0
|
| 559 |
+
description: "Sampling percentage at which control ends (default 0.5, use 0.5 for 3D mannequin)."
|
| 560 |
+
|
| 561 |
+
moss_image_edit:
|
| 562 |
+
chains: moss_image_edit
|
| 563 |
+
display_name: "MOSS-Image Edit"
|
| 564 |
+
description: "Edits images or generates variants using MOSS-Image Edit LoRA (officially packaged by Comfy-Org as moss_image_edit_lora_rank_64_bf16.safetensors) and native TextEncodeMOSSImageEdit node."
|
| 565 |
+
max_count: 10
|
| 566 |
+
usage_guideline: "Upload 1-10 reference images and specify prompt. Automatically uses the official MOSS-Image Edit LoRA layout."
|
| 567 |
+
parameters_schema:
|
| 568 |
+
type: object
|
| 569 |
+
properties:
|
| 570 |
+
images:
|
| 571 |
+
type: array
|
| 572 |
+
items:
|
| 573 |
+
type: string
|
| 574 |
+
description: "List of 1 to 10 reference images encoded as Base64 Data URI, HTTP/HTTPS URL, or local file path."
|
yaml/constants.yaml
CHANGED
|
@@ -4,9 +4,26 @@ MAX_IPADAPTERS: 5
|
|
| 4 |
MAX_EMBEDDINGS: 5
|
| 5 |
MAX_CONDITIONINGS: 10
|
| 6 |
MAX_REFERENCE_LATENTS: 10
|
|
|
|
| 7 |
LORA_SOURCE_CHOICES: ["Civitai", "Hugging Face", "Upload File"]
|
| 8 |
|
| 9 |
RESOLUTION_MAP:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
krea-2:
|
| 11 |
"1:1 (Square)": [1024, 1024]
|
| 12 |
"16:9 (Landscape)": [1344, 768]
|
|
@@ -103,6 +120,14 @@ RESOLUTION_MAP:
|
|
| 103 |
"3:4 (Classic Portrait)": [896, 1152]
|
| 104 |
"3:2 (Photography)": [1216, 832]
|
| 105 |
"2:3 (Photography Portrait)": [832, 1216]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
ming-image:
|
| 107 |
"1:1 (Square)": [1024, 1024]
|
| 108 |
"16:9 (Landscape)": [1344, 768]
|
|
@@ -289,6 +314,8 @@ RESOLUTION_MAP:
|
|
| 289 |
"2:3 (Portrait)": [512, 768]
|
| 290 |
|
| 291 |
MULTIPLIERS_MAP:
|
|
|
|
|
|
|
| 292 |
krea-2: 1
|
| 293 |
sensenova-u1.5: 1
|
| 294 |
mage-flow: 1
|
|
|
|
| 4 |
MAX_EMBEDDINGS: 5
|
| 5 |
MAX_CONDITIONINGS: 10
|
| 6 |
MAX_REFERENCE_LATENTS: 10
|
| 7 |
+
MAX_MOSS_IMAGE_EDITS: 10
|
| 8 |
LORA_SOURCE_CHOICES: ["Civitai", "Hugging Face", "Upload File"]
|
| 9 |
|
| 10 |
RESOLUTION_MAP:
|
| 11 |
+
fibo-2.0:
|
| 12 |
+
"1:1 (Square)": [1024, 1024]
|
| 13 |
+
"16:9 (Landscape)": [1344, 768]
|
| 14 |
+
"9:16 (Portrait)": [768, 1344]
|
| 15 |
+
"4:3 (Classic)": [1152, 896]
|
| 16 |
+
"3:4 (Classic Portrait)": [896, 1152]
|
| 17 |
+
"3:2 (Photography)": [1216, 832]
|
| 18 |
+
"2:3 (Photography Portrait)": [832, 1216]
|
| 19 |
+
flux3:
|
| 20 |
+
"1:1 (Square)": [1024, 1024]
|
| 21 |
+
"16:9 (Landscape)": [1344, 768]
|
| 22 |
+
"9:16 (Portrait)": [768, 1344]
|
| 23 |
+
"4:3 (Classic)": [1152, 896]
|
| 24 |
+
"3:4 (Classic Portrait)": [896, 1152]
|
| 25 |
+
"3:2 (Photography)": [1216, 832]
|
| 26 |
+
"2:3 (Photography Portrait)": [832, 1216]
|
| 27 |
krea-2:
|
| 28 |
"1:1 (Square)": [1024, 1024]
|
| 29 |
"16:9 (Landscape)": [1344, 768]
|
|
|
|
| 120 |
"3:4 (Classic Portrait)": [896, 1152]
|
| 121 |
"3:2 (Photography)": [1216, 832]
|
| 122 |
"2:3 (Photography Portrait)": [832, 1216]
|
| 123 |
+
moss-image:
|
| 124 |
+
"1:1 (Square)": [1024, 1024]
|
| 125 |
+
"16:9 (Landscape)": [1344, 768]
|
| 126 |
+
"9:16 (Portrait)": [768, 1344]
|
| 127 |
+
"4:3 (Classic)": [1152, 896]
|
| 128 |
+
"3:4 (Classic Portrait)": [896, 1152]
|
| 129 |
+
"3:2 (Photography)": [1216, 832]
|
| 130 |
+
"2:3 (Photography Portrait)": [832, 1216]
|
| 131 |
ming-image:
|
| 132 |
"1:1 (Square)": [1024, 1024]
|
| 133 |
"16:9 (Landscape)": [1344, 768]
|
|
|
|
| 314 |
"2:3 (Portrait)": [512, 768]
|
| 315 |
|
| 316 |
MULTIPLIERS_MAP:
|
| 317 |
+
fibo-2.0: 1
|
| 318 |
+
flux3: 1
|
| 319 |
krea-2: 1
|
| 320 |
sensenova-u1.5: 1
|
| 321 |
mage-flow: 1
|
yaml/file_list.yaml
CHANGED
|
@@ -405,6 +405,85 @@ file:
|
|
| 405 |
repo_id: "comfyanonymous/ControlNet-v1-1_fp16_safetensors"
|
| 406 |
repository_file_path: "control_v11u_sd15_tile_fp16.safetensors"
|
| 407 |
diffusion_models:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
# Krea-2
|
| 409 |
- filename: "krea2_turbo_nvfp4.safetensors"
|
| 410 |
source: "hf"
|
|
@@ -414,6 +493,10 @@ file:
|
|
| 414 |
source: "hf"
|
| 415 |
repo_id: "tsukiyomi/krea2_raw-nvfp4"
|
| 416 |
repository_file_path: "krea2_raw-nvfp4.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
# Mage-Flow
|
| 418 |
- filename: "mage_flow_turbo_int8_convrot.safetensors"
|
| 419 |
source: "hf"
|
|
@@ -524,64 +607,20 @@ file:
|
|
| 524 |
repo_id: "lylogummy/Anima-3.8B"
|
| 525 |
repository_file_path: "difussion_models/Anima-3.8B-v1.1.safetensors"
|
| 526 |
# Anima-3.8B
|
| 527 |
-
- filename: "boleromixAnima_v01438B.safetensors"
|
| 528 |
-
source: "civitai"
|
| 529 |
-
model_version_id: "3348912"
|
| 530 |
- filename: "Anima-3.8B.safetensors"
|
| 531 |
source: "hf"
|
| 532 |
repo_id: "lylogummy/Anima-3.8B"
|
| 533 |
repository_file_path: "difussion_models/Anima-3.8B.safetensors"
|
| 534 |
-
- filename: "fiamixAnima38B_v12.safetensors"
|
| 535 |
-
source: "civitai"
|
| 536 |
-
model_version_id: "3371597"
|
| 537 |
-
- filename: "pppAnima38BQwen06B_v20.safetensors"
|
| 538 |
-
source: "civitai"
|
| 539 |
-
model_version_id: "3359866"
|
| 540 |
-
- filename: "animaDeltaMix38BQwen06B.safetensors"
|
| 541 |
-
source: "civitai"
|
| 542 |
-
model_version_id: "3427825"
|
| 543 |
-
- filename: "fiamixAnima38B_new.safetensors"
|
| 544 |
-
source: "civitai"
|
| 545 |
-
model_version_id: "3423916"
|
| 546 |
# Anima-2.9B
|
| 547 |
- filename: "Anima-2.9B-preview-v1.safetensors"
|
| 548 |
source: "hf"
|
| 549 |
repo_id: "Gazingstars123/Anima-2.9B"
|
| 550 |
repository_file_path: "Anima-2.9B-preview-v1.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
# Anima
|
| 552 |
-
- filename: "NovaAnimeAMV50.safetensors"
|
| 553 |
-
source: "civitai"
|
| 554 |
-
model_version_id: "3338179"
|
| 555 |
-
- filename: "dreamcake_redvelvet.safetensors"
|
| 556 |
-
source: "civitai"
|
| 557 |
-
model_version_id: "3359687"
|
| 558 |
-
- filename: "huggingreverieAnima_v20B.safetensors"
|
| 559 |
-
source: "civitai"
|
| 560 |
-
model_version_id: "3373038"
|
| 561 |
-
- filename: "fiamixAnimaReboot25D_v11.safetensors"
|
| 562 |
-
source: "civitai"
|
| 563 |
-
model_version_id: "3316592"
|
| 564 |
-
- filename: "hybridcelTurboAnima_v30.safetensors"
|
| 565 |
-
source: "civitai"
|
| 566 |
-
model_version_id: "3340336"
|
| 567 |
-
- filename: "animaLuc_v3a.safetensors"
|
| 568 |
-
source: "civitai"
|
| 569 |
-
model_version_id: "3339348"
|
| 570 |
-
- filename: "novaBaraMaleAnima_novaV20.safetensors"
|
| 571 |
-
source: "civitai"
|
| 572 |
-
model_version_id: "3304198"
|
| 573 |
-
- filename: "fiamixAnimaReboot_v17.safetensors"
|
| 574 |
-
source: "civitai"
|
| 575 |
-
model_version_id: "3359008"
|
| 576 |
-
- filename: "novaCrossAM_new.safetensors"
|
| 577 |
-
source: "civitai"
|
| 578 |
-
model_version_id: "3431917"
|
| 579 |
-
- filename: "animaDeltaMix29B.safetensors"
|
| 580 |
-
source: "civitai"
|
| 581 |
-
model_version_id: "3414789"
|
| 582 |
-
- filename: "nununuMix.safetensors"
|
| 583 |
-
source: "civitai"
|
| 584 |
-
model_version_id: "3172155"
|
| 585 |
- filename: "anima-turbo-v1.0.safetensors"
|
| 586 |
source: "hf"
|
| 587 |
repo_id: "circlestone-labs/Anima"
|
|
@@ -698,10 +737,22 @@ file:
|
|
| 698 |
source: "hf"
|
| 699 |
repo_id: "Comfy-Org/Qwen-Image-2.1"
|
| 700 |
repository_file_path: "diffusion_models/qwen_image_2.1_int8_convrot.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 701 |
- filename: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
| 702 |
source: "hf"
|
| 703 |
repo_id: "abenzerps/Qwen-Image-2.1-Uncensored-GGUF"
|
| 704 |
repository_file_path: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
# Ming-Image
|
| 706 |
- filename: "ming_image_0.1_design_int8_convrot.safetensors"
|
| 707 |
source: "hf"
|
|
@@ -884,6 +935,16 @@ file:
|
|
| 884 |
repo_id: "black-forest-labs/FLUX.1-Redux-dev"
|
| 885 |
repository_file_path: "flux1-redux-dev.safetensors"
|
| 886 |
loras:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 887 |
# Krea2 Identity Edit
|
| 888 |
- filename: "krea2_identity_edit_v1_2.safetensors"
|
| 889 |
source: "hf"
|
|
@@ -954,10 +1015,15 @@ file:
|
|
| 954 |
# Qwen-Image-2.1
|
| 955 |
- filename: "qwen_image_2.1_fun_controlnet_union_int8_convrot.safetensors"
|
| 956 |
source: "hf"
|
| 957 |
-
repo_id: "
|
| 958 |
repository_file_path: "model_patches/qwen_image_2.1_fun_controlnet_union_int8_convrot.safetensors"
|
| 959 |
text_encoders:
|
| 960 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 961 |
- filename: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 962 |
source: "hf"
|
| 963 |
repo_id: "Comfy-Org/Krea-2"
|
|
@@ -1000,6 +1066,11 @@ file:
|
|
| 1000 |
source: "hf"
|
| 1001 |
repo_id: "Comfy-Org/Qwen3.8-27B"
|
| 1002 |
repository_file_path: "text_encoders/qwen3.8_27b_w4a8.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
# PixelDiT
|
| 1004 |
- filename: "gemma_2_2b_it_elm_fp8_scaled.safetensors"
|
| 1005 |
source: "hf"
|
|
|
|
| 405 |
repo_id: "comfyanonymous/ControlNet-v1-1_fp16_safetensors"
|
| 406 |
repository_file_path: "control_v11u_sd15_tile_fp16.safetensors"
|
| 407 |
diffusion_models:
|
| 408 |
+
- filename: "boleromixAnima_v01438B.safetensors"
|
| 409 |
+
source: "civitai"
|
| 410 |
+
model_version_id: "3348912"
|
| 411 |
+
- filename: "fiamixAnima38B_v12.safetensors"
|
| 412 |
+
source: "civitai"
|
| 413 |
+
model_version_id: "3371597"
|
| 414 |
+
- filename: "pppAnima38BQwen06B_v20.safetensors"
|
| 415 |
+
source: "civitai"
|
| 416 |
+
model_version_id: "3359866"
|
| 417 |
+
- filename: "animaDeltaMix38BQwen06B.safetensors"
|
| 418 |
+
source: "civitai"
|
| 419 |
+
model_version_id: "3427825"
|
| 420 |
+
- filename: "fiamixAnima38B_new.safetensors"
|
| 421 |
+
source: "civitai"
|
| 422 |
+
model_version_id: "3423916"
|
| 423 |
+
- filename: "NovaAnimeAMV50.safetensors"
|
| 424 |
+
source: "civitai"
|
| 425 |
+
model_version_id: "3338179"
|
| 426 |
+
- filename: "dreamcake_redvelvet.safetensors"
|
| 427 |
+
source: "civitai"
|
| 428 |
+
model_version_id: "3359687"
|
| 429 |
+
- filename: "huggingreverieAnima_v20B.safetensors"
|
| 430 |
+
source: "civitai"
|
| 431 |
+
model_version_id: "3373038"
|
| 432 |
+
- filename: "fiamixAnimaReboot25D_v11.safetensors"
|
| 433 |
+
source: "civitai"
|
| 434 |
+
model_version_id: "3316592"
|
| 435 |
+
- filename: "hybridcelTurboAnima_v30.safetensors"
|
| 436 |
+
source: "civitai"
|
| 437 |
+
model_version_id: "3340336"
|
| 438 |
+
- filename: "animaLuc_v3a.safetensors"
|
| 439 |
+
source: "civitai"
|
| 440 |
+
model_version_id: "3339348"
|
| 441 |
+
- filename: "novaBaraMaleAnima_novaV20.safetensors"
|
| 442 |
+
source: "civitai"
|
| 443 |
+
model_version_id: "3304198"
|
| 444 |
+
- filename: "fiamixAnimaReboot_v17.safetensors"
|
| 445 |
+
source: "civitai"
|
| 446 |
+
model_version_id: "3359008"
|
| 447 |
+
- filename: "novaCrossAM_new.safetensors"
|
| 448 |
+
source: "civitai"
|
| 449 |
+
model_version_id: "3431917"
|
| 450 |
+
- filename: "animaDeltaMix29B.safetensors"
|
| 451 |
+
source: "civitai"
|
| 452 |
+
model_version_id: "3414789"
|
| 453 |
+
- filename: "nununuMix.safetensors"
|
| 454 |
+
source: "civitai"
|
| 455 |
+
model_version_id: "3172155"
|
| 456 |
+
# Fibo 2.0
|
| 457 |
+
- filename: "fibo2_dev_int8_convrot.safetensors"
|
| 458 |
+
source: "hf"
|
| 459 |
+
repo_id: "Comfy-Org/Fibo-2.0"
|
| 460 |
+
repository_file_path: "diffusion_models/fibo2_dev_int8_convrot.safetensors"
|
| 461 |
+
- filename: "fibo2_edit_turbo_int8_convrot.safetensors"
|
| 462 |
+
source: "hf"
|
| 463 |
+
repo_id: "Comfy-Org/Fibo-2.0"
|
| 464 |
+
repository_file_path: "diffusion_models/fibo2_edit_turbo_int8_convrot.safetensors"
|
| 465 |
+
# Flux-3
|
| 466 |
+
- filename: "flux3-image-klein-nvfp4.safetensors"
|
| 467 |
+
source: "hf"
|
| 468 |
+
repo_id: "black-forest-labs/flux-3-image-klein-comfy"
|
| 469 |
+
repository_file_path: "diffusion_models/flux3-image-klein-nvfp4.safetensors"
|
| 470 |
+
- filename: "flux3_image_klein_sd_nvfp4.safetensors"
|
| 471 |
+
source: "hf"
|
| 472 |
+
repo_id: "black-forest-labs/flux-3-image-klein-comfy"
|
| 473 |
+
repository_file_path: "diffusion_models/flux3_image_klein_sd_nvfp4.safetensors"
|
| 474 |
+
- filename: "flux3-image-klein-base-nvfp4.safetensors"
|
| 475 |
+
source: "hf"
|
| 476 |
+
repo_id: "black-forest-labs/flux-3-image-klein-comfy"
|
| 477 |
+
repository_file_path: "diffusion_models/flux3-image-klein-base-nvfp4.safetensors"
|
| 478 |
+
# MOSS-Image
|
| 479 |
+
- filename: "moss_image_base_int8_convrot.safetensors"
|
| 480 |
+
source: "hf"
|
| 481 |
+
repo_id: "Comfy-Org/MOSS-Image"
|
| 482 |
+
repository_file_path: "diffusion_models/moss_image_base_int8_convrot.safetensors"
|
| 483 |
+
- filename: "moss_image_sft_int8_convrot.safetensors"
|
| 484 |
+
source: "hf"
|
| 485 |
+
repo_id: "Comfy-Org/MOSS-Image"
|
| 486 |
+
repository_file_path: "diffusion_models/moss_image_sft_int8_convrot.safetensors"
|
| 487 |
# Krea-2
|
| 488 |
- filename: "krea2_turbo_nvfp4.safetensors"
|
| 489 |
source: "hf"
|
|
|
|
| 493 |
source: "hf"
|
| 494 |
repo_id: "tsukiyomi/krea2_raw-nvfp4"
|
| 495 |
repository_file_path: "krea2_raw-nvfp4.safetensors"
|
| 496 |
+
- filename: "KreaNoct_V4_int8_convrot.safetensors"
|
| 497 |
+
source: "hf"
|
| 498 |
+
repo_id: "Noctaluna/Krea2-Noct-Uncensored"
|
| 499 |
+
repository_file_path: "KreaNoct_V4_int8_convrot.safetensors"
|
| 500 |
# Mage-Flow
|
| 501 |
- filename: "mage_flow_turbo_int8_convrot.safetensors"
|
| 502 |
source: "hf"
|
|
|
|
| 607 |
repo_id: "lylogummy/Anima-3.8B"
|
| 608 |
repository_file_path: "difussion_models/Anima-3.8B-v1.1.safetensors"
|
| 609 |
# Anima-3.8B
|
|
|
|
|
|
|
|
|
|
| 610 |
- filename: "Anima-3.8B.safetensors"
|
| 611 |
source: "hf"
|
| 612 |
repo_id: "lylogummy/Anima-3.8B"
|
| 613 |
repository_file_path: "difussion_models/Anima-3.8B.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 614 |
# Anima-2.9B
|
| 615 |
- filename: "Anima-2.9B-preview-v1.safetensors"
|
| 616 |
source: "hf"
|
| 617 |
repo_id: "Gazingstars123/Anima-2.9B"
|
| 618 |
repository_file_path: "Anima-2.9B-preview-v1.safetensors"
|
| 619 |
+
- filename: "AnimaNoct_V2_int8_convrot.safetensors"
|
| 620 |
+
source: "hf"
|
| 621 |
+
repo_id: "Noctaluna/Anima-Noct-Anime-Mix"
|
| 622 |
+
repository_file_path: "AnimaNoct_V2_int8_convrot.safetensors"
|
| 623 |
# Anima
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
- filename: "anima-turbo-v1.0.safetensors"
|
| 625 |
source: "hf"
|
| 626 |
repo_id: "circlestone-labs/Anima"
|
|
|
|
| 737 |
source: "hf"
|
| 738 |
repo_id: "Comfy-Org/Qwen-Image-2.1"
|
| 739 |
repository_file_path: "diffusion_models/qwen_image_2.1_int8_convrot.safetensors"
|
| 740 |
+
- filename: "qwen_image_2.1_turbo_int8_convrot.safetensors"
|
| 741 |
+
source: "hf"
|
| 742 |
+
repo_id: "Comfy-Org/Qwen-Image-2.1"
|
| 743 |
+
repository_file_path: "diffusion_models/qwen_image_2.1_turbo_int8_convrot.safetensors"
|
| 744 |
- filename: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
| 745 |
source: "hf"
|
| 746 |
repo_id: "abenzerps/Qwen-Image-2.1-Uncensored-GGUF"
|
| 747 |
repository_file_path: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
| 748 |
+
- filename: "NoctQ_V4_int8_convrot.safetensors"
|
| 749 |
+
source: "hf"
|
| 750 |
+
repo_id: "Noctaluna/Noct-Q-Uncensored-Qwen-Image-2.1"
|
| 751 |
+
repository_file_path: "NoctQ_V4_int8_convrot.safetensors"
|
| 752 |
+
- filename: "NoctQA_V1_int8_convrot.safetensors"
|
| 753 |
+
source: "hf"
|
| 754 |
+
repo_id: "Noctaluna/Noct-Q-Anime-Uncensored-Qwen-Image-2.1"
|
| 755 |
+
repository_file_path: "NoctQA_V1_int8_convrot.safetensors"
|
| 756 |
# Ming-Image
|
| 757 |
- filename: "ming_image_0.1_design_int8_convrot.safetensors"
|
| 758 |
source: "hf"
|
|
|
|
| 935 |
repo_id: "black-forest-labs/FLUX.1-Redux-dev"
|
| 936 |
repository_file_path: "flux1-redux-dev.safetensors"
|
| 937 |
loras:
|
| 938 |
+
# MOSS-Image Color/Depth Control
|
| 939 |
+
- filename: "moss-control.safetensors"
|
| 940 |
+
source: "hf"
|
| 941 |
+
repo_id: "darask0/MOSS-Image-color-depth-control"
|
| 942 |
+
repository_file_path: "moss-control.safetensors"
|
| 943 |
+
# MOSS-Image Edit
|
| 944 |
+
- filename: "moss_image_edit_lora_rank_64_bf16.safetensors"
|
| 945 |
+
source: "hf"
|
| 946 |
+
repo_id: "Comfy-Org/MOSS-Image"
|
| 947 |
+
repository_file_path: "loras/moss_image_edit_lora_rank_64_bf16.safetensors"
|
| 948 |
# Krea2 Identity Edit
|
| 949 |
- filename: "krea2_identity_edit_v1_2.safetensors"
|
| 950 |
source: "hf"
|
|
|
|
| 1015 |
# Qwen-Image-2.1
|
| 1016 |
- filename: "qwen_image_2.1_fun_controlnet_union_int8_convrot.safetensors"
|
| 1017 |
source: "hf"
|
| 1018 |
+
repo_id: "Comfy-Org/Qwen-Image-2.1"
|
| 1019 |
repository_file_path: "model_patches/qwen_image_2.1_fun_controlnet_union_int8_convrot.safetensors"
|
| 1020 |
text_encoders:
|
| 1021 |
+
# MOSS-Image
|
| 1022 |
+
- filename: "qwen3.5_9b_w4a8_gptq.safetensors"
|
| 1023 |
+
source: "hf"
|
| 1024 |
+
repo_id: "Comfy-Org/MOSS-Image"
|
| 1025 |
+
repository_file_path: "text_encoders/qwen3.5_9b_w4a8_gptq.safetensors"
|
| 1026 |
+
# Krea-2 / Fibo-2.0
|
| 1027 |
- filename: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 1028 |
source: "hf"
|
| 1029 |
repo_id: "Comfy-Org/Krea-2"
|
|
|
|
| 1066 |
source: "hf"
|
| 1067 |
repo_id: "Comfy-Org/Qwen3.8-27B"
|
| 1068 |
repository_file_path: "text_encoders/qwen3.8_27b_w4a8.safetensors"
|
| 1069 |
+
# FLUX.3 Prompt Upsampler
|
| 1070 |
+
- filename: "qwen3.5_9b_flux3_prompt_upsampler_w4a8.safetensors"
|
| 1071 |
+
source: "hf"
|
| 1072 |
+
repo_id: "black-forest-labs/flux-3-image-klein-comfy"
|
| 1073 |
+
repository_file_path: "text_encoders/qwen3.5_9b_flux3_prompt_upsampler_w4a8.safetensors"
|
| 1074 |
# PixelDiT
|
| 1075 |
- filename: "gemma_2_2b_it_elm_fp8_scaled.safetensors"
|
| 1076 |
source: "hf"
|
yaml/model_architecture_features.yaml
CHANGED
|
@@ -1,4 +1,17 @@
|
|
| 1 |
# Feature names in enabled_chains correspond 1-to-1 with chain_injectors/<name>_injector.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
krea-2:
|
| 3 |
enabled_chains:
|
| 4 |
- lora
|
|
@@ -70,6 +83,13 @@ z-image:
|
|
| 70 |
- vae
|
| 71 |
- pid
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
ming-image:
|
| 74 |
enabled_chains:
|
| 75 |
- lora
|
|
|
|
| 1 |
# Feature names in enabled_chains correspond 1-to-1 with chain_injectors/<name>_injector.py
|
| 2 |
+
fibo-2.0:
|
| 3 |
+
enabled_chains:
|
| 4 |
+
- lora
|
| 5 |
+
- reference_image
|
| 6 |
+
- pid
|
| 7 |
+
|
| 8 |
+
flux3:
|
| 9 |
+
enabled_chains:
|
| 10 |
+
- lora
|
| 11 |
+
- reference_latent
|
| 12 |
+
- flux3_prompt_upsampler
|
| 13 |
+
- pid
|
| 14 |
+
|
| 15 |
krea-2:
|
| 16 |
enabled_chains:
|
| 17 |
- lora
|
|
|
|
| 83 |
- vae
|
| 84 |
- pid
|
| 85 |
|
| 86 |
+
moss-image:
|
| 87 |
+
enabled_chains:
|
| 88 |
+
- lora
|
| 89 |
+
- moss_image_control
|
| 90 |
+
- moss_image_edit
|
| 91 |
+
- pid
|
| 92 |
+
|
| 93 |
ming-image:
|
| 94 |
enabled_chains:
|
| 95 |
- lora
|
yaml/model_architectures.yaml
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
architecture_order:
|
|
|
|
| 2 |
- "Krea-2"
|
| 3 |
- "Qwen-Image-2.1"
|
|
|
|
|
|
|
| 4 |
- "Ming-Image"
|
| 5 |
- "SenseNova-U1.5"
|
| 6 |
- "Mage-Flow"
|
|
@@ -36,6 +39,10 @@ architecture_order:
|
|
| 36 |
- "SD1.5"
|
| 37 |
|
| 38 |
architectures:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
Krea-2:
|
| 40 |
model_type: "krea-2"
|
| 41 |
"SenseNova-U1.5":
|
|
@@ -63,6 +70,8 @@ architectures:
|
|
| 63 |
"Z-Image":
|
| 64 |
model_type: "z-image"
|
| 65 |
controlnet_key: "Z-Image"
|
|
|
|
|
|
|
| 66 |
"Ming-Image":
|
| 67 |
model_type: "ming-image"
|
| 68 |
"Qwen-Image-2.1":
|
|
|
|
| 1 |
architecture_order:
|
| 2 |
+
- "FLUX.3"
|
| 3 |
- "Krea-2"
|
| 4 |
- "Qwen-Image-2.1"
|
| 5 |
+
- "Fibo-2.0"
|
| 6 |
+
- "MOSS-Image"
|
| 7 |
- "Ming-Image"
|
| 8 |
- "SenseNova-U1.5"
|
| 9 |
- "Mage-Flow"
|
|
|
|
| 39 |
- "SD1.5"
|
| 40 |
|
| 41 |
architectures:
|
| 42 |
+
"Fibo-2.0":
|
| 43 |
+
model_type: "fibo-2.0"
|
| 44 |
+
"FLUX.3":
|
| 45 |
+
model_type: "flux3"
|
| 46 |
Krea-2:
|
| 47 |
model_type: "krea-2"
|
| 48 |
"SenseNova-U1.5":
|
|
|
|
| 70 |
"Z-Image":
|
| 71 |
model_type: "z-image"
|
| 72 |
controlnet_key: "Z-Image"
|
| 73 |
+
"MOSS-Image":
|
| 74 |
+
model_type: "moss-image"
|
| 75 |
"Ming-Image":
|
| 76 |
model_type: "ming-image"
|
| 77 |
"Qwen-Image-2.1":
|
yaml/model_defaults.yaml
CHANGED
|
@@ -1,11 +1,50 @@
|
|
| 1 |
Default:
|
| 2 |
-
steps:
|
| 3 |
-
cfg:
|
| 4 |
sampler_name: "euler"
|
| 5 |
scheduler: "simple"
|
| 6 |
positive_prompt: ""
|
| 7 |
negative_prompt: ""
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
Krea-2:
|
| 10 |
_defaults:
|
| 11 |
steps: 52
|
|
@@ -17,6 +56,11 @@ Krea-2:
|
|
| 17 |
cfg: 1.0
|
| 18 |
sampler_name: "euler"
|
| 19 |
scheduler: "simple"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
SenseNova-U1.5:
|
| 22 |
_defaults:
|
|
@@ -131,6 +175,13 @@ Z-Image:
|
|
| 131 |
steps: 9
|
| 132 |
cfg: 1.0
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
Ming-Image:
|
| 135 |
_defaults:
|
| 136 |
steps: 12
|
|
@@ -144,6 +195,24 @@ Qwen-Image-2.1:
|
|
| 144 |
cfg: 1.0
|
| 145 |
sampler_name: "euler"
|
| 146 |
scheduler: "simple"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
Qwen-Image:
|
| 149 |
_defaults:
|
|
@@ -199,6 +268,13 @@ Anima:
|
|
| 199 |
scheduler: "simple"
|
| 200 |
positive_prompt: "masterpiece, best quality, score_7, safe, "
|
| 201 |
negative_prompt: "worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
"circlestone-labs/Anima-Turbo-v1.0":
|
| 203 |
steps: 10
|
| 204 |
cfg: 1.0
|
|
|
|
| 1 |
Default:
|
| 2 |
+
steps: 25
|
| 3 |
+
cfg: 3.5
|
| 4 |
sampler_name: "euler"
|
| 5 |
scheduler: "simple"
|
| 6 |
positive_prompt: ""
|
| 7 |
negative_prompt: ""
|
| 8 |
|
| 9 |
+
Fibo-2.0:
|
| 10 |
+
_defaults:
|
| 11 |
+
steps: 4
|
| 12 |
+
cfg: 1.0
|
| 13 |
+
sampler_name: "euler"
|
| 14 |
+
scheduler: "simple"
|
| 15 |
+
"Fibo-2.0-Edit-Turbo":
|
| 16 |
+
steps: 4
|
| 17 |
+
cfg: 1.0
|
| 18 |
+
sampler_name: "euler"
|
| 19 |
+
scheduler: "simple"
|
| 20 |
+
"Fibo-2.0-Dev":
|
| 21 |
+
steps: 30
|
| 22 |
+
cfg: 5.0
|
| 23 |
+
sampler_name: "euler"
|
| 24 |
+
scheduler: "simple"
|
| 25 |
+
|
| 26 |
+
FLUX.3:
|
| 27 |
+
_defaults:
|
| 28 |
+
steps: 25
|
| 29 |
+
cfg: 3.5
|
| 30 |
+
sampler_name: "euler"
|
| 31 |
+
scheduler: "simple"
|
| 32 |
+
"FLUX.3-Image-Klein":
|
| 33 |
+
steps: 25
|
| 34 |
+
cfg: 3.5
|
| 35 |
+
sampler_name: "euler"
|
| 36 |
+
scheduler: "simple"
|
| 37 |
+
"FLUX.3-Image-Klein-Distilled":
|
| 38 |
+
steps: 15
|
| 39 |
+
cfg: 1.0
|
| 40 |
+
sampler_name: "euler"
|
| 41 |
+
scheduler: "simple"
|
| 42 |
+
"FLUX-3-Image-Klein-Base":
|
| 43 |
+
steps: 50
|
| 44 |
+
cfg: 3.5
|
| 45 |
+
sampler_name: "euler"
|
| 46 |
+
scheduler: "simple"
|
| 47 |
+
|
| 48 |
Krea-2:
|
| 49 |
_defaults:
|
| 50 |
steps: 52
|
|
|
|
| 56 |
cfg: 1.0
|
| 57 |
sampler_name: "euler"
|
| 58 |
scheduler: "simple"
|
| 59 |
+
"Noctaluna/Krea2-Noct-Uncensored-V4":
|
| 60 |
+
steps: 8
|
| 61 |
+
cfg: 1.0
|
| 62 |
+
sampler_name: "euler"
|
| 63 |
+
scheduler: "simple"
|
| 64 |
|
| 65 |
SenseNova-U1.5:
|
| 66 |
_defaults:
|
|
|
|
| 175 |
steps: 9
|
| 176 |
cfg: 1.0
|
| 177 |
|
| 178 |
+
MOSS-Image:
|
| 179 |
+
_defaults:
|
| 180 |
+
steps: 50
|
| 181 |
+
cfg: 3.0
|
| 182 |
+
sampler_name: "euler"
|
| 183 |
+
scheduler: "simple"
|
| 184 |
+
|
| 185 |
Ming-Image:
|
| 186 |
_defaults:
|
| 187 |
steps: 12
|
|
|
|
| 195 |
cfg: 1.0
|
| 196 |
sampler_name: "euler"
|
| 197 |
scheduler: "simple"
|
| 198 |
+
"Qwen-Image-2.1-Turbo":
|
| 199 |
+
steps: 8
|
| 200 |
+
cfg: 1.0
|
| 201 |
+
sampler_name: "euler"
|
| 202 |
+
scheduler: "simple"
|
| 203 |
+
"Noctaluna/Noct-Q-Uncensored-Qwen-Image-2.1-V4":
|
| 204 |
+
steps: 25
|
| 205 |
+
cfg: 3.0
|
| 206 |
+
sampler_name: "euler"
|
| 207 |
+
scheduler: "simple"
|
| 208 |
+
negative_prompt: "artifacts, gpt-image, washed-out colors, low quality, low resolution, AI slop, deviantart, sloppy lines, rough sketch, blurry, indistinct, missing fingers, badly drawn hands, wrong number of fingers"
|
| 209 |
+
"Noctaluna/Noct-Q-Anime-Uncensored-Qwen-Image-2.1-V1":
|
| 210 |
+
steps: 25
|
| 211 |
+
cfg: 3.0
|
| 212 |
+
sampler_name: "euler"
|
| 213 |
+
scheduler: "simple"
|
| 214 |
+
positive_prompt: "An anime illustration of "
|
| 215 |
+
negative_prompt: "low quality, low resolution, blurry, jpeg artifacts, washed-out colors, sloppy lines, messy line art, extra fingers, missing fingers, badly drawn hands, deformed anatomy, 3d render, photograph, photorealistic"
|
| 216 |
|
| 217 |
Qwen-Image:
|
| 218 |
_defaults:
|
|
|
|
| 268 |
scheduler: "simple"
|
| 269 |
positive_prompt: "masterpiece, best quality, score_7, safe, "
|
| 270 |
negative_prompt: "worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia"
|
| 271 |
+
"Noctaluna/Anima-Noct-Anime-Mix-V2":
|
| 272 |
+
steps: 30
|
| 273 |
+
cfg: 4.0
|
| 274 |
+
sampler_name: "euler"
|
| 275 |
+
scheduler: "simple"
|
| 276 |
+
positive_prompt: "masterpiece, best quality, safe, 1girl, solo, mature female, An anime illustration of "
|
| 277 |
+
negative_prompt: "worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia"
|
| 278 |
"circlestone-labs/Anima-Turbo-v1.0":
|
| 279 |
steps: 10
|
| 280 |
cfg: 1.0
|
yaml/model_list.yaml
CHANGED
|
@@ -1,4 +1,22 @@
|
|
| 1 |
Checkpoint:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
Krea-2:
|
| 3 |
latent_type: latent
|
| 4 |
models:
|
|
@@ -12,6 +30,11 @@ Checkpoint:
|
|
| 12 |
unet: "krea2_raw-nvfp4.safetensors"
|
| 13 |
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 14 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
Qwen-Image-2.1:
|
| 16 |
latent_type: latent
|
| 17 |
models:
|
|
@@ -20,11 +43,52 @@ Checkpoint:
|
|
| 20 |
unet: "qwen_image_2.1_int8_convrot.safetensors"
|
| 21 |
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 22 |
clip: "qwen3vl_8b_nvfp4.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
- display_name: "abenzerps/Qwen-Image-2.1-Uncensored"
|
| 24 |
components:
|
| 25 |
unet: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
| 26 |
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 27 |
clip: "qwen3vl_8b_nvfp4.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
Ming-Image:
|
| 29 |
latent_type: latent
|
| 30 |
models:
|
|
@@ -269,7 +333,6 @@ Checkpoint:
|
|
| 269 |
clip: "qwen_3_06b_base.safetensors"
|
| 270 |
qwen35_model: "qwen35_4b.safetensors"
|
| 271 |
vae: "qwen_image_vae.safetensors"
|
| 272 |
-
|
| 273 |
Anima-3.8B:
|
| 274 |
latent_type: sd3_latent
|
| 275 |
models:
|
|
@@ -280,35 +343,30 @@ Checkpoint:
|
|
| 280 |
qwen35_model: "qwen35_4b.safetensors"
|
| 281 |
adapter_name: "Anima-3.8B-expanded_adapter.safetensors"
|
| 282 |
vae: "qwen_image_vae.safetensors"
|
| 283 |
-
|
| 284 |
- display_name: "BoleroMixAnima-v0.14-3.8B"
|
| 285 |
components:
|
| 286 |
unet: "boleromixAnima_v01438B.safetensors"
|
| 287 |
clip: "qwen_3_06b_base.safetensors"
|
| 288 |
qwen35_model: "qwen35_4b.safetensors"
|
| 289 |
vae: "qwen_image_vae.safetensors"
|
| 290 |
-
|
| 291 |
- display_name: "FiaMix Anima 3.8B"
|
| 292 |
components:
|
| 293 |
unet: "fiamixAnima38B_v12.safetensors"
|
| 294 |
clip: "qwen_3_06b_base.safetensors"
|
| 295 |
qwen35_model: "qwen35_4b.safetensors"
|
| 296 |
vae: "qwen_image_vae.safetensors"
|
| 297 |
-
|
| 298 |
- display_name: "PPP Anima 3.8B / Qwen 0.6B"
|
| 299 |
components:
|
| 300 |
unet: "pppAnima38BQwen06B_v20.safetensors"
|
| 301 |
clip: "qwen_3_06b_base.safetensors"
|
| 302 |
qwen35_model: "qwen35_4b.safetensors"
|
| 303 |
vae: "qwen_image_vae.safetensors"
|
| 304 |
-
|
| 305 |
- display_name: "Anima Delta Mix 3.8B / Qwen 0.6B"
|
| 306 |
components:
|
| 307 |
unet: "animaDeltaMix38BQwen06B.safetensors"
|
| 308 |
clip: "qwen_3_06b_base.safetensors"
|
| 309 |
qwen35_model: "qwen35_4b.safetensors"
|
| 310 |
vae: "qwen_image_vae.safetensors"
|
| 311 |
-
|
| 312 |
- display_name: "FiaMix Anima 3.8B (new)"
|
| 313 |
components:
|
| 314 |
unet: "fiamixAnima38B_new.safetensors"
|
|
@@ -324,139 +382,121 @@ Checkpoint:
|
|
| 324 |
unet: "anima-turbo-v1.0.safetensors"
|
| 325 |
vae: "qwen_image_vae.safetensors"
|
| 326 |
clip: "qwen_3_06b_base.safetensors"
|
| 327 |
-
|
| 328 |
- display_name: "circlestone-labs/Anima-Aesthetic-v1.1"
|
| 329 |
components:
|
| 330 |
unet: "anima-aesthetic-v1.1.safetensors"
|
| 331 |
vae: "qwen_image_vae.safetensors"
|
| 332 |
clip: "qwen_3_06b_base.safetensors"
|
| 333 |
-
|
| 334 |
- display_name: "circlestone-labs/Anima-Base-v1.0"
|
| 335 |
components:
|
| 336 |
unet: "anima-base-v1.0.safetensors"
|
| 337 |
vae: "qwen_image_vae.safetensors"
|
| 338 |
clip: "qwen_3_06b_base.safetensors"
|
| 339 |
-
|
| 340 |
- display_name: "WAI0731/waiANIMA-v1.0"
|
| 341 |
components:
|
| 342 |
unet: "waiANIMA_v10Base10.safetensors"
|
| 343 |
vae: "qwen_image_vae.safetensors"
|
| 344 |
clip: "qwen_3_06b_base.safetensors"
|
| 345 |
-
|
| 346 |
- display_name: "Gazingstars123/Anima-2.9B-preview-v1"
|
| 347 |
components:
|
| 348 |
unet: "Anima-2.9B-preview-v1.safetensors"
|
| 349 |
vae: "qwen_image_vae.safetensors"
|
| 350 |
clip: "qwen_3_06b_base.safetensors"
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
- display_name: "duongve/AnimaYume-v1.5"
|
| 353 |
components:
|
| 354 |
unet: "AnimaYume_v15_base.safetensors"
|
| 355 |
vae: "qwen_image_vae.safetensors"
|
| 356 |
clip: "qwen_3_06b_base.safetensors"
|
| 357 |
-
|
| 358 |
- display_name: "bluepen5805/Anima-pencil-v2.1"
|
| 359 |
components:
|
| 360 |
unet: "anima_pencil-v2.1.0.safetensors"
|
| 361 |
vae: "qwen_image_vae.safetensors"
|
| 362 |
clip: "qwen_3_06b_base.safetensors"
|
| 363 |
-
|
| 364 |
- display_name: "Ikena/Hassaku-Anima-v1.3"
|
| 365 |
components:
|
| 366 |
unet: "hassakuAnima_v13.safetensors"
|
| 367 |
vae: "qwen_image_vae.safetensors"
|
| 368 |
clip: "qwen_3_06b_base.safetensors"
|
| 369 |
-
|
| 370 |
- display_name: "motimalu/Kirazuri (Anima)-v4.0"
|
| 371 |
components:
|
| 372 |
unet: "anima-kirazuri-v4.safetensors"
|
| 373 |
vae: "qwen_image_vae.safetensors"
|
| 374 |
clip: "qwen_3_06b_base.safetensors"
|
| 375 |
-
|
| 376 |
- display_name: "Crody/Nova-Anime-AM-v4.0"
|
| 377 |
components:
|
| 378 |
unet: "novaAnimeAM_v40.safetensors"
|
| 379 |
vae: "qwen_image_vae.safetensors"
|
| 380 |
clip: "qwen_3_06b_base.safetensors"
|
| 381 |
-
|
| 382 |
-
- display_name: "Nova-Anime-AM-v5.0"
|
| 383 |
-
components:
|
| 384 |
-
unet: "NovaAnimeAMV50.safetensors"
|
| 385 |
-
vae: "qwen_image_vae.safetensors"
|
| 386 |
-
clip: "qwen_3_06b_base.safetensors"
|
| 387 |
-
|
| 388 |
- display_name: "Crody/Nova-Cross-AM-v1.0-C"
|
| 389 |
components:
|
| 390 |
unet: "novaCrossAM_v10C.safetensors"
|
| 391 |
vae: "qwen_image_vae.safetensors"
|
| 392 |
clip: "qwen_3_06b_base.safetensors"
|
| 393 |
-
|
| 394 |
- display_name: "Crody/Nova-Reality-AM-v1.0"
|
| 395 |
components:
|
| 396 |
unet: "novaRealityAM_v10.safetensors"
|
| 397 |
vae: "qwen_image_vae.safetensors"
|
| 398 |
clip: "qwen_3_06b_base.safetensors"
|
| 399 |
-
|
| 400 |
- display_name: "Crody/RAMTHRUST'S-NSFW-PINK-ALCHEMY-ANIMA-v3.1"
|
| 401 |
components:
|
| 402 |
unet: "ramthrustsNSFWPINK_v31RAMSDelicious.safetensors"
|
| 403 |
vae: "qwen_image_vae.safetensors"
|
| 404 |
clip: "qwen_3_06b_base.safetensors"
|
| 405 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
- display_name: "DreamCake"
|
| 407 |
components:
|
| 408 |
unet: "dreamcake_redvelvet.safetensors"
|
| 409 |
vae: "qwen_image_vae.safetensors"
|
| 410 |
clip: "qwen_3_06b_base.safetensors"
|
| 411 |
-
|
| 412 |
- display_name: "HuggingReverie | Anima"
|
| 413 |
components:
|
| 414 |
unet: "huggingreverieAnima_v20B.safetensors"
|
| 415 |
vae: "qwen_image_vae.safetensors"
|
| 416 |
clip: "qwen_3_06b_base.safetensors"
|
| 417 |
-
|
| 418 |
- display_name: "FiaMix Anima Reboot 2.5D"
|
| 419 |
components:
|
| 420 |
unet: "fiamixAnimaReboot25D_v11.safetensors"
|
| 421 |
vae: "qwen_image_vae.safetensors"
|
| 422 |
clip: "qwen_3_06b_base.safetensors"
|
| 423 |
-
|
| 424 |
- display_name: "HybridCel Turbo - Anima Model"
|
| 425 |
components:
|
| 426 |
unet: "hybridcelTurboAnima_v30.safetensors"
|
| 427 |
vae: "qwen_image_vae.safetensors"
|
| 428 |
clip: "qwen_3_06b_base.safetensors"
|
| 429 |
-
|
| 430 |
- display_name: "Anima Luc"
|
| 431 |
components:
|
| 432 |
unet: "animaLuc_v3a.safetensors"
|
| 433 |
vae: "qwen_image_vae.safetensors"
|
| 434 |
clip: "qwen_3_06b_base.safetensors"
|
| 435 |
-
|
| 436 |
- display_name: "NoVa Bara - Male Anima"
|
| 437 |
components:
|
| 438 |
unet: "novaBaraMaleAnima_novaV20.safetensors"
|
| 439 |
vae: "qwen_image_vae.safetensors"
|
| 440 |
clip: "qwen_3_06b_base.safetensors"
|
| 441 |
-
|
| 442 |
- display_name: "FiaMix Anima Reboot Cute"
|
| 443 |
components:
|
| 444 |
unet: "fiamixAnimaReboot_v17.safetensors"
|
| 445 |
vae: "qwen_image_vae.safetensors"
|
| 446 |
clip: "qwen_3_06b_base.safetensors"
|
| 447 |
-
|
| 448 |
- display_name: "Nova-Cross-AM (new)"
|
| 449 |
components:
|
| 450 |
unet: "novaCrossAM_new.safetensors"
|
| 451 |
vae: "qwen_image_vae.safetensors"
|
| 452 |
clip: "qwen_3_06b_base.safetensors"
|
| 453 |
-
|
| 454 |
- display_name: "Anima Delta Mix 2.9B"
|
| 455 |
components:
|
| 456 |
unet: "animaDeltaMix29B.safetensors"
|
| 457 |
vae: "qwen_image_vae.safetensors"
|
| 458 |
clip: "qwen_3_06b_base.safetensors"
|
| 459 |
-
|
| 460 |
- display_name: "NuNuNu Mix"
|
| 461 |
components:
|
| 462 |
unet: "nununuMix.safetensors"
|
|
|
|
| 1 |
Checkpoint:
|
| 2 |
+
FLUX.3:
|
| 3 |
+
latent_type: flux2_latent
|
| 4 |
+
models:
|
| 5 |
+
- display_name: "FLUX.3-Image-Klein"
|
| 6 |
+
components:
|
| 7 |
+
unet: "flux3-image-klein-nvfp4.safetensors"
|
| 8 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 9 |
+
vae: "flux2-vae.safetensors"
|
| 10 |
+
- display_name: "FLUX.3-Image-Klein-Distilled"
|
| 11 |
+
components:
|
| 12 |
+
unet: "flux3_image_klein_sd_nvfp4.safetensors"
|
| 13 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 14 |
+
vae: "flux2-vae.safetensors"
|
| 15 |
+
- display_name: "FLUX-3-Image-Klein-Base"
|
| 16 |
+
components:
|
| 17 |
+
unet: "flux3-image-klein-base-nvfp4.safetensors"
|
| 18 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 19 |
+
vae: "flux2-vae.safetensors"
|
| 20 |
Krea-2:
|
| 21 |
latent_type: latent
|
| 22 |
models:
|
|
|
|
| 30 |
unet: "krea2_raw-nvfp4.safetensors"
|
| 31 |
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 32 |
vae: "qwen_image_vae.safetensors"
|
| 33 |
+
- display_name: "Noctaluna/Krea2-Noct-Uncensored-V4"
|
| 34 |
+
components:
|
| 35 |
+
unet: "KreaNoct_V4_int8_convrot.safetensors"
|
| 36 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 37 |
+
vae: "qwen_image_vae.safetensors"
|
| 38 |
Qwen-Image-2.1:
|
| 39 |
latent_type: latent
|
| 40 |
models:
|
|
|
|
| 43 |
unet: "qwen_image_2.1_int8_convrot.safetensors"
|
| 44 |
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 45 |
clip: "qwen3vl_8b_nvfp4.safetensors"
|
| 46 |
+
- display_name: "Qwen-Image-2.1-Turbo"
|
| 47 |
+
components:
|
| 48 |
+
unet: "qwen_image_2.1_turbo_int8_convrot.safetensors"
|
| 49 |
+
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 50 |
+
clip: "qwen3vl_8b_nvfp4.safetensors"
|
| 51 |
- display_name: "abenzerps/Qwen-Image-2.1-Uncensored"
|
| 52 |
components:
|
| 53 |
unet: "qwen-image-2.1-UC-int8_convrot.safetensors"
|
| 54 |
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 55 |
clip: "qwen3vl_8b_nvfp4.safetensors"
|
| 56 |
+
- display_name: "Noctaluna/Noct-Q-Uncensored-Qwen-Image-2.1-V4"
|
| 57 |
+
components:
|
| 58 |
+
unet: "NoctQ_V4_int8_convrot.safetensors"
|
| 59 |
+
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 60 |
+
clip: "qwen3vl_8b_nvfp4.safetensors"
|
| 61 |
+
- display_name: "Noctaluna/Noct-Q-Anime-Uncensored-Qwen-Image-2.1-V1"
|
| 62 |
+
components:
|
| 63 |
+
unet: "NoctQA_V1_int8_convrot.safetensors"
|
| 64 |
+
vae: "qwen_image_2.1_vae_bf16.safetensors"
|
| 65 |
+
clip: "qwen3vl_8b_nvfp4.safetensors"
|
| 66 |
+
Fibo-2.0:
|
| 67 |
+
latent_type: none
|
| 68 |
+
models:
|
| 69 |
+
- display_name: "Fibo-2.0-Edit-Turbo"
|
| 70 |
+
components:
|
| 71 |
+
unet: "fibo2_edit_turbo_int8_convrot.safetensors"
|
| 72 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 73 |
+
vae: "flux2-vae.safetensors"
|
| 74 |
+
- display_name: "Fibo-2.0-Dev"
|
| 75 |
+
components:
|
| 76 |
+
unet: "fibo2_dev_int8_convrot.safetensors"
|
| 77 |
+
clip: "qwen3vl_4b_fp8_scaled.safetensors"
|
| 78 |
+
vae: "flux2-vae.safetensors"
|
| 79 |
+
MOSS-Image:
|
| 80 |
+
latent_type: flux2_latent
|
| 81 |
+
models:
|
| 82 |
+
- display_name: "MOSS-Image-SFT"
|
| 83 |
+
components:
|
| 84 |
+
unet: "moss_image_sft_int8_convrot.safetensors"
|
| 85 |
+
clip: "qwen3.5_9b_w4a8_gptq.safetensors"
|
| 86 |
+
vae: "flux2-vae.safetensors"
|
| 87 |
+
- display_name: "MOSS-Image-Base"
|
| 88 |
+
components:
|
| 89 |
+
unet: "moss_image_base_int8_convrot.safetensors"
|
| 90 |
+
clip: "qwen3.5_9b_w4a8_gptq.safetensors"
|
| 91 |
+
vae: "flux2-vae.safetensors"
|
| 92 |
Ming-Image:
|
| 93 |
latent_type: latent
|
| 94 |
models:
|
|
|
|
| 333 |
clip: "qwen_3_06b_base.safetensors"
|
| 334 |
qwen35_model: "qwen35_4b.safetensors"
|
| 335 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 336 |
Anima-3.8B:
|
| 337 |
latent_type: sd3_latent
|
| 338 |
models:
|
|
|
|
| 343 |
qwen35_model: "qwen35_4b.safetensors"
|
| 344 |
adapter_name: "Anima-3.8B-expanded_adapter.safetensors"
|
| 345 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 346 |
- display_name: "BoleroMixAnima-v0.14-3.8B"
|
| 347 |
components:
|
| 348 |
unet: "boleromixAnima_v01438B.safetensors"
|
| 349 |
clip: "qwen_3_06b_base.safetensors"
|
| 350 |
qwen35_model: "qwen35_4b.safetensors"
|
| 351 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 352 |
- display_name: "FiaMix Anima 3.8B"
|
| 353 |
components:
|
| 354 |
unet: "fiamixAnima38B_v12.safetensors"
|
| 355 |
clip: "qwen_3_06b_base.safetensors"
|
| 356 |
qwen35_model: "qwen35_4b.safetensors"
|
| 357 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 358 |
- display_name: "PPP Anima 3.8B / Qwen 0.6B"
|
| 359 |
components:
|
| 360 |
unet: "pppAnima38BQwen06B_v20.safetensors"
|
| 361 |
clip: "qwen_3_06b_base.safetensors"
|
| 362 |
qwen35_model: "qwen35_4b.safetensors"
|
| 363 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 364 |
- display_name: "Anima Delta Mix 3.8B / Qwen 0.6B"
|
| 365 |
components:
|
| 366 |
unet: "animaDeltaMix38BQwen06B.safetensors"
|
| 367 |
clip: "qwen_3_06b_base.safetensors"
|
| 368 |
qwen35_model: "qwen35_4b.safetensors"
|
| 369 |
vae: "qwen_image_vae.safetensors"
|
|
|
|
| 370 |
- display_name: "FiaMix Anima 3.8B (new)"
|
| 371 |
components:
|
| 372 |
unet: "fiamixAnima38B_new.safetensors"
|
|
|
|
| 382 |
unet: "anima-turbo-v1.0.safetensors"
|
| 383 |
vae: "qwen_image_vae.safetensors"
|
| 384 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 385 |
- display_name: "circlestone-labs/Anima-Aesthetic-v1.1"
|
| 386 |
components:
|
| 387 |
unet: "anima-aesthetic-v1.1.safetensors"
|
| 388 |
vae: "qwen_image_vae.safetensors"
|
| 389 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 390 |
- display_name: "circlestone-labs/Anima-Base-v1.0"
|
| 391 |
components:
|
| 392 |
unet: "anima-base-v1.0.safetensors"
|
| 393 |
vae: "qwen_image_vae.safetensors"
|
| 394 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 395 |
- display_name: "WAI0731/waiANIMA-v1.0"
|
| 396 |
components:
|
| 397 |
unet: "waiANIMA_v10Base10.safetensors"
|
| 398 |
vae: "qwen_image_vae.safetensors"
|
| 399 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 400 |
- display_name: "Gazingstars123/Anima-2.9B-preview-v1"
|
| 401 |
components:
|
| 402 |
unet: "Anima-2.9B-preview-v1.safetensors"
|
| 403 |
vae: "qwen_image_vae.safetensors"
|
| 404 |
clip: "qwen_3_06b_base.safetensors"
|
| 405 |
+
- display_name: "Noctaluna/Anima-Noct-Anime-Mix-V2"
|
| 406 |
+
components:
|
| 407 |
+
unet: "AnimaNoct_V2_int8_convrot.safetensors"
|
| 408 |
+
vae: "qwen_image_vae.safetensors"
|
| 409 |
+
clip: "qwen_3_06b_base.safetensors"
|
| 410 |
- display_name: "duongve/AnimaYume-v1.5"
|
| 411 |
components:
|
| 412 |
unet: "AnimaYume_v15_base.safetensors"
|
| 413 |
vae: "qwen_image_vae.safetensors"
|
| 414 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 415 |
- display_name: "bluepen5805/Anima-pencil-v2.1"
|
| 416 |
components:
|
| 417 |
unet: "anima_pencil-v2.1.0.safetensors"
|
| 418 |
vae: "qwen_image_vae.safetensors"
|
| 419 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 420 |
- display_name: "Ikena/Hassaku-Anima-v1.3"
|
| 421 |
components:
|
| 422 |
unet: "hassakuAnima_v13.safetensors"
|
| 423 |
vae: "qwen_image_vae.safetensors"
|
| 424 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 425 |
- display_name: "motimalu/Kirazuri (Anima)-v4.0"
|
| 426 |
components:
|
| 427 |
unet: "anima-kirazuri-v4.safetensors"
|
| 428 |
vae: "qwen_image_vae.safetensors"
|
| 429 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 430 |
- display_name: "Crody/Nova-Anime-AM-v4.0"
|
| 431 |
components:
|
| 432 |
unet: "novaAnimeAM_v40.safetensors"
|
| 433 |
vae: "qwen_image_vae.safetensors"
|
| 434 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
- display_name: "Crody/Nova-Cross-AM-v1.0-C"
|
| 436 |
components:
|
| 437 |
unet: "novaCrossAM_v10C.safetensors"
|
| 438 |
vae: "qwen_image_vae.safetensors"
|
| 439 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 440 |
- display_name: "Crody/Nova-Reality-AM-v1.0"
|
| 441 |
components:
|
| 442 |
unet: "novaRealityAM_v10.safetensors"
|
| 443 |
vae: "qwen_image_vae.safetensors"
|
| 444 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 445 |
- display_name: "Crody/RAMTHRUST'S-NSFW-PINK-ALCHEMY-ANIMA-v3.1"
|
| 446 |
components:
|
| 447 |
unet: "ramthrustsNSFWPINK_v31RAMSDelicious.safetensors"
|
| 448 |
vae: "qwen_image_vae.safetensors"
|
| 449 |
clip: "qwen_3_06b_base.safetensors"
|
| 450 |
+
- display_name: "Nova-Anime-AM-v5.0"
|
| 451 |
+
components:
|
| 452 |
+
unet: "NovaAnimeAMV50.safetensors"
|
| 453 |
+
vae: "qwen_image_vae.safetensors"
|
| 454 |
+
clip: "qwen_3_06b_base.safetensors"
|
| 455 |
- display_name: "DreamCake"
|
| 456 |
components:
|
| 457 |
unet: "dreamcake_redvelvet.safetensors"
|
| 458 |
vae: "qwen_image_vae.safetensors"
|
| 459 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 460 |
- display_name: "HuggingReverie | Anima"
|
| 461 |
components:
|
| 462 |
unet: "huggingreverieAnima_v20B.safetensors"
|
| 463 |
vae: "qwen_image_vae.safetensors"
|
| 464 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 465 |
- display_name: "FiaMix Anima Reboot 2.5D"
|
| 466 |
components:
|
| 467 |
unet: "fiamixAnimaReboot25D_v11.safetensors"
|
| 468 |
vae: "qwen_image_vae.safetensors"
|
| 469 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 470 |
- display_name: "HybridCel Turbo - Anima Model"
|
| 471 |
components:
|
| 472 |
unet: "hybridcelTurboAnima_v30.safetensors"
|
| 473 |
vae: "qwen_image_vae.safetensors"
|
| 474 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 475 |
- display_name: "Anima Luc"
|
| 476 |
components:
|
| 477 |
unet: "animaLuc_v3a.safetensors"
|
| 478 |
vae: "qwen_image_vae.safetensors"
|
| 479 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 480 |
- display_name: "NoVa Bara - Male Anima"
|
| 481 |
components:
|
| 482 |
unet: "novaBaraMaleAnima_novaV20.safetensors"
|
| 483 |
vae: "qwen_image_vae.safetensors"
|
| 484 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 485 |
- display_name: "FiaMix Anima Reboot Cute"
|
| 486 |
components:
|
| 487 |
unet: "fiamixAnimaReboot_v17.safetensors"
|
| 488 |
vae: "qwen_image_vae.safetensors"
|
| 489 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 490 |
- display_name: "Nova-Cross-AM (new)"
|
| 491 |
components:
|
| 492 |
unet: "novaCrossAM_new.safetensors"
|
| 493 |
vae: "qwen_image_vae.safetensors"
|
| 494 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 495 |
- display_name: "Anima Delta Mix 2.9B"
|
| 496 |
components:
|
| 497 |
unet: "animaDeltaMix29B.safetensors"
|
| 498 |
vae: "qwen_image_vae.safetensors"
|
| 499 |
clip: "qwen_3_06b_base.safetensors"
|
|
|
|
| 500 |
- display_name: "NuNuNu Mix"
|
| 501 |
components:
|
| 502 |
unet: "nununuMix.safetensors"
|
yaml/pid.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
PiD:
|
| 2 |
- filepath: "pid_1.5_flux2_1024_to_4096_4step_int8_convrot.safetensors"
|
| 3 |
latent_format: "flux"
|
| 4 |
-
architectures: ["flux2", "flux2-kv", "ideogram-4", "lens", "ernie-image"]
|
| 5 |
- filepath: "pid_1.5_qwenimage_1024_to_4096_4step_int8_convrot.safetensors"
|
| 6 |
latent_format: "qwenimage"
|
| 7 |
architectures: ["anima-3.8b-v1.1", "anima-3.8b", "anima", "qwen-image", "krea-2"]
|
|
|
|
| 1 |
PiD:
|
| 2 |
- filepath: "pid_1.5_flux2_1024_to_4096_4step_int8_convrot.safetensors"
|
| 3 |
latent_format: "flux"
|
| 4 |
+
architectures: ["flux3", "flux2", "flux2-kv", "ideogram-4", "lens", "ernie-image", "moss-image", "fibo-2.0"]
|
| 5 |
- filepath: "pid_1.5_qwenimage_1024_to_4096_4step_int8_convrot.safetensors"
|
| 6 |
latent_format: "qwenimage"
|
| 7 |
architectures: ["anima-3.8b-v1.1", "anima-3.8b", "anima", "qwen-image", "krea-2"]
|
yaml/task_features.yaml
CHANGED
|
@@ -24,6 +24,9 @@ txt2img:
|
|
| 24 |
- reference_image
|
| 25 |
- qwen_image_2_1_prompt_enhancer
|
| 26 |
- ming_image_prompt_enhancer
|
|
|
|
|
|
|
|
|
|
| 27 |
- pid
|
| 28 |
- vae
|
| 29 |
|
|
@@ -48,6 +51,9 @@ img2img:
|
|
| 48 |
- reference_image
|
| 49 |
- qwen_image_2_1_prompt_enhancer
|
| 50 |
- ming_image_prompt_enhancer
|
|
|
|
|
|
|
|
|
|
| 51 |
- vae
|
| 52 |
|
| 53 |
inpaint:
|
|
@@ -71,6 +77,9 @@ inpaint:
|
|
| 71 |
- reference_image
|
| 72 |
- qwen_image_2_1_prompt_enhancer
|
| 73 |
- ming_image_prompt_enhancer
|
|
|
|
|
|
|
|
|
|
| 74 |
- vae
|
| 75 |
|
| 76 |
outpaint:
|
|
@@ -94,6 +103,9 @@ outpaint:
|
|
| 94 |
- reference_image
|
| 95 |
- qwen_image_2_1_prompt_enhancer
|
| 96 |
- ming_image_prompt_enhancer
|
|
|
|
|
|
|
|
|
|
| 97 |
- vae
|
| 98 |
|
| 99 |
hires_fix:
|
|
@@ -117,4 +129,7 @@ hires_fix:
|
|
| 117 |
- reference_image
|
| 118 |
- qwen_image_2_1_prompt_enhancer
|
| 119 |
- ming_image_prompt_enhancer
|
|
|
|
|
|
|
|
|
|
| 120 |
- vae
|
|
|
|
| 24 |
- reference_image
|
| 25 |
- qwen_image_2_1_prompt_enhancer
|
| 26 |
- ming_image_prompt_enhancer
|
| 27 |
+
- flux3_prompt_upsampler
|
| 28 |
+
- moss_image_control
|
| 29 |
+
- moss_image_edit
|
| 30 |
- pid
|
| 31 |
- vae
|
| 32 |
|
|
|
|
| 51 |
- reference_image
|
| 52 |
- qwen_image_2_1_prompt_enhancer
|
| 53 |
- ming_image_prompt_enhancer
|
| 54 |
+
- flux3_prompt_upsampler
|
| 55 |
+
- moss_image_control
|
| 56 |
+
- moss_image_edit
|
| 57 |
- vae
|
| 58 |
|
| 59 |
inpaint:
|
|
|
|
| 77 |
- reference_image
|
| 78 |
- qwen_image_2_1_prompt_enhancer
|
| 79 |
- ming_image_prompt_enhancer
|
| 80 |
+
- flux3_prompt_upsampler
|
| 81 |
+
- moss_image_control
|
| 82 |
+
- moss_image_edit
|
| 83 |
- vae
|
| 84 |
|
| 85 |
outpaint:
|
|
|
|
| 103 |
- reference_image
|
| 104 |
- qwen_image_2_1_prompt_enhancer
|
| 105 |
- ming_image_prompt_enhancer
|
| 106 |
+
- flux3_prompt_upsampler
|
| 107 |
+
- moss_image_control
|
| 108 |
+
- moss_image_edit
|
| 109 |
- vae
|
| 110 |
|
| 111 |
hires_fix:
|
|
|
|
| 129 |
- reference_image
|
| 130 |
- qwen_image_2_1_prompt_enhancer
|
| 131 |
- ming_image_prompt_enhancer
|
| 132 |
+
- flux3_prompt_upsampler
|
| 133 |
+
- moss_image_control
|
| 134 |
+
- moss_image_edit
|
| 135 |
- vae
|