Files
ComfyUI-Ethanfel-Prompt-Bui…/pair_builder.py
T

309 lines
14 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Callable
try:
from . import pair_camera
from . import pair_cast
from . import pair_clothing
from . import pair_output
from . import pair_rows
except ImportError: # Allows local smoke tests with top-level imports.
import pair_camera
import pair_cast
import pair_clothing
import pair_output
import pair_rows
BuildPrompt = Callable[..., dict[str, Any]]
AxisRng = Callable[[dict[str, int], str, int, int], Any]
Choose = Callable[[Any, list[str]], str]
@dataclass(frozen=True)
class InstaPairBuildRequest:
row_number: int
start_index: int
seed: int
ethnicity: str
figure: str
no_plus_women: bool
no_black: bool
trigger: str
prepend_trigger_to_prompt: bool
seed_config: str | dict[str, Any] | None = None
softcore_seed_config: str | dict[str, Any] | None = None
hardcore_seed_config: str | dict[str, Any] | None = None
options_json: str | dict[str, Any] | None = None
filter_config: str | dict[str, Any] | None = None
camera_config: str | dict[str, Any] | None = None
softcore_camera_config: str | dict[str, Any] | None = None
hardcore_camera_config: str | dict[str, Any] | None = None
character_profile: str | dict[str, Any] | None = ""
character_cast: str | dict[str, Any] | list[Any] | None = ""
hardcore_position_config: str | dict[str, Any] | None = ""
location_config: str | dict[str, Any] | None = ""
composition_config: str | dict[str, Any] | None = ""
style_config: str | dict[str, Any] | None = ""
extra_positive: str = ""
extra_negative: str = ""
@dataclass(frozen=True)
class InstaPairBuildDependencies:
default_trigger: str
random_subcategory: str
soft_negative_base: str
hard_negative_base: str
camera_detail_choices: list[str] | tuple[str, ...]
hardcore_clothing_continuity: dict[str, str]
platform_styles: dict[str, str]
soft_levels: dict[str, str]
hardcore_levels: dict[str, str]
parse_options: Callable[[str | dict[str, Any] | None], dict[str, Any]]
parse_filter_config: Callable[[str | dict[str, Any] | None], dict[str, Any]]
parse_seed_config: Callable[[str | dict[str, Any] | None], dict[str, int]]
parse_character_cast: Callable[[str | dict[str, Any] | list[Any] | None], list[dict[str, Any]]]
character_slot_label_map: Callable[[list[dict[str, Any]]], dict[str, dict[str, Any]]]
pov_character_labels: Callable[[dict[str, dict[str, Any]], int], list[str]]
softcore_category: Callable[[str], tuple[str, str]]
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]
hardcore_counts: Callable[[dict[str, Any]], tuple[int, int]]
character_context_for_label: Callable[
[str, dict[str, dict[str, Any]], Any, str, str, bool, bool],
tuple[dict[str, Any], dict[str, Any] | None],
]
slot_is_pov: Callable[[dict[str, Any] | None], bool]
choose: Choose
camera_config_with_mode: Callable[[str | dict[str, Any] | None, str], dict[str, Any]]
camera_directive: Callable[[str | dict[str, Any] | None], tuple[str, dict[str, Any]]]
apply_contextual_composition: Callable[[dict[str, Any], str], dict[str, Any]]
contextual_composition_prompt: Callable[[Any, Any, str], str]
composition_prompt: Callable[[Any], str]
camera_scene_directive_for_context: Callable[
[Any, Any, str | dict[str, Any] | None, list[str] | None, str],
tuple[str, dict[str, Any]],
]
slot_hardcore_clothing: Callable[[dict[str, Any] | None, Any], str]
hardcore_detail_directive: Callable[[Any], str]
camera_caption_text: Callable[[dict[str, Any]], str]
def build_insta_of_pair(request: InstaPairBuildRequest, deps: InstaPairBuildDependencies) -> dict[str, Any]:
options = deps.parse_options(request.options_json)
ethnicity = request.ethnicity
figure = request.figure
no_plus_women = request.no_plus_women
no_black = request.no_black
if request.filter_config:
filters = deps.parse_filter_config(request.filter_config)
ethnicity = filters["ethnicity"]
figure = filters["figure"]
no_plus_women = filters["no_plus_women"]
no_black = filters["no_black"]
hard_women_count, hard_men_count = deps.hardcore_counts(options)
active_trigger = request.trigger.strip() or deps.default_trigger
parsed_seed_config = deps.parse_seed_config(request.seed_config)
parsed_softcore_seed_config = (
deps.parse_seed_config(request.softcore_seed_config)
if request.softcore_seed_config
else parsed_seed_config
)
parsed_hardcore_seed_config = (
deps.parse_seed_config(request.hardcore_seed_config)
if request.hardcore_seed_config
else parsed_seed_config
)
character_slots = deps.parse_character_cast(request.character_cast)
character_slot_map = deps.character_slot_label_map(character_slots)
pov_character_labels = deps.pov_character_labels(character_slot_map, hard_men_count)
softcore_level_key = str(options["softcore_level"])
soft_category, soft_subcategory = deps.softcore_category(softcore_level_key)
row_route = pair_rows.build_insta_pair_rows_result(
row_number=request.row_number,
start_index=request.start_index,
seed=request.seed,
active_trigger=active_trigger,
parsed_seed_config=parsed_seed_config,
parsed_softcore_seed_config=parsed_softcore_seed_config,
parsed_hardcore_seed_config=parsed_hardcore_seed_config,
options=options,
ethnicity=ethnicity,
figure=figure,
no_plus_women=no_plus_women,
no_black=no_black,
character_profile=request.character_profile,
character_cast=request.character_cast or "",
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=deps.random_subcategory,
hardcore_position_config=request.hardcore_position_config,
location_config=request.location_config or "",
composition_config=request.composition_config or "",
style_config=request.style_config or "",
build_prompt=deps.build_prompt,
axis_rng=deps.axis_rng,
cast_expression_intensity_override=deps.cast_expression_intensity_override,
context_from_character_slot=deps.context_from_character_slot,
apply_character_context_to_row=deps.apply_character_context_to_row,
disable_row_expression=deps.disable_row_expression,
slot_softcore_outfit=deps.slot_softcore_outfit,
softcore_outfit=deps.softcore_outfit,
softcore_pose=deps.softcore_pose,
softcore_item_prompt_label=deps.softcore_item_prompt_label,
pov_prompt_directive=deps.pov_prompt_directive,
pov_composition_prompt=deps.pov_composition_prompt,
)
soft_row = row_route.soft_row
hard_row = row_route.hard_row
hard_content_rng = row_route.hard_content_rng
cast_context = pair_cast.resolve_insta_pair_cast_context(
soft_row=soft_row,
options=options,
parsed_seed_config=parsed_seed_config,
parsed_softcore_seed_config=parsed_softcore_seed_config,
seed=request.seed,
row_number=request.row_number,
ethnicity=ethnicity,
figure=figure,
no_plus_women=no_plus_women,
no_black=no_black,
hard_women_count=hard_women_count,
hard_men_count=hard_men_count,
character_slots=character_slots,
character_slot_map=character_slot_map,
pov_character_labels=pov_character_labels,
platform_styles=deps.platform_styles,
soft_levels=deps.soft_levels,
hardcore_levels=deps.hardcore_levels,
axis_rng=deps.axis_rng,
character_context_for_label=deps.character_context_for_label,
slot_is_pov=deps.slot_is_pov,
choose=deps.choose,
slot_softcore_outfit=deps.slot_softcore_outfit,
)
camera_route = pair_camera.resolve_insta_pair_camera_result(
soft_row=soft_row,
hard_row=hard_row,
options=options,
camera_config=request.camera_config,
softcore_camera_config=request.softcore_camera_config,
hardcore_camera_config=request.hardcore_camera_config,
hard_women_count=hard_women_count,
hard_men_count=hard_men_count,
pov_character_labels=pov_character_labels,
camera_detail_choices=deps.camera_detail_choices,
camera_config_with_mode=deps.camera_config_with_mode,
camera_directive=deps.camera_directive,
apply_contextual_composition=deps.apply_contextual_composition,
contextual_composition_prompt=deps.contextual_composition_prompt,
composition_prompt=deps.composition_prompt,
camera_scene_directive_for_context=deps.camera_scene_directive_for_context,
)
soft_row = camera_route.soft_row
hard_row = camera_route.hard_row
hard_scene = camera_route.hard_scene
character_hardcore_clothing_entries = pair_clothing.character_hardcore_clothing_entries(
character_slot_map,
hard_women_count,
hard_men_count,
pov_character_labels,
hard_content_rng,
deps.slot_hardcore_clothing,
)
clothing_route = pair_clothing.resolve_hardcore_pair_clothing_result(
hard_row=hard_row,
mode=options["hardcore_clothing_continuity"],
softcore_outfit=soft_row["item"],
character_hardcore_clothing_entries=character_hardcore_clothing_entries,
men_count=hard_men_count,
pov_labels=pov_character_labels,
rng=hard_content_rng,
continuity_map=deps.hardcore_clothing_continuity,
choose=deps.choose,
label_map=character_slot_map,
slot_hardcore_clothing=deps.slot_hardcore_clothing,
)
if clothing_route.requires_body_exposure_scene:
hard_scene = pair_clothing.body_exposure_scene_text(hard_scene)
hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
hard_row["scene_text"] = hard_scene
hard_detail_density = options["hardcore_detail_density"]
return pair_output.assemble_insta_pair_metadata(
active_trigger=active_trigger,
prepend_trigger_to_prompt=bool(request.prepend_trigger_to_prompt),
extra_positive=request.extra_positive,
extra_negative=request.extra_negative,
soft_negative_base=deps.soft_negative_base,
hard_negative_base=deps.hard_negative_base,
options=options,
platform_style=cast_context["platform_style"],
soft_descriptor_sentence=cast_context["soft_descriptor_sentence"],
soft_level=cast_context["soft_level"],
soft_cast=cast_context["soft_cast"],
soft_cast_presence=cast_context["soft_cast_presence"],
soft_cast_styling_sentence=cast_context["soft_cast_styling_sentence"],
soft_row=soft_row,
soft_camera_scene_sentence=camera_route.soft_camera_scene_sentence,
soft_camera_sentence=camera_route.soft_camera_sentence,
hard_level=cast_context["hard_level"],
hard_cast=cast_context["hard_cast"],
cast_descriptor_text=cast_context["cast_descriptor_text"],
pov_directive=deps.pov_prompt_directive(pov_character_labels),
pov_character_labels=pov_character_labels,
hard_clothing_sentence=clothing_route.hardcore_clothing_sentence,
hard_row=hard_row,
hard_scene=hard_scene,
hard_camera_scene_sentence=camera_route.hard_camera_scene_sentence,
hard_composition=camera_route.hard_composition,
hard_detail_directive=deps.hardcore_detail_directive(hard_detail_density),
hard_camera_sentence=camera_route.hard_camera_sentence,
descriptor=cast_context["descriptor"],
soft_partner_outfit_text=cast_context["soft_partner_outfit_text"],
soft_partner_styling=cast_context["soft_partner_styling"],
soft_camera_scene_directive=camera_route.soft_camera_scene_directive,
soft_camera_config=camera_route.soft_camera_config,
soft_camera_directive=camera_route.soft_camera_directive,
hard_camera_scene_directive=camera_route.hard_camera_scene_directive,
hard_camera_config=camera_route.hard_camera_config,
hard_camera_directive=camera_route.hard_camera_directive,
camera_caption_text=deps.camera_caption_text,
cast_descriptors=cast_context["cast_descriptors"],
character_hardcore_clothing_entries=character_hardcore_clothing_entries,
pov_hardcore_clothing_entries=clothing_route.pov_hardcore_clothing,
default_man_hardcore_clothing_entries=clothing_route.default_man_hardcore_clothing,
hard_clothing_state=clothing_route.hardcore_clothing_state,
hard_detail_density=hard_detail_density,
hard_women_count=hard_women_count,
hard_men_count=hard_men_count,
character_slots=character_slots,
character_slot_map=character_slot_map,
)