From 14f984a629b37e20cd6fde41f57846029cdb89c5 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sun, 28 Jun 2026 01:07:54 +0200 Subject: [PATCH] Use couple wording for same-cast softcore camera prompts --- pair_camera.py | 20 +++++++++++++++++-- scene_camera_adapters.py | 24 +++++++++++++++++++++-- tools/prompt_smoke.py | 42 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/pair_camera.py b/pair_camera.py index d87c061..0be84df 100644 --- a/pair_camera.py +++ b/pair_camera.py @@ -24,6 +24,22 @@ def camera_config_with_detail( return camera_config +def softcore_subject_kind(softcore_cast: Any, hard_women_count: int, hard_men_count: int) -> str: + if str(softcore_cast) == "solo": + return "woman" + total = int(hard_women_count or 0) + int(hard_men_count or 0) + if total == 2: + return "couple" + return "subjects" + + +def hardcore_subject_kind(hard_women_count: int, hard_men_count: int) -> str: + total = int(hard_women_count or 0) + int(hard_men_count or 0) + if total == 2: + return "couple" + return "subjects" + + @dataclass(frozen=True) class InstaPairCameraRoute: soft_row: dict[str, Any] @@ -101,8 +117,8 @@ def resolve_insta_pair_camera_result( soft_camera_directive, soft_camera_config_dict = camera_directive(soft_camera_config_dict) hard_camera_directive, hard_camera_config_dict = camera_directive(hard_camera_config_dict) - soft_subject_kind = "woman" if options["softcore_cast"] == "solo" else "subjects" - hard_subject_kind = "couple" if hard_women_count + hard_men_count == 2 else "subjects" + soft_subject_kind = softcore_subject_kind(options["softcore_cast"], hard_women_count, hard_men_count) + hard_subject_kind = hardcore_subject_kind(hard_women_count, hard_men_count) soft_row = apply_contextual_composition(soft_row, soft_subject_kind) hard_row = apply_contextual_composition(hard_row, hard_subject_kind) diff --git a/scene_camera_adapters.py b/scene_camera_adapters.py index 45cfb90..954f1d6 100644 --- a/scene_camera_adapters.py +++ b/scene_camera_adapters.py @@ -1054,13 +1054,32 @@ def profile_composition_text(profile: dict[str, Any], subject_kind: str) -> str: if subject_kind == "man" and composition.get("man"): return str(composition["man"]) text = str(composition.get("default") or f"{profile['place']} frame with the subjects clearly placed in the room") - if subject_kind == "couple": - text = text.replace("the subjects", "the couple") + text = composition_subject_text(text, subject_kind) if "composition" not in text.lower(): text = f"{text} composition" return text +def composition_subject_text(text: str, subject_kind: str) -> str: + replacements: dict[str, str] = {} + if subject_kind == "woman": + replacements = {"The subjects": "The woman", "the subjects": "the woman"} + elif subject_kind == "man": + replacements = {"The subjects": "The man", "the subjects": "the man"} + elif subject_kind == "couple": + replacements = { + "The subjects": "The couple", + "the subjects": "the couple", + "The woman": "The couple", + "the woman": "the couple", + "The man": "The couple", + "the man": "the couple", + } + for old, new in replacements.items(): + text = text.replace(old, new) + return text + + def contextual_composition_prompt( scene_text: Any, composition: Any, @@ -1073,6 +1092,7 @@ def contextual_composition_prompt( text = str(composition or "").strip() if not text: return text + text = composition_subject_text(text, subject_kind) profile = scene_camera_profile(scene_text, scene_entry=scene_entry, theme=theme, profile_key=profile_key) if not profile: return text diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index bec5119..7a9d097 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -881,6 +881,46 @@ def smoke_row_camera_policy() -> None: "coworking lounge frame with the couple near a desk edge" in updated.get("composition", ""), "row camera policy did not adapt coworking composition for couple rows", ) + already_matching_row = dict(row) + already_matching_row["pov_character_labels"] = [] + already_matching_row["composition"] = "coworking lounge frame with the subjects near a desk edge and tall-window depth behind them" + already_matching_row["prompt"] = ( + "A generated adult prompt. Framed as coworking lounge frame with the subjects near a desk edge and tall-window depth behind them. " + "Avoid: low quality." + ) + updated_matching = row_camera.apply_camera_config( + already_matching_row, + _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5), + compact_labels=pb.CAMERA_COMPACT_LABELS, + ) + _expect( + "the couple near a desk edge" in str(updated_matching.get("composition", "")), + "row camera policy did not adapt generic matching composition subject wording", + ) + _expect( + "the subjects near a desk edge" not in str(updated_matching.get("prompt", "")), + "row camera policy left generic matching composition subject wording in prompt", + ) + pre_normalized_row = dict(row) + pre_normalized_row["pov_character_labels"] = [] + pre_normalized_row["composition"] = "coworking lounge frame with the woman near a desk edge and tall-window depth behind them" + pre_normalized_row["prompt"] = ( + "A generated adult prompt. Framed as coworking lounge frame with the woman near a desk edge and tall-window depth behind them. " + "Avoid: low quality." + ) + updated_pre_normalized = row_camera.apply_camera_config( + pre_normalized_row, + _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5), + compact_labels=pb.CAMERA_COMPACT_LABELS, + ) + _expect( + "the couple near a desk edge" in str(updated_pre_normalized.get("composition", "")), + "row camera policy did not adapt pre-normalized woman composition for couple rows", + ) + _expect( + "the woman near a desk edge" not in str(updated_pre_normalized.get("prompt", "")), + "row camera policy left pre-normalized woman composition wording in prompt", + ) stale_internal_row = dict(row) stale_internal_row["pov_character_labels"] = [] stale_internal_row["composition"] = "camera-aware coworking lounge frame with subjects near a desk edge" @@ -6203,6 +6243,8 @@ def smoke_pair_route_policy() -> None: _expect(camera_route.as_dict() == camera_legacy, "Typed pair camera route should match legacy dict route") _expect(camera_route.hard_scene == "soft room", "Typed pair camera route lost same-room continuity") _expect(camera_route.hard_camera_sentence.startswith("Camera control:"), "Typed pair camera route lost hard camera sentence") + _expect(camera_route.soft_row.get("subject_kind") == "couple", "Same-cast softcore camera route should use couple subject kind") + _expect("couple scene directive" in camera_route.soft_camera_scene_directive, "Same-cast softcore camera directive should use couple wording") clothing_common = { "hard_row": {