from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable try: from . import pair_clothing except ImportError: # Allows local smoke tests with top-level imports. import pair_clothing BuildPrompt = Callable[..., dict[str, Any]] AxisRng = Callable[[dict[str, int], str, int, int], Any] @dataclass(frozen=True) class InstaPairRowsRoute: soft_row: dict[str, Any] hard_row: dict[str, Any] hard_content_rng: Any def as_dict(self) -> dict[str, Any]: return { "soft_row": self.soft_row, "hard_row": self.hard_row, "hard_content_rng": self.hard_content_rng, } def build_insta_pair_rows_result( *, row_number: int, start_index: int, seed: int, active_trigger: str, parsed_seed_config: dict[str, int], options: dict[str, Any], ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, character_profile: str | dict[str, Any] | None, character_cast: str | dict[str, Any] | list[Any] | None, character_slot_map: dict[str, dict[str, Any]], pov_character_labels: list[str], hard_women_count: int, hard_men_count: int, soft_category: str, soft_subcategory: str, softcore_level_key: str, hardcore_random_subcategory: str, hardcore_position_config: str | dict[str, Any] | None, location_config: str | dict[str, Any] | None, composition_config: str | dict[str, Any] | None, build_prompt: BuildPrompt, axis_rng: AxisRng, cast_expression_intensity_override: Callable[ [float, dict[str, dict[str, Any]], int, int, str], tuple[float | None, str], ], context_from_character_slot: Callable[[Any, dict[str, Any], str, str, str, bool, bool], dict[str, Any]], apply_character_context_to_row: Callable[[dict[str, Any], dict[str, Any]], dict[str, Any]], disable_row_expression: Callable[[dict[str, Any], str], dict[str, Any]], slot_softcore_outfit: Callable[[dict[str, Any] | None, Any], str], softcore_outfit: Callable[[Any, str], str], softcore_pose: Callable[[Any, str], str], softcore_item_prompt_label: Callable[[str], str], pov_prompt_directive: Callable[[list[str]], str], pov_composition_prompt: Callable[[Any, list[str]], str], style_config: str | dict[str, Any] | None = "", ) -> InstaPairRowsRoute: soft_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 311) hard_content_rng = axis_rng(parsed_seed_config, "content", seed, row_number + 317) soft_person_rng = axis_rng(parsed_seed_config, "person", seed, row_number) soft_expression_women_count = hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1 soft_expression_men_count = hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0 soft_expression_enabled = bool(options["softcore_expression_enabled"]) soft_expression_intensity = options["softcore_expression_intensity"] soft_expression_intensity_source = "input" if soft_expression_enabled: soft_expression_intensity, soft_expression_intensity_source = cast_expression_intensity_override( options["softcore_expression_intensity"], character_slot_map, soft_expression_women_count, soft_expression_men_count, "softcore", ) if soft_expression_intensity is None: soft_expression_enabled = False else: soft_expression_intensity_source = "disabled" primary_slot = character_slot_map.get("Woman A") primary_slot_context = None if primary_slot: primary_slot_context = context_from_character_slot( soft_person_rng, primary_slot, "woman", ethnicity, figure, no_plus_women, no_black, ) soft_row = build_prompt( category=soft_category, subcategory=soft_subcategory, row_number=row_number, start_index=start_index, seed=seed, clothing="minimal", ethnicity=ethnicity, poses="evocative", backside_bias=0.0, figure=figure, no_plus_women=no_plus_women, no_black=no_black, minimal_clothing_ratio=-1, standard_pose_ratio=-1, trigger=active_trigger, prepend_trigger_to_prompt=False, extra_positive="", extra_negative="", seed_config=parsed_seed_config, women_count=1, men_count=0, expression_enabled=soft_expression_enabled, expression_intensity=soft_expression_intensity, character_profile="" if primary_slot else character_profile or "", character_cast="", location_config=location_config or "", composition_config=composition_config or "", style_config=style_config or "", ) soft_row["expression_intensity_source"] = soft_expression_intensity_source if primary_slot_context: soft_row = apply_character_context_to_row(soft_row, primary_slot_context) soft_row["character_slot"] = primary_slot soft_row["character_slot_status"] = "applied:Woman A" if not soft_expression_enabled: soft_row = disable_row_expression(soft_row, soft_expression_intensity_source) primary_softcore_outfit = slot_softcore_outfit(primary_slot, soft_content_rng) soft_row["item"] = primary_softcore_outfit or softcore_outfit(soft_content_rng, softcore_level_key) soft_row["pose"] = softcore_pose(soft_content_rng, softcore_level_key) soft_row["item_label"] = ( "Insta/OF softcore body exposure" if softcore_level_key == "explicit_nude" else "Insta/OF softcore outfit" ) soft_row["softcore_item_prompt_label"] = softcore_item_prompt_label(softcore_level_key) soft_row["custom_item"] = "insta_of_softcore_outfit" soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore" if softcore_level_key == "explicit_nude": soft_row["source_scene_text"] = soft_row.get("source_scene_text") or soft_row.get("scene_text", "") soft_row["scene_text"] = pair_clothing.body_exposure_scene_text(soft_row.get("scene_text", "")) soft_row["pov_character_labels"] = ( pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else [] ) soft_row["pov_prompt_directive"] = pov_prompt_directive(soft_row["pov_character_labels"]) if soft_row["pov_character_labels"]: soft_row["source_composition"] = soft_row.get("source_composition") or soft_row.get("composition", "") soft_row["composition"] = pov_composition_prompt( soft_row["source_composition"], soft_row["pov_character_labels"], ) hard_row = build_prompt( category="Hardcore sexual poses", subcategory=hardcore_random_subcategory, row_number=row_number, start_index=start_index, seed=seed, clothing="minimal", ethnicity=ethnicity, poses="evocative", backside_bias=0.0, figure=figure, no_plus_women=no_plus_women, no_black=no_black, minimal_clothing_ratio=-1, standard_pose_ratio=-1, trigger=active_trigger, prepend_trigger_to_prompt=False, extra_positive="", extra_negative="", seed_config=parsed_seed_config, women_count=hard_women_count, men_count=hard_men_count, expression_enabled=options["hardcore_expression_enabled"], expression_intensity=options["hardcore_expression_intensity"], character_cast=character_cast or "", expression_phase="hardcore", hardcore_position_config=hardcore_position_config or "", location_config=location_config or "", composition_config=composition_config or "", style_config=style_config or "", ) hard_row["hardcore_detail_density"] = options["hardcore_detail_density"] hard_row["pov_character_labels"] = pov_character_labels hard_row["pov_prompt_directive"] = pov_prompt_directive(pov_character_labels) return InstaPairRowsRoute( soft_row=soft_row, hard_row=hard_row, hard_content_rng=hard_content_rng, ) def build_insta_pair_rows( *, row_number: int, start_index: int, seed: int, active_trigger: str, parsed_seed_config: dict[str, int], options: dict[str, Any], ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, character_profile: str | dict[str, Any] | None, character_cast: str | dict[str, Any] | list[Any] | None, character_slot_map: dict[str, dict[str, Any]], pov_character_labels: list[str], hard_women_count: int, hard_men_count: int, soft_category: str, soft_subcategory: str, softcore_level_key: str, hardcore_random_subcategory: str, hardcore_position_config: str | dict[str, Any] | None, location_config: str | dict[str, Any] | None, composition_config: str | dict[str, Any] | None, build_prompt: BuildPrompt, axis_rng: AxisRng, cast_expression_intensity_override: Callable[ [float, dict[str, dict[str, Any]], int, int, str], tuple[float | None, str], ], context_from_character_slot: Callable[[Any, dict[str, Any], str, str, str, bool, bool], dict[str, Any]], apply_character_context_to_row: Callable[[dict[str, Any], dict[str, Any]], dict[str, Any]], disable_row_expression: Callable[[dict[str, Any], str], dict[str, Any]], slot_softcore_outfit: Callable[[dict[str, Any] | None, Any], str], softcore_outfit: Callable[[Any, str], str], softcore_pose: Callable[[Any, str], str], softcore_item_prompt_label: Callable[[str], str], pov_prompt_directive: Callable[[list[str]], str], pov_composition_prompt: Callable[[Any, list[str]], str], style_config: str | dict[str, Any] | None = "", ) -> dict[str, Any]: return build_insta_pair_rows_result( row_number=row_number, start_index=start_index, seed=seed, active_trigger=active_trigger, parsed_seed_config=parsed_seed_config, options=options, ethnicity=ethnicity, figure=figure, no_plus_women=no_plus_women, no_black=no_black, character_profile=character_profile, character_cast=character_cast, character_slot_map=character_slot_map, pov_character_labels=pov_character_labels, hard_women_count=hard_women_count, hard_men_count=hard_men_count, soft_category=soft_category, soft_subcategory=soft_subcategory, softcore_level_key=softcore_level_key, hardcore_random_subcategory=hardcore_random_subcategory, hardcore_position_config=hardcore_position_config, location_config=location_config, composition_config=composition_config, style_config=style_config, build_prompt=build_prompt, axis_rng=axis_rng, cast_expression_intensity_override=cast_expression_intensity_override, context_from_character_slot=context_from_character_slot, apply_character_context_to_row=apply_character_context_to_row, disable_row_expression=disable_row_expression, slot_softcore_outfit=slot_softcore_outfit, softcore_outfit=softcore_outfit, softcore_pose=softcore_pose, softcore_item_prompt_label=softcore_item_prompt_label, pov_prompt_directive=pov_prompt_directive, pov_composition_prompt=pov_composition_prompt, ).as_dict()