Add formatter metadata smoke fixtures

This commit is contained in:
2026-06-26 16:50:26 +02:00
parent 2f7c359fab
commit 3cbded3f45
3 changed files with 159 additions and 4 deletions
+4 -4
View File
@@ -257,8 +257,8 @@ Improve later:
Near-term: Near-term:
- Add final row hygiene already done through `prompt_hygiene.py`. - Add final row hygiene already done through `prompt_hygiene.py`.
- Add a metadata smoke checker for representative rows through - Add a metadata smoke checker for representative generated rows and static
`tools/prompt_smoke.py`. formatter fixtures through `tools/prompt_smoke.py`.
- Normalize every row with one function before JSON serialization. - Normalize every row with one function before JSON serialization.
Medium-term: Medium-term:
@@ -359,5 +359,5 @@ Medium-term:
checks. checks.
2. Extract hardcore role graph generation from `prompt_builder.py` into a 2. Extract hardcore role graph generation from `prompt_builder.py` into a
dedicated `hardcore_role_graphs.py` module. dedicated `hardcore_role_graphs.py` module.
3. Add route-level smoke fixtures for more representative Krea/SDXL/caption metadata 3. Add more route-level smoke fixtures for generated edge cases that are not
rows. covered by the current static Krea/SDXL/caption metadata fixtures.
+3
View File
@@ -722,6 +722,9 @@ pair metadata through the core Python APIs, then verifies:
normal camera text, and preserve composition punctuation before the style normal camera text, and preserve composition punctuation before the style
suffix; suffix;
- expression-disabled rows do not fall back to generated expression text. - expression-disabled rows do not fall back to generated expression text.
- static formatter metadata fixtures keep source-provided action families
stable across Krea2 prose, SDXL tags, and natural captions even when raw item
text contains distracting wording.
## Editing Cheatsheet ## Editing Cheatsheet
+152
View File
@@ -273,6 +273,64 @@ def _prompt_row(
return row return row
def _fixture_hardcore_row(**overrides: Any) -> dict[str, Any]:
row: dict[str, Any] = {
"source": "json_category",
"prompt": "Fixture explicit adult prompt for metadata route.",
"caption": "fixture caption",
"negative_prompt": "low quality, bad anatomy",
"main_category": "Hardcore sexual poses",
"subcategory": "Penetrative sex",
"category_slug": "hardcore_sexual_poses",
"subcategory_slug": "penetrative_sex",
"subject_type": "configured_cast",
"subject_phrase": "1 adult woman and 1 adult man",
"cast_summary": "1 woman, 1 man",
"cast_descriptor_text": (
"Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes; "
"Man A: 40-year-old adult man, average figure, tan skin, dark hair"
),
"cast_descriptors": [
"Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
"Man A: 40-year-old adult man, average figure, tan skin, dark hair",
],
"women_count": 1,
"men_count": 1,
"person_count": 2,
"item": (
"missionary position while full-body penetrative sex, hands gripping the ass, "
"mouth close to the ear, and explicit genital contact visible"
),
"custom_item": "Penetrative sex",
"item_label": "Sexual pose",
"item_axis_values": {
"position": "missionary position",
"penetration_act": "full-body penetrative sex",
"mouth_detail": "mouth close to the ear",
},
"scene_text": "private studio room with warm light",
"scene_kind": "explicit adult sex scene",
"pose": "configured explicit pose",
"composition": "front-facing full-body frame",
"source_composition": "front-facing full-body frame",
"role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
"Man A's hips press close and Man A's penis thrusts into her pussy."
),
"source_role_graph": (
"Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
"Man A's hips press close and Man A's penis thrusts into her pussy."
),
"expression": "focused adult expression",
"action_family": "penetration",
"position_family": "penetrative",
"position_key": "missionary",
"position_keys": ["missionary"],
}
row.update(overrides)
return row
def smoke_builtin_single() -> None: def smoke_builtin_single() -> None:
row = _prompt_row(name="builtin_single_woman", category="woman", subcategory="random", seed=1001, men_count=0) row = _prompt_row(name="builtin_single_woman", category="woman", subcategory="random", seed=1001, men_count=0)
_expect(row.get("source") == "built_in_generator", "builtin row should come from built-in generator") _expect(row.get("source") == "built_in_generator", "builtin row should come from built-in generator")
@@ -681,6 +739,99 @@ def smoke_no_expression_fallback() -> None:
_expect_formatter_outputs(row, "hardcore_expression_disabled", target="single") _expect_formatter_outputs(row, "hardcore_expression_disabled", target="single")
def smoke_formatter_metadata_fixtures() -> None:
cases = [
{
"name": "fixture_penetration_text_noise",
"row": _fixture_hardcore_row(),
"krea_terms": ("penis thrusts",),
"sdxl_terms": ("penetrative sex", "missionary"),
"caption_terms": ("penetrative action",),
},
{
"name": "fixture_manual_source_family",
"row": _fixture_hardcore_row(
subcategory="Manual stimulation",
subcategory_slug="manual_stimulation",
item="wet fingers moving between the thighs, one hand braced on the hip, wet shine on fingers and inner thighs",
custom_item="Manual stimulation",
item_axis_values={
"position": "kneeling hand-between-thighs position",
"manual_act": "wet fingers moving between the thighs",
},
composition="close crop on hands and face",
source_composition="close crop on hands and face",
role_graph=(
"Woman A reclines with thighs open while Man A's hand is between her legs, "
"fingers visibly stimulating her pussy."
),
source_role_graph=(
"Woman A reclines with thighs open while Man A's hand is between her legs, "
"fingers visibly stimulating her pussy."
),
action_family="foreplay",
position_family="manual",
position_key="fingering",
position_keys=["fingering", "open_thighs"],
),
"krea_terms": ("fingers visibly stimulating",),
"sdxl_terms": ("manual stimulation", "fingering"),
"caption_terms": ("manual action",),
},
{
"name": "fixture_climax_family",
"row": _fixture_hardcore_row(
subcategory="Cumshot and climax",
subcategory_slug="cumshot_climax",
item="external cumshot with cum on lower back and ass, explicit semen aftermath visible",
custom_item="Cumshot and climax",
item_axis_values={"position": "on all fours with hips raised"},
composition="low-angle post-orgasm frame",
source_composition="low-angle post-orgasm frame",
role_graph=(
"Woman A is on all fours with hips raised while Man A is positioned behind her "
"and ejaculates semen across her ass, thighs, and lower back."
),
source_role_graph=(
"Woman A is on all fours with hips raised while Man A is positioned behind her "
"and ejaculates semen across her ass, thighs, and lower back."
),
action_family="climax",
position_family="climax",
position_key="doggy",
position_keys=["doggy"],
),
"krea_terms": ("ejaculates semen",),
"sdxl_terms": ("climax", "semen"),
"caption_terms": ("climax action",),
},
]
for case in cases:
name = case["name"]
row = case["row"]
_expect_custom_row(row, name)
metadata = _json(row)
krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="single")
krea_prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 40).lower()
_expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
_expect("role graph:" not in krea_prompt and "sexual pose:" not in krea_prompt, f"{name}.krea leaked raw labels")
for term in case["krea_terms"]:
_expect(term in krea_prompt, f"{name}.krea missing {term!r}")
sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=metadata, target="single", trigger=SdxlTrigger, prepend_trigger=True)
sdxl_prompt = _expect_text(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
_expect("metadata" in sdxl.get("method", ""), f"{name}.sdxl did not use metadata")
for term in case["sdxl_terms"]:
_expect(term in sdxl_prompt, f"{name}.sdxl missing {term!r}")
caption, method = caption_naturalizer.naturalize_caption("", metadata_json=metadata, trigger=Trigger, include_trigger=True)
caption_text = _expect_text(f"{name}.caption", caption, 40).lower()
_expect("metadata" in method, f"{name}.caption did not use metadata")
for term in case["caption_terms"]:
_expect(term in caption_text, f"{name}.caption missing {term!r}")
SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("builtin_single_woman", smoke_builtin_single), ("builtin_single_woman", smoke_builtin_single),
("camera_scene_single", smoke_camera_scene_single), ("camera_scene_single", smoke_camera_scene_single),
@@ -694,6 +845,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("pov_camera_scene", smoke_pov_camera_scene), ("pov_camera_scene", smoke_pov_camera_scene),
("krea_pov_penetration_route", smoke_krea_pov_penetration_route), ("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
("expression_disabled", smoke_no_expression_fallback), ("expression_disabled", smoke_no_expression_fallback),
("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
] ]