Add gender-specific character slot nodes

This commit is contained in:
2026-06-24 15:30:48 +02:00
parent a7743cfd4b
commit cad2f4b4e4
3 changed files with 196 additions and 11 deletions
+20 -11
View File
@@ -14,6 +14,8 @@ The node is registered as:
- `prompt_builder / SxCP Generation Profile` - `prompt_builder / SxCP Generation Profile`
- `prompt_builder / SxCP Advanced Filters` - `prompt_builder / SxCP Advanced Filters`
- `prompt_builder / SxCP Prompt Builder From Configs` - `prompt_builder / SxCP Prompt Builder From Configs`
- `prompt_builder / SxCP Woman Slot`
- `prompt_builder / SxCP Man Slot`
- `prompt_builder / SxCP Character Slot` - `prompt_builder / SxCP Character Slot`
- `prompt_builder / SxCP Character Profile Save` - `prompt_builder / SxCP Character Profile Save`
- `prompt_builder / SxCP Character Profile Load` - `prompt_builder / SxCP Character Profile Load`
@@ -53,7 +55,7 @@ The practical compact workflow is:
`Category Preset` + `Cast Control` + `Generation Profile` + optional `Category Preset` + `Cast Control` + `Generation Profile` + optional
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control`, `Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control`,
`Character Slot`, and `Character Profile` `Woman Slot` / `Man Slot`, and `Character Profile`
into `Prompt Builder From Configs`. into `Prompt Builder From Configs`.
An importable default workflow is included at An importable default workflow is included at
@@ -70,11 +72,18 @@ as one long chain:
## Character Profiles ## Character Profiles
`SxCP Character Slot` is the scalable per-participant control node. Each slot `SxCP Woman Slot` and `SxCP Man Slot` are the scalable per-participant control
defines one woman or man with optional overrides for age, ethnicity, figure, nodes. `Cast Control` still decides how many women and men are generated; slot
body/body phrase, skin, hair, and eyes. Leave any field on `random` or blank to nodes decide who those people are. Each slot defines one participant with
let the generator fill that part from the normal pools; set exact values only optional overrides for age, ethnicity, body/body phrase, skin, hair, and eyes.
where you want control. Leave any field on `random` or blank to let the generator fill that part from
the normal pools; set exact values only where you want control.
Use `Woman Slot` for women because it exposes woman-focused body choices and a
`figure_bias` selector. Use `Man Slot` for men because it exposes man-focused
body choices and omits figure bias. The older generic `SxCP Character Slot`
remains available for compatibility and manual mixed use, but the gendered
slots are the cleaner default.
Slots are chainable through the `character_cast` input/output. In automatic Slots are chainable through the `character_cast` input/output. In automatic
label mode, the slot closest to the final generator becomes `A` for its gender, label mode, the slot closest to the final generator becomes `A` for its gender,
@@ -225,11 +234,11 @@ and location. The generated positive prompts are still standalone: each output
lists the relevant cast descriptors directly and does not depend on the image lists the relevant cast descriptors directly and does not depend on the image
model carrying context from another prompt. model carrying context from another prompt.
For per-character control, chain `SxCP Character Slot` nodes into the pair For per-character control, chain `SxCP Woman Slot` and `SxCP Man Slot` nodes
node's `character_cast` input. The nearest woman slot controls the shared into the pair node's `character_cast` input. The nearest woman slot controls
primary creator (`Woman A`) in both softcore and hardcore outputs; additional the shared primary creator (`Woman A`) in both softcore and hardcore outputs;
woman/man slots fill partner descriptors before random fallback descriptors are additional woman/man slots fill partner descriptors before random fallback
used. descriptors are used.
It outputs: It outputs:
+132
View File
@@ -34,7 +34,9 @@ try:
character_ethnicity_choices, character_ethnicity_choices,
character_figure_choices, character_figure_choices,
character_label_choices, character_label_choices,
character_man_body_choices,
character_profile_choices, character_profile_choices,
character_woman_body_choices,
ethnicity_choices, ethnicity_choices,
generation_profile_choices, generation_profile_choices,
load_character_profile_json, load_character_profile_json,
@@ -74,7 +76,9 @@ except ImportError:
character_ethnicity_choices, character_ethnicity_choices,
character_figure_choices, character_figure_choices,
character_label_choices, character_label_choices,
character_man_body_choices,
character_profile_choices, character_profile_choices,
character_woman_body_choices,
ethnicity_choices, ethnicity_choices,
generation_profile_choices, generation_profile_choices,
load_character_profile_json, load_character_profile_json,
@@ -606,6 +610,130 @@ class SxCPCharacterSlot:
return result["character_cast"], result["character_slot"], result["summary"], result["status"] return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPWomanSlot:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"enabled": ("BOOLEAN", {"default": True}),
"label": (character_label_choices(), {"default": "auto_chain"}),
"age": (character_age_choices(), {"default": "random"}),
"manual_age": ("STRING", {"default": ""}),
"ethnicity": (character_ethnicity_choices(), {"default": "random"}),
"figure_bias": (character_figure_choices(), {"default": "random"}),
"body": (character_woman_body_choices(), {"default": "random"}),
"manual_body": ("STRING", {"default": ""}),
"body_phrase": ("STRING", {"default": ""}),
"skin": ("STRING", {"default": ""}),
"hair": ("STRING", {"default": ""}),
"eyes": ("STRING", {"default": ""}),
},
"optional": {
"character_cast": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
FUNCTION = "build"
CATEGORY = "prompt_builder"
def build(
self,
enabled,
label,
age,
manual_age,
ethnicity,
figure_bias,
body,
manual_body,
body_phrase,
skin,
hair,
eyes,
character_cast="",
):
result = build_character_slot_json(
subject_type="woman",
label=label,
age=age,
manual_age=manual_age,
ethnicity=ethnicity,
figure=figure_bias,
body=body,
manual_body=manual_body,
body_phrase=body_phrase,
skin=skin,
hair=hair,
eyes=eyes,
enabled=enabled,
character_cast=character_cast or "",
)
return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPManSlot:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"enabled": ("BOOLEAN", {"default": True}),
"label": (character_label_choices(), {"default": "auto_chain"}),
"age": (character_age_choices(), {"default": "random"}),
"manual_age": ("STRING", {"default": ""}),
"ethnicity": (character_ethnicity_choices(), {"default": "random"}),
"body": (character_man_body_choices(), {"default": "random"}),
"manual_body": ("STRING", {"default": ""}),
"body_phrase": ("STRING", {"default": ""}),
"skin": ("STRING", {"default": ""}),
"hair": ("STRING", {"default": ""}),
"eyes": ("STRING", {"default": ""}),
},
"optional": {
"character_cast": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
FUNCTION = "build"
CATEGORY = "prompt_builder"
def build(
self,
enabled,
label,
age,
manual_age,
ethnicity,
body,
manual_body,
body_phrase,
skin,
hair,
eyes,
character_cast="",
):
result = build_character_slot_json(
subject_type="man",
label=label,
age=age,
manual_age=manual_age,
ethnicity=ethnicity,
figure="random",
body=body,
manual_body=manual_body,
body_phrase=body_phrase,
skin=skin,
hair=hair,
eyes=eyes,
enabled=enabled,
character_cast=character_cast or "",
)
return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPCharacterProfileSave: class SxCPCharacterProfileSave:
@classmethod @classmethod
def INPUT_TYPES(cls): def INPUT_TYPES(cls):
@@ -985,6 +1113,8 @@ NODE_CLASS_MAPPINGS = {
"SxCPGenerationProfile": SxCPGenerationProfile, "SxCPGenerationProfile": SxCPGenerationProfile,
"SxCPAdvancedFilters": SxCPAdvancedFilters, "SxCPAdvancedFilters": SxCPAdvancedFilters,
"SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs, "SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs,
"SxCPWomanSlot": SxCPWomanSlot,
"SxCPManSlot": SxCPManSlot,
"SxCPCharacterSlot": SxCPCharacterSlot, "SxCPCharacterSlot": SxCPCharacterSlot,
"SxCPCharacterProfileSave": SxCPCharacterProfileSave, "SxCPCharacterProfileSave": SxCPCharacterProfileSave,
"SxCPCharacterProfileLoad": SxCPCharacterProfileLoad, "SxCPCharacterProfileLoad": SxCPCharacterProfileLoad,
@@ -1004,6 +1134,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SxCPGenerationProfile": "SxCP Generation Profile", "SxCPGenerationProfile": "SxCP Generation Profile",
"SxCPAdvancedFilters": "SxCP Advanced Filters", "SxCPAdvancedFilters": "SxCP Advanced Filters",
"SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs", "SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs",
"SxCPWomanSlot": "SxCP Woman Slot",
"SxCPManSlot": "SxCP Man Slot",
"SxCPCharacterSlot": "SxCP Character Slot", "SxCPCharacterSlot": "SxCP Character Slot",
"SxCPCharacterProfileSave": "SxCP Character Profile Save", "SxCPCharacterProfileSave": "SxCP Character Profile Save",
"SxCPCharacterProfileLoad": "SxCP Character Profile Load", "SxCPCharacterProfileLoad": "SxCP Character Profile Load",
+44
View File
@@ -137,6 +137,42 @@ CHARACTER_BODY_CHOICES = [
"broad", "broad",
"muscular", "muscular",
] ]
CHARACTER_WOMAN_BODY_CHOICES = [
"random",
"manual",
"slim",
"petite adult",
"toned",
"athletic",
"average",
"curvy",
"soft curvy",
"curvy athletic",
"hourglass",
"slim busty",
"busty",
"busty curvy",
"voluptuous",
"plus-size",
"heavyset",
"fat",
]
CHARACTER_MAN_BODY_CHOICES = [
"random",
"manual",
"slim",
"lean",
"lean athletic",
"toned",
"average",
"athletic",
"muscular",
"broad",
"broad-shouldered",
"stocky",
"heavyset",
"fat",
]
CHARACTER_RANDOM_TOKENS = {"", "random", "auto", "global", "from_global", "default"} CHARACTER_RANDOM_TOKENS = {"", "random", "auto", "global", "from_global", "default"}
CAMERA_DETAIL_CHOICES = ["off", "compact", "full"] CAMERA_DETAIL_CHOICES = ["off", "compact", "full"]
@@ -1296,6 +1332,14 @@ def character_body_choices() -> list[str]:
return list(CHARACTER_BODY_CHOICES) return list(CHARACTER_BODY_CHOICES)
def character_woman_body_choices() -> list[str]:
return list(CHARACTER_WOMAN_BODY_CHOICES)
def character_man_body_choices() -> list[str]:
return list(CHARACTER_MAN_BODY_CHOICES)
def character_ethnicity_choices() -> list[str]: def character_ethnicity_choices() -> list[str]:
return ["random"] + list(ETHNICITY_FILTER_CHOICES) return ["random"] + list(ETHNICITY_FILTER_CHOICES)