Compare commits
8 Commits
c0985cddbe
...
84d4383f26
| Author | SHA1 | Date | |
|---|---|---|---|
| 84d4383f26 | |||
| b6c6df03ee | |||
| 50ded3c5fa | |||
| 12c5f73104 | |||
| 8c3f61ea6d | |||
| 885f136cf3 | |||
| 57c73279ef | |||
| 1d23ce32aa |
@@ -146,13 +146,14 @@ def build_prompt_result(request: PromptBuildRequest, deps: PromptBuildDependenci
|
|||||||
parsed_location_config = deps.parse_location_config(request.location_config)
|
parsed_location_config = deps.parse_location_config(request.location_config)
|
||||||
parsed_composition_config = deps.parse_composition_config(request.composition_config)
|
parsed_composition_config = deps.parse_composition_config(request.composition_config)
|
||||||
content_rng = deps.axis_rng(parsed_seed_config, "content", seed, row_number)
|
content_rng = deps.axis_rng(parsed_seed_config, "content", seed, row_number)
|
||||||
|
clothing_rng = deps.axis_rng(parsed_seed_config, "clothing", seed, row_number)
|
||||||
pose_axis_rng = deps.axis_rng(parsed_seed_config, "pose", seed, row_number)
|
pose_axis_rng = deps.axis_rng(parsed_seed_config, "pose", seed, row_number)
|
||||||
person_rng = deps.axis_rng(parsed_seed_config, "person", seed, row_number)
|
person_rng = deps.axis_rng(parsed_seed_config, "person", seed, row_number)
|
||||||
expression_rng = deps.axis_rng(parsed_seed_config, "expression", seed, row_number)
|
expression_rng = deps.axis_rng(parsed_seed_config, "expression", seed, row_number)
|
||||||
clothing = request.clothing if request.clothing in ("full", "minimal", "random") else "full"
|
clothing = request.clothing if request.clothing in ("full", "minimal", "random") else "full"
|
||||||
poses = request.poses if request.poses in ("standard", "evocative", "random") else "standard"
|
poses = request.poses if request.poses in ("standard", "evocative", "random") else "standard"
|
||||||
figure = request.figure if request.figure in ("curvy", "balanced", "bombshell", "random") else "curvy"
|
figure = request.figure if request.figure in ("curvy", "balanced", "bombshell", "random") else "curvy"
|
||||||
clothing = deps.pick_clothing_mode(content_rng, clothing, minimal_ratio)
|
clothing = deps.pick_clothing_mode(clothing_rng, clothing, minimal_ratio)
|
||||||
poses = deps.pick_pose_mode(pose_axis_rng, poses, pose_ratio)
|
poses = deps.pick_pose_mode(pose_axis_rng, poses, pose_ratio)
|
||||||
figure = deps.pick_figure_bias(person_rng, figure)
|
figure = deps.pick_figure_bias(person_rng, figure)
|
||||||
minimal_ratio = None
|
minimal_ratio = None
|
||||||
@@ -185,6 +186,7 @@ def build_prompt_result(request: PromptBuildRequest, deps: PromptBuildDependenci
|
|||||||
minimal_ratio,
|
minimal_ratio,
|
||||||
pose_ratio,
|
pose_ratio,
|
||||||
seed,
|
seed,
|
||||||
|
seed_config=parsed_seed_config,
|
||||||
)
|
)
|
||||||
elif category in ("woman", "man", "couple", "group_or_layout") and not exact_custom_subcategory:
|
elif category in ("woman", "man", "couple", "group_or_layout") and not exact_custom_subcategory:
|
||||||
branch = "built_in"
|
branch = "built_in"
|
||||||
@@ -202,6 +204,7 @@ def build_prompt_result(request: PromptBuildRequest, deps: PromptBuildDependenci
|
|||||||
minimal_ratio,
|
minimal_ratio,
|
||||||
pose_ratio,
|
pose_ratio,
|
||||||
seed,
|
seed,
|
||||||
|
seed_config=parsed_seed_config,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row = deps.build_custom_row(
|
row = deps.build_custom_row(
|
||||||
|
|||||||
+15
-13
@@ -3059,16 +3059,17 @@ def row_base(index: int, batch: int, subject: str, age: str, body: str, scene_sl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_single(index: int, batch: int, rng: random.Random, gender: str, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", poses: str = "standard", backside_bias: float = 0.0, figure: str = "curvy", no_plus: bool = False, no_black: bool = False) -> dict:
|
def make_single(index: int, batch: int, rng: random.Random, gender: str, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", poses: str = "standard", backside_bias: float = 0.0, figure: str = "curvy", no_plus: bool = False, no_black: bool = False, clothing_rng: random.Random | None = None) -> dict:
|
||||||
minimal = clothing == "minimal"
|
minimal = clothing == "minimal"
|
||||||
|
wardrobe_rng = clothing_rng or rng
|
||||||
if gender == "woman":
|
if gender == "woman":
|
||||||
subject, age, body, skin, hair, eyes = choose_woman(rng, ethnicity, no_plus, no_black)
|
subject, age, body, skin, hair, eyes = choose_woman(rng, ethnicity, no_plus, no_black)
|
||||||
clothes = choose(rng, WOMEN_CLOTHES_MINIMAL if minimal else WOMEN_CLOTHES)
|
clothes = choose(wardrobe_rng, WOMEN_CLOTHES_MINIMAL if minimal else WOMEN_CLOTHES)
|
||||||
figure_note = choose(rng, figure_pool(figure))
|
figure_note = choose(rng, figure_pool(figure))
|
||||||
else:
|
else:
|
||||||
men_pool = by_ethnicity(MEN, ethnicity)
|
men_pool = by_ethnicity(MEN, ethnicity)
|
||||||
subject, age, body, skin, hair, eyes = choose(rng, men_pool)
|
subject, age, body, skin, hair, eyes = choose(rng, men_pool)
|
||||||
clothes = choose(rng, MEN_CLOTHES_MINIMAL if minimal else MEN_CLOTHES)
|
clothes = choose(wardrobe_rng, MEN_CLOTHES_MINIMAL if minimal else MEN_CLOTHES)
|
||||||
figure_note = ""
|
figure_note = ""
|
||||||
body_phrase = make_body_phrase(body, figure_note)
|
body_phrase = make_body_phrase(body, figure_note)
|
||||||
|
|
||||||
@@ -3119,7 +3120,7 @@ def make_single(index: int, batch: int, rng: random.Random, gender: str, expr_de
|
|||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
def make_couple(index: int, batch: int, rng: random.Random, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", no_plus: bool = False) -> dict:
|
def make_couple(index: int, batch: int, rng: random.Random, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", no_plus: bool = False, clothing_rng: random.Random | None = None) -> dict:
|
||||||
primary_subject, subject_phrase, pose = choose(rng, COUPLE_TYPES)
|
primary_subject, subject_phrase, pose = choose(rng, COUPLE_TYPES)
|
||||||
if ethnicity == "asian":
|
if ethnicity == "asian":
|
||||||
subject_phrase = {
|
subject_phrase = {
|
||||||
@@ -3140,7 +3141,7 @@ def make_couple(index: int, batch: int, rng: random.Random, expr_deck: Expressio
|
|||||||
if no_plus:
|
if no_plus:
|
||||||
body_options = [b for b in body_options if "plus" not in b and "fat" not in b]
|
body_options = [b for b in body_options if "plus" not in b and "fat" not in b]
|
||||||
body = choose(rng, body_options)
|
body = choose(rng, body_options)
|
||||||
outfits = choose(rng, COUPLE_OUTFITS_MINIMAL if clothing == "minimal" else COUPLE_OUTFITS)
|
outfits = choose(clothing_rng or rng, COUPLE_OUTFITS_MINIMAL if clothing == "minimal" else COUPLE_OUTFITS)
|
||||||
expr_a, expr_b = expr_deck.draw_two()
|
expr_a, expr_b = expr_deck.draw_two()
|
||||||
|
|
||||||
row = row_base(index, batch, primary_subject, ages, body, scene_slug, composition)
|
row = row_base(index, batch, primary_subject, ages, body, scene_slug, composition)
|
||||||
@@ -3164,7 +3165,7 @@ def make_couple(index: int, batch: int, rng: random.Random, expr_deck: Expressio
|
|||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
def make_group_or_layout(index: int, batch: int, rng: random.Random, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", no_plus: bool = False) -> dict:
|
def make_group_or_layout(index: int, batch: int, rng: random.Random, expr_deck: ExpressionDeck, clothing: str = "full", ethnicity: str = "any", no_plus: bool = False, clothing_rng: random.Random | None = None) -> dict:
|
||||||
minimal = clothing == "minimal"
|
minimal = clothing == "minimal"
|
||||||
group_outfits = "minimal beachwear and lingerie-inspired outfits" if minimal else "stylish revealing party outfits"
|
group_outfits = "minimal beachwear and lingerie-inspired outfits" if minimal else "stylish revealing party outfits"
|
||||||
if ethnicity == "asian":
|
if ethnicity == "asian":
|
||||||
@@ -3203,7 +3204,7 @@ def make_group_or_layout(index: int, batch: int, rng: random.Random, expr_deck:
|
|||||||
)
|
)
|
||||||
return row
|
return row
|
||||||
|
|
||||||
layout_slug, layout_desc = choose(rng, LAYOUTS_MINIMAL if minimal else LAYOUTS_FULL)
|
layout_slug, layout_desc = choose(clothing_rng or rng, LAYOUTS_MINIMAL if minimal else LAYOUTS_FULL)
|
||||||
if ethnicity == "asian":
|
if ethnicity == "asian":
|
||||||
layout_desc = layout_desc.replace("adult", "Asian adult")
|
layout_desc = layout_desc.replace("adult", "Asian adult")
|
||||||
elif ethnicity == "white_asian":
|
elif ethnicity == "white_asian":
|
||||||
@@ -3231,8 +3232,9 @@ def make_group_or_layout(index: int, batch: int, rng: random.Random, expr_deck:
|
|||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
def build_rows(total: int, start_index: int, clothing: str = "full", ethnicity: str = "any", poses: str = "standard", backside_bias: float = 0.0, figure: str = "curvy", no_plus: bool = False, no_black: bool = False, minimal_clothing_ratio: float | None = None, standard_pose_ratio: float | None = None, seed: int = DEFAULT_RNG_SEED, expression_seed: int = EXPRESSION_SEED) -> list[dict]:
|
def build_rows(total: int, start_index: int, clothing: str = "full", ethnicity: str = "any", poses: str = "standard", backside_bias: float = 0.0, figure: str = "curvy", no_plus: bool = False, no_black: bool = False, minimal_clothing_ratio: float | None = None, standard_pose_ratio: float | None = None, seed: int = DEFAULT_RNG_SEED, expression_seed: int = EXPRESSION_SEED, clothing_rng: random.Random | None = None) -> list[dict]:
|
||||||
rng = random.Random(seed)
|
rng = random.Random(seed)
|
||||||
|
wardrobe_rng = clothing_rng or rng
|
||||||
expr_deck = ExpressionDeck(EXPRESSIONS, random.Random(expression_seed))
|
expr_deck = ExpressionDeck(EXPRESSIONS, random.Random(expression_seed))
|
||||||
rows: list[dict] = []
|
rows: list[dict] = []
|
||||||
batch_quotas = batch_category_quotas()
|
batch_quotas = batch_category_quotas()
|
||||||
@@ -3244,21 +3246,21 @@ def build_rows(total: int, start_index: int, clothing: str = "full", ethnicity:
|
|||||||
index = start_index
|
index = start_index
|
||||||
for batch in range(1, batch_count + 1):
|
for batch in range(1, batch_count + 1):
|
||||||
batch_rows: list[dict] = []
|
batch_rows: list[dict] = []
|
||||||
clothing_modes = batch_clothing_modes(rng, clothing, minimal_clothing_ratio)
|
clothing_modes = batch_clothing_modes(wardrobe_rng, clothing, minimal_clothing_ratio)
|
||||||
single_pose_modes = batch_single_pose_modes(rng, poses, standard_pose_ratio, single_subject_count)
|
single_pose_modes = batch_single_pose_modes(rng, poses, standard_pose_ratio, single_subject_count)
|
||||||
for category, count in batch_quotas:
|
for category, count in batch_quotas:
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
row_clothing = clothing_modes.pop()
|
row_clothing = clothing_modes.pop()
|
||||||
if category == "woman":
|
if category == "woman":
|
||||||
row_pose = single_pose_modes.pop()
|
row_pose = single_pose_modes.pop()
|
||||||
row = make_single(index, batch, rng, "woman", expr_deck, row_clothing, ethnicity, row_pose, backside_bias, figure, no_plus, no_black)
|
row = make_single(index, batch, rng, "woman", expr_deck, row_clothing, ethnicity, row_pose, backside_bias, figure, no_plus, no_black, clothing_rng=wardrobe_rng if clothing_rng else None)
|
||||||
elif category == "man":
|
elif category == "man":
|
||||||
row_pose = single_pose_modes.pop()
|
row_pose = single_pose_modes.pop()
|
||||||
row = make_single(index, batch, rng, "man", expr_deck, row_clothing, ethnicity, row_pose, backside_bias, figure, no_plus, no_black)
|
row = make_single(index, batch, rng, "man", expr_deck, row_clothing, ethnicity, row_pose, backside_bias, figure, no_plus, no_black, clothing_rng=wardrobe_rng if clothing_rng else None)
|
||||||
elif category == "couple":
|
elif category == "couple":
|
||||||
row = make_couple(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus)
|
row = make_couple(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus, clothing_rng=wardrobe_rng if clothing_rng else None)
|
||||||
else:
|
else:
|
||||||
row = make_group_or_layout(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus)
|
row = make_group_or_layout(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus, clothing_rng=wardrobe_rng if clothing_rng else None)
|
||||||
batch_rows.append(row)
|
batch_rows.append(row)
|
||||||
index += 1
|
index += 1
|
||||||
rng.shuffle(batch_rows)
|
rng.shuffle(batch_rows)
|
||||||
|
|||||||
+5
-2
@@ -215,7 +215,7 @@ BRANCH_TARGET_CHOICES = ["both", "softcore", "hardcore"]
|
|||||||
SCENE_LAYER_SEED_AXES = {
|
SCENE_LAYER_SEED_AXES = {
|
||||||
"cast": ("category",),
|
"cast": ("category",),
|
||||||
"character": ("person",),
|
"character": ("person",),
|
||||||
"wardrobe": ("content",),
|
"wardrobe": ("clothing",),
|
||||||
"location": ("scene",),
|
"location": ("scene",),
|
||||||
"set_dressing": ("scene",),
|
"set_dressing": ("scene",),
|
||||||
"blocking": ("pose",),
|
"blocking": ("pose",),
|
||||||
@@ -224,7 +224,7 @@ SCENE_LAYER_SEED_AXES = {
|
|||||||
"camera": ("composition",),
|
"camera": ("composition",),
|
||||||
"composition": ("composition",),
|
"composition": ("composition",),
|
||||||
"lighting": ("composition",),
|
"lighting": ("composition",),
|
||||||
"softcore_branch": ("content", "pose", "role"),
|
"softcore_branch": ("clothing", "pose", "role"),
|
||||||
"hardcore_branch": ("pose", "role"),
|
"hardcore_branch": ("pose", "role"),
|
||||||
}
|
}
|
||||||
SCENE_REROLL_GROUPS = {
|
SCENE_REROLL_GROUPS = {
|
||||||
@@ -232,6 +232,7 @@ SCENE_REROLL_GROUPS = {
|
|||||||
"category": ("category",),
|
"category": ("category",),
|
||||||
"subcategory": ("subcategory",),
|
"subcategory": ("subcategory",),
|
||||||
"content": ("content",),
|
"content": ("content",),
|
||||||
|
"clothing": ("clothing",),
|
||||||
"person": ("person",),
|
"person": ("person",),
|
||||||
"scene": ("scene",),
|
"scene": ("scene",),
|
||||||
"pose": ("pose", "role"),
|
"pose": ("pose", "role"),
|
||||||
@@ -239,6 +240,8 @@ SCENE_REROLL_GROUPS = {
|
|||||||
"expression": ("expression",),
|
"expression": ("expression",),
|
||||||
"composition": ("composition",),
|
"composition": ("composition",),
|
||||||
"content_pose": ("content", "pose", "role"),
|
"content_pose": ("content", "pose", "role"),
|
||||||
|
"content_clothing": ("content", "clothing"),
|
||||||
|
"clothing_pose": ("clothing", "pose", "role"),
|
||||||
"scene_pose": ("scene", "pose", "role"),
|
"scene_pose": ("scene", "pose", "role"),
|
||||||
}
|
}
|
||||||
SCENE_OPTION_TEXT_KEYS = {
|
SCENE_OPTION_TEXT_KEYS = {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class SxCPSeedControl:
|
|||||||
"role",
|
"role",
|
||||||
"expression",
|
"expression",
|
||||||
"composition",
|
"composition",
|
||||||
|
"clothing",
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -129,11 +130,14 @@ class SxCPSeedControl:
|
|||||||
expression_seed,
|
expression_seed,
|
||||||
composition_seed_mode,
|
composition_seed_mode,
|
||||||
composition_seed,
|
composition_seed,
|
||||||
|
clothing_seed_mode="auto",
|
||||||
|
clothing_seed=-1,
|
||||||
):
|
):
|
||||||
config = build_seed_config_json(
|
config = build_seed_config_json(
|
||||||
category_seed=category_seed,
|
category_seed=category_seed,
|
||||||
subcategory_seed=subcategory_seed,
|
subcategory_seed=subcategory_seed,
|
||||||
content_seed=content_seed,
|
content_seed=content_seed,
|
||||||
|
clothing_seed=clothing_seed,
|
||||||
person_seed=person_seed,
|
person_seed=person_seed,
|
||||||
scene_seed=scene_seed,
|
scene_seed=scene_seed,
|
||||||
pose_seed=pose_seed,
|
pose_seed=pose_seed,
|
||||||
@@ -143,6 +147,7 @@ class SxCPSeedControl:
|
|||||||
category_seed_mode=category_seed_mode,
|
category_seed_mode=category_seed_mode,
|
||||||
subcategory_seed_mode=subcategory_seed_mode,
|
subcategory_seed_mode=subcategory_seed_mode,
|
||||||
content_seed_mode=content_seed_mode,
|
content_seed_mode=content_seed_mode,
|
||||||
|
clothing_seed_mode=clothing_seed_mode,
|
||||||
person_seed_mode=person_seed_mode,
|
person_seed_mode=person_seed_mode,
|
||||||
scene_seed_mode=scene_seed_mode,
|
scene_seed_mode=scene_seed_mode,
|
||||||
pose_seed_mode=pose_seed_mode,
|
pose_seed_mode=pose_seed_mode,
|
||||||
|
|||||||
+3
-1
@@ -257,6 +257,8 @@ NODE_INPUT_TOOLTIPS = {
|
|||||||
"category_seed_mode": "auto/follow_main follows the main seed; fixed uses category_seed; random rerolls this axis at queue time while the field value stays unchanged.",
|
"category_seed_mode": "auto/follow_main follows the main seed; fixed uses category_seed; random rerolls this axis at queue time while the field value stays unchanged.",
|
||||||
"subcategory_seed_mode": "Controls which subcategory is selected. Change this to switch oral vs penetration when both are allowed.",
|
"subcategory_seed_mode": "Controls which subcategory is selected. Change this to switch oral vs penetration when both are allowed.",
|
||||||
"content_seed_mode": "Controls item/outfit content for non-pose categories.",
|
"content_seed_mode": "Controls item/outfit content for non-pose categories.",
|
||||||
|
"clothing_seed_mode": "Controls clothing/outfit selection separately from content item selection.",
|
||||||
|
"clothing_seed": "Seed used when clothing_seed_mode is fixed or auto with a non-negative value.",
|
||||||
"person_seed_mode": "Controls generated character appearance unless a slot seed overrides it.",
|
"person_seed_mode": "Controls generated character appearance unless a slot seed overrides it.",
|
||||||
"scene_seed_mode": "Controls location/scene selection.",
|
"scene_seed_mode": "Controls location/scene selection.",
|
||||||
"pose_seed_mode": "Controls pose/item selection for pose categories, including hardcore positions.",
|
"pose_seed_mode": "Controls pose/item selection for pose categories, including hardcore positions.",
|
||||||
@@ -266,7 +268,7 @@ NODE_INPUT_TOOLTIPS = {
|
|||||||
},
|
},
|
||||||
"SxCPSeedLocker": {
|
"SxCPSeedLocker": {
|
||||||
"base_seed": "Master seed for the locked result. Use the same value as the generator seed for simplest reproduction.",
|
"base_seed": "Master seed for the locked result. Use the same value as the generator seed for simplest reproduction.",
|
||||||
"reroll_axis": "Choose the one axis to change while the rest stays locked. Use pose for sexual pose, scene for location, person for appearance.",
|
"reroll_axis": "Choose the one axis to change while the rest stays locked. Use clothing for outfit-only rerolls, pose for sexual pose, scene for location, person for appearance.",
|
||||||
"reroll_seed": "Seed for the selected axis only. Leave -1 to derive a stable reroll from base_seed.",
|
"reroll_seed": "Seed for the selected axis only. Leave -1 to derive a stable reroll from base_seed.",
|
||||||
},
|
},
|
||||||
"SxCPCastBias": {
|
"SxCPCastBias": {
|
||||||
|
|||||||
+5
-5
@@ -166,15 +166,15 @@ def softcore_partner_styling(
|
|||||||
choose: Choose,
|
choose: Choose,
|
||||||
slot_softcore_outfit: SlotSoftcoreOutfit,
|
slot_softcore_outfit: SlotSoftcoreOutfit,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
content_rng = axis_rng(seed_config, "content", seed, row_number + 421)
|
clothing_rng = axis_rng(seed_config, "clothing", seed, row_number + 421)
|
||||||
pose_rng = axis_rng(seed_config, "pose", seed, row_number + 421)
|
pose_rng = axis_rng(seed_config, "pose", seed, row_number + 421)
|
||||||
pov_set = set(pov_labels or [])
|
pov_set = set(pov_labels or [])
|
||||||
outfits: list[str] = []
|
outfits: list[str] = []
|
||||||
for index in range(max(0, women_count - 1)):
|
for index in range(max(0, women_count - 1)):
|
||||||
label = chr(ord("B") + index)
|
label = chr(ord("B") + index)
|
||||||
full_label = f"Woman {label}"
|
full_label = f"Woman {label}"
|
||||||
outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose(
|
outfit = slot_softcore_outfit((label_map or {}).get(full_label), clothing_rng) or choose(
|
||||||
content_rng,
|
clothing_rng,
|
||||||
pair_options.INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS,
|
pair_options.INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS,
|
||||||
)
|
)
|
||||||
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
||||||
@@ -185,8 +185,8 @@ def softcore_partner_styling(
|
|||||||
full_label = f"Man {label}"
|
full_label = f"Man {label}"
|
||||||
if full_label in pov_set:
|
if full_label in pov_set:
|
||||||
continue
|
continue
|
||||||
outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose(
|
outfit = slot_softcore_outfit((label_map or {}).get(full_label), clothing_rng) or choose(
|
||||||
content_rng,
|
clothing_rng,
|
||||||
pair_options.INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS,
|
pair_options.INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS,
|
||||||
)
|
)
|
||||||
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
||||||
|
|||||||
+3
-2
@@ -74,6 +74,7 @@ def build_insta_pair_rows_result(
|
|||||||
soft_seed_config = parsed_softcore_seed_config or parsed_seed_config
|
soft_seed_config = parsed_softcore_seed_config or parsed_seed_config
|
||||||
hard_seed_config = parsed_hardcore_seed_config or parsed_seed_config
|
hard_seed_config = parsed_hardcore_seed_config or parsed_seed_config
|
||||||
soft_content_rng = axis_rng(soft_seed_config, "content", seed, row_number + 311)
|
soft_content_rng = axis_rng(soft_seed_config, "content", seed, row_number + 311)
|
||||||
|
soft_clothing_rng = axis_rng(soft_seed_config, "clothing", seed, row_number + 311)
|
||||||
soft_pose_rng = axis_rng(soft_seed_config, "pose", seed, row_number + 313)
|
soft_pose_rng = axis_rng(soft_seed_config, "pose", seed, row_number + 313)
|
||||||
hard_content_rng = axis_rng(hard_seed_config, "content", seed, row_number + 317)
|
hard_content_rng = axis_rng(hard_seed_config, "content", seed, row_number + 317)
|
||||||
soft_person_rng = axis_rng(soft_seed_config, "person", seed, row_number)
|
soft_person_rng = axis_rng(soft_seed_config, "person", seed, row_number)
|
||||||
@@ -147,8 +148,8 @@ def build_insta_pair_rows_result(
|
|||||||
if not soft_expression_enabled:
|
if not soft_expression_enabled:
|
||||||
soft_row = disable_row_expression(soft_row, soft_expression_intensity_source)
|
soft_row = disable_row_expression(soft_row, soft_expression_intensity_source)
|
||||||
|
|
||||||
primary_softcore_outfit = slot_softcore_outfit(primary_slot, soft_content_rng)
|
primary_softcore_outfit = slot_softcore_outfit(primary_slot, soft_clothing_rng)
|
||||||
soft_row["item"] = primary_softcore_outfit or softcore_outfit(soft_content_rng, softcore_level_key)
|
soft_row["item"] = primary_softcore_outfit or softcore_outfit(soft_clothing_rng, softcore_level_key)
|
||||||
soft_row["pose"] = softcore_pose(soft_pose_rng, softcore_level_key)
|
soft_row["pose"] = softcore_pose(soft_pose_rng, softcore_level_key)
|
||||||
soft_row["item_label"] = (
|
soft_row["item_label"] = (
|
||||||
"Insta/OF softcore body exposure"
|
"Insta/OF softcore body exposure"
|
||||||
|
|||||||
@@ -890,11 +890,14 @@ def build_seed_config_json(
|
|||||||
role_seed_mode: str = "auto",
|
role_seed_mode: str = "auto",
|
||||||
expression_seed_mode: str = "auto",
|
expression_seed_mode: str = "auto",
|
||||||
composition_seed_mode: str = "auto",
|
composition_seed_mode: str = "auto",
|
||||||
|
clothing_seed: int = -1,
|
||||||
|
clothing_seed_mode: str = "auto",
|
||||||
) -> str:
|
) -> str:
|
||||||
return seed_policy.build_seed_config_json(
|
return seed_policy.build_seed_config_json(
|
||||||
category_seed=category_seed,
|
category_seed=category_seed,
|
||||||
subcategory_seed=subcategory_seed,
|
subcategory_seed=subcategory_seed,
|
||||||
content_seed=content_seed,
|
content_seed=content_seed,
|
||||||
|
clothing_seed=clothing_seed,
|
||||||
person_seed=person_seed,
|
person_seed=person_seed,
|
||||||
scene_seed=scene_seed,
|
scene_seed=scene_seed,
|
||||||
pose_seed=pose_seed,
|
pose_seed=pose_seed,
|
||||||
@@ -904,6 +907,7 @@ def build_seed_config_json(
|
|||||||
category_seed_mode=category_seed_mode,
|
category_seed_mode=category_seed_mode,
|
||||||
subcategory_seed_mode=subcategory_seed_mode,
|
subcategory_seed_mode=subcategory_seed_mode,
|
||||||
content_seed_mode=content_seed_mode,
|
content_seed_mode=content_seed_mode,
|
||||||
|
clothing_seed_mode=clothing_seed_mode,
|
||||||
person_seed_mode=person_seed_mode,
|
person_seed_mode=person_seed_mode,
|
||||||
scene_seed_mode=scene_seed_mode,
|
scene_seed_mode=scene_seed_mode,
|
||||||
pose_seed_mode=pose_seed_mode,
|
pose_seed_mode=pose_seed_mode,
|
||||||
@@ -1406,6 +1410,7 @@ def _build_auto_weighted_row(
|
|||||||
minimal_clothing_ratio: float | None,
|
minimal_clothing_ratio: float | None,
|
||||||
standard_pose_ratio: float | None,
|
standard_pose_ratio: float | None,
|
||||||
seed: int,
|
seed: int,
|
||||||
|
seed_config: dict[str, int] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
return row_generation_policy.build_auto_weighted_row(
|
return row_generation_policy.build_auto_weighted_row(
|
||||||
row_number,
|
row_number,
|
||||||
@@ -1420,6 +1425,7 @@ def _build_auto_weighted_row(
|
|||||||
minimal_clothing_ratio,
|
minimal_clothing_ratio,
|
||||||
standard_pose_ratio,
|
standard_pose_ratio,
|
||||||
seed,
|
seed,
|
||||||
|
seed_config=seed_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1437,6 +1443,7 @@ def _build_direct_builtin_row(
|
|||||||
minimal_clothing_ratio: float | None,
|
minimal_clothing_ratio: float | None,
|
||||||
standard_pose_ratio: float | None,
|
standard_pose_ratio: float | None,
|
||||||
seed: int,
|
seed: int,
|
||||||
|
seed_config: dict[str, int] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
return row_generation_policy.build_direct_builtin_row(
|
return row_generation_policy.build_direct_builtin_row(
|
||||||
category,
|
category,
|
||||||
@@ -1452,6 +1459,7 @@ def _build_direct_builtin_row(
|
|||||||
minimal_clothing_ratio,
|
minimal_clothing_ratio,
|
||||||
standard_pose_ratio,
|
standard_pose_ratio,
|
||||||
seed,
|
seed,
|
||||||
|
seed_config=seed_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -50,6 +50,32 @@ def is_pose_content_category(category: dict[str, Any], subcategory: dict[str, An
|
|||||||
return bool(tokens.intersection({"pose", "poses", "sex", "sexual"}))
|
return bool(tokens.intersection({"pose", "poses", "sex", "sexual"}))
|
||||||
|
|
||||||
|
|
||||||
|
def is_clothing_content_category(category: dict[str, Any], subcategory: dict[str, Any]) -> bool:
|
||||||
|
haystack = " ".join(
|
||||||
|
str(value)
|
||||||
|
for value in (
|
||||||
|
category.get("name", ""),
|
||||||
|
category.get("slug", ""),
|
||||||
|
category.get("item_label", ""),
|
||||||
|
subcategory.get("name", ""),
|
||||||
|
subcategory.get("slug", ""),
|
||||||
|
subcategory.get("item_label", ""),
|
||||||
|
)
|
||||||
|
).lower()
|
||||||
|
tokens = set(re.findall(r"[a-z0-9]+", haystack))
|
||||||
|
if tokens.intersection({"clothes", "clothing", "outfit", "outfits", "streetwear", "menswear", "daywear", "activewear", "lingerie", "wardrobe"}):
|
||||||
|
return True
|
||||||
|
return any(
|
||||||
|
phrase in haystack
|
||||||
|
for phrase in (
|
||||||
|
"casual clothes",
|
||||||
|
"men casual clothes",
|
||||||
|
"couple casual clothes",
|
||||||
|
"provocative erotic clothes",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def cast_count_adjustment(
|
def cast_count_adjustment(
|
||||||
requested_women_count: int,
|
requested_women_count: int,
|
||||||
requested_men_count: int,
|
requested_men_count: int,
|
||||||
@@ -266,7 +292,8 @@ def select_category_item_route_result(
|
|||||||
)
|
)
|
||||||
|
|
||||||
is_pose_category = is_pose_content_category(category, subcategory)
|
is_pose_category = is_pose_content_category(category, subcategory)
|
||||||
content_axis = "pose" if is_pose_category else "content"
|
is_clothing_category = is_clothing_content_category(category, subcategory)
|
||||||
|
content_axis = "pose" if is_pose_category else ("clothing" if is_clothing_category else "content")
|
||||||
content_rng = seed_policy.axis_rng(seed_config, content_axis, seed, row_number)
|
content_rng = seed_policy.axis_rng(seed_config, content_axis, seed, row_number)
|
||||||
item = row_item_policy.weighted_choice(content_rng, _list_from(subcategory.get("items", [subcategory["name"]])))
|
item = row_item_policy.weighted_choice(content_rng, _list_from(subcategory.get("items", [subcategory["name"]])))
|
||||||
item_text, item_name, item_axis_values, item_template_metadata = row_item_policy.compose_item(
|
item_text, item_name, item_axis_values, item_template_metadata = row_item_policy.compose_item(
|
||||||
|
|||||||
+26
-4
@@ -78,8 +78,12 @@ def build_auto_weighted_row(
|
|||||||
minimal_clothing_ratio: float | None,
|
minimal_clothing_ratio: float | None,
|
||||||
standard_pose_ratio: float | None,
|
standard_pose_ratio: float | None,
|
||||||
seed: int,
|
seed: int,
|
||||||
|
seed_config: dict[str, int] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
batch_number = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1)
|
batch_number = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1)
|
||||||
|
clothing_rng = None
|
||||||
|
if seed_config is not None:
|
||||||
|
clothing_rng = seed_policy.axis_rng(seed_config, "clothing", seed, row_number)
|
||||||
rows = g.build_rows(
|
rows = g.build_rows(
|
||||||
batch_number * g.BATCH_SIZE,
|
batch_number * g.BATCH_SIZE,
|
||||||
start_index,
|
start_index,
|
||||||
@@ -94,6 +98,7 @@ def build_auto_weighted_row(
|
|||||||
standard_pose_ratio,
|
standard_pose_ratio,
|
||||||
seed,
|
seed,
|
||||||
g.EXPRESSION_SEED + seed,
|
g.EXPRESSION_SEED + seed,
|
||||||
|
clothing_rng=clothing_rng,
|
||||||
)
|
)
|
||||||
row = rows[row_number - 1]
|
row = rows[row_number - 1]
|
||||||
row["main_category"] = "auto_weighted"
|
row["main_category"] = "auto_weighted"
|
||||||
@@ -116,15 +121,19 @@ def build_direct_builtin_row(
|
|||||||
minimal_clothing_ratio: float | None,
|
minimal_clothing_ratio: float | None,
|
||||||
standard_pose_ratio: float | None,
|
standard_pose_ratio: float | None,
|
||||||
seed: int,
|
seed: int,
|
||||||
|
seed_config: dict[str, int] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
rng = random.Random(seed_policy.row_seed(seed, row_number))
|
rng = random.Random(seed_policy.row_seed(seed, row_number))
|
||||||
|
clothing_rng = None
|
||||||
|
if seed_config is not None:
|
||||||
|
clothing_rng = seed_policy.axis_rng(seed_config, "clothing", seed, row_number)
|
||||||
expr_deck = g.ExpressionDeck(
|
expr_deck = g.ExpressionDeck(
|
||||||
g.EXPRESSIONS,
|
g.EXPRESSIONS,
|
||||||
random.Random(seed_policy.row_seed(g.EXPRESSION_SEED + seed, row_number)),
|
random.Random(seed_policy.row_seed(g.EXPRESSION_SEED + seed, row_number)),
|
||||||
)
|
)
|
||||||
batch = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1)
|
batch = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1)
|
||||||
index = start_index + row_number - 1
|
index = start_index + row_number - 1
|
||||||
row_clothing = pick_clothing_mode(rng, clothing, minimal_clothing_ratio)
|
row_clothing = pick_clothing_mode(clothing_rng or rng, clothing, minimal_clothing_ratio)
|
||||||
row_poses = pick_pose_mode(rng, poses, standard_pose_ratio)
|
row_poses = pick_pose_mode(rng, poses, standard_pose_ratio)
|
||||||
|
|
||||||
if category == "woman":
|
if category == "woman":
|
||||||
@@ -141,13 +150,26 @@ def build_direct_builtin_row(
|
|||||||
figure,
|
figure,
|
||||||
no_plus_women,
|
no_plus_women,
|
||||||
no_black,
|
no_black,
|
||||||
|
clothing_rng=clothing_rng,
|
||||||
)
|
)
|
||||||
elif category == "man":
|
elif category == "man":
|
||||||
row = g.make_single(index, batch, rng, "man", expr_deck, row_clothing, ethnicity, row_poses, backside_bias, figure)
|
row = g.make_single(
|
||||||
|
index,
|
||||||
|
batch,
|
||||||
|
rng,
|
||||||
|
"man",
|
||||||
|
expr_deck,
|
||||||
|
row_clothing,
|
||||||
|
ethnicity,
|
||||||
|
row_poses,
|
||||||
|
backside_bias,
|
||||||
|
figure,
|
||||||
|
clothing_rng=clothing_rng,
|
||||||
|
)
|
||||||
elif category == "couple":
|
elif category == "couple":
|
||||||
row = g.make_couple(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women)
|
row = g.make_couple(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women, clothing_rng=clothing_rng)
|
||||||
elif category == "group_or_layout":
|
elif category == "group_or_layout":
|
||||||
row = g.make_group_or_layout(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women)
|
row = g.make_group_or_layout(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women, clothing_rng=clothing_rng)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown built-in category: {category}")
|
raise ValueError(f"Unknown built-in category: {category}")
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -9,6 +9,7 @@ SEED_AXIS_SALTS = {
|
|||||||
"category": 31,
|
"category": 31,
|
||||||
"subcategory": 37,
|
"subcategory": 37,
|
||||||
"content": 41,
|
"content": 41,
|
||||||
|
"clothing": 41,
|
||||||
"person": 43,
|
"person": 43,
|
||||||
"scene": 47,
|
"scene": 47,
|
||||||
"pose": 53,
|
"pose": 53,
|
||||||
@@ -20,7 +21,8 @@ SEED_AXIS_SALTS = {
|
|||||||
SEED_AXIS_ALIASES = {
|
SEED_AXIS_ALIASES = {
|
||||||
"category": ("category_seed", "category"),
|
"category": ("category_seed", "category"),
|
||||||
"subcategory": ("subcategory_seed", "subcategory"),
|
"subcategory": ("subcategory_seed", "subcategory"),
|
||||||
"content": ("content_seed", "item_seed", "outfit_seed", "sexual_pose_seed", "content"),
|
"content": ("content_seed", "item_seed", "sexual_pose_seed", "content"),
|
||||||
|
"clothing": ("clothing_seed", "outfit_seed", "wardrobe_seed", "content_seed", "content"),
|
||||||
"person": ("person_seed", "appearance_seed", "cast_seed", "person"),
|
"person": ("person_seed", "appearance_seed", "cast_seed", "person"),
|
||||||
"scene": ("scene_seed", "scene"),
|
"scene": ("scene_seed", "scene"),
|
||||||
"pose": ("pose_seed", "sexual_pose_seed", "pose"),
|
"pose": ("pose_seed", "sexual_pose_seed", "pose"),
|
||||||
@@ -33,6 +35,7 @@ SEED_LOCK_AXES = (
|
|||||||
"category",
|
"category",
|
||||||
"subcategory",
|
"subcategory",
|
||||||
"content",
|
"content",
|
||||||
|
"clothing",
|
||||||
"person",
|
"person",
|
||||||
"scene",
|
"scene",
|
||||||
"pose",
|
"pose",
|
||||||
@@ -46,6 +49,7 @@ SEED_REROLL_GROUPS = {
|
|||||||
"category": ("category",),
|
"category": ("category",),
|
||||||
"subcategory": ("subcategory",),
|
"subcategory": ("subcategory",),
|
||||||
"content": ("content",),
|
"content": ("content",),
|
||||||
|
"clothing": ("clothing",),
|
||||||
"person": ("person",),
|
"person": ("person",),
|
||||||
"scene": ("scene",),
|
"scene": ("scene",),
|
||||||
"pose": ("pose", "role"),
|
"pose": ("pose", "role"),
|
||||||
@@ -53,6 +57,8 @@ SEED_REROLL_GROUPS = {
|
|||||||
"expression": ("expression",),
|
"expression": ("expression",),
|
||||||
"composition": ("composition",),
|
"composition": ("composition",),
|
||||||
"content_pose": ("content", "pose", "role"),
|
"content_pose": ("content", "pose", "role"),
|
||||||
|
"content_clothing": ("content", "clothing"),
|
||||||
|
"clothing_pose": ("clothing", "pose", "role"),
|
||||||
"scene_pose": ("scene", "pose", "role"),
|
"scene_pose": ("scene", "pose", "role"),
|
||||||
}
|
}
|
||||||
SEED_REROLL_AXIS_CHOICES = list(SEED_REROLL_GROUPS.keys())
|
SEED_REROLL_AXIS_CHOICES = list(SEED_REROLL_GROUPS.keys())
|
||||||
@@ -87,6 +93,8 @@ def normalize_reroll_axis(value: Any) -> str:
|
|||||||
normalized = _normal_key(value)
|
normalized = _normal_key(value)
|
||||||
aliases = {
|
aliases = {
|
||||||
"contentpose": "content_pose",
|
"contentpose": "content_pose",
|
||||||
|
"contentclothing": "content_clothing",
|
||||||
|
"clothingpose": "clothing_pose",
|
||||||
"scenepose": "scene_pose",
|
"scenepose": "scene_pose",
|
||||||
}
|
}
|
||||||
normalized = aliases.get(normalized, normalized)
|
normalized = aliases.get(normalized, normalized)
|
||||||
@@ -116,6 +124,8 @@ def build_seed_config_json(
|
|||||||
role_seed_mode: str = "auto",
|
role_seed_mode: str = "auto",
|
||||||
expression_seed_mode: str = "auto",
|
expression_seed_mode: str = "auto",
|
||||||
composition_seed_mode: str = "auto",
|
composition_seed_mode: str = "auto",
|
||||||
|
clothing_seed: int = -1,
|
||||||
|
clothing_seed_mode: str = "auto",
|
||||||
) -> str:
|
) -> str:
|
||||||
rng = random.SystemRandom()
|
rng = random.SystemRandom()
|
||||||
|
|
||||||
@@ -134,6 +144,7 @@ def build_seed_config_json(
|
|||||||
"category_seed": axis_seed(category_seed, category_seed_mode),
|
"category_seed": axis_seed(category_seed, category_seed_mode),
|
||||||
"subcategory_seed": axis_seed(subcategory_seed, subcategory_seed_mode),
|
"subcategory_seed": axis_seed(subcategory_seed, subcategory_seed_mode),
|
||||||
"content_seed": axis_seed(content_seed, content_seed_mode),
|
"content_seed": axis_seed(content_seed, content_seed_mode),
|
||||||
|
"clothing_seed": axis_seed(clothing_seed, clothing_seed_mode),
|
||||||
"person_seed": axis_seed(person_seed, person_seed_mode),
|
"person_seed": axis_seed(person_seed, person_seed_mode),
|
||||||
"scene_seed": axis_seed(scene_seed, scene_seed_mode),
|
"scene_seed": axis_seed(scene_seed, scene_seed_mode),
|
||||||
"pose_seed": axis_seed(pose_seed, pose_seed_mode),
|
"pose_seed": axis_seed(pose_seed, pose_seed_mode),
|
||||||
|
|||||||
@@ -1273,11 +1273,11 @@ def _pair_seed_pose_reroll_check(seed: int) -> dict[str, Any]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _pair_seed_content_reroll_check(seed: int) -> dict[str, Any]:
|
def _pair_seed_clothing_reroll_check(seed: int) -> dict[str, Any]:
|
||||||
return _pair_seed_reroll_check(
|
return _pair_seed_reroll_check(
|
||||||
seed,
|
seed,
|
||||||
name="pair_seed.content_reroll",
|
name="pair_seed.clothing_reroll",
|
||||||
reroll_axis="content",
|
reroll_axis="clothing",
|
||||||
changed_fields=("soft_item",),
|
changed_fields=("soft_item",),
|
||||||
stable_fields=(
|
stable_fields=(
|
||||||
"shared_cast_descriptors",
|
"shared_cast_descriptors",
|
||||||
@@ -1432,7 +1432,7 @@ def _pair_seed_checks(seed: int) -> list[dict[str, Any]]:
|
|||||||
_pair_seed_determinism_check(seed),
|
_pair_seed_determinism_check(seed),
|
||||||
_pair_seed_person_reroll_check(seed),
|
_pair_seed_person_reroll_check(seed),
|
||||||
_pair_seed_scene_reroll_check(seed),
|
_pair_seed_scene_reroll_check(seed),
|
||||||
_pair_seed_content_reroll_check(seed),
|
_pair_seed_clothing_reroll_check(seed),
|
||||||
_pair_seed_pose_reroll_check(seed),
|
_pair_seed_pose_reroll_check(seed),
|
||||||
_pair_seed_expression_reroll_check(seed),
|
_pair_seed_expression_reroll_check(seed),
|
||||||
_pair_seed_composition_reroll_check(seed),
|
_pair_seed_composition_reroll_check(seed),
|
||||||
|
|||||||
+555
-55
@@ -479,6 +479,10 @@ def _prompt_row(
|
|||||||
subcategory: str,
|
subcategory: str,
|
||||||
seed: int,
|
seed: int,
|
||||||
seed_config: str | dict[str, Any] | None = None,
|
seed_config: str | dict[str, Any] | None = None,
|
||||||
|
clothing: str = "random",
|
||||||
|
poses: str = "random",
|
||||||
|
minimal_clothing_ratio: float = 0.5,
|
||||||
|
standard_pose_ratio: float = 0.5,
|
||||||
character_cast: str = "",
|
character_cast: str = "",
|
||||||
women_count: int = 1,
|
women_count: int = 1,
|
||||||
men_count: int = 1,
|
men_count: int = 1,
|
||||||
@@ -494,15 +498,15 @@ def _prompt_row(
|
|||||||
row_number=1,
|
row_number=1,
|
||||||
start_index=1,
|
start_index=1,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
clothing="random",
|
clothing=clothing,
|
||||||
ethnicity="any",
|
ethnicity="any",
|
||||||
poses="random",
|
poses=poses,
|
||||||
backside_bias=0.35,
|
backside_bias=0.35,
|
||||||
figure="random",
|
figure="random",
|
||||||
no_plus_women=False,
|
no_plus_women=False,
|
||||||
no_black=False,
|
no_black=False,
|
||||||
minimal_clothing_ratio=0.5,
|
minimal_clothing_ratio=minimal_clothing_ratio,
|
||||||
standard_pose_ratio=0.5,
|
standard_pose_ratio=standard_pose_ratio,
|
||||||
trigger=Trigger,
|
trigger=Trigger,
|
||||||
prepend_trigger_to_prompt=True,
|
prepend_trigger_to_prompt=True,
|
||||||
extra_positive="",
|
extra_positive="",
|
||||||
@@ -645,10 +649,17 @@ def smoke_builtin_single() -> None:
|
|||||||
_expect(hair in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost hair")
|
_expect(hair in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost hair")
|
||||||
_expect(eyes in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost eyes")
|
_expect(eyes in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost eyes")
|
||||||
item_anchor = " ".join(re.findall(r"[a-z0-9]+", item.lower())[:3])
|
item_anchor = " ".join(re.findall(r"[a-z0-9]+", item.lower())[:3])
|
||||||
pose_anchor = " ".join(re.findall(r"[a-z0-9]+", pose.lower())[:4])
|
pose_anchor_tokens = [
|
||||||
|
token
|
||||||
|
for token in re.findall(r"[a-z0-9]+", pose.lower())
|
||||||
|
if token not in {"a", "an", "and", "both", "by", "in", "of", "on", "the", "to", "with"}
|
||||||
|
][:4]
|
||||||
sdxl_metadata_prompt = str(sdxl_metadata.get("sdxl_prompt", "")).lower()
|
sdxl_metadata_prompt = str(sdxl_metadata.get("sdxl_prompt", "")).lower()
|
||||||
_expect(item_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit item")
|
_expect(item_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit item")
|
||||||
_expect(pose_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit pose")
|
_expect(
|
||||||
|
bool(pose_anchor_tokens) and all(token in sdxl_metadata_prompt for token in pose_anchor_tokens),
|
||||||
|
"SDXL metadata-only built-in route lost explicit pose",
|
||||||
|
)
|
||||||
for body_tag in sdxl_tag_policy.split_tag_text(body_phrase):
|
for body_tag in sdxl_tag_policy.split_tag_text(body_phrase):
|
||||||
_expect(body_tag.lower() in sdxl_metadata_prompt, f"SDXL metadata-only built-in route lost body tag: {body_tag}")
|
_expect(body_tag.lower() in sdxl_metadata_prompt, f"SDXL metadata-only built-in route lost body tag: {body_tag}")
|
||||||
_expect(skin.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost skin")
|
_expect(skin.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost skin")
|
||||||
@@ -1396,7 +1407,7 @@ def smoke_builder_prompt_route_policy() -> None:
|
|||||||
_expect(custom_trace.get("branch") == "custom", "Builder custom generation_trace lost branch")
|
_expect(custom_trace.get("branch") == "custom", "Builder custom generation_trace lost branch")
|
||||||
_expect(custom_trace.get("source") == "json_category", "Builder custom generation_trace lost source")
|
_expect(custom_trace.get("source") == "json_category", "Builder custom generation_trace lost source")
|
||||||
_expect(custom_trace.get("category_slug") == "casual_clothes", "Builder custom generation_trace lost category slug")
|
_expect(custom_trace.get("category_slug") == "casual_clothes", "Builder custom generation_trace lost category slug")
|
||||||
_expect(custom_trace.get("content_seed_axis") == "content", "Builder custom generation_trace lost content axis")
|
_expect(custom_trace.get("content_seed_axis") == "clothing", "Builder custom generation_trace lost clothing axis")
|
||||||
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("source") == "configured", "Builder custom generation_trace lost configured content seed")
|
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("source") == "configured", "Builder custom generation_trace lost configured content seed")
|
||||||
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("seed") == 3502, "Builder custom generation_trace lost content seed value")
|
_expect(custom_trace.get("seed_axes", {}).get("content", {}).get("seed") == 3502, "Builder custom generation_trace lost content seed value")
|
||||||
_expect("typed builder route marker" in typed_route.row.get("prompt", ""), "Builder prompt route lost extra positive")
|
_expect("typed builder route marker" in typed_route.row.get("prompt", ""), "Builder prompt route lost extra positive")
|
||||||
@@ -2493,7 +2504,7 @@ def smoke_row_category_route_policy() -> None:
|
|||||||
hardcore_position_config={},
|
hardcore_position_config={},
|
||||||
)
|
)
|
||||||
_expect(casual_route["category"]["slug"] == "casual_clothes", "Row category route selected wrong casual category")
|
_expect(casual_route["category"]["slug"] == "casual_clothes", "Row category route selected wrong casual category")
|
||||||
_expect(casual_route["content_axis"] == "content", "Non-pose category should use content seed axis")
|
_expect(casual_route["content_axis"] == "clothing", "Casual clothes category should use clothing seed axis")
|
||||||
_expect(casual_route["is_pose_category"] is False, "Non-pose category should not be marked as pose content")
|
_expect(casual_route["is_pose_category"] is False, "Non-pose category should not be marked as pose content")
|
||||||
|
|
||||||
exposed_route = row_category_route.select_category_item_route(
|
exposed_route = row_category_route.select_category_item_route(
|
||||||
@@ -2507,9 +2518,35 @@ def smoke_row_category_route_policy() -> None:
|
|||||||
hardcore_position_config={},
|
hardcore_position_config={},
|
||||||
)
|
)
|
||||||
_expect(exposed_route["subcategory"]["slug"] == "sheer_exposed", "Row category route selected wrong exposed category")
|
_expect(exposed_route["subcategory"]["slug"] == "sheer_exposed", "Row category route selected wrong exposed category")
|
||||||
_expect(exposed_route["content_axis"] == "content", "Exposed clothing slug should not be treated as pose content")
|
_expect(exposed_route["content_axis"] == "clothing", "Exposed clothing slug should use clothing seed axis")
|
||||||
_expect(exposed_route["is_pose_category"] is False, "Exposed clothing slug should not be marked as pose content")
|
_expect(exposed_route["is_pose_category"] is False, "Exposed clothing slug should not be marked as pose content")
|
||||||
|
|
||||||
|
generic_categories = [
|
||||||
|
{
|
||||||
|
"name": "Lighting mood",
|
||||||
|
"slug": "lighting_mood",
|
||||||
|
"subcategories": [
|
||||||
|
{
|
||||||
|
"name": "Window light",
|
||||||
|
"slug": "window_light",
|
||||||
|
"items": ["soft window glow", "warm rim light"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
generic_route = row_category_route.select_category_item_route(
|
||||||
|
category_choice="Lighting mood",
|
||||||
|
subcategory_choice="Window light",
|
||||||
|
seed_config=seed_cfg,
|
||||||
|
seed=2303,
|
||||||
|
row_number=1,
|
||||||
|
women_count=1,
|
||||||
|
men_count=0,
|
||||||
|
hardcore_position_config={},
|
||||||
|
categories=generic_categories,
|
||||||
|
)
|
||||||
|
_expect(generic_route["content_axis"] == "content", "Generic non-clothing category should keep content seed axis")
|
||||||
|
|
||||||
|
|
||||||
def smoke_row_generation_policy() -> None:
|
def smoke_row_generation_policy() -> None:
|
||||||
_expect(pb._ratio_or_none(-1) is None, "Prompt builder ratio helper should treat negative as unset")
|
_expect(pb._ratio_or_none(-1) is None, "Prompt builder ratio helper should treat negative as unset")
|
||||||
@@ -2557,6 +2594,36 @@ def smoke_row_generation_policy() -> None:
|
|||||||
pb._build_direct_builtin_row(**direct_args) == row_generation.build_direct_builtin_row(**direct_args),
|
pb._build_direct_builtin_row(**direct_args) == row_generation.build_direct_builtin_row(**direct_args),
|
||||||
"Prompt builder direct built-in row should delegate to row_generation",
|
"Prompt builder direct built-in row should delegate to row_generation",
|
||||||
)
|
)
|
||||||
|
direct_random_args = {**direct_args, "clothing": "random", "minimal_clothing_ratio": 0.5}
|
||||||
|
direct_locked = row_generation.build_direct_builtin_row(
|
||||||
|
**direct_random_args,
|
||||||
|
seed_config=seed_config.parse_seed_config(pb.build_seed_lock_config_json(base_seed=5050)),
|
||||||
|
)
|
||||||
|
direct_clothing_reroll = row_generation.build_direct_builtin_row(
|
||||||
|
**direct_random_args,
|
||||||
|
seed_config=seed_config.parse_seed_config(
|
||||||
|
pb.build_seed_lock_config_json(base_seed=5050, reroll_axis="clothing", reroll_seed=5052)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
direct_stable_keys = ("primary_subject", "age_band", "body_type", "scene", "composition", "pose_mode")
|
||||||
|
_expect(
|
||||||
|
tuple(direct_locked.get(key) for key in direct_stable_keys)
|
||||||
|
== tuple(direct_clothing_reroll.get(key) for key in direct_stable_keys),
|
||||||
|
"Direct built-in clothing reroll should keep non-clothing row fields stable",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
(
|
||||||
|
direct_locked.get("clothing_mode"),
|
||||||
|
direct_locked.get("prompt"),
|
||||||
|
direct_locked.get("caption"),
|
||||||
|
)
|
||||||
|
!= (
|
||||||
|
direct_clothing_reroll.get("clothing_mode"),
|
||||||
|
direct_clothing_reroll.get("prompt"),
|
||||||
|
direct_clothing_reroll.get("caption"),
|
||||||
|
),
|
||||||
|
"Direct built-in clothing reroll should change clothing mode or clothing text",
|
||||||
|
)
|
||||||
auto_args = dict(
|
auto_args = dict(
|
||||||
row_number=2,
|
row_number=2,
|
||||||
start_index=41,
|
start_index=41,
|
||||||
@@ -5820,6 +5887,8 @@ def smoke_category_subcategory_matrix() -> None:
|
|||||||
_expect(isinstance(row.get("position_keys"), list), f"{name}.position_keys missing")
|
_expect(isinstance(row.get("position_keys"), list), f"{name}.position_keys missing")
|
||||||
_expect(isinstance(row.get("item_template_metadata"), dict), f"{name}.item_template_metadata missing")
|
_expect(isinstance(row.get("item_template_metadata"), dict), f"{name}.item_template_metadata missing")
|
||||||
_expect(row.get("item_template_metadata"), f"{name}.item_template_metadata should not be empty")
|
_expect(row.get("item_template_metadata"), f"{name}.item_template_metadata should not be empty")
|
||||||
|
elif row_category_route.is_clothing_content_category(category, subcategory):
|
||||||
|
_expect(row.get("content_seed_axis") == "clothing", f"{name}.content_seed_axis should be clothing")
|
||||||
else:
|
else:
|
||||||
_expect(row.get("content_seed_axis") == "content", f"{name}.content_seed_axis should be content")
|
_expect(row.get("content_seed_axis") == "content", f"{name}.content_seed_axis should be content")
|
||||||
|
|
||||||
@@ -13902,6 +13971,10 @@ def smoke_node_utility_registration() -> None:
|
|||||||
seed_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedControl"]
|
seed_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedControl"]
|
||||||
seed_inputs = seed_control.INPUT_TYPES().get("required") or {}
|
seed_inputs = seed_control.INPUT_TYPES().get("required") or {}
|
||||||
_expect("category_seed_mode" in seed_inputs, "Seed Control lost category seed mode input")
|
_expect("category_seed_mode" in seed_inputs, "Seed Control lost category seed mode input")
|
||||||
|
_expect("clothing_seed_mode" in seed_inputs, "Seed Control lost clothing seed mode input")
|
||||||
|
_expect("clothing_seed" in seed_inputs, "Seed Control lost clothing seed input")
|
||||||
|
seed_control_js = (ROOT / "web" / "seed_control.js").read_text(encoding="utf-8")
|
||||||
|
_expect('"clothing"' in seed_control_js, "Seed Control frontend random-lock axes should include clothing")
|
||||||
_expect("tooltip" in seed_inputs["category_seed_mode"][1], "Seed Control tooltip injection missing")
|
_expect("tooltip" in seed_inputs["category_seed_mode"][1], "Seed Control tooltip injection missing")
|
||||||
_expect(seed_control.RETURN_NAMES == ("seed_config", "summary"), "Seed Control lost visible summary output")
|
_expect(seed_control.RETURN_NAMES == ("seed_config", "summary"), "Seed Control lost visible summary output")
|
||||||
category_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "category_seed_mode")
|
category_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "category_seed_mode")
|
||||||
@@ -13910,6 +13983,8 @@ def smoke_node_utility_registration() -> None:
|
|||||||
and "field value stays unchanged" in category_seed_tooltip,
|
and "field value stays unchanged" in category_seed_tooltip,
|
||||||
"Node tooltip policy lost Seed Control override",
|
"Node tooltip policy lost Seed Control override",
|
||||||
)
|
)
|
||||||
|
clothing_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "clothing_seed_mode")
|
||||||
|
_expect("clothing/outfit" in clothing_seed_tooltip, "Node tooltip policy lost Seed Control clothing override")
|
||||||
_expect(
|
_expect(
|
||||||
"Autoscaling switch input" in node_tooltips._tooltip_for_input("SxCPIndexSwitch", "input_12"),
|
"Autoscaling switch input" in node_tooltips._tooltip_for_input("SxCPIndexSwitch", "input_12"),
|
||||||
"Node tooltip policy lost autoscaling input fallback",
|
"Node tooltip policy lost autoscaling input fallback",
|
||||||
@@ -13933,11 +14008,14 @@ def smoke_node_utility_registration() -> None:
|
|||||||
-1,
|
-1,
|
||||||
"fixed",
|
"fixed",
|
||||||
999,
|
999,
|
||||||
|
"fixed",
|
||||||
|
222,
|
||||||
)
|
)
|
||||||
parsed_seed_control = json.loads(seed_control_config)
|
parsed_seed_control = json.loads(seed_control_config)
|
||||||
_expect(parsed_seed_control.get("category_seed") == 0, "Seed Control fixed mode did not clamp negative seed")
|
_expect(parsed_seed_control.get("category_seed") == 0, "Seed Control fixed mode did not clamp negative seed")
|
||||||
_expect(parsed_seed_control.get("subcategory_seed") == -1, "Seed Control follow_main mode should emit -1")
|
_expect(parsed_seed_control.get("subcategory_seed") == -1, "Seed Control follow_main mode should emit -1")
|
||||||
_expect(int(parsed_seed_control.get("content_seed", -1)) >= 0, "Seed Control random mode did not emit resolved seed")
|
_expect(int(parsed_seed_control.get("content_seed", -1)) >= 0, "Seed Control random mode did not emit resolved seed")
|
||||||
|
_expect(parsed_seed_control.get("clothing_seed") == 222, "Seed Control fixed clothing seed changed")
|
||||||
_expect(parsed_seed_control.get("person_seed") == 123, "Seed Control auto mode did not preserve explicit value")
|
_expect(parsed_seed_control.get("person_seed") == 123, "Seed Control auto mode did not preserve explicit value")
|
||||||
_expect(parsed_seed_control.get("pose_seed") == 777, "Seed Control fixed mode did not preserve positive seed")
|
_expect(parsed_seed_control.get("pose_seed") == 777, "Seed Control fixed mode did not preserve positive seed")
|
||||||
_expect(parsed_seed_control.get("role_seed") == -1, "Seed Control follow_main role did not emit -1")
|
_expect(parsed_seed_control.get("role_seed") == -1, "Seed Control follow_main role did not emit -1")
|
||||||
@@ -13948,6 +14026,34 @@ def smoke_node_utility_registration() -> None:
|
|||||||
f"content={parsed_seed_control['content_seed']}" in seed_control_summary,
|
f"content={parsed_seed_control['content_seed']}" in seed_control_summary,
|
||||||
"Seed Control summary lost random resolved seed value",
|
"Seed Control summary lost random resolved seed value",
|
||||||
)
|
)
|
||||||
|
legacy_seed_control_config, _legacy_seed_control_summary = seed_control().build(
|
||||||
|
"fixed",
|
||||||
|
-1,
|
||||||
|
"follow_main",
|
||||||
|
-1,
|
||||||
|
"random",
|
||||||
|
-1,
|
||||||
|
"auto",
|
||||||
|
123,
|
||||||
|
"auto",
|
||||||
|
456,
|
||||||
|
"fixed",
|
||||||
|
777,
|
||||||
|
"follow_main",
|
||||||
|
888,
|
||||||
|
"auto",
|
||||||
|
321,
|
||||||
|
"fixed",
|
||||||
|
999,
|
||||||
|
)
|
||||||
|
legacy_seed_control = json.loads(legacy_seed_control_config)
|
||||||
|
_expect(legacy_seed_control.get("clothing_seed") == -1, "Legacy Seed Control call should leave clothing seed unset")
|
||||||
|
_expect(legacy_seed_control.get("person_seed") == 123, "Legacy Seed Control call shifted person seed")
|
||||||
|
_expect(legacy_seed_control.get("scene_seed") == 456, "Legacy Seed Control call shifted scene seed")
|
||||||
|
_expect(legacy_seed_control.get("pose_seed") == 777, "Legacy Seed Control call shifted pose seed")
|
||||||
|
_expect(legacy_seed_control.get("role_seed") == -1, "Legacy Seed Control call shifted role seed")
|
||||||
|
_expect(legacy_seed_control.get("expression_seed") == 321, "Legacy Seed Control call shifted expression seed")
|
||||||
|
_expect(legacy_seed_control.get("composition_seed") == 999, "Legacy Seed Control call shifted composition seed")
|
||||||
|
|
||||||
seed_locker = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedLocker"]
|
seed_locker = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedLocker"]
|
||||||
locker_inputs = seed_locker.INPUT_TYPES().get("required") or {}
|
locker_inputs = seed_locker.INPUT_TYPES().get("required") or {}
|
||||||
@@ -14098,6 +14204,11 @@ def smoke_seed_config_policy() -> None:
|
|||||||
_expect(pb.normalize_seed_mode("follow main") == "follow_main", "seed mode normalizer should accept spaced labels")
|
_expect(pb.normalize_seed_mode("follow main") == "follow_main", "seed mode normalizer should accept spaced labels")
|
||||||
_expect(pb.normalize_seed_mode("FOLLOW-MAIN") == "follow_main", "seed mode normalizer should accept hyphenated labels")
|
_expect(pb.normalize_seed_mode("FOLLOW-MAIN") == "follow_main", "seed mode normalizer should accept hyphenated labels")
|
||||||
_expect(pb.normalize_reroll_axis("content pose") == "content_pose", "reroll axis normalizer should accept spaced labels")
|
_expect(pb.normalize_reroll_axis("content pose") == "content_pose", "reroll axis normalizer should accept spaced labels")
|
||||||
|
reroll_choices = pb.seed_reroll_axis_choices()
|
||||||
|
for expected_axis in ("clothing", "content_clothing", "clothing_pose"):
|
||||||
|
_expect(expected_axis in reroll_choices, f"seed reroll axis choices missing {expected_axis}")
|
||||||
|
_expect(pb.normalize_reroll_axis("clothing pose") == "clothing_pose", "reroll axis normalizer should accept clothing pose")
|
||||||
|
_expect(pb.normalize_reroll_axis("content clothing") == "content_clothing", "reroll axis normalizer should accept content clothing")
|
||||||
|
|
||||||
fixed_config = json.loads(
|
fixed_config = json.loads(
|
||||||
pb.build_seed_config_json(
|
pb.build_seed_config_json(
|
||||||
@@ -14116,9 +14227,21 @@ def smoke_seed_config_policy() -> None:
|
|||||||
_expect(fixed_config["pose_seed"] == -1, "follow_main seed mode should emit unlocked axis")
|
_expect(fixed_config["pose_seed"] == -1, "follow_main seed mode should emit unlocked axis")
|
||||||
_expect(fixed_config["role_seed"] == 789, "auto seed mode should preserve numeric seed")
|
_expect(fixed_config["role_seed"] == 789, "auto seed mode should preserve numeric seed")
|
||||||
|
|
||||||
parsed = pb._parse_seed_config({"item_seed": "44", "pose_seed": "55", "bad": "nope"})
|
parsed = pb._parse_seed_config({"item_seed": "44", "pose_seed": "55", "outfit_seed": "66", "bad": "nope"})
|
||||||
_expect(parsed == {"item_seed": 44, "pose_seed": 55}, "seed parser should keep integer-like values only")
|
_expect(
|
||||||
|
parsed == {"item_seed": 44, "pose_seed": 55, "outfit_seed": 66},
|
||||||
|
"seed parser should keep integer-like values only",
|
||||||
|
)
|
||||||
_expect(pb._configured_axis_seed(parsed, "content") == 44, "content axis should honor item_seed alias")
|
_expect(pb._configured_axis_seed(parsed, "content") == 44, "content axis should honor item_seed alias")
|
||||||
|
_expect(pb._configured_axis_seed(parsed, "clothing") == 66, "clothing axis should honor outfit_seed alias")
|
||||||
|
_expect(
|
||||||
|
pb._configured_axis_seed({"content_seed": 77}, "clothing") == 77,
|
||||||
|
"clothing axis should keep content_seed as a legacy fallback",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pb._configured_axis_seed({"content_seed": 77, "clothing_seed": 88}, "clothing") == 88,
|
||||||
|
"clothing_seed should override legacy content_seed fallback",
|
||||||
|
)
|
||||||
_expect(pb._configured_axis_seed(parsed, "role") == 55, "role axis should honor pose seed alias")
|
_expect(pb._configured_axis_seed(parsed, "role") == 55, "role axis should honor pose seed alias")
|
||||||
_expect(
|
_expect(
|
||||||
seed_config.configured_seed_from_axes({"camera_seed": "88", "content_seed": "99"}, ("composition", "content")) == 88,
|
seed_config.configured_seed_from_axes({"camera_seed": "88", "content_seed": "99"}, ("composition", "content")) == 88,
|
||||||
@@ -14133,9 +14256,30 @@ def smoke_seed_config_policy() -> None:
|
|||||||
_expect(locked["content_seed"] == 999, "content_pose reroll should alter content seed")
|
_expect(locked["content_seed"] == 999, "content_pose reroll should alter content seed")
|
||||||
_expect(locked["pose_seed"] == 999 and locked["role_seed"] == 999, "content_pose reroll should alter pose and role seeds")
|
_expect(locked["pose_seed"] == 999 and locked["role_seed"] == 999, "content_pose reroll should alter pose and role seeds")
|
||||||
_expect(locked["scene_seed"] == 100, "content_pose reroll should leave scene locked")
|
_expect(locked["scene_seed"] == 100, "content_pose reroll should leave scene locked")
|
||||||
axis_trace = seed_config.axis_seed_trace({"content_seed": 44}, 99, 3, axes=("content", "scene"))
|
clothing_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="clothing", reroll_seed=777))
|
||||||
|
_expect(clothing_locked["clothing_seed"] == 777, "clothing reroll should alter clothing seed")
|
||||||
|
_expect(clothing_locked["content_seed"] == 100, "clothing reroll should leave content locked")
|
||||||
|
_expect(clothing_locked["pose_seed"] == 100 and clothing_locked["role_seed"] == 100, "clothing reroll should leave pose and role locked")
|
||||||
|
|
||||||
|
content_clothing_locked = json.loads(
|
||||||
|
pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content_clothing", reroll_seed=778)
|
||||||
|
)
|
||||||
|
_expect(content_clothing_locked["content_seed"] == 778, "content_clothing reroll should alter content seed")
|
||||||
|
_expect(content_clothing_locked["clothing_seed"] == 778, "content_clothing reroll should alter clothing seed")
|
||||||
|
_expect(content_clothing_locked["pose_seed"] == 100, "content_clothing reroll should leave pose locked")
|
||||||
|
|
||||||
|
clothing_pose_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="clothing_pose", reroll_seed=779))
|
||||||
|
_expect(clothing_pose_locked["clothing_seed"] == 779, "clothing_pose reroll should alter clothing seed")
|
||||||
|
_expect(clothing_pose_locked["pose_seed"] == 779 and clothing_pose_locked["role_seed"] == 779, "clothing_pose reroll should alter pose and role seeds")
|
||||||
|
_expect(clothing_pose_locked["content_seed"] == 100, "clothing_pose reroll should leave content locked")
|
||||||
|
|
||||||
|
content_pose_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content_pose", reroll_seed=780))
|
||||||
|
_expect(content_pose_locked["clothing_seed"] == 100, "content_pose reroll should not alter clothing seed")
|
||||||
|
axis_trace = seed_config.axis_seed_trace({"content_seed": 44, "clothing_seed": 66}, 99, 3, axes=("content", "clothing", "scene"))
|
||||||
_expect(axis_trace["content"]["source"] == "configured", "Seed axis trace lost configured source")
|
_expect(axis_trace["content"]["source"] == "configured", "Seed axis trace lost configured source")
|
||||||
_expect(axis_trace["content"]["seed"] == 44, "Seed axis trace lost configured seed")
|
_expect(axis_trace["content"]["seed"] == 44, "Seed axis trace lost configured seed")
|
||||||
|
_expect(axis_trace["clothing"]["source"] == "configured", "Seed axis trace lost clothing configured source")
|
||||||
|
_expect(axis_trace["clothing"]["seed"] == 66, "Seed axis trace lost configured clothing seed")
|
||||||
_expect(axis_trace["scene"]["source"] == "main", "Seed axis trace lost main source")
|
_expect(axis_trace["scene"]["source"] == "main", "Seed axis trace lost main source")
|
||||||
_expect(axis_trace["scene"]["seed"] == 99, "Seed axis trace lost main seed")
|
_expect(axis_trace["scene"]["seed"] == 99, "Seed axis trace lost main seed")
|
||||||
_expect(
|
_expect(
|
||||||
@@ -14261,6 +14405,261 @@ def smoke_seed_config_policy() -> None:
|
|||||||
break
|
break
|
||||||
_expect(pose_changed, "pose reroll should change pose/action metadata while cast and scene stay locked")
|
_expect(pose_changed, "pose reroll should change pose/action metadata while cast and scene stay locked")
|
||||||
|
|
||||||
|
clothing_axis_seed = 42001
|
||||||
|
|
||||||
|
auto_weighted_seeded = _prompt_row(
|
||||||
|
name="seed_config_policy_auto_weighted_seed_config",
|
||||||
|
category="auto_weighted",
|
||||||
|
subcategory="random",
|
||||||
|
seed=clothing_axis_seed,
|
||||||
|
seed_config=pb.build_seed_lock_config_json(base_seed=clothing_axis_seed),
|
||||||
|
women_count=1,
|
||||||
|
men_count=0,
|
||||||
|
)
|
||||||
|
_expect(auto_weighted_seeded.get("source") == "built_in_generator", "auto_weighted prompt with seed_config should build")
|
||||||
|
|
||||||
|
def direct_builtin_woman(seed_config_value: str | dict[str, Any], *, name: str) -> dict[str, Any]:
|
||||||
|
return _prompt_row(
|
||||||
|
name=name,
|
||||||
|
category="woman",
|
||||||
|
subcategory="random",
|
||||||
|
seed=43001,
|
||||||
|
seed_config=seed_config_value,
|
||||||
|
clothing="full",
|
||||||
|
poses="standard",
|
||||||
|
minimal_clothing_ratio=-1,
|
||||||
|
standard_pose_ratio=-1,
|
||||||
|
women_count=1,
|
||||||
|
men_count=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
direct_builtin_locked = direct_builtin_woman(
|
||||||
|
pb.build_seed_lock_config_json(base_seed=43001),
|
||||||
|
name="seed_config_policy_direct_builtin_locked",
|
||||||
|
)
|
||||||
|
direct_builtin_clothing_reroll = direct_builtin_woman(
|
||||||
|
pb.build_seed_lock_config_json(base_seed=43001, reroll_axis="clothing", reroll_seed=43002),
|
||||||
|
name="seed_config_policy_direct_builtin_clothing_reroll",
|
||||||
|
)
|
||||||
|
direct_builtin_stable_fields = ("primary_subject", "age_band", "body_type", "scene", "composition", "pose_mode")
|
||||||
|
_expect(
|
||||||
|
tuple(direct_builtin_locked.get(key) for key in direct_builtin_stable_fields)
|
||||||
|
== tuple(direct_builtin_clothing_reroll.get(key) for key in direct_builtin_stable_fields),
|
||||||
|
"Prompt-level direct built-in clothing reroll should keep non-clothing fields stable",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
(
|
||||||
|
direct_builtin_locked.get("prompt"),
|
||||||
|
direct_builtin_locked.get("caption"),
|
||||||
|
)
|
||||||
|
!= (
|
||||||
|
direct_builtin_clothing_reroll.get("prompt"),
|
||||||
|
direct_builtin_clothing_reroll.get("caption"),
|
||||||
|
),
|
||||||
|
"Prompt-level direct built-in clothing reroll should change clothing text",
|
||||||
|
)
|
||||||
|
|
||||||
|
def clothing_category_row(seed_config_value: str | dict[str, Any], *, name: str = "seed_config_policy_clothing_axis") -> dict[str, Any]:
|
||||||
|
return pb.build_prompt(
|
||||||
|
category="Casual clothes",
|
||||||
|
subcategory="Casual clothes / Streetwear",
|
||||||
|
row_number=1,
|
||||||
|
start_index=1,
|
||||||
|
seed=clothing_axis_seed,
|
||||||
|
clothing="random",
|
||||||
|
ethnicity="any",
|
||||||
|
poses="standard",
|
||||||
|
backside_bias=0.0,
|
||||||
|
figure="curvy",
|
||||||
|
no_plus_women=False,
|
||||||
|
no_black=False,
|
||||||
|
minimal_clothing_ratio=0.5,
|
||||||
|
standard_pose_ratio=-1,
|
||||||
|
trigger=Trigger,
|
||||||
|
prepend_trigger_to_prompt=True,
|
||||||
|
extra_positive="",
|
||||||
|
extra_negative="",
|
||||||
|
seed_config=seed_config_value,
|
||||||
|
women_count=1,
|
||||||
|
men_count=0,
|
||||||
|
expression_enabled=True,
|
||||||
|
expression_intensity=0.6,
|
||||||
|
)
|
||||||
|
|
||||||
|
def clothing_trace(row: dict[str, Any]) -> str:
|
||||||
|
trace = row.get("generation_trace") if isinstance(row.get("generation_trace"), dict) else {}
|
||||||
|
return str(trace.get("clothing") or "")
|
||||||
|
|
||||||
|
clothing_locked_row = clothing_category_row(pb.build_seed_lock_config_json(base_seed=clothing_axis_seed))
|
||||||
|
content_pose_row = clothing_category_row(
|
||||||
|
pb.build_seed_lock_config_json(
|
||||||
|
base_seed=clothing_axis_seed,
|
||||||
|
reroll_axis="content_pose",
|
||||||
|
reroll_seed=clothing_axis_seed + 2,
|
||||||
|
),
|
||||||
|
name="seed_config_policy_content_pose_locked_clothing",
|
||||||
|
)
|
||||||
|
clothing_stable_fields = ("item", "scene_text", "subject_phrase")
|
||||||
|
_expect(
|
||||||
|
tuple(clothing_locked_row.get(key) for key in clothing_stable_fields)
|
||||||
|
== tuple(content_pose_row.get(key) for key in clothing_stable_fields),
|
||||||
|
"content_pose reroll should keep clothing-category item, scene, and subject stable when clothing is locked",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
clothing_trace(clothing_locked_row) == clothing_trace(content_pose_row),
|
||||||
|
"content_pose reroll should not change prompt clothing mode when clothing seed is locked",
|
||||||
|
)
|
||||||
|
|
||||||
|
clothing_reroll_row = clothing_category_row(
|
||||||
|
pb.build_seed_lock_config_json(
|
||||||
|
base_seed=clothing_axis_seed,
|
||||||
|
reroll_axis="clothing",
|
||||||
|
reroll_seed=clothing_axis_seed + 2,
|
||||||
|
),
|
||||||
|
name="seed_config_policy_clothing_reroll",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
tuple(clothing_locked_row.get(key) for key in ("scene_text", "pose", "subject_phrase"))
|
||||||
|
== tuple(clothing_reroll_row.get(key) for key in ("scene_text", "pose", "subject_phrase")),
|
||||||
|
"clothing reroll should keep scene, pose, and subject stable",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
(
|
||||||
|
clothing_trace(clothing_locked_row),
|
||||||
|
clothing_locked_row.get("item"),
|
||||||
|
)
|
||||||
|
!= (
|
||||||
|
clothing_trace(clothing_reroll_row),
|
||||||
|
clothing_reroll_row.get("item"),
|
||||||
|
),
|
||||||
|
"clothing reroll should change clothing mode or outfit text",
|
||||||
|
)
|
||||||
|
|
||||||
|
content_clothing_row = clothing_category_row(
|
||||||
|
pb.build_seed_lock_config_json(
|
||||||
|
base_seed=clothing_axis_seed,
|
||||||
|
reroll_axis="content_clothing",
|
||||||
|
reroll_seed=clothing_axis_seed + 2,
|
||||||
|
),
|
||||||
|
name="seed_config_policy_content_clothing_reroll",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
clothing_trace(clothing_locked_row) != clothing_trace(content_clothing_row),
|
||||||
|
"content_clothing reroll should change prompt clothing mode for clothing categories",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
clothing_locked_row.get("item") != content_clothing_row.get("item"),
|
||||||
|
"content_clothing reroll should change outfit text for clothing categories",
|
||||||
|
)
|
||||||
|
|
||||||
|
outfit_alias_config = seed_config.parse_seed_config(pb.build_seed_lock_config_json(base_seed=clothing_axis_seed))
|
||||||
|
outfit_alias_config.pop("clothing_seed", None)
|
||||||
|
outfit_alias_config["content_seed"] = clothing_axis_seed
|
||||||
|
outfit_alias_config["outfit_seed"] = clothing_axis_seed + 2
|
||||||
|
outfit_alias_row = clothing_category_row(outfit_alias_config, name="seed_config_policy_outfit_alias")
|
||||||
|
_expect(
|
||||||
|
clothing_trace(outfit_alias_row) == clothing_trace(clothing_reroll_row),
|
||||||
|
"outfit_seed alias should drive prompt clothing mode ahead of content_seed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
outfit_alias_row.get("item") == clothing_reroll_row.get("item"),
|
||||||
|
"outfit_seed alias should drive clothing-category outfit text ahead of content_seed",
|
||||||
|
)
|
||||||
|
|
||||||
|
def pair_row_outfit(seed_config_value: dict[str, int]) -> str:
|
||||||
|
route = pair_rows.build_insta_pair_rows_result(
|
||||||
|
row_number=1,
|
||||||
|
start_index=1,
|
||||||
|
seed=clothing_axis_seed,
|
||||||
|
active_trigger=Trigger,
|
||||||
|
parsed_seed_config=seed_config_value,
|
||||||
|
options={
|
||||||
|
"softcore_cast": "solo",
|
||||||
|
"softcore_expression_enabled": False,
|
||||||
|
"softcore_expression_intensity": 0.5,
|
||||||
|
"hardcore_expression_enabled": False,
|
||||||
|
"hardcore_expression_intensity": 0.5,
|
||||||
|
"hardcore_detail_density": "standard",
|
||||||
|
},
|
||||||
|
ethnicity="any",
|
||||||
|
figure="curvy",
|
||||||
|
no_plus_women=False,
|
||||||
|
no_black=False,
|
||||||
|
character_profile="",
|
||||||
|
character_cast="",
|
||||||
|
character_slot_map={},
|
||||||
|
pov_character_labels=[],
|
||||||
|
hard_women_count=1,
|
||||||
|
hard_men_count=1,
|
||||||
|
soft_category="Casual clothes",
|
||||||
|
soft_subcategory="Casual clothes / Streetwear",
|
||||||
|
softcore_level_key="suggestive",
|
||||||
|
hardcore_random_subcategory="Penetrative sex",
|
||||||
|
hardcore_position_config="",
|
||||||
|
location_config="",
|
||||||
|
composition_config="",
|
||||||
|
build_prompt=lambda **kwargs: {"prompt": "", "negative_prompt": "", "caption": ""},
|
||||||
|
axis_rng=seed_config.axis_rng,
|
||||||
|
cast_expression_intensity_override=lambda *_args: (None, "disabled"),
|
||||||
|
context_from_character_slot=lambda *_args: {},
|
||||||
|
apply_character_context_to_row=lambda row, _context: row,
|
||||||
|
disable_row_expression=lambda row, _source: row,
|
||||||
|
slot_softcore_outfit=lambda _slot, _rng: "",
|
||||||
|
softcore_outfit=lambda rng, _level: f"outfit-{rng.randint(1, 1000000)}",
|
||||||
|
softcore_pose=lambda rng, _level: f"pose-{rng.randint(1, 1000000)}",
|
||||||
|
softcore_item_prompt_label=lambda _level: "outfit",
|
||||||
|
pov_prompt_directive=lambda _labels: "",
|
||||||
|
pov_composition_prompt=lambda composition, _labels: str(composition),
|
||||||
|
)
|
||||||
|
return str(route.soft_row.get("item") or "")
|
||||||
|
|
||||||
|
pair_base_config = seed_config.parse_seed_config(pb.build_seed_lock_config_json(base_seed=clothing_axis_seed))
|
||||||
|
pair_content_reroll_config = seed_config.parse_seed_config(
|
||||||
|
pb.build_seed_lock_config_json(
|
||||||
|
base_seed=clothing_axis_seed,
|
||||||
|
reroll_axis="content",
|
||||||
|
reroll_seed=clothing_axis_seed + 4,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pair_clothing_reroll_config = seed_config.parse_seed_config(
|
||||||
|
pb.build_seed_lock_config_json(
|
||||||
|
base_seed=clothing_axis_seed,
|
||||||
|
reroll_axis="clothing",
|
||||||
|
reroll_seed=clothing_axis_seed + 4,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pair_row_outfit(pair_base_config) == pair_row_outfit(pair_content_reroll_config),
|
||||||
|
"Pair softcore primary outfit should stay stable across content reroll when clothing is locked",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pair_row_outfit(pair_base_config) != pair_row_outfit(pair_clothing_reroll_config),
|
||||||
|
"Pair softcore primary outfit should follow clothing reroll",
|
||||||
|
)
|
||||||
|
|
||||||
|
def partner_outfits(seed_config_value: dict[str, int]) -> list[str]:
|
||||||
|
return pair_cast.softcore_partner_styling(
|
||||||
|
seed_config=seed_config_value,
|
||||||
|
seed=clothing_axis_seed,
|
||||||
|
row_number=1,
|
||||||
|
women_count=2,
|
||||||
|
men_count=1,
|
||||||
|
pov_labels=[],
|
||||||
|
label_map={},
|
||||||
|
axis_rng=seed_config.axis_rng,
|
||||||
|
choose=lambda rng, values: values[rng.randrange(len(values))],
|
||||||
|
slot_softcore_outfit=lambda _slot, _rng: "",
|
||||||
|
)["outfits"]
|
||||||
|
|
||||||
|
_expect(
|
||||||
|
partner_outfits(pair_base_config) == partner_outfits(pair_content_reroll_config),
|
||||||
|
"Pair partner outfits should stay stable across content reroll when clothing is locked",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
partner_outfits(pair_base_config) != partner_outfits(pair_clothing_reroll_config),
|
||||||
|
"Pair partner outfits should follow clothing reroll",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def smoke_prompt_route_simulation_policy() -> None:
|
def smoke_prompt_route_simulation_policy() -> None:
|
||||||
report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
|
report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
|
||||||
@@ -14355,7 +14754,7 @@ def smoke_prompt_route_simulation_policy() -> None:
|
|||||||
"pair_seed.locked_determinism",
|
"pair_seed.locked_determinism",
|
||||||
"pair_seed.person_reroll",
|
"pair_seed.person_reroll",
|
||||||
"pair_seed.scene_reroll",
|
"pair_seed.scene_reroll",
|
||||||
"pair_seed.content_reroll",
|
"pair_seed.clothing_reroll",
|
||||||
"pair_seed.pose_reroll",
|
"pair_seed.pose_reroll",
|
||||||
"pair_seed.expression_reroll",
|
"pair_seed.expression_reroll",
|
||||||
"pair_seed.composition_reroll",
|
"pair_seed.composition_reroll",
|
||||||
@@ -14370,15 +14769,15 @@ def smoke_prompt_route_simulation_policy() -> None:
|
|||||||
for check_name in (
|
for check_name in (
|
||||||
"pair_seed.person_reroll",
|
"pair_seed.person_reroll",
|
||||||
"pair_seed.scene_reroll",
|
"pair_seed.scene_reroll",
|
||||||
"pair_seed.content_reroll",
|
"pair_seed.clothing_reroll",
|
||||||
"pair_seed.pose_reroll",
|
"pair_seed.pose_reroll",
|
||||||
"pair_seed.expression_reroll",
|
"pair_seed.expression_reroll",
|
||||||
"pair_seed.composition_reroll",
|
"pair_seed.composition_reroll",
|
||||||
):
|
):
|
||||||
_expect(pair_seed_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
|
_expect(pair_seed_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
|
||||||
_expect(
|
_expect(
|
||||||
pair_seed_checks["pair_seed.content_reroll"].get("changed") is True,
|
pair_seed_checks["pair_seed.clothing_reroll"].get("changed") is True,
|
||||||
"Pair content reroll should prove soft outfit/content can reroll while hard action stays locked",
|
"Pair clothing reroll should prove soft outfit can reroll while hard action stays locked",
|
||||||
)
|
)
|
||||||
_expect(
|
_expect(
|
||||||
pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True,
|
pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True,
|
||||||
@@ -16077,7 +16476,7 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
-1,
|
-1,
|
||||||
)
|
)
|
||||||
seed_fixture_scene = scene
|
seed_fixture_scene = scene
|
||||||
wardrobe_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("wardrobe", "fixed", 9981, "content", "same_for_all_rows", "replace_layer")[0]
|
wardrobe_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("wardrobe", "fixed", 9981, "clothing", "same_for_all_rows", "replace_layer")[0]
|
||||||
wardrobe_options = nodes["SxCPSceneWardrobeOptions"]().build(
|
wardrobe_options = nodes["SxCPSceneWardrobeOptions"]().build(
|
||||||
"replace",
|
"replace",
|
||||||
"woman",
|
"woman",
|
||||||
@@ -16106,7 +16505,34 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
woman_slot = next(slot for slot in slots if slot.get("subject_type") == "woman")
|
woman_slot = next(slot for slot in slots if slot.get("subject_type") == "woman")
|
||||||
_expect(woman_slot.get("softcore_outfit") == "simple black dress", "Scene Wardrobe did not update softcore outfit")
|
_expect(woman_slot.get("softcore_outfit") == "simple black dress", "Scene Wardrobe did not update softcore outfit")
|
||||||
_expect(woman_slot.get("hardcore_clothing") == "fully nude", "Scene Wardrobe did not update hardcore clothing")
|
_expect(woman_slot.get("hardcore_clothing") == "fully nude", "Scene Wardrobe did not update hardcore clothing")
|
||||||
_expect(json.loads(scene).get("seed_trace", {}).get("wardrobe", {}).get("seed") == 9981, "Scene Wardrobe seed options did not write seed trace")
|
wardrobe_trace = json.loads(scene).get("seed_trace", {}).get("wardrobe", {})
|
||||||
|
_expect(wardrobe_trace.get("seed") == 9981, "Scene Wardrobe seed options did not write seed trace")
|
||||||
|
_expect(wardrobe_trace.get("axes") == ["clothing"], "Scene Wardrobe seed options should write clothing trace axes")
|
||||||
|
wardrobe_default_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
||||||
|
"wardrobe",
|
||||||
|
"fixed",
|
||||||
|
9982,
|
||||||
|
"none",
|
||||||
|
"same_for_all_rows",
|
||||||
|
"replace_layer",
|
||||||
|
)[0]
|
||||||
|
wardrobe_default_scene = nodes["SxCPSceneWardrobe"]().build(
|
||||||
|
seed_fixture_scene,
|
||||||
|
True,
|
||||||
|
"woman",
|
||||||
|
"A",
|
||||||
|
"full",
|
||||||
|
"simple black dress",
|
||||||
|
"fully nude",
|
||||||
|
"",
|
||||||
|
wardrobe_options=wardrobe_options,
|
||||||
|
seed_options=wardrobe_default_seed_options,
|
||||||
|
)[0]
|
||||||
|
wardrobe_default_trace = json.loads(wardrobe_default_scene).get("seed_trace", {}).get("wardrobe", {})
|
||||||
|
_expect(
|
||||||
|
wardrobe_default_trace.get("axes") == ["clothing"],
|
||||||
|
"Scene Wardrobe default seed axis should resolve to clothing",
|
||||||
|
)
|
||||||
|
|
||||||
location_options = nodes["SxCPSceneLocationLayoutOptions"]().build(
|
location_options = nodes["SxCPSceneLocationLayoutOptions"]().build(
|
||||||
"replace",
|
"replace",
|
||||||
@@ -16225,32 +16651,14 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
hard_trace_axes.get("pose", {}).get("seed") == 7799,
|
hard_trace_axes.get("pose", {}).get("seed") == 7799,
|
||||||
"Scene Pair Output generation trace did not use hardcore branch pose seed",
|
"Scene Pair Output generation trace did not use hardcore branch pose seed",
|
||||||
)
|
)
|
||||||
soft_content_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
soft_clothing_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
||||||
"softcore_branch",
|
"softcore_branch",
|
||||||
"fixed",
|
"fixed",
|
||||||
6679,
|
6679,
|
||||||
"content",
|
"clothing",
|
||||||
"same_for_all_rows",
|
"same_for_all_rows",
|
||||||
"replace_layer",
|
"replace_layer",
|
||||||
)[0]
|
)[0]
|
||||||
seeded_soft_scene, seeded_hard_scene, _summary, _metadata = nodes["SxCPSceneBranchPair"]().build(
|
|
||||||
seed_fixture_scene,
|
|
||||||
"same_creator_same_room",
|
|
||||||
"hybrid",
|
|
||||||
branch_options=branch_options,
|
|
||||||
)
|
|
||||||
seeded_soft_scene = nodes["SxCPSoftcoreBranchOptions"]().build(
|
|
||||||
seeded_soft_scene,
|
|
||||||
"same_as_hardcore",
|
|
||||||
"lingerie_tease",
|
|
||||||
True,
|
|
||||||
0.45,
|
|
||||||
"from_camera_config",
|
|
||||||
"compact",
|
|
||||||
"",
|
|
||||||
branch_options=branch_options,
|
|
||||||
seed_options=soft_content_seed_options,
|
|
||||||
)[0]
|
|
||||||
default_hardcore_continuity = (
|
default_hardcore_continuity = (
|
||||||
nodes["SxCPHardcoreBranchOptions"].INPUT_TYPES()
|
nodes["SxCPHardcoreBranchOptions"].INPUT_TYPES()
|
||||||
.get("required", {})
|
.get("required", {})
|
||||||
@@ -16261,28 +16669,76 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
default_hardcore_continuity == "partially_removed",
|
default_hardcore_continuity == "partially_removed",
|
||||||
"Hardcore Branch Options default should inherit softcore outfit continuity",
|
"Hardcore Branch Options default should inherit softcore outfit continuity",
|
||||||
)
|
)
|
||||||
seeded_hard_scene = nodes["SxCPHardcoreBranchOptions"]().build(
|
|
||||||
seeded_hard_scene,
|
def _soft_clothing_pair(clothing_seed: int, seed_options: str | None = None) -> dict[str, Any]:
|
||||||
"couple",
|
soft_seed_options = seed_options or nodes["SxCPSceneLayerSeedOptions"]().build(
|
||||||
1,
|
"softcore_branch",
|
||||||
1,
|
"fixed",
|
||||||
"hardcore",
|
clothing_seed,
|
||||||
True,
|
"clothing",
|
||||||
0.85,
|
"same_for_all_rows",
|
||||||
default_hardcore_continuity,
|
"replace_layer",
|
||||||
"from_camera_config",
|
)[0]
|
||||||
"compact",
|
seeded_soft_scene, seeded_hard_scene, _summary, _metadata = nodes["SxCPSceneBranchPair"]().build(
|
||||||
"balanced",
|
seed_fixture_scene,
|
||||||
"",
|
"same_creator_same_room",
|
||||||
branch_options=branch_options,
|
"hybrid",
|
||||||
)[0]
|
branch_options=branch_options,
|
||||||
seeded_pair = json.loads(nodes["SxCPScenePairOutput"]().build(seeded_soft_scene, seeded_hard_scene)[7])
|
)
|
||||||
|
seeded_soft_scene = nodes["SxCPSoftcoreBranchOptions"]().build(
|
||||||
|
seeded_soft_scene,
|
||||||
|
"same_as_hardcore",
|
||||||
|
"lingerie_tease",
|
||||||
|
True,
|
||||||
|
0.45,
|
||||||
|
"from_camera_config",
|
||||||
|
"compact",
|
||||||
|
"",
|
||||||
|
branch_options=branch_options,
|
||||||
|
seed_options=soft_seed_options,
|
||||||
|
)[0]
|
||||||
|
seeded_hard_scene = nodes["SxCPHardcoreBranchOptions"]().build(
|
||||||
|
seeded_hard_scene,
|
||||||
|
"couple",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"hardcore",
|
||||||
|
True,
|
||||||
|
0.85,
|
||||||
|
default_hardcore_continuity,
|
||||||
|
"from_camera_config",
|
||||||
|
"compact",
|
||||||
|
"balanced",
|
||||||
|
"",
|
||||||
|
branch_options=branch_options,
|
||||||
|
)[0]
|
||||||
|
return json.loads(nodes["SxCPScenePairOutput"]().build(seeded_soft_scene, seeded_hard_scene)[7])
|
||||||
|
|
||||||
|
seeded_pair = _soft_clothing_pair(6679, soft_clothing_seed_options)
|
||||||
seeded_soft_item = str(seeded_pair.get("softcore_row", {}).get("item") or "").lower()
|
seeded_soft_item = str(seeded_pair.get("softcore_row", {}).get("item") or "").lower()
|
||||||
seeded_hard_clothing = str(seeded_pair.get("hardcore_clothing_state") or "").lower()
|
seeded_hard_clothing = str(seeded_pair.get("hardcore_clothing_state") or "").lower()
|
||||||
seeded_hard_prompt = str(seeded_pair.get("hardcore_prompt") or "").lower()
|
seeded_hard_prompt = str(seeded_pair.get("hardcore_prompt") or "").lower()
|
||||||
|
seeded_soft_seed_config = (
|
||||||
|
seeded_pair.get("softcore_row", {}).get("seed_config")
|
||||||
|
if isinstance(seeded_pair.get("softcore_row"), dict)
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
seeded_trace = seeded_pair.get("scene_chain", {}).get("softcore", {}).get("seed_trace", {}).get("softcore.softcore_branch", {})
|
||||||
|
_expect(
|
||||||
|
seeded_soft_seed_config.get("clothing_seed") == 6679,
|
||||||
|
"Scene softcore branch clothing seed did not reach softcore row clothing_seed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
seeded_soft_seed_config.get("content_seed") != 6679,
|
||||||
|
"Scene softcore branch clothing seed should not write softcore row content_seed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
seeded_trace.get("axes") == ["clothing"],
|
||||||
|
"Scene softcore branch clothing seed should write clothing trace axes",
|
||||||
|
)
|
||||||
_expect(
|
_expect(
|
||||||
"black lace lingerie" in seeded_soft_item,
|
"black lace lingerie" in seeded_soft_item,
|
||||||
"Scene softcore branch content seed fixture no longer selects the expected outfit",
|
"Scene softcore branch clothing seed fixture no longer selects the expected outfit",
|
||||||
)
|
)
|
||||||
_expect(
|
_expect(
|
||||||
"black lace lingerie" in seeded_hard_clothing,
|
"black lace lingerie" in seeded_hard_clothing,
|
||||||
@@ -16292,6 +16748,46 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
"black lace lingerie" in seeded_hard_prompt,
|
"black lace lingerie" in seeded_hard_prompt,
|
||||||
"Hardcore prompt text did not include the softcore-branch seeded woman outfit",
|
"Hardcore prompt text did not include the softcore-branch seeded woman outfit",
|
||||||
)
|
)
|
||||||
|
soft_clothing_pairs = [_soft_clothing_pair(seed) for seed in (6677, 6678, 6679, 6680)]
|
||||||
|
soft_clothing_items = {pair.get("softcore_row", {}).get("item") for pair in soft_clothing_pairs}
|
||||||
|
soft_clothing_poses = {pair.get("softcore_row", {}).get("pose") for pair in soft_clothing_pairs}
|
||||||
|
inherited_hard_clothing = {pair.get("hardcore_clothing_state") for pair in soft_clothing_pairs}
|
||||||
|
_expect(len(soft_clothing_items) > 1, "Softcore branch clothing reroll did not change softcore outfit")
|
||||||
|
_expect(len(inherited_hard_clothing) > 1, "Softcore branch clothing reroll did not change inherited hard clothing state")
|
||||||
|
_expect(len(soft_clothing_poses) == 1, "Softcore branch clothing reroll should keep softcore pose stable")
|
||||||
|
soft_default_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
||||||
|
"softcore_branch",
|
||||||
|
"fixed",
|
||||||
|
6681,
|
||||||
|
"none",
|
||||||
|
"same_for_all_rows",
|
||||||
|
"replace_layer",
|
||||||
|
)[0]
|
||||||
|
soft_default_pair = _soft_clothing_pair(6681, soft_default_seed_options)
|
||||||
|
soft_default_trace = (
|
||||||
|
soft_default_pair.get("scene_chain", {}).get("softcore", {}).get("seed_trace", {}).get("softcore.softcore_branch", {})
|
||||||
|
)
|
||||||
|
soft_default_seed_config = (
|
||||||
|
soft_default_pair.get("softcore_row", {}).get("seed_config")
|
||||||
|
if isinstance(soft_default_pair.get("softcore_row"), dict)
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
soft_default_trace.get("axes") == ["clothing", "pose", "role"],
|
||||||
|
"Scene softcore branch default seed axes should resolve to clothing, pose, and role",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
soft_default_seed_config.get("clothing_seed") == 6681,
|
||||||
|
"Scene softcore branch default seed axis did not write clothing_seed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
soft_default_seed_config.get("pose_seed") == 6681 and soft_default_seed_config.get("role_seed") == 6681,
|
||||||
|
"Scene softcore branch default seed axis did not write pose and role seeds",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
soft_default_seed_config.get("content_seed") != 6681,
|
||||||
|
"Scene softcore branch default seed axis should not write content_seed",
|
||||||
|
)
|
||||||
content_pose_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
content_pose_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
|
||||||
"hardcore_branch",
|
"hardcore_branch",
|
||||||
"fixed",
|
"fixed",
|
||||||
@@ -16353,6 +16849,10 @@ def smoke_node_scene_chain_registration() -> None:
|
|||||||
content_hard_seed_config.get("content_seed") == 8899,
|
content_hard_seed_config.get("content_seed") == 8899,
|
||||||
"Hardcore branch content_pose reroll did not reach hardcore content seed",
|
"Hardcore branch content_pose reroll did not reach hardcore content seed",
|
||||||
)
|
)
|
||||||
|
_expect(
|
||||||
|
content_hard_seed_config.get("clothing_seed") != 8899,
|
||||||
|
"Hardcore branch content_pose reroll should not write clothing_seed",
|
||||||
|
)
|
||||||
_expect(
|
_expect(
|
||||||
content_hard_seed_config.get("pose_seed") == 8899 and content_hard_seed_config.get("role_seed") == 8899,
|
content_hard_seed_config.get("pose_seed") == 8899 and content_hard_seed_config.get("role_seed") == 8899,
|
||||||
"Hardcore branch content_pose reroll did not reach hardcore pose and role seeds",
|
"Hardcore branch content_pose reroll did not reach hardcore pose and role seeds",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const SEED_AXES = [
|
|||||||
"role",
|
"role",
|
||||||
"expression",
|
"expression",
|
||||||
"composition",
|
"composition",
|
||||||
|
"clothing",
|
||||||
];
|
];
|
||||||
|
|
||||||
function widget(node, name) {
|
function widget(node, name) {
|
||||||
|
|||||||
Reference in New Issue
Block a user