Add Reference Latent Chains
Browse files- chain_injectors/reference_latent_injector.py +66 -0
- core/pipelines/sd_image_pipeline.py +11 -0
- core/pipelines/workflow_recipes/_partials/conditioning/flux2.yaml +5 -0
- core/settings.py +2 -0
- ui/events.py +37 -3
- ui/shared/hires_fix_ui.py +3 -2
- ui/shared/img2img_ui.py +3 -2
- ui/shared/inpaint_ui.py +3 -2
- ui/shared/outpaint_ui.py +3 -2
- ui/shared/txt2img_ui.py +3 -2
- ui/shared/ui_components.py +30 -1
- yaml/constants.yaml +1 -0
- yaml/injectors.yaml +3 -0
chain_injectors/reference_latent_injector.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def inject(assembler, chain_definition, chain_items):
|
| 2 |
+
if not chain_items:
|
| 3 |
+
return
|
| 4 |
+
|
| 5 |
+
ksampler_name = chain_definition.get('ksampler_node', 'ksampler')
|
| 6 |
+
flux_guidance_name = chain_definition.get('flux_guidance_node')
|
| 7 |
+
vae_node_name = chain_definition.get('vae_node', 'vae_loader')
|
| 8 |
+
|
| 9 |
+
if ksampler_name not in assembler.node_map:
|
| 10 |
+
print(f"Warning: [ReferenceLatent] KSampler node '{ksampler_name}' not found. Skipping.")
|
| 11 |
+
return
|
| 12 |
+
if vae_node_name not in assembler.node_map:
|
| 13 |
+
print(f"Warning: [ReferenceLatent] VAE loader node '{vae_node_name}' not found. Skipping.")
|
| 14 |
+
return
|
| 15 |
+
|
| 16 |
+
ksampler_id = assembler.node_map[ksampler_name]
|
| 17 |
+
vae_node_id = assembler.node_map[vae_node_name]
|
| 18 |
+
|
| 19 |
+
pos_target_node_id = None
|
| 20 |
+
pos_target_input_name = None
|
| 21 |
+
if flux_guidance_name and flux_guidance_name in assembler.node_map:
|
| 22 |
+
flux_guidance_id = assembler.node_map[flux_guidance_name]
|
| 23 |
+
if 'conditioning' in assembler.workflow[flux_guidance_id]['inputs']:
|
| 24 |
+
pos_target_node_id = flux_guidance_id
|
| 25 |
+
pos_target_input_name = 'conditioning'
|
| 26 |
+
print(f"ReferenceLatent injector targeting FluxGuidance node '{flux_guidance_name}'.")
|
| 27 |
+
|
| 28 |
+
if not pos_target_node_id:
|
| 29 |
+
if 'positive' in assembler.workflow[ksampler_id]['inputs']:
|
| 30 |
+
pos_target_node_id = ksampler_id
|
| 31 |
+
pos_target_input_name = 'positive'
|
| 32 |
+
print(f"ReferenceLatent injector targeting KSampler node '{ksampler_name}'.")
|
| 33 |
+
else:
|
| 34 |
+
print(f"Warning: [ReferenceLatent] Could not find a valid positive injection point. Skipping.")
|
| 35 |
+
return
|
| 36 |
+
|
| 37 |
+
current_pos_conditioning = assembler.workflow[pos_target_node_id]['inputs'][pos_target_input_name]
|
| 38 |
+
|
| 39 |
+
for i, img_filename in enumerate(chain_items):
|
| 40 |
+
if not img_filename or not isinstance(img_filename, str):
|
| 41 |
+
continue
|
| 42 |
+
|
| 43 |
+
load_id = assembler._get_unique_id()
|
| 44 |
+
load_node = assembler._get_node_template("LoadImage")
|
| 45 |
+
load_node['inputs']['image'] = img_filename
|
| 46 |
+
assembler.workflow[load_id] = load_node
|
| 47 |
+
|
| 48 |
+
vae_encode_id = assembler._get_unique_id()
|
| 49 |
+
vae_encode_node = assembler._get_node_template("VAEEncode")
|
| 50 |
+
vae_encode_node['inputs']['pixels'] = [load_id, 0]
|
| 51 |
+
vae_encode_node['inputs']['vae'] = [vae_node_id, 0]
|
| 52 |
+
assembler.workflow[vae_encode_id] = vae_encode_node
|
| 53 |
+
|
| 54 |
+
latent_conn = [vae_encode_id, 0]
|
| 55 |
+
|
| 56 |
+
ref_latent_id = assembler._get_unique_id()
|
| 57 |
+
ref_latent_node = assembler._get_node_template("ReferenceLatent")
|
| 58 |
+
ref_latent_node['inputs']['conditioning'] = current_pos_conditioning
|
| 59 |
+
ref_latent_node['inputs']['latent'] = latent_conn
|
| 60 |
+
assembler.workflow[ref_latent_id] = ref_latent_node
|
| 61 |
+
|
| 62 |
+
current_pos_conditioning = [ref_latent_id, 0]
|
| 63 |
+
|
| 64 |
+
assembler.workflow[pos_target_node_id]['inputs'][pos_target_input_name] = current_pos_conditioning
|
| 65 |
+
|
| 66 |
+
print(f"ReferenceLatent injector applied. Re-routed inputs through {len(chain_items)} reference image(s).")
|
core/pipelines/sd_image_pipeline.py
CHANGED
|
@@ -312,6 +312,16 @@ class SdImagePipeline(BasePipeline):
|
|
| 312 |
"strength": float(strengths[i])
|
| 313 |
})
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
loras_string = f"LoRAs: [{', '.join(active_loras_for_meta)}]" if active_loras_for_meta else ""
|
| 316 |
|
| 317 |
progress(0.8, desc="Assembling workflow...")
|
|
@@ -346,6 +356,7 @@ class SdImagePipeline(BasePipeline):
|
|
| 346 |
"clip_name": components['clip'],
|
| 347 |
"vae_name": ui_inputs.get('vae_name', components['vae']),
|
| 348 |
"conditioning_chain": active_conditioning,
|
|
|
|
| 349 |
}
|
| 350 |
|
| 351 |
if task_type == 'txt2img':
|
|
|
|
| 312 |
"strength": float(strengths[i])
|
| 313 |
})
|
| 314 |
|
| 315 |
+
reference_latent_data = ui_inputs.get('reference_latent_data', [])
|
| 316 |
+
active_reference_latents = []
|
| 317 |
+
if reference_latent_data:
|
| 318 |
+
for img_pil in reference_latent_data:
|
| 319 |
+
if img_pil is not None:
|
| 320 |
+
temp_file_path = os.path.join(INPUT_DIR, f"temp_ref_{random.randint(1000, 9999)}.png")
|
| 321 |
+
img_pil.save(temp_file_path, "PNG")
|
| 322 |
+
active_reference_latents.append(os.path.basename(temp_file_path))
|
| 323 |
+
temp_files_to_clean.append(temp_file_path)
|
| 324 |
+
|
| 325 |
loras_string = f"LoRAs: [{', '.join(active_loras_for_meta)}]" if active_loras_for_meta else ""
|
| 326 |
|
| 327 |
progress(0.8, desc="Assembling workflow...")
|
|
|
|
| 356 |
"clip_name": components['clip'],
|
| 357 |
"vae_name": ui_inputs.get('vae_name', components['vae']),
|
| 358 |
"conditioning_chain": active_conditioning,
|
| 359 |
+
"reference_latent_chain": active_reference_latents,
|
| 360 |
}
|
| 361 |
|
| 362 |
if task_type == 'txt2img':
|
core/pipelines/workflow_recipes/_partials/conditioning/flux2.yaml
CHANGED
|
@@ -43,6 +43,11 @@ dynamic_conditioning_chains:
|
|
| 43 |
ksampler_node: "ksampler"
|
| 44 |
clip_source: "clip_loader:0"
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
ui_map:
|
| 47 |
unet_name: "unet_loader:unet_name"
|
| 48 |
clip_name: "clip_loader:clip_name"
|
|
|
|
| 43 |
ksampler_node: "ksampler"
|
| 44 |
clip_source: "clip_loader:0"
|
| 45 |
|
| 46 |
+
dynamic_reference_latent_chains:
|
| 47 |
+
reference_latent_chain:
|
| 48 |
+
ksampler_node: "ksampler"
|
| 49 |
+
vae_node: "vae_loader"
|
| 50 |
+
|
| 51 |
ui_map:
|
| 52 |
unet_name: "unet_loader:unet_name"
|
| 53 |
clip_name: "clip_loader:clip_name"
|
core/settings.py
CHANGED
|
@@ -111,11 +111,13 @@ try:
|
|
| 111 |
MAX_EMBEDDINGS = _constants.get('MAX_EMBEDDINGS', 5)
|
| 112 |
MAX_CONDITIONINGS = _constants.get('MAX_CONDITIONINGS', 10)
|
| 113 |
MAX_CONTROLNETS = _constants.get('MAX_CONTROLNETS', 5)
|
|
|
|
| 114 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "File"])
|
| 115 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
| 116 |
except Exception as e:
|
| 117 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 118 |
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS = 5, 5, 10, 5
|
|
|
|
| 119 |
LORA_SOURCE_CHOICES = ["Civitai", "File"]
|
| 120 |
RESOLUTION_MAP = {}
|
| 121 |
|
|
|
|
| 111 |
MAX_EMBEDDINGS = _constants.get('MAX_EMBEDDINGS', 5)
|
| 112 |
MAX_CONDITIONINGS = _constants.get('MAX_CONDITIONINGS', 10)
|
| 113 |
MAX_CONTROLNETS = _constants.get('MAX_CONTROLNETS', 5)
|
| 114 |
+
MAX_REFERENCE_LATENTS = _constants.get('MAX_REFERENCE_LATENTS', 10)
|
| 115 |
LORA_SOURCE_CHOICES = _constants.get('LORA_SOURCE_CHOICES', ["Civitai", "File"])
|
| 116 |
RESOLUTION_MAP = _constants.get('RESOLUTION_MAP', {})
|
| 117 |
except Exception as e:
|
| 118 |
print(f"FATAL: Could not load constants from YAML. Error: {e}")
|
| 119 |
MAX_LORAS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_CONTROLNETS = 5, 5, 10, 5
|
| 120 |
+
MAX_REFERENCE_LATENTS = 10
|
| 121 |
LORA_SOURCE_CHOICES = ["Civitai", "File"]
|
| 122 |
RESOLUTION_MAP = {}
|
| 123 |
|
ui/events.py
CHANGED
|
@@ -10,7 +10,7 @@ from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
|
| 10 |
|
| 11 |
from core.pipelines.controlnet_preprocessor import CPU_ONLY_PREPROCESSORS
|
| 12 |
from utils.app_utils import PREPROCESSOR_MODEL_MAP, PREPROCESSOR_PARAMETER_MAP, save_uploaded_file_with_hash
|
| 13 |
-
from ui.shared.ui_components import RESOLUTION_MAP, MAX_CONTROLNETS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_LORAS
|
| 14 |
|
| 15 |
|
| 16 |
def on_model_change(model_display_name):
|
|
@@ -253,6 +253,36 @@ def attach_event_handlers(ui_components, demo):
|
|
| 253 |
del_outputs = [count_state, add_button, del_button] + rows + prompts
|
| 254 |
add_button.click(fn=add_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 255 |
del_button.click(fn=del_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
del_button.click(fn=del_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
| 257 |
|
| 258 |
def on_vae_upload(file_obj):
|
|
@@ -318,7 +348,7 @@ def attach_event_handlers(ui_components, demo):
|
|
| 318 |
|
| 319 |
input_keys = list(run_inputs_map.keys())
|
| 320 |
input_list_flat = [v for v in run_inputs_map.values() if v is not None]
|
| 321 |
-
input_list_flat += lora_data_components + embedding_data_components + conditioning_data_components
|
| 322 |
|
| 323 |
def create_ui_inputs_dict(*args):
|
| 324 |
valid_keys = [k for k in input_keys if run_inputs_map[k] is not None]
|
|
@@ -331,6 +361,8 @@ def attach_event_handlers(ui_components, demo):
|
|
| 331 |
arg_idx += len(embedding_data_components)
|
| 332 |
ui_dict['conditioning_data'] = list(args[arg_idx : arg_idx + len(conditioning_data_components)])
|
| 333 |
arg_idx += len(conditioning_data_components)
|
|
|
|
|
|
|
| 334 |
|
| 335 |
return ui_dict
|
| 336 |
|
|
@@ -383,7 +415,9 @@ def attach_event_handlers(ui_components, demo):
|
|
| 383 |
outputs=[emb_ids[i], emb_sources[i], emb_files[i]],
|
| 384 |
show_progress=False
|
| 385 |
)
|
| 386 |
-
if f'add_conditioning_button_{prefix}' in ui_components: create_conditioning_event_handlers(prefix)
|
|
|
|
|
|
|
| 387 |
upload_button = ui_components.get(f'vae_upload_button_{prefix}')
|
| 388 |
if upload_button:
|
| 389 |
upload_button.upload(
|
|
|
|
| 10 |
|
| 11 |
from core.pipelines.controlnet_preprocessor import CPU_ONLY_PREPROCESSORS
|
| 12 |
from utils.app_utils import PREPROCESSOR_MODEL_MAP, PREPROCESSOR_PARAMETER_MAP, save_uploaded_file_with_hash
|
| 13 |
+
from ui.shared.ui_components import RESOLUTION_MAP, MAX_CONTROLNETS, MAX_EMBEDDINGS, MAX_CONDITIONINGS, MAX_LORAS, MAX_REFERENCE_LATENTS
|
| 14 |
|
| 15 |
|
| 16 |
def on_model_change(model_display_name):
|
|
|
|
| 253 |
del_outputs = [count_state, add_button, del_button] + rows + prompts
|
| 254 |
add_button.click(fn=add_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 255 |
del_button.click(fn=del_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
| 256 |
+
|
| 257 |
+
def create_reference_latent_event_handlers(prefix):
|
| 258 |
+
rows = ui_components[f'reference_latent_rows_{prefix}']
|
| 259 |
+
images = ui_components[f'reference_latent_images_{prefix}']
|
| 260 |
+
count_state = ui_components[f'reference_latent_count_state_{prefix}']
|
| 261 |
+
add_button = ui_components[f'add_reference_latent_button_{prefix}']
|
| 262 |
+
del_button = ui_components[f'delete_reference_latent_button_{prefix}']
|
| 263 |
+
|
| 264 |
+
def add_row(c):
|
| 265 |
+
c += 1
|
| 266 |
+
return {
|
| 267 |
+
count_state: c,
|
| 268 |
+
rows[c - 1]: gr.update(visible=True),
|
| 269 |
+
add_button: gr.update(visible=c < MAX_REFERENCE_LATENTS),
|
| 270 |
+
del_button: gr.update(visible=True),
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
def del_row(c):
|
| 274 |
+
c -= 1
|
| 275 |
+
return {
|
| 276 |
+
count_state: c,
|
| 277 |
+
rows[c]: gr.update(visible=False),
|
| 278 |
+
images[c]: None,
|
| 279 |
+
add_button: gr.update(visible=True),
|
| 280 |
+
del_button: gr.update(visible=c > 0),
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
add_outputs = [count_state, add_button, del_button] + rows
|
| 284 |
+
del_outputs = [count_state, add_button, del_button] + rows + images
|
| 285 |
+
add_button.click(fn=add_row, inputs=[count_state], outputs=add_outputs, show_progress=False)
|
| 286 |
del_button.click(fn=del_row, inputs=[count_state], outputs=del_outputs, show_progress=False)
|
| 287 |
|
| 288 |
def on_vae_upload(file_obj):
|
|
|
|
| 348 |
|
| 349 |
input_keys = list(run_inputs_map.keys())
|
| 350 |
input_list_flat = [v for v in run_inputs_map.values() if v is not None]
|
| 351 |
+
input_list_flat += lora_data_components + embedding_data_components + conditioning_data_components + reference_latent_components
|
| 352 |
|
| 353 |
def create_ui_inputs_dict(*args):
|
| 354 |
valid_keys = [k for k in input_keys if run_inputs_map[k] is not None]
|
|
|
|
| 361 |
arg_idx += len(embedding_data_components)
|
| 362 |
ui_dict['conditioning_data'] = list(args[arg_idx : arg_idx + len(conditioning_data_components)])
|
| 363 |
arg_idx += len(conditioning_data_components)
|
| 364 |
+
ui_dict['reference_latent_data'] = list(args[arg_idx : arg_idx + len(reference_latent_components)])
|
| 365 |
+
|
| 366 |
|
| 367 |
return ui_dict
|
| 368 |
|
|
|
|
| 415 |
outputs=[emb_ids[i], emb_sources[i], emb_files[i]],
|
| 416 |
show_progress=False
|
| 417 |
)
|
| 418 |
+
if f'add_conditioning_button_{prefix}' in ui_components: create_conditioning_event_handlers(prefix)
|
| 419 |
+
if f'add_reference_latent_button_{prefix}' in ui_components: create_reference_latent_event_handlers(prefix)
|
| 420 |
+
if f'vae_source_{prefix}' in ui_components:
|
| 421 |
upload_button = ui_components.get(f'vae_upload_button_{prefix}')
|
| 422 |
if upload_button:
|
| 423 |
upload_button.upload(
|
ui/shared/hires_fix_ui.py
CHANGED
|
@@ -4,7 +4,8 @@ from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
-
create_conditioning_ui, create_vae_override_ui, create_api_key_ui
|
|
|
|
| 8 |
)
|
| 9 |
|
| 10 |
def create_ui():
|
|
@@ -68,7 +69,7 @@ def create_ui():
|
|
| 68 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 69 |
# components.update(create_controlnet_ui(prefix))
|
| 70 |
# components.update(create_embedding_ui(prefix))
|
| 71 |
-
components.update((prefix))
|
| 72 |
components.update(create_conditioning_ui(prefix))
|
| 73 |
# components.update(create_vae_override_ui(prefix))
|
| 74 |
|
|
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
+
create_conditioning_ui, create_vae_override_ui, create_api_key_ui,
|
| 8 |
+
create_reference_latent_ui
|
| 9 |
)
|
| 10 |
|
| 11 |
def create_ui():
|
|
|
|
| 69 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 70 |
# components.update(create_controlnet_ui(prefix))
|
| 71 |
# components.update(create_embedding_ui(prefix))
|
| 72 |
+
components.update(create_reference_latent_ui(prefix))
|
| 73 |
components.update(create_conditioning_ui(prefix))
|
| 74 |
# components.update(create_vae_override_ui(prefix))
|
| 75 |
|
ui/shared/img2img_ui.py
CHANGED
|
@@ -4,7 +4,8 @@ from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
-
create_conditioning_ui, create_vae_override_ui, create_api_key_ui
|
|
|
|
| 8 |
)
|
| 9 |
|
| 10 |
def create_ui():
|
|
@@ -51,7 +52,7 @@ def create_ui():
|
|
| 51 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 52 |
# components.update(create_controlnet_ui(prefix))
|
| 53 |
# components.update(create_embedding_ui(prefix))
|
| 54 |
-
components.update((prefix))
|
| 55 |
components.update(create_conditioning_ui(prefix))
|
| 56 |
# components.update(create_vae_override_ui(prefix))
|
| 57 |
|
|
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
+
create_conditioning_ui, create_vae_override_ui, create_api_key_ui,
|
| 8 |
+
create_reference_latent_ui
|
| 9 |
)
|
| 10 |
|
| 11 |
def create_ui():
|
|
|
|
| 52 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 53 |
# components.update(create_controlnet_ui(prefix))
|
| 54 |
# components.update(create_embedding_ui(prefix))
|
| 55 |
+
components.update(create_reference_latent_ui(prefix))
|
| 56 |
components.update(create_conditioning_ui(prefix))
|
| 57 |
# components.update(create_vae_override_ui(prefix))
|
| 58 |
|
ui/shared/inpaint_ui.py
CHANGED
|
@@ -3,7 +3,8 @@ from core.settings import MODEL_MAP_CHECKPOINT
|
|
| 3 |
from .ui_components import (
|
| 4 |
create_base_parameter_ui, create_lora_settings_ui,
|
| 5 |
create_embedding_ui,
|
| 6 |
-
create_conditioning_ui, create_vae_override_ui, create_api_key_ui
|
|
|
|
| 7 |
)
|
| 8 |
|
| 9 |
def create_ui():
|
|
@@ -74,7 +75,7 @@ def create_ui():
|
|
| 74 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 75 |
# components.update(create_controlnet_ui(prefix))
|
| 76 |
# components.update(create_embedding_ui(prefix))
|
| 77 |
-
components.update((prefix))
|
| 78 |
components.update(create_conditioning_ui(prefix))
|
| 79 |
# components.update(create_vae_override_ui(prefix))
|
| 80 |
components[f'accordion_wrapper_{prefix}'] = accordion_wrapper
|
|
|
|
| 3 |
from .ui_components import (
|
| 4 |
create_base_parameter_ui, create_lora_settings_ui,
|
| 5 |
create_embedding_ui,
|
| 6 |
+
create_conditioning_ui, create_vae_override_ui, create_api_key_ui,
|
| 7 |
+
create_reference_latent_ui
|
| 8 |
)
|
| 9 |
|
| 10 |
def create_ui():
|
|
|
|
| 75 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 76 |
# components.update(create_controlnet_ui(prefix))
|
| 77 |
# components.update(create_embedding_ui(prefix))
|
| 78 |
+
components.update(create_reference_latent_ui(prefix))
|
| 79 |
components.update(create_conditioning_ui(prefix))
|
| 80 |
# components.update(create_vae_override_ui(prefix))
|
| 81 |
components[f'accordion_wrapper_{prefix}'] = accordion_wrapper
|
ui/shared/outpaint_ui.py
CHANGED
|
@@ -4,7 +4,8 @@ from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
-
create_conditioning_ui, create_vae_override_ui, create_api_key_ui
|
|
|
|
| 8 |
)
|
| 9 |
|
| 10 |
def create_ui():
|
|
@@ -62,7 +63,7 @@ def create_ui():
|
|
| 62 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 63 |
# components.update(create_controlnet_ui(prefix))
|
| 64 |
# components.update(create_embedding_ui(prefix))
|
| 65 |
-
components.update((prefix))
|
| 66 |
components.update(create_conditioning_ui(prefix))
|
| 67 |
# components.update(create_vae_override_ui(prefix))
|
| 68 |
|
|
|
|
| 4 |
from .ui_components import (
|
| 5 |
create_lora_settings_ui,
|
| 6 |
create_embedding_ui,
|
| 7 |
+
create_conditioning_ui, create_vae_override_ui, create_api_key_ui,
|
| 8 |
+
create_reference_latent_ui
|
| 9 |
)
|
| 10 |
|
| 11 |
def create_ui():
|
|
|
|
| 63 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 64 |
# components.update(create_controlnet_ui(prefix))
|
| 65 |
# components.update(create_embedding_ui(prefix))
|
| 66 |
+
components.update(create_reference_latent_ui(prefix))
|
| 67 |
components.update(create_conditioning_ui(prefix))
|
| 68 |
# components.update(create_vae_override_ui(prefix))
|
| 69 |
|
ui/shared/txt2img_ui.py
CHANGED
|
@@ -3,7 +3,8 @@ from core.settings import MODEL_MAP_CHECKPOINT
|
|
| 3 |
from .ui_components import (
|
| 4 |
create_base_parameter_ui, create_lora_settings_ui,
|
| 5 |
create_embedding_ui,
|
| 6 |
-
create_conditioning_ui, create_vae_override_ui, create_api_key_ui
|
|
|
|
| 7 |
)
|
| 8 |
|
| 9 |
def create_ui():
|
|
@@ -32,7 +33,7 @@ def create_ui():
|
|
| 32 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 33 |
# components.update(create_controlnet_ui(prefix))
|
| 34 |
# components.update(create_embedding_ui(prefix))
|
| 35 |
-
components.update((prefix))
|
| 36 |
components.update(create_conditioning_ui(prefix))
|
| 37 |
# components.update(create_vae_override_ui(prefix))
|
| 38 |
|
|
|
|
| 3 |
from .ui_components import (
|
| 4 |
create_base_parameter_ui, create_lora_settings_ui,
|
| 5 |
create_embedding_ui,
|
| 6 |
+
create_conditioning_ui, create_vae_override_ui, create_api_key_ui,
|
| 7 |
+
create_reference_latent_ui
|
| 8 |
)
|
| 9 |
|
| 10 |
def create_ui():
|
|
|
|
| 33 |
# components.update(create_diffsynth_controlnet_ui(prefix))
|
| 34 |
# components.update(create_controlnet_ui(prefix))
|
| 35 |
# components.update(create_embedding_ui(prefix))
|
| 36 |
+
components.update(create_reference_latent_ui(prefix))
|
| 37 |
components.update(create_conditioning_ui(prefix))
|
| 38 |
# components.update(create_vae_override_ui(prefix))
|
| 39 |
|
ui/shared/ui_components.py
CHANGED
|
@@ -2,7 +2,7 @@ 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, RESOLUTION_MAP
|
| 6 |
)
|
| 7 |
import yaml
|
| 8 |
import os
|
|
@@ -172,6 +172,35 @@ def create_conditioning_ui(prefix: str):
|
|
| 172 |
components[key('all_conditioning_components_flat')] = all_cond_components_flat
|
| 173 |
|
| 174 |
return components
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
def create_vae_override_ui(prefix: str):
|
| 177 |
components = {}
|
|
|
|
| 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, RESOLUTION_MAP, MAX_REFERENCE_LATENTS
|
| 6 |
)
|
| 7 |
import yaml
|
| 8 |
import os
|
|
|
|
| 172 |
components[key('all_conditioning_components_flat')] = all_cond_components_flat
|
| 173 |
|
| 174 |
return components
|
| 175 |
+
|
| 176 |
+
def create_reference_latent_ui(prefix: str):
|
| 177 |
+
components = {}
|
| 178 |
+
key = lambda name: f"{name}_{prefix}"
|
| 179 |
+
|
| 180 |
+
with gr.Accordion("Reference Edit", open=False) as accordion:
|
| 181 |
+
components[key('reference_latent_accordion')] = accordion
|
| 182 |
+
gr.Markdown("💡 **Tip:** For multimodal models (like FLUX.2), 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**.")
|
| 183 |
+
|
| 184 |
+
ref_rows, ref_images = [], []
|
| 185 |
+
components.update({
|
| 186 |
+
key('reference_latent_rows'): ref_rows,
|
| 187 |
+
key('reference_latent_images'): ref_images,
|
| 188 |
+
})
|
| 189 |
+
|
| 190 |
+
with gr.Row():
|
| 191 |
+
for i in range(MAX_REFERENCE_LATENTS):
|
| 192 |
+
with gr.Column(visible=(i < 1), min_width=160) as row_wrapper:
|
| 193 |
+
ref_images.append(gr.Image(type="pil", label=f"Reference {i+1}", sources=["upload"], height=150))
|
| 194 |
+
ref_rows.append(row_wrapper)
|
| 195 |
+
|
| 196 |
+
with gr.Row():
|
| 197 |
+
components[key('add_reference_latent_button')] = gr.Button("✚ Add Reference Image")
|
| 198 |
+
components[key('delete_reference_latent_button')] = gr.Button("➖ Delete Reference Image", visible=False)
|
| 199 |
+
components[key('reference_latent_count_state')] = gr.State(1)
|
| 200 |
+
|
| 201 |
+
components[key('all_reference_latent_components_flat')] = ref_images
|
| 202 |
+
|
| 203 |
+
return components
|
| 204 |
|
| 205 |
def create_vae_override_ui(prefix: str):
|
| 206 |
components = {}
|
yaml/constants.yaml
CHANGED
|
@@ -2,6 +2,7 @@ MAX_LORAS: 5
|
|
| 2 |
MAX_CONTROLNETS: 5
|
| 3 |
MAX_EMBEDDINGS: 5
|
| 4 |
MAX_CONDITIONINGS: 10
|
|
|
|
| 5 |
LORA_SOURCE_CHOICES: ["Civitai", "File"]
|
| 6 |
|
| 7 |
RESOLUTION_MAP:
|
|
|
|
| 2 |
MAX_CONTROLNETS: 5
|
| 3 |
MAX_EMBEDDINGS: 5
|
| 4 |
MAX_CONDITIONINGS: 10
|
| 5 |
+
MAX_REFERENCE_LATENTS: 10
|
| 6 |
LORA_SOURCE_CHOICES: ["Civitai", "File"]
|
| 7 |
|
| 8 |
RESOLUTION_MAP:
|
yaml/injectors.yaml
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
injector_definitions:
|
| 2 |
dynamic_conditioning_chains:
|
| 3 |
module: "chain_injectors.conditioning_injector"
|
|
|
|
|
|
|
| 4 |
|
| 5 |
injector_order:
|
|
|
|
| 6 |
- dynamic_conditioning_chains
|
|
|
|
| 1 |
injector_definitions:
|
| 2 |
dynamic_conditioning_chains:
|
| 3 |
module: "chain_injectors.conditioning_injector"
|
| 4 |
+
dynamic_reference_latent_chains:
|
| 5 |
+
module: "chain_injectors.reference_latent_injector"
|
| 6 |
|
| 7 |
injector_order:
|
| 8 |
+
- dynamic_reference_latent_chains
|
| 9 |
- dynamic_conditioning_chains
|