From 95dc8939b665601e549c7376119a261ba0f24f49 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 27 Jun 2026 14:31:21 +0200 Subject: [PATCH] Normalize external pair metadata shape --- row_normalization.py | 34 ++++++++++++++----------------- tools/prompt_smoke.py | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/row_normalization.py b/row_normalization.py index 6ecbf5e..f8b618b 100644 --- a/row_normalization.py +++ b/row_normalization.py @@ -100,21 +100,25 @@ def sanitize_metadata_row_text(row: dict[str, Any], *, active_trigger: str = "") return row +def _sync_pair_root_row_field(pair: dict[str, Any], row_key: str, root_key: str, row_field: str) -> None: + row = pair.get(row_key) + if not isinstance(row, dict): + return + if root_key in pair: + row[row_field] = pair.get(root_key) + elif row_field in row: + pair[root_key] = row.get(row_field) + + def synchronize_pair_row_outputs(pair: dict[str, Any]) -> dict[str, Any]: mapping = ( ("softcore_row", "softcore_prompt", "softcore_caption", "softcore_negative_prompt"), ("hardcore_row", "hardcore_prompt", "hardcore_caption", "hardcore_negative_prompt"), ) for row_key, prompt_key, caption_key, negative_key in mapping: - row = pair.get(row_key) - if not isinstance(row, dict): - continue - if prompt_key in pair: - row["prompt"] = pair.get(prompt_key, "") - if caption_key in pair: - row["caption"] = pair.get(caption_key, "") - if negative_key in pair: - row["negative_prompt"] = pair.get(negative_key, "") + _sync_pair_root_row_field(pair, row_key, prompt_key, "prompt") + _sync_pair_root_row_field(pair, row_key, caption_key, "caption") + _sync_pair_root_row_field(pair, row_key, negative_key, "negative_prompt") return pair @@ -132,12 +136,8 @@ def synchronize_pair_side_metadata(pair: dict[str, Any]) -> dict[str, Any]: ), } for row_key, keys in side_keys.items(): - row = pair.get(row_key) - if not isinstance(row, dict): - continue for key in keys: - if key in pair: - row[key] = pair.get(key) + _sync_pair_root_row_field(pair, row_key, key, key) return pair @@ -155,12 +155,8 @@ def synchronize_pair_camera_metadata(pair: dict[str, Any]) -> dict[str, Any]: ), } for row_key, keys in mapping.items(): - row = pair.get(row_key) - if not isinstance(row, dict): - continue for source_key, target_key in keys: - if source_key in pair: - row[target_key] = pair.get(source_key) + _sync_pair_root_row_field(pair, row_key, source_key, target_key) return pair diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index ba3d628..5b541b4 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -2436,6 +2436,53 @@ def smoke_row_normalization_policy() -> None: _expect_no_duplicate_comma_items("row_normalization.pair.soft_negative", pair.get("softcore_negative_prompt")) _expect_no_duplicate_comma_items("row_normalization.pair.hard_row_negative", pair["hardcore_row"].get("negative_prompt")) + reverse_pair = row_normalization.normalize_pair_metadata( + { + "softcore_row": { + "prompt": f"{Trigger}, {Trigger}, embedded-only soft.", + "caption": f"{Trigger}, {Trigger}, embedded-only soft caption.", + "negative_prompt": "bad anatomy, bad anatomy", + "softcore_partner_styling": {"outfits": ["row partner outfit"], "pose": "row partner pose"}, + "camera_config": {"camera_mode": "standard"}, + "camera_directive": "Camera: row soft front view.", + "camera_scene_directive": "Row soft scene camera layout.", + }, + "hardcore_row": { + "prompt": f"{Trigger}, {Trigger}, embedded-only hard.", + "caption": f"{Trigger}, {Trigger}, embedded-only hard caption.", + "negative_prompt": "low quality, low quality", + "hardcore_clothing_state": "row hard clothing state", + "character_hardcore_clothing": ["Woman A row hard clothing"], + "default_man_hardcore_clothing": ["Man A row default hard clothing"], + "hardcore_detail_density": "concise", + "hardcore_position_config": {"family": "outercourse"}, + "camera_config": {"camera_mode": "pov"}, + "camera_directive": "Camera: row hard side view.", + "camera_scene_directive": "Row hard scene camera layout.", + }, + }, + active_trigger=Trigger, + ) + _expect_trigger_once("row_normalization.reverse.soft_prompt", reverse_pair.get("softcore_prompt"), Trigger) + _expect_trigger_once("row_normalization.reverse.hard_caption", reverse_pair.get("hardcore_caption"), Trigger) + _expect( + reverse_pair.get("softcore_partner_styling") == reverse_pair["softcore_row"].get("softcore_partner_styling"), + "Pair normalization did not lift soft side metadata from embedded row", + ) + _expect( + reverse_pair.get("hardcore_clothing_state") == reverse_pair["hardcore_row"].get("hardcore_clothing_state"), + "Pair normalization did not lift hard side metadata from embedded row", + ) + _expect( + reverse_pair.get("softcore_camera_config") == reverse_pair["softcore_row"].get("camera_config"), + "Pair normalization did not lift soft camera config from embedded row", + ) + _expect( + reverse_pair.get("hardcore_camera_scene_directive") == reverse_pair["hardcore_row"].get("camera_scene_directive"), + "Pair normalization did not lift hard camera scene from embedded row", + ) + _expect_no_duplicate_comma_items("row_normalization.reverse.hard_negative", reverse_pair.get("hardcore_negative_prompt")) + def smoke_prompt_hygiene_policy() -> None: merged = prompt_hygiene.combine_negative_text(