from __future__ import annotations import json import random import re from pathlib import Path from string import Formatter from typing import Any, Callable try: from .category_library import ( category_json_files as _json_files, compatible_entries as _compatible_entries, compatible_entry as _compatible_entry, configured_pool as _configured_pool, find_subcategory as _find_subcategory, load_category_library, load_composition_pool_library, load_expression_pool_library, load_scene_pool_library, merged_axes as _merged_axes, merged_field as _merged_field, read_category_json as _read_json, template_list as _template_list, ) from . import camera_config as camera_policy from . import category_template_metadata as item_template_policy from . import character_config as character_policy from . import character_profile as character_profile_policy from . import category_cast_config as category_cast_policy from . import filter_config as filter_policy from . import generate_prompt_batches as g from . import generation_profile_config as generation_profile_policy from . import hardcore_position_config as hardcore_position_policy from . import location_config as location_policy from . import pair_clothing from . import pair_camera from . import pair_cast from . import pair_output from . import pair_rows from . import pair_options from . import pov_policy from . import row_normalization as row_policy from . import row_camera as row_camera_policy from . import row_location as row_location_policy from . import seed_config as seed_policy from .hardcore_text_cleanup import ( sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values, sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors, ) from .hardcore_action_metadata import source_hardcore_action_family from .hardcore_role_graphs import build_hardcore_role_graph except ImportError: # Allows local smoke tests with `python -c`. from category_library import ( category_json_files as _json_files, compatible_entries as _compatible_entries, compatible_entry as _compatible_entry, configured_pool as _configured_pool, find_subcategory as _find_subcategory, load_category_library, load_composition_pool_library, load_expression_pool_library, load_scene_pool_library, merged_axes as _merged_axes, merged_field as _merged_field, read_category_json as _read_json, template_list as _template_list, ) import camera_config as camera_policy import category_template_metadata as item_template_policy import character_config as character_policy import character_profile as character_profile_policy import category_cast_config as category_cast_policy import filter_config as filter_policy import generate_prompt_batches as g import generation_profile_config as generation_profile_policy import hardcore_position_config as hardcore_position_policy import location_config as location_policy import pair_clothing import pair_camera import pair_cast import pair_output import pair_rows import pair_options import pov_policy import row_normalization as row_policy import row_camera as row_camera_policy import row_location as row_location_policy import seed_config as seed_policy from hardcore_text_cleanup import ( sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values, sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors, ) from hardcore_action_metadata import source_hardcore_action_family from hardcore_role_graphs import build_hardcore_role_graph ROOT_DIR = Path(__file__).resolve().parent PROFILE_DIR = character_profile_policy.PROFILE_DIR BUILTIN_CATEGORIES = [ "auto_weighted", "auto_full", "woman", "man", "couple", "group_or_layout", "custom_random", ] RANDOM_SUBCATEGORY = "random" SEED_AXIS_SALTS = seed_policy.SEED_AXIS_SALTS SEED_AXIS_ALIASES = seed_policy.SEED_AXIS_ALIASES SEED_LOCK_AXES = seed_policy.SEED_LOCK_AXES SEED_MODE_CHOICES = seed_policy.SEED_MODE_CHOICES ETHNICITY_FILTER_CHOICES = filter_policy.ETHNICITY_FILTER_CHOICES ETHNICITY_LIST_KEYS = filter_policy.ETHNICITY_LIST_KEYS ETHNICITY_BASE_LIST_KEYS = filter_policy.ETHNICITY_BASE_LIST_KEYS EUROPEAN_REGIONAL_LIST_KEYS = filter_policy.EUROPEAN_REGIONAL_LIST_KEYS MEDITERRANEAN_REGIONAL_LIST_KEYS = filter_policy.MEDITERRANEAN_REGIONAL_LIST_KEYS CHARACTER_LABEL_CHOICES = character_policy.CHARACTER_LABEL_CHOICES CHARACTER_AGE_CHOICES = character_policy.CHARACTER_AGE_CHOICES CHARACTER_BODY_CHOICES = character_policy.CHARACTER_BODY_CHOICES CHARACTER_WOMAN_BODY_CHOICES = character_policy.CHARACTER_WOMAN_BODY_CHOICES CHARACTER_MAN_BODY_CHOICES = character_policy.CHARACTER_MAN_BODY_CHOICES CHARACTER_DESCRIPTOR_DETAIL_CHOICES = character_policy.CHARACTER_DESCRIPTOR_DETAIL_CHOICES CHARACTER_PRESENCE_CHOICES = character_policy.CHARACTER_PRESENCE_CHOICES CHARACTER_RANDOM_TOKENS = character_policy.CHARACTER_RANDOM_TOKENS CHARACTER_SLOT_SEED_MAX = character_policy.CHARACTER_SLOT_SEED_MAX CHARACTER_HAIR_COLOR_CHOICES = character_policy.CHARACTER_HAIR_COLOR_CHOICES CHARACTER_HAIR_LENGTH_CHOICES = character_policy.CHARACTER_HAIR_LENGTH_CHOICES CHARACTER_HAIR_STYLE_CHOICES = character_policy.CHARACTER_HAIR_STYLE_CHOICES CHARACTER_EYE_COLOR_CHOICES = character_policy.CHARACTER_EYE_COLOR_CHOICES CAMERA_DETAIL_CHOICES = camera_policy.CAMERA_DETAIL_CHOICES HARDCORE_DETAIL_DENSITY_CHOICES = pair_options.HARDCORE_DETAIL_DENSITY_CHOICES HARDCORE_POSITION_FAMILY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FAMILY_CHOICES HARDCORE_POSITION_FOCUS_CHOICES = hardcore_position_policy.HARDCORE_POSITION_FOCUS_CHOICES HARDCORE_POSITION_KEY_CHOICES = hardcore_position_policy.HARDCORE_POSITION_KEY_CHOICES HARDCORE_POSITION_FAMILY_SUBCATEGORIES = hardcore_position_policy.HARDCORE_POSITION_FAMILY_SUBCATEGORIES HARDCORE_SOURCE_FAMILY_BY_SUBCATEGORY = hardcore_position_policy.HARDCORE_SOURCE_FAMILY_BY_SUBCATEGORY def _hardcore_source_position_family(subcategory: dict[str, Any], config: dict[str, Any] | None = None) -> str: return hardcore_position_policy.hardcore_source_position_family(subcategory, config) def _hardcore_position_keys(*parts: Any, axis_values: dict[str, Any] | None = None) -> list[str]: return hardcore_position_policy.hardcore_position_keys(*parts, axis_values=axis_values) CAMERA_ORBIT_FRAMING_CHOICES = camera_policy.CAMERA_ORBIT_FRAMING_CHOICES CAMERA_ORBIT_FOCUS_CHOICES = camera_policy.CAMERA_ORBIT_FOCUS_CHOICES GENERIC_POSITIVE_SUFFIX = ( "Use crisp clean comic linework, detailed hatching, soft blended shading, " "pastel skin tones, muted blues and pinks, warm sensual lighting, and tactile textured paper." ) SINGLE_TEMPLATE = ( "A {subject}: {style}, {age}, {body_phrase}, {skin}, {hair}, {eyes}. " "{item_label}: {item}. Scene: {scene}. Pose: {pose}. Facial expression: {expression}. " "Composition: {composition_prompt}. {positive_suffix} Avoid: {negative_prompt}." ) COUPLE_TEMPLATE = ( "{subject_phrase}: {style}. Ages: {age}. Body types: {body}. {item_label}: {item}. " "Scene: {scene}. Pose: {pose}. Facial expressions: {expression}. " "Composition: {composition_prompt}. {positive_suffix} Avoid: {negative_prompt}." ) GROUP_TEMPLATE = ( "{subject_phrase}: {style}, ages {age}, diverse adult body types. {item_label}: {item}. " "Scene: {scene}. Facial expressions: {expression}. Composition: {composition_prompt}. " "{positive_suffix} Avoid: {negative_prompt}." ) LAYOUT_TEMPLATE = ( "{item}: {style}, adults only, clean designed composition. Scene: {scene}. " "Facial expression: {expression}. Composition: {composition}. {positive_suffix} " "Avoid: {negative_prompt}. Use no readable text unless the layout naturally needs small decorative placeholder marks." ) CAMERA_MODE_PROMPTS = camera_policy.CAMERA_MODE_PROMPTS CAMERA_COMPACT_LABELS = camera_policy.CAMERA_COMPACT_LABELS CAMERA_SHOT_PROMPTS = camera_policy.CAMERA_SHOT_PROMPTS CAMERA_ANGLE_PROMPTS = camera_policy.CAMERA_ANGLE_PROMPTS CAMERA_LENS_PROMPTS = camera_policy.CAMERA_LENS_PROMPTS CAMERA_DISTANCE_PROMPTS = camera_policy.CAMERA_DISTANCE_PROMPTS CAMERA_ORIENTATION_PROMPTS = camera_policy.CAMERA_ORIENTATION_PROMPTS CAMERA_PHONE_PROMPTS = camera_policy.CAMERA_PHONE_PROMPTS CAMERA_PRIORITY_PROMPTS = camera_policy.CAMERA_PRIORITY_PROMPTS _EXTENSIONS_APPLIED = False class SafeFormatDict(dict): def __missing__(self, key: str) -> str: return "{" + key + "}" def _slug(value: str) -> str: return g.slugify(value) or "custom" def _list_from(value: Any) -> list[Any]: if value is None: return [] if isinstance(value, list): return value return [value] def _is_false(value: Any) -> bool: if isinstance(value, bool): return value is False if isinstance(value, str): return value.strip().lower() in ("false", "0", "no", "off") return False def _unique_extend(target: list[Any], additions: list[Any]) -> None: seen = set() for item in target: try: seen.add(json.dumps(item, sort_keys=True)) except TypeError: seen.add(repr(item)) for item in additions: try: marker = json.dumps(item, sort_keys=True) except TypeError: marker = repr(item) if marker not in seen: target.append(item) seen.add(marker) def _pair_from(value: Any) -> tuple[str, str]: if isinstance(value, dict): text = str( value.get("prompt") or value.get("description") or value.get("text") or value.get("name") or "" ).strip() slug = str(value.get("slug") or _slug(str(value.get("name") or text))).strip() if not text: raise ValueError(f"Pair extension is missing prompt text: {value!r}") return slug, text if isinstance(value, (list, tuple)) and len(value) == 2: return str(value[0]), str(value[1]) text = str(value).strip() if not text: raise ValueError("Pair extension cannot be empty") return _slug(text), text def _weighted_choice(rng: random.Random, items: list[Any]) -> Any: if not items: raise ValueError("Cannot choose from an empty list") weights: list[float] = [] for item in items: weight = item.get("weight", 1.0) if isinstance(item, dict) else 1.0 try: weights.append(max(0.0, float(weight))) except (TypeError, ValueError): weights.append(1.0) total = sum(weights) if total <= 0: return items[rng.randrange(len(items))] pick = rng.random() * total running = 0.0 for item, weight in zip(items, weights): running += weight if pick <= running: return item return items[-1] def _entry_text(item: Any) -> str: if isinstance(item, dict): return str( item.get("template") or item.get("prompt") or item.get("text") or item.get("description") or item.get("name") or "" ).strip() return str(item).strip() def _item_text(item: Any) -> str: return _entry_text(item) def _item_name(item: Any) -> str: if isinstance(item, dict): return str(item.get("name") or _item_text(item)).strip() return _item_text(item) def _template_metadata(item: Any) -> dict[str, Any]: return item_template_policy.template_metadata(item) def _template_position_family(metadata: dict[str, Any]) -> str: return item_template_policy.template_position_family(metadata) def _template_position_keys(metadata: dict[str, Any]) -> list[str]: return item_template_policy.template_position_keys(metadata) def _template_action_family(metadata: dict[str, Any]) -> str: return item_template_policy.template_action_family(metadata) def _template_formatter_hints(metadata: dict[str, Any]) -> dict[str, list[str]]: return item_template_policy.formatter_hints(metadata) def _merge_position_keys(primary: list[str], fallback: list[str]) -> list[str]: return item_template_policy.merge_position_keys(primary, fallback) def _oral_acts_for_position(values: list[Any], position: str) -> list[Any]: position_text = str(position or "").lower() if not position_text: return values def act_text(value: Any) -> str: return _entry_text(value).lower() def filtered(predicate: Callable[[str], bool]) -> list[Any]: matches = [value for value in values if predicate(act_text(value))] return matches or values penis_terms = ("fellatio", "blowjob", "deepthroat", "penis sucking", "penis in mouth") cunnilingus_terms = ("cunnilingus", "pussy licking", "tongue on pussy", "oral sex with tongue and fingers", "mouth on genitals") if "sixty-nine" in position_text: return filtered(lambda text: "sixty-nine" in text) if "face-sitting" in position_text: return filtered(lambda text: "face-sitting" in text or any(term in text for term in cunnilingus_terms)) if "kneeling oral" in position_text: return filtered(lambda text: any(term in text for term in penis_terms)) if "straddled oral" in position_text or "reclining cunnilingus" in position_text: return filtered(lambda text: "sixty-nine" not in text and not any(term in text for term in penis_terms)) if "spread-leg oral" in position_text: return filtered(lambda text: "sixty-nine" not in text and "face-sitting" not in text) if any(term in position_text for term in ("standing oral", "kneeling oral", "edge-of-bed oral", "chair oral", "side-lying oral")): return filtered(lambda text: "sixty-nine" not in text and "face-sitting" not in text) return values def _oral_axis_values_for_context(values: list[Any], position: str, oral_act: str, axis_name: str) -> list[Any]: axis_name = str(axis_name or "").lower() if axis_name not in {"body_contact", "hand_detail", "mouth_detail", "saliva_detail", "climax_hint", "visibility"}: return values position_text = str(position or "").lower() act_text = str(oral_act or "").lower() woman_gives = any( term in act_text for term in ("fellatio", "blowjob", "deepthroat", "penis sucking", "penis in mouth") ) man_gives = any( term in act_text for term in ("cunnilingus", "pussy licking", "tongue on pussy") ) if not (woman_gives or man_gives): return values def value_text(value: Any) -> str: return _entry_text(value).lower() def filtered(terms: tuple[str, ...], excluded_terms: tuple[str, ...] = ()) -> list[Any]: matches = [ value for value in values if any(term in value_text(value) for term in terms) and not any(term in value_text(value) for term in excluded_terms) ] return matches or values if woman_gives: by_axis = { "body_contact": ("hips pushed", "fingers tangled", "bodies stacked", "hands on thighs"), "hand_detail": ("hips", "penis", "head", "hair"), "mouth_detail": ("lips", "mouth", "deep mouth", "saliva"), "saliva_detail": ("saliva", "wet lips", "slick wet mouth", "drool", "mouth"), "climax_hint": ("mouth", "lips", "tongue", "breasts", "belly", "sexual fluids"), "visibility": ("mouth", "penis", "oral"), } excluded = { "body_contact": ("legs held open", "spread legs", "ass lifted", "chest pressed to thighs"), "hand_detail": ("spreading thighs", "sheets", "cupping breasts", "pressing into thighs", "holding the ass"), } return filtered(by_axis.get(axis_name, ("mouth", "penis")), excluded.get(axis_name, ())) if man_gives and ("kneeling oral" in position_text or "standing oral" in position_text): by_axis = { "body_contact": ("legs held open", "one body kneeling", "chest pressed", "ass lifted", "hands on thighs"), "hand_detail": ("thigh", "hips", "head", "ass"), "mouth_detail": ("tongue", "wet lips", "deep mouth", "genitals"), "saliva_detail": ("saliva", "wet lips", "tongue", "drool"), "climax_hint": ("sexual fluids", "orgasmic tension"), "visibility": ("mouth", "pussy", "oral", "genital"), } return filtered(by_axis.get(axis_name, ("mouth", "pussy", "tongue")), ("penis", "breasts")) return values def _outercourse_acts_for_position(values: list[Any], position: str) -> list[Any]: position_text = str(position or "").lower() if not position_text: return values def act_text(value: Any) -> str: return _entry_text(value).lower() def filtered(predicate: Callable[[str], bool]) -> list[Any]: matches = [value for value in values if predicate(act_text(value))] return matches or values if any(term in position_text for term in ("boobjob", "titjob", "breast-sex", "breast sex")): return filtered(lambda text: any(term in text for term in ("boobjob", "titjob", "breast sex", "breasts"))) if any(term in position_text for term in ("testicle", "balls")): return filtered(lambda text: any(term in text for term in ("testicle", "balls"))) if "penis-licking" in position_text or "penis licking" in position_text: return filtered(lambda text: "licking" in text or "tongue" in text) if "handjob" in position_text or "hand job" in position_text: return filtered(lambda text: any(term in text for term in ("handjob", "hand job", "hand wrapped", "two-handed"))) if "footjob" in position_text: return filtered(lambda text: any(term in text for term in ("footjob", "feet", "soles", "toes"))) return values def _outercourse_axis_values_for_position(values: list[Any], position: str, axis_name: str) -> list[Any]: position_text = str(position or "").lower() if not position_text: return values axis_name = str(axis_name or "").lower() if axis_name not in {"contact_detail", "hand_detail", "texture_detail", "visibility", "body_contact"}: return values def value_text(value: Any) -> str: return _entry_text(value).lower() def filtered(terms: tuple[str, ...], excluded_terms: tuple[str, ...] = ()) -> list[Any]: matches = [ value for value in values if any(term in value_text(value) for term in terms) and not any(term in value_text(value) for term in excluded_terms) ] return matches or values if any(term in position_text for term in ("boobjob", "titjob", "breast-sex", "breast sex")): by_axis = { "contact_detail": ("compressed", "glans", "breast", "breasts", "soft tissue", "skin visibly"), "hand_detail": ("breast", "breasts", "fingers"), "texture_detail": ("compression", "soft flesh", "skin", "flesh", "asymmetry"), "visibility": ("breast", "breasts", "glans", "shaft"), "body_contact": ("torso", "body angled", "shoulders", "hips"), } excluded_by_axis = { "contact_detail": ("hand wrapped", "fingers and palm", "soles", "toes", "balls", "tongue"), "hand_detail": ("base of the penis", "penis shaft", "balls", "thigh", "ankles", "stroking"), "texture_detail": ("toes", "soles", "tongue"), "visibility": ("balls", "soles", "toes", "hand"), "body_contact": ("head tucked", "face directly", "base of the penis"), } return filtered( by_axis.get(axis_name, ("breast", "breasts", "shaft")), excluded_by_axis.get(axis_name, ()), ) if any(term in position_text for term in ("testicle", "balls")): by_axis = { "contact_detail": ("balls", "lips", "tongue", "wet"), "hand_detail": ("balls", "base", "thigh"), "texture_detail": ("wet", "saliva", "skin"), "visibility": ("balls", "mouth"), "body_contact": ("torso", "shoulders", "head tucked", "base of the penis", "knees", "thigh"), } return filtered(by_axis.get(axis_name, ("balls", "mouth", "tongue"))) if "penis-licking" in position_text or "penis licking" in position_text: by_axis = { "contact_detail": ("tongue", "lips", "glans", "shaft", "wet"), "hand_detail": ("base", "penis", "thigh"), "texture_detail": ("wet", "saliva", "skin"), "visibility": ("tongue", "penis"), "body_contact": ("head low", "face directly", "torso", "pelvis", "base of the penis", "hips", "body angled"), } return filtered(by_axis.get(axis_name, ("tongue", "glans", "shaft"))) if "handjob" in position_text or "hand job" in position_text: by_axis = { "contact_detail": ("hand", "fingers", "palm", "shaft", "glans"), "hand_detail": ("hand", "hands", "shaft", "penis"), "texture_detail": ("fingers", "pressure", "skin", "shaft"), "visibility": ("hand", "penis", "shaft", "glans"), "body_contact": ("hips", "knees", "body angle"), } return filtered(by_axis.get(axis_name, ("hand", "penis", "shaft"))) if "footjob" in position_text: by_axis = { "contact_detail": ("soles", "toes"), "hand_detail": ("ankles", "thighs"), "texture_detail": ("toes", "soles", "pressure"), "visibility": ("feet", "soles"), "body_contact": ("legs", "knees", "body angled"), } excluded_by_axis = { "contact_detail": ("hand", "finger", "palm", "balls", "tongue", "breast"), "texture_detail": ("fingers", "tongue", "breast"), "visibility": ("hand", "balls", "breast"), } return filtered( by_axis.get(axis_name, ("feet", "soles", "toes")), excluded_by_axis.get(axis_name, ()), ) return values def _compose_item( rng: random.Random, category: dict[str, Any], subcategory: dict[str, Any], item: Any, women_count: int = 1, men_count: int = 1, ) -> tuple[str, str, dict[str, str], dict[str, Any]]: templates = _template_list(category, subcategory, item, "item_templates") axes = _merged_axes(category, subcategory, item) if templates and axes: template_entry = _weighted_choice(rng, _compatible_entries(templates, women_count, men_count)) template = _entry_text(template_entry) fields = [key for _, key, _, _ in Formatter().parse(template) if key] unique_fields = list(dict.fromkeys(fields)) axis_values: dict[str, str] = {} subcategory_slug = str(subcategory.get("slug") or "").lower() if subcategory_slug in ("oral_sex", "outercourse_sex") and "position" in unique_fields and axes.get("position"): position_values = _compatible_entries(axes["position"], women_count, men_count) axis_values["position"] = _entry_text(_weighted_choice(rng, position_values)) for name in unique_fields: if name in axis_values or name not in axes or not axes[name]: continue values = _compatible_entries(axes[name], women_count, men_count) if subcategory_slug == "oral_sex" and name == "oral_act": values = _oral_acts_for_position(values, axis_values.get("position", "")) elif subcategory_slug == "oral_sex": values = _oral_axis_values_for_context( values, axis_values.get("position", ""), axis_values.get("oral_act", ""), name, ) if subcategory_slug == "outercourse_sex" and name == "outer_act": values = _outercourse_acts_for_position(values, axis_values.get("position", "")) if subcategory_slug == "outercourse_sex": values = _outercourse_axis_values_for_position(values, axis_values.get("position", ""), name) axis_values[name] = _entry_text(_weighted_choice(rng, values)) item_text = _format(template, axis_values).strip() item_name = _item_name(item) or subcategory["name"] return item_text, item_name, axis_values, _template_metadata(template_entry) return _item_text(item), _item_name(item), {}, _template_metadata(item) def _choose_text(rng: random.Random, items: list[Any]) -> str: item = _weighted_choice(rng, items) return _item_text(item) def _choose_distinct_text(rng: random.Random, items: list[Any], first_text: str) -> str: first_text = _item_text(first_text).lower() distinct = [item for item in items if _item_text(item).lower() != first_text] if not distinct: return "" return _choose_text(rng, distinct) def _choose_pair(rng: random.Random, items: list[Any]) -> tuple[str, str]: return _pair_from(_weighted_choice(rng, items)) def _extension_targets() -> dict[str, tuple[list[Any], bool]]: return { "women_clothes": (g.WOMEN_CLOTHES, False), "women_clothes_minimal": (g.WOMEN_CLOTHES_MINIMAL, False), "men_clothes": (g.MEN_CLOTHES, False), "men_clothes_minimal": (g.MEN_CLOTHES_MINIMAL, False), "couple_outfits": (g.COUPLE_OUTFITS, False), "couple_outfits_minimal": (g.COUPLE_OUTFITS_MINIMAL, False), "poses": (g.POSES, False), "evocative_poses": (g.EVOCATIVE_POSES, False), "backside_poses": (g.BACKSIDE_POSES, False), "expressions": (g.EXPRESSIONS, False), "compositions": (g.COMPOSITIONS, False), "props": (g.PROPS, False), "figure_curvy": (g.FIGURE_CURVY, False), "figure_athletic": (g.FIGURE_ATHLETIC, False), "figure_bombshell": (g.FIGURE_BOMBSHELL, False), "scenes": (g.SCENES, True), "group_scenes": (g.GROUP_SCENES, True), "layouts_full": (g.LAYOUTS_FULL, True), "layouts_minimal": (g.LAYOUTS_MINIMAL, True), "group_compositions": (g.GROUP_COMPOSITIONS, False), "group_ages": (g.GROUP_AGES, False), } def apply_pool_extensions() -> None: global _EXTENSIONS_APPLIED if _EXTENSIONS_APPLIED: return targets = _extension_targets() for path in _json_files(): data = _read_json(path) extensions = data.get("pool_extensions", {}) if not isinstance(extensions, dict): raise ValueError(f"pool_extensions in {path} must be an object") for target_name, additions in extensions.items(): if target_name not in targets: known = ", ".join(sorted(targets)) raise ValueError(f"Unknown pool extension '{target_name}' in {path}. Known: {known}") target, expects_pair = targets[target_name] normalized = [_pair_from(item) for item in _list_from(additions)] if expects_pair else [ _item_text(item) for item in _list_from(additions) ] _unique_extend(target, normalized) g.EVOCATIVE_ALL = g.EVOCATIVE_POSES + g.BACKSIDE_POSES _EXTENSIONS_APPLIED = True def category_choices() -> list[str]: apply_pool_extensions() custom = [category["name"] for category in load_category_library()] return BUILTIN_CATEGORIES + [name for name in custom if name not in BUILTIN_CATEGORIES] def subcategory_choices() -> list[str]: apply_pool_extensions() choices = [RANDOM_SUBCATEGORY] for category in load_category_library(): for subcategory in category["subcategories"]: choices.append(f"{category['name']} / {subcategory['name']}") return choices def seed_mode_choices() -> list[str]: return seed_policy.seed_mode_choices() CATEGORY_PRESETS = category_cast_policy.CATEGORY_PRESETS CAST_PRESETS = category_cast_policy.CAST_PRESETS GENERATION_PROFILE_PRESETS = generation_profile_policy.GENERATION_PROFILE_PRESETS def category_preset_choices() -> list[str]: return category_cast_policy.category_preset_choices() def cast_preset_choices() -> list[str]: return category_cast_policy.cast_preset_choices() def generation_profile_choices() -> list[str]: return generation_profile_policy.generation_profile_choices() def build_category_config_json(preset: str = "auto_weighted", subcategory: str = RANDOM_SUBCATEGORY) -> str: return category_cast_policy.build_category_config_json(preset=preset, subcategory=subcategory) def _parse_category_config(category_config: str | dict[str, Any] | None) -> tuple[str, str]: return category_cast_policy.parse_category_config(category_config) def build_cast_config_json(cast_mode: str = "mixed_couple", women_count: int = 1, men_count: int = 1) -> str: return category_cast_policy.build_cast_config_json(cast_mode=cast_mode, women_count=women_count, men_count=men_count) def _parse_cast_config(cast_config: str | dict[str, Any] | None) -> dict[str, int | str]: return category_cast_policy.parse_cast_config(cast_config) def build_generation_profile_json( profile: str = "balanced", clothing_override: str = "profile_default", poses_override: str = "profile_default", expression_intensity_mode: str = "profile_default", expression_intensity: float = -1.0, backside_bias: float = -1.0, minimal_clothing_ratio: float = -1.0, standard_pose_ratio: float = -1.0, trigger_policy: str = "profile_default", expression_enabled: bool = True, ) -> str: return generation_profile_policy.build_generation_profile_json( profile=profile, clothing_override=clothing_override, poses_override=poses_override, expression_intensity_mode=expression_intensity_mode, expression_intensity=expression_intensity, backside_bias=backside_bias, minimal_clothing_ratio=minimal_clothing_ratio, standard_pose_ratio=standard_pose_ratio, trigger_policy=trigger_policy, expression_enabled=expression_enabled, ) def _parse_generation_profile(profile_config: str | dict[str, Any] | None) -> dict[str, Any]: return generation_profile_policy.parse_generation_profile(profile_config) def build_filter_config_json( ethnicity: str = "any", figure: str = "curvy", no_plus_women: bool = False, no_black: bool = False, include_european: bool = True, include_mediterranean_mena: bool = True, include_latina: bool = True, include_east_asian: bool = True, include_southeast_asian: bool = True, include_south_asian: bool = True, include_black_african: bool = True, include_indigenous: bool = True, include_mixed: bool = True, include_plus_size: bool = True, ) -> str: return filter_policy.build_filter_config_json( ethnicity=ethnicity, figure=figure, no_plus_women=no_plus_women, no_black=no_black, include_european=include_european, include_mediterranean_mena=include_mediterranean_mena, include_latina=include_latina, include_east_asian=include_east_asian, include_southeast_asian=include_southeast_asian, include_south_asian=include_south_asian, include_black_african=include_black_african, include_indigenous=include_indigenous, include_mixed=include_mixed, include_plus_size=include_plus_size, ) LOCATION_POOL_PRESETS = location_policy.LOCATION_POOL_PRESETS COMPOSITION_POOL_PRESETS = location_policy.COMPOSITION_POOL_PRESETS COMPOSITION_INLINE_PRESETS = location_policy.COMPOSITION_INLINE_PRESETS THEMATIC_LOCATION_PRESETS = location_policy.THEMATIC_LOCATION_PRESETS def location_pool_preset_choices() -> list[str]: return location_policy.location_pool_preset_choices() def composition_pool_preset_choices() -> list[str]: return location_policy.composition_pool_preset_choices() def location_theme_choices() -> list[str]: return location_policy.location_theme_choices() def _location_pool_names_for_preset(preset: str) -> list[str]: return location_policy.location_pool_names_for_preset(preset) def _custom_location_entries(custom_locations: str) -> list[dict[str, str]]: return location_policy.custom_location_entries(custom_locations) def _scene_entries_for_pool_names(pool_names: list[str]) -> list[Any]: return location_policy.scene_entries_for_pool_names(pool_names) def build_location_pool_json( enabled: bool = True, combine_mode: str = "replace", preset: str = "custom_only", custom_locations: str = "", location_config: str | dict[str, Any] | None = "", ) -> str: return location_policy.build_location_pool_json( enabled=enabled, combine_mode=combine_mode, preset=preset, custom_locations=custom_locations, location_config=location_config, ) def _parse_location_config(location_config: str | dict[str, Any] | None) -> dict[str, Any]: return location_policy.parse_location_config(location_config) def _location_config_active(location_config: dict[str, Any]) -> bool: return location_policy.location_config_active(location_config) def _composition_pool_names_for_preset(preset: str) -> list[str]: return location_policy.composition_pool_names_for_preset(preset) def _custom_composition_entries(custom_compositions: str) -> list[str]: return location_policy.custom_composition_entries(custom_compositions) def _composition_entries_for_pool_names(pool_names: list[str]) -> list[Any]: return location_policy.composition_entries_for_pool_names(pool_names) def build_composition_pool_json( enabled: bool = True, combine_mode: str = "replace", preset: str = "custom_only", custom_compositions: str = "", composition_config: str | dict[str, Any] | None = "", ) -> str: return location_policy.build_composition_pool_json( enabled=enabled, combine_mode=combine_mode, preset=preset, custom_compositions=custom_compositions, composition_config=composition_config, ) def _parse_composition_config(composition_config: str | dict[str, Any] | None) -> dict[str, Any]: return location_policy.parse_composition_config(composition_config) def _composition_config_active(composition_config: dict[str, Any]) -> bool: return location_policy.composition_config_active(composition_config) def build_thematic_location_json( enabled: bool = True, combine_mode: str = "replace", theme: str = "semi_public_affair", custom_locations: str = "", custom_compositions: str = "", location_config: str | dict[str, Any] | None = "", composition_config: str | dict[str, Any] | None = "", ) -> tuple[str, str, str]: return location_policy.build_thematic_location_json( enabled=enabled, combine_mode=combine_mode, theme=theme, custom_locations=custom_locations, custom_compositions=custom_compositions, location_config=location_config, composition_config=composition_config, ) def _ethnicity_text_from_value(value: Any) -> str: return filter_policy.ethnicity_text_from_value(value) def _is_valid_ethnicity_filter(value: Any) -> bool: return filter_policy.is_valid_ethnicity_filter(value) def normalize_ethnicity_filter(value: Any, default: str = "any", allow_random: bool = False) -> str: return filter_policy.normalize_ethnicity_filter(value, default, allow_random) def build_ethnicity_list_json( include_european: bool = False, include_mediterranean_mena: bool = False, include_latina: bool = False, include_east_asian: bool = False, include_southeast_asian: bool = False, include_south_asian: bool = False, include_black_african: bool = False, include_indigenous: bool = False, include_mixed: bool = False, include_asian: bool = False, include_white_asian: bool = False, include_western_european: bool = False, include_french_european: bool = False, include_germanic_european: bool = False, include_nordic_european: bool = False, include_celtic_european: bool = False, include_slavic_european: bool = False, include_baltic_european: bool = False, include_alpine_european: bool = False, include_balkan_european: bool = False, include_greek_mediterranean: bool = False, include_italian_mediterranean: bool = False, include_iberian_mediterranean: bool = False, strict_excludes: bool = True, ) -> dict[str, str]: return filter_policy.build_ethnicity_list_json( include_european=include_european, include_mediterranean_mena=include_mediterranean_mena, include_latina=include_latina, include_east_asian=include_east_asian, include_southeast_asian=include_southeast_asian, include_south_asian=include_south_asian, include_black_african=include_black_african, include_indigenous=include_indigenous, include_mixed=include_mixed, include_asian=include_asian, include_white_asian=include_white_asian, include_western_european=include_western_european, include_french_european=include_french_european, include_germanic_european=include_germanic_european, include_nordic_european=include_nordic_european, include_celtic_european=include_celtic_european, include_slavic_european=include_slavic_european, include_baltic_european=include_baltic_european, include_alpine_european=include_alpine_european, include_balkan_european=include_balkan_european, include_greek_mediterranean=include_greek_mediterranean, include_italian_mediterranean=include_italian_mediterranean, include_iberian_mediterranean=include_iberian_mediterranean, strict_excludes=strict_excludes, ) def _parse_filter_config(filter_config: str | dict[str, Any] | None) -> dict[str, Any]: return filter_policy.parse_filter_config(filter_config) def _normalize_hardcore_position_family(value: Any, default: str = "any") -> str: return hardcore_position_policy.normalize_hardcore_position_family(value, default) def _normalize_hardcore_position_values(values: Any) -> list[str]: return hardcore_position_policy.normalize_hardcore_position_values(values) def _empty_hardcore_position_config() -> dict[str, Any]: return hardcore_position_policy.empty_hardcore_position_config() def _parse_hardcore_position_config(value: str | dict[str, Any] | None) -> dict[str, Any]: return hardcore_position_policy.parse_hardcore_position_config(value) def _hardcore_position_summary(config: dict[str, Any]) -> str: return hardcore_position_policy.hardcore_position_summary(config) def build_hardcore_position_pool_json( hardcore_position_config: str | dict[str, Any] | None = "", combine_mode: str = "replace", family: str = "any", selected_positions: list[str] | tuple[str, ...] | str | None = None, ) -> str: return hardcore_position_policy.build_hardcore_position_pool_json( hardcore_position_config=hardcore_position_config, combine_mode=combine_mode, family=family, selected_positions=selected_positions, ) def build_hardcore_action_filter_json( hardcore_position_config: str | dict[str, Any] | None = "", focus: str = "keep_pool", allow_toys: bool = False, allow_double: bool = False, allow_penetration: bool = True, allow_foreplay: bool = True, allow_interaction: bool = True, allow_manual: bool = True, allow_oral: bool = True, allow_outercourse: bool = True, allow_anal: bool = True, allow_climax: bool = True, ) -> str: return hardcore_position_policy.build_hardcore_action_filter_json( hardcore_position_config=hardcore_position_config, focus=focus, allow_toys=allow_toys, allow_double=allow_double, allow_penetration=allow_penetration, allow_foreplay=allow_foreplay, allow_interaction=allow_interaction, allow_manual=allow_manual, allow_oral=allow_oral, allow_outercourse=allow_outercourse, allow_anal=allow_anal, allow_climax=allow_climax, ) def _hardcore_position_config_active(config: dict[str, Any]) -> bool: return hardcore_position_policy.hardcore_position_config_active(config) def _is_hardcore_sexual_category(category: dict[str, Any]) -> bool: return hardcore_position_policy.is_hardcore_sexual_category(category) def _filter_hardcore_categories_for_position( categories: list[dict[str, Any]], config: dict[str, Any], women_count: int, men_count: int, ) -> list[dict[str, Any]]: return hardcore_position_policy.filter_hardcore_categories_for_position( categories, config, women_count, men_count, _compatible_entry, ) def _apply_hardcore_position_config_to_subcategory( subcategory: dict[str, Any], config: dict[str, Any], ) -> dict[str, Any]: return hardcore_position_policy.apply_hardcore_position_config_to_subcategory(subcategory, config) def _ratio_or_none(value: float) -> float | None: try: ratio = float(value) except (TypeError, ValueError): return None if ratio < 0: return None return max(0.0, min(1.0, ratio)) def _clamped_float(value: Any, default: float = 0.5, min_value: float = 0.0, max_value: float = 1.0) -> float: try: number = float(value) except (TypeError, ValueError): return default return max(min_value, min(max_value, number)) def build_seed_config_json( category_seed: int = -1, subcategory_seed: int = -1, content_seed: int = -1, person_seed: int = -1, scene_seed: int = -1, pose_seed: int = -1, role_seed: int = -1, expression_seed: int = -1, composition_seed: int = -1, category_seed_mode: str = "auto", subcategory_seed_mode: str = "auto", content_seed_mode: str = "auto", person_seed_mode: str = "auto", scene_seed_mode: str = "auto", pose_seed_mode: str = "auto", role_seed_mode: str = "auto", expression_seed_mode: str = "auto", composition_seed_mode: str = "auto", ) -> str: return seed_policy.build_seed_config_json( category_seed=category_seed, subcategory_seed=subcategory_seed, content_seed=content_seed, person_seed=person_seed, scene_seed=scene_seed, pose_seed=pose_seed, role_seed=role_seed, expression_seed=expression_seed, composition_seed=composition_seed, category_seed_mode=category_seed_mode, subcategory_seed_mode=subcategory_seed_mode, content_seed_mode=content_seed_mode, person_seed_mode=person_seed_mode, scene_seed_mode=scene_seed_mode, pose_seed_mode=pose_seed_mode, role_seed_mode=role_seed_mode, expression_seed_mode=expression_seed_mode, composition_seed_mode=composition_seed_mode, ) def build_seed_lock_config_json( base_seed: int = 20260614, reroll_axis: str = "none", reroll_seed: int = -1, ) -> str: return seed_policy.build_seed_lock_config_json( base_seed=base_seed, reroll_axis=reroll_axis, reroll_seed=reroll_seed, ) def _parse_seed_config(seed_config: str | dict[str, Any] | None) -> dict[str, int]: return seed_policy.parse_seed_config(seed_config) def _configured_axis_seed(seed_config: dict[str, int], axis: str) -> int | None: return seed_policy.configured_axis_seed(seed_config, axis) def _axis_rng(seed_config: dict[str, int], axis: str, base_seed: int, row_number: int) -> random.Random: return seed_policy.axis_rng(seed_config, axis, base_seed, row_number) def _is_pose_content_category(category: dict[str, Any], subcategory: dict[str, Any]) -> bool: haystack = " ".join( str(value) for value in ( category.get("name", ""), category.get("slug", ""), category.get("item_label", ""), subcategory.get("name", ""), subcategory.get("slug", ""), subcategory.get("item_label", ""), ) ).lower() return "pose" in haystack or "sex" in haystack def _format(template: str, context: dict[str, Any]) -> str: fields = {key for _, key, _, _ in Formatter().parse(template) if key} safe_context = SafeFormatDict({key: str(value) for key, value in context.items()}) for field in fields: safe_context.setdefault(field, "{" + field + "}") return template.format_map(safe_context) def _clean_prompt_punctuation(text: str) -> str: text = re.sub(r"\s+", " ", str(text or "")).strip() text = re.sub(r"\s+([,.;:])", r"\1", text) text = re.sub(r"(?:,\s*){2,}", ", ", text) text = re.sub(r"\.\s*\.", ".", text) text = re.sub(r":\s*\.", ".", text) return text.strip() def _strip_expression_text(text: str, expression: Any = "") -> str: text = str(text or "") if not text: return "" text = re.sub(r"\s*Facial expressions?:\s*[^.]*\.\s*", " ", text, flags=re.IGNORECASE) text = re.sub(r",\s*one with [^,]+ and the other with [^,]+(?=,)", "", text, flags=re.IGNORECASE) text = re.sub(r",\s*a lively mix of expressions from [^,]+(?=,)", "", text, flags=re.IGNORECASE) text = re.sub(r"\s+with\s+(?:an?|the)\s+[^,]*expression(?=,)", "", text, flags=re.IGNORECASE) expression_text = str(expression or "").strip() if expression_text: for part in [piece.strip() for piece in expression_text.split(";") if piece.strip()]: escaped = re.escape(part) text = re.sub(rf",\s*{escaped}(?=,)", "", text, flags=re.IGNORECASE) text = re.sub(rf"\s+with\s+(?:an?|the)?\s*{escaped}", "", text, flags=re.IGNORECASE) return _clean_prompt_punctuation(text) def _disable_row_expression(row: dict[str, Any], source: str = "disabled") -> dict[str, Any]: previous_expression = row.get("expression", "") row["prompt"] = _strip_expression_text(row.get("prompt", ""), previous_expression) row["caption"] = _strip_expression_text(row.get("caption", ""), previous_expression) row["expression"] = "" row["shared_expression"] = "" row["character_expressions"] = [] row["character_expression_text"] = "" row["expression_enabled"] = False row["expression_disabled"] = True row["expression_intensity"] = None row["expression_intensity_source"] = source return row def _prepend_trigger(prompt: str, trigger: str, enabled: bool) -> str: return row_policy.prepend_trigger(prompt, trigger, enabled) def _combined_negative(base: str, extra: str) -> str: return row_policy.combined_negative(base, extra) def camera_mode_choices() -> list[str]: return camera_policy.camera_mode_choices() def ethnicity_choices() -> list[str]: return list(ETHNICITY_FILTER_CHOICES) def character_label_choices() -> list[str]: return character_policy.character_label_choices() def character_age_choices() -> list[str]: return character_policy.character_age_choices() def character_body_choices() -> list[str]: return character_policy.character_body_choices() def character_woman_body_choices() -> list[str]: return character_policy.character_woman_body_choices() def character_man_body_choices() -> list[str]: return character_policy.character_man_body_choices() def character_descriptor_detail_choices() -> list[str]: return character_policy.character_descriptor_detail_choices() def character_presence_choices() -> list[str]: return character_policy.character_presence_choices() def character_hair_color_choices() -> list[str]: return character_policy.character_hair_color_choices() def character_hair_length_choices() -> list[str]: return character_policy.character_hair_length_choices() def character_hair_style_choices() -> list[str]: return character_policy.character_hair_style_choices() def character_eye_color_choices() -> list[str]: return character_policy.character_eye_color_choices() def character_ethnicity_choices() -> list[str]: return ["random"] + list(ETHNICITY_FILTER_CHOICES) def character_figure_choices() -> list[str]: return ["random", "curvy", "balanced", "bombshell"] def camera_detail_choices() -> list[str]: return camera_policy.camera_detail_choices() def hardcore_detail_density_choices() -> list[str]: return pair_options.hardcore_detail_density_choices() def hardcore_position_family_choices() -> list[str]: return hardcore_position_policy.hardcore_position_family_choices() def hardcore_position_focus_choices() -> list[str]: return hardcore_position_policy.hardcore_position_focus_choices() def hardcore_position_key_choices() -> list[str]: return hardcore_position_policy.hardcore_position_key_choices() def character_softcore_outfit_source_choices() -> list[str]: return [ "no_change", "social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude", "partner_woman", "partner_man", "custom", ] def character_hardcore_clothing_state_choices() -> list[str]: return [ "no_change", "fully_nude", "partly_exposed", "same_outfit", "partially_removed", "custom", ] def camera_orbit_framing_choices() -> list[str]: return camera_policy.camera_orbit_framing_choices() def camera_orbit_focus_choices() -> list[str]: return camera_policy.camera_orbit_focus_choices() def camera_shot_choices() -> list[str]: return camera_policy.camera_shot_choices() def camera_angle_choices() -> list[str]: return camera_policy.camera_angle_choices() def camera_lens_choices() -> list[str]: return camera_policy.camera_lens_choices() def camera_distance_choices() -> list[str]: return camera_policy.camera_distance_choices() def camera_orientation_choices() -> list[str]: return camera_policy.camera_orientation_choices() def camera_phone_choices() -> list[str]: return camera_policy.camera_phone_choices() def camera_priority_choices() -> list[str]: return camera_policy.camera_priority_choices() def build_camera_config_json( camera_mode: str = "standard", shot_size: str = "auto", angle: str = "auto", lens: str = "auto", distance: str = "auto", orientation: str = "auto", phone_visibility: str = "auto", priority: str = "strong", camera_detail: str = "compact", ) -> str: return camera_policy.build_camera_config_json( camera_mode=camera_mode, shot_size=shot_size, angle=angle, lens=lens, distance=distance, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, ) def _camera_orbit_direction(horizontal_angle: Any) -> str: return camera_policy._camera_orbit_direction(horizontal_angle) def _camera_orbit_elevation(vertical_angle: Any) -> str: return camera_policy._camera_orbit_elevation(vertical_angle) def _camera_orbit_distance(zoom: Any, framing: str = "from_zoom") -> str: return camera_policy._camera_orbit_distance(zoom, framing) def _camera_orbit_focus(subject_focus: str) -> str: return camera_policy._camera_orbit_focus(subject_focus) def _camera_orbit_prompt( horizontal_angle: Any, vertical_angle: Any, zoom: Any, framing: str = "from_zoom", subject_focus: str = "auto", include_degrees: bool = True, ) -> tuple[str, dict[str, Any]]: return camera_policy.camera_orbit_prompt( horizontal_angle, vertical_angle, zoom, framing=framing, subject_focus=subject_focus, include_degrees=include_degrees, ) def build_camera_orbit_config_json( enabled: bool = True, camera_mode: str = "standard", horizontal_angle: int = 0, vertical_angle: int = 0, zoom: float = 5.0, framing: str = "from_zoom", subject_focus: str = "auto", lens: str = "auto", orientation: str = "auto", phone_visibility: str = "auto", priority: str = "locked", camera_detail: str = "compact", include_degrees: bool = True, ) -> str: return camera_policy.build_camera_orbit_config_json( enabled=enabled, camera_mode=camera_mode, horizontal_angle=horizontal_angle, vertical_angle=vertical_angle, zoom=zoom, framing=framing, subject_focus=subject_focus, lens=lens, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, include_degrees=include_degrees, ) QWEN_CAMERA_DIRECTIONS = camera_policy.QWEN_CAMERA_DIRECTIONS QWEN_CAMERA_ELEVATIONS = camera_policy.QWEN_CAMERA_ELEVATIONS QWEN_CAMERA_ZOOMS = camera_policy.QWEN_CAMERA_ZOOMS QWEN_CAMERA_SCENE_CENTER_Y = camera_policy.QWEN_CAMERA_SCENE_CENTER_Y def _qwen_prompt_camera_values(qwen_prompt: Any) -> tuple[int, int, float]: return camera_policy._qwen_prompt_camera_values(qwen_prompt) def _camera_info_dict(camera_info: Any) -> dict[str, Any] | None: return camera_policy._camera_info_dict(camera_info) def _qwen_camera_info_values(camera_info: Any) -> tuple[int, int, float] | None: return camera_policy._qwen_camera_info_values(camera_info) def build_qwen_camera_config_json( qwen_prompt: str = "", camera_info: Any = None, prefer_camera_info: bool = True, camera_mode: str = "standard", subject_focus: str = "auto", lens: str = "auto", orientation: str = "auto", phone_visibility: str = "auto", priority: str = "locked", camera_detail: str = "compact", include_degrees: bool = False, suppress_phone_visibility: bool = True, ) -> str: return camera_policy.build_qwen_camera_config_json( qwen_prompt=qwen_prompt, camera_info=camera_info, prefer_camera_info=prefer_camera_info, camera_mode=camera_mode, subject_focus=subject_focus, lens=lens, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, include_degrees=include_degrees, suppress_phone_visibility=suppress_phone_visibility, ) def _choice(value: Any, choices: dict[str, str], default: str) -> str: return camera_policy._choice(value, choices, default) def _parse_camera_config(camera_config: str | dict[str, Any] | None) -> dict[str, Any]: return camera_policy.parse_camera_config(camera_config) def _camera_config_with_mode(camera_config: str | dict[str, Any] | None, camera_mode: str) -> dict[str, Any]: return camera_policy.camera_config_with_mode(camera_config, camera_mode) def _camera_directive(camera_config: str | dict[str, Any] | None) -> tuple[str, dict[str, Any]]: return camera_policy.camera_directive(camera_config) def _insert_positive_directive(prompt: str, directive: str) -> str: return row_camera_policy.insert_positive_directive(prompt, directive) def _camera_caption_text(parsed: dict[str, Any]) -> str: return row_camera_policy.camera_caption_text(parsed) def _coworking_composition_prompt(scene_text: Any, composition: Any, subject_kind: str = "subjects") -> str: return row_camera_policy.coworking_composition_prompt(scene_text, composition, subject_kind) def _apply_coworking_composition(row: dict[str, Any], subject_kind: str) -> dict[str, Any]: return row_camera_policy.apply_contextual_composition(row, subject_kind) def _camera_scene_directive_for_context( scene_text: Any, composition: Any, camera_config: str | dict[str, Any] | None, pov_labels: list[str] | None = None, subject_kind: str = "subjects", ) -> tuple[str, dict[str, Any]]: directive, parsed = row_camera_policy.camera_scene_directive_for_context( scene_text, composition, camera_config, pov_labels, subject_kind, CAMERA_COMPACT_LABELS, ) return directive, parsed def _row_camera_subject_kind(row: dict[str, Any]) -> str: return row_camera_policy.row_camera_subject_kind(row) def _camera_pov_labels_for_row(row: dict[str, Any]) -> list[str]: return _pov_character_labels( _character_slot_label_map(_parse_character_cast(row.get("character_cast_slots"))), int(row.get("men_count") or 0) if str(row.get("men_count") or "").isdigit() else 0, ) def _apply_camera_config(row: dict[str, Any], camera_config: str | dict[str, Any] | None) -> dict[str, Any]: return row_camera_policy.apply_camera_config( row, camera_config, pov_label_resolver=_camera_pov_labels_for_row, compact_labels=CAMERA_COMPACT_LABELS, ) def _row_seed(seed: int, row_number: int, salt: int = 0) -> int: return seed_policy.row_seed(seed, row_number, salt) def _pick_clothing_mode(rng: random.Random, clothing: str, minimal_ratio: float | None) -> str: if clothing == "random": return "minimal" if rng.random() < 0.5 else "full" if minimal_ratio is None: return clothing return "minimal" if rng.random() < minimal_ratio else "full" def _pick_pose_mode(rng: random.Random, poses: str, standard_ratio: float | None) -> str: if poses == "random": return "standard" if rng.random() < 0.5 else "evocative" if standard_ratio is None: return poses return "standard" if rng.random() < standard_ratio else "evocative" def _pick_figure_bias(rng: random.Random, figure: str) -> str: if figure in ("curvy", "balanced", "bombshell"): return figure return g.choose(rng, ["curvy", "balanced", "bombshell"]) def _pick_expression_intensity(rng: random.Random, expression_intensity: Any) -> tuple[float, str]: try: value = float(expression_intensity) except (TypeError, ValueError): return 0.5, "default" if value < 0: return round(rng.random(), 2), "random" return _clamped_float(value, 0.5), "input" def _build_auto_weighted_row( row_number: int, start_index: int, clothing: str, ethnicity: str, poses: str, backside_bias: float, figure: str, no_plus_women: bool, no_black: bool, minimal_clothing_ratio: float | None, standard_pose_ratio: float | None, seed: int, ) -> dict[str, Any]: batch_number = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1) rows = g.build_rows( batch_number * g.BATCH_SIZE, start_index, clothing, ethnicity, poses, backside_bias, figure, no_plus_women, no_black, minimal_clothing_ratio, standard_pose_ratio, seed, g.EXPRESSION_SEED + seed, ) row = rows[row_number - 1] row["main_category"] = "auto_weighted" row["subcategory"] = row.get("primary_subject", "auto") row["source"] = "built_in_generator" return row def _build_direct_builtin_row( category: str, row_number: int, start_index: int, clothing: str, ethnicity: str, poses: str, backside_bias: float, figure: str, no_plus_women: bool, no_black: bool, minimal_clothing_ratio: float | None, standard_pose_ratio: float | None, seed: int, ) -> dict[str, Any]: rng = random.Random(_row_seed(seed, row_number)) expr_deck = g.ExpressionDeck(g.EXPRESSIONS, random.Random(_row_seed(g.EXPRESSION_SEED + seed, row_number))) batch = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1) index = start_index + row_number - 1 row_clothing = _pick_clothing_mode(rng, clothing, minimal_clothing_ratio) row_poses = _pick_pose_mode(rng, poses, standard_pose_ratio) if category == "woman": row = g.make_single( index, batch, rng, "woman", expr_deck, row_clothing, ethnicity, row_poses, backside_bias, figure, no_plus_women, no_black, ) elif category == "man": row = g.make_single(index, batch, rng, "man", expr_deck, row_clothing, ethnicity, row_poses, backside_bias, figure) elif category == "couple": row = g.make_couple(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women) elif category == "group_or_layout": row = g.make_group_or_layout(index, batch, rng, expr_deck, row_clothing, ethnicity, no_plus_women) else: raise ValueError(f"Unknown built-in category: {category}") row["main_category"] = category row["subcategory"] = row.get("pose_mode", category) row["source"] = "built_in_generator" return row def _auto_full_choice(seed_config: dict[str, int], seed: int, row_number: int) -> str: categories = load_category_library() if not categories: return "auto_weighted" category_rng = _axis_rng(seed_config, "category", seed, row_number) choices: list[dict[str, Any]] = [{"category": "auto_weighted", "weight": 1.0}] choices.extend( { "category": category["name"], "weight": category.get("weight", 1.0), } for category in categories ) choice = _weighted_choice(category_rng, choices) return str(choice.get("category") or "auto_weighted") def _body_phrase(body: Any, figure_note: Any = "") -> str: return character_profile_policy.body_phrase(body, figure_note) def _safe_profile_name(profile_name: str) -> str: return character_profile_policy.safe_profile_name(profile_name) def _profile_path(profile_name: str) -> Path: return character_profile_policy.profile_path(profile_name) def character_profile_choices() -> list[str]: return character_profile_policy.character_profile_choices() def _load_json_object(value: str | dict[str, Any] | None, label: str) -> dict[str, Any]: return character_profile_policy.load_json_object(value, label) CHARACTER_MANUAL_FIELDS = character_profile_policy.CHARACTER_MANUAL_FIELDS def _parse_character_manual_config(value: str | dict[str, Any] | None) -> dict[str, str]: return character_profile_policy.parse_character_manual_config(value) def _character_manual_summary(config: dict[str, str]) -> str: return character_profile_policy.character_manual_summary(config) def build_character_manual_config_json( manual: str | dict[str, Any] | None = "", combine_mode: str = "merge_nonempty", manual_age: str = "", manual_body: str = "", body_phrase: str = "", skin: str = "", hair: str = "", eyes: str = "", softcore_outfit: str = "", hardcore_clothing: str = "", ) -> str: return character_profile_policy.build_character_manual_config_json( manual=manual, combine_mode=combine_mode, manual_age=manual_age, manual_body=manual_body, body_phrase=body_phrase, skin=skin, hair=hair, eyes=eyes, softcore_outfit=softcore_outfit, hardcore_clothing=hardcore_clothing, ) def _slot_value(value: Any) -> str: return character_policy.slot_value(value) CHARACTER_CHARACTERISTIC_AXES = character_policy.CHARACTER_CHARACTERISTIC_AXES def _empty_characteristics_config() -> dict[str, Any]: return character_policy.empty_characteristics_config() def _normalize_characteristic_choice(value: Any, choices: list[str] | tuple[str, ...]) -> str: return character_policy.normalize_characteristic_choice(value, choices) def _normalize_characteristic_values( values: Any, choices: list[str] | tuple[str, ...] | None = None, *, allow_free_text: bool = False, ) -> list[str]: return character_policy.normalize_characteristic_values(values, choices, allow_free_text=allow_free_text) def _parse_characteristics_config(value: str | dict[str, Any] | None) -> dict[str, Any]: return character_policy.parse_characteristics_config(value) def _characteristics_summary(config: dict[str, Any]) -> str: return character_policy.characteristics_summary(config) def build_characteristics_config_json( characteristics: str | dict[str, Any] | None = "", axis: str = "ages", selected_values: list[str] | tuple[str, ...] | str | None = None, combine_mode: str = "replace_axis", ) -> str: return character_policy.build_characteristics_config_json( characteristics=characteristics, axis=axis, selected_values=selected_values, combine_mode=combine_mode, ) def _characteristic_choice(config: dict[str, Any], key: str, rng: random.Random) -> str: return character_policy.characteristic_choice(config, key, rng) def _eye_phrase_from_key(key: str) -> str: return character_policy.eye_phrase_from_key(key) def _normalize_descriptor_detail(value: Any) -> str: return character_policy.normalize_descriptor_detail(value) def _normalize_presence_mode(value: Any, subject_type: str) -> str: return character_policy.normalize_presence_mode(value, subject_type) def _slot_is_pov(slot: dict[str, Any] | None) -> bool: return pov_policy.slot_is_pov(slot) def _normalize_slot_expression_intensity(value: Any) -> float: try: intensity = float(value) except (TypeError, ValueError): return -1.0 if intensity < 0: return -1.0 return _clamped_float(intensity, 0.5) def _slot_expression_enabled(slot: dict[str, Any] | None) -> bool: if not slot: return True return not _is_false(slot.get("expression_enabled", True)) def _slot_expression_intensity(slot: dict[str, Any] | None) -> float | None: if not slot or not _slot_expression_enabled(slot): return None intensity = _normalize_slot_expression_intensity(slot.get("expression_intensity")) return intensity if intensity >= 0 else None def _slot_expression_intensity_for_phase(slot: dict[str, Any] | None, phase: str = "") -> float | None: if not slot or not _slot_expression_enabled(slot): return None phase_key = f"{phase}_expression_intensity" if phase in ("softcore", "hardcore") else "" if phase_key: intensity = _normalize_slot_expression_intensity(slot.get(phase_key)) if intensity >= 0: return intensity return _slot_expression_intensity(slot) def _normalize_slot_seed(value: Any) -> int: return character_policy.normalize_slot_seed(value) def _slot_seed(slot: dict[str, Any] | None) -> int: if not slot: return -1 return _normalize_slot_seed(slot.get("slot_seed")) def _slot_seeded_rng(slot: dict[str, Any] | None, salt: int) -> random.Random | None: seed = _slot_seed(slot) if seed < 0: return None return random.Random(_row_seed(seed, 1, salt)) def _slot_context_rng(slot: dict[str, Any], fallback_rng: random.Random) -> random.Random: return _slot_seeded_rng(slot, 701) or fallback_rng def _slot_effective_figure( slot: dict[str, Any], subject_type: str, fallback_figure: str, ) -> str: raw_figure = str(slot.get("figure") or "random").strip() if raw_figure in ("curvy", "balanced", "bombshell"): return raw_figure seeded_rng = _slot_seeded_rng(slot, 709) if subject_type == "woman" and seeded_rng is not None: return g.choose(seeded_rng, ["curvy", "balanced", "bombshell"]) return fallback_figure def _mean(values: list[float]) -> float: return sum(values) / len(values) def _cast_expression_intensity_override( fallback: float, label_map: dict[str, dict[str, Any]], women_count: int, men_count: int, expression_phase: str = "", ) -> tuple[float | None, str]: groups: list[tuple[str, list[str]]] = [ ("women", [f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))]), ("men", [f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))]), ] all_values: list[float] = [] matching_slots: list[dict[str, Any]] = [] for group_name, labels in groups: values: list[float] = [] value_labels: list[str] = [] for label in labels: slot = label_map.get(label) if _slot_is_pov(slot): continue if slot: matching_slots.append(slot) value = _slot_expression_intensity_for_phase(slot, expression_phase) if value is not None: values.append(value) value_labels.append(label) all_values.append(value) if values: if len(values) == 1: return values[0], f"character_slot:{value_labels[0]}" return _mean(values), f"character_slots:{group_name}" if all_values: return _mean(all_values), "character_slots:cast" if matching_slots and all(not _slot_expression_enabled(slot) for slot in matching_slots): return None, "character_slots:disabled" return fallback, "input" def _character_expression_entries( rng: random.Random, expression_pool: list[Any], fallback_intensity: float, label_map: dict[str, dict[str, Any]], women_count: int, men_count: int, expression_phase: str = "", ) -> list[str]: labels = [ *[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))], *[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))], ] expressions: list[str] = [] used: set[str] = set() for label in labels: slot = label_map.get(label) if not slot: continue if _slot_is_pov(slot): continue if not _slot_expression_enabled(slot): continue intensity = _slot_expression_intensity_for_phase(slot, expression_phase) if intensity is None: intensity = fallback_intensity entries = _compatible_entries( _expression_entries_for_intensity(expression_pool, intensity), women_count, men_count, ) if not entries: continue choice = "" for _attempt in range(5): candidate = _choose_text(rng, entries) if candidate not in used: choice = candidate break if not choice: choice = _choose_text(rng, entries) used.add(choice) expressions.append(f"{label} has {choice}") return expressions def _sanitize_character_expression_text_for_action( expression_text: str, role_graph: Any, item: Any, axis_values: Any = None, ) -> str: text = str(expression_text or "").strip() if not text: return "" context = " ".join( str(part or "").lower() for part in ( role_graph, item, *((axis_values or {}).values() if isinstance(axis_values, dict) else ()), ) ) woman_active_outercourse = ( re.search(r"\bwoman [a-z]\b", context) and re.search(r"\bman [a-z]\b", context) and any( term in context for term in ( "boobjob", "titjob", "breast sex", "breasts tightly", "testicle", "balls-licking", "balls licking", "penis-licking", "penis licking", "handjob", "hand job", "footjob", ) ) ) woman_gives_oral = ( re.search(r"\bwoman [a-z]\b", context) and re.search(r"\bman [a-z]\b", context) and any( term in context for term in ( "takes man", "penis in her mouth", "mouth at penis level", "fellatio", "blowjob", "deepthroat", "penis sucking", "lips wrapped", ) ) ) man_gives_oral = ( re.search(r"\bwoman [a-z]\b", context) and re.search(r"\bman [a-z]\b", context) and any( term in context for term in ( "mouth on her pussy", "mouth on woman", "mouth pressed to her pussy", "cunnilingus", "pussy licking", "tongue on pussy", ) ) ) mouth_expression_terms = ("mouth", "oral", "tongue", "lips", "gagging", "saliva") clauses = [clause.strip() for clause in text.split(";") if clause.strip()] if woman_active_outercourse: clauses = [clause for clause in clauses if not re.match(r"^Man [A-Z] has\b", clause)] if woman_gives_oral: clauses = [ clause for clause in clauses if not ( re.match(r"^Man [A-Z] has\b", clause) and any(term in clause.lower() for term in mouth_expression_terms) ) ] if man_gives_oral: clauses = [ clause for clause in clauses if not ( re.match(r"^Woman [A-Z] has\b", clause) and any(term in clause.lower() for term in mouth_expression_terms) ) ] return "; ".join(clauses) def _descriptor_detail_for_subject(subject: Any, descriptor_detail: Any) -> str: return character_profile_policy.descriptor_detail_for_subject(subject, descriptor_detail) def _descriptor_from_parts( subject: Any, age: Any, body_phrase: Any, skin: Any, hair: Any, eyes: Any, descriptor_detail: Any = "auto", ) -> str: return character_profile_policy.descriptor_from_parts( subject, age, body_phrase, skin, hair, eyes, descriptor_detail, ) def _slot_manual_or_choice(choice: str, manual_value: str) -> str: choice = str(choice or "").strip() manual_value = str(manual_value or "").strip() if choice == "manual": return manual_value or "random" if choice.lower() in CHARACTER_RANDOM_TOKENS: return "random" return choice def _normalize_slot_ethnicity(value: Any) -> str: return normalize_ethnicity_filter(value, "random", allow_random=True) def _normalize_hair_choice(value: Any, choices: list[str]) -> str: return character_policy.normalize_hair_choice(value, choices) def _infer_hair_color_key(text: Any) -> str: return character_policy.infer_hair_color_key(text) def _infer_hair_length_key(text: Any) -> str: return character_policy.infer_hair_length_key(text) def _infer_hair_style_key(text: Any) -> str: return character_policy.infer_hair_style_key(text) def _choose_hair_key(rng: random.Random, choices: list[str]) -> str: return character_policy.choose_hair_key(rng, choices) def _normalize_hair_values(values: Any, choices: list[str]) -> list[str]: return character_policy.normalize_hair_values(values, choices) def _empty_hair_config() -> dict[str, Any]: return character_policy.empty_hair_config() def _parse_hair_config(value: str | dict[str, Any] | None) -> dict[str, Any]: return character_policy.parse_hair_config(value) def _hair_config_summary(config: dict[str, Any]) -> str: return character_policy.hair_config_summary(config) def build_hair_config_json( hair_config: str | dict[str, Any] | None = "", axis: str = "color", selected_values: list[str] | tuple[str, ...] | str | None = None, combine_mode: str = "replace_axis", ) -> str: return character_policy.build_hair_config_json( hair_config=hair_config, axis=axis, selected_values=selected_values, combine_mode=combine_mode, ) def _hair_color_text(key: str) -> str: return character_policy.hair_color_text(key) def _hair_length_text(key: str) -> str: return character_policy.hair_length_text(key) def _hair_phrase_from_parts(color_key: str, length_key: str, style_key: str) -> str: return character_policy.hair_phrase_from_parts(color_key, length_key, style_key) def _hair_descriptor_from_slot(base_hair: Any, slot: dict[str, Any], rng: random.Random) -> str: hair_config = _parse_hair_config(slot.get("hair_config")) color_choice = _normalize_hair_choice(slot.get("hair_color"), CHARACTER_HAIR_COLOR_CHOICES) length_choice = _normalize_hair_choice(slot.get("hair_length"), CHARACTER_HAIR_LENGTH_CHOICES) style_choice = _normalize_hair_choice(slot.get("hair_style"), CHARACTER_HAIR_STYLE_CHOICES) color_options = hair_config.get("colors") or [] length_options = hair_config.get("lengths") or [] style_options = hair_config.get("styles") or [] if ( color_choice == "random" and length_choice == "random" and style_choice == "random" and not color_options and not length_options and not style_options ): return "" if color_choice != "random": color_key = color_choice elif color_options: color_key = g.choose(rng, color_options) else: color_key = _infer_hair_color_key(base_hair) if length_choice != "random": length_key = length_choice elif length_options: length_key = g.choose(rng, length_options) else: length_key = _infer_hair_length_key(base_hair) if style_choice != "random": style_key = style_choice elif style_options: style_key = g.choose(rng, style_options) else: style_key = _infer_hair_style_key(base_hair) if color_key == "random": color_key = _choose_hair_key(rng, CHARACTER_HAIR_COLOR_CHOICES) if length_key == "random": length_key = _choose_hair_key(rng, CHARACTER_HAIR_LENGTH_CHOICES) if style_key == "random": style_key = _choose_hair_key(rng, CHARACTER_HAIR_STYLE_CHOICES) if length_key == "updo" and style_key not in ("ponytail", "braid", "braids", "bun", "messy_bun", "locs", "twists"): style_key = g.choose(rng, ["ponytail", "braid", "bun", "messy_bun"]) return _hair_phrase_from_parts(color_key, length_key, style_key) def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]: subject_type = str(slot.get("subject_type") or slot.get("subject") or "").strip().lower() if subject_type not in ("woman", "man"): subject_type = "woman" label = str(slot.get("label") or slot.get("label_mode") or "auto_chain").strip() label = label.replace("Woman ", "").replace("Man ", "").strip().upper() if label == "AUTO_CHAIN": label = "auto_chain" if label not in CHARACTER_LABEL_CHOICES: label = "auto_chain" manual_config = _parse_character_manual_config(slot.get("manual") or slot.get("manual_config")) raw_age = str(slot.get("age") or "random") raw_manual_age = str(slot.get("manual_age") or "").strip() if not raw_manual_age and manual_config.get("manual_age"): raw_manual_age = manual_config["manual_age"] if raw_age.lower() in CHARACTER_RANDOM_TOKENS: raw_age = "manual" age = _slot_manual_or_choice(raw_age, raw_manual_age) raw_body = str(slot.get("body") or "random") raw_manual_body = str(slot.get("manual_body") or "").strip() if not raw_manual_body and manual_config.get("manual_body"): raw_manual_body = manual_config["manual_body"] if raw_body.lower() in CHARACTER_RANDOM_TOKENS: raw_body = "manual" body = _slot_manual_or_choice(raw_body, raw_manual_body) figure = str(slot.get("figure") or "random").strip() if figure not in character_figure_choices(): figure = "random" def manual_fallback(field: str) -> str: direct = _slot_value(slot.get(field)) return direct or manual_config.get(field, "") normalized = { "profile_type": "character_slot", "subject_type": subject_type, "label": label, "slot_seed": _normalize_slot_seed(slot.get("slot_seed")), "age": age, "ethnicity": _normalize_slot_ethnicity(slot.get("ethnicity")), "figure": figure, "body": body, "body_phrase": manual_fallback("body_phrase"), "skin": manual_fallback("skin"), "hair": manual_fallback("hair"), "manual": manual_config, "characteristics": ( slot.get("characteristics") if isinstance(slot.get("characteristics"), dict) else _slot_value(slot.get("characteristics") or slot.get("characteristics_config")) ), "hair_config": ( slot.get("hair_config") if isinstance(slot.get("hair_config"), dict) else _slot_value(slot.get("hair_config")) ), "hair_color": _normalize_hair_choice(slot.get("hair_color"), CHARACTER_HAIR_COLOR_CHOICES), "hair_length": _normalize_hair_choice(slot.get("hair_length"), CHARACTER_HAIR_LENGTH_CHOICES), "hair_style": _normalize_hair_choice(slot.get("hair_style"), CHARACTER_HAIR_STYLE_CHOICES), "eyes": manual_fallback("eyes"), "descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")), "presence_mode": _normalize_presence_mode(slot.get("presence_mode"), subject_type), "softcore_outfit": manual_fallback("softcore_outfit"), "hardcore_clothing": ( _slot_value(slot.get("hardcore_clothing") or slot.get("hardcore_outfit")) or manual_config.get("hardcore_clothing", "") ), "expression_enabled": not _is_false(slot.get("expression_enabled", True)), "expression_intensity": _normalize_slot_expression_intensity(slot.get("expression_intensity")), "softcore_expression_intensity": _normalize_slot_expression_intensity(slot.get("softcore_expression_intensity")), "hardcore_expression_intensity": _normalize_slot_expression_intensity(slot.get("hardcore_expression_intensity")), } normalized["summary"] = _character_slot_summary(normalized) return normalized def _parse_character_cast(character_cast: str | dict[str, Any] | list[Any] | None) -> list[dict[str, Any]]: if not character_cast: return [] if isinstance(character_cast, list): raw = character_cast elif isinstance(character_cast, dict): raw = character_cast else: try: raw = json.loads(str(character_cast)) except json.JSONDecodeError as exc: raise ValueError(f"Invalid character_cast JSON: {exc}") from exc if isinstance(raw, list): slots = raw elif isinstance(raw, dict) and isinstance(raw.get("slots"), list): slots = raw["slots"] elif isinstance(raw, dict) and raw.get("profile_type") == "character_slot": slots = [raw] elif isinstance(raw, dict) and raw.get("subject_type") in ("woman", "man"): slots = [raw] else: return [] return [_normalize_character_slot(slot) for slot in slots if isinstance(slot, dict)] def _character_slot_summary(slot: dict[str, Any]) -> str: subject = str(slot.get("subject_type") or "woman") label = str(slot.get("label") or "auto_chain") label_text = "nearest free label" if label == "auto_chain" else f"{subject.capitalize()} {label}" parts = [ subject, label_text, f"seed={slot.get('slot_seed')}" if _slot_seed(slot) >= 0 else "", f"age={slot.get('age', 'random')}", f"ethnicity={slot.get('ethnicity', 'random')}", f"figure={slot.get('figure', 'random')}", f"body={slot.get('body', 'random')}", f"detail={slot.get('descriptor_detail', 'auto')}", ] parts = [part for part in parts if part] if _slot_is_pov(slot): parts.append("presence=pov") if not _slot_expression_enabled(slot): parts.append("expression=disabled") else: expression_intensity = _slot_expression_intensity(slot) if expression_intensity is not None: parts.append(f"expression={expression_intensity:.2f}") softcore_expression_intensity = _slot_expression_intensity_for_phase(slot, "softcore") hardcore_expression_intensity = _slot_expression_intensity_for_phase(slot, "hardcore") if softcore_expression_intensity is not None and softcore_expression_intensity != expression_intensity: parts.append(f"soft_expr={softcore_expression_intensity:.2f}") if hardcore_expression_intensity is not None and hardcore_expression_intensity != expression_intensity: parts.append(f"hard_expr={hardcore_expression_intensity:.2f}") if slot.get("softcore_outfit"): parts.append(f"soft_outfit={slot['softcore_outfit']}") if slot.get("hardcore_clothing"): parts.append(f"hard_clothing={slot['hardcore_clothing']}") characteristics = _parse_characteristics_config(slot.get("characteristics")) characteristics_summary = _characteristics_summary(characteristics) if characteristics_summary != "characteristics unrestricted": parts.append(f"characteristics={characteristics_summary}") hair_config = _parse_hair_config(slot.get("hair_config")) hair_config_summary = _hair_config_summary(hair_config) if hair_config_summary != "hair unrestricted": parts.append(f"hair={hair_config_summary}") for key in ("hair_color", "hair_length", "hair_style"): value = slot.get(key) if value and value != "random": parts.append(f"{key}={value}") for key in ("body_phrase", "skin", "hair", "eyes"): value = slot.get(key) if value: parts.append(f"{key}={value}") return "; ".join(parts) def build_character_slot_json( subject_type: str = "woman", label: str = "auto_chain", slot_seed: int = -1, age: str = "random", manual_age: str = "", manual: str | dict[str, Any] | None = "", ethnicity: str = "random", figure: str = "random", body: str = "random", manual_body: str = "", body_phrase: str = "", skin: str = "", hair: str = "", characteristics: str | dict[str, Any] | None = "", hair_config: str | dict[str, Any] | None = "", hair_color: str = "random", hair_length: str = "random", hair_style: str = "random", eyes: str = "", descriptor_detail: str = "auto", expression_enabled: bool = True, expression_intensity: float = -1.0, enabled: bool = True, character_cast: str | dict[str, Any] | list[Any] | None = "", presence_mode: str = "visible", softcore_expression_intensity: float = -1.0, hardcore_expression_intensity: float = -1.0, softcore_outfit: str = "", hardcore_clothing: str = "", ) -> dict[str, str]: existing_slots = _parse_character_cast(character_cast) slot = _normalize_character_slot( { "subject_type": subject_type, "label": label, "slot_seed": slot_seed, "age": age, "manual_age": manual_age, "manual": manual, "ethnicity": ethnicity, "figure": figure, "body": body, "manual_body": manual_body, "body_phrase": body_phrase, "skin": skin, "hair": hair, "characteristics": characteristics, "hair_config": hair_config, "hair_color": hair_color, "hair_length": hair_length, "hair_style": hair_style, "eyes": eyes, "descriptor_detail": descriptor_detail, "presence_mode": presence_mode, "softcore_outfit": softcore_outfit, "hardcore_clothing": hardcore_clothing, "expression_enabled": expression_enabled, "expression_intensity": expression_intensity, "softcore_expression_intensity": softcore_expression_intensity, "hardcore_expression_intensity": hardcore_expression_intensity, } ) slots = existing_slots + ([slot] if enabled else []) cast = { "profile_type": "character_cast", "version": 1, "slots": slots, } return { "character_cast": json.dumps(cast, ensure_ascii=True, sort_keys=True), "character_slot": json.dumps(slot, ensure_ascii=True, sort_keys=True) if enabled else "", "summary": slot["summary"] if enabled else "disabled", "status": f"{len(slots)} slot(s)", } def _slot_explicit_label(slot: dict[str, Any]) -> str: label = str(slot.get("label") or "").strip().upper() if label in CHARACTER_LABEL_CHOICES and label != "AUTO_CHAIN": return label return "" def _character_slot_label_map(slots: list[dict[str, Any]]) -> dict[str, dict[str, Any]]: label_map: dict[str, dict[str, Any]] = {} letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" for subject_type, prefix in (("woman", "Woman"), ("man", "Man")): subject_slots = [slot for slot in slots if slot.get("subject_type") == subject_type] auto_slots = [slot for slot in subject_slots if not _slot_explicit_label(slot)] for index, slot in enumerate(reversed(auto_slots)): if index >= len(letters): break label_map[f"{prefix} {letters[index]}"] = slot for slot in subject_slots: explicit = _slot_explicit_label(slot) if explicit: label_map[f"{prefix} {explicit}"] = slot return label_map def _pov_character_labels( label_map: dict[str, dict[str, Any]], men_count: int | None = None, ) -> list[str]: return pov_policy.pov_character_labels(label_map, men_count) def _pov_text_with_viewer(text: Any, pov_labels: list[str]) -> str: return pov_policy.pov_text_with_viewer(text, pov_labels) def _pov_role_graph_prompt(role_graph: Any, pov_labels: list[str]) -> str: return pov_policy.pov_role_graph_prompt(role_graph, pov_labels) def _pov_prompt_directive(pov_labels: list[str]) -> str: return pov_policy.pov_prompt_directive(pov_labels) def _pov_composition_prompt(composition: Any, pov_labels: list[str]) -> str: return pov_policy.pov_composition_prompt(composition, pov_labels) def _slot_softcore_outfit(slot: dict[str, Any] | None, rng: random.Random | None = None) -> str: if not slot: return "" outfit = _slot_value(slot.get("softcore_outfit")) if outfit: return outfit if rng is None: return "" return _characteristic_choice(_parse_characteristics_config(slot.get("characteristics")), "softcore_outfits", rng) def _slot_hardcore_clothing(slot: dict[str, Any] | None, rng: random.Random | None = None) -> str: if not slot: return "" clothing = _slot_value(slot.get("hardcore_clothing")) if clothing: return clothing if rng is None: return "" return _characteristic_choice(_parse_characteristics_config(slot.get("characteristics")), "hardcore_clothing", rng) def _context_from_character_slot( rng: random.Random, slot: dict[str, Any], subject_type: str, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, ) -> dict[str, str]: slot_ethnicity = _slot_value(slot.get("ethnicity")) slot_body = _slot_value(slot.get("body")) effective_ethnicity = slot_ethnicity or ethnicity effective_figure = _slot_effective_figure(slot, subject_type, figure) effective_no_plus = bool(no_plus_women) and not slot_body effective_no_black = bool(no_black) and not slot_ethnicity appearance_rng = _slot_context_rng(slot, rng) context = _appearance_for_subject( appearance_rng, subject_type, effective_ethnicity, effective_figure, effective_no_plus, effective_no_black, ) characteristics = _parse_characteristics_config(slot.get("characteristics")) age = _slot_value(slot.get("age")) or _characteristic_choice(characteristics, "ages", appearance_rng) body_phrase = _slot_value(slot.get("body_phrase")) if not slot_body: slot_body = _characteristic_choice(characteristics, "bodies", appearance_rng) if age: context["age"] = age if slot_body: context["body"] = slot_body if subject_type == "woman": context["body_phrase"] = _body_phrase(slot_body, context.get("figure", "")) else: context["body_phrase"] = f"{slot_body} figure" if body_phrase: context["body_phrase"] = body_phrase skin_value = _slot_value(slot.get("skin")) if skin_value: context["skin"] = skin_value eyes_value = _slot_value(slot.get("eyes")) if not eyes_value: eyes_value = _eye_phrase_from_key(_characteristic_choice(characteristics, "eyes", appearance_rng)) if eyes_value: context["eyes"] = eyes_value hair_value = _slot_value(slot.get("hair")) if hair_value: context["hair"] = hair_value else: hair_descriptor = _hair_descriptor_from_slot(context.get("hair"), slot, appearance_rng) if hair_descriptor: context["hair"] = hair_descriptor context["descriptor_detail"] = _normalize_descriptor_detail(slot.get("descriptor_detail")) context["presence_mode"] = _normalize_presence_mode(slot.get("presence_mode"), subject_type) context["expression_enabled"] = _slot_expression_enabled(slot) expression_intensity = _slot_expression_intensity(slot) if expression_intensity is not None: context["expression_intensity"] = expression_intensity context["subject_type"] = subject_type context["subject"] = subject_type context["subject_phrase"] = subject_type return context def _character_context_for_label( label: str, label_map: dict[str, dict[str, Any]], rng: random.Random, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, ) -> tuple[dict[str, str], dict[str, Any] | None]: subject_type = "man" if label.startswith("Man ") else "woman" slot = label_map.get(label) if slot: return _context_from_character_slot(rng, slot, subject_type, ethnicity, figure, no_plus_women, no_black), slot return _appearance_for_subject(rng, subject_type, ethnicity, figure, no_plus_women, no_black), None def _apply_character_context_to_row(row: dict[str, Any], context: dict[str, Any]) -> dict[str, Any]: for key in ( "subject_type", "subject", "subject_phrase", "age", "body", "body_phrase", "skin", "hair", "eyes", "figure", "descriptor_detail", "presence_mode", "expression_enabled", "expression_intensity", ): value = context.get(key) if value is not None and value != "": row[key] = value if context.get("age"): row["age_band"] = context["age"] return row def _cast_descriptor_entries( seed_config: dict[str, int], seed: int, row_number: int, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, women_count: int, men_count: int, character_cast: str | dict[str, Any] | list[Any] | None = "", primary_descriptor: str = "", ) -> tuple[list[str], list[dict[str, Any]]]: slots = _parse_character_cast(character_cast) label_map = _character_slot_label_map(slots) rng = _axis_rng(seed_config, "person", seed, row_number + 997) descriptors: list[str] = [] for index in range(max(0, women_count)): label = f"Woman {chr(ord('A') + index)}" if index == 0 and primary_descriptor: descriptors.append(f"Woman A / primary creator: {primary_descriptor}") continue context, _slot = _character_context_for_label(label, label_map, rng, ethnicity, figure, no_plus_women, no_black) descriptors.append(f"{label}: {_insta_of_descriptor_from_context(context)}") for index in range(max(0, men_count)): label = f"Man {chr(ord('A') + index)}" if _slot_is_pov(label_map.get(label)): continue context, _slot = _character_context_for_label(label, label_map, rng, ethnicity, figure, no_plus_women, no_black) descriptors.append(f"{label}: {_insta_of_descriptor_from_context(context)}") return descriptors, slots def _row_from_profile_metadata(metadata_json: str | dict[str, Any] | None) -> dict[str, Any]: return character_profile_policy.row_from_profile_metadata(metadata_json) def _row_from_character_slot(character_slot: str | dict[str, Any] | None) -> dict[str, Any]: slots = _parse_character_cast(character_slot) if not slots: return {} slot = slots[-1] if _slot_seed(slot) >= 0: subject_type = str(slot.get("subject_type") or "woman") return _context_from_character_slot( random.Random(_row_seed(_slot_seed(slot), 1, 719)), slot, subject_type, "any", "curvy", False, False, ) return slot def _character_profile_descriptor(profile: dict[str, Any]) -> str: return character_profile_policy.character_profile_descriptor(profile) def _normalize_character_profile(profile: dict[str, Any], profile_name: str = "") -> dict[str, Any]: return character_profile_policy.normalize_character_profile(profile, profile_name) def build_character_profile_json( profile_name: str = "", source: str = "metadata_json", metadata_json: str | dict[str, Any] | None = "", character_slot: str | dict[str, Any] | None = "", subject_type: str = "woman", age: str = "", body: str = "", body_phrase: str = "", skin: str = "", hair: str = "", eyes: str = "", figure: str = "", save_now: bool = False, ) -> dict[str, str]: character_slot_row = _row_from_character_slot(character_slot or metadata_json) if source == "character_slot" else None return character_profile_policy.build_character_profile_json( profile_name=profile_name, source=source, metadata_json=metadata_json, character_slot_row=character_slot_row, subject_type=subject_type, age=age, body=body, body_phrase_value=body_phrase, skin=skin, hair=hair, eyes=eyes, figure=figure, save_now=save_now, ) def save_character_profile_payload(profile_name: str = "", profile_json: str | dict[str, Any] | None = "") -> dict[str, str]: return character_profile_policy.save_character_profile_payload(profile_name, profile_json) def _empty_profile_result(status: str = "empty") -> dict[str, str]: return character_profile_policy.empty_profile_result(status) def _apply_character_profile_overrides( profile: dict[str, Any], override_subject_type: str = "", override_age: str = "", override_body: str = "", override_body_phrase: str = "", override_skin: str = "", override_hair: str = "", override_eyes: str = "", override_figure: str = "", override_descriptor_detail: str = "", ) -> dict[str, Any]: return character_profile_policy.apply_character_profile_overrides( profile, override_subject_type=override_subject_type, override_age=override_age, override_body=override_body, override_body_phrase=override_body_phrase, override_skin=override_skin, override_hair=override_hair, override_eyes=override_eyes, override_figure=override_figure, override_descriptor_detail=override_descriptor_detail, ) def load_character_profile_json( profile_name: str = "", fallback_profile_json: str | dict[str, Any] | None = "", enabled: bool = True, delete_now: bool = False, rename_now: bool = False, rename_to: str = "", override_subject_type: str = "", override_age: str = "", override_body: str = "", override_body_phrase: str = "", override_skin: str = "", override_hair: str = "", override_eyes: str = "", override_figure: str = "", override_descriptor_detail: str = "", ) -> dict[str, str]: return character_profile_policy.load_character_profile_json( profile_name=profile_name, fallback_profile_json=fallback_profile_json, enabled=enabled, delete_now=delete_now, rename_now=rename_now, rename_to=rename_to, override_subject_type=override_subject_type, override_age=override_age, override_body=override_body, override_body_phrase=override_body_phrase, override_skin=override_skin, override_hair=override_hair, override_eyes=override_eyes, override_figure=override_figure, override_descriptor_detail=override_descriptor_detail, ) def _parse_character_profile(character_profile: str | dict[str, Any] | None) -> dict[str, Any]: return character_profile_policy.parse_character_profile(character_profile) def _apply_character_profile_to_context( context: dict[str, Any], character_profile: str | dict[str, Any] | None, ) -> tuple[dict[str, Any], dict[str, Any], str]: return character_profile_policy.apply_character_profile_to_context(context, character_profile) def _composition_prompt(composition: str) -> str: return row_camera_policy.composition_prompt(composition) def _appearance_for_subject( rng: random.Random, subject_type: str, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, ) -> dict[str, str]: if subject_type == "single_any": subject_type = "woman" if rng.random() < 0.82 else "man" if subject_type == "man": men_ethnicity = ethnicity if ethnicity else "any" subject, age, body, skin, hair, eyes = g.choose(rng, g.by_ethnicity(g.MEN, men_ethnicity)) return { "subject_type": "man", "subject": subject, "subject_phrase": subject, "age": age, "body": body, "skin": skin, "hair": hair, "eyes": eyes, "body_phrase": f"{body} figure", } subject, age, body, skin, hair, eyes = g.choose_woman(rng, ethnicity, no_plus_women, no_black) figure_note = g.choose(rng, g.figure_pool(figure)) return { "subject_type": "woman", "subject": subject, "subject_phrase": subject, "age": age, "body": body, "skin": skin, "hair": hair, "eyes": eyes, "body_phrase": _body_phrase(body, figure_note), "figure": figure_note, } def _count_phrase(count: int, singular: str, plural: str) -> str: words = { 0: "no", 1: "one", 2: "two", 3: "three", 4: "four", 5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine", 10: "ten", 11: "eleven", 12: "twelve", } label = singular if count == 1 else plural return f"{words.get(count, str(count))} {label}" def _configured_cast_context(women_count: int, men_count: int) -> dict[str, str]: women_count = max(0, int(women_count)) men_count = max(0, int(men_count)) if women_count + men_count == 0: women_count = 1 parts = [] if women_count: parts.append(_count_phrase(women_count, "adult woman", "adult women")) if men_count: parts.append(_count_phrase(men_count, "adult man", "adult men")) if len(parts) == 1: subject_phrase = parts[0] else: subject_phrase = f"{parts[0]} and {parts[1]}" person_count = women_count + men_count if person_count == 1: scene_kind = "solo adult sexual pose" elif person_count == 2: scene_kind = "adult couple sex scene" elif person_count == 3: scene_kind = "adult threesome sex scene" else: scene_kind = "adult group sex scene" women_label = "woman" if women_count == 1 else "women" men_label = "man" if men_count == 1 else "men" cast_summary = f"{women_count} {women_label}, {men_count} {men_label}, {person_count} total adults" return { "subject_type": "configured_cast", "subject": f"{women_count}w_{men_count}m_sex_scene", "subject_phrase": subject_phrase, "age": "21+ adults", "body": "varied", "skin": "", "hair": "", "eyes": "", "body_phrase": "varied adult bodies", "women_count": str(women_count), "men_count": str(men_count), "person_count": str(person_count), "cast_summary": cast_summary, "scene_kind": scene_kind, } def _couple_type_from_counts( rng: random.Random, women_count: int, men_count: int, ) -> tuple[str, str, str, int, int]: women_count = max(0, int(women_count)) men_count = max(0, int(men_count)) if women_count >= 2 and men_count == 0: return "two women", "two women", "close affectionate couple pose", 2, 0 if men_count >= 2 and women_count == 0: return "two men", "two men", "relaxed romantic couple pose", 0, 2 if women_count >= 1 and men_count >= 1: return "woman and man", "a woman and a man", "playful date-night pose", 1, 1 primary_subject, subject_phrase, pose = g.choose(rng, g.COUPLE_TYPES) if primary_subject == "two women": return primary_subject, subject_phrase, pose, 2, 0 if primary_subject == "two men": return primary_subject, subject_phrase, pose, 0, 2 return primary_subject, subject_phrase, pose, 1, 1 def _subject_context( rng: random.Random, subject_type: str, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, women_count: int = 1, men_count: int = 1, ) -> dict[str, str]: if subject_type in ("woman", "man", "single_any"): return _appearance_for_subject(rng, subject_type, ethnicity, figure, no_plus_women, no_black) if subject_type == "configured_cast": return _configured_cast_context(women_count, men_count) if subject_type == "couple": primary_subject, subject_phrase, pose, effective_women_count, effective_men_count = _couple_type_from_counts( rng, women_count, men_count, ) return { "subject_type": "couple", "subject": primary_subject, "subject_phrase": subject_phrase, "age": g.choose(rng, g.COUPLE_AGES), "body": g.choose(rng, ["slim and average", "curvy and broad", "stocky and curvy", "average and athletic"]), "skin": "", "hair": "", "eyes": "", "body_phrase": "", "fallback_pose": pose, "women_count": str(effective_women_count), "men_count": str(effective_men_count), "person_count": "2", } if subject_type == "group": eth = "Asian " if ethnicity == "asian" else "" return { "subject_type": "group", "subject": f"mixed {eth}adult group", "subject_phrase": f"A mixed {eth}adult group of women and men", "age": g.choose(rng, g.GROUP_AGES), "body": "diverse", "skin": "", "hair": "", "eyes": "", "body_phrase": "diverse adult body types", } return { "subject_type": subject_type, "subject": "layout scene", "subject_phrase": "Adult layout scene", "age": "adult", "body": "varied", "skin": "", "hair": "", "eyes": "", "body_phrase": "varied adult figures", } def _scene_pool( category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, location_config: dict[str, Any] | None = None, ) -> list[Any]: location_config = location_config or {} location_entries = _list_from(location_config.get("scene_entries")) if _location_config_active(location_config) and location_config.get("apply_mode") == "replace": return location_entries fallback = g.GROUP_SCENES if subject_type in ("group", "configured_cast") else g.SCENES scene_entries: list[Any] = [] scene_pools = load_scene_pool_library() item_source = item if isinstance(item, dict) else None if item_source is not None and _is_false(item_source.get("inherit_scenes")): sources = (item_source,) elif _is_false(subcategory.get("inherit_scenes")): sources = (subcategory, item_source) else: sources = (category, subcategory, item_source) for source in sources: if not isinstance(source, dict): continue if "scenes" in source: _unique_extend(scene_entries, _list_from(source["scenes"])) refs = _list_from(source.get("scene_pool")) + _list_from(source.get("scene_pools")) for ref in refs: ref_name = str(ref).strip() if ref_name not in scene_pools: raise ValueError(f"Unknown scene pool '{ref_name}'") _unique_extend(scene_entries, scene_pools[ref_name]) if _location_config_active(location_config) and location_config.get("apply_mode") == "add": _unique_extend(scene_entries, location_entries) return scene_entries or fallback def _expression_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any) -> list[Any]: return _configured_pool( category, subcategory, item, "expressions", "expression_pools", load_expression_pool_library(), "inherit_expressions", ) or g.EXPRESSIONS def _expression_intensity_hint(entry: Any) -> float: if isinstance(entry, dict): for key in ("expression_intensity", "intensity"): if key in entry: return _clamped_float(entry[key], 0.5) text = _entry_text(entry).lower() high_terms = ( "ahegao", "orgasm", "climax", "drool", "drooling", "tongue out", "eyes rolled", "fucked-out", "cum-smeared", "saliva", "gagging", "slack jaw", "jaw slack", "slack-jawed", "sex-drunk", "overwhelmed", "strained", "messy", "panting", "trembling", "shaking", "wide open mouth", "raw ", "wild ", "dazed", "spent", ) if any(term in text for term in high_terms): return 0.9 medium_terms = ( "seductive", "teasing", "lustful", "aroused", "bedroom", "dominant", "predatory", "control", "stern", "strict", "smirk", "parted lips", "open-mouthed", "heated", "hungry", "inviting", "sensual", "fetish", "commanding", "flushed", "moan", ) if any(term in text for term in medium_terms): return 0.62 low_terms = ( "neutral", "quiet", "calm", "reserved", "relaxed", "candid", "closed-mouth", "thoughtful", "controlled", "focused", "steady", "bitten-lip", "braced", "held breath", "concentrated", "aloof", "bored", "tired", "unfocused", "contented", "fashion", "soft", "sleepy", "fresh-faced", ) if any(term in text for term in low_terms): return 0.25 return 0.5 def _expression_entries_for_intensity(entries: list[Any], expression_intensity: float) -> list[Any]: target = _clamped_float(expression_intensity, 0.5) weighted: list[Any] = [] for entry in entries: entry_intensity = _expression_intensity_hint(entry) distance = abs(target - entry_intensity) if distance <= 0.18: intensity_weight = 4.0 elif distance <= 0.35: intensity_weight = 1.4 elif distance <= 0.55: intensity_weight = 0.35 else: intensity_weight = 0.05 if isinstance(entry, dict): adjusted = dict(entry) try: base_weight = float(adjusted.get("weight", 1.0)) except (TypeError, ValueError): base_weight = 1.0 adjusted["weight"] = max(0.0, base_weight) * intensity_weight weighted.append(adjusted) else: weighted.append({"text": _entry_text(entry), "weight": intensity_weight}) return weighted or entries def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]: configured = _merged_field(category, subcategory, item, "poses") if configured: return _list_from(configured) if subject_type == "couple": return [entry[2] for entry in g.COUPLE_TYPES] if subject_type in ("layout", "scene"): return ["clean designed layout"] return g.EVOCATIVE_ALL if poses == "evocative" else g.POSES def _composition_pool( category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, composition_config: dict[str, Any] | None = None, ) -> list[Any]: composition_config = composition_config or {} composition_entries = _list_from(composition_config.get("composition_entries")) if _composition_config_active(composition_config) and composition_config.get("apply_mode") == "replace": return composition_entries configured = _configured_pool( category, subcategory, item, "compositions", "composition_pools", load_composition_pool_library(), "inherit_compositions", ) if _composition_config_active(composition_config) and composition_config.get("apply_mode") == "add": configured = list(configured or []) _unique_extend(configured, composition_entries) if configured: return configured if subject_type in ("group", "configured_cast"): return g.GROUP_COMPOSITIONS if subject_type in ("layout", "scene"): return ["designed illustration layout"] return g.COMPOSITIONS def _build_custom_row( category_choice: str, subcategory_choice: str, row_number: int, start_index: int, ethnicity: str, poses: str, figure: str, no_plus_women: bool, no_black: bool, women_count: int, men_count: int, seed: int, seed_config: dict[str, int], expression_enabled: bool, expression_intensity: float, expression_intensity_source: str = "input", character_profile: str | dict[str, Any] | None = None, character_cast: str | dict[str, Any] | list[Any] | None = None, expression_phase: str = "", hardcore_position_config: str | dict[str, Any] | None = None, location_config: str | dict[str, Any] | None = None, composition_config: str | dict[str, Any] | None = None, ) -> dict[str, Any]: categories = load_category_library() category_rng = _axis_rng(seed_config, "category", seed, row_number) subcategory_rng = _axis_rng(seed_config, "subcategory", seed, row_number) person_rng = _axis_rng(seed_config, "person", seed, row_number) scene_rng = _axis_rng(seed_config, "scene", seed, row_number) pose_rng = _axis_rng(seed_config, "pose", seed, row_number) role_rng = _axis_rng(seed_config, "role", seed, row_number) expression_rng = _axis_rng(seed_config, "expression", seed, row_number) composition_rng = _axis_rng(seed_config, "composition", seed, row_number) parsed_hardcore_position_config = _parse_hardcore_position_config(hardcore_position_config) parsed_location_config = _parse_location_config(location_config) parsed_composition_config = _parse_composition_config(composition_config) requested_women_count = women_count requested_men_count = men_count categories = _filter_hardcore_categories_for_position( categories, parsed_hardcore_position_config, women_count, men_count, ) category, subcategory, women_count, men_count = _find_subcategory( categories, category_choice, subcategory_choice, category_rng, subcategory_rng, women_count, men_count, ) count_adjustment = {} if women_count != requested_women_count or men_count != requested_men_count: count_adjustment = { "requested_women_count": requested_women_count, "requested_men_count": requested_men_count, "effective_women_count": women_count, "effective_men_count": men_count, } if _is_hardcore_sexual_category(category): subcategory = _apply_hardcore_position_config_to_subcategory(subcategory, parsed_hardcore_position_config) content_axis = "pose" if _is_pose_content_category(category, subcategory) else "content" content_rng = _axis_rng(seed_config, content_axis, seed, row_number) items = _list_from(subcategory.get("items", [subcategory["name"]])) item = _weighted_choice(content_rng, items) item_text, item_name, item_axis_values, item_template_metadata = _compose_item( content_rng, category, subcategory, item, women_count, men_count, ) is_pose_category = _is_pose_content_category(category, subcategory) if is_pose_category: item_text = _sanitize_hardcore_environment_anchors(item_text) item_axis_values = _sanitize_hardcore_axis_values(item_axis_values) item_formatter_hints = _template_formatter_hints(item_template_metadata) subject_type = str(_merged_field(category, subcategory, item, "subject_type", "single_any")) context = _subject_context(person_rng, subject_type, ethnicity, figure, no_plus_women, no_black, women_count, men_count) character_slots = _parse_character_cast(character_cast) character_slot_map = _character_slot_label_map(character_slots) applied_slot: dict[str, Any] = {} slot_status = "none" if context.get("subject_type") in ("woman", "man"): slot_label = "Woman A" if context["subject_type"] == "woman" else "Man A" if slot_label in character_slot_map: context, applied_slot = _character_context_for_label( slot_label, character_slot_map, person_rng, ethnicity, figure, no_plus_women, no_black, ) slot_status = f"applied:{slot_label}" applied_profile, profile_status = {}, "skipped_character_slot" else: context, applied_profile, profile_status = _apply_character_profile_to_context(context, character_profile) else: context, applied_profile, profile_status = _apply_character_profile_to_context(context, character_profile) subject_type = context["subject_type"] pov_character_labels = ( _pov_character_labels(character_slot_map, men_count) if subject_type == "configured_cast" else [] ) source_role_graph = build_hardcore_role_graph(role_rng, subcategory, context, item_axis_values, pov_character_labels) if is_pose_category: source_role_graph = _sanitize_hardcore_environment_anchors(source_role_graph) role_graph = _pov_role_graph_prompt(source_role_graph, pov_character_labels) cast_descriptors: list[str] = [] cast_descriptor_text = "" expression_intensity_source = expression_intensity_source or "input" expression_disabled = not bool(expression_enabled) if expression_disabled: expression_intensity_source = "disabled" elif subject_type in ("woman", "man") and applied_slot: slot_label = "Woman A" if subject_type == "woman" else "Man A" if not _slot_expression_enabled(applied_slot): expression_disabled = True expression_intensity_source = f"character_slot:{slot_label}:disabled" else: slot_expression_intensity = _slot_expression_intensity_for_phase(applied_slot, expression_phase) if slot_expression_intensity is not None: expression_intensity = slot_expression_intensity expression_intensity_source = f"character_slot:{slot_label}" elif subject_type == "configured_cast" and character_slots: expression_intensity, expression_intensity_source = _cast_expression_intensity_override( expression_intensity, character_slot_map, women_count, men_count, expression_phase, ) if expression_intensity is None: expression_disabled = True if subject_type == "configured_cast" and character_slots: cast_descriptors, _descriptor_slots = _cast_descriptor_entries( seed_config, seed, row_number, ethnicity, figure, no_plus_women, no_black, women_count, men_count, character_slots, ) cast_descriptor_text = _insta_of_prompt_cast_descriptors("; ".join(cast_descriptors)) scene_slug, scene = _choose_pair( scene_rng, _compatible_entries( _scene_pool(category, subcategory, item, subject_type, parsed_location_config), women_count, men_count, ), ) pose = str(_merged_field(category, subcategory, item, "pose", "") or context.get("fallback_pose") or _choose_text( pose_rng, _compatible_entries(_pose_pool(category, subcategory, item, subject_type, poses), women_count, men_count) )) if is_pose_category: pose = _sanitize_hardcore_environment_anchors(pose) expression_pool = _expression_pool(category, subcategory, item) if expression_disabled: expression = "" else: expression_entries = _compatible_entries( _expression_entries_for_intensity(expression_pool, expression_intensity), women_count, men_count, ) expression = _choose_text(expression_rng, expression_entries) if subject_type in ("couple", "group") and ";" not in expression: secondary_expression = _choose_distinct_text(expression_rng, expression_entries, expression) if secondary_expression: expression = f"{expression}; {secondary_expression}" shared_expression = expression character_expressions: list[str] = [] character_expression_text = "" if not expression_disabled and subject_type == "configured_cast" and character_slots: character_expressions = _character_expression_entries( expression_rng, expression_pool, expression_intensity, character_slot_map, women_count, men_count, expression_phase, ) character_expression_text = "; ".join(character_expressions) character_expression_text = _sanitize_character_expression_text_for_action( character_expression_text, source_role_graph, item, item_axis_values, ) character_expressions = [part.strip() for part in character_expression_text.split(";") if part.strip()] if character_expression_text: expression = character_expression_text source_composition = _choose_text( composition_rng, _compatible_entries( _composition_pool(category, subcategory, item, subject_type, parsed_composition_config), women_count, men_count, ), ) if is_pose_category: source_composition = _sanitize_hardcore_environment_anchors(source_composition) composition = _pov_composition_prompt(source_composition, pov_character_labels) position_family = "" position_keys: list[str] = [] position_key = "" action_family = "" if is_pose_category: template_position_family = _template_position_family(item_template_metadata) position_family = template_position_family or _hardcore_source_position_family( subcategory, parsed_hardcore_position_config, ) inferred_position_keys = _hardcore_position_keys( item_text, source_role_graph, source_composition, pose, axis_values=item_axis_values, ) position_keys = _merge_position_keys(_template_position_keys(item_template_metadata), inferred_position_keys) position_key = position_keys[0] if position_keys else "" action_family = _template_action_family(item_template_metadata) if not action_family: action_family = source_hardcore_action_family( position_family, source_role_graph, item_text, source_composition, item_axis_values, ) negative_prompt = str(_merged_field(category, subcategory, item, "negative_prompt", g.NEGATIVE_PROMPT)) positive_suffix = str(_merged_field(category, subcategory, item, "positive_suffix", GENERIC_POSITIVE_SUFFIX)) style = str( _merged_field( category, subcategory, item, "style", "sexy but tasteful adult pin-up coloured-pencil comic illustration", ) ) item_label = str(_merged_field(category, subcategory, item, "item_label", category["name"])) context.update( { "trigger": g.TRIGGER, "main_category": category["name"], "subcategory": subcategory["name"], "category": category["name"], "item": item_text, "item_name": item_name, "item_label": item_label, "style": style, "scene": scene, "scene_slug": scene_slug, "pose": pose, "expression": expression, "shared_expression": shared_expression, "character_expressions": character_expressions, "character_expression_text": character_expression_text, "expression_enabled": not expression_disabled, "expression_disabled": expression_disabled, "expression_intensity": expression_intensity, "expression_intensity_source": expression_intensity_source, "composition": composition, "source_composition": source_composition, "composition_prompt": _composition_prompt(composition), "composition_config": parsed_composition_config if _composition_config_active(parsed_composition_config) else {}, "role_graph": role_graph, "source_role_graph": source_role_graph, "action_family": action_family, "position_family": position_family, "position_key": position_key, "position_keys": position_keys, "pov_character_labels": pov_character_labels, "pov_prompt_directive": _pov_prompt_directive(pov_character_labels), "cast_descriptors": cast_descriptor_text, "positive_suffix": positive_suffix, "negative_prompt": negative_prompt, } ) if isinstance(item, dict) and "prompt_template" in item: template = str(item["prompt_template"]) else: template = str(subcategory.get("prompt_template") or category.get("prompt_template") or "") if not template: if subject_type in ("woman", "man"): template = SINGLE_TEMPLATE elif subject_type == "couple": template = COUPLE_TEMPLATE elif subject_type == "group": template = GROUP_TEMPLATE else: template = LAYOUT_TEMPLATE caption_template = str( (item.get("caption_template") if isinstance(item, dict) else None) or subcategory.get("caption_template") or category.get("caption_template") or "{trigger}, {subject_phrase}, {age}, {item}, {scene}, {composition}, coloured pencil comic illustration" ) prompt = _format(template, context) if subject_type == "configured_cast" and cast_descriptor_text and "{cast_descriptors}" not in template: prompt = _insert_positive_directive(prompt, f"Characters: {cast_descriptor_text}.") if subject_type == "configured_cast" and pov_character_labels: prompt = _insert_positive_directive(prompt, _pov_prompt_directive(pov_character_labels)) caption = _format(caption_template, context) if subject_type == "configured_cast" and cast_descriptor_text and "{cast_descriptors}" not in caption_template: caption = f"{caption.rstrip()}, {cast_descriptor_text}" batch = max(1, ((row_number - 1) // g.BATCH_SIZE) + 1) index = start_index + row_number - 1 row = g.row_base(index, batch, context["subject"], context["age"], context["body"], scene_slug, composition) row.update( { "prompt": prompt, "caption": caption, "negative_prompt": negative_prompt, "expression": expression, "main_category": category["name"], "subcategory": subcategory["name"], "category_slug": category["slug"], "subcategory_slug": subcategory["slug"], "subject_type": subject_type, "subject_phrase": context.get("subject_phrase", ""), "body_phrase": context.get("body_phrase", ""), "skin": context.get("skin", ""), "hair": context.get("hair", ""), "eyes": context.get("eyes", ""), "style": style, "item": item_text, "item_label": item_label, "positive_suffix": positive_suffix, "custom_item": item_name, "item_axis_values": item_axis_values, "item_template_metadata": item_template_metadata, "formatter_hints": item_formatter_hints, "scene_text": scene, "location_config": parsed_location_config if _location_config_active(parsed_location_config) else {}, "pose": pose, "seed_config": seed_config, "hardcore_position_config": ( parsed_hardcore_position_config if _hardcore_position_config_active(parsed_hardcore_position_config) else {} ), "content_seed_axis": content_axis, "role_graph": role_graph, "source_role_graph": source_role_graph, "action_family": action_family, "position_family": position_family, "position_key": position_key, "position_keys": position_keys, "source_composition": source_composition, "pov_character_labels": pov_character_labels, "pov_prompt_directive": _pov_prompt_directive(pov_character_labels), "shared_expression": shared_expression, "character_expressions": character_expressions, "character_expression_text": character_expression_text, "expression_enabled": not expression_disabled, "expression_disabled": expression_disabled, "cast_summary": context.get("cast_summary", ""), "cast_descriptors": cast_descriptors, "cast_descriptor_text": cast_descriptor_text, "scene_kind": context.get("scene_kind", ""), "women_count": context.get("women_count", ""), "men_count": context.get("men_count", ""), "person_count": context.get("person_count", ""), "cast_count_adjustment": count_adjustment if subject_type == "configured_cast" else {}, "character_profile": applied_profile, "character_profile_status": profile_status, "character_slot": applied_slot, "character_slot_status": slot_status, "character_cast_slots": character_slots, "expression_intensity": expression_intensity, "expression_intensity_source": expression_intensity_source, "source": "json_category", } ) if context.get("figure"): row["figure"] = context["figure"] if expression_disabled: row = _disable_row_expression(row, expression_intensity_source) return row def build_prompt( category: str, subcategory: str, row_number: int, start_index: int, seed: int, clothing: str, ethnicity: str, poses: str, backside_bias: float, figure: str, no_plus_women: bool, no_black: bool, minimal_clothing_ratio: float, standard_pose_ratio: float, trigger: str, prepend_trigger_to_prompt: bool, extra_positive: str, extra_negative: str, seed_config: str | dict[str, Any] | None = None, women_count: int = 1, men_count: int = 1, camera_config: str | dict[str, Any] | None = None, expression_intensity: float = 0.5, character_profile: str | dict[str, Any] | None = None, character_cast: str | dict[str, Any] | list[Any] | None = None, expression_enabled: bool = True, expression_phase: str = "", hardcore_position_config: str | dict[str, Any] | None = None, location_config: str | dict[str, Any] | None = None, composition_config: str | dict[str, Any] | None = None, ) -> dict[str, Any]: apply_pool_extensions() row_number = max(1, int(row_number)) start_index = max(1, int(start_index)) seed = int(seed) ethnicity = normalize_ethnicity_filter(ethnicity, "any") expression_enabled = not _is_false(expression_enabled) minimal_ratio = _ratio_or_none(minimal_clothing_ratio) pose_ratio = _ratio_or_none(standard_pose_ratio) parsed_seed_config = _parse_seed_config(seed_config) parsed_location_config = _parse_location_config(location_config) parsed_composition_config = _parse_composition_config(composition_config) content_rng = _axis_rng(parsed_seed_config, "content", seed, row_number) pose_axis_rng = _axis_rng(parsed_seed_config, "pose", seed, row_number) person_rng = _axis_rng(parsed_seed_config, "person", seed, row_number) expression_rng = _axis_rng(parsed_seed_config, "expression", seed, row_number) clothing = clothing if clothing in ("full", "minimal", "random") else "full" poses = poses if poses in ("standard", "evocative", "random") else "standard" figure = figure if figure in ("curvy", "balanced", "bombshell", "random") else "curvy" clothing = _pick_clothing_mode(content_rng, clothing, minimal_ratio) poses = _pick_pose_mode(pose_axis_rng, poses, pose_ratio) figure = _pick_figure_bias(person_rng, figure) minimal_ratio = None pose_ratio = None expression_intensity, expression_intensity_source = _pick_expression_intensity(expression_rng, expression_intensity) exact_custom_subcategory = bool(subcategory and subcategory != RANDOM_SUBCATEGORY and " / " in subcategory) if category == "auto_full" and not exact_custom_subcategory: category = _auto_full_choice(parsed_seed_config, seed, row_number) if category == "auto_weighted" and not exact_custom_subcategory: row = _build_auto_weighted_row( row_number, start_index, clothing, ethnicity, poses, float(backside_bias), figure, bool(no_plus_women), bool(no_black), minimal_ratio, pose_ratio, seed, ) elif category in ("woman", "man", "couple", "group_or_layout") and not exact_custom_subcategory: row = _build_direct_builtin_row( category, row_number, start_index, clothing, ethnicity, poses, float(backside_bias), figure, bool(no_plus_women), bool(no_black), minimal_ratio, pose_ratio, seed, ) else: row = _build_custom_row( category, subcategory, row_number, start_index, ethnicity, poses, figure, bool(no_plus_women), bool(no_black), int(women_count), int(men_count), seed, parsed_seed_config, expression_enabled, expression_intensity, expression_intensity_source, character_profile, character_cast, expression_phase, hardcore_position_config, parsed_location_config, parsed_composition_config, ) if row.get("source") == "built_in_generator": row = row_location_policy.apply_location_config_to_legacy_row( row, parsed_location_config, parsed_seed_config, seed, row_number, ) row = row_location_policy.apply_composition_config_to_legacy_row( row, parsed_composition_config, parsed_seed_config, seed, row_number, ) if not expression_enabled: row = _disable_row_expression(row, "disabled") row = _apply_camera_config(row, camera_config) active_trigger = trigger.strip() or g.TRIGGER row = row_policy.normalize_prompt_row( row, active_trigger=active_trigger, prepend_trigger_to_prompt=bool(prepend_trigger_to_prompt), extra_positive=extra_positive, extra_negative=extra_negative, default_negative=g.NEGATIVE_PROMPT, ) row.setdefault("expression_intensity", expression_intensity) row.setdefault("expression_intensity_source", expression_intensity_source) return row def build_prompt_from_configs( row_number: int, start_index: int, seed: int, category_config: str | dict[str, Any] | None = "", cast_config: str | dict[str, Any] | None = "", generation_profile: str | dict[str, Any] | None = "", filter_config: str | dict[str, Any] | None = "", seed_config: str | dict[str, Any] | None = "", camera_config: str | dict[str, Any] | 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 = "", extra_positive: str = "", extra_negative: str = "", ) -> dict[str, Any]: category, subcategory = _parse_category_config(category_config) cast = _parse_cast_config(cast_config) profile = _parse_generation_profile(generation_profile) filters = _parse_filter_config(filter_config) return build_prompt( category=category, subcategory=subcategory, row_number=row_number, start_index=start_index, seed=seed, clothing=profile["clothing"], ethnicity=filters["ethnicity"], poses=profile["poses"], expression_enabled=profile["expression_enabled"], expression_intensity=profile["expression_intensity"], backside_bias=profile["backside_bias"], figure=filters["figure"], no_plus_women=filters["no_plus_women"], no_black=filters["no_black"], women_count=int(cast["women_count"]), men_count=int(cast["men_count"]), minimal_clothing_ratio=profile["minimal_clothing_ratio"], standard_pose_ratio=profile["standard_pose_ratio"], trigger=profile["trigger"], prepend_trigger_to_prompt=profile["prepend_trigger_to_prompt"], extra_positive=extra_positive or "", extra_negative=extra_negative or "", seed_config=seed_config or "", camera_config=camera_config or "", character_profile=character_profile or "", character_cast=character_cast or "", hardcore_position_config=hardcore_position_config or "", location_config=location_config or "", composition_config=composition_config or "", ) INSTA_OF_SOFT_LEVELS = pair_options.INSTA_OF_SOFT_LEVELS INSTA_OF_HARDCORE_LEVELS = pair_options.INSTA_OF_HARDCORE_LEVELS INSTA_OF_PLATFORM_STYLES = pair_options.INSTA_OF_PLATFORM_STYLES INSTA_OF_HARDCORE_CLOTHING_CONTINUITY = pair_options.INSTA_OF_HARDCORE_CLOTHING_CONTINUITY INSTA_OF_NEGATIVE = pair_options.INSTA_OF_NEGATIVE INSTA_OF_SOFT_NEGATIVE = pair_options.INSTA_OF_SOFT_NEGATIVE INSTA_OF_SOFTCORE_SUBCATEGORY_BY_LEVEL = pair_options.INSTA_OF_SOFTCORE_SUBCATEGORY_BY_LEVEL INSTA_OF_SOFTCORE_OUTFITS = pair_options.INSTA_OF_SOFTCORE_OUTFITS INSTA_OF_SOFTCORE_POSES = pair_options.INSTA_OF_SOFTCORE_POSES INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS = pair_options.INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS = pair_options.INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS def character_softcore_outfit_values(source: str, custom_outfits: str = "") -> list[str]: return pair_options.character_softcore_outfit_values(source, custom_outfits) def character_hardcore_clothing_values(state: str, custom_clothing: str = "") -> list[str]: return pair_options.character_hardcore_clothing_values(state, custom_clothing) def build_insta_of_options_json( softcore_cast: str = "solo", hardcore_cast: str = "use_counts", hardcore_women_count: int = 1, hardcore_men_count: int = 1, softcore_level: str = "lingerie_tease", hardcore_level: str = "hardcore", platform_style: str = "hybrid", continuity: str = "same_creator_same_room", hardcore_clothing_continuity: str = "partially_removed", softcore_camera_mode: str = "handheld_selfie", hardcore_camera_mode: str = "from_camera_config", camera_detail: str = "from_camera_config", softcore_expression_intensity: float = 0.45, hardcore_expression_intensity: float = 0.85, softcore_expression_enabled: bool = True, hardcore_expression_enabled: bool = True, hardcore_detail_density: str = "balanced", ) -> str: return pair_options.build_insta_of_options_json( softcore_cast=softcore_cast, hardcore_cast=hardcore_cast, hardcore_women_count=hardcore_women_count, hardcore_men_count=hardcore_men_count, softcore_level=softcore_level, hardcore_level=hardcore_level, platform_style=platform_style, continuity=continuity, hardcore_clothing_continuity=hardcore_clothing_continuity, softcore_camera_mode=softcore_camera_mode, hardcore_camera_mode=hardcore_camera_mode, camera_detail=camera_detail, softcore_expression_intensity=softcore_expression_intensity, hardcore_expression_intensity=hardcore_expression_intensity, softcore_expression_enabled=softcore_expression_enabled, hardcore_expression_enabled=hardcore_expression_enabled, hardcore_detail_density=hardcore_detail_density, hardcore_detail_density_choices=HARDCORE_DETAIL_DENSITY_CHOICES, ) def _parse_insta_of_options(options_json: str | dict[str, Any] | None) -> dict[str, Any]: return pair_options.parse_insta_of_options( options_json, camera_mode_choices=CAMERA_MODE_PROMPTS, camera_detail_choices=CAMERA_DETAIL_CHOICES, hardcore_detail_density_choices=HARDCORE_DETAIL_DENSITY_CHOICES, ) def _insta_of_hardcore_counts(options: dict[str, Any]) -> tuple[int, int]: return pair_options.hardcore_counts(options) def _insta_of_descriptor(row: dict[str, Any]) -> str: return _descriptor_from_parts( "woman", row.get("age_band") or row.get("age"), row.get("body_phrase"), row.get("skin"), row.get("hair"), row.get("eyes"), row.get("descriptor_detail"), ) def _insta_of_descriptor_from_context(context: dict[str, Any]) -> str: subject = str(context.get("subject") or context.get("subject_type") or "person").strip() return _descriptor_from_parts( subject, context.get("age"), context.get("body_phrase"), context.get("skin"), context.get("hair"), context.get("eyes"), context.get("descriptor_detail"), ) def _insta_of_cast_descriptors( primary_descriptor: str, seed_config: dict[str, int], seed: int, row_number: int, ethnicity: str, figure: str, no_plus_women: bool, no_black: bool, women_count: int, men_count: int, character_cast: str | dict[str, Any] | list[Any] | None = "", ) -> list[str]: descriptors, _slots = _cast_descriptor_entries( seed_config, seed, row_number, ethnicity, figure, no_plus_women, no_black, women_count, men_count, character_cast, primary_descriptor=primary_descriptor, ) return descriptors def _insta_of_cast_phrase(women_count: int, men_count: int) -> str: context = _configured_cast_context(women_count, men_count) return context["cast_summary"] def _insta_of_prompt_cast_descriptors(text: str) -> str: return str(text or "").replace("Woman A / primary creator:", "Woman A:") SOFTCORE_CAST_POSES = pair_options.SOFTCORE_CAST_POSES def _insta_of_softcore_category(level: str) -> tuple[str, str]: return pair_options.softcore_category(level) def _insta_of_softcore_outfit(rng: random.Random, level: str) -> str: return g.choose(rng, pair_options.softcore_outfit_pool(level)) def _insta_of_softcore_item_prompt_label(level: str) -> str: return pair_options.softcore_item_prompt_label(level) def _insta_of_softcore_pose(rng: random.Random, level: str) -> str: return g.choose(rng, pair_options.softcore_pose_pool(level)) def _insta_of_partner_styling( seed_config: dict[str, int], seed: int, row_number: int, women_count: int, men_count: int, pov_labels: list[str] | None = None, label_map: dict[str, dict[str, Any]] | None = None, ) -> 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 g.choose(content_rng, 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 g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS) sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit) if sentence: outfits.append(sentence) return { "outfits": outfits, "pose": g.choose(pose_rng, SOFTCORE_CAST_POSES), } def build_insta_of_pair( 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, 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 = "", extra_positive: str = "", extra_negative: str = "", ) -> dict[str, Any]: options = _parse_insta_of_options(options_json) if filter_config: filters = _parse_filter_config(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 = _insta_of_hardcore_counts(options) active_trigger = trigger.strip() or g.TRIGGER parsed_seed_config = _parse_seed_config(seed_config) character_slots = _parse_character_cast(character_cast) character_slot_map = _character_slot_label_map(character_slots) pov_character_labels = _pov_character_labels(character_slot_map, hard_men_count) softcore_level_key = str(options["softcore_level"]) soft_category, soft_subcategory = _insta_of_softcore_category(softcore_level_key) row_route = pair_rows.build_insta_pair_rows( 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 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=RANDOM_SUBCATEGORY, hardcore_position_config=hardcore_position_config, location_config=location_config or "", composition_config=composition_config or "", 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=_insta_of_softcore_outfit, softcore_pose=_insta_of_softcore_pose, softcore_item_prompt_label=_insta_of_softcore_item_prompt_label, pov_prompt_directive=_pov_prompt_directive, pov_composition_prompt=_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, seed=seed, row_number=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=INSTA_OF_PLATFORM_STYLES, soft_levels=INSTA_OF_SOFT_LEVELS, hardcore_levels=INSTA_OF_HARDCORE_LEVELS, descriptor_from_row=_insta_of_descriptor, build_cast_descriptors=_insta_of_cast_descriptors, prompt_cast_descriptors=_insta_of_prompt_cast_descriptors, partner_styling=_insta_of_partner_styling, cast_phrase=_insta_of_cast_phrase, ) descriptor = cast_context["descriptor"] cast_descriptors = cast_context["cast_descriptors"] cast_descriptor_text = cast_context["cast_descriptor_text"] soft_partner_styling = cast_context["soft_partner_styling"] soft_partner_outfit_text = cast_context["soft_partner_outfit_text"] platform_style = cast_context["platform_style"] soft_level = cast_context["soft_level"] hard_level = cast_context["hard_level"] camera_route = pair_camera.resolve_insta_pair_camera( soft_row=soft_row, hard_row=hard_row, options=options, camera_config=camera_config, softcore_camera_config=softcore_camera_config, hardcore_camera_config=hardcore_camera_config, hard_women_count=hard_women_count, hard_men_count=hard_men_count, pov_character_labels=pov_character_labels, camera_detail_choices=CAMERA_DETAIL_CHOICES, camera_config_with_mode=_camera_config_with_mode, camera_directive=_camera_directive, apply_contextual_composition=_apply_coworking_composition, contextual_composition_prompt=_coworking_composition_prompt, composition_prompt=_composition_prompt, camera_scene_directive_for_context=_camera_scene_directive_for_context, ) soft_row = camera_route["soft_row"] hard_row = camera_route["hard_row"] hard_scene = camera_route["hard_scene"] hard_composition = camera_route["hard_composition"] soft_camera_config = camera_route["soft_camera_config"] hard_camera_config = camera_route["hard_camera_config"] soft_camera_directive = camera_route["soft_camera_directive"] hard_camera_directive = camera_route["hard_camera_directive"] soft_camera_scene_directive = camera_route["soft_camera_scene_directive"] hard_camera_scene_directive = camera_route["hard_camera_scene_directive"] soft_camera_scene_sentence = camera_route["soft_camera_scene_sentence"] hard_camera_scene_sentence = camera_route["hard_camera_scene_sentence"] soft_camera_sentence = camera_route["soft_camera_sentence"] hard_camera_sentence = camera_route["hard_camera_sentence"] soft_cast = cast_context["soft_cast"] soft_cast_presence = cast_context["soft_cast_presence"] soft_cast_styling_sentence = cast_context["soft_cast_styling_sentence"] hard_cast = cast_context["hard_cast"] 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, _slot_hardcore_clothing, ) clothing_route = pair_clothing.resolve_hardcore_pair_clothing( 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=INSTA_OF_HARDCORE_CLOTHING_CONTINUITY, choose=g.choose, ) default_man_hardcore_clothing_entries = clothing_route["default_man_hardcore_clothing"] hard_clothing_state = clothing_route["hardcore_clothing_state"] hard_clothing_sentence = clothing_route["hardcore_clothing_sentence"] 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"] hard_detail_directive = pair_options.hardcore_detail_directive(hard_detail_density) pov_directive = _pov_prompt_directive(pov_character_labels) soft_descriptor_sentence = cast_context["soft_descriptor_sentence"] return pair_output.assemble_insta_pair_metadata( active_trigger=active_trigger, prepend_trigger_to_prompt=bool(prepend_trigger_to_prompt), extra_positive=extra_positive, extra_negative=extra_negative, soft_negative_base=INSTA_OF_SOFT_NEGATIVE, hard_negative_base=INSTA_OF_NEGATIVE, options=options, platform_style=platform_style, soft_descriptor_sentence=soft_descriptor_sentence, soft_level=soft_level, soft_cast=soft_cast, soft_cast_presence=soft_cast_presence, soft_cast_styling_sentence=soft_cast_styling_sentence, soft_row=soft_row, soft_camera_scene_sentence=soft_camera_scene_sentence, soft_camera_sentence=soft_camera_sentence, hard_level=hard_level, hard_cast=hard_cast, cast_descriptor_text=cast_descriptor_text, pov_directive=pov_directive, pov_character_labels=pov_character_labels, hard_clothing_sentence=hard_clothing_sentence, hard_row=hard_row, hard_scene=hard_scene, hard_camera_scene_sentence=hard_camera_scene_sentence, hard_composition=hard_composition, hard_detail_directive=hard_detail_directive, hard_camera_sentence=hard_camera_sentence, descriptor=descriptor, soft_partner_outfit_text=soft_partner_outfit_text, soft_partner_styling=soft_partner_styling, soft_camera_scene_directive=soft_camera_scene_directive, soft_camera_config=soft_camera_config, soft_camera_directive=soft_camera_directive, hard_camera_scene_directive=hard_camera_scene_directive, hard_camera_config=hard_camera_config, hard_camera_directive=hard_camera_directive, camera_caption_text=_camera_caption_text, cast_descriptors=cast_descriptors, character_hardcore_clothing_entries=character_hardcore_clothing_entries, default_man_hardcore_clothing_entries=default_man_hardcore_clothing_entries, hard_clothing_state=hard_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, )