3083 lines
109 KiB
Python
3083 lines
109 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import random
|
|
import re
|
|
from pathlib import Path
|
|
from string import Formatter
|
|
from typing import Any
|
|
|
|
try:
|
|
from .category_library import (
|
|
category_json_files as _json_files,
|
|
compatible_entries as _compatible_entries,
|
|
compatible_entry as _compatible_entry,
|
|
find_subcategory as _find_subcategory,
|
|
load_category_library,
|
|
merged_field as _merged_field,
|
|
read_category_json as _read_json,
|
|
)
|
|
from . import camera_config as camera_policy
|
|
from . import cast_context as cast_context_policy
|
|
from . import category_template_metadata as item_template_policy
|
|
from . import character_appearance as character_appearance_policy
|
|
from . import character_config as character_policy
|
|
from . import character_profile as character_profile_policy
|
|
from . import character_slot as character_slot_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_expression as row_expression_policy
|
|
from . import row_generation as row_generation_policy
|
|
from . import row_item as row_item_policy
|
|
from . import row_location as row_location_policy
|
|
from . import row_pools as row_pool_policy
|
|
from . import seed_config as seed_policy
|
|
from . import subject_context as subject_context_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,
|
|
find_subcategory as _find_subcategory,
|
|
load_category_library,
|
|
merged_field as _merged_field,
|
|
read_category_json as _read_json,
|
|
)
|
|
import camera_config as camera_policy
|
|
import cast_context as cast_context_policy
|
|
import category_template_metadata as item_template_policy
|
|
import character_appearance as character_appearance_policy
|
|
import character_config as character_policy
|
|
import character_profile as character_profile_policy
|
|
import character_slot as character_slot_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_expression as row_expression_policy
|
|
import row_generation as row_generation_policy
|
|
import row_item as row_item_policy
|
|
import row_location as row_location_policy
|
|
import row_pools as row_pool_policy
|
|
import seed_config as seed_policy
|
|
import subject_context as subject_context_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 row_item_policy.slug(value)
|
|
|
|
|
|
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]:
|
|
return row_item_policy.pair_from(value)
|
|
|
|
|
|
def _weighted_choice(rng: random.Random, items: list[Any]) -> Any:
|
|
return row_item_policy.weighted_choice(rng, items)
|
|
|
|
|
|
def _entry_text(item: Any) -> str:
|
|
return row_item_policy.entry_text(item)
|
|
|
|
|
|
def _item_text(item: Any) -> str:
|
|
return row_item_policy.item_text(item)
|
|
|
|
|
|
def _item_name(item: Any) -> str:
|
|
return row_item_policy.item_name(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]:
|
|
return row_item_policy.oral_acts_for_position(values, position)
|
|
|
|
|
|
def _oral_axis_values_for_context(values: list[Any], position: str, oral_act: str, axis_name: str) -> list[Any]:
|
|
return row_item_policy.oral_axis_values_for_context(values, position, oral_act, axis_name)
|
|
|
|
|
|
def _outercourse_acts_for_position(values: list[Any], position: str) -> list[Any]:
|
|
return row_item_policy.outercourse_acts_for_position(values, position)
|
|
|
|
|
|
def _outercourse_axis_values_for_position(values: list[Any], position: str, axis_name: str) -> list[Any]:
|
|
return row_item_policy.outercourse_axis_values_for_position(values, position, axis_name)
|
|
|
|
|
|
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]]:
|
|
return row_item_policy.compose_item(
|
|
rng,
|
|
category,
|
|
subcategory,
|
|
item,
|
|
women_count,
|
|
men_count,
|
|
)
|
|
|
|
|
|
def _choose_text(rng: random.Random, items: list[Any]) -> str:
|
|
return row_item_policy.choose_text(rng, items)
|
|
|
|
|
|
def _choose_distinct_text(rng: random.Random, items: list[Any], first_text: str) -> str:
|
|
return row_item_policy.choose_distinct_text(rng, items, first_text)
|
|
|
|
|
|
def _choose_pair(rng: random.Random, items: list[Any]) -> tuple[str, str]:
|
|
return row_item_policy.choose_pair(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:
|
|
return row_generation_policy.ratio_or_none(value)
|
|
|
|
|
|
def _clamped_float(value: Any, default: float = 0.5, min_value: float = 0.0, max_value: float = 1.0) -> float:
|
|
return row_generation_policy.clamped_float(value, default, min_value, max_value)
|
|
|
|
|
|
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:
|
|
return row_expression_policy.clean_prompt_punctuation(text)
|
|
|
|
|
|
def _strip_expression_text(text: str, expression: Any = "") -> str:
|
|
return row_expression_policy.strip_expression_text(text, expression)
|
|
|
|
|
|
def _disable_row_expression(row: dict[str, Any], source: str = "disabled") -> dict[str, Any]:
|
|
return row_expression_policy.disable_row_expression(row, source)
|
|
|
|
|
|
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 character_policy.character_figure_choices()
|
|
|
|
|
|
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:
|
|
return row_generation_policy.pick_clothing_mode(rng, clothing, minimal_ratio)
|
|
|
|
|
|
def _pick_pose_mode(rng: random.Random, poses: str, standard_ratio: float | None) -> str:
|
|
return row_generation_policy.pick_pose_mode(rng, poses, standard_ratio)
|
|
|
|
|
|
def _pick_figure_bias(rng: random.Random, figure: str) -> str:
|
|
return row_generation_policy.pick_figure_bias(rng, figure)
|
|
|
|
|
|
def _pick_expression_intensity(rng: random.Random, expression_intensity: Any) -> tuple[float, str]:
|
|
return row_generation_policy.pick_expression_intensity(rng, expression_intensity)
|
|
|
|
|
|
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]:
|
|
return row_generation_policy.build_auto_weighted_row(
|
|
row_number,
|
|
start_index,
|
|
clothing,
|
|
ethnicity,
|
|
poses,
|
|
backside_bias,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
minimal_clothing_ratio,
|
|
standard_pose_ratio,
|
|
seed,
|
|
)
|
|
|
|
|
|
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]:
|
|
return row_generation_policy.build_direct_builtin_row(
|
|
category,
|
|
row_number,
|
|
start_index,
|
|
clothing,
|
|
ethnicity,
|
|
poses,
|
|
backside_bias,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
minimal_clothing_ratio,
|
|
standard_pose_ratio,
|
|
seed,
|
|
)
|
|
|
|
|
|
def _auto_full_choice(seed_config: dict[str, int], seed: int, row_number: int) -> str:
|
|
return row_generation_policy.auto_full_choice(seed_config, seed, row_number)
|
|
|
|
|
|
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:
|
|
return character_slot_policy.normalize_slot_expression_intensity(value)
|
|
|
|
|
|
def _slot_expression_enabled(slot: dict[str, Any] | None) -> bool:
|
|
return character_slot_policy.slot_expression_enabled(slot)
|
|
|
|
|
|
def _slot_expression_intensity(slot: dict[str, Any] | None) -> float | None:
|
|
return character_slot_policy.slot_expression_intensity(slot)
|
|
|
|
|
|
def _slot_expression_intensity_for_phase(slot: dict[str, Any] | None, phase: str = "") -> float | None:
|
|
return character_slot_policy.slot_expression_intensity_for_phase(slot, phase)
|
|
|
|
|
|
def _normalize_slot_seed(value: Any) -> int:
|
|
return character_policy.normalize_slot_seed(value)
|
|
|
|
|
|
def _slot_seed(slot: dict[str, Any] | None) -> int:
|
|
return character_slot_policy.slot_seed(slot)
|
|
|
|
|
|
def _slot_seeded_rng(slot: dict[str, Any] | None, salt: int) -> random.Random | None:
|
|
return character_slot_policy.slot_seeded_rng(slot, salt)
|
|
|
|
|
|
def _slot_context_rng(slot: dict[str, Any], fallback_rng: random.Random) -> random.Random:
|
|
return character_slot_policy.slot_context_rng(slot, fallback_rng)
|
|
|
|
|
|
def _slot_effective_figure(
|
|
slot: dict[str, Any],
|
|
subject_type: str,
|
|
fallback_figure: str,
|
|
) -> str:
|
|
return character_slot_policy.slot_effective_figure(slot, subject_type, fallback_figure)
|
|
|
|
|
|
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]:
|
|
return row_expression_policy.cast_expression_intensity_override(
|
|
fallback,
|
|
label_map,
|
|
women_count,
|
|
men_count,
|
|
expression_phase,
|
|
)
|
|
|
|
|
|
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]:
|
|
return row_expression_policy.character_expression_entries(
|
|
rng,
|
|
expression_pool,
|
|
fallback_intensity,
|
|
label_map,
|
|
women_count,
|
|
men_count,
|
|
expression_phase,
|
|
)
|
|
|
|
|
|
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:
|
|
return character_slot_policy.slot_manual_or_choice(choice, manual_value)
|
|
|
|
|
|
def _normalize_slot_ethnicity(value: Any) -> str:
|
|
return character_slot_policy.normalize_slot_ethnicity(value)
|
|
|
|
|
|
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:
|
|
return character_appearance_policy.hair_descriptor_from_slot(base_hair, slot, rng)
|
|
|
|
|
|
def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
|
return character_slot_policy.normalize_character_slot(slot)
|
|
|
|
|
|
def _parse_character_cast(character_cast: str | dict[str, Any] | list[Any] | None) -> list[dict[str, Any]]:
|
|
return character_slot_policy.parse_character_cast(character_cast)
|
|
|
|
|
|
def _character_slot_summary(slot: dict[str, Any]) -> str:
|
|
return character_slot_policy.character_slot_summary(slot)
|
|
|
|
|
|
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]:
|
|
return character_slot_policy.build_character_slot_json(
|
|
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,
|
|
expression_enabled=expression_enabled,
|
|
expression_intensity=expression_intensity,
|
|
enabled=enabled,
|
|
character_cast=character_cast,
|
|
presence_mode=presence_mode,
|
|
softcore_expression_intensity=softcore_expression_intensity,
|
|
hardcore_expression_intensity=hardcore_expression_intensity,
|
|
softcore_outfit=softcore_outfit,
|
|
hardcore_clothing=hardcore_clothing,
|
|
)
|
|
|
|
|
|
def _slot_explicit_label(slot: dict[str, Any]) -> str:
|
|
return cast_context_policy.explicit_character_slot_label(slot)
|
|
|
|
|
|
def _character_slot_label_map(slots: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
|
|
return cast_context_policy.character_slot_label_map(slots)
|
|
|
|
|
|
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:
|
|
return character_appearance_policy.slot_softcore_outfit(slot, rng)
|
|
|
|
|
|
def _slot_hardcore_clothing(slot: dict[str, Any] | None, rng: random.Random | None = None) -> str:
|
|
return character_appearance_policy.slot_hardcore_clothing(slot, 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]:
|
|
return character_appearance_policy.context_from_character_slot(
|
|
rng,
|
|
slot,
|
|
subject_type,
|
|
ethnicity,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
)
|
|
|
|
|
|
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]:
|
|
return character_appearance_policy.character_context_for_label(
|
|
label,
|
|
label_map,
|
|
rng,
|
|
ethnicity,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
)
|
|
|
|
|
|
def _apply_character_context_to_row(row: dict[str, Any], context: dict[str, Any]) -> dict[str, Any]:
|
|
return character_appearance_policy.apply_character_context_to_row(row, context)
|
|
|
|
|
|
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]]]:
|
|
return pair_cast.cast_descriptor_entries(
|
|
seed_config=seed_config,
|
|
seed=seed,
|
|
row_number=row_number,
|
|
ethnicity=ethnicity,
|
|
figure=figure,
|
|
no_plus_women=no_plus_women,
|
|
no_black=no_black,
|
|
women_count=women_count,
|
|
men_count=men_count,
|
|
character_cast=character_cast,
|
|
primary_descriptor=primary_descriptor,
|
|
parse_character_cast=_parse_character_cast,
|
|
character_slot_label_map=_character_slot_label_map,
|
|
axis_rng=_axis_rng,
|
|
character_context_for_label=_character_context_for_label,
|
|
slot_is_pov=_slot_is_pov,
|
|
)
|
|
|
|
|
|
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]:
|
|
return character_appearance_policy.row_from_character_slot(character_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]:
|
|
return character_appearance_policy.appearance_for_subject(
|
|
rng,
|
|
subject_type,
|
|
ethnicity,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
)
|
|
|
|
|
|
def _count_phrase(count: int, singular: str, plural: str) -> str:
|
|
return cast_context_policy.count_phrase(count, singular, plural)
|
|
|
|
|
|
def _configured_cast_context(women_count: int, men_count: int) -> dict[str, str]:
|
|
return cast_context_policy.configured_cast_context(women_count, men_count)
|
|
|
|
|
|
def _couple_type_from_counts(
|
|
rng: random.Random,
|
|
women_count: int,
|
|
men_count: int,
|
|
) -> tuple[str, str, str, int, int]:
|
|
return cast_context_policy.couple_type_from_counts(
|
|
rng,
|
|
women_count,
|
|
men_count,
|
|
choose=g.choose,
|
|
couple_types=g.COUPLE_TYPES,
|
|
)
|
|
|
|
|
|
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]:
|
|
return subject_context_policy.subject_context(
|
|
rng,
|
|
subject_type,
|
|
ethnicity,
|
|
figure,
|
|
no_plus_women,
|
|
no_black,
|
|
women_count,
|
|
men_count,
|
|
)
|
|
|
|
|
|
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]:
|
|
return row_pool_policy.scene_pool(category, subcategory, item, subject_type, location_config)
|
|
|
|
|
|
def _expression_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any) -> list[Any]:
|
|
return row_pool_policy.expression_pool(category, subcategory, item)
|
|
|
|
|
|
def _expression_intensity_hint(entry: Any) -> float:
|
|
return row_expression_policy.expression_intensity_hint(entry)
|
|
|
|
|
|
def _expression_entries_for_intensity(entries: list[Any], expression_intensity: float) -> list[Any]:
|
|
return row_expression_policy.expression_entries_for_intensity(entries, expression_intensity)
|
|
|
|
|
|
def _pose_pool(category: dict[str, Any], subcategory: dict[str, Any], item: Any, subject_type: str, poses: str) -> list[Any]:
|
|
return row_pool_policy.pose_pool(category, subcategory, item, subject_type, 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]:
|
|
return row_pool_policy.composition_pool(category, subcategory, item, subject_type, composition_config)
|
|
|
|
|
|
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 pair_cast.insta_descriptor_from_row(row)
|
|
|
|
|
|
def _insta_of_descriptor_from_context(context: dict[str, Any]) -> str:
|
|
return pair_cast.insta_descriptor_from_context(context)
|
|
|
|
|
|
def _insta_of_prompt_cast_descriptors(text: str) -> str:
|
|
return pair_cast.prompt_cast_descriptors(text)
|
|
|
|
|
|
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 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,
|
|
axis_rng=_axis_rng,
|
|
character_context_for_label=_character_context_for_label,
|
|
slot_is_pov=_slot_is_pov,
|
|
choose=g.choose,
|
|
slot_softcore_outfit=_slot_softcore_outfit,
|
|
)
|
|
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,
|
|
)
|