Improve random prompt controls
This commit is contained in:
@@ -19,6 +19,7 @@ The node is registered as:
|
||||
- `prompt_builder / SxCP Accumulator`
|
||||
- `prompt_builder / SxCP Category Preset`
|
||||
- `prompt_builder / SxCP Cast Control`
|
||||
- `prompt_builder / SxCP Cast Bias`
|
||||
- `prompt_builder / SxCP Generation Profile`
|
||||
- `prompt_builder / SxCP Ethnicity List`
|
||||
- `prompt_builder / SxCP Hair Length`
|
||||
@@ -55,9 +56,15 @@ node. For cleaner workflows, use the split nodes:
|
||||
women casual, men casual, couple casual, provocative erotic, or hardcore pose.
|
||||
- `SxCP Cast Control` outputs `cast_config` plus raw `women_count` and
|
||||
`men_count`, so couple/two-women/two-men/group setups can be reused.
|
||||
- `SxCP Cast Bias` outputs the same cast sockets, but chooses counts from
|
||||
weighted lists. With `women_start_count=1`, `women_weights=0.6,0.2` means
|
||||
60% one woman and 20% two women; with `men_start_count=0`,
|
||||
`men_weights=0.5,0.3` means 50% no man and 30% one man.
|
||||
- `SxCP Generation Profile` outputs `generation_profile` for common behavior
|
||||
presets such as casual-clean, evocative-softcore, hardcore-intense,
|
||||
Krea2-friendly, or Flux-original.
|
||||
Krea2-friendly, or Flux-original. Its clothing and pose overrides can be
|
||||
`random`, and `expression_intensity_mode=random` makes expression strength
|
||||
vary by seeded row.
|
||||
- `SxCP Ethnicity List` outputs a reusable ethnicity filter from checkboxes.
|
||||
It includes broad groups and narrower European/Mediterranean groups such as
|
||||
`french_european`, `germanic_european`, `nordic_european`,
|
||||
@@ -539,22 +546,22 @@ Options:
|
||||
The node keeps the original generator controls:
|
||||
|
||||
- `category`: `auto_weighted`, `woman`, `man`, `couple`, `group_or_layout`, or a custom JSON category.
|
||||
- `clothing`: `full` or `minimal`.
|
||||
- `minimal_clothing_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes minimal/full clothing.
|
||||
- `clothing`: `random`, `full`, or `minimal`.
|
||||
- `minimal_clothing_ratio`: `-1` disables ratio mixing; `0.0` to `1.0` mixes minimal/full clothing when `clothing` is fixed.
|
||||
- `ethnicity`: `any`, `european`, `mediterranean_mena`, `latina`,
|
||||
`east_asian`, `southeast_asian`, `south_asian`, `black_african`,
|
||||
`indigenous`, `mixed`, `asian`, or `white_asian`. Combined filter strings
|
||||
such as `latina+south_asian` are also accepted in config JSON.
|
||||
- `poses`: `standard` or `evocative`.
|
||||
- `poses`: `random`, `standard`, or `evocative`.
|
||||
- `expression_enabled`: disable facial-expression text entirely for this row.
|
||||
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
||||
- `expression_intensity`: `-1` randomizes per row; `0.0` favors mild, neutral, controlled expressions;
|
||||
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
||||
intense expressions available in the selected category. This affects custom
|
||||
JSON categories such as `Provocative erotic clothes` and `Hardcore sexual
|
||||
poses`.
|
||||
- `standard_pose_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes standard/evocative poses.
|
||||
- `standard_pose_ratio`: `-1` disables ratio mixing; `0.0` to `1.0` mixes standard/evocative poses when `poses` is fixed.
|
||||
- `backside_bias`: `0.0` to `1.0`, applies to evocative single-subject poses.
|
||||
- `figure`: `curvy`, `balanced`, `bombshell`.
|
||||
- `figure`: `random`, `curvy`, `balanced`, `bombshell`.
|
||||
- In split workflows, use `SxCP Advanced Filters` checkboxes instead of negative
|
||||
toggles. Black/African and plus-size are positive include choices there.
|
||||
- Optional `camera_config`: connect `SxCP Camera Control` or
|
||||
@@ -813,7 +820,7 @@ Seed values in `auto` mode:
|
||||
|
||||
Axes:
|
||||
|
||||
- `category_seed`: custom category selection when `custom_random` is used.
|
||||
- `category_seed`: custom category selection when `custom_random` is used; also drives `SxCP Cast Bias` when its `seed_config` input is connected.
|
||||
- `subcategory_seed`: random subcategory selection.
|
||||
- `content_seed`: generated item content, such as outfit wording.
|
||||
- `person_seed`: appearance/person selection.
|
||||
|
||||
+156
-12
@@ -114,7 +114,8 @@ COMMON_INPUT_TOOLTIPS = {
|
||||
"eyes": "Manual eye description.",
|
||||
"descriptor_detail": "How detailed this character's descriptor should be. Men usually work better compact.",
|
||||
"expression_enabled": "Master expression toggle for this generator or character.",
|
||||
"expression_intensity": "Expression intensity from 0 to 1. Use -1 on slots to inherit the generator setting.",
|
||||
"expression_intensity": "Expression intensity from 0 to 1. On the direct builder, -1 randomizes per row; on slots, -1 inherits the generator setting.",
|
||||
"expression_intensity_mode": "For Generation Profile, choose profile_default, random, or fixed value from expression_intensity.",
|
||||
"softcore_expression_intensity": "Optional expression intensity override for this character in softcore prompts. -1 inherits.",
|
||||
"hardcore_expression_intensity": "Optional expression intensity override for this character in hardcore prompts. -1 inherits.",
|
||||
"presence_mode": "Controls whether the character is visible, implied POV, or otherwise present.",
|
||||
@@ -151,8 +152,8 @@ COMMON_INPUT_TOOLTIPS = {
|
||||
"missing_behavior": "What to do when the requested switch input is not connected: use fallback, output none, clamp, or wrap.",
|
||||
"fallback": "Optional value used by SxCP Index Switch when the requested input is missing and missing_behavior is fallback.",
|
||||
"route_value": "Value routed to output_N when mode is route_output.",
|
||||
"clothing": "Built-in clothing density for legacy direct generation. Category/profile nodes can override this.",
|
||||
"poses": "Built-in pose pool for legacy direct generation.",
|
||||
"clothing": "Built-in clothing density for legacy direct generation. random picks full/minimal from the seeded row.",
|
||||
"poses": "Built-in pose pool for legacy direct generation. random picks standard/evocative from the seeded row.",
|
||||
"backside_bias": "Legacy bias toward rear/backside poses where that category supports it.",
|
||||
"minimal_clothing_ratio": "Legacy weighted ratio override. -1 keeps the category/profile default.",
|
||||
"standard_pose_ratio": "Legacy weighted ratio override. -1 keeps the category/profile default.",
|
||||
@@ -161,6 +162,11 @@ COMMON_INPUT_TOOLTIPS = {
|
||||
"poses_override": "Override the profile pose setting, or leave profile_default.",
|
||||
"trigger_policy": "Controls whether the profile prepends the trigger token.",
|
||||
"cast_mode": "Preset cast shape. Custom counts are used when the preset allows them.",
|
||||
"women_weights": "Comma-separated count weights. First value maps to women_start_count, second to +1, and so on.",
|
||||
"men_weights": "Comma-separated count weights. First value maps to men_start_count, second to +1, and so on.",
|
||||
"women_start_count": "Woman count represented by the first women_weights value.",
|
||||
"men_start_count": "Man count represented by the first men_weights value.",
|
||||
"empty_behavior": "What to do if the weighted pick selects zero women and zero men.",
|
||||
"preset": "Category preset for common workflow lanes.",
|
||||
"camera_mode": "Camera style preset.",
|
||||
"shot_size": "How much of the body/frame should be visible.",
|
||||
@@ -203,6 +209,13 @@ NODE_INPUT_TOOLTIPS = {
|
||||
"SxCPSeedLocker": {
|
||||
"reroll_axis": "Choose the one axis to change while the rest stays locked. Use pose for sexual pose, scene for location, person for appearance.",
|
||||
},
|
||||
"SxCPCastBias": {
|
||||
"seed": "Fixed cast-bias seed. Use -1 for a fresh cast each queue, or connect Global Seed/Seed Locker through seed_config.",
|
||||
"seed_config": "Optional seed config. The category seed controls weighted cast selection.",
|
||||
"women_weights": "Example with women_start_count=1: 0.6,0.25,0.1 means 60% one woman, 25% two women, 10% three women.",
|
||||
"men_weights": "Example with men_start_count=0: 0.5,0.35,0.1 means 50% no man, 35% one man, 10% two men.",
|
||||
"empty_behavior": "Prevents accidental empty casts when both weighted pools pick zero.",
|
||||
},
|
||||
"SxCPSDXLBucketSize": {
|
||||
"orientation": "Bucket orientation filter. any uses the full table; portrait/square/landscape restrict random selection.",
|
||||
"seed": "Fixed bucket seed. Use -1 for a fresh random bucket each queue, or connect Global Seed for reproducible sizes.",
|
||||
@@ -597,13 +610,13 @@ class SxCPPromptBuilder:
|
||||
"row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}),
|
||||
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
||||
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
||||
"clothing": (["full", "minimal"], {"default": "full"}),
|
||||
"clothing": (["random", "full", "minimal"], {"default": "random"}),
|
||||
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||
"poses": (["standard", "evocative"], {"default": "standard"}),
|
||||
"poses": (["random", "standard", "evocative"], {"default": "random"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"backside_bias": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||
"figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}),
|
||||
"women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||
"men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
@@ -1154,15 +1167,137 @@ class SxCPCastControl:
|
||||
return config, parsed["women_count"], parsed["men_count"], summary
|
||||
|
||||
|
||||
class SxCPCastBias:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"default": -1, "min": -1, "max": 0xFFFFFFFF, "step": 1}),
|
||||
"row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}),
|
||||
"women_weights": ("STRING", {"default": "0.60,0.25,0.10,0.05"}),
|
||||
"women_start_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}),
|
||||
"men_weights": ("STRING", {"default": "0.45,0.40,0.10,0.05"}),
|
||||
"men_start_count": ("INT", {"default": 0, "min": 0, "max": 12, "step": 1}),
|
||||
"empty_behavior": (["force_one_woman", "force_one_man", "allow_empty"], {"default": "force_one_woman"}),
|
||||
},
|
||||
"optional": {
|
||||
"seed_config": (SXCP_SEED_CONFIG,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = (SXCP_CAST_CONFIG, "INT", "INT", "STRING")
|
||||
RETURN_NAMES = ("cast_config", "women_count", "men_count", "cast_summary")
|
||||
FUNCTION = "build"
|
||||
CATEGORY = "prompt_builder"
|
||||
|
||||
@staticmethod
|
||||
def _configured_cast_seed(seed_config):
|
||||
if not seed_config:
|
||||
return None
|
||||
if isinstance(seed_config, dict):
|
||||
raw = seed_config
|
||||
else:
|
||||
try:
|
||||
raw = json.loads(str(seed_config))
|
||||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
return None
|
||||
if not isinstance(raw, dict):
|
||||
return None
|
||||
for key in ("category_seed", "content_seed", "role_seed", "seed", "global_seed"):
|
||||
try:
|
||||
value = int(raw.get(key))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if value >= 0:
|
||||
return value
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _weight_pairs(weights_text, start_count):
|
||||
pairs = []
|
||||
start = max(0, min(12, int(start_count)))
|
||||
parts = str(weights_text or "").replace("\n", ",").split(",")
|
||||
for offset, raw in enumerate(parts):
|
||||
count = start + offset
|
||||
if count > 12:
|
||||
break
|
||||
try:
|
||||
weight = float(raw.strip())
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if weight > 0:
|
||||
pairs.append((count, weight))
|
||||
return pairs or [(start, 1.0)]
|
||||
|
||||
@staticmethod
|
||||
def _weighted_count(rng, pairs):
|
||||
total = sum(weight for _count, weight in pairs)
|
||||
point = rng.random() * total
|
||||
upto = 0.0
|
||||
for count, weight in pairs:
|
||||
upto += weight
|
||||
if point <= upto:
|
||||
return int(count)
|
||||
return int(pairs[-1][0])
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *args, **kwargs):
|
||||
seed_value = kwargs.get("seed")
|
||||
if seed_value is None and args:
|
||||
seed_value = args[0]
|
||||
seed_config = kwargs.get("seed_config", "")
|
||||
if not seed_config and len(args) > 7:
|
||||
seed_config = args[7]
|
||||
try:
|
||||
seed = int(seed_value)
|
||||
except (TypeError, ValueError):
|
||||
seed = -1
|
||||
if seed < 0 and cls._configured_cast_seed(seed_config) is None:
|
||||
return random.random()
|
||||
return tuple(args), tuple(sorted(kwargs.items()))
|
||||
|
||||
def build(
|
||||
self,
|
||||
seed,
|
||||
row_number,
|
||||
women_weights,
|
||||
women_start_count,
|
||||
men_weights,
|
||||
men_start_count,
|
||||
empty_behavior,
|
||||
seed_config="",
|
||||
):
|
||||
configured_seed = self._configured_cast_seed(seed_config)
|
||||
if configured_seed is None and int(seed) < 0:
|
||||
rng = random.Random(random.getrandbits(64))
|
||||
else:
|
||||
cast_seed = configured_seed if configured_seed is not None else int(seed)
|
||||
rng = random.Random(f"sxcp_cast_bias:{cast_seed}:{int(row_number)}")
|
||||
women_pairs = self._weight_pairs(women_weights, women_start_count)
|
||||
men_pairs = self._weight_pairs(men_weights, men_start_count)
|
||||
women_count = self._weighted_count(rng, women_pairs)
|
||||
men_count = self._weighted_count(rng, men_pairs)
|
||||
if women_count + men_count == 0:
|
||||
if empty_behavior == "force_one_man":
|
||||
men_count = 1
|
||||
elif empty_behavior != "allow_empty":
|
||||
women_count = 1
|
||||
config = build_cast_config_json(cast_mode="custom_counts", women_count=women_count, men_count=men_count)
|
||||
parsed = json.loads(config)
|
||||
summary = f"weighted cast: {parsed['women_count']} women, {parsed['men_count']} men"
|
||||
return config, parsed["women_count"], parsed["men_count"], summary
|
||||
|
||||
|
||||
class SxCPGenerationProfile:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"profile": (generation_profile_choices(), {"default": "balanced"}),
|
||||
"clothing_override": (["profile_default", "full", "minimal"], {"default": "profile_default"}),
|
||||
"poses_override": (["profile_default", "standard", "evocative"], {"default": "profile_default"}),
|
||||
"clothing_override": (["profile_default", "random", "full", "minimal"], {"default": "profile_default"}),
|
||||
"poses_override": (["profile_default", "random", "standard", "evocative"], {"default": "profile_default"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity_mode": (["profile_default", "random", "fixed"], {"default": "profile_default"}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"backside_bias": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
@@ -1182,6 +1317,7 @@ class SxCPGenerationProfile:
|
||||
clothing_override,
|
||||
poses_override,
|
||||
expression_enabled,
|
||||
expression_intensity_mode,
|
||||
expression_intensity,
|
||||
backside_bias,
|
||||
minimal_clothing_ratio,
|
||||
@@ -1193,6 +1329,7 @@ class SxCPGenerationProfile:
|
||||
clothing_override=clothing_override,
|
||||
poses_override=poses_override,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity_mode=expression_intensity_mode,
|
||||
expression_intensity=expression_intensity,
|
||||
backside_bias=backside_bias,
|
||||
minimal_clothing_ratio=minimal_clothing_ratio,
|
||||
@@ -1200,7 +1337,12 @@ class SxCPGenerationProfile:
|
||||
trigger_policy=trigger_policy,
|
||||
)
|
||||
parsed = json.loads(config)
|
||||
expression_summary = "expression disabled" if not parsed.get("expression_enabled", True) else f"expression {parsed['expression_intensity']}"
|
||||
if not parsed.get("expression_enabled", True):
|
||||
expression_summary = "expression disabled"
|
||||
elif float(parsed.get("expression_intensity", 0.5)) < 0:
|
||||
expression_summary = "expression random"
|
||||
else:
|
||||
expression_summary = f"expression {parsed['expression_intensity']}"
|
||||
summary = f"{parsed['profile']}: {parsed['clothing']}, {parsed['poses']}, {expression_summary}"
|
||||
return config, summary
|
||||
|
||||
@@ -1220,7 +1362,7 @@ class SxCPAdvancedFilters:
|
||||
"include_indigenous": ("BOOLEAN", {"default": True}),
|
||||
"include_mixed": ("BOOLEAN", {"default": True}),
|
||||
"include_plus_size": ("BOOLEAN", {"default": True}),
|
||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||
"figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2499,7 +2641,7 @@ class SxCPInstaOFPromptPair:
|
||||
"start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}),
|
||||
"seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
||||
"ethnicity": (ethnicity_choices(), {"default": "any"}),
|
||||
"figure": (["curvy", "balanced", "bombshell"], {"default": "curvy"}),
|
||||
"figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}),
|
||||
"trigger": ("STRING", {"default": "sxcpinup_coloredpencil"}),
|
||||
"prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
@@ -2602,6 +2744,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"SxCPQwenCameraTranslator": SxCPQwenCameraTranslator,
|
||||
"SxCPCategoryPreset": SxCPCategoryPreset,
|
||||
"SxCPCastControl": SxCPCastControl,
|
||||
"SxCPCastBias": SxCPCastBias,
|
||||
"SxCPGenerationProfile": SxCPGenerationProfile,
|
||||
"SxCPEthnicityList": SxCPEthnicityList,
|
||||
"SxCPHairLength": SxCPHairLength,
|
||||
@@ -2643,6 +2786,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SxCPQwenCameraTranslator": "SxCP Qwen Camera Translator",
|
||||
"SxCPCategoryPreset": "SxCP Category Preset",
|
||||
"SxCPCastControl": "SxCP Cast Control",
|
||||
"SxCPCastBias": "SxCP Cast Bias",
|
||||
"SxCPGenerationProfile": "SxCP Generation Profile",
|
||||
"SxCPEthnicityList": "SxCP Ethnicity List",
|
||||
"SxCPHairLength": "SxCP Hair Length",
|
||||
|
||||
+52
-14
@@ -1372,6 +1372,7 @@ def build_generation_profile_json(
|
||||
profile: str = "balanced",
|
||||
clothing_override: str = "profile_default",
|
||||
poses_override: str = "profile_default",
|
||||
expression_intensity_mode: str = "profile_default",
|
||||
expression_intensity: float = -1.0,
|
||||
backside_bias: float = -1.0,
|
||||
minimal_clothing_ratio: float = -1.0,
|
||||
@@ -1381,12 +1382,14 @@ def build_generation_profile_json(
|
||||
) -> str:
|
||||
profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
|
||||
config = dict(GENERATION_PROFILE_PRESETS[profile])
|
||||
if clothing_override in ("full", "minimal"):
|
||||
if clothing_override in ("full", "minimal", "random"):
|
||||
config["clothing"] = clothing_override
|
||||
if poses_override in ("standard", "evocative"):
|
||||
if poses_override in ("standard", "evocative", "random"):
|
||||
config["poses"] = poses_override
|
||||
config["expression_enabled"] = not _is_false(expression_enabled)
|
||||
if float(expression_intensity) >= 0:
|
||||
if expression_intensity_mode == "random":
|
||||
config["expression_intensity"] = -1.0
|
||||
elif expression_intensity_mode == "fixed" and float(expression_intensity) >= 0:
|
||||
config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
|
||||
if float(backside_bias) >= 0:
|
||||
config["backside_bias"] = _clamped_float(backside_bias, config["backside_bias"])
|
||||
@@ -1417,10 +1420,14 @@ def _parse_generation_profile(profile_config: str | dict[str, Any] | None) -> di
|
||||
profile = str(raw.get("profile") or "balanced")
|
||||
parsed = dict(GENERATION_PROFILE_PRESETS.get(profile, GENERATION_PROFILE_PRESETS["balanced"]))
|
||||
parsed.update(raw)
|
||||
parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal") else "full"
|
||||
parsed["poses"] = parsed["poses"] if parsed.get("poses") in ("standard", "evocative") else "standard"
|
||||
parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal", "random") else "full"
|
||||
parsed["poses"] = parsed["poses"] if parsed.get("poses") in ("standard", "evocative", "random") else "standard"
|
||||
parsed["expression_enabled"] = not _is_false(parsed.get("expression_enabled", True))
|
||||
parsed["expression_intensity"] = _clamped_float(parsed.get("expression_intensity"), 0.5)
|
||||
try:
|
||||
raw_expression_intensity = float(parsed.get("expression_intensity"))
|
||||
except (TypeError, ValueError):
|
||||
raw_expression_intensity = 0.5
|
||||
parsed["expression_intensity"] = -1.0 if raw_expression_intensity < 0 else _clamped_float(raw_expression_intensity, 0.5)
|
||||
parsed["backside_bias"] = _clamped_float(parsed.get("backside_bias"), 0.0)
|
||||
parsed["minimal_clothing_ratio"] = _clamped_float(parsed.get("minimal_clothing_ratio"), -1.0, -1.0, 1.0)
|
||||
parsed["standard_pose_ratio"] = _clamped_float(parsed.get("standard_pose_ratio"), -1.0, -1.0, 1.0)
|
||||
@@ -1469,7 +1476,7 @@ def build_filter_config_json(
|
||||
{
|
||||
"ethnicity": ethnicity,
|
||||
"ethnicity_includes": selected_ethnicities,
|
||||
"figure": figure if figure in ("curvy", "balanced", "bombshell") else "curvy",
|
||||
"figure": figure if figure in ("curvy", "balanced", "bombshell", "random") else "curvy",
|
||||
"include_plus_size": bool(include_plus_size),
|
||||
"include_black_african": bool(include_black_african),
|
||||
"no_plus_women": not bool(include_plus_size) or bool(no_plus_women),
|
||||
@@ -1620,7 +1627,7 @@ def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str
|
||||
raise ValueError("filter_config must be a JSON object")
|
||||
parsed = {**defaults, **raw}
|
||||
parsed["ethnicity"] = normalize_ethnicity_filter(parsed.get("ethnicity"), "any")
|
||||
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell") else "curvy"
|
||||
parsed["figure"] = parsed["figure"] if parsed.get("figure") in ("curvy", "balanced", "bombshell", "random") else "curvy"
|
||||
parsed["include_plus_size"] = bool(parsed.get("include_plus_size"))
|
||||
parsed["include_black_african"] = bool(parsed.get("include_black_african"))
|
||||
parsed["no_plus_women"] = bool(parsed.get("no_plus_women"))
|
||||
@@ -2765,17 +2772,37 @@ def _row_seed(seed: int, row_number: int, salt: int = 0) -> int:
|
||||
|
||||
|
||||
def _pick_clothing_mode(rng: random.Random, clothing: str, minimal_ratio: float | None) -> str:
|
||||
if clothing == "random":
|
||||
return "minimal" if rng.random() < 0.5 else "full"
|
||||
if minimal_ratio is None:
|
||||
return clothing
|
||||
return "minimal" if rng.random() < minimal_ratio else "full"
|
||||
|
||||
|
||||
def _pick_pose_mode(rng: random.Random, poses: str, standard_ratio: float | None) -> str:
|
||||
if poses == "random":
|
||||
return "standard" if rng.random() < 0.5 else "evocative"
|
||||
if standard_ratio is None:
|
||||
return poses
|
||||
return "standard" if rng.random() < standard_ratio else "evocative"
|
||||
|
||||
|
||||
def _pick_figure_bias(rng: random.Random, figure: str) -> str:
|
||||
if figure in ("curvy", "balanced", "bombshell"):
|
||||
return figure
|
||||
return g.choose(rng, ["curvy", "balanced", "bombshell"])
|
||||
|
||||
|
||||
def _pick_expression_intensity(rng: random.Random, expression_intensity: Any) -> tuple[float, str]:
|
||||
try:
|
||||
value = float(expression_intensity)
|
||||
except (TypeError, ValueError):
|
||||
return 0.5, "default"
|
||||
if value < 0:
|
||||
return round(rng.random(), 2), "random"
|
||||
return _clamped_float(value, 0.5), "input"
|
||||
|
||||
|
||||
def _build_auto_weighted_row(
|
||||
row_number: int,
|
||||
start_index: int,
|
||||
@@ -5424,6 +5451,7 @@ def _build_custom_row(
|
||||
seed_config: dict[str, int],
|
||||
expression_enabled: bool,
|
||||
expression_intensity: float,
|
||||
expression_intensity_source: str = "input",
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
expression_phase: str = "",
|
||||
@@ -5512,7 +5540,7 @@ def _build_custom_row(
|
||||
role_graph = _pov_role_graph_prompt(source_role_graph, pov_character_labels)
|
||||
cast_descriptors: list[str] = []
|
||||
cast_descriptor_text = ""
|
||||
expression_intensity_source = "input"
|
||||
expression_intensity_source = expression_intensity_source or "input"
|
||||
expression_disabled = not bool(expression_enabled)
|
||||
if expression_disabled:
|
||||
expression_intensity_source = "disabled"
|
||||
@@ -5774,15 +5802,24 @@ def build_prompt(
|
||||
row_number = max(1, int(row_number))
|
||||
start_index = max(1, int(start_index))
|
||||
seed = int(seed)
|
||||
clothing = clothing if clothing in ("full", "minimal") else "full"
|
||||
ethnicity = normalize_ethnicity_filter(ethnicity, "any")
|
||||
poses = poses if poses in ("standard", "evocative") else "standard"
|
||||
figure = figure if figure in ("curvy", "balanced", "bombshell") else "curvy"
|
||||
expression_enabled = not _is_false(expression_enabled)
|
||||
minimal_ratio = _ratio_or_none(minimal_clothing_ratio)
|
||||
pose_ratio = _ratio_or_none(standard_pose_ratio)
|
||||
expression_intensity = _clamped_float(expression_intensity, 0.5)
|
||||
parsed_seed_config = _parse_seed_config(seed_config)
|
||||
content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number)
|
||||
pose_axis_rng = _axis_rng(parsed_seed_config, "pose", seed, row_number)
|
||||
person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number)
|
||||
expression_rng = _axis_rng(parsed_seed_config, "expression", seed, row_number)
|
||||
clothing = clothing if clothing in ("full", "minimal", "random") else "full"
|
||||
poses = poses if poses in ("standard", "evocative", "random") else "standard"
|
||||
figure = figure if figure in ("curvy", "balanced", "bombshell", "random") else "curvy"
|
||||
clothing = _pick_clothing_mode(content_rng, clothing, minimal_ratio)
|
||||
poses = _pick_pose_mode(pose_axis_rng, poses, pose_ratio)
|
||||
figure = _pick_figure_bias(person_rng, figure)
|
||||
minimal_ratio = None
|
||||
pose_ratio = None
|
||||
expression_intensity, expression_intensity_source = _pick_expression_intensity(expression_rng, expression_intensity)
|
||||
|
||||
exact_custom_subcategory = bool(subcategory and subcategory != RANDOM_SUBCATEGORY and " / " in subcategory)
|
||||
|
||||
@@ -5834,6 +5871,7 @@ def build_prompt(
|
||||
parsed_seed_config,
|
||||
expression_enabled,
|
||||
expression_intensity,
|
||||
expression_intensity_source,
|
||||
character_profile,
|
||||
character_cast,
|
||||
expression_phase,
|
||||
@@ -5850,7 +5888,7 @@ def build_prompt(
|
||||
row["negative_prompt"] = _combined_negative(row.get("negative_prompt", g.NEGATIVE_PROMPT), extra_negative)
|
||||
row["trigger"] = active_trigger
|
||||
row.setdefault("expression_intensity", expression_intensity)
|
||||
row.setdefault("expression_intensity_source", "input")
|
||||
row.setdefault("expression_intensity_source", expression_intensity_source)
|
||||
return row
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user