Route clothing choices through clothing seed
This commit is contained in:
+261
-2
@@ -2493,7 +2493,7 @@ def smoke_row_category_route_policy() -> None:
|
||||
hardcore_position_config={},
|
||||
)
|
||||
_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")
|
||||
|
||||
exposed_route = row_category_route.select_category_item_route(
|
||||
@@ -2507,9 +2507,35 @@ def smoke_row_category_route_policy() -> None:
|
||||
hardcore_position_config={},
|
||||
)
|
||||
_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")
|
||||
|
||||
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:
|
||||
_expect(pb._ratio_or_none(-1) is None, "Prompt builder ratio helper should treat negative as unset")
|
||||
@@ -2557,6 +2583,36 @@ def smoke_row_generation_policy() -> None:
|
||||
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",
|
||||
)
|
||||
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(
|
||||
row_number=2,
|
||||
start_index=41,
|
||||
@@ -14336,6 +14392,209 @@ def smoke_seed_config_policy() -> None:
|
||||
break
|
||||
_expect(pose_changed, "pose reroll should change pose/action metadata while cast and scene stay locked")
|
||||
|
||||
clothing_axis_seed = 42001
|
||||
|
||||
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:
|
||||
report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
|
||||
|
||||
Reference in New Issue
Block a user