Files
2026-02-14 11:40:36 +08:00

684 lines
29 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import torch
import re
import torch.nn.functional as F
from nodes import CLIPTextEncode
class pre_Unit_PromptWeight:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"clip": ("CLIP",),
"prompt": ("STRING", {"multiline": True}),
},
"optional": {
"main_prompt_ratio": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("CONDITIONING", "STRING")
FUNCTION = "process"
def __init__(self):
pass
def get_pooled_from_cond(self, cond_feature):
if cond_feature is None:
return torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
device = cond_feature.device
dtype = cond_feature.dtype
embed_dim = int(cond_feature.shape[-1])
pooled = torch.mean(cond_feature, dim=0)
pooled = F.layer_norm(pooled, normalized_shape=[embed_dim])
return pooled
def clean_text(self, text):
clean_chars = ',,.。 \t\n'
cleaned = text.strip(clean_chars)
return cleaned
def parse_with_auto_pack(self, prompt):
pattern = re.compile(r'\[([^\]]+)@([0-9.]+)\]')
matches = list(pattern.finditer(prompt))
segments = []
weights = []
all_text_parts = []
last_end = 0
for match in matches:
start = match.start()
end = match.end()
prefix_text = prompt[last_end:start]
cleaned_prefix = self.clean_text(prefix_text)
if cleaned_prefix:
segments.append(cleaned_prefix)
weights.append(1.0)
all_text_parts.append(cleaned_prefix)
anchor_content = match.group(1).strip()
anchor_weight = float(match.group(2)) if match.group(2).replace('.','').isdigit() else 1.0
segments.append(anchor_content)
weights.append(anchor_weight)
all_text_parts.append(anchor_content)
last_end = end
suffix_text = prompt[last_end:]
cleaned_suffix = self.clean_text(suffix_text)
if cleaned_suffix:
segments.append(cleaned_suffix)
weights.append(1.0)
all_text_parts.append(cleaned_suffix)
whole_content = ','.join(all_text_parts)
return segments, weights, whole_content
def fuse_conditions_equal(self, cond_features, cond_strengths):
if len(cond_features) == 0 or len(cond_strengths) == 0:
return None, None, None
device = cond_features[0].device if cond_features[0] is not None else ("cuda" if torch.cuda.is_available() else "cpu")
dtype = cond_features[0].dtype if cond_features[0] is not None else torch.float16
total_w = sum(cond_strengths)
norm_w = [s / total_w if total_w > 1e-6 else 1.0/len(cond_strengths) for s in cond_strengths]
max_len = max([cf.shape[0] for cf in cond_features if cf is not None], default=1)
padded_feats = []
for cf in cond_features:
if cf is None:
cf = torch.zeros((max_len, 768), dtype=dtype, device=device)
pad_len = max_len - cf.shape[0]
if pad_len > 0:
pad = torch.zeros((pad_len, cf.shape[-1]), device=device, dtype=dtype)
padded_feat = torch.cat([cf, pad], dim=0)
else:
padded_feat = cf
padded_feats.append(padded_feat)
fused_feat = torch.zeros_like(padded_feats[0], device=device, dtype=dtype)
for feat, w in zip(padded_feats, norm_w):
fused_feat += feat * w
pooled_list = [self.get_pooled_from_cond(cf) for cf in cond_features]
fused_pooled = torch.zeros_like(pooled_list[0], device=device, dtype=dtype)
for pl, w in zip(pooled_list, norm_w):
fused_pooled += pl * w
return fused_feat.unsqueeze(0), fused_pooled, norm_w
def process(self, clip, prompt, main_prompt_ratio=0.5):
prompt_info = ""
segments, weights, whole_content = self.parse_with_auto_pack(prompt)
has_custom_weight = any(w != 1.0 for w in weights)
if not has_custom_weight and len(segments) > 0:
main_prompt_ratio = 1.0
if len(segments) == 0 or not whole_content:
tok_empty = clip.tokenize("")
c_empty = torch.zeros((1, 768), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
p_empty = torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
prompt_info = "Weight:main_prompt()0%|No valid individuals"
return ([[c_empty, {"pooled_output": p_empty}]], prompt_info)
try:
tokens_whole = clip.tokenize(whole_content)
c_whole, p_whole = clip.encode_from_tokens(tokens_whole, return_pooled=True)
feat_whole = c_whole.squeeze(0) if c_whole is not None else torch.zeros((77, 768), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
pooled_whole = p_whole if p_whole is not None else self.get_pooled_from_cond(feat_whole)
except:
feat_whole = torch.zeros((77, 768), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
pooled_whole = torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
cond_features = []
for seg in segments:
try:
tokens = clip.tokenize(seg)
c_out, p_out = clip.encode_from_tokens(tokens, return_pooled=True)
cond_feat = c_out.squeeze(0) if c_out is not None else None
except:
cond_feat = None
cond_features.append(cond_feat)
fused_individual, pooled_individual, norm_w = self.fuse_conditions_equal(cond_features, weights)
if fused_individual is None:
fused_individual = torch.zeros_like(feat_whole.unsqueeze(0))
pooled_individual = torch.zeros_like(pooled_whole)
w_overall = main_prompt_ratio
w_individual_total = 1.0 - w_overall
fused_feat = feat_whole.unsqueeze(0) * w_overall + fused_individual * w_individual_total
fused_pooled = pooled_whole * w_overall + pooled_individual * w_individual_total
final_parts = []
main_pct = int(round(w_overall * 100))
final_parts.append(f"main({whole_content}){main_pct}%")
part_pcts = []
for seg, nw in zip(segments, norm_w):
pct = int(round(nw * w_individual_total * 100))
part_pcts.append(pct)
final_parts.append(f"{seg}{pct}%")
total_pct = main_pct + sum(part_pcts)
if total_pct != 100 and len(part_pcts) > 0:
diff = 100 - total_pct
part_pcts[0] += diff
final_parts = [f"main_prompt({whole_content}){main_pct}%"]
for i, seg in enumerate(segments):
final_parts.append(f"{seg}{part_pcts[i]}%")
prompt_info = "Weight:" + "|".join(final_parts)
return ([[fused_feat, {"pooled_output": fused_pooled}]], prompt_info)
class pre_qwenimage_PromptWeight:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"clip": ("CLIP",),
"prompt": ("STRING", {"multiline": True}),
},
"optional": {
"main_prompt_ratio": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("CONDITIONING", "STRING")
FUNCTION = "process"
def __init__(self):
pass
def get_pooled_from_cond(self, cond_feature):
if cond_feature is None:
return torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
device = cond_feature.device
dtype = cond_feature.dtype
embed_dim = int(cond_feature.shape[-1])
pooled = torch.mean(cond_feature, dim=0)
pooled = F.layer_norm(pooled, normalized_shape=[embed_dim])
return pooled
def clean_text(self, text):
clean_chars = ',,.。 \t\n'
cleaned = text.strip(clean_chars)
return cleaned
def parse_with_auto_pack(self, prompt):
pattern = re.compile(r'\[([^\]]+)@([0-9.]+)\]')
matches = list(pattern.finditer(prompt))
segments = []
weights = []
all_text_parts = []
last_end = 0
for match in matches:
start = match.start()
end = match.end()
prefix_text = prompt[last_end:start]
cleaned_prefix = self.clean_text(prefix_text)
if cleaned_prefix:
segments.append(cleaned_prefix)
weights.append(1.0)
all_text_parts.append(cleaned_prefix)
anchor_content = match.group(1).strip()
anchor_weight = float(match.group(2)) if match.group(2).replace('.','').isdigit() else 1.0
segments.append(anchor_content)
weights.append(anchor_weight)
all_text_parts.append(anchor_content)
last_end = end
suffix_text = prompt[last_end:]
cleaned_suffix = self.clean_text(suffix_text)
if cleaned_suffix:
segments.append(cleaned_suffix)
weights.append(1.0)
all_text_parts.append(cleaned_suffix)
whole_content = ','.join(all_text_parts)
return segments, weights, whole_content
def fuse_conditions_equal(self, cond_features, cond_strengths):
if len(cond_features) == 0 or len(cond_strengths) == 0:
return None, None, None
device = cond_features[0].device if cond_features[0] is not None else ("cuda" if torch.cuda.is_available() else "cpu")
dtype = cond_features[0].dtype if cond_features[0] is not None else torch.float16
total_w = sum(cond_strengths)
norm_w = [s / total_w if total_w > 1e-6 else 1.0/len(cond_strengths) for s in cond_strengths]
max_len = max([cf.shape[0] for cf in cond_features if cf is not None], default=1)
padded_feats = []
for cf in cond_features:
if cf is None:
cf = torch.zeros((max_len, 768), dtype=dtype, device=device)
pad_len = max_len - cf.shape[0]
if pad_len > 0:
pad = torch.zeros((pad_len, cf.shape[-1]), device=device, dtype=dtype)
padded_feat = torch.cat([cf, pad], dim=0)
else:
padded_feat = cf
padded_feats.append(padded_feat)
fused_feat = torch.zeros_like(padded_feats[0], device=device, dtype=dtype)
for feat, w in zip(padded_feats, norm_w):
fused_feat += feat * w
pooled_list = [self.get_pooled_from_cond(cf) for cf in cond_features]
fused_pooled = torch.zeros_like(pooled_list[0], device=device, dtype=dtype)
for pl, w in zip(pooled_list, norm_w):
fused_pooled += pl * w
return fused_feat.unsqueeze(0), fused_pooled, norm_w
def align_features(self, feat1, feat2):
len1 = feat1.shape[-2] if feat1.dim() >= 2 else 0
len2 = feat2.shape[-2] if feat2.dim() >= 2 else 0
if len1 == len2:
return feat1, feat2
target_len = max(len1, len2)
device = feat1.device
dtype = feat1.dtype
if feat1.dim() >= 2:
if len1 < target_len:
pad_size = target_len - len1
pad_shape = list(feat1.shape)
pad_shape[-2] = pad_size
pad = torch.zeros(pad_shape, device=device, dtype=dtype)
feat1_aligned = torch.cat([feat1, pad], dim=-2)
else:
feat1_aligned = feat1[..., :target_len, :]
else:
feat1_aligned = feat1
if feat2.dim() >= 2:
if len2 < target_len:
pad_size = target_len - len2
pad_shape = list(feat2.shape)
pad_shape[-2] = pad_size
pad = torch.zeros(pad_shape, device=device, dtype=dtype)
feat2_aligned = torch.cat([feat2, pad], dim=-2)
else:
feat2_aligned = feat2[..., :target_len, :]
else:
feat2_aligned = feat2
return feat1_aligned, feat2_aligned
def process(self, clip, prompt, main_prompt_ratio=0.5):
prompt_info = ""
segments, weights, whole_content = self.parse_with_auto_pack(prompt)
has_custom_weight = any(w != 1.0 for w in weights)
if not has_custom_weight and len(segments) > 0:
main_prompt_ratio = 1.0
if len(segments) == 0 or not whole_content:
tok_empty = clip.tokenize("")
c_empty = torch.zeros((1, 768), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
p_empty = torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
prompt_info = "Weight:main_prompt()0%|No valid individuals"
return ([[c_empty, {"pooled_output": p_empty}]], prompt_info)
cond_features = []
for seg in segments:
try:
tokens = clip.tokenize(seg)
c_out, p_out = clip.encode_from_tokens(tokens, return_pooled=True)
cond_feat = c_out.squeeze(0) if c_out is not None else None
except:
cond_feat = None
cond_features.append(cond_feat)
fused_individual, pooled_individual, norm_w = self.fuse_conditions_equal(cond_features, weights)
if fused_individual is None:
c_empty = torch.zeros((1, 768), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
p_empty = torch.zeros((768,), dtype=torch.float16, device="cuda" if torch.cuda.is_available() else "cpu")
prompt_info = "Weight:main_prompt()0%|No valid individuals"
return ([[c_empty, {"pooled_output": p_empty}]], prompt_info)
try:
tokens_whole = clip.tokenize(whole_content)
c_whole, p_whole = clip.encode_from_tokens(tokens_whole, return_pooled=True)
feat_whole = c_whole.squeeze(0) if c_whole is not None else torch.zeros_like(fused_individual.squeeze(0))
except:
feat_whole = torch.zeros_like(fused_individual.squeeze(0))
p_whole = torch.zeros_like(pooled_individual)
if p_whole is None:
p_whole = torch.zeros_like(pooled_individual)
if pooled_individual is None:
pooled_individual = torch.zeros_like(p_whole)
w_overall = main_prompt_ratio
w_individual_total = 1.0 - main_prompt_ratio
feat_whole_aligned, fused_individual_aligned = self.align_features(
feat_whole.unsqueeze(0),
fused_individual
)
fused_feat = feat_whole_aligned * w_overall + fused_individual_aligned * w_individual_total
fused_pooled = p_whole * w_overall + pooled_individual * w_individual_total
final_parts = []
main_pct = int(round(w_overall * 100))
final_parts.append(f"main_prompt({whole_content}){main_pct}%")
part_pcts = []
for seg, nw in zip(segments, norm_w):
pct = int(round(nw * w_individual_total * 100))
part_pcts.append(pct)
final_parts.append(f"{seg}{pct}%")
total_pct = main_pct + sum(part_pcts)
if total_pct != 100 and len(part_pcts) > 0:
diff = 100 - total_pct
part_pcts[0] += diff
final_parts = [f"main_prompt({whole_content}){main_pct}%"]
for i, seg in enumerate(segments):
final_parts.append(f"{seg}{part_pcts[i]}%")
prompt_info = "Weight:" + "|".join(final_parts)
return ([[fused_feat, {"pooled_output": fused_pooled}]], prompt_info)
class pre_zimage_PromptWeight:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"clip": ("CLIP",),
"prompt": ("STRING", {"multiline": True}),
},
"optional": {
"main_prompt_ratio": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("CONDITIONING", "STRING")
FUNCTION = "process"
CATEGORY = "Apt_Preset/chx_tool/conditioning"
def __init__(self):
pass
def get_pooled_from_cond(self, cond_feature):
if cond_feature is None:
# 不再硬编码768,改为动态获取默认维度(兼容不同CLIP模型)
default_dim = 768 # 默认值,会被实际张量覆盖
device = "cuda" if torch.cuda.is_available() else "cpu"
return torch.zeros((default_dim,), dtype=torch.float16, device=device)
device = cond_feature.device
dtype = cond_feature.dtype
embed_dim = int(cond_feature.shape[-1]) # 动态获取实际维度
pooled = torch.mean(cond_feature, dim=0)
pooled = F.layer_norm(pooled, normalized_shape=[embed_dim])
return pooled
def clean_text(self, text):
clean_chars = ',,.。 \t\n'
cleaned = text.strip(clean_chars)
return cleaned
def parse_with_auto_pack(self, prompt):
pattern = re.compile(r'\[([^\]]+)@([0-9.]+)\]')
matches = list(pattern.finditer(prompt))
segments = []
weights = []
all_text_parts = []
last_end = 0
for match in matches:
start = match.start()
end = match.end()
prefix_text = prompt[last_end:start]
cleaned_prefix = self.clean_text(prefix_text)
if cleaned_prefix:
segments.append(cleaned_prefix)
weights.append(1.0)
all_text_parts.append(cleaned_prefix)
anchor_content = match.group(1).strip()
anchor_weight = float(match.group(2)) if match.group(2).replace('.','').isdigit() else 1.0
segments.append(anchor_content)
weights.append(anchor_weight)
all_text_parts.append(anchor_content)
last_end = end
suffix_text = prompt[last_end:]
cleaned_suffix = self.clean_text(suffix_text)
if cleaned_suffix:
segments.append(cleaned_suffix)
weights.append(1.0)
all_text_parts.append(cleaned_suffix)
whole_content = ','.join(all_text_parts)
return segments, weights, whole_content
def fuse_conditions_equal(self, cond_features, cond_strengths):
if len(cond_features) == 0 or len(cond_strengths) == 0:
return None, None, None
# 确定基准维度(从第一个有效张量获取)
valid_feats = [cf for cf in cond_features if cf is not None]
if not valid_feats:
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.float16
embed_dim = 768
else:
device = valid_feats[0].device
dtype = valid_feats[0].dtype
embed_dim = valid_feats[0].shape[-1] # 动态获取嵌入维度
total_w = sum(cond_strengths)
norm_w = [s / total_w if total_w > 1e-6 else 1.0/len(cond_strengths) for s in cond_strengths]
max_len = max([cf.shape[0] for cf in valid_feats], default=1)
padded_feats = []
for cf in cond_features:
if cf is None:
# 使用动态获取的维度创建空张量
cf = torch.zeros((max_len, embed_dim), dtype=dtype, device=device)
pad_len = max_len - cf.shape[0]
if pad_len > 0:
# 填充张量使用实际的嵌入维度
pad = torch.zeros((pad_len, embed_dim), device=device, dtype=dtype)
padded_feat = torch.cat([cf, pad], dim=0)
else:
padded_feat = cf
padded_feats.append(padded_feat)
fused_feat = torch.zeros_like(padded_feats[0], device=device, dtype=dtype)
for feat, w in zip(padded_feats, norm_w):
fused_feat += feat * w
pooled_list = [self.get_pooled_from_cond(cf) for cf in cond_features]
fused_pooled = torch.zeros_like(pooled_list[0], device=device, dtype=dtype)
for pl, w in zip(pooled_list, norm_w):
fused_pooled += pl * w
return fused_feat.unsqueeze(0), fused_pooled, norm_w
def process(self, clip, prompt, main_prompt_ratio=0.5):
prompt_info = ""
segments, weights, whole_content = self.parse_with_auto_pack(prompt)
has_custom_weight = any(w != 1.0 for w in weights)
if not has_custom_weight and len(segments) > 0:
main_prompt_ratio = 1.0
if len(segments) == 0 or not whole_content:
# 动态确定空张量维度
embed_dim = 768
try:
# 尝试从clip获取实际维度
test_tokens = clip.tokenize("test")
test_c, _ = clip.encode_from_tokens(test_tokens, return_pooled=True)
embed_dim = test_c.shape[-1] if test_c is not None else 768
except:
pass
device = "cuda" if torch.cuda.is_available() else "cpu"
c_empty = torch.zeros((1, embed_dim), dtype=torch.float16, device=device)
p_empty = torch.zeros((embed_dim,), dtype=torch.float16, device=device)
prompt_info = "权重分配:main_prompt()0%|无有效个体"
return ([[c_empty, {"pooled_output": p_empty}]], prompt_info)
try:
tokens_whole = clip.tokenize(whole_content)
c_whole, p_whole = clip.encode_from_tokens(tokens_whole, return_pooled=True)
# 动态获取维度,不再硬编码77和768
embed_dim = c_whole.shape[-1] if c_whole is not None else 768
default_len = c_whole.shape[1] if c_whole is not None else 77
feat_whole = c_whole.squeeze(0) if c_whole is not None else torch.zeros((default_len, embed_dim), dtype=torch.float16, device=c_whole.device if c_whole is not None else ("cuda" if torch.cuda.is_available() else "cpu"))
pooled_whole = p_whole if p_whole is not None else self.get_pooled_from_cond(feat_whole)
except Exception as e:
print(f"处理完整提示词时出错: {e}")
embed_dim = 768
device = "cuda" if torch.cuda.is_available() else "cpu"
feat_whole = torch.zeros((77, embed_dim), dtype=torch.float16, device=device)
pooled_whole = torch.zeros((embed_dim,), dtype=torch.float16, device=device)
cond_features = []
for seg in segments:
try:
tokens = clip.tokenize(seg)
c_out, p_out = clip.encode_from_tokens(tokens, return_pooled=True)
cond_feat = c_out.squeeze(0) if c_out is not None else None
except Exception as e:
print(f"处理分段提示词 '{seg}' 时出错: {e}")
cond_feat = None
cond_features.append(cond_feat)
fused_individual, pooled_individual, norm_w = self.fuse_conditions_equal(cond_features, weights)
if fused_individual is None:
# 确保维度匹配
fused_individual = torch.zeros_like(feat_whole.unsqueeze(0))
pooled_individual = torch.zeros_like(pooled_whole)
w_overall = main_prompt_ratio
w_individual_total = 1.0 - w_overall
# 修复形状不匹配问题:动态获取维度并统一
# 获取两个张量的实际维度
feat_whole_dim = feat_whole.shape[-1]
fused_individual_dim = fused_individual.shape[-1]
# 确保维度一致
if feat_whole_dim != fused_individual_dim:
print(f"维度不匹配: feat_whole={feat_whole_dim}, fused_individual={fused_individual_dim},自动对齐到 {feat_whole_dim}")
# 重新调整fused_individual的维度
if fused_individual_dim < feat_whole_dim:
pad = torch.zeros((*fused_individual.shape[:-1], feat_whole_dim - fused_individual_dim),
device=fused_individual.device, dtype=fused_individual.dtype)
fused_individual = torch.cat([fused_individual, pad], dim=-1)
else:
fused_individual = fused_individual[..., :feat_whole_dim]
max_token_len = max(feat_whole.shape[0], fused_individual.shape[1])
device = feat_whole.device
dtype = feat_whole.dtype
# 填充 feat_whole 到 max_token_len(使用动态维度)
if feat_whole.shape[0] < max_token_len:
pad_len = max_token_len - feat_whole.shape[0]
pad_feat = torch.zeros((pad_len, feat_whole_dim), device=device, dtype=dtype)
feat_whole_padded = torch.cat([feat_whole, pad_feat], dim=0)
else:
feat_whole_padded = feat_whole
# 填充 fused_individual 到 max_token_len(使用动态维度)
if fused_individual.shape[1] < max_token_len:
pad_len = max_token_len - fused_individual.shape[1]
pad_feat = torch.zeros((1, pad_len, feat_whole_dim), device=device, dtype=dtype)
fused_individual_padded = torch.cat([fused_individual, pad_feat], dim=1)
else:
fused_individual_padded = fused_individual
# 确保最终计算的张量形状匹配
assert feat_whole_padded.unsqueeze(0).shape == fused_individual_padded.shape, \
f"张量形状不匹配: {feat_whole_padded.unsqueeze(0).shape} vs {fused_individual_padded.shape}"
fused_feat = feat_whole_padded.unsqueeze(0) * w_overall + fused_individual_padded * w_individual_total
fused_pooled = pooled_whole * w_overall + pooled_individual * w_individual_total
final_parts = []
main_pct = int(round(w_overall * 100))
final_parts.append(f"main({whole_content}){main_pct}%")
part_pcts = []
for seg, nw in zip(segments, norm_w):
pct = int(round(nw * w_individual_total * 100))
part_pcts.append(pct)
final_parts.append(f"{seg}{pct}%")
total_pct = main_pct + sum(part_pcts)
if total_pct != 100 and len(part_pcts) > 0:
diff = 100 - total_pct
part_pcts[0] += diff
final_parts = [f"main_prompt({whole_content}){main_pct}%"]
for i, seg in enumerate(segments):
final_parts.append(f"{seg}{part_pcts[i]}%")
prompt_info = "权重分配:" + "|".join(final_parts)
return ([[fused_feat, {"pooled_output": fused_pooled}]], prompt_info)
class Unit_PromptWeight:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"clip": ("CLIP",),
},
"optional": {
"pos": ("STRING", {"default": "", "multiline": True}),
"neg": ("STRING", {"default": "", "multiline": False}),
"mode": (["normal", "flux2.klein", "z-image", "qwen-image",], {"default": "normal"}),
"main_prompt_ratio": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ( "CONDITIONING", "CONDITIONING", )
RETURN_NAMES = ("positive", "negative", )
CATEGORY = "Apt_UnitPromptWeight"
FUNCTION = "sum_text_encode"
OUTPUT_NODE = True
DESCRIPTION = """
特征权重追加:[语义@权重数值0~10]
例如:个性特征默认权重1.0 ,<1则减弱,>1则加强
风格减弱: 3d风格,[女孩打伞,瀑布@0.2]
风格增强:[3d风格@0.2],女孩打伞,瀑布
main_prompt_ratio:整体权重占比越高,语义越偏向整体
"""
def sum_text_encode(self, clip, main_prompt_ratio=0.5, pos="", neg="bad", mode="normal"):
prompt_info = "默认:无提示词处理"
if neg.strip() == '':
neg = "bad"
negative = CLIPTextEncode().encode(clip, neg)[0]
if pos.strip() == '':
pos = "a cat"
if mode == "flux2.klein":
prompt_weight_processor = pre_Unit_PromptWeight()
positive, prompt_info = prompt_weight_processor.process(clip, pos, main_prompt_ratio)
elif mode == "z-image":
prompt_weight_processor = pre_zimage_PromptWeight()
positive, prompt_info = prompt_weight_processor.process(clip, pos, main_prompt_ratio)
elif mode == "qwen-image":
prompt_weight_processor = pre_qwenimage_PromptWeight()
positive, prompt_info = prompt_weight_processor.process(clip, pos, main_prompt_ratio)
else:
positive = CLIPTextEncode().encode(clip, pos)[0]
prompt_info = f"原生编码:{pos[:20]}... | 模式:{mode}"
return { "ui": {"text": (prompt_info,)}, "result": (positive, negative, ) }