Broaden seed axis simulation checks

This commit is contained in:
2026-06-27 18:54:22 +02:00
parent f91953f12b
commit 7778a5f31f
2 changed files with 206 additions and 53 deletions
+184 -52
View File
@@ -93,6 +93,30 @@ def _character_cast(*, pov_man: bool = False) -> str:
)["character_cast"]
def _random_character_cast() -> str:
cast = pb.build_character_slot_json(
subject_type="woman",
label="A",
age="random",
ethnicity="random",
figure="random",
body="random",
hair_color="random",
hair_length="random",
hair_style="random",
descriptor_detail="full",
)["character_cast"]
return pb.build_character_slot_json(
subject_type="man",
label="A",
age="random",
ethnicity="random",
body="random",
descriptor_detail="compact",
character_cast=cast,
)["character_cast"]
def _coworking_location_config() -> str:
return pb.build_location_pool_json(
enabled=True,
@@ -106,6 +130,32 @@ def _coworking_location_config() -> str:
)
def _seed_probe_location_config() -> str:
return pb.build_location_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_locations=(
"seed_coworking_desk: coworking desk row with warm lamps and laptops\n"
"seed_coworking_glass: coworking glass office with plants and partition seams\n"
"seed_coworking_windows: coworking window lounge with repeated desks and city light"
),
)
def _seed_probe_composition_config() -> str:
return pb.build_composition_pool_json(
enabled=True,
combine_mode="replace",
preset="custom_only",
custom_compositions=(
"seed composition near a desk edge\n"
"seed composition through a glass partition\n"
"seed composition down repeating desk rows"
),
)
def _orbit_camera(horizontal_angle: int = 45, vertical_angle: int = 0, zoom: float = 6.0) -> str:
return pb.build_camera_orbit_config_json(
enabled=True,
@@ -602,8 +652,8 @@ def _insta_pair_case(seed: int, *, pov: bool, position: str, focus: str, family:
)
def _seed_axis_check(seed: int) -> dict[str, Any]:
base = pb.build_prompt(
def _seed_probe_row(seed: int, *, reroll_axis: str = "none", reroll_seed: int = -1) -> dict[str, Any]:
return pb.build_prompt(
category="Hardcore sexual poses",
subcategory="Penetrative sex",
row_number=1,
@@ -622,64 +672,146 @@ def _seed_axis_check(seed: int) -> dict[str, Any]:
prepend_trigger_to_prompt=True,
extra_positive="",
extra_negative="",
seed_config=pb.build_seed_lock_config_json(base_seed=seed),
seed_config=pb.build_seed_lock_config_json(base_seed=seed, reroll_axis=reroll_axis, reroll_seed=reroll_seed),
women_count=1,
men_count=1,
character_cast=_character_cast(),
character_cast=_random_character_cast(),
hardcore_position_config=_position_filter("penetration_only", "penetration", ["missionary", "doggy", "cowgirl"]),
location_config=_coworking_location_config(),
location_config=_seed_probe_location_config(),
composition_config=_seed_probe_composition_config(),
expression_intensity=0.75,
)
changed = False
mismatches: list[str] = []
for reroll_seed in range(seed + 1, seed + 10):
rerolled = pb.build_prompt(
category="Hardcore sexual poses",
subcategory="Penetrative sex",
row_number=1,
start_index=1,
seed=seed,
clothing="random",
ethnicity="any",
poses="random",
backside_bias=0.0,
figure="random",
no_plus_women=False,
no_black=False,
minimal_clothing_ratio=-1,
standard_pose_ratio=-1,
trigger=TRIGGER,
prepend_trigger_to_prompt=True,
extra_positive="",
extra_negative="",
seed_config=pb.build_seed_lock_config_json(base_seed=seed, reroll_axis="pose", reroll_seed=reroll_seed),
women_count=1,
men_count=1,
character_cast=_character_cast(),
hardcore_position_config=_position_filter("penetration_only", "penetration", ["missionary", "doggy", "cowgirl"]),
location_config=_coworking_location_config(),
)
if rerolled.get("cast_descriptor_text") != base.get("cast_descriptor_text"):
mismatches.append(f"cast changed on pose reroll {reroll_seed}")
if rerolled.get("scene_text") != base.get("scene_text"):
mismatches.append(f"scene changed on pose reroll {reroll_seed}")
if (
rerolled.get("position_key") != base.get("position_key")
or rerolled.get("source_role_graph") != base.get("source_role_graph")
or rerolled.get("item") != base.get("item")
):
changed = True
break
issues = list(mismatches)
if not changed:
issues.append("pose reroll did not change pose/action metadata within 9 attempts")
def _seed_probe_snapshot(row: dict[str, Any]) -> dict[str, Any]:
return {
"name": "seed_axis.pose_reroll",
"base": _row_summary(base),
"changed": changed,
"cast_descriptor_text": row.get("cast_descriptor_text"),
"scene": row.get("scene"),
"scene_text": row.get("scene_text"),
"position_key": row.get("position_key"),
"position_keys": row.get("position_keys") or [],
"item": row.get("item"),
"source_role_graph": row.get("source_role_graph"),
"character_expression_text": row.get("character_expression_text"),
"composition": row.get("composition"),
}
def _same_fields_issues(
name: str,
base: dict[str, Any],
rerolled: dict[str, Any],
fields: tuple[str, ...],
reroll_seed: int,
) -> list[str]:
return [
f"{name}: stable_field_changed:{field}:reroll_seed={reroll_seed}"
for field in fields
if base.get(field) != rerolled.get(field)
]
def _formatter_output_texts(row: dict[str, Any]) -> dict[str, str]:
formatted = _format_metadata(row, "single")
return {
"krea": str(formatted["krea"].get("krea_prompt") or ""),
"sdxl": str(formatted["sdxl"].get("sdxl_prompt") or ""),
"caption": str(formatted["caption"].get("natural_caption") or ""),
}
def _seed_determinism_check(seed: int) -> dict[str, Any]:
first = _seed_probe_row(seed)
second = _seed_probe_row(seed)
issues: list[str] = []
if first != second:
issues.append("locked seed config did not reproduce identical row metadata")
if _formatter_output_texts(first) != _formatter_output_texts(second):
issues.append("locked seed config did not reproduce identical formatter outputs")
return {
"name": "seed_axis.locked_determinism",
"base": _row_summary(first),
"changed": False,
"issues": issues,
}
def _seed_reroll_check(
seed: int,
*,
reroll_axis: str,
changed_fields: tuple[str, ...],
stable_fields: tuple[str, ...],
) -> dict[str, Any]:
name = f"seed_axis.{reroll_axis}_reroll"
base = _seed_probe_row(seed)
base_snapshot = _seed_probe_snapshot(base)
changed = False
changed_seed = None
changed_field_names: list[str] = []
issues: list[str] = []
for reroll_seed in range(seed + 1, seed + 16):
rerolled = _seed_probe_row(seed, reroll_axis=reroll_axis, reroll_seed=reroll_seed)
rerolled_snapshot = _seed_probe_snapshot(rerolled)
field_issues = _same_fields_issues(name, base_snapshot, rerolled_snapshot, stable_fields, reroll_seed)
if field_issues:
issues.extend(field_issues)
break
changed_field_names = [
field for field in changed_fields if base_snapshot.get(field) != rerolled_snapshot.get(field)
]
if changed_field_names:
changed = True
changed_seed = reroll_seed
break
if not changed:
issues.append(f"{reroll_axis} reroll did not change {', '.join(changed_fields)} within 15 attempts")
return {
"name": name,
"base": _row_summary(base),
"changed": changed,
"changed_seed": changed_seed,
"changed_fields": changed_field_names,
"issues": issues,
}
def _seed_axis_checks(seed: int) -> list[dict[str, Any]]:
return [
_seed_determinism_check(seed),
_seed_reroll_check(
seed,
reroll_axis="person",
changed_fields=("cast_descriptor_text",),
stable_fields=("scene_text", "position_key", "item", "source_role_graph", "character_expression_text", "composition"),
),
_seed_reroll_check(
seed,
reroll_axis="scene",
changed_fields=("scene", "scene_text"),
stable_fields=("cast_descriptor_text", "position_key", "item", "source_role_graph", "character_expression_text", "composition"),
),
_seed_reroll_check(
seed,
reroll_axis="pose",
changed_fields=("position_key", "item", "source_role_graph"),
stable_fields=("cast_descriptor_text", "scene_text", "character_expression_text", "composition"),
),
_seed_reroll_check(
seed,
reroll_axis="expression",
changed_fields=("character_expression_text",),
stable_fields=("cast_descriptor_text", "scene_text", "position_key", "item", "source_role_graph", "composition"),
),
_seed_reroll_check(
seed,
reroll_axis="composition",
changed_fields=("composition",),
stable_fields=("cast_descriptor_text", "scene_text", "position_key", "item", "source_role_graph", "character_expression_text"),
),
]
def run_simulation(seed: int = 3901, *, include_prompts: bool = False) -> dict[str, Any]:
cases: list[dict[str, Any]] = []
regular = _regular_single_case(seed)
@@ -705,7 +837,7 @@ def run_simulation(seed: int = 3901, *, include_prompts: bool = False) -> dict[s
cases.extend(_pair_reports("insta_pair.penetration", penetration_pair, include_prompts=include_prompts))
pov_pair = _insta_pair_case(seed + 2, pov=True, position="penis_licking", focus="outercourse_only", family="outercourse")
cases.extend(_pair_reports("insta_pair.pov_outercourse", pov_pair, include_prompts=include_prompts))
axis_checks = [_seed_axis_check(seed + 3)]
axis_checks = _seed_axis_checks(seed + 3)
issues = [
{"case": case["name"], "issue": issue}
for case in cases
+22 -1
View File
@@ -7834,7 +7834,7 @@ def smoke_prompt_route_simulation_policy() -> None:
report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
summary = report.get("summary") or {}
_expect(summary.get("cases") == 11, "Prompt route simulation case count changed unexpectedly")
_expect(summary.get("axis_checks") == 1, "Prompt route simulation lost axis check coverage")
_expect(summary.get("axis_checks") == 6, "Prompt route simulation lost axis check coverage")
_expect(summary.get("issues") == 0, f"Prompt route simulation reported issues: {report.get('issues')}")
cases = {case.get("name"): case for case in report.get("cases") or []}
for route_name in (
@@ -7856,6 +7856,27 @@ def smoke_prompt_route_simulation_policy() -> None:
"penis_licking" in (pov_summary.get("position_keys") or []),
"Prompt route simulation lost selected outercourse key from position_keys",
)
axis_checks = {check.get("name"): check for check in report.get("axis_checks") or []}
for check_name in (
"seed_axis.locked_determinism",
"seed_axis.person_reroll",
"seed_axis.scene_reroll",
"seed_axis.pose_reroll",
"seed_axis.expression_reroll",
"seed_axis.composition_reroll",
):
check = axis_checks.get(check_name) or {}
_expect(check, f"Prompt route simulation lost seed-axis check {check_name}")
_expect(not check.get("issues"), f"Prompt route simulation seed-axis check reported issues: {check_name}")
_expect(axis_checks["seed_axis.locked_determinism"].get("changed") is False, "Locked determinism check should not be a reroll")
for check_name in (
"seed_axis.person_reroll",
"seed_axis.scene_reroll",
"seed_axis.pose_reroll",
"seed_axis.expression_reroll",
"seed_axis.composition_reroll",
):
_expect(axis_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
def smoke_node_camera_registration() -> None: