RioShiina commited on
Commit
9ded778
·
verified ·
1 Parent(s): 2565245

Upload folder using huggingface_hub

Browse files
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 Dict, List, Any, Set
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, required_models_for_gpu: List[str], workflow: Dict[str, Any], assembler: WorkflowAssembler, progress=gr.Progress(track_tqdm=True)):
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
- loaded_model_tuple = self.model_manager.loaded_models[model_display_name]
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
- sources, ids, scales, files = lora_data[0::4], lora_data[1::4], lora_data[2::4], lora_data[3::4]
167
-
168
- for i, (source, lora_id, scale, _) in enumerate(zip(sources, ids, scales, files)):
169
- if scale > 0 and lora_id and lora_id.strip():
170
- lora_filename = None
171
- if source == "File":
172
- lora_filename = sanitize_filename(lora_id)
173
- elif source == "Civitai":
174
- local_path, status = get_lora_path(source, lora_id, ui_inputs['civitai_api_key'], progress)
175
- if local_path: lora_filename = os.path.basename(local_path)
176
- else: raise gr.Error(f"Failed to prepare LoRA {lora_id}: {status}")
177
-
178
- if lora_filename:
179
- active_loras_for_gpu.append({"lora_name": lora_filename, "strength_model": scale, "strength_clip": scale})
180
- active_loras_for_meta.append(f"{source} {lora_id}:{scale}")
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, SAMPLER_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.41.20
2
- comfyui-workflow-templates==0.9.21
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
- demo.load(
769
- fn=run_on_load,
770
- outputs=all_load_outputs
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