from __future__ import annotations from typing import Any, Callable try: from . import pair_clothing from . import pair_options except ImportError: # Allows local smoke tests with top-level imports. import pair_clothing import pair_options DescriptorFromRow = Callable[[dict[str, Any]], str] CastDescriptors = Callable[..., list[str]] AxisRng = Callable[[dict[str, int], str, int, int], Any] Choose = Callable[[Any, list[str]], str] SlotSoftcoreOutfit = Callable[[dict[str, Any] | None, Any], str] def cast_summary_phrase(women_count: int, men_count: int) -> str: women_count = max(0, int(women_count)) men_count = max(0, int(men_count)) if women_count + men_count == 0: women_count = 1 person_count = women_count + men_count women_label = "woman" if women_count == 1 else "women" men_label = "man" if men_count == 1 else "men" return f"{women_count} {women_label}, {men_count} {men_label}, {person_count} total adults" def softcore_partner_styling( *, seed_config: dict[str, int], seed: int, row_number: int, women_count: int, men_count: int, pov_labels: list[str] | None, label_map: dict[str, dict[str, Any]] | None, axis_rng: AxisRng, choose: Choose, slot_softcore_outfit: SlotSoftcoreOutfit, ) -> dict[str, Any]: content_rng = axis_rng(seed_config, "content", seed, row_number + 421) pose_rng = axis_rng(seed_config, "pose", seed, row_number + 421) pov_set = set(pov_labels or []) outfits: list[str] = [] for index in range(max(0, women_count - 1)): label = chr(ord("B") + index) full_label = f"Woman {label}" outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose( content_rng, pair_options.INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS, ) sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit) if sentence: outfits.append(sentence) for index in range(max(0, men_count)): label = chr(ord("A") + index) full_label = f"Man {label}" if full_label in pov_set: continue outfit = slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or choose( content_rng, pair_options.INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS, ) sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit) if sentence: outfits.append(sentence) return { "outfits": outfits, "pose": choose(pose_rng, pair_options.SOFTCORE_CAST_POSES), } def resolve_insta_pair_cast_context( *, soft_row: dict[str, Any], options: dict[str, Any], parsed_seed_config: dict[str, int], seed: int, row_number: int, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, hard_women_count: int, hard_men_count: int, character_slots: list[dict[str, Any]], character_slot_map: dict[str, dict[str, Any]], pov_character_labels: list[str], platform_styles: dict[str, str], soft_levels: dict[str, str], hardcore_levels: dict[str, str], descriptor_from_row: DescriptorFromRow, build_cast_descriptors: CastDescriptors, prompt_cast_descriptors: Callable[[str], str], axis_rng: AxisRng, choose: Choose, slot_softcore_outfit: SlotSoftcoreOutfit, ) -> dict[str, Any]: descriptor = descriptor_from_row(soft_row) cast_descriptors = build_cast_descriptors( descriptor, parsed_seed_config, seed, row_number, ethnicity, figure, no_plus_women, no_black, hard_women_count, hard_men_count, character_slots, ) cast_descriptor_text = prompt_cast_descriptors("; ".join(cast_descriptors)) same_softcore_cast = options["softcore_cast"] == "same_as_hardcore" soft_cast_descriptor_text = cast_descriptor_text if same_softcore_cast else f"Woman A: {descriptor}" soft_partner_styling = softcore_partner_styling( seed_config=parsed_seed_config, seed=seed, row_number=row_number, women_count=hard_women_count if same_softcore_cast else 1, men_count=hard_men_count if same_softcore_cast else 0, pov_labels=pov_character_labels if same_softcore_cast else [], label_map=character_slot_map, axis_rng=axis_rng, choose=choose, slot_softcore_outfit=slot_softcore_outfit, ) if not same_softcore_cast: soft_partner_styling = {"outfits": [], "pose": ""} soft_partner_outfit_text = "; ".join(soft_partner_styling["outfits"]) soft_cast = ( "solo creator setup with Woman A alone" if options["softcore_cast"] == "solo" else f"soft creator-teaser setup with {cast_summary_phrase(hard_women_count, hard_men_count)}" ) soft_cast_presence = ( ( "Frame Woman A from the POV participant's first-person camera in a soft creator-teaser setup; " "keep the POV participant off-camera as the viewpoint and implied by camera perspective or foreground cues. " ) if same_softcore_cast and pov_character_labels else ( "Place Woman A and the listed partners together in a soft creator-teaser pose. " if same_softcore_cast else "Keep the softcore version focused on Woman A alone. " ) ) soft_cast_styling_sentence = ( f"Partner softcore styling: {soft_partner_outfit_text}. Cast pose: {soft_partner_styling['pose']}. " if same_softcore_cast and soft_partner_outfit_text else "" ) hard_cast = cast_summary_phrase(hard_women_count, hard_men_count) soft_descriptor_sentence = ( f"Cast descriptors: {soft_cast_descriptor_text}. " if same_softcore_cast else f"Woman A: {descriptor}. " ) return { "descriptor": descriptor, "cast_descriptors": cast_descriptors, "cast_descriptor_text": cast_descriptor_text, "soft_cast_descriptor_text": soft_cast_descriptor_text, "soft_partner_styling": soft_partner_styling, "soft_partner_outfit_text": soft_partner_outfit_text, "platform_style": platform_styles[options["platform_style"]], "soft_level": soft_levels[options["softcore_level"]], "hard_level": hardcore_levels[options["hardcore_level"]], "soft_cast": soft_cast, "soft_cast_presence": soft_cast_presence, "soft_cast_styling_sentence": soft_cast_styling_sentence, "hard_cast": hard_cast, "soft_descriptor_sentence": soft_descriptor_sentence, }