Spaces:
Running on Zero
Running on Zero
Upload folder using huggingface_hub
Browse files- chain_injectors/lora_injector.py +67 -0
- core/model_manager.py +3 -115
- core/pipelines/sd_image_pipeline.py +60 -33
- core/pipelines/workflow_recipes/_partials/conditioning/sdxl.yaml +13 -0
- core/settings.py +1 -2
- requirements.txt +2 -2
- ui/events.py +5 -98
- ui/layout.py +2 -27
- utils/app_utils.py +0 -37
- yaml/constants.yaml +1 -17
- yaml/injectors.yaml +3 -0
chain_injectors/lora_injector.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from copy import deepcopy
|
| 2 |
+
|
| 3 |
+
def inject(assembler, chain_definition, chain_items):
|
| 4 |
+
if not chain_items:
|
| 5 |
+
return
|
| 6 |
+
|
| 7 |
+
start_node_name = chain_definition.get('start')
|
| 8 |
+
start_node_id = None
|
| 9 |
+
if start_node_name:
|
| 10 |
+
if start_node_name not in assembler.node_map:
|
| 11 |
+
print(f"Warning: Start node '{start_node_name}' for dynamic LoRA chain not found. Skipping chain.")
|
| 12 |
+
return
|
| 13 |
+
start_node_id = assembler.node_map[start_node_name]
|
| 14 |
+
|
| 15 |
+
output_map = chain_definition.get('output_map', {})
|
| 16 |
+
current_connections = {}
|
| 17 |
+
for key, type_name in output_map.items():
|
| 18 |
+
if ':' in str(key):
|
| 19 |
+
node_name, idx_str = key.split(':')
|
| 20 |
+
if node_name not in assembler.node_map:
|
| 21 |
+
print(f"Warning: Node '{node_name}' in chain's output_map not found. Skipping.")
|
| 22 |
+
continue
|
| 23 |
+
node_id = assembler.node_map[node_name]
|
| 24 |
+
start_output_idx = int(idx_str)
|
| 25 |
+
current_connections[type_name] = [node_id, start_output_idx]
|
| 26 |
+
elif start_node_id:
|
| 27 |
+
start_output_idx = int(key)
|
| 28 |
+
current_connections[type_name] = [start_node_id, start_output_idx]
|
| 29 |
+
else:
|
| 30 |
+
print(f"Warning: LoRA chain has no 'start' node defined, and an output_map key '{key}' is not in 'node:index' format. Skipping this connection.")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
input_map = chain_definition.get('input_map', {})
|
| 34 |
+
chain_output_map = chain_definition.get('template_output_map', { "0": "model", "1": "clip" })
|
| 35 |
+
|
| 36 |
+
for item_data in chain_items:
|
| 37 |
+
template_name = chain_definition['template']
|
| 38 |
+
template = assembler._get_node_template(template_name)
|
| 39 |
+
node_data = deepcopy(template)
|
| 40 |
+
|
| 41 |
+
for param_name, value in item_data.items():
|
| 42 |
+
if param_name in node_data['inputs']:
|
| 43 |
+
node_data['inputs'][param_name] = value
|
| 44 |
+
|
| 45 |
+
for type_name, input_name in input_map.items():
|
| 46 |
+
if type_name in current_connections:
|
| 47 |
+
node_data['inputs'][input_name] = current_connections[type_name]
|
| 48 |
+
|
| 49 |
+
new_node_id = assembler._get_unique_id()
|
| 50 |
+
assembler.workflow[new_node_id] = node_data
|
| 51 |
+
|
| 52 |
+
for idx_str, type_name in chain_output_map.items():
|
| 53 |
+
current_connections[type_name] = [new_node_id, int(idx_str)]
|
| 54 |
+
|
| 55 |
+
end_input_map = chain_definition.get('end_input_map', {})
|
| 56 |
+
for type_name, targets in end_input_map.items():
|
| 57 |
+
if type_name in current_connections:
|
| 58 |
+
if not isinstance(targets, list):
|
| 59 |
+
targets = [targets]
|
| 60 |
+
|
| 61 |
+
for target_str in targets:
|
| 62 |
+
end_node_name, end_input_name = target_str.split(':')
|
| 63 |
+
if end_node_name in assembler.node_map:
|
| 64 |
+
end_node_id = assembler.node_map[end_node_name]
|
| 65 |
+
assembler.workflow[end_node_id]['inputs'][end_input_name] = current_connections[type_name]
|
| 66 |
+
else:
|
| 67 |
+
print(f"Warning: End node '{end_node_name}' for dynamic chain not found. Skipping connection.")
|
core/model_manager.py
CHANGED
|
@@ -1,15 +1,9 @@
|
|
| 1 |
import gc
|
| 2 |
-
from typing import
|
| 3 |
-
|
| 4 |
-
import torch
|
| 5 |
import gradio as gr
|
| 6 |
-
from comfy import model_management
|
| 7 |
-
|
| 8 |
-
from core.settings import ALL_MODEL_MAP, CHECKPOINT_DIR, LORA_DIR, DIFFUSION_MODELS_DIR, VAE_DIR, TEXT_ENCODERS_DIR
|
| 9 |
-
from comfy_integration.nodes import checkpointloadersimple, LoraLoader
|
| 10 |
-
from nodes import NODE_CLASS_MAPPINGS
|
| 11 |
-
from utils.app_utils import get_value_at_index, _ensure_model_downloaded
|
| 12 |
|
|
|
|
|
|
|
| 13 |
|
| 14 |
class ModelManager:
|
| 15 |
_instance = None
|
|
@@ -22,59 +16,9 @@ class ModelManager:
|
|
| 22 |
def __init__(self):
|
| 23 |
if hasattr(self, 'initialized'):
|
| 24 |
return
|
| 25 |
-
self.loaded_models: Dict[str, Any] = {}
|
| 26 |
-
self.last_active_loras: List[Dict[str, Any]] = []
|
| 27 |
self.initialized = True
|
| 28 |
print("✅ ModelManager initialized.")
|
| 29 |
|
| 30 |
-
def get_loaded_model_names(self) -> Set[str]:
|
| 31 |
-
return set(self.loaded_models.keys())
|
| 32 |
-
|
| 33 |
-
def _load_single_model(self, display_name: str, progress) -> Any:
|
| 34 |
-
print(f"--- [ModelManager] Loading model: '{display_name}' ---")
|
| 35 |
-
|
| 36 |
-
filename = _ensure_model_downloaded(display_name, progress)
|
| 37 |
-
|
| 38 |
-
_, _, model_type, _ = ALL_MODEL_MAP[display_name]
|
| 39 |
-
|
| 40 |
-
loader_map = {
|
| 41 |
-
"SDXL": (checkpointloadersimple, "load_checkpoint", {"ckpt_name": filename}),
|
| 42 |
-
"SD1.5": (checkpointloadersimple, "load_checkpoint", {"ckpt_name": filename}),
|
| 43 |
-
"UNET": (NODE_CLASS_MAPPINGS["UNETLoader"](), "load_unet", {"unet_name": filename, "weight_dtype": "default"}),
|
| 44 |
-
"VAE": (NODE_CLASS_MAPPINGS["VAELoader"](), "load_vae", {"vae_name": filename}),
|
| 45 |
-
"TEXT_ENCODER": (NODE_CLASS_MAPPINGS["CLIPLoader"](), "load_clip", {"clip_name": filename, "type": "wan", "device": "default"}),
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
if model_type not in loader_map:
|
| 49 |
-
if model_type == "LORA":
|
| 50 |
-
print(f"--- [ModelManager] ✅ '{display_name}' is a LoRA. It will be loaded dynamically. ---")
|
| 51 |
-
return (filename,)
|
| 52 |
-
raise ValueError(f"[ModelManager] No loader configured for model type '{model_type}'")
|
| 53 |
-
|
| 54 |
-
loader_instance, method_name, kwargs = loader_map[model_type]
|
| 55 |
-
|
| 56 |
-
load_method = getattr(loader_instance, method_name)
|
| 57 |
-
loaded_tuple = load_method(**kwargs)
|
| 58 |
-
|
| 59 |
-
print(f"--- [ModelManager] ✅ Successfully loaded '{display_name}' to CPU/RAM ---")
|
| 60 |
-
return loaded_tuple
|
| 61 |
-
|
| 62 |
-
def move_models_to_gpu(self, required_models: List[str]):
|
| 63 |
-
print(f"--- [ModelManager] Moving models to GPU: {required_models} ---")
|
| 64 |
-
models_to_load_gpu = []
|
| 65 |
-
for name in required_models:
|
| 66 |
-
if name in self.loaded_models:
|
| 67 |
-
model_tuple = self.loaded_models[name]
|
| 68 |
-
_, _, model_type, _ = ALL_MODEL_MAP[name]
|
| 69 |
-
if model_type in ["SDXL", "SD1.5"]:
|
| 70 |
-
models_to_load_gpu.append(get_value_at_index(model_tuple, 0))
|
| 71 |
-
|
| 72 |
-
if models_to_load_gpu:
|
| 73 |
-
model_management.load_models_gpu(models_to_load_gpu)
|
| 74 |
-
print("--- [ModelManager] ✅ Models successfully moved to GPU. ---")
|
| 75 |
-
else:
|
| 76 |
-
print("--- [ModelManager] ⚠️ No checkpoint models found to move to GPU. ---")
|
| 77 |
-
|
| 78 |
def ensure_models_downloaded(self, required_models: List[str], progress):
|
| 79 |
print(f"--- [ModelManager] Ensuring models are downloaded: {required_models} ---")
|
| 80 |
for i, display_name in enumerate(required_models):
|
|
@@ -85,61 +29,5 @@ class ModelManager:
|
|
| 85 |
except Exception as e:
|
| 86 |
raise gr.Error(f"Failed to download model '{display_name}'. Reason: {e}")
|
| 87 |
print(f"--- [ModelManager] ✅ All required models are present on disk. ---")
|
| 88 |
-
|
| 89 |
-
def load_managed_models(self, required_models: List[str], active_loras: List[Dict[str, Any]], progress) -> Dict[str, Any]:
|
| 90 |
-
required_set = set(required_models)
|
| 91 |
-
current_set = set(self.loaded_models.keys())
|
| 92 |
-
|
| 93 |
-
loras_changed = self.last_active_loras != active_loras
|
| 94 |
-
models_to_unload = current_set - required_set
|
| 95 |
-
|
| 96 |
-
must_reload = bool(models_to_unload) or loras_changed
|
| 97 |
-
|
| 98 |
-
if must_reload:
|
| 99 |
-
if models_to_unload:
|
| 100 |
-
print(f"--- [ModelManager] Models to unload: {models_to_unload} ---")
|
| 101 |
-
if loras_changed and not models_to_unload:
|
| 102 |
-
print(f"--- [ModelManager] LoRA configuration changed. Reloading base model(s): {current_set.intersection(required_set)} ---")
|
| 103 |
-
|
| 104 |
-
model_management.unload_all_models()
|
| 105 |
-
self.loaded_models.clear()
|
| 106 |
-
gc.collect()
|
| 107 |
-
torch.cuda.empty_cache()
|
| 108 |
-
print("--- [ModelManager] All models unloaded to free RAM. ---")
|
| 109 |
-
|
| 110 |
-
models_to_load = required_set if must_reload else (required_set - current_set)
|
| 111 |
-
|
| 112 |
-
if models_to_load:
|
| 113 |
-
print(f"--- [ModelManager] Models to load: {models_to_load} ---")
|
| 114 |
-
for i, display_name in enumerate(models_to_load):
|
| 115 |
-
progress(i / len(models_to_load), desc=f"Loading model: {display_name}")
|
| 116 |
-
try:
|
| 117 |
-
loaded_model_data = self._load_single_model(display_name, progress)
|
| 118 |
-
|
| 119 |
-
if active_loras and ALL_MODEL_MAP[display_name][2] in ["SDXL", "SD1.5"]:
|
| 120 |
-
print(f"--- [ModelManager] Applying {len(active_loras)} LoRAs on CPU... ---")
|
| 121 |
-
lora_loader = LoraLoader()
|
| 122 |
-
patched_model, patched_clip = loaded_model_data[0], loaded_model_data[1]
|
| 123 |
-
|
| 124 |
-
for lora_info in active_loras:
|
| 125 |
-
patched_model, patched_clip = lora_loader.load_lora(
|
| 126 |
-
model=patched_model,
|
| 127 |
-
clip=patched_clip,
|
| 128 |
-
lora_name=lora_info["lora_name"],
|
| 129 |
-
strength_model=lora_info["strength_model"],
|
| 130 |
-
strength_clip=lora_info["strength_clip"]
|
| 131 |
-
)
|
| 132 |
-
|
| 133 |
-
loaded_model_data = (patched_model, patched_clip, loaded_model_data[2])
|
| 134 |
-
print(f"--- [ModelManager] ✅ All LoRAs merged into the model on CPU. ---")
|
| 135 |
-
|
| 136 |
-
self.loaded_models[display_name] = loaded_model_data
|
| 137 |
-
except Exception as e:
|
| 138 |
-
raise gr.Error(f"Failed to load model or apply LoRA '{display_name}'. Reason: {e}")
|
| 139 |
-
else:
|
| 140 |
-
print(f"--- [ModelManager] All required models are already loaded. ---")
|
| 141 |
-
|
| 142 |
-
self.last_active_loras = active_loras
|
| 143 |
-
return {name: self.loaded_models[name] for name in required_models}
|
| 144 |
|
| 145 |
model_manager = ModelManager()
|
|
|
|
| 1 |
import gc
|
| 2 |
+
from typing import List
|
|
|
|
|
|
|
| 3 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
from core.settings import ALL_MODEL_MAP
|
| 6 |
+
from utils.app_utils import _ensure_model_downloaded
|
| 7 |
|
| 8 |
class ModelManager:
|
| 9 |
_instance = None
|
|
|
|
| 16 |
def __init__(self):
|
| 17 |
if hasattr(self, 'initialized'):
|
| 18 |
return
|
|
|
|
|
|
|
| 19 |
self.initialized = True
|
| 20 |
print("✅ ModelManager initialized.")
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
def ensure_models_downloaded(self, required_models: List[str], progress):
|
| 23 |
print(f"--- [ModelManager] Ensuring models are downloaded: {required_models} ---")
|
| 24 |
for i, display_name in enumerate(required_models):
|
|
|
|
| 29 |
except Exception as e:
|
| 30 |
raise gr.Error(f"Failed to download model '{display_name}'. Reason: {e}")
|
| 31 |
print(f"--- [ModelManager] ✅ All required models are present on disk. ---")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
model_manager = ModelManager()
|
core/pipelines/sd_image_pipeline.py
CHANGED
|
@@ -47,7 +47,6 @@ class SdImagePipeline(BasePipeline):
|
|
| 47 |
|
| 48 |
return sorted_nodes
|
| 49 |
|
| 50 |
-
|
| 51 |
def _execute_workflow(self, workflow: Dict[str, Any], initial_objects: Dict[str, Any]):
|
| 52 |
with torch.no_grad():
|
| 53 |
computed_outputs = initial_objects
|
|
@@ -71,6 +70,10 @@ class SdImagePipeline(BasePipeline):
|
|
| 71 |
node_info = workflow[node_id]
|
| 72 |
class_type = node_info['class_type']
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
node_class = NODE_CLASS_MAPPINGS.get(class_type)
|
| 75 |
if node_class is None:
|
| 76 |
raise RuntimeError(f"Could not find node class '{class_type}'. Is it imported in comfy_integration/nodes.py?")
|
|
@@ -109,23 +112,12 @@ class SdImagePipeline(BasePipeline):
|
|
| 109 |
|
| 110 |
return get_value_at_index(computed_outputs[image_source_node_id], image_source_index)
|
| 111 |
|
| 112 |
-
def _gpu_logic(self, ui_inputs: Dict, loras_string: str,
|
| 113 |
model_display_name = ui_inputs['model_display_name']
|
| 114 |
|
| 115 |
-
progress(0.1, desc="Moving models to GPU...")
|
| 116 |
-
self.model_manager.move_models_to_gpu(required_models_for_gpu)
|
| 117 |
-
|
| 118 |
progress(0.4, desc="Executing workflow...")
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
ckpt_loader_node_id = assembler.node_map.get("ckpt_loader")
|
| 123 |
-
if not ckpt_loader_node_id:
|
| 124 |
-
raise RuntimeError("Workflow is missing the 'ckpt_loader' node required for model injection.")
|
| 125 |
-
|
| 126 |
-
initial_objects = {
|
| 127 |
-
ckpt_loader_node_id: loaded_model_tuple
|
| 128 |
-
}
|
| 129 |
|
| 130 |
decoded_images_tensor = self._execute_workflow(workflow, initial_objects=initial_objects)
|
| 131 |
|
|
@@ -163,24 +155,22 @@ class SdImagePipeline(BasePipeline):
|
|
| 163 |
|
| 164 |
lora_data = ui_inputs.get('lora_data', [])
|
| 165 |
active_loras_for_gpu, active_loras_for_meta = [], []
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
progress(0.1, desc="Loading models into RAM...")
|
| 183 |
-
self.model_manager.load_managed_models(required_models, active_loras=active_loras_for_gpu, progress=progress)
|
| 184 |
|
| 185 |
ui_inputs['denoise'] = 1.0
|
| 186 |
if task_type == 'img2img': ui_inputs['denoise'] = ui_inputs.get('img2img_denoise', 0.7)
|
|
@@ -380,6 +370,7 @@ class SdImagePipeline(BasePipeline):
|
|
| 380 |
"hires_upscaler": ui_inputs.get('hires_upscaler'), "hires_scale_by": ui_inputs.get('hires_scale_by'),
|
| 381 |
"model_name": ALL_MODEL_MAP[ui_inputs['model_display_name']][1],
|
| 382 |
"vae_name": ui_inputs.get('vae_name'),
|
|
|
|
| 383 |
"controlnet_chain": active_controlnets,
|
| 384 |
"ipadapter_chain": active_ipadapters,
|
| 385 |
"conditioning_chain": active_conditioning,
|
|
@@ -420,11 +411,47 @@ class SdImagePipeline(BasePipeline):
|
|
| 420 |
task_name=f"ImageGen ({task_type})",
|
| 421 |
ui_inputs=ui_inputs,
|
| 422 |
loras_string=loras_string,
|
| 423 |
-
required_models_for_gpu=required_models,
|
| 424 |
workflow=workflow,
|
| 425 |
assembler=assembler,
|
| 426 |
progress=progress
|
| 427 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
finally:
|
| 429 |
for temp_file in temp_files_to_clean:
|
| 430 |
if temp_file and os.path.exists(temp_file):
|
|
|
|
| 47 |
|
| 48 |
return sorted_nodes
|
| 49 |
|
|
|
|
| 50 |
def _execute_workflow(self, workflow: Dict[str, Any], initial_objects: Dict[str, Any]):
|
| 51 |
with torch.no_grad():
|
| 52 |
computed_outputs = initial_objects
|
|
|
|
| 70 |
node_info = workflow[node_id]
|
| 71 |
class_type = node_info['class_type']
|
| 72 |
|
| 73 |
+
is_loader_with_filename = 'Loader' in class_type and any(key.endswith('_name') for key in node_info['inputs'])
|
| 74 |
+
if node_id in initial_objects and is_loader_with_filename:
|
| 75 |
+
continue
|
| 76 |
+
|
| 77 |
node_class = NODE_CLASS_MAPPINGS.get(class_type)
|
| 78 |
if node_class is None:
|
| 79 |
raise RuntimeError(f"Could not find node class '{class_type}'. Is it imported in comfy_integration/nodes.py?")
|
|
|
|
| 112 |
|
| 113 |
return get_value_at_index(computed_outputs[image_source_node_id], image_source_index)
|
| 114 |
|
| 115 |
+
def _gpu_logic(self, ui_inputs: Dict, loras_string: str, workflow: Dict[str, Any], assembler: WorkflowAssembler, progress=gr.Progress(track_tqdm=True)):
|
| 116 |
model_display_name = ui_inputs['model_display_name']
|
| 117 |
|
|
|
|
|
|
|
|
|
|
| 118 |
progress(0.4, desc="Executing workflow...")
|
| 119 |
|
| 120 |
+
initial_objects = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
decoded_images_tensor = self._execute_workflow(workflow, initial_objects=initial_objects)
|
| 123 |
|
|
|
|
| 155 |
|
| 156 |
lora_data = ui_inputs.get('lora_data', [])
|
| 157 |
active_loras_for_gpu, active_loras_for_meta = [], []
|
| 158 |
+
if lora_data:
|
| 159 |
+
sources, ids, scales, files = lora_data[0::4], lora_data[1::4], lora_data[2::4], lora_data[3::4]
|
| 160 |
+
|
| 161 |
+
for i, (source, lora_id, scale, _) in enumerate(zip(sources, ids, scales, files)):
|
| 162 |
+
if scale > 0 and lora_id and lora_id.strip():
|
| 163 |
+
lora_filename = None
|
| 164 |
+
if source == "File":
|
| 165 |
+
lora_filename = sanitize_filename(lora_id)
|
| 166 |
+
elif source == "Civitai":
|
| 167 |
+
local_path, status = get_lora_path(source, lora_id, ui_inputs['civitai_api_key'], progress)
|
| 168 |
+
if local_path: lora_filename = os.path.basename(local_path)
|
| 169 |
+
else: raise gr.Error(f"Failed to prepare LoRA {lora_id}: {status}")
|
| 170 |
+
|
| 171 |
+
if lora_filename:
|
| 172 |
+
active_loras_for_gpu.append({"lora_name": lora_filename, "strength_model": scale, "strength_clip": scale})
|
| 173 |
+
active_loras_for_meta.append(f"{source} {lora_id}:{scale}")
|
|
|
|
|
|
|
| 174 |
|
| 175 |
ui_inputs['denoise'] = 1.0
|
| 176 |
if task_type == 'img2img': ui_inputs['denoise'] = ui_inputs.get('img2img_denoise', 0.7)
|
|
|
|
| 370 |
"hires_upscaler": ui_inputs.get('hires_upscaler'), "hires_scale_by": ui_inputs.get('hires_scale_by'),
|
| 371 |
"model_name": ALL_MODEL_MAP[ui_inputs['model_display_name']][1],
|
| 372 |
"vae_name": ui_inputs.get('vae_name'),
|
| 373 |
+
"lora_chain": active_loras_for_gpu,
|
| 374 |
"controlnet_chain": active_controlnets,
|
| 375 |
"ipadapter_chain": active_ipadapters,
|
| 376 |
"conditioning_chain": active_conditioning,
|
|
|
|
| 411 |
task_name=f"ImageGen ({task_type})",
|
| 412 |
ui_inputs=ui_inputs,
|
| 413 |
loras_string=loras_string,
|
|
|
|
| 414 |
workflow=workflow,
|
| 415 |
assembler=assembler,
|
| 416 |
progress=progress
|
| 417 |
)
|
| 418 |
+
|
| 419 |
+
import json
|
| 420 |
+
import glob
|
| 421 |
+
from PIL import PngImagePlugin
|
| 422 |
+
|
| 423 |
+
prompt_json = json.dumps(workflow)
|
| 424 |
+
|
| 425 |
+
out_dir = os.path.abspath(OUTPUT_DIR)
|
| 426 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 427 |
+
|
| 428 |
+
try:
|
| 429 |
+
existing_files = glob.glob(os.path.join(out_dir, "gen_*.png"))
|
| 430 |
+
existing_files.sort(key=os.path.getmtime)
|
| 431 |
+
while len(existing_files) > 50:
|
| 432 |
+
os.remove(existing_files.pop(0))
|
| 433 |
+
except Exception as e:
|
| 434 |
+
print(f"Warning: Failed to cleanup output dir: {e}")
|
| 435 |
+
|
| 436 |
+
final_results = []
|
| 437 |
+
for img in results:
|
| 438 |
+
if not isinstance(img, Image.Image):
|
| 439 |
+
final_results.append(img)
|
| 440 |
+
continue
|
| 441 |
+
|
| 442 |
+
metadata = PngImagePlugin.PngInfo()
|
| 443 |
+
params_string = img.info.get("parameters", "")
|
| 444 |
+
if params_string:
|
| 445 |
+
metadata.add_text("parameters", params_string)
|
| 446 |
+
metadata.add_text("prompt", prompt_json)
|
| 447 |
+
|
| 448 |
+
filename = f"gen_{random.randint(1000000, 9999999)}.png"
|
| 449 |
+
filepath = os.path.join(out_dir, filename)
|
| 450 |
+
img.save(filepath, "PNG", pnginfo=metadata)
|
| 451 |
+
final_results.append(filepath)
|
| 452 |
+
|
| 453 |
+
results = final_results
|
| 454 |
+
|
| 455 |
finally:
|
| 456 |
for temp_file in temp_files_to_clean:
|
| 457 |
if temp_file and os.path.exists(temp_file):
|
core/pipelines/workflow_recipes/_partials/conditioning/sdxl.yaml
CHANGED
|
@@ -32,6 +32,19 @@ ui_map:
|
|
| 32 |
positive_prompt: "pos_prompt:text"
|
| 33 |
negative_prompt: "neg_prompt:text"
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
dynamic_controlnet_chains:
|
| 36 |
controlnet_chain:
|
| 37 |
template: "ControlNetApplyAdvanced"
|
|
|
|
| 32 |
positive_prompt: "pos_prompt:text"
|
| 33 |
negative_prompt: "neg_prompt:text"
|
| 34 |
|
| 35 |
+
dynamic_lora_chains:
|
| 36 |
+
lora_chain:
|
| 37 |
+
template: "LoraLoader"
|
| 38 |
+
output_map:
|
| 39 |
+
"ckpt_loader:0": "model"
|
| 40 |
+
"ckpt_loader:1": "clip"
|
| 41 |
+
input_map:
|
| 42 |
+
"model": "model"
|
| 43 |
+
"clip": "clip"
|
| 44 |
+
end_input_map:
|
| 45 |
+
"model": ["ksampler:model"]
|
| 46 |
+
"clip": ["pos_prompt:clip", "neg_prompt:clip"]
|
| 47 |
+
|
| 48 |
dynamic_controlnet_chains:
|
| 49 |
controlnet_chain:
|
| 50 |
template: "ControlNetApplyAdvanced"
|
core/settings.py
CHANGED
|
@@ -106,12 +106,11 @@ try:
|
|
| 106 |
MAX_IPADAPTERS = _constants.get('MAX_IPADAPTERS', 5)
|
| 107 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "Custom URL", "File"])
|
| 108 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
| 109 |
-
SAMPLER_MAP = _constants.get('SAMPLER_MAP', {})
|
| 110 |
except Exception as e:
|
| 111 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 112 |
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS, MAX_IPADAPTERS = 5, 5, 10, 5, 5
|
| 113 |
LORA_SOURCE_CHOICES = ["Civitai", "Custom URL", "File"]
|
| 114 |
-
RESOLUTION_MAP
|
| 115 |
|
| 116 |
|
| 117 |
DEFAULT_NEGATIVE_PROMPT = "monochrome, (low quality, worst quality:1.2), 3d, watermark, signature, ugly, poorly drawn,"
|
|
|
|
| 106 |
MAX_IPADAPTERS = _constants.get('MAX_IPADAPTERS', 5)
|
| 107 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "Custom URL", "File"])
|
| 108 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
|
|
|
| 109 |
except Exception as e:
|
| 110 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 111 |
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS, MAX_IPADAPTERS = 5, 5, 10, 5, 5
|
| 112 |
LORA_SOURCE_CHOICES = ["Civitai", "Custom URL", "File"]
|
| 113 |
+
RESOLUTION_MAP = {}
|
| 114 |
|
| 115 |
|
| 116 |
DEFAULT_NEGATIVE_PROMPT = "monochrome, (low quality, worst quality:1.2), 3d, watermark, signature, ugly, poorly drawn,"
|
requirements.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
-
comfyui-frontend-package==1.
|
| 2 |
-
comfyui-workflow-templates==0.9.
|
| 3 |
comfyui-embedded-docs==0.4.3
|
| 4 |
torch
|
| 5 |
torchsde
|
|
|
|
| 1 |
+
comfyui-frontend-package==1.42.10
|
| 2 |
+
comfyui-workflow-templates==0.9.47
|
| 3 |
comfyui-embedded-docs==0.4.3
|
| 4 |
torch
|
| 5 |
torchsde
|
ui/events.py
CHANGED
|
@@ -42,85 +42,6 @@ def load_ipadapter_config():
|
|
| 42 |
return {}
|
| 43 |
|
| 44 |
|
| 45 |
-
def apply_data_to_ui(data, prefix, ui_components):
|
| 46 |
-
final_sampler = data.get('sampler') if data.get('sampler') in SAMPLER_CHOICES else SAMPLER_CHOICES[0]
|
| 47 |
-
default_scheduler = 'normal' if 'normal' in SCHEDULER_CHOICES else SCHEDULER_CHOICES[0]
|
| 48 |
-
final_scheduler = data.get('scheduler') if data.get('scheduler') in SCHEDULER_CHOICES else default_scheduler
|
| 49 |
-
|
| 50 |
-
updates = {}
|
| 51 |
-
base_model_name = data.get('base_model')
|
| 52 |
-
|
| 53 |
-
model_map = MODEL_MAP_CHECKPOINT
|
| 54 |
-
|
| 55 |
-
if f'base_model_{prefix}' in ui_components:
|
| 56 |
-
model_dropdown_component = ui_components[f'base_model_{prefix}']
|
| 57 |
-
if base_model_name and base_model_name in model_map:
|
| 58 |
-
updates[model_dropdown_component] = base_model_name
|
| 59 |
-
else:
|
| 60 |
-
updates[model_dropdown_component] = gr.update()
|
| 61 |
-
|
| 62 |
-
common_params = {
|
| 63 |
-
f'prompt_{prefix}': data.get('prompt', ''),
|
| 64 |
-
f'neg_prompt_{prefix}': data.get('negative_prompt', ''),
|
| 65 |
-
f'seed_{prefix}': data.get('seed', -1),
|
| 66 |
-
f'cfg_{prefix}': data.get('cfg_scale', 7.5),
|
| 67 |
-
f'steps_{prefix}': data.get('steps', 28),
|
| 68 |
-
f'sampler_{prefix}': final_sampler,
|
| 69 |
-
f'scheduler_{prefix}': final_scheduler,
|
| 70 |
-
}
|
| 71 |
-
|
| 72 |
-
for comp_name, value in common_params.items():
|
| 73 |
-
if comp_name in ui_components:
|
| 74 |
-
updates[ui_components[comp_name]] = value
|
| 75 |
-
|
| 76 |
-
if prefix == 'txt2img':
|
| 77 |
-
if f'width_{prefix}' in ui_components:
|
| 78 |
-
updates[ui_components[f'width_{prefix}']] = data.get('width', 1024)
|
| 79 |
-
if f'height_{prefix}' in ui_components:
|
| 80 |
-
updates[ui_components[f'height_{prefix}']] = data.get('height', 1024)
|
| 81 |
-
|
| 82 |
-
tab_indices = {"txt2img": 0, "img2img": 1, "inpaint": 2, "outpaint": 3, "hires_fix": 4}
|
| 83 |
-
tab_index = tab_indices.get(prefix, 0)
|
| 84 |
-
|
| 85 |
-
updates[ui_components['tabs']] = gr.Tabs(selected=0)
|
| 86 |
-
updates[ui_components['image_gen_tabs']] = gr.Tabs(selected=tab_index)
|
| 87 |
-
|
| 88 |
-
return updates
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def send_info_to_tab(image, prefix, ui_components):
|
| 92 |
-
if not image or not image.info.get('parameters', ''):
|
| 93 |
-
all_comps = [comp for comp_or_list in ui_components.values() for comp in (comp_or_list if isinstance(comp_or_list, list) else [comp_or_list])]
|
| 94 |
-
return {comp: gr.update() for comp in all_comps}
|
| 95 |
-
|
| 96 |
-
data = parse_parameters(image.info['parameters'])
|
| 97 |
-
|
| 98 |
-
image_input_map = {
|
| 99 |
-
"img2img": 'input_image_img2img',
|
| 100 |
-
"inpaint": 'input_image_dict_inpaint',
|
| 101 |
-
"outpaint": 'input_image_outpaint',
|
| 102 |
-
"hires_fix": 'input_image_hires_fix'
|
| 103 |
-
}
|
| 104 |
-
|
| 105 |
-
updates = apply_data_to_ui(data, prefix, ui_components)
|
| 106 |
-
|
| 107 |
-
if prefix in image_input_map and image_input_map[prefix] in ui_components:
|
| 108 |
-
component_key = image_input_map[prefix]
|
| 109 |
-
updates[ui_components[component_key]] = gr.update(value=image)
|
| 110 |
-
|
| 111 |
-
return updates
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
def send_info_by_hash(image, ui_components):
|
| 115 |
-
if not image or not image.info.get('parameters', ''):
|
| 116 |
-
all_comps = [comp for comp_or_list in ui_components.values() for comp in (comp_or_list if isinstance(comp_or_list, list) else [comp_or_list])]
|
| 117 |
-
return {comp: gr.update() for comp in all_comps}
|
| 118 |
-
|
| 119 |
-
data = parse_parameters(image.info['parameters'])
|
| 120 |
-
|
| 121 |
-
return apply_data_to_ui(data, "txt2img", ui_components)
|
| 122 |
-
|
| 123 |
-
|
| 124 |
def attach_event_handlers(ui_components, demo):
|
| 125 |
def update_cn_input_visibility(choice):
|
| 126 |
return {
|
|
@@ -615,21 +536,6 @@ def attach_event_handlers(ui_components, demo):
|
|
| 615 |
|
| 616 |
create_run_event(prefix, task_type)
|
| 617 |
|
| 618 |
-
|
| 619 |
-
ui_components["info_get_button"].click(
|
| 620 |
-
get_png_info,
|
| 621 |
-
[ui_components["info_image_input"]],
|
| 622 |
-
[ui_components["info_prompt_output"], ui_components["info_neg_prompt_output"], ui_components["info_params_output"]]
|
| 623 |
-
)
|
| 624 |
-
|
| 625 |
-
flat_ui_list = [comp for comp_or_list in ui_components.values() for comp in (comp_or_list if isinstance(comp_or_list, list) else [comp_or_list])]
|
| 626 |
-
|
| 627 |
-
ui_components["send_to_txt2img_button"].click(lambda img: send_info_by_hash(img, ui_components), [ui_components["info_image_input"]], flat_ui_list)
|
| 628 |
-
ui_components["send_to_img2img_button"].click(lambda img: send_info_to_tab(img, "img2img", ui_components), [ui_components["info_image_input"]], flat_ui_list)
|
| 629 |
-
ui_components["send_to_inpaint_button"].click(lambda img: send_info_to_tab(img, "inpaint", ui_components), [ui_components["info_image_input"]], flat_ui_list)
|
| 630 |
-
ui_components["send_to_outpaint_button"].click(lambda img: send_info_to_tab(img, "outpaint", ui_components), [ui_components["info_image_input"]], flat_ui_list)
|
| 631 |
-
ui_components["send_to_hires_fix_button"].click(lambda img: send_info_to_tab(img, "hires_fix", ui_components), [ui_components["info_image_input"]], flat_ui_list)
|
| 632 |
-
|
| 633 |
def on_aspect_ratio_change(ratio_key, model_display_name):
|
| 634 |
model_type = MODEL_TYPE_MAP.get(model_display_name, 'sdxl').lower()
|
| 635 |
res_map = RESOLUTION_MAP.get(model_type, RESOLUTION_MAP.get("sdxl", {}))
|
|
@@ -765,7 +671,8 @@ def attach_event_handlers(ui_components, demo):
|
|
| 765 |
ui_components["zero_gpu_cn"]
|
| 766 |
])
|
| 767 |
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
|
|
|
|
|
| 42 |
return {}
|
| 43 |
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
def attach_event_handlers(ui_components, demo):
|
| 46 |
def update_cn_input_visibility(choice):
|
| 47 |
return {
|
|
|
|
| 536 |
|
| 537 |
create_run_event(prefix, task_type)
|
| 538 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
def on_aspect_ratio_change(ratio_key, model_display_name):
|
| 540 |
model_type = MODEL_TYPE_MAP.get(model_display_name, 'sdxl').lower()
|
| 541 |
res_map = RESOLUTION_MAP.get(model_type, RESOLUTION_MAP.get("sdxl", {}))
|
|
|
|
| 671 |
ui_components["zero_gpu_cn"]
|
| 672 |
])
|
| 673 |
|
| 674 |
+
if all_load_outputs:
|
| 675 |
+
demo.load(
|
| 676 |
+
fn=run_on_load,
|
| 677 |
+
outputs=all_load_outputs
|
| 678 |
+
)
|
ui/layout.py
CHANGED
|
@@ -28,10 +28,10 @@ def build_ui(event_handler_function):
|
|
| 28 |
"[FLUX.2](https://huggingface.co/spaces/RioShiina/ImageGen-FLUX.2), "
|
| 29 |
"[Z-Image](https://huggingface.co/spaces/RioShiina/ImageGen-Z-Image), "
|
| 30 |
"[Qwen-Image](https://huggingface.co/spaces/RioShiina/ImageGen-Qwen-Image), "
|
|
|
|
| 31 |
"[Illstrious](https://huggingface.co/spaces/RioShiina/ImageGen-Illstrious), "
|
| 32 |
"[NoobAI](https://huggingface.co/spaces/RioShiina/ImageGen-NoobAI), "
|
| 33 |
-
"[SDXL](https://huggingface.co/spaces/RioShiina/ImageGen-SDXL)
|
| 34 |
-
"[SD1.5](https://huggingface.co/spaces/RioShiina/ImageGen-SD15)"
|
| 35 |
)
|
| 36 |
with gr.Tabs(elem_id="tabs_container") as tabs:
|
| 37 |
with gr.TabItem("Pony", id=0):
|
|
@@ -82,31 +82,6 @@ def build_ui(event_handler_function):
|
|
| 82 |
"cn_dropdowns": cn_dropdowns, "cn_checkboxes": cn_checkboxes
|
| 83 |
})
|
| 84 |
|
| 85 |
-
with gr.TabItem("PNG Info", id=2):
|
| 86 |
-
with gr.Column():
|
| 87 |
-
info_image_input = gr.Image(type="pil", label="Upload Image", height=512)
|
| 88 |
-
with gr.Row():
|
| 89 |
-
info_get_button = gr.Button("Get Info")
|
| 90 |
-
send_to_txt2img_button = gr.Button("Send to Txt2Img", variant="primary")
|
| 91 |
-
with gr.Row():
|
| 92 |
-
send_to_img2img_button = gr.Button("Send to Img2Img")
|
| 93 |
-
send_to_inpaint_button = gr.Button("Send to Inpaint")
|
| 94 |
-
send_to_outpaint_button = gr.Button("Send to Outpaint")
|
| 95 |
-
send_to_hires_fix_button = gr.Button("Send to Hires. Fix")
|
| 96 |
-
gr.Markdown("### Positive Prompt"); info_prompt_output = gr.Textbox(lines=3, interactive=False, show_label=False)
|
| 97 |
-
gr.Markdown("### Negative Prompt"); info_neg_prompt_output = gr.Textbox(lines=3, interactive=False, show_label=False)
|
| 98 |
-
gr.Markdown("### Other Parameters"); info_params_output = gr.Textbox(lines=5, interactive=False, show_label=False)
|
| 99 |
-
ui_components.update({
|
| 100 |
-
"info_image_input": info_image_input, "info_get_button": info_get_button,
|
| 101 |
-
"send_to_txt2img_button": send_to_txt2img_button,
|
| 102 |
-
"send_to_img2img_button": send_to_img2img_button,
|
| 103 |
-
"send_to_inpaint_button": send_to_inpaint_button,
|
| 104 |
-
"send_to_outpaint_button": send_to_outpaint_button,
|
| 105 |
-
"send_to_hires_fix_button": send_to_hires_fix_button,
|
| 106 |
-
"info_prompt_output": info_prompt_output, "info_neg_prompt_output": info_neg_prompt_output,
|
| 107 |
-
"info_params_output": info_params_output
|
| 108 |
-
})
|
| 109 |
-
|
| 110 |
ui_components["tabs"] = tabs
|
| 111 |
|
| 112 |
gr.Markdown("<div style='text-align: center; 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>")
|
|
|
|
| 28 |
"[FLUX.2](https://huggingface.co/spaces/RioShiina/ImageGen-FLUX.2), "
|
| 29 |
"[Z-Image](https://huggingface.co/spaces/RioShiina/ImageGen-Z-Image), "
|
| 30 |
"[Qwen-Image](https://huggingface.co/spaces/RioShiina/ImageGen-Qwen-Image), "
|
| 31 |
+
"[Anima](https://huggingface.co/spaces/RioShiina/ImageGen-Anima), "
|
| 32 |
"[Illstrious](https://huggingface.co/spaces/RioShiina/ImageGen-Illstrious), "
|
| 33 |
"[NoobAI](https://huggingface.co/spaces/RioShiina/ImageGen-NoobAI), "
|
| 34 |
+
"[SDXL](https://huggingface.co/spaces/RioShiina/ImageGen-SDXL)"
|
|
|
|
| 35 |
)
|
| 36 |
with gr.Tabs(elem_id="tabs_container") as tabs:
|
| 37 |
with gr.TabItem("Pony", id=0):
|
|
|
|
| 82 |
"cn_dropdowns": cn_dropdowns, "cn_checkboxes": cn_checkboxes
|
| 83 |
})
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
ui_components["tabs"] = tabs
|
| 86 |
|
| 87 |
gr.Markdown("<div style='text-align: center; 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>")
|
utils/app_utils.py
CHANGED
|
@@ -482,43 +482,6 @@ def ensure_ipadapter_models_downloaded(preset_name: str, progress):
|
|
| 482 |
print(f"❌ Error ensuring download for IPAdapter asset '{filename}': {e}")
|
| 483 |
|
| 484 |
|
| 485 |
-
def parse_parameters(params_text: str) -> dict:
|
| 486 |
-
data = {}
|
| 487 |
-
lines = params_text.strip().split('\n')
|
| 488 |
-
data['prompt'] = lines[0]
|
| 489 |
-
data['negative_prompt'] = lines[1].replace("Negative prompt:", "").strip() if len(lines) > 1 and lines[1].startswith("Negative prompt:") else ""
|
| 490 |
-
|
| 491 |
-
params_line = '\n'.join(lines[2:])
|
| 492 |
-
|
| 493 |
-
def find_param(key, default, cast_type=str):
|
| 494 |
-
match = re.search(fr"\b{key}: ([^,]+?)(,|$|\n)", params_line)
|
| 495 |
-
return cast_type(match.group(1).strip()) if match else default
|
| 496 |
-
|
| 497 |
-
data['steps'] = find_param("Steps", 28, int)
|
| 498 |
-
data['sampler'] = find_param("Sampler", "euler", str)
|
| 499 |
-
data['scheduler'] = find_param("Scheduler", "normal", str)
|
| 500 |
-
data['cfg_scale'] = find_param("CFG scale", 7.5, float)
|
| 501 |
-
data['seed'] = find_param("Seed", -1, int)
|
| 502 |
-
data['clip_skip'] = find_param("Clip skip", 1, int)
|
| 503 |
-
data['base_model'] = find_param("Base Model", list(ALL_MODEL_MAP.keys())[0] if ALL_MODEL_MAP else "", str)
|
| 504 |
-
data['model_hash'] = find_param("Model hash", None, str)
|
| 505 |
-
|
| 506 |
-
size_match = re.search(r"Size: (\d+)x(\d+)", params_line)
|
| 507 |
-
data['width'], data['height'] = (int(size_match.group(1)), int(size_match.group(2))) if size_match else (1024, 1024)
|
| 508 |
-
|
| 509 |
-
return data
|
| 510 |
-
|
| 511 |
-
def get_png_info(image) -> tuple[str, str, str]:
|
| 512 |
-
if not image or not (params := image.info.get('parameters')):
|
| 513 |
-
return "", "", "No metadata found in the image."
|
| 514 |
-
|
| 515 |
-
parsed_data = parse_parameters(params)
|
| 516 |
-
raw_params_list = '\n'.join(params.strip().split('\n')[2:]).split(',')
|
| 517 |
-
other_params_text = "\n".join([p.strip() for p in raw_params_list])
|
| 518 |
-
|
| 519 |
-
return parsed_data.get('prompt', ''), parsed_data.get('negative_prompt', ''), other_params_text
|
| 520 |
-
|
| 521 |
-
|
| 522 |
def build_preprocessor_model_map():
|
| 523 |
global PREPROCESSOR_MODEL_MAP
|
| 524 |
if PREPROCESSOR_MODEL_MAP is not None: return PREPROCESSOR_MODEL_MAP
|
|
|
|
| 482 |
print(f"❌ Error ensuring download for IPAdapter asset '{filename}': {e}")
|
| 483 |
|
| 484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
def build_preprocessor_model_map():
|
| 486 |
global PREPROCESSOR_MODEL_MAP
|
| 487 |
if PREPROCESSOR_MODEL_MAP is not None: return PREPROCESSOR_MODEL_MAP
|
yaml/constants.yaml
CHANGED
|
@@ -13,20 +13,4 @@ RESOLUTION_MAP:
|
|
| 13 |
"4:3 (Classic)": [1152, 896]
|
| 14 |
"3:4 (Classic Portrait)": [896, 1152]
|
| 15 |
"3:2 (Photography)": [1216, 832]
|
| 16 |
-
"2:3 (Photography Portrait)": [832, 1216]
|
| 17 |
-
|
| 18 |
-
SAMPLER_MAP:
|
| 19 |
-
euler a: "euler_ancestral"
|
| 20 |
-
dpm++ 2s a: "dpmpp_2s_ancestral"
|
| 21 |
-
dpm++ 2m: "dpmpp_2m"
|
| 22 |
-
dpm++ sde: "dpmpp_sde"
|
| 23 |
-
dpm++ 2m sde: "dpmpp_2m_sde"
|
| 24 |
-
dpm++ 3m sde: "dpmpp_3m_sde"
|
| 25 |
-
ddim: "ddim"
|
| 26 |
-
uni_pc: "uni_pc"
|
| 27 |
-
euler_a: "euler_ancestral"
|
| 28 |
-
dpm++ 2s a karras: "dpmpp_2s_ancestral"
|
| 29 |
-
dpm++ 2m karras: "dpmpp_2m"
|
| 30 |
-
dpm++ sde karras: "dpmpp_sde"
|
| 31 |
-
dpm++ 2m sde karras: "dpmpp_2m_sde"
|
| 32 |
-
dpm++ 3m sde karras: "dpmpp_3m_sde"
|
|
|
|
| 13 |
"4:3 (Classic)": [1152, 896]
|
| 14 |
"3:4 (Classic Portrait)": [896, 1152]
|
| 15 |
"3:2 (Photography)": [1216, 832]
|
| 16 |
+
"2:3 (Photography Portrait)": [832, 1216]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
yaml/injectors.yaml
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
injector_definitions:
|
|
|
|
|
|
|
| 2 |
dynamic_controlnet_chains:
|
| 3 |
module: "chain_injectors.controlnet_injector"
|
| 4 |
dynamic_ipadapter_chains:
|
|
@@ -7,6 +9,7 @@ injector_definitions:
|
|
| 7 |
module: "chain_injectors.conditioning_injector"
|
| 8 |
|
| 9 |
injector_order:
|
|
|
|
| 10 |
- dynamic_ipadapter_chains
|
| 11 |
- dynamic_conditioning_chains
|
| 12 |
- dynamic_controlnet_chains
|
|
|
|
| 1 |
injector_definitions:
|
| 2 |
+
dynamic_lora_chains:
|
| 3 |
+
module: "chain_injectors.lora_injector"
|
| 4 |
dynamic_controlnet_chains:
|
| 5 |
module: "chain_injectors.controlnet_injector"
|
| 6 |
dynamic_ipadapter_chains:
|
|
|
|
| 9 |
module: "chain_injectors.conditioning_injector"
|
| 10 |
|
| 11 |
injector_order:
|
| 12 |
+
- dynamic_lora_chains
|
| 13 |
- dynamic_ipadapter_chains
|
| 14 |
- dynamic_conditioning_chains
|
| 15 |
- dynamic_controlnet_chains
|