V 2.0.0 - Auto config #22 - new params

This commit is contained in:
DESKTOP-TVBJISQ\Primere
2026-03-15 16:17:03 +01:00
parent 866b16c018
commit 5d7337f16c
12 changed files with 640 additions and 440 deletions
+135 -102
View File
@@ -647,11 +647,14 @@ class PrimereModelConceptSelector:
zimage_model, zimage_clip, zimage_vae
)
class PrimereAutoSamplerSettings:
class PrimereModelControl:
def __init__(self):
pass
CATEGORY = TREE_DASHBOARD
RETURN_TYPES = ("TUPLE", comfy.samplers.KSampler.SAMPLERS, comfy.samplers.KSampler.SCHEDULERS, "INT", "FLOAT", "STRING")
RETURN_NAMES = ("DATA", "SAMPLER_NAME", "SCHEDULER_NAME", "STEPS", "CFG", "MODEL_CONCEPT")
FUNCTION = "get_controlledsampler"
RETURN_NAMES = ("CONTROL_DATA", "SAMPLER_NAME", "SCHEDULER_NAME", "STEPS", "CFG", "MODEL_CONCEPT")
FUNCTION = "get_primeremodelcontrol"
OUTPUT_NODE = True
kolors_schedulers = ["EulerDiscreteScheduler", "EulerAncestralDiscreteScheduler", "DPMSolverMultistepScheduler", "DPMSolverMultistepScheduler_SDE_karras", "UniPCMultistepScheduler", "DEISMultistepScheduler"]
@@ -686,57 +689,74 @@ class PrimereAutoSamplerSettings:
"required": {
"model_concept": ("STRING", {"default": None, "forceInput": True}),
"model_name": ("CHECKPOINT_NAME", {"default": None, "forceInput": True}),
"concepts": (["Auto"] + cls.CONCEPT_LIST,),
"models": (["Auto"] + cls.MODELLIST,),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler_name": (cls.sana_schedulers + cls.kolors_schedulers + comfy.samplers.KSampler.SCHEDULERS,),
"steps": ("INT", {"default": 12, "min": 1, "max": 1000, "step": 1}),
"override_steps": ("BOOLEAN", {"default": False, "label_off": "Set by sampler settings", "label_on": "Set by model filename"}),
"cfg": ("FLOAT", {"default": 7, "min": 0.1, "max": 100, "step": 0.01}),
"rescale_cfg": ("FLOAT", {"default": 1, "min": 0.0, "max": 1.0, "step": 0.01}),
"align_your_steps": ("BOOLEAN", {"default": False, "label_on": "Use AlignYourSteps", "label_off": "Ignore AlignYourSteps"}),
"model_sampling": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 10.0, "step": 0.01}),
"last_layer": ("INT", {"default": 0, "min": -24, "max": 0, "step": 1}),
"sigma_max": ("FLOAT", {"default": 120, "min": 1, "max": 200, "step": 0.001}),
"sigma_min": ("FLOAT", {"default": 1, "min": 0.001, "max": 100, "step": 0.001}),
"vae": (cls.VAELIST,),
"vae_selection": ("BOOLEAN", {"default": True, "label_on": "Use baked if exist", "label_off": "Always use custom"}),
"clip_selection": ("BOOLEAN", {"default": True, "label_on": "Use baked if exist", "label_off": "Always use custom"}),
"last_layer": ("INT", {"default": 0, "min": -24, "max": 0, "step": 1}),
"encoder_1": (list(dict.fromkeys(["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.UNETLIST + cls.TEXT_ENCODERS_PATHS)),),
"encoder_2": (list(dict.fromkeys(["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.UNETLIST + cls.TEXT_ENCODERS_PATHS)),),
"encoder_3": (list(dict.fromkeys(["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.UNETLIST + cls.TEXT_ENCODERS_PATHS)),),
"clip_attn": (["Custom"] + list(clipping.CLIP_ATTN_PRESETS.keys()), {"default": "Natural"}),
"clip_attn_mult_query": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"clip_attn_mult_key": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"clip_attn_mult_value": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"clip_attn_mult_output": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_preset": (["Custom", "Auto"] + list(clipping.ATTN_PRESETS.keys()), {"default": "Off"}),
"attn_query": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_key": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_value": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_output": ('FLOAT', {"default": 1.00, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_cross_query": ("FLOAT", {"default": 1.0, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_cross_key": ("FLOAT", {"default": 1.0, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_cross_value": ("FLOAT", {"default": 1.0, "min": 0.80, "max": 1.20, "step": 0.01}),
"attn_cross_output": ("FLOAT", {"default": 1.0, "min": 0.80, "max": 1.20, "step": 0.01}),
"sampler": (["custom_advanced", "ksampler"], {"default": "ksampler"}),
"align_your_steps": ("BOOLEAN", {"default": False, "label_on": "Use AlignYourSteps", "label_off": "Ignore AlignYourSteps"}),
"model_sampling": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 10.0, "step": 0.01}),
"edm_sampling": (["edm_playground_v2.5", "v_prediction", "edm", "eps", "cosmos_rflow"], {"default": "edm_playground_v2.5"}),
"discrete_sampling": (["default", "eps", "v_prediction", "x0"], {"default": "default"}),
"discrete_zsnr": ("BOOLEAN", {"default": False, "label_on": "Zero SNR", "label_off": "No Zero SNR"}),
"sigma_max": ("FLOAT", {"default": 120, "min": 1, "max": 200, "step": 0.001}),
"sigma_min": ("FLOAT", {"default": 1, "min": 0.001, "max": 100, "step": 0.001}),
"flux_max_shift": ("FLOAT", {"default": 1.15, "min": 0.0, "max": 100.0, "step": 0.01}),
"flux_base_shift": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 100.0, "step": 0.01}),
"beta_alpha": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 50.0, "step": 0.01}),
"beta_beta": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 50.0, "step": 0.01}),
"guidance": ('FLOAT', {"default": 3.5, "min": 0.0, "max": 100.0, "step": 0.1}),
"weight_dtype": (["None"] + ["Auto", "default", "fp16", "bf16", "fp32", "fp8_e4m3fn", "fp8_e5m2"], {"default": "default"}),
"precision": (["None"] + ['fp32', 'fp16', 'quant8', 'quant4'], {"default": "fp16"}),
"lcm_lora": ("BOOLEAN", {"default": False, "label_on": "LCM lora ON", "label_off": "LCM lora OFF"}),
# "lcm_lora_name": (cls.LCM_LORAS,),
"lcm_lora_strength": ("FLOAT", {"default": 1.000, "min": -20.000, "max": 20.000, "step": 0.001}),
"speed_lora": ("BOOLEAN", {"default": False, "label_on": "Speed lora ON", "label_off": "Speed lora OFF"}),
"speed_lora_name": (cls.SPEED_LORAS,),
# "speed_lora_version": ([1.0, 1.1, 2.0], {"default": 2.0}),
# "speed_lora_precision": ("BOOLEAN", {"default": True, "label_on": "FP32", "label_off": "BF16"}),
# "speed_lora_step": ([4, 6, 8, 10, 12, 16], {"default": 8}),
"speed_lora_strength": ("FLOAT", {"default": 1.00, "min": -20.00, "max": 20.00, "step": 0.01}),
"speed_lora_cfg": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 100, "step": 0.01}),
"speed_lora_steps_offset": ("INT", {"default": 0, "min": -5, "max": 5, "step": 1}),
"srpo_lora": ("BOOLEAN", {"default": False, "label_on": "Use SRPO Lora", "label_off": "Ignore SRPO Lora"}),
"srpo_lora_name": (cls.SRPO_LORAS,),
# "srpo_lora_type": (["R&Q", "RockerBOO", "oficial", "adaptive"], {"default": "oficial"}),
# "srpo_lora_rank": ([8, 16, 32, 64, 128, 256], {"default": 8}),
"srpo_lora_strength": ("FLOAT", {"default": 1, "min": -20.000, "max": 20.000, "step": 0.001}),
"srpo_svdq_lora": ("BOOLEAN", {"default": False, "label_on": "Use SRPO SVDQ Lora", "label_off": "Ignore SRPO SVDQ Lora"}),
"srpo_svdq_lora_name": (cls.SRPO_SVDQ_LORAS,),
"srpo_svdq_lora_strength": ("FLOAT", {"default": 1, "min": -20.000, "max": 20.000, "step": 0.001}),
"nunchaku_lora": ("BOOLEAN", {"default": False, "label_on": "Use nunchaku Lora", "label_off": "Ignore nunchaku Lora"}),
"nunchaku_lora_name": (cls.NUNCHAKU_LORAS,),
# "nunchaku_lora_type": (["kontext_deblur", "kontext_face_detailer", "anything_extracted"], {"default": "anything_extracted"}),
# "nunchaku_lora_rank": ([64, 256], {"default": 64}),
"nunchaku_lora_strength": ("FLOAT", {"default": 1, "min": -20.000, "max": 20.000, "step": 0.001}),
"refiner": ("BOOLEAN", {"default": False, "label_on": "Refiner ON", "label_off": "Refiner OFF"}),
"refiner_model": (cls.REFINER_MODELS,),
"refiner_sampler": (comfy.samplers.KSampler.SAMPLERS, {"default": "dpmpp_2m"}),
@@ -746,11 +766,11 @@ class PrimereAutoSamplerSettings:
"refiner_start": ("INT", {"default": 12, "min": 1, "max": 1000, "step": 1}),
"refiner_denoise": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 1.0, "step": 0.01}),
"refiner_sampling_denoise": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 1.0, "step": 0.01}),
"refiner_ignore_prompt": ("BOOLEAN", {"default": True, "label_on": "Ignore prompt", "label_off": "Send prompt to refiner"}),
"refiner_ignore_prompt": ("BOOLEAN", {"default": True, "label_on": "Ignore prompt", "label_off": "Send prompt to refiner"})
}
}
def get_controlledsampler(self, **kwargs):
def get_primeremodelcontrol(self, **kwargs):
model_concept = kwargs.pop('model_concept', 'SD1')
model_name = kwargs.pop('model_name', None)
concepts = kwargs.pop('concepts', 'Auto')
@@ -802,18 +822,27 @@ class PrimereAutoSamplerSettings:
speed_lora_cfg_val = kwargs.get('speed_lora_cfg')
if speed_lora_cfg_val is not None:
cfg = float(speed_lora_cfg_val)
clip_attn = kwargs.pop('clip_attn', 'Natural')
clip_attn_mult_query = kwargs.pop('clip_attn_mult_query', 1.0)
clip_attn_mult_key = kwargs.pop('clip_attn_mult_key', 1.0)
clip_attn_mult_value = kwargs.pop('clip_attn_mult_value', 1.0)
clip_attn_mult_output = kwargs.pop('clip_attn_mult_output', 1.0)
if clip_attn == 'Custom':
attn_q, attn_k, attn_v, attn_out = clip_attn_mult_query, clip_attn_mult_key, clip_attn_mult_value, clip_attn_mult_output
attn_preset = kwargs.pop('attn_preset', 'Off')
attn_query = kwargs.pop('attn_query', 1.0)
attn_key = kwargs.pop('attn_key', 1.0)
attn_value = kwargs.pop('attn_value', 1.0)
attn_output = kwargs.pop('attn_output', 1.0)
attn_cross_query = kwargs.pop('attn_cross_query', 1.0)
attn_cross_key = kwargs.pop('attn_cross_key', 1.0)
attn_cross_value = kwargs.pop('attn_cross_value', 1.0)
attn_cross_output = kwargs.pop('attn_cross_output', 1.0)
if attn_preset == 'Auto':
attn_preset = clipping.detect_attn_preset(model_name)
attn_q, attn_k, attn_v, attn_out, cross_q, cross_k, cross_v, cross_out = clipping.ATTN_PRESETS.get(attn_preset, (1.0,)*8)
elif attn_preset == 'Custom':
attn_q, attn_k, attn_v, attn_out = attn_query, attn_key, attn_value, attn_output
cross_q, cross_k, cross_v, cross_out = attn_cross_query, attn_cross_key, attn_cross_value, attn_cross_output
else:
attn_q, attn_k, attn_v, attn_out = clipping.CLIP_ATTN_PRESETS.get(clip_attn, (1.0, 1.0, 1.0, 1.0))
attn_q, attn_k, attn_v, attn_out, cross_q, cross_k, cross_v, cross_out = clipping.ATTN_PRESETS.get(attn_preset, (1.0,)*8)
suppressed = [k + "_" for k, v in kwargs.items() if v == "None" or v is False]
kwargs = {k: v for k, v in kwargs.items() if v != "None" and not any(k.startswith(p) for p in suppressed)}
kwargs['encoders'] = [kwargs[k] for k in ('encoder_1', 'encoder_2', 'encoder_3') if kwargs.get(k) not in (None, 'None')]
kwargs['model_name'] = model_name
kwargs['model_concept'] = active_concept
kwargs['sampler_name'] = sampler_name
kwargs['scheduler_name'] = scheduler_name
@@ -823,6 +852,10 @@ class PrimereAutoSamplerSettings:
kwargs['clip_attn_k'] = attn_k
kwargs['clip_attn_v'] = attn_v
kwargs['clip_attn_out'] = attn_out
kwargs['attn_cross_q'] = cross_q
kwargs['attn_cross_k'] = cross_k
kwargs['attn_cross_v'] = cross_v
kwargs['attn_cross_out'] = cross_out
return {"ui": {"active_concept": [active_display]}, "result": (kwargs, sampler_name, scheduler_name, steps, round(cfg, 2), active_concept,)}
class PrimereConceptDataTuple:
@@ -864,15 +897,15 @@ class PrimereCKPTLoader:
},
"optional": {
# "model_concept": ("STRING", {"forceInput": True}),
"concept_data": ("TUPLE", {"default": None, "forceInput": True}),
"control_data": ("TUPLE", {"default": None, "forceInput": True}),
"loaded_model": ('MODEL', {"forceInput": True, "default": None}),
"loaded_clip": ('CLIP', {"forceInput": True, "default": None}),
"loaded_vae": ('VAE', {"forceInput": True, "default": None}),
},
}
def load_primere_ckpt(self, ckpt_name, use_yaml, concept_data=None, loaded_model=None, loaded_clip=None, loaded_vae=None):
model_concept = concept_data['model_concept']
def load_primere_ckpt(self, ckpt_name, use_yaml, control_data=None, loaded_model=None, loaded_clip=None, loaded_vae=None):
model_concept = control_data['model_concept']
try:
comfy.model_management.soft_empty_cache()
@@ -901,35 +934,35 @@ class PrimereCKPTLoader:
match model_concept:
case 'SD1' | 'SD2' | 'SDXL' | 'Illustrious' | 'Turbo' | 'Pony':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sd_model(self, ckpt_name, use_yaml, ModelConfigFullPath, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sd_model(self, ckpt_name, use_yaml, ModelConfigFullPath, control_data)
case 'SD3':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sd3_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sd3_model(self, ckpt_name, control_data)
case 'StableCascade':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_stable_cascade_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_stable_cascade_model(self, ckpt_name, control_data)
case 'Z-Image':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_zimage_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_zimage_model(self, ckpt_name, control_data)
case 'Flux':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_flux_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_flux_model(self, ckpt_name, control_data)
case 'LCM':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_lcm_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_lcm_model(self, ckpt_name, control_data)
case 'Hyper' | 'Lightning':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_lightning_hyper_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_lightning_hyper_model(self, ckpt_name, control_data)
case 'Playground':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_playground_model(self, ckpt_name, use_yaml, ModelConfigFullPath, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_playground_model(self, ckpt_name, use_yaml, ModelConfigFullPath, control_data)
case 'PixartSigma':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_pixart_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_pixart_model(self, ckpt_name, control_data)
case 'AuraFlow':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_auraflow_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_auraflow_model(self, ckpt_name, control_data)
case 'SANA1024' | 'SANA512':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sana_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_sana_model(self, ckpt_name, control_data)
case 'KwaiKolors':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_kolors_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_kolors_model(self, ckpt_name, control_data)
case 'Hunyuan':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_hunyuan_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_hunyuan_model(self, ckpt_name, control_data)
case 'QwenGen' | 'QwenEdit':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_qwen_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_qwen_model(self, ckpt_name, control_data)
case 'Chroma':
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_chroma_model(self, ckpt_name, concept_data)
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = model_loaders.load_chroma_model(self, ckpt_name, control_data)
return (OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE, MODEL_VERSION_ORIGINAL)
@@ -1036,7 +1069,7 @@ class PrimereFastSeed:
class PrimereFractalLatent:
RETURN_TYPES = ("LATENT", "IMAGE", "TUPLE")
RETURN_NAMES = ("LATENTS", "PREVIEWS", "WORKFLOW_TUPLE")
RETURN_NAMES = ("LATENTS", "PREVIEWS", "CONTROL_DATA")
FUNCTION = "primere_latent_noise"
CATEGORY = TREE_DASHBOARD
@@ -1062,7 +1095,7 @@ class PrimereFractalLatent:
},
"optional": {
"optional_vae": ("VAE",),
"workflow_tuple": ("TUPLE", {"default": None}),
"control_data": ("TUPLE", {"default": None}),
}
}
@@ -1075,19 +1108,19 @@ class PrimereFractalLatent:
if kwargs['expand_random_limits'] == True or kwargs['rand_noise_type'] == True or kwargs['rand_device'] == True or kwargs['rand_alpha_exponent'] == True or kwargs['rand_modulator'] == True:
return float('NaN')
def primere_latent_noise(self, width, height, rand_noise_type, noise_type, rand_alpha_exponent, alpha_exponent, alpha_exp_rand_min, alpha_exp_rand_max, rand_modulator, modulator, modulator_rand_min, modulator_rand_max, noise_seed, rand_device, device, optional_vae=None, workflow_tuple=None, expand_random_limits=False):
if workflow_tuple is not None and len(workflow_tuple) > 0 and 'exif_status' in workflow_tuple and workflow_tuple['exif_status'] == 'SUCCEED':
if 'latent_data' in workflow_tuple and len(workflow_tuple['latent_data']) > 0 and 'setup_states' in workflow_tuple and 'latent_setup' in workflow_tuple['setup_states']:
if workflow_tuple['setup_states']['latent_setup'] == True:
def primere_latent_noise(self, width, height, rand_noise_type, noise_type, rand_alpha_exponent, alpha_exponent, alpha_exp_rand_min, alpha_exp_rand_max, rand_modulator, modulator, modulator_rand_min, modulator_rand_max, noise_seed, rand_device, device, optional_vae=None, control_data=None, expand_random_limits=False):
if control_data is not None and len(control_data) > 0 and 'exif_status' in control_data and control_data['exif_status'] == 'SUCCEED':
if 'latent_data' in control_data and len(control_data['latent_data']) > 0 and 'setup_states' in control_data and 'latent_setup' in control_data['setup_states']:
if control_data['setup_states']['latent_setup'] == True:
expand_random_limits = False
rand_device = False
rand_alpha_exponent = False
rand_modulator = False
rand_noise_type = False
noise_type = workflow_tuple['latent_data']['noise_type']
device = workflow_tuple['latent_data']['device']
alpha_exponent = workflow_tuple['latent_data']['alpha_exponent']
modulator = workflow_tuple['latent_data']['modulator']
noise_type = control_data['latent_data']['noise_type']
device = control_data['latent_data']['device']
alpha_exponent = control_data['latent_data']['alpha_exponent']
modulator = control_data['latent_data']['modulator']
if expand_random_limits == True:
rand_device = True
@@ -1124,7 +1157,7 @@ class PrimereFractalLatent:
if optional_vae is None:
latents = tensors.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((height // 8), (width // 8)), mode='nearest-exact')
return {'samples': latents}, tensors, workflow_tuple
return {'samples': latents}, tensors, control_data
encoder = nodes.VAEEncode()
latents = []
@@ -1135,21 +1168,21 @@ class PrimereFractalLatent:
except Exception:
latents = tensors.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((height // 8), (width // 8)), mode='nearest-exact')
return {'samples': latents}, tensors, workflow_tuple
return {'samples': latents}, tensors, control_data
latents = torch.cat(latents)
if workflow_tuple is not None:
workflow_tuple['latent_data'] = {}
workflow_tuple['latent_data']['noise_type'] = noise_type
workflow_tuple['latent_data']['alpha_exponent'] = alpha_exponent
workflow_tuple['latent_data']['modulator'] = modulator
workflow_tuple['latent_data']['device'] = device
if control_data is not None:
control_data['latent_data'] = {}
control_data['latent_data']['noise_type'] = noise_type
control_data['latent_data']['alpha_exponent'] = alpha_exponent
control_data['latent_data']['modulator'] = modulator
control_data['latent_data']['device'] = device
return {'samples': latents}, tensors, workflow_tuple
return {'samples': latents}, tensors, control_data
class PrimereCLIP:
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "STRING", "STRING", "STRING", "STRING", "STRING", "TUPLE")
RETURN_NAMES = ("COND+", "COND-", "PROMPT+", "PROMPT-", "T5XXL_PROMPT", "PROMPT L+", "PROMPT L-", "WORKFLOW_TUPLE")
RETURN_NAMES = ("COND+", "COND-", "PROMPT+", "PROMPT-", "T5XXL_PROMPT", "PROMPT L+", "PROMPT L-", "CONTROL_DATA")
FUNCTION = "clip_encode"
CATEGORY = TREE_DASHBOARD
@@ -1176,7 +1209,7 @@ class PrimereCLIP:
return {
"required": {
"clip": ("CLIP", {"forceInput": True}),
"concept_data": ("TUPLE", {"default": None, "forceInput": True}),
"control_data": ("TUPLE", {"default": None, "forceInput": True}),
"positive_prompt": ("STRING", {"forceInput": True}),
"negative_prompt": ("STRING", {"forceInput": True}),
"negative_strength": ("FLOAT", {"default": 1.2, "min": 0.0, "max": 10.0, "step": 0.01}),
@@ -1220,7 +1253,7 @@ class PrimereCLIP:
"l_strength": ("FLOAT", {"default": 1, "min": 0.0, "max": 10.0, "step": 0.01}),
"width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION, "forceInput": True}),
"height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION, "forceInput": True}),
"workflow_tuple": ("TUPLE", {"default": None}),
"control_data": ("TUPLE", {"default": None}),
},
"hidden": {
"extra_pnginfo": "EXTRA_PNGINFO",
@@ -1228,8 +1261,8 @@ class PrimereCLIP:
}
}
def clip_encode(self, clip, concept_data, negative_strength, int_style_pos_strength, int_style_neg_strength, opt_pos_strength, opt_neg_strength, style_pos_strength, style_neg_strength, style_handling, style_swap, enhanced_prompt_strength, int_style_pos, int_style_neg, adv_encode, token_normalization, weight_interpretation, l_strength, extra_pnginfo, prompt, copy_prompt_to_l=True, width=1024, height=1024, positive_prompt="", negative_prompt="", enhanced_prompt="", enhanced_prompt_usage="T5-XXL", clip_model='Default', longclip_model='Default', model_keywords=None, lora_keywords=None, lycoris_keywords=None, embedding_pos=None, embedding_neg=None, opt_pos_prompt="", opt_neg_prompt="", style_position=False, style_neg_prompt="", style_pos_prompt="", positive_l="", negative_l="", use_int_style=False, edit_image_list=None, edit_vae=None, workflow_tuple=None):
model_concept = concept_data.get('model_concept', 'SD1')
def clip_encode(self, clip, negative_strength, int_style_pos_strength, int_style_neg_strength, opt_pos_strength, opt_neg_strength, style_pos_strength, style_neg_strength, style_handling, style_swap, enhanced_prompt_strength, int_style_pos, int_style_neg, adv_encode, token_normalization, weight_interpretation, l_strength, extra_pnginfo, prompt, copy_prompt_to_l=True, width=1024, height=1024, positive_prompt="", negative_prompt="", enhanced_prompt="", enhanced_prompt_usage="T5-XXL", clip_model='Default', longclip_model='Default', model_keywords=None, lora_keywords=None, lycoris_keywords=None, embedding_pos=None, embedding_neg=None, opt_pos_prompt="", opt_neg_prompt="", style_position=False, style_neg_prompt="", style_pos_prompt="", positive_l="", negative_l="", use_int_style=False, edit_image_list=None, edit_vae=None, control_data=None):
model_concept = control_data.get('model_concept', 'SD1')
advanced_default = ['StableCascade', 'Chroma', 'KwaiKolors', 'Flux', "Z-Image", 'Pony', 'SD1', 'SD2', 'SD3', 'Lightning', 'Hunyuan', 'QwenGen', 'QwenEdit', 'AuraFlow']
if model_concept in advanced_default:
@@ -1250,29 +1283,29 @@ class PrimereCLIP:
embedding_pos, embedding_neg,
)
clip = clipping.apply_clip_attention_multiply(clip, workflow_tuple)
clip = clipping.apply_clip_attention_multiply(clip, control_data)
match model_concept:
case 'SD3':
return clipping.encode_sd3(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
return clipping.encode_sd3(clip, positive_text, negative_text, t5xxl_prompt, control_data)
case 'StableCascade':
return clipping.encode_stable_cascade(clip, positive_text, negative_text, workflow_tuple)
return clipping.encode_stable_cascade(clip, positive_text, negative_text, control_data)
case 'Flux':
return clipping.encode_flux(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
return clipping.encode_flux(clip, positive_text, negative_text, t5xxl_prompt, control_data)
case 'PixartSigma':
return clipping.encode_pixart_sigma(clip, positive_text, negative_text, workflow_tuple)
return clipping.encode_pixart_sigma(clip, positive_text, negative_text, control_data)
case 'SANA1024' | 'SANA512':
return clipping.encode_sana(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
return clipping.encode_sana(clip, positive_text, negative_text, t5xxl_prompt, control_data)
case 'KwaiKolors':
return clipping.encode_kolors(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
return clipping.encode_kolors(clip, positive_text, negative_text, t5xxl_prompt, control_data)
case 'Hunyuan':
return clipping.encode_hunyuan(self, clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
return clipping.encode_hunyuan(self, clip, positive_text, negative_text, t5xxl_prompt, control_data)
case 'QwenEdit':
return clipping.encode_qwen_edit(self, clip, positive_text, negative_text, t5xxl_prompt, edit_vae, edit_image_list, workflow_tuple)
return clipping.encode_qwen_edit(self, clip, positive_text, negative_text, t5xxl_prompt, edit_vae, edit_image_list, control_data)
# case 'Chroma':
# return clipping.encode_chroma(clip, positive_text, negative_text, workflow_tuple)
# return clipping.encode_chroma(clip, positive_text, negative_text, control_data)
case _:
clip = clipping.apply_clip_overrides(self, clip, workflow_tuple)
return clipping.encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode, token_normalization, weight_interpretation, positive_l, negative_l, width, height, workflow_tuple, advanced_encode)
clip = clipping.apply_clip_overrides(self, clip, control_data)
return clipping.encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode, token_normalization, weight_interpretation, positive_l, negative_l, width, height, control_data, advanced_encode)
class PrimereResolution:
RETURN_TYPES = ("INT", "INT", "INT", "STRING")
@@ -1750,37 +1783,37 @@ class PrimereNetworkTagLoader:
"lycoris_keyword_weight": ("FLOAT", {"default": 1.0, "min": 0, "max": 10.0, "step": 0.1}),
},
"optional": {
"workflow_tuple": ("TUPLE", {"default": None}),
"control_data": ("TUPLE", {"default": None}),
}
}
def load_networks(self, model, clip, positive_prompt, process_lora, process_lycoris, process_hypernetwork, copy_weight_to_clip, lora_clip_custom_weight, lycoris_clip_custom_weight, use_lora_keyword, use_lycoris_keyword, lora_keyword_placement, lycoris_keyword_placement, lora_keyword_selection, lycoris_keyword_selection, lora_keywords_num, lycoris_keywords_num, lora_keyword_weight, lycoris_keyword_weight, hypernetwork_safe_load=True, workflow_tuple=None):
if workflow_tuple is not None and len(workflow_tuple) > 0 and 'setup_states' in workflow_tuple and 'exif_status' in workflow_tuple and workflow_tuple['exif_status'] == 'SUCCEED':
def load_networks(self, model, clip, positive_prompt, process_lora, process_lycoris, process_hypernetwork, copy_weight_to_clip, lora_clip_custom_weight, lycoris_clip_custom_weight, use_lora_keyword, use_lycoris_keyword, lora_keyword_placement, lycoris_keyword_placement, lora_keyword_selection, lycoris_keyword_selection, lora_keywords_num, lycoris_keywords_num, lora_keyword_weight, lycoris_keyword_weight, hypernetwork_safe_load=True, control_data=None):
if control_data is not None and len(control_data) > 0 and 'setup_states' in control_data and 'exif_status' in control_data and control_data['exif_status'] == 'SUCCEED':
concept = 'Auto'
stack_version = workflow_tuple['model_version']
if 'model_concept' in workflow_tuple:
concept = workflow_tuple['model_concept']
if 'model_version' in workflow_tuple:
if concept == 'Auto' and workflow_tuple['model_version'] == 'SDXL':
stack_version = control_data['model_version']
if 'model_concept' in control_data:
concept = control_data['model_concept']
if 'model_version' in control_data:
if concept == 'Auto' and control_data['model_version'] == 'SDXL':
stack_version = 'SDXL'
if 'setup_states' in workflow_tuple and 'network_data' in workflow_tuple:
if 'lora_setup' in workflow_tuple['setup_states'] and workflow_tuple['setup_states']['lora_setup'] == True:
loader = networkhandler.getNetworkLoader(workflow_tuple, 'lora', self.LORASCOUNT, True, stack_version)
if 'setup_states' in control_data and 'network_data' in control_data:
if 'lora_setup' in control_data['setup_states'] and control_data['setup_states']['lora_setup'] == True:
loader = networkhandler.getNetworkLoader(control_data, 'lora', self.LORASCOUNT, True, stack_version)
if len(loader) > 0:
networkData = networkhandler.LoraHandler(self, loader, model, clip, [], False, lora_keywords_num, use_lora_keyword, lora_keyword_selection, lora_keyword_weight, lora_keyword_placement)
model = networkData[0]
clip = networkData[1]
if 'lycoris_setup' in workflow_tuple['setup_states'] and workflow_tuple['setup_states']['lycoris_setup'] == True:
loader = networkhandler.getNetworkLoader(workflow_tuple, 'lycoris', self.LYCOSCOUNT, True, stack_version)
if 'lycoris_setup' in control_data['setup_states'] and control_data['setup_states']['lycoris_setup'] == True:
loader = networkhandler.getNetworkLoader(control_data, 'lycoris', self.LYCOSCOUNT, True, stack_version)
if len(loader) > 0:
networkData = networkhandler.LycorisHandler(self, loader, model, clip, [], False, lycoris_keywords_num, use_lycoris_keyword, lycoris_keyword_selection, lycoris_keyword_weight, lycoris_keyword_placement)
model = networkData[0]
clip = networkData[1]
if 'embedding_setup' in workflow_tuple['setup_states'] and workflow_tuple['setup_states']['embedding_setup'] == True:
loader = networkhandler.getNetworkLoader(workflow_tuple, 'embedding', self.EMBCOUNT, False, stack_version)
if 'embedding_setup' in control_data['setup_states'] and control_data['setup_states']['embedding_setup'] == True:
loader = networkhandler.getNetworkLoader(control_data, 'embedding', self.EMBCOUNT, False, stack_version)
if len(loader) > 0:
networkData = networkhandler.EmbeddingHandler(self, loader, None, None)
if networkData[0][0] is not None:
@@ -1788,8 +1821,8 @@ class PrimereNetworkTagLoader:
tokens = clip.tokenize(positive_prompt)
clip = clip.encode_from_tokens(tokens, return_pooled=False)
if 'hypernetwork_setup' in workflow_tuple['setup_states'] and workflow_tuple['setup_states']['hypernetwork_setup'] == True:
loader = networkhandler.getNetworkLoader(workflow_tuple, 'hypernetwork', self.HNCOUNT, False, stack_version)
if 'hypernetwork_setup' in control_data['setup_states'] and control_data['setup_states']['hypernetwork_setup'] == True:
loader = networkhandler.getNetworkLoader(control_data, 'hypernetwork', self.HNCOUNT, False, stack_version)
if len(loader) > 0:
networkData = networkhandler.HypernetworkHandler(self, loader, model, hypernetwork_safe_load)
model = networkData[0]
+81 -145
View File
@@ -160,20 +160,8 @@ class PrimereMetaSave:
subdirs.append(file_output.sanitize_path_part(Path(concept_name).stem.upper()))
if add_modelname_to_path == True and 'model' in image_metadata:
if 'model_concept' in image_metadata and 'model_version' in image_metadata:
original_model_concept_selector = 'Auto'
if extra_pnginfo is not None:
WORKFLOWDATA = extra_pnginfo['workflow']['nodes']
original_model_concept_selector = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'model_concept', prompt)
if image_metadata['model_concept'] != image_metadata['model_version'] or original_model_concept_selector != 'Auto':
match image_metadata['model_concept']:
case 'Flux':
if image_metadata['concept_data']['flux_selector'] == 'GGUF':
image_metadata['model'] = image_metadata['concept_data']['flux_gguf']
else:
image_metadata['model'] = image_metadata['concept_data']['flux_diffusion']
case 'StableCascade':
image_metadata['model'] = image_metadata['concept_data']['cascade_stage_c']
if image_metadata.get('model_name'):
image_metadata['model'] = image_metadata['model_name']
subdirs.append(file_output.sanitize_path_part(Path(image_metadata['model']).stem.upper()))
if subpath_priority == True and 'preferred' in image_metadata and type(image_metadata['preferred']).__name__ == 'dict' and len(image_metadata['preferred']) > 0 and 'subpath' in image_metadata['preferred']:
@@ -396,27 +384,22 @@ class PrimereMetaCollector:
INPUT_DICT = {
"required": {
"positive": ('STRING', {"forceInput": True, "default": "Red sportcar racing"}),
"model_name": ('CHECKPOINT_NAME', {"forceInput": True, "default": None}),
"positive": ('STRING', {"forceInput": True, "default": "Red sportcar racing on the street of metropolis"}),
"negative": ('STRING', {"forceInput": True, "default": "Cute cat, nsfw, nude, nudity, porn"})
}, "optional": {
"model_concept": ("STRING", {"default": "Auto", "forceInput": True}),
"control_data": ("TUPLE", {"default": None, "forceInput": True}),
"seed": ('INT', {"forceInput": True, "default": 1}),
"t5_xxl_prompt": ('STRING', {"forceInput": True}),
"positive_l": ('STRING', {"forceInput": True}),
"negative_l": ('STRING', {"forceInput": True}),
"positive_r": ('STRING', {"forceInput": True}),
"negative_r": ('STRING', {"forceInput": True}),
"model": ('CHECKPOINT_NAME', {"forceInput": True, "default": None}),
"model_version": ("STRING", {"default": 'SD1', "forceInput": True}),
"model_concept": ("STRING", {"default": "Auto", "forceInput": True}),
"concept_data": ("TUPLE", {"default": None, "forceInput": True}),
"sampler": (comfy.samplers.KSampler.SAMPLERS, {"forceInput": True, "default": "euler"}),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"forceInput": True, "default": "normal"}),
"width": ('INT', {"forceInput": True, "default": 512}),
"height": ('INT', {"forceInput": True, "default": 512}),
"model_shapes": ('TUPLE', {"forceInput": True, "default": None}),
"cfg": ('FLOAT', {"forceInput": True, "default": 7}),
"steps": ('INT', {"forceInput": True, "default": 12}),
"vae_name_sd": ('VAE_NAME', {"forceInput": True, "default": None}),
"vae_name_sdxl": ('VAE_NAME', {"forceInput": True, "default": None}),
"width": ('INT', {"forceInput": True, "default": 512}),
"height": ('INT', {"forceInput": True, "default": 512}),
"preferred": ("TUPLE", {"default": None, "forceInput": True}),
"aesthetic_score": ('INT', {"forceInput": True, "default": 0})
},
@@ -446,7 +429,7 @@ class PrimereMetaCollector:
class PrimereKSampler:
CATEGORY = TREE_OUTPUTS
RETURN_TYPES = ("LATENT", "TUPLE")
RETURN_NAMES = ("LATENT", "WORKFLOW_TUPLE")
RETURN_NAMES = ("LATENT", "CONTROL_DATA")
FUNCTION = "pk_sampler"
def __init__(self):
@@ -471,13 +454,11 @@ class PrimereKSampler:
"variation_extender": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"variation_batch_step": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.5, "step": 0.01}),
"variation_level": ("BOOLEAN", {"default": False, "label_on": "Maximize", "label_off": "Off"}),
# "model_sampling": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 10.0, "step": 0.01}),
"device": (["DEFAULT", "GPU", "CPU"], {"default": 'DEFAULT'}),
# "align_your_steps": ("BOOLEAN", {"default": False, "label_on": "Use AlignYourSteps", "label_off": "Ignore AlignYourSteps"}),
"device": (["DEFAULT", "GPU", "CPU"], {"default": 'DEFAULT'})
},
"optional": {
"model_concept": ("STRING", {"default": "Auto", "forceInput": True}),
"workflow_tuple": ("TUPLE", {"default": None}),
"control_data": ("TUPLE", {"default": None}),
},
"hidden": {
"extra_pnginfo": "EXTRA_PNGINFO",
@@ -490,24 +471,24 @@ class PrimereKSampler:
if kwargs['variation_extender'] > 0 or kwargs['device'] != 'DEFAULT' or kwargs['variation_batch_step'] > 0 or kwargs['variation_level'] == True:
return float("NaN")
def pk_sampler(self, model, seed, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, extra_pnginfo, prompt, model_concept = "Auto", workflow_tuple = None, denoise=1.0, variation_extender = 0, variation_batch_step = 0, variation_level = False, model_sampling = 2.5, device = 'DEFAULT', align_your_steps = False):
def pk_sampler(self, model, seed, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, extra_pnginfo, prompt, model_concept = "Auto", control_data = None, denoise=1.0, variation_extender = 0, variation_batch_step = 0, variation_level = False, model_sampling = 2.5, device = 'DEFAULT', align_your_steps = False):
timestamp_start = time.time()
if workflow_tuple is not None:
align_your_steps = workflow_tuple.get('align_your_steps', align_your_steps)
model_sampling = workflow_tuple.get('model_sampling', model_sampling)
if workflow_tuple is not None and len(workflow_tuple) > 0 and 'exif_status' in workflow_tuple and workflow_tuple['exif_status'] == 'SUCCEED':
if 'sampler_settings' in workflow_tuple and len(workflow_tuple['sampler_settings']) > 0 and 'setup_states' in workflow_tuple and 'sampler_setup' in workflow_tuple['setup_states']:
if workflow_tuple['setup_states']['sampler_setup'] == True:
if control_data is not None:
align_your_steps = control_data.get('align_your_steps', align_your_steps)
model_sampling = control_data.get('model_sampling', model_sampling)
if control_data is not None and len(control_data) > 0 and 'exif_status' in control_data and control_data['exif_status'] == 'SUCCEED':
if 'sampler_settings' in control_data and len(control_data['sampler_settings']) > 0 and 'setup_states' in control_data and 'sampler_setup' in control_data['setup_states']:
if control_data['setup_states']['sampler_setup'] == True:
variation_batch_step = 0
variation_level = False
denoise = workflow_tuple['sampler_settings']['denoise']
device = workflow_tuple['sampler_settings']['device']
align_your_steps = workflow_tuple['sampler_settings']['align_your_steps']
model_sampling = workflow_tuple['sampler_settings']['model_sampling']
if workflow_tuple['sampler_settings']['variation_level'] == True:
variation_extender = workflow_tuple['sampler_settings']['noise_constant']
denoise = control_data['sampler_settings']['denoise']
device = control_data['sampler_settings']['device']
align_your_steps = control_data['sampler_settings']['align_your_steps']
model_sampling = control_data['sampler_settings']['model_sampling']
if control_data['sampler_settings']['variation_level'] == True:
variation_extender = control_data['sampler_settings']['noise_constant']
else:
variation_extender = workflow_tuple['sampler_settings']['variation_extender_original']
variation_extender = control_data['sampler_settings']['variation_extender_original']
samples_out = latent_image
# out = latent_image.copy()
@@ -605,7 +586,7 @@ class PrimereKSampler:
variation_extender, variation_batch_step_original,
batch_counter, variation_extender_original,
variation_batch_step, variation_level, variation_limit,
align_your_steps, noise_extender_ksampler, workflow_tuple)[0]
align_your_steps, noise_extender_ksampler, control_data)[0]
case "KwaiKolors":
samples_out = primeresamplers.PSamplerKOROLS(self, model, seed, cfg, positive, negative, latent_image, steps, denoise, sampler_name, scheduler_name, model_sampling, 1000)[0]
case "SD3":
@@ -617,20 +598,17 @@ class PrimereKSampler:
noise_constant = noise_extender_cascade
samples_out = primeresamplers.PCascadeSampler(self, model, seed, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise, device, variation_level, variation_limit, variation_extender_original, variation_batch_step_original, variation_extender, variation_batch_step, batch_counter, noise_extender_cascade)[0]
case "Hyper":
CONCEPT_SELECTOR = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'model_concept', prompt)
OriginalBaseModel = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'base_model', prompt)
CONCEPT_SELECTOR = control_data.get('model_concept') if control_data else None
OriginalBaseModel = control_data.get('model_name') if control_data else None
fullpathFile = folder_paths.get_full_path('checkpoints', OriginalBaseModel)
is_link = os.path.islink(str(fullpathFile))
if is_link == True:
HYPERSD_SELECTOR = 'UNET'
else:
HYPERSD_SELECTOR = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'hypersd_selector', prompt)
HYPERSD_SAMPLER = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'hypersd_sampler', prompt)
HYPERSD_SELECTOR = 'UNET' if is_link else 'LORA'
HYPERSD_SAMPLER = True
if model_concept == 'Hyper' and (CONCEPT_SELECTOR == 'Hyper' or CONCEPT_SELECTOR == 'Auto') and steps == 12 and HYPERSD_SELECTOR == 'LORA' and HYPERSD_SAMPLER == True:
cfg = 3.80
scheduler_name = 'normal'
samples_out = primeresamplers.PSamplerHyper(self, extra_pnginfo, model, seed, steps, cfg, positive, negative, sampler_name, scheduler_name, latent_image, denoise, prompt)[0]
cfg = float(control_data.get('cfg', 3.80))
scheduler_name = control_data.get('scheduler_name', "normal")
samples_out = primeresamplers.PSamplerHyper(self, extra_pnginfo, model, seed, steps, cfg, positive, negative, sampler_name, scheduler_name, latent_image, denoise, prompt, control_data)[0]
case 'QwenGen' | 'QwenEdit':
align_your_steps = False
@@ -657,14 +635,14 @@ class PrimereKSampler:
align_your_steps, noise_extender_ksampler, None)[0]
case 'Flux':
FLUX_SAMPLER = workflow_tuple.get('sampler', 'ksampler') if workflow_tuple else 'ksampler'
FLUX_GUIDANCE = float(workflow_tuple.get('guidance', 3.5)) if workflow_tuple else 3.5
FLUX_SAMPLER = control_data.get('sampler', 'ksampler') if control_data else 'ksampler'
FLUX_GUIDANCE = float(control_data.get('guidance', 3.5)) if control_data else 3.5
align_your_steps = False
if FLUX_SAMPLER == 'custom_advanced':
samples_out = primeresamplers.PSamplerAdvanced(self, model, seed, FLUX_GUIDANCE, positive, scheduler_name, sampler_name, steps, denoise, latent_image)[0]
elif FLUX_SAMPLER == 'ksampler':
CONDITIONING_POS = nodes_flux.FluxGuidance.execute(positive, FLUX_GUIDANCE)[0] if FLUX_GUIDANCE > 0 else positive
if workflow_tuple is not None and float(workflow_tuple.get('cfg', 2.0)) < 1.2:
if control_data is not None and float(control_data.get('cfg', 2.0)) < 1.2:
CONDITIONING_NEG = CONDITIONING_POS
else:
CONDITIONING_NEG = nodes_flux.FluxGuidance.execute(negative, FLUX_GUIDANCE)[0]
@@ -673,14 +651,14 @@ class PrimereKSampler:
CONDITIONING_POS, CONDITIONING_NEG,
latent_image, denoise,
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit,
align_your_steps, noise_extender_ksampler, None)[0]
align_your_steps, noise_extender_ksampler, None, control_data)[0]
else:
samples_out = primeresamplers.PKSampler(self, device, seed, model,
steps, cfg, sampler_name, scheduler_name,
positive, negative,
latent_image, denoise,
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit,
align_your_steps, noise_extender_ksampler, None)[0]
align_your_steps, noise_extender_ksampler, None, control_data)[0]
case _:
if model_concept == 'AuraFlow' and model_sampling is not None and model_sampling > 0:
@@ -690,25 +668,24 @@ class PrimereKSampler:
positive, negative,
latent_image, denoise,
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit,
align_your_steps, noise_extender_ksampler, None)[0]
align_your_steps, noise_extender_ksampler, None, control_data)[0]
if workflow_tuple is not None:
workflow_tuple['sampler_settings'] = {}
workflow_tuple['sampler_settings']['denoise'] = denoise
workflow_tuple['sampler_settings']['variation_extender_original'] = variation_extender_original
workflow_tuple['sampler_settings']['variation_batch_step_original'] = variation_batch_step_original
workflow_tuple['sampler_settings']['variation_level'] = variation_level
workflow_tuple['sampler_settings']['device'] = device
workflow_tuple['sampler_settings']['align_your_steps'] = align_your_steps
workflow_tuple['sampler_settings']['noise_constant'] = noise_constant
workflow_tuple['sampler_settings']['variation_seed'] = seed
workflow_tuple['sampler_settings']['batch_counter'] = batch_counter
workflow_tuple['sampler_settings']['model_sampling'] = model_sampling
if control_data is not None:
control_data['sampler_settings'] = {}
control_data['sampler_settings']['denoise'] = denoise
control_data['sampler_settings']['variation_extender_original'] = variation_extender_original
control_data['sampler_settings']['variation_batch_step_original'] = variation_batch_step_original
control_data['sampler_settings']['variation_level'] = variation_level
control_data['sampler_settings']['device'] = device
control_data['sampler_settings']['align_your_steps'] = align_your_steps
control_data['sampler_settings']['noise_constant'] = noise_constant
control_data['sampler_settings']['variation_seed'] = seed
control_data['sampler_settings']['batch_counter'] = batch_counter
control_data['sampler_settings']['model_sampling'] = model_sampling
timestamp_diff = int(time.time() - timestamp_start)
original_model_concept_selector = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'model_concept', prompt)
is_random_model = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'random_model', prompt)
selected_model = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'base_model', prompt)
selected_model = control_data.get('model_name') if control_data else utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'base_model', prompt)
if is_random_model == True:
fullSource = PrimereVisualCKPT.allModels
slashIndex = selected_model.find('\\')
@@ -718,17 +695,6 @@ class PrimereKSampler:
random.seed(seed)
selected_model = random.choice(models_by_path)
if original_model_concept_selector != 'Auto':
match original_model_concept_selector:
case 'Flux':
flux_selector = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'flux_selector', prompt)
if flux_selector == 'GGUF':
selected_model = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'flux_gguf', prompt)
else:
selected_model = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'flux_diffusion', prompt)
case 'StableCascade':
selected_model = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'cascade_stage_c', prompt)
if selected_model is not None:
modelname_only = Path(selected_model).stem
model_samplingtime = utility.get_value_from_cache('model_samplingtime', modelname_only)
@@ -740,7 +706,7 @@ class PrimereKSampler:
diffvalue = str(int(model_samplingtime_list[1]) + timestamp_diff)
utility.add_value_to_cache('model_samplingtime', modelname_only, counter + '|' + diffvalue)
return (samples_out, workflow_tuple)
return (samples_out, control_data)
class PrimerePreviewImage():
CATEGORY = TREE_OUTPUTS
@@ -878,7 +844,7 @@ class PrimereAestheticCKPTScorer:
"image": ("IMAGE", ),
},
"optional": {
"workflow_data": ('TUPLE', {"forceInput": True}),
"control_data": ('TUPLE', {"forceInput": True}),
},
"hidden": {
"extra_pnginfo": "EXTRA_PNGINFO",
@@ -886,59 +852,17 @@ class PrimereAestheticCKPTScorer:
},
}
def aesthetic_scorer(self, image, get_aesthetic_score, add_to_checkpoint, add_to_saved_prompt, prompt, dual_mode = True, workflow_data = None, **kwargs):
def aesthetic_scorer(self, image, get_aesthetic_score, add_to_checkpoint, add_to_saved_prompt, prompt, dual_mode = True, control_data = None, **kwargs):
final_prediction = '*** Aesthetic scorer off ***'
models = []
WORKFLOWDATA = kwargs['extra_pnginfo']['workflow']['nodes']
AE_SCORE_MIN = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'aescore_percent_min', prompt)
AE_SCORE_MAX = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'aescore_percent_max', prompt)
def pipe(model):
return pipeline(task="image-classification", model=model, device=model_management.get_torch_device())
if (get_aesthetic_score == True):
'''AESTHETIC_PATH = os.path.join(folder_paths.models_dir, 'aesthetic')
folder_paths.add_model_folder_path("aesthetic", AESTHETIC_PATH)
if os.path.exists(AESTHETIC_PATH) == False:
Path(AESTHETIC_PATH).mkdir(parents=True, exist_ok=True)
AESTH_FULL_LIST = folder_paths.get_filename_list("aesthetic")
aestheticFiles = folder_paths.filter_files_extensions(AESTH_FULL_LIST, ['.pth'])
if 'chadscorer.pth' not in aestheticFiles:
FileUrl = 'https://huggingface.co/primerecomfydev/chadscorer/resolve/main/chadscorer.pth?download=true'
FullFilePath = os.path.join(AESTHETIC_PATH, 'chadscorer.pth')
ModelDownload = utility.downloader(FileUrl, FullFilePath)
if (ModelDownload == True):
AESTH_FULL_LIST = folder_paths.get_filename_list("aesthetic")
aestheticFiles = folder_paths.filter_files_extensions(AESTH_FULL_LIST, ['.pth'])
if 'chadscorer.pth' in aestheticFiles:
folder_paths.folder_names_and_paths["aesthetic"] = ([os.path.join(folder_paths.models_dir, "aesthetic")], folder_paths.supported_pt_extensions)
m_path = folder_paths.folder_names_and_paths["aesthetic"][0]
aesthetic_model = os.path.join(m_path[0], 'chadscorer.pth')
fsize = os.path.getsize(aesthetic_model)
freemem = comfy.model_management.get_free_memory()
if (fsize * 1.2) < freemem:
model = utility.MLP(768)
s = torch.load(aesthetic_model)
model.load_state_dict(s)
model.to("cuda")
model.eval()
device = "cuda"
try:
model2, preprocess = clip.load("ViT-L/14", device=device) # RN50x64
tensor_image = image[0]
img = (tensor_image * 255).to(torch.uint8).numpy()
pil_image = Image.fromarray(img, mode='RGB')
image2 = preprocess(pil_image).unsqueeze(0).to(device)
with torch.no_grad():
image_features = model2.encode_image(image2)
pass
im_emb_arr = utility.normalized(image_features.cpu().detach().numpy())
prediction = model(torch.from_numpy(im_emb_arr).to(device).type(torch.cuda.FloatTensor))
final_prediction = int(float(prediction[0]) * 100)
del model
except Exception:
final_prediction = 0
else:
final_prediction = 0'''
AE_MODEL_ROOT = os.path.join(folder_paths.models_dir, 'aesthetic')
AEMODELS_ENCODERS_PATHS = utility.getValidAscorerPaths(AE_MODEL_ROOT)
if len(AEMODELS_ENCODERS_PATHS) > 0:
@@ -985,13 +909,15 @@ class PrimereAestheticCKPTScorer:
else:
final_prediction = '*** No aesthetic models downloaded ***'
if (type(final_prediction) != 'str'):
if type(final_prediction) != str:
final_prediction = str(final_prediction)
if workflow_data is not None and final_prediction.isdigit():
if add_to_checkpoint == True and (workflow_data['model_concept'] == workflow_data['model_version']):
if 'model' in workflow_data:
selected_model = workflow_data['model']
if control_data is not None and final_prediction.isdigit():
if add_to_checkpoint == True and (control_data['model_concept']):
if 'model_name' in control_data:
AE_SCORE_MIN = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'aescore_percent_min', prompt)
AE_SCORE_MAX = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'aescore_percent_max', prompt)
selected_model = control_data['model_name']
modelname_only = Path(selected_model).stem
model_ascore = utility.get_value_from_cache('model_ascores', modelname_only)
if model_ascore is None:
@@ -1003,8 +929,7 @@ class PrimereAestheticCKPTScorer:
utility.add_value_to_cache('model_ascores', modelname_only, counter + '|' + score)
if add_to_saved_prompt == True and final_prediction.isdigit():
if 'positive' in workflow_data:
WORKFLOWDATA = kwargs['extra_pnginfo']['workflow']['nodes']
if 'positive' in control_data:
selectedStyle = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualStyle', 'styles', prompt)
if selectedStyle is None:
selectedStyle = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereStyleLoader', 'styles', prompt)
@@ -1042,7 +967,9 @@ class PrimereAestheticCKPTScorer:
if (positive_prompt is not None):
if len(positive_prompt) > 100:
positive_prompt = positive_prompt[:100]
if positive_prompt in workflow_data['positive']:
if positive_prompt in control_data['positive']:
AE_SCORE_MIN = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualStyle', 'aescore_percent_min', prompt)
AE_SCORE_MAX = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualStyle', 'aescore_percent_max', prompt)
style_ascore = utility.get_value_from_cache('styles_ascores', selectedStyle)
if style_ascore is None:
utility.add_value_to_cache('styles_ascores', selectedStyle, '1|' + final_prediction)
@@ -1052,7 +979,16 @@ class PrimereAestheticCKPTScorer:
score = str(int(style_ascore_list[1]) + int(final_prediction))
utility.add_value_to_cache('styles_ascores', selectedStyle, counter + '|' + score)
return {"ui": {"text": [final_prediction]}, "result": (final_prediction,)}
if isinstance(final_prediction, str) and final_prediction.isdigit():
if AE_SCORE_MIN is not None and AE_SCORE_MAX is not None:
final_prediction = max(0, min(100, int(((int(final_prediction) - AE_SCORE_MIN) / (AE_SCORE_MAX - AE_SCORE_MIN)) * 100)))
else:
final_prediction = int(final_prediction)
else:
final_prediction = '*** Aesthetic scorer error ***'
result_int = final_prediction if isinstance(final_prediction, int) else 0
return {"ui": {"text": [final_prediction]}, "result": (result_int,)}
class DebugToFile():
CATEGORY = TREE_OUTPUTS
+2 -2
View File
@@ -38,7 +38,7 @@ for subdirs in valid_FElist:
NODE_CLASS_MAPPINGS = {
"PrimereSamplersSteps": Dashboard.PrimereSamplersSteps,
"PrimereAutoSamplerSettings": Dashboard.PrimereAutoSamplerSettings,
"PrimereModelControl": Dashboard.PrimereModelControl,
"PrimereVAE": Dashboard.PrimereVAE,
"PrimereCKPT": Dashboard.PrimereCKPT,
"PrimereVAELoader": Dashboard.PrimereVAELoader,
@@ -117,7 +117,7 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"PrimereSamplersSteps": "Primere Samplers & Steps & Cfg",
"PrimereAutoSamplerSettings": "Primere Auto Sampler Settings",
"PrimereModelControl": "Primere Primere ModelControl",
"PrimereVAE": "Primere VAE Selector",
"PrimereCKPT": "Primere CKPT Selector",
"PrimereVAELoader": "Primere VAE Loader",
+78 -55
View File
@@ -558,32 +558,55 @@ def build_prompt_context(
SDXL_CONCEPTS = {'SDXL', 'Illustrious', 'Pony', 'Playground'}
CLIP_ATTN_PRESETS = {
"Off": (1.00, 1.00, 1.00, 1.00),
"Natural": (1.00, 1.02, 0.98, 1.00),
"Realism": (1.00, 1.05, 0.95, 1.00),
"Photography": (1.02, 1.05, 0.93, 0.98),
"Cinematic": (1.05, 1.05, 1.00, 0.95),
"Portrait": (1.03, 1.08, 0.92, 0.97),
"Art": (0.95, 0.95, 1.10, 1.05),
"Illustration": (0.90, 1.00, 1.15, 1.00),
"Anime": (0.88, 0.95, 1.18, 1.05),
"Prompt adherence": (1.10, 1.10, 1.00, 1.00),
"Abstract": (0.85, 0.88, 1.18, 1.12),
"Creative": (0.85, 0.90, 1.20, 1.10),
"Surreal": (0.80, 0.85, 1.25, 1.15),
# Unified preset: (q, k, v, out, cross_q, cross_k, cross_v, cross_out)
# First 4: applied to CLIP attention + UNet self-attention (attn1)
# Last 4: applied to UNet cross-attention (attn2) — ignored for clip-only models
ATTN_PRESETS = {
"Off": (1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00),
"Natural": (1.00, 1.02, 0.98, 1.00, 1.00, 1.02, 0.98, 1.00),
"Realism": (1.00, 1.05, 0.95, 1.00, 1.05, 1.05, 0.95, 1.00),
"Photography": (1.02, 1.05, 0.93, 0.98, 1.05, 1.05, 0.93, 0.98),
"Cinematic": (1.05, 1.05, 1.00, 0.95, 1.05, 1.05, 1.00, 0.95),
"Portrait": (1.03, 1.08, 0.92, 0.97, 1.05, 1.10, 0.90, 1.00),
"Art": (0.95, 0.95, 1.10, 1.05, 0.90, 0.95, 1.10, 1.00),
"Illustration": (0.90, 1.00, 1.15, 1.00, 0.90, 1.00, 1.15, 1.00),
"Anime": (0.88, 0.95, 1.18, 1.05, 0.88, 0.95, 1.18, 1.00),
"Prompt adherence": (1.10, 1.10, 1.00, 1.00, 1.10, 1.10, 1.00, 1.00),
"Abstract": (0.85, 0.88, 1.18, 1.12, 0.85, 0.88, 1.15, 1.05),
"Creative": (0.85, 0.90, 1.20, 1.10, 0.85, 0.90, 1.15, 1.05),
}
def apply_clip_overrides(loader_self, clip, workflow_tuple):
if not workflow_tuple:
ATTN_PRESET_KEYWORDS = {
'Anime': ['anime', 'waifu', 'nai', 'hentai', 'manga'],
'Photography': ['photo', 'realistic', 'realvis', 'realism'],
'Illustration': ['illustration', 'illus', 'cartoon', 'draw'],
'Cinematic': ['cinematic', 'film', 'movie', 'cinema'],
'Portrait': ['portrait'],
'Art': ['paint', 'artistic', 'watercolor'],
'Natural': ['natural'],
'Abstract': ['abstract', 'surreal'],
}
def detect_attn_preset(model_name, default='Off'):
if not model_name:
return default
name = model_name.lower().replace('\\', '/').split('/')[-1].split('.')[0]
for preset, keywords in ATTN_PRESET_KEYWORDS.items():
if any(kw in name for kw in keywords):
return preset
return default
def apply_clip_overrides(loader_self, clip, control_data):
if not control_data:
return clip
encoder_1 = workflow_tuple.get('encoder_1', None)
last_layer = int(workflow_tuple.get('last_layer', 0))
encoder_1 = control_data.get('encoder_1', None)
last_layer = int(control_data.get('last_layer', 0))
baked_clip = clip
if encoder_1 and encoder_1 != 'None' and not workflow_tuple.get('clip_selection', False):
if encoder_1 and encoder_1 != 'None' and not control_data.get('clip_selection', False):
try:
model_concept = workflow_tuple.get('model_concept', 'SD1')
model_concept = control_data.get('model_concept', 'SD1')
is_longclip = 'longclip' in encoder_1.lower() or encoder_1.lower().endswith('.pt')
if is_longclip:
if model_concept in SDXL_CONCEPTS:
@@ -603,13 +626,13 @@ def apply_clip_overrides(loader_self, clip, workflow_tuple):
return clip
def apply_clip_attention_multiply(clip, workflow_tuple):
if not workflow_tuple:
def apply_clip_attention_multiply(clip, control_data):
if not control_data:
return clip
q = float(workflow_tuple.get('clip_attn_q', 1.0))
k = float(workflow_tuple.get('clip_attn_k', 1.0))
v = float(workflow_tuple.get('clip_attn_v', 1.0))
out = float(workflow_tuple.get('clip_attn_out', 1.0))
q = float(control_data.get('clip_attn_q', 1.0))
k = float(control_data.get('clip_attn_k', 1.0))
v = float(control_data.get('clip_attn_v', 1.0))
out = float(control_data.get('clip_attn_out', 1.0))
if q == 1.0 and k == 1.0 and v == 1.0 and out == 1.0:
return clip
try:
@@ -618,14 +641,14 @@ def apply_clip_attention_multiply(clip, workflow_tuple):
return clip
def encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode, token_normalization, weight_interpretation, positive_l, negative_l, width, height, workflow_tuple, advanced_encode_fn):
def encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode, token_normalization, weight_interpretation, positive_l, negative_l, width, height, control_data, advanced_encode_fn):
if adv_encode:
tokens_p = clip.tokenize(positive_text)
tokens_n = clip.tokenize(negative_text)
if 'l' not in tokens_p or 'g' not in tokens_p or 'l' not in tokens_n or 'g' not in tokens_n:
embeddings_final_pos, pooled_pos = advanced_encode_fn(clip, positive_text, token_normalization, weight_interpretation, w_max=1.0, apply_to_pooled=True)
embeddings_final_neg, pooled_neg = advanced_encode_fn(clip, negative_text, token_normalization, weight_interpretation, w_max=1.0, apply_to_pooled=True)
return ([[embeddings_final_pos, {"pooled_output": pooled_pos}]], [[embeddings_final_neg, {"pooled_output": pooled_neg}]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[embeddings_final_pos, {"pooled_output": pooled_pos}]], [[embeddings_final_neg, {"pooled_output": pooled_neg}]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
else:
if 'l' in clip.tokenize(positive_l):
tokens_p["l"] = clip.tokenize(positive_l)["l"]
@@ -645,7 +668,7 @@ def encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode
tokens_n["g"] += empty["g"]
cond_p, pooled_p = clip.encode_from_tokens(tokens_p, return_pooled=True)
cond_n, pooled_n = clip.encode_from_tokens(tokens_n, return_pooled=True)
return ([[cond_p, {"pooled_output": pooled_p, "width": width, "height": height, "crop_w": 0, "crop_h": 0, "target_width": width, "target_height": height}]], [[cond_n, {"pooled_output": pooled_n, "width": width, "height": height, "crop_w": 0, "crop_h": 0, "target_width": width, "target_height": height}]], positive_text, negative_text, "", positive_l, negative_l, workflow_tuple)
return ([[cond_p, {"pooled_output": pooled_p, "width": width, "height": height, "crop_w": 0, "crop_h": 0, "target_width": width, "target_height": height}]], [[cond_n, {"pooled_output": pooled_n, "width": width, "height": height, "crop_w": 0, "crop_h": 0, "target_width": width, "target_height": height}]], positive_text, negative_text, "", positive_l, negative_l, control_data)
else:
tokens_pos = clip.tokenize(positive_text)
tokens_neg = clip.tokenize(negative_text)
@@ -657,16 +680,16 @@ def encode_standard(clip, positive_text, negative_text, t5xxl_prompt, adv_encode
out_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True, return_dict=True)
cond_pos = out_pos.pop("cond")
cond_neg = out_neg.pop("cond")
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
def encode_sd3(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple):
def encode_sd3(clip, positive_text, negative_text, t5xxl_prompt, control_data):
if t5xxl_prompt:
pos_out = nodes_sd3.CLIPTextEncodeSD3.execute(clip, positive_text, positive_text, t5xxl_prompt, 'none')
tokens_neg = clip.tokenize(negative_text)
out_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True, return_dict=True)
cond_neg = out_neg.pop("cond")
return (pos_out[0], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return (pos_out[0], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
else:
tokens_pos = clip.tokenize(positive_text)
tokens_neg = clip.tokenize(negative_text)
@@ -674,20 +697,20 @@ def encode_sd3(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple)
out_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True, return_dict=True)
cond_pos = out_pos.pop("cond")
cond_neg = out_neg.pop("cond")
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, "", "", "", workflow_tuple)
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, "", "", "", control_data)
def encode_stable_cascade(clip, positive_text, negative_text, workflow_tuple):
def encode_stable_cascade(clip, positive_text, negative_text, control_data):
positive_text = utility.DiT_cleaner(positive_text)
negative_text = utility.DiT_cleaner(negative_text)
tokens_pos = clip.tokenize(positive_text)
tokens_neg = clip.tokenize(negative_text)
cond_pos, pooled_pos = clip.encode_from_tokens(tokens_pos, return_pooled=True)
cond_neg, pooled_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True)
return ([[cond_pos, {"pooled_output": pooled_pos}]], [[cond_neg, {"pooled_output": pooled_neg}]], positive_text, negative_text, "", "", "", workflow_tuple)
return ([[cond_pos, {"pooled_output": pooled_pos}]], [[cond_neg, {"pooled_output": pooled_neg}]], positive_text, negative_text, "", "", "", control_data)
def encode_pixart_sigma(clip, positive_text, negative_text, workflow_tuple):
def encode_pixart_sigma(clip, positive_text, negative_text, control_data):
positive_text = utility.DiT_cleaner(positive_text)
negative_text = utility.DiT_cleaner(negative_text)
@@ -710,32 +733,32 @@ def encode_pixart_sigma(clip, positive_text, negative_text, workflow_tuple):
cond_pos_main = out_pos_main.pop("cond")
cond_neg_main = out_neg_main.pop("cond")
return ({'refiner': [[cond_pos_ref, out_pos_ref]], 'main': [[cond_pos_main, out_pos_main]]}, {'refiner': [[cond_neg_ref, out_neg_ref]], 'main': [[cond_neg_main, out_neg_main]]}, positive_text, negative_text, "", "", "", workflow_tuple)
return ({'refiner': [[cond_pos_ref, out_pos_ref]], 'main': [[cond_pos_main, out_pos_main]]}, {'refiner': [[cond_neg_ref, out_neg_ref]], 'main': [[cond_neg_main, out_neg_main]]}, positive_text, negative_text, "", "", "", control_data)
def encode_chroma(clip, positive_text, negative_text, workflow_tuple):
def encode_chroma(clip, positive_text, negative_text, control_data):
tokens_pos = clip.tokenize(positive_text)
tokens_neg = clip.tokenize(negative_text)
out_pos = clip.encode_from_tokens(tokens_pos, return_pooled=True, return_dict=True)
out_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True, return_dict=True)
cond_pos = out_pos.pop("cond")
cond_neg = out_neg.pop("cond")
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, "", "", "", workflow_tuple)
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, "", "", "", control_data)
def encode_flux(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple):
FLUX_SAMPLER = workflow_tuple.get('sampler', 'ksampler')
FLUX_GUIDANCE = workflow_tuple.get('guidance', 2)
def encode_flux(clip, positive_text, negative_text, t5xxl_prompt, control_data):
FLUX_SAMPLER = control_data.get('sampler', 'ksampler')
FLUX_GUIDANCE = control_data.get('guidance', 2)
if FLUX_SAMPLER == 'custom_advanced' and len(t5xxl_prompt) > 5:
CONDITIONING_POS = nodes_flux.CLIPTextEncodeFlux.execute(clip, positive_text, t5xxl_prompt, FLUX_GUIDANCE)[0]
return (CONDITIONING_POS, CONDITIONING_POS, positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return (CONDITIONING_POS, CONDITIONING_POS, positive_text, negative_text, t5xxl_prompt, "", "", control_data)
tokens_pos = clip.tokenize(positive_text)
tokens_neg = clip.tokenize(negative_text)
out_pos = clip.encode_from_tokens(tokens_pos, return_pooled=True, return_dict=True)
out_neg = clip.encode_from_tokens(tokens_neg, return_pooled=True, return_dict=True)
cond_pos = out_pos.pop("cond")
cond_neg = out_neg.pop("cond")
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
_SANA_MAX_TOKENS = 300
@@ -757,8 +780,8 @@ def _sana_encode_text(tokenizer, text_encoder, text, device):
return embs * masks.unsqueeze(-1)
def encode_sana(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple):
scheduler_name = workflow_tuple.get('scheduler_name', 'flow_dpm-solver') if workflow_tuple else 'flow_dpm-solver'
def encode_sana(clip, positive_text, negative_text, t5xxl_prompt, control_data):
scheduler_name = control_data.get('scheduler_name', 'flow_dpm-solver') if control_data else 'flow_dpm-solver'
device = model_management.get_torch_device()
if scheduler_name == 'flow_dpm-solver' and hasattr(clip, 'text_encoder'):
@@ -776,7 +799,7 @@ def encode_sana(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple
null_y = null_embs.repeat(len(prompts), 1, 1)[:, None]
clip.text_encoder.to(model_management.text_encoder_offload_device())
comfy.model_management.soft_empty_cache(True)
return ([[caption_embs, {"emb_masks": emb_masks}]], [[null_y, {}]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[caption_embs, {"emb_masks": emb_masks}]], [[null_y, {}]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
else:
tokenizer = clip["tokenizer"]
text_encoder = clip["text_encoder"]
@@ -784,10 +807,10 @@ def encode_sana(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple
with torch.no_grad():
sana_embs_pos = _sana_encode_text(tokenizer, text_encoder, positive_text, enc_device)
sana_embs_neg = _sana_encode_text(tokenizer, text_encoder, negative_text, enc_device)
return ([[sana_embs_pos, {}]], [[sana_embs_neg, {}]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[sana_embs_pos, {}]], [[sana_embs_neg, {}]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
def encode_qwen_edit(loader_self, clip, positive_text, negative_text, t5xxl_prompt, edit_vae, edit_image_list, workflow_tuple):
def encode_qwen_edit(loader_self, clip, positive_text, negative_text, t5xxl_prompt, edit_vae, edit_image_list, control_data):
if type(edit_image_list).__name__ == "Tensor":
edit_image_list = [edit_image_list]
positive_text = utility.DiT_cleaner(positive_text)
@@ -795,10 +818,10 @@ def encode_qwen_edit(loader_self, clip, positive_text, negative_text, t5xxl_prom
conditioning = utility.edit_encoder(clip, positive_text, edit_vae, edit_image_list)
tokens_neg = clip.tokenize(negative_text, images=[])
conditioning_neg = clip.encode_from_tokens_scheduled(tokens_neg)
return (conditioning, conditioning_neg, positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return (conditioning, conditioning_neg, positive_text, negative_text, t5xxl_prompt, "", "", control_data)
def encode_kolors(clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple):
def encode_kolors(clip, positive_text, negative_text, t5xxl_prompt, control_data):
positive_text = utility.DiT_cleaner(positive_text)
negative_text = utility.DiT_cleaner(negative_text)
device = model_management.text_encoder_device()
@@ -847,17 +870,17 @@ def encode_kolors(clip, positive_text, negative_text, t5xxl_prompt, workflow_tup
'pooled_prompt_embeds': text_proj.half(),
'negative_pooled_prompt_embeds': negative_text_proj.half(),
}
return (kolors_embeds, None, positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return (kolors_embeds, None, positive_text, negative_text, t5xxl_prompt, "", "", control_data)
def encode_hunyuan(loader_self, clip, positive_text, negative_text, t5xxl_prompt, workflow_tuple):
def encode_hunyuan(loader_self, clip, positive_text, negative_text, t5xxl_prompt, control_data):
if clip['t5'] is not None:
positive_text = utility.DiT_cleaner(positive_text)
negative_text = utility.DiT_cleaner(negative_text)
t5xxl_prompt = utility.DiT_cleaner(t5xxl_prompt)
pos_out = HunyuanClipping(loader_self, positive_text, t5xxl_prompt, clip['clip'], clip['t5'])
neg_out = HunyuanClipping(loader_self, negative_text, "", clip['clip'], clip['t5'])
return (pos_out[0], neg_out[0], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return (pos_out[0], neg_out[0], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
else:
clip_model = clip['clip']
positive_text = utility.DiT_cleaner(positive_text, 512)
@@ -866,4 +889,4 @@ def encode_hunyuan(loader_self, clip, positive_text, negative_text, t5xxl_prompt
out_neg = clip_model.encode_from_tokens(clip_model.tokenize(negative_text), return_pooled=True, return_dict=True)
cond_pos = out_pos.pop("cond")
cond_neg = out_neg.pop("cond")
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", workflow_tuple)
return ([[cond_pos, out_pos]], [[cond_neg, out_neg]], positive_text, negative_text, t5xxl_prompt, "", "", control_data)
+69 -6
View File
@@ -8,6 +8,7 @@ import nodes
import comfy_extras.nodes_sd3 as nodes_sd3
import comfy_extras.nodes_model_advanced as nodes_model_advanced
import comfy_extras.nodes_cfg as nodes_cfg
from comfy_extras.nodes_attention_multiply import attention_multiply
from comfy import model_management
from pathlib import Path
from .tree import PRIMERE_ROOT
@@ -56,6 +57,56 @@ def resolve_symlink(ckpt_name):
return File_link, linkedFileName, model_ext
DISCRETE_CONCEPTS = {'SD1', 'SD2', 'SDXL', 'Illustrious', 'Turbo', 'Pony', 'Hyper', 'Lightning'}
UNET_CONCEPTS = {'SD1', 'SD2', 'SDXL', 'Illustrious', 'Turbo', 'Pony', 'Hyper', 'Lightning', 'Playground', 'LCM'}
def apply_generic_patches(loader_self, model, concept_data):
model_concept = concept_data.get('model_concept', '')
discrete_sampling = concept_data.get('discrete_sampling', 'default')
if discrete_sampling != 'default' and model_concept in DISCRETE_CONCEPTS:
try:
discrete_zsnr = bool(concept_data.get('discrete_zsnr', False))
model = nodes_model_advanced.ModelSamplingDiscrete.patch(loader_self, model, discrete_sampling, discrete_zsnr)[0]
except Exception as e:
print(f"Primere: ModelSamplingDiscrete failed: {e}")
if model_concept in UNET_CONCEPTS:
self_q = concept_data.get('clip_attn_q', 1.0)
self_k = concept_data.get('clip_attn_k', 1.0)
self_v = concept_data.get('clip_attn_v', 1.0)
self_out = concept_data.get('clip_attn_out', 1.0)
if (self_q, self_k, self_v, self_out) != (1.0, 1.0, 1.0, 1.0):
try:
model = attention_multiply("attn1", model, self_q, self_k, self_v, self_out)
except Exception as e:
print(f"Primere: UNet self-attention multiply failed: {e}")
if model_concept in UNET_CONCEPTS:
cross_q = concept_data.get('attn_cross_q', 1.0)
cross_k = concept_data.get('attn_cross_k', 1.0)
cross_v = concept_data.get('attn_cross_v', 1.0)
cross_out = concept_data.get('attn_cross_out', 1.0)
if (cross_q, cross_k, cross_v, cross_out) != (1.0, 1.0, 1.0, 1.0):
try:
model = attention_multiply("attn2", model, cross_q, cross_k, cross_v, cross_out)
except Exception as e:
print(f"Primere: UNet cross-attention multiply failed: {e}")
precision = concept_data.get('precision', None)
if precision and precision not in ('quant8', 'quant4'):
dtype_map = {'fp32': 'fp32', 'fp16': 'fp16'}
dtype = dtype_map.get(precision)
if dtype:
try:
model = nodes_model_advanced.ModelComputeDtype.patch(loader_self, model, dtype)[0]
except Exception as e:
print(f"Primere: ModelComputeDtype failed: {e}")
return model
def apply_lora(loader_self, model, lora_path, strength):
if not os.path.exists(lora_path) or strength == 0:
return model
@@ -102,6 +153,7 @@ def load_sd_model(loader_self, ckpt_name, use_yaml, model_config_full_path, conc
OUTPUT_VAE = utility.vae_loader_class.load_vae(vae_name)[0]
else:
OUTPUT_VAE = LOADED_CHECKPOINT[2]
OUTPUT_MODEL = apply_generic_patches(loader_self, OUTPUT_MODEL, concept_data)
return OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE
@@ -131,6 +183,7 @@ def load_sd3_model(loader_self, ckpt_name, concept_data):
lora_path = folder_paths.get_full_path('loras', lora_name)
if lora_path:
OUTPUT_MODEL = apply_lora(loader_self, OUTPUT_MODEL, lora_path, lora_strength)
OUTPUT_MODEL = apply_generic_patches(loader_self, OUTPUT_MODEL, concept_data)
return OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE
@@ -212,6 +265,13 @@ def load_flux_model(loader_self, ckpt_name, concept_data):
rescale_cfg = concept_data.get('rescale_cfg', 1.0)
if rescale_cfg != 1.0:
OUTPUT_MODEL = nodes_model_advanced.RescaleCFG.patch(loader_self, OUTPUT_MODEL, rescale_cfg)[0]
flux_max_shift = concept_data.get('flux_max_shift', 1.15)
flux_base_shift = concept_data.get('flux_base_shift', 0.5)
try:
OUTPUT_MODEL = nodes_model_advanced.ModelSamplingFlux.patch(loader_self, OUTPUT_MODEL, flux_max_shift, flux_base_shift, 1024, 1024)[0]
except Exception as e:
print(f"Primere: ModelSamplingFlux failed: {e}")
OUTPUT_MODEL = apply_generic_patches(loader_self, OUTPUT_MODEL, concept_data)
return OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE
@@ -268,7 +328,11 @@ def load_playground_model(loader_self, ckpt_name, use_yaml, model_config_full_pa
OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE = load_sd_model(loader_self, ckpt_name, use_yaml, model_config_full_path, concept_data)
sigma_max = concept_data.get('sigma_max', 120)
sigma_min = concept_data.get('sigma_min', 0.002)
OUTPUT_MODEL = nodes_model_advanced.ModelSamplingContinuousEDM.patch(loader_self, OUTPUT_MODEL, 'edm_playground_v2.5', sigma_max, sigma_min)[0]
edm_sampling = concept_data.get('edm_sampling', 'edm_playground_v2.5')
try:
OUTPUT_MODEL = nodes_model_advanced.ModelSamplingContinuousEDM.patch(loader_self, OUTPUT_MODEL, edm_sampling, sigma_max, sigma_min)[0]
except Exception as e:
print(f"Primere: ModelSamplingContinuousEDM failed: {e}")
return OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE
@@ -300,6 +364,7 @@ def load_lightning_hyper_model(loader_self, ckpt_name, concept_data):
if lora_path:
OUTPUT_MODEL = utility.BDanceConceptHelper(loader_self, model_concept, True, 'LORA', None, OUTPUT_MODEL, lora_path, None, None, lora_strength)
OUTPUT_MODEL = apply_generic_patches(loader_self, OUTPUT_MODEL, concept_data)
return OUTPUT_MODEL, OUTPUT_CLIP, OUTPUT_VAE
@@ -312,11 +377,9 @@ def load_lcm_model(loader_self, ckpt_name, concept_data):
MODEL_VERSION = utility.getModelType(ckpt_name, 'checkpoints')
if concept_data.get('lcm_lora') == True:
lora_name = concept_data.get('lcm_lora_name', None)
if lora_name:
lora_path = folder_paths.get_full_path('loras', lora_name)
if lora_path:
OUTPUT_MODEL = apply_lora(loader_self, OUTPUT_MODEL, lora_path, concept_data.get('lcm_lora_strength', 1.0))
lora_file = 'lcm_lora_sdxl.safetensors' if 'SDXL' in MODEL_VERSION else 'lcm_lora_sd.safetensors'
lora_path = os.path.join(PRIMERE_ROOT, 'Nodes', 'Downloads', lora_file)
OUTPUT_MODEL = apply_lora(loader_self, OUTPUT_MODEL, lora_path, concept_data.get('lcm_lora_strength', 1.0))
class ModelSamplingAdvanced(utility.ModelSamplingDiscreteLCM, nodes_model_advanced.LCM):
pass
+79 -41
View File
@@ -25,32 +25,71 @@ def PKSampler(self, device, seed, model,
steps, cfg, sampler_name, scheduler_name,
positive, negative,
latent_image, denoise,
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit, align_your_steps, noise_extender, model_sampling = None):
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit, align_your_steps, noise_extender, model_sampling=None, control_data=None):
if model_sampling is not None and model_sampling > 0:
model = nodes_model_advanced.ModelSamplingSD3.patch(self, model, model_sampling, 1.0)[0]
if variation_level == True:
samples = latentnoise.noisy_samples(model, device, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise, seed, noise_extender)
else:
if variation_extender_original > 0 or device != 'DEFAULT' or variation_batch_step_original > 0:
samples = None
if scheduler_name == 'beta' and control_data is not None:
beta_alpha = float(control_data.get('beta_alpha', 0.6))
beta_beta = float(control_data.get('beta_beta', 0.6))
try:
sigmas = comfy.samplers.beta_scheduler(model.get_model_object("model_sampling"), steps, alpha=beta_alpha, beta=beta_beta)
sampler = comfy.samplers.sampler_object(sampler_name)
samples = (nodes_custom_sampler.SamplerCustom.execute(model, True, seed, cfg, positive, negative, sampler, sigmas, latent_image)[0],)
except Exception as e:
print(f"Primere: BetaSamplingScheduler failed: {e}")
if samples is None:
if variation_level == True:
samples = latentnoise.noisy_samples(model, device, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise, seed, noise_extender)
else:
if align_your_steps == True:
modelname_only = model
model_version = utility.get_value_from_cache('model_version', modelname_only)
match model_version:
case 'SDXL':
model_type = 'SDXL'
case _:
model_type = 'SD1'
sigmas = nodes_align_your_steps.AlignYourStepsScheduler.get_sigmas(self, model_type, steps, denoise)
sampler = comfy.samplers.sampler_object(sampler_name)
AYS_samples = nodes_custom_sampler.SamplerCustom.execute(model, True, seed, cfg, positive, negative, sampler, sigmas[0], latent_image)
samples = (AYS_samples[0],)
if variation_extender_original > 0 or device != 'DEFAULT' or variation_batch_step_original > 0:
samples = latentnoise.noisy_samples(model, device, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise, seed, noise_extender)
else:
samples = nodes.KSampler.sample(self, model, seed, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise=denoise)
if align_your_steps == True:
modelname_only = model
model_version = utility.get_value_from_cache('model_version', modelname_only)
match model_version:
case 'SDXL':
model_type = 'SDXL'
case _:
model_type = 'SD1'
sigmas = nodes_align_your_steps.AlignYourStepsScheduler.get_sigmas(self, model_type, steps, denoise)
sampler = comfy.samplers.sampler_object(sampler_name)
AYS_samples = nodes_custom_sampler.SamplerCustom.execute(model, True, seed, cfg, positive, negative, sampler, sigmas[0], latent_image)
samples = (AYS_samples[0],)
else:
samples = nodes.KSampler.sample(self, model, seed, steps, cfg, sampler_name, scheduler_name, positive, negative, latent_image, denoise=denoise)
if control_data and control_data.get('refiner') == True:
refiner_model_name = control_data.get('refiner_model', None)
if refiner_model_name and refiner_model_name != 'None':
try:
REFINER_CHECKPOINT = nodes.CheckpointLoaderSimple.load_checkpoint(self, refiner_model_name)
RAW_IMAGE = nodes.VAEDecode.decode(self, REFINER_CHECKPOINT[2], samples[0])[0]
RAW_IMAGE_ENCODED = nodes.VAEEncode.encode(self, REFINER_CHECKPOINT[2], RAW_IMAGE)[0]
REFINER_SAMPLER = control_data.get('refiner_sampler', 'dpmpp_2m')
REFINER_SCHEDULER = control_data.get('refiner_scheduler', 'normal')
REFINER_CFG = float(control_data.get('refiner_cfg', 2.0))
REFINER_STEPS = int(control_data.get('refiner_steps', 22))
REFINER_DENOISE = float(control_data.get('refiner_denoise', 0.9))
REFINER_START = int(control_data.get('refiner_start', 12))
sigmas_refiner = nodes_custom_sampler.BasicScheduler.execute(REFINER_CHECKPOINT[0], REFINER_SCHEDULER, REFINER_STEPS, REFINER_DENOISE)[0]
splitted_sigma = nodes_custom_sampler.SplitSigmas.execute(sigmas_refiner, REFINER_START)[1]
sampler_refiner = comfy.samplers.sampler_object(REFINER_SAMPLER)
if control_data.get('refiner_ignore_prompt', True):
empty_cond = nodes.CLIPTextEncode.encode(self, REFINER_CHECKPOINT[1], "")[0]
refiner_pos = empty_cond
refiner_neg = empty_cond
else:
refiner_pos = positive
refiner_neg = negative
samples = (nodes_custom_sampler.SamplerCustom.execute(REFINER_CHECKPOINT[0], True, seed, REFINER_CFG, refiner_pos, refiner_neg, sampler_refiner, splitted_sigma, RAW_IMAGE_ENCODED)[0],)
except Exception as e:
print(f"Primere: Refiner sampling failed: {e}")
return samples
@@ -97,16 +136,15 @@ def PCascadeSampler(self, model, seed, steps, cfg, sampler_name, scheduler_name,
return samples
def PSamplerHyper(self, extra_pnginfo, model, seed, steps, cfg, positive, negative, sampler_name, scheduler_name, latent_image, denoise, prompt):
WORKFLOWDATA = extra_pnginfo['workflow']['nodes']
OriginalBaseModel = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'base_model', prompt)
def PSamplerHyper(self, extra_pnginfo, model, seed, steps, cfg, positive, negative, sampler_name, scheduler_name, latent_image, denoise, prompt, control_data):
# WORKFLOWDATA = extra_pnginfo['workflow']['nodes']
OriginalBaseModel = control_data['model_name'] # OriginalBaseModel = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereVisualCKPT', 'base_model', prompt)
fullpathFile = folder_paths.get_full_path('checkpoints', OriginalBaseModel)
is_link = os.path.islink(str(fullpathFile))
HyperSDSelector = None
if is_link == True:
HyperSDSelector = 'UNET'
else:
HyperSDSelector = utility.getDataFromWorkflowByName(WORKFLOWDATA, 'PrimereModelConceptSelector', 'hypersd_selector', prompt)
if (HyperSDSelector == 'UNET'):
if HyperSDSelector == 'UNET':
sigmas = utility.get_hypersd_sigmas(model)
sampler = comfy.samplers.sampler_object(sampler_name)
hyper_samples = nodes_custom_sampler.SamplerCustom.execute(model, True, seed, cfg, positive, negative, sampler, sigmas[0], latent_image)
@@ -155,14 +193,14 @@ def PSamplerPixart(self, device, seed, model,
steps, cfg, sampler_name, scheduler_name,
positive, negative,
latent_image, denoise,
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit, align_your_steps, noise_extender, workflow_tuple):
variation_extender, variation_batch_step_original, batch_counter, variation_extender_original, variation_batch_step, variation_level, variation_limit, align_your_steps, noise_extender, control_data):
if workflow_tuple:
sampler_name = workflow_tuple.get('sampler_name', sampler_name)
scheduler_name = workflow_tuple.get('scheduler_name', scheduler_name)
steps = workflow_tuple.get('steps', steps)
cfg = workflow_tuple.get('cfg', cfg)
PIXART_DENOISE = float(workflow_tuple.get('refiner_sampling_denoise', denoise)) if workflow_tuple and workflow_tuple.get('refiner') == True else denoise
if control_data:
sampler_name = control_data.get('sampler_name', sampler_name)
scheduler_name = control_data.get('scheduler_name', scheduler_name)
steps = control_data.get('steps', steps)
cfg = control_data.get('cfg', cfg)
PIXART_DENOISE = float(control_data.get('refiner_sampling_denoise', denoise)) if control_data and control_data.get('refiner') == True else denoise
sigmas_main = nodes_custom_sampler.BasicScheduler.execute(model['main'], scheduler_name, steps, denoise=PIXART_DENOISE)[0]
sampler = comfy.samplers.sampler_object(sampler_name)
@@ -182,22 +220,22 @@ def PSamplerPixart(self, device, seed, model,
samples_main = nodes_custom_sampler.SamplerCustom.execute(model['main'], True, seed, cfg, positive['main'], negative['main'], sampler, sigmas_main, latent_image)[0]
if 'refiner' in model and model['refiner'] is not None:
PIXART_VAE = utility.vae_loader_class.load_vae(workflow_tuple.get('vae'))[0]
PIXART_VAE = utility.vae_loader_class.load_vae(control_data.get('vae'))[0]
RAW_IMAGE = nodes.VAEDecode.decode(self, PIXART_VAE, samples_main)[0]
PIXART_REFINER_CHECKPOINT = nodes.CheckpointLoaderSimple.load_checkpoint(self, workflow_tuple.get('refiner_model'))
PIXART_REFINER_CHECKPOINT = nodes.CheckpointLoaderSimple.load_checkpoint(self, control_data.get('refiner_model'))
RAW_IMAGE_ENCODED = nodes.VAEEncode.encode(self, PIXART_REFINER_CHECKPOINT[2], RAW_IMAGE)[0]
REFINER_SAMPLER = workflow_tuple.get('refiner_sampler', 'dpmpp_2m')
REFINER_SCHEDULER = workflow_tuple.get('refiner_scheduler', 'normal')
REFINER_CFG = float(workflow_tuple.get('refiner_cfg', 2.0))
REFINER_STEPS = int(workflow_tuple.get('refiner_steps', 22))
PIXART_DENOISE_REFINER = float(workflow_tuple.get('refiner_denoise', 0.9))
PIXART_REFINER_START = int(workflow_tuple.get('refiner_start', 12))
REFINER_SAMPLER = control_data.get('refiner_sampler', 'dpmpp_2m')
REFINER_SCHEDULER = control_data.get('refiner_scheduler', 'normal')
REFINER_CFG = float(control_data.get('refiner_cfg', 2.0))
REFINER_STEPS = int(control_data.get('refiner_steps', 22))
PIXART_DENOISE_REFINER = float(control_data.get('refiner_denoise', 0.9))
PIXART_REFINER_START = int(control_data.get('refiner_start', 12))
sigmas_refiner = nodes_custom_sampler.BasicScheduler.execute(model['refiner'], REFINER_SCHEDULER, REFINER_STEPS, PIXART_DENOISE_REFINER)[0]
splitted_low_sigma = nodes_custom_sampler.SplitSigmas.execute(sigmas_refiner, PIXART_REFINER_START)[1]
sampler_refiner = comfy.samplers.sampler_object(REFINER_SAMPLER)
REFINER_IGNORE_PROMPT = workflow_tuple.get('refiner_ignore_prompt', False)
REFINER_IGNORE_PROMPT = control_data.get('refiner_ignore_prompt', False)
if REFINER_IGNORE_PROMPT:
empty_cond = nodes.CLIPTextEncode.encode(self, PIXART_REFINER_CHECKPOINT[1], "")[0]
refiner_pos = empty_cond
+5 -3
View File
@@ -2,16 +2,18 @@ import { app } from "/scripts/app.js";
import { ComfyWidgets } from "/scripts/widgets.js";
import { applyPrimereButtonStyle, showToast } from "./frontend_helper.js";
const TARGET_NODE_NAME = "PrimereAutoSamplerSettings";
const TARGET_NODE_NAME = "PrimereModelControl";
const CONCEPT_JSON_URL = new URL("/extensions/ComfyUI_Primere_Nodes/model_concept.json", import.meta.url).href;
const JSON_EXCLUDE_KEYS = new Set(["model_name"]);
function modelNameToKey(modelPath) {
const base = modelPath.split(/[\\/]/).pop();
return base.replace(/\.[^/.]+$/, "");
}
function collectNodeData(node, includeLoraToggles = false) {
const SKIP_KEYS = new Set(["concepts", "models", "runtime_concept"]);
const SKIP_KEYS = new Set(["concepts", "models", "runtime_concept", ...JSON_EXCLUDE_KEYS]);
const widgets = node.widgets || [];
const loraBooleans = new Set(
@@ -175,7 +177,7 @@ function initializeSamplerNode(node) {
}
app.registerExtension({
name: "Primere.AutoSamplerSettings",
name: "Primere.ModelControl",
setup() {
app.api.addEventListener("primere.concept_setting", (event) => {
+2 -2
View File
@@ -20,8 +20,8 @@ const state = {
sortType: 'name',
operator: 'ASC',
PreviewPath: true,
aeScoreMin: 400,
aeScoreMax: 900,
aeScoreMin: 550,
aeScoreMax: 800,
nodeHelper: {},
cache_key: '',
source_subdirname: '',
+37 -12
View File
@@ -12,25 +12,50 @@ primere_root = Path(__file__).parent.parent.absolute()
components_path = os.path.join(primere_root, 'components')
sys.path.append(components_path)
import utility as utility
import utility
EmbeddingList = folder_paths.get_filename_list("embeddings")
def match_supported(name):
name_lower = name.lower()
for supported in utility.SUPPORTED_MODELS:
if supported.lower() == name_lower:
return supported
return None
def get_type_from_dir(oneEmbedding):
parts = Path(oneEmbedding).parts
if len(parts) > 1:
return match_supported(parts[0])
return None
print('------------------- START -------------------------')
print(str(len(EmbeddingList)) + ' embeddings in system')
print('--------------- CACHED EMBEDDING INFO ---------------------')
if len(EmbeddingList) > 0:
model_counter = 1
for onelora in EmbeddingList:
name_only = Path(onelora).stem
for oneEmbedding in EmbeddingList:
name_only = Path(oneEmbedding).stem
prefix = f"Embedding [{model_counter}] / {len(EmbeddingList)}"
model_version = utility.get_value_from_cache('embedding_version', name_only)
if model_version is None or model_version not in utility.SUPPORTED_MODELS:
model_version = 'SD1'
utility.add_value_to_cache('embedding_version', name_only, model_version)
print('Embedding cached: ' + name_only + ' -> ' + str(model_version))
else:
print('Embedding already cached: ' + name_only + ' -> ' + str(model_version))
model_counter = model_counter + 1
if model_version is not None and model_version in utility.SUPPORTED_MODELS:
print(f"{prefix} already cached: {name_only} -> {model_version}")
model_counter += 1
continue
dir_version = get_type_from_dir(oneEmbedding)
if dir_version:
utility.add_value_to_cache('embedding_version', name_only, dir_version)
print(f"{prefix} cached from directory: {name_only} -> {dir_version}")
model_counter += 1
continue
cache_msg = f"UNKNOWN | path: models/embeddings/{oneEmbedding}"
utility.add_value_to_cache('embedding_version', name_only, cache_msg)
print(f"{prefix} {cache_msg}")
model_counter += 1
else:
print('No embedding in your system....')
print('No embeddings in your system....')
+44 -17
View File
@@ -12,30 +12,57 @@ primere_root = Path(__file__).parent.parent.absolute()
components_path = os.path.join(primere_root, 'components')
sys.path.append(components_path)
import utility as utility
import utility
def match_supported(name):
name_lower = name.lower()
for supported in utility.SUPPORTED_MODELS:
if supported.lower() == name_lower:
return supported
return None
def get_type_from_dir(oneModel):
parts = Path(oneModel).parts
if len(parts) > 1:
return match_supported(parts[0])
return None
ModelsList = folder_paths.get_filename_list("loras")
print('------------------- START -------------------------')
print(str(len(ModelsList)) + ' loras in system')
print('--------------- CACHED LORAS INFO ---------------------')
if len(ModelsList) > 0:
model_counter = 1
for oneModel in ModelsList:
model_path = folder_paths.get_full_path("loras", oneModel)
modelaname_only = Path(oneModel).stem
model_version = utility.get_value_from_cache('lora_version', modelaname_only)
if model_version is None or model_version not in utility.SUPPORTED_MODELS:
model_version = utility.getModelType(oneModel, 'loras')
if model_version and model_version is not None and model_version != 'NoneType':
utility.add_value_to_cache('lora_version', modelaname_only, str(model_version))
print('Lora [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + str(model_version))
else:
utility.add_value_to_cache('lora_version', modelaname_only, 'unknown')
print('Lora [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + 'unknown')
else:
print('Lora [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' already cached: ' + modelaname_only + ' -> ' + str(model_version))
model_counter = model_counter + 1
modelname_only = Path(oneModel).stem
prefix = f"Lora [{model_counter}] / {len(ModelsList)}"
model_version = utility.get_value_from_cache('lora_version', modelname_only)
if model_version is not None and model_version in utility.SUPPORTED_MODELS:
print(f"{prefix} already cached: {modelname_only} -> {model_version}")
model_counter += 1
continue
model_version = utility.getModelType(oneModel, 'loras')
if model_version and model_version not in (None, False, 'NoneType') and model_version in utility.SUPPORTED_MODELS:
utility.add_value_to_cache('lora_version', modelname_only, model_version)
print(f"{prefix} cached from metadata: {modelname_only} -> {model_version}")
model_counter += 1
continue
dir_version = get_type_from_dir(oneModel)
if dir_version:
utility.add_value_to_cache('lora_version', modelname_only, dir_version)
print(f"{prefix} cached from directory: {modelname_only} -> {dir_version}")
model_counter += 1
continue
cache_msg = f"UNKNOWN | path: models/loras/{oneModel}"
utility.add_value_to_cache('lora_version', modelname_only, cache_msg)
print(f"{prefix} {cache_msg}")
model_counter += 1
else:
print('No loras in your system....')
print('No loras in your system....')
+44 -17
View File
@@ -12,33 +12,60 @@ primere_root = Path(__file__).parent.parent.absolute()
components_path = os.path.join(primere_root, 'components')
sys.path.append(components_path)
import utility as utility
import utility
LYCO_DIR = os.path.join(folder_paths.models_dir, 'lycoris')
folder_paths.add_model_folder_path("lycoris", LYCO_DIR)
LyCORIS = folder_paths.get_filename_list("lycoris")
ModelsList = folder_paths.filter_files_extensions(LyCORIS, ['.ckpt', '.safetensors'])
def match_supported(name):
name_lower = name.lower()
for supported in utility.SUPPORTED_MODELS:
if supported.lower() == name_lower:
return supported
return None
def get_type_from_dir(oneModel):
parts = Path(oneModel).parts
if len(parts) > 1:
return match_supported(parts[0])
return None
print('------------------- START -------------------------')
print(str(len(ModelsList)) + ' lycoris in system')
print('--------------- CACHED LYCORIS INFO ---------------------')
if len(ModelsList) > 0:
model_counter = 1
for oneModel in ModelsList:
model_path = folder_paths.get_full_path("lycoris", oneModel)
modelaname_only = Path(oneModel).stem
model_version = utility.get_value_from_cache('lycoris_version', modelaname_only)
if model_version is None or model_version not in utility.SUPPORTED_MODELS:
model_version = utility.getModelType(oneModel, 'lycoris')
if model_version and model_version is not None and model_version != 'NoneType':
utility.add_value_to_cache('lycoris_version', modelaname_only, str(model_version))
print('Lyco [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + str(model_version))
else:
utility.add_value_to_cache('lycoris_version', modelaname_only, 'unknown')
print('Lyco [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + 'unknown')
else:
print('Lyco [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' already cached: ' + modelaname_only + ' -> ' + str(model_version))
model_counter = model_counter + 1
modelname_only = Path(oneModel).stem
prefix = f"Lyco [{model_counter}] / {len(ModelsList)}"
model_version = utility.get_value_from_cache('lycoris_version', modelname_only)
if model_version is not None and model_version in utility.SUPPORTED_MODELS:
print(f"{prefix} already cached: {modelname_only} -> {model_version}")
model_counter += 1
continue
model_version = utility.getModelType(oneModel, 'lycoris')
if model_version and model_version not in (None, False, 'NoneType') and model_version in utility.SUPPORTED_MODELS:
utility.add_value_to_cache('lycoris_version', modelname_only, model_version)
print(f"{prefix} cached from metadata: {modelname_only} -> {model_version}")
model_counter += 1
continue
dir_version = get_type_from_dir(oneModel)
if dir_version:
utility.add_value_to_cache('lycoris_version', modelname_only, dir_version)
print(f"{prefix} cached from directory: {modelname_only} -> {dir_version}")
model_counter += 1
continue
cache_msg = f"UNKNOWN | path: models/lycoris/{oneModel}"
utility.add_value_to_cache('lycoris_version', modelname_only, cache_msg)
print(f"{prefix} {cache_msg}")
model_counter += 1
else:
print('No lycoris in your system....')
print('No lycoris in your system....')
+64 -38
View File
@@ -12,54 +12,80 @@ primere_root = Path(__file__).parent.parent.absolute()
components_path = os.path.join(primere_root, 'components')
sys.path.append(components_path)
import utility as utility
import utility
EXCLUDED_SUBDIRS = {'.locks', 'Bjornulf_civitAI', 'depthfm', 'models--xiaozaa--cat-tryoff-flux'}
def match_supported(name):
name_lower = name.lower()
for supported in utility.SUPPORTED_MODELS:
if supported.lower() == name_lower:
return supported
return None
def get_type_from_dirs(oneModel, resolved_path=None):
parts = Path(oneModel).parts
if len(parts) > 1:
matched = match_supported(parts[0])
if matched:
return matched
if resolved_path is not None:
parent_dir = Path(resolved_path).parent.name
matched = match_supported(parent_dir)
if matched:
return matched
return None
ModelsList = folder_paths.get_filename_list("checkpoints")
print('------------------- START -------------------------')
print(str(len(ModelsList)) + ' models in system')
print('--------------- CACHED MODELS INFO ---------------------')
if len(ModelsList) > 0:
model_counter = 1
for oneModel in ModelsList:
parts = Path(oneModel).parts
first_part = parts[0] if len(parts) > 1 else ''
if first_part in EXCLUDED_SUBDIRS:
model_counter += 1
continue
model_path = folder_paths.get_full_path("checkpoints", oneModel)
is_link = os.path.islink(str(model_path))
modelaname_only = Path(oneModel).stem
if is_link == False:
model_version = utility.get_value_from_cache('model_version', modelaname_only)
if model_version is None or model_version not in utility.SUPPORTED_MODELS:
model_version = utility.getModelType(oneModel, 'checkpoints')
if model_version and model_version is not None and model_version != 'NoneType':
utility.add_value_to_cache('model_version', modelaname_only, str(model_version))
print('Model [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + str(model_version))
else:
utility.add_value_to_cache('model_version', modelaname_only, 'unknown')
print('Model [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' cached: ' + modelaname_only + ' -> ' + 'unknown')
else:
print('Model [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' already cached: ' + modelaname_only + ' -> ' + str(model_version))
else:
model_version = utility.get_value_from_cache('model_version', modelaname_only)
if model_version is None or model_version not in utility.SUPPORTED_MODELS:
File_link = Path(str(model_path)).resolve()
comfyModelDir = str(Path(folder_paths.folder_names_and_paths['checkpoints'][0][0]).parent)
# modelType = str(File_link)[(len(comfyModelDir) + 1):(str(File_link).find('\\', len(comfyModelDir) + 1))]
try:
modelType = str(File_link)[str(File_link).index(os.sep + Path(comfyModelDir).stem) + len(Path(comfyModelDir).stem) + 2:(str(File_link).find(os.sep, len(comfyModelDir) + 1))]
linkName_U = str(folder_paths.folder_names_and_paths["diffusion_models"][0][0])
linkName_D = str(folder_paths.folder_names_and_paths["diffusion_models"][0][1])
if str(Path(linkName_U).stem + '\\') in str(File_link):
modelType = str(Path(linkName_U).stem)
if str(Path(linkName_D).stem + '\\') in str(File_link):
modelType = str(Path(linkName_D).stem)
modelname_only = Path(oneModel).stem
resolved_path = Path(str(model_path)).resolve() if is_link else None
prefix = f"Model [{model_counter}] / {len(ModelsList)}"
utility.add_value_to_cache('model_version', modelaname_only, f"{modelType}_symlink")
print(f"Model [{model_counter}] / {len(ModelsList)} symlinked file: {modelaname_only} -> from: {modelType}")
except Exception:
utility.add_value_to_cache('model_version', modelaname_only, f"unknown_symlink")
print(f"Model [{model_counter}] / {len(ModelsList)} unknown by meta error: {modelaname_only}")
else:
print('Model [' + str(model_counter) + '] / ' + str(len(ModelsList)) + ' already cached: ' + modelaname_only + ' -> ' + str(model_version))
model_counter = model_counter + 1
model_version = utility.get_value_from_cache('model_version', modelname_only)
if model_version is not None and model_version in utility.SUPPORTED_MODELS:
print(f"{prefix} already cached: {modelname_only} -> {model_version}")
model_counter += 1
continue
model_version = utility.getModelType(oneModel, 'checkpoints')
if model_version and model_version not in (None, False, 'NoneType') and model_version in utility.SUPPORTED_MODELS:
utility.add_value_to_cache('model_version', modelname_only, model_version)
src = f" (symlink from: {resolved_path})" if is_link else ""
print(f"{prefix} cached from metadata: {modelname_only} -> {model_version}{src}")
model_counter += 1
continue
dir_version = get_type_from_dirs(oneModel, resolved_path)
if dir_version:
utility.add_value_to_cache('model_version', modelname_only, dir_version)
src = f" (symlink from: {resolved_path})" if is_link else ""
print(f"{prefix} cached from directory: {modelname_only} -> {dir_version}{src}")
model_counter += 1
continue
if is_link:
cache_msg = f"UNKNOWN | checkpoint: models/checkpoints/{oneModel} | original: {resolved_path}"
else:
cache_msg = f"UNKNOWN | path: models/checkpoints/{oneModel}"
utility.add_value_to_cache('model_version', modelname_only, cache_msg)
print(f"{prefix} {cache_msg}")
model_counter += 1
else:
print('No models in your system....')
print('No models in your system....')