Move pair clothing wording policy
This commit is contained in:
@@ -231,9 +231,10 @@ Already isolated:
|
|||||||
camera config selection, same-as-softcore mode, camera-detail override,
|
camera config selection, same-as-softcore mode, camera-detail override,
|
||||||
same-room hard scene continuity, camera-aware composition mutation, POV camera
|
same-room hard scene continuity, camera-aware composition mutation, POV camera
|
||||||
suppression, and row/root camera metadata synchronization.
|
suppression, and row/root camera metadata synchronization.
|
||||||
- pair-level hardcore clothing continuity lives in `pair_clothing.py`,
|
- pair-level clothing policy lives in `pair_clothing.py`, including clothing
|
||||||
including action-aware body-access flags, conflicting outfit-piece cleanup,
|
sentence formatting, body-exposure scene cleanup, action-aware body-access
|
||||||
default visible-men clothing, character-clothing override handling, and final
|
flags, conflicting outfit-piece cleanup, default visible-men clothing,
|
||||||
|
character-clothing override handling, hardcore clothing continuity, and final
|
||||||
root clothing-state assembly.
|
root clothing-state assembly.
|
||||||
- final pair output assembly lives in `pair_output.py`, including soft/hard
|
- final pair output assembly lives in `pair_output.py`, including soft/hard
|
||||||
prompt strings, trigger preservation, negatives, captions, and root metadata
|
prompt strings, trigger preservation, negatives, captions, and root metadata
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ Core helper ownership:
|
|||||||
| `pair_options.py` | Insta/OF option schema/defaults, softcore category/outfit/pose pools, partner outfit pools, clothing-continuity labels, negatives, hardcore cast count policy, and hardcore detail-density directives. |
|
| `pair_options.py` | Insta/OF option schema/defaults, softcore category/outfit/pose pools, partner outfit pools, clothing-continuity labels, negatives, hardcore cast count policy, and hardcore detail-density directives. |
|
||||||
| `pair_rows.py` | Insta/OF soft/hard row creation, softcore expression override resolution, Woman A slot context application, soft outfit/pose overrides, and POV row fields. |
|
| `pair_rows.py` | Insta/OF soft/hard row creation, softcore expression override resolution, Woman A slot context application, soft outfit/pose overrides, and POV row fields. |
|
||||||
| `pair_camera.py` | Insta/OF soft/hard camera route resolution, same-as-softcore camera mode, camera-detail override, camera-aware composition mutation, POV camera suppression, and synchronized row/root camera metadata. |
|
| `pair_camera.py` | Insta/OF soft/hard camera route resolution, same-as-softcore camera mode, camera-detail override, camera-aware composition mutation, POV camera suppression, and synchronized row/root camera metadata. |
|
||||||
| `pair_clothing.py` | Insta/OF hardcore clothing continuity, action-aware body-access flags, conflicting outfit-piece cleanup, default visible-men clothing, and final root clothing-state assembly. |
|
| `pair_clothing.py` | Insta/OF clothing sentence formatting, body-exposure scene cleanup, hardcore clothing continuity, action-aware body-access flags, conflicting outfit-piece cleanup, configured/default visible-person clothing, and final root clothing-state assembly. |
|
||||||
| `pair_output.py` | Insta/OF final pair prompts, trigger preservation, negative prompts, captions, and root pair metadata assembly. |
|
| `pair_output.py` | Insta/OF final pair prompts, trigger preservation, negative prompts, captions, and root pair metadata assembly. |
|
||||||
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction geometry. |
|
| `hardcore_role_graphs.py` | Source role graph construction for hardcore configured-cast rows, including POV-aware interaction geometry. |
|
||||||
| `hardcore_role_fallback.py` | Solo, same-sex, mixed group fallback, and support-partner role graph wording for configured casts. |
|
| `hardcore_role_fallback.py` | Solo, same-sex, mixed group fallback, and support-partner role graph wording for configured casts. |
|
||||||
|
|||||||
+84
-4
@@ -147,6 +147,89 @@ INSTA_OF_HARDCORE_MEN_CLOTHING_VISIBLE = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_pair_punctuation(text: Any) -> str:
|
||||||
|
text = re.sub(r"\s+", " ", str(text or "")).strip()
|
||||||
|
text = re.sub(r"\s+([,.;:])", r"\1", text)
|
||||||
|
text = re.sub(r"(?:,\s*){2,}", ", ", text)
|
||||||
|
text = re.sub(r"\.\s*\.", ".", text)
|
||||||
|
text = re.sub(r":\s*\.", ".", text)
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def body_exposure_scene_text(scene: Any) -> str:
|
||||||
|
text = str(scene or "").strip()
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
replacements = (
|
||||||
|
(r",?\s*\bscattered (?:clothes|clothing)\b", ""),
|
||||||
|
(r",?\s*\bfloor clothes\b", ""),
|
||||||
|
(r"\bclothes scattered\b", "soft floor shadows"),
|
||||||
|
(r",?\s*\bscattered lingerie\b", ""),
|
||||||
|
(r",?\s*\blingerie visible nearby\b", ""),
|
||||||
|
(r"\boutfit racks\b", "mirror shelves"),
|
||||||
|
(r"\bcostume racks\b", "mirror shelves"),
|
||||||
|
(r"\bhanging outfits\b", "hanging fabric"),
|
||||||
|
(r"\bclothing hooks\b", "wall hooks"),
|
||||||
|
(r"\boutfit-check\b", "creator-shot"),
|
||||||
|
(r"\boutfit framing\b", "body framing"),
|
||||||
|
(r"\bfull outfits\b", "full bodies"),
|
||||||
|
(r"\bcoordinated outfits\b", "coordinated posing"),
|
||||||
|
)
|
||||||
|
for pattern, replacement in replacements:
|
||||||
|
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r"\bwith,\s*", "with ", text, flags=re.IGNORECASE)
|
||||||
|
text = re.sub(r",\s*,", ",", text)
|
||||||
|
return _clean_pair_punctuation(text)
|
||||||
|
|
||||||
|
|
||||||
|
def softcore_outfit_sentence(label: str, outfit: str) -> str:
|
||||||
|
outfit = str(outfit or "").strip()
|
||||||
|
if not outfit:
|
||||||
|
return ""
|
||||||
|
lower = outfit.lower()
|
||||||
|
if lower.startswith(("wears ", "wearing ", "in ")):
|
||||||
|
return f"{label} {outfit}"
|
||||||
|
return f"{label} wears {outfit}"
|
||||||
|
|
||||||
|
|
||||||
|
def hardcore_clothing_sentence(label: str, clothing: str) -> str:
|
||||||
|
clothing = str(clothing or "").strip().rstrip(".")
|
||||||
|
if not clothing:
|
||||||
|
return ""
|
||||||
|
lower = clothing.lower()
|
||||||
|
if lower.startswith(("fully nude", "nude")):
|
||||||
|
return f"{label}'s body is fully exposed, bare skin unobstructed"
|
||||||
|
if lower.startswith("partly nude"):
|
||||||
|
return f"{label}'s body is partly exposed"
|
||||||
|
if lower.startswith(("is ", "wears ", "wearing ", "keeps ", "has ", "with ")):
|
||||||
|
return f"{label} {clothing}"
|
||||||
|
return f"{label}'s clothing: {clothing}"
|
||||||
|
|
||||||
|
|
||||||
|
def character_hardcore_clothing_entries(
|
||||||
|
label_map: dict[str, dict[str, Any]],
|
||||||
|
women_count: int,
|
||||||
|
men_count: int,
|
||||||
|
pov_labels: list[str] | None,
|
||||||
|
rng: Any,
|
||||||
|
slot_hardcore_clothing: Callable[[dict[str, Any] | None, Any], str],
|
||||||
|
) -> list[str]:
|
||||||
|
pov_set = set(pov_labels or [])
|
||||||
|
labels = [
|
||||||
|
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||||
|
*[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))],
|
||||||
|
]
|
||||||
|
entries: list[str] = []
|
||||||
|
for label in labels:
|
||||||
|
if label in pov_set:
|
||||||
|
continue
|
||||||
|
clothing = slot_hardcore_clothing(label_map.get(label), rng)
|
||||||
|
sentence = hardcore_clothing_sentence(label, clothing)
|
||||||
|
if sentence:
|
||||||
|
entries.append(sentence)
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
def hardcore_row_access_flags(row: dict[str, Any]) -> dict[str, bool]:
|
def hardcore_row_access_flags(row: dict[str, Any]) -> dict[str, bool]:
|
||||||
axis_values = row.get("item_axis_values")
|
axis_values = row.get("item_axis_values")
|
||||||
axis_text = " ".join(str(value) for value in axis_values.values()) if isinstance(axis_values, dict) else ""
|
axis_text = " ".join(str(value) for value in axis_values.values()) if isinstance(axis_values, dict) else ""
|
||||||
@@ -272,7 +355,6 @@ def default_man_hardcore_clothing_entries(
|
|||||||
rng: Any,
|
rng: Any,
|
||||||
needs_lower_access: bool,
|
needs_lower_access: bool,
|
||||||
choose: Callable[[Any, list[str]], str],
|
choose: Callable[[Any, list[str]], str],
|
||||||
sentence_builder: Callable[[str, str], str],
|
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
pov_set = set(pov_labels or [])
|
pov_set = set(pov_labels or [])
|
||||||
configured_labels = {
|
configured_labels = {
|
||||||
@@ -287,7 +369,7 @@ def default_man_hardcore_clothing_entries(
|
|||||||
label = f"Man {chr(ord('A') + index)}"
|
label = f"Man {chr(ord('A') + index)}"
|
||||||
if label in pov_set or label in configured_labels:
|
if label in pov_set or label in configured_labels:
|
||||||
continue
|
continue
|
||||||
entries.append(sentence_builder(label, choose(rng, pool)))
|
entries.append(hardcore_clothing_sentence(label, choose(rng, pool)))
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
|
||||||
@@ -302,7 +384,6 @@ def resolve_hardcore_pair_clothing(
|
|||||||
rng: Any,
|
rng: Any,
|
||||||
continuity_map: dict[str, str],
|
continuity_map: dict[str, str],
|
||||||
choose: Callable[[Any, list[str]], str],
|
choose: Callable[[Any, list[str]], str],
|
||||||
sentence_builder: Callable[[str, str], str],
|
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
access_flags = hardcore_row_access_flags(hard_row)
|
access_flags = hardcore_row_access_flags(hard_row)
|
||||||
woman_access = "lower" if access_flags["woman_lower"] else "upper" if access_flags["woman_upper"] else ""
|
woman_access = "lower" if access_flags["woman_lower"] else "upper" if access_flags["woman_upper"] else ""
|
||||||
@@ -313,7 +394,6 @@ def resolve_hardcore_pair_clothing(
|
|||||||
rng,
|
rng,
|
||||||
access_flags["man_lower"],
|
access_flags["man_lower"],
|
||||||
choose,
|
choose,
|
||||||
sentence_builder,
|
|
||||||
)
|
)
|
||||||
has_primary_hardcore_clothing = any(entry.startswith("Woman A") for entry in character_hardcore_clothing_entries)
|
has_primary_hardcore_clothing = any(entry.startswith("Woman A") for entry in character_hardcore_clothing_entries)
|
||||||
fallback_state = "" if has_primary_hardcore_clothing else hardcore_clothing_state(
|
fallback_state = "" if has_primary_hardcore_clothing else hardcore_clothing_state(
|
||||||
|
|||||||
+6
-2
@@ -2,6 +2,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import pair_clothing
|
||||||
|
except ImportError: # Allows local smoke tests with top-level imports.
|
||||||
|
import pair_clothing
|
||||||
|
|
||||||
|
|
||||||
BuildPrompt = Callable[..., dict[str, Any]]
|
BuildPrompt = Callable[..., dict[str, Any]]
|
||||||
AxisRng = Callable[[dict[str, int], str, int, int], Any]
|
AxisRng = Callable[[dict[str, int], str, int, int], Any]
|
||||||
@@ -45,7 +50,6 @@ def build_insta_pair_rows(
|
|||||||
softcore_outfit: Callable[[Any, str], str],
|
softcore_outfit: Callable[[Any, str], str],
|
||||||
softcore_pose: Callable[[Any, str], str],
|
softcore_pose: Callable[[Any, str], str],
|
||||||
softcore_item_prompt_label: Callable[[str], str],
|
softcore_item_prompt_label: Callable[[str], str],
|
||||||
body_exposure_scene_text: Callable[[Any], str],
|
|
||||||
pov_prompt_directive: Callable[[list[str]], str],
|
pov_prompt_directive: Callable[[list[str]], str],
|
||||||
pov_composition_prompt: Callable[[Any, list[str]], str],
|
pov_composition_prompt: Callable[[Any, list[str]], str],
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -134,7 +138,7 @@ def build_insta_pair_rows(
|
|||||||
soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore"
|
soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore"
|
||||||
if softcore_level_key == "explicit_nude":
|
if softcore_level_key == "explicit_nude":
|
||||||
soft_row["source_scene_text"] = soft_row.get("source_scene_text") or soft_row.get("scene_text", "")
|
soft_row["source_scene_text"] = soft_row.get("source_scene_text") or soft_row.get("scene_text", "")
|
||||||
soft_row["scene_text"] = body_exposure_scene_text(soft_row.get("scene_text", ""))
|
soft_row["scene_text"] = pair_clothing.body_exposure_scene_text(soft_row.get("scene_text", ""))
|
||||||
soft_row["pov_character_labels"] = (
|
soft_row["pov_character_labels"] = (
|
||||||
pov_character_labels
|
pov_character_labels
|
||||||
if options["softcore_cast"] == "same_as_hardcore"
|
if options["softcore_cast"] == "same_as_hardcore"
|
||||||
|
|||||||
+5
-79
@@ -2476,32 +2476,6 @@ def _pov_composition_prompt(composition: Any, pov_labels: list[str]) -> str:
|
|||||||
return pov_policy.pov_composition_prompt(composition, pov_labels)
|
return pov_policy.pov_composition_prompt(composition, pov_labels)
|
||||||
|
|
||||||
|
|
||||||
def _body_exposure_scene_text(scene: Any) -> str:
|
|
||||||
text = str(scene or "").strip()
|
|
||||||
if not text:
|
|
||||||
return ""
|
|
||||||
replacements = (
|
|
||||||
(r",?\s*\bscattered (?:clothes|clothing)\b", ""),
|
|
||||||
(r",?\s*\bfloor clothes\b", ""),
|
|
||||||
(r"\bclothes scattered\b", "soft floor shadows"),
|
|
||||||
(r",?\s*\bscattered lingerie\b", ""),
|
|
||||||
(r",?\s*\blingerie visible nearby\b", ""),
|
|
||||||
(r"\boutfit racks\b", "mirror shelves"),
|
|
||||||
(r"\bcostume racks\b", "mirror shelves"),
|
|
||||||
(r"\bhanging outfits\b", "hanging fabric"),
|
|
||||||
(r"\bclothing hooks\b", "wall hooks"),
|
|
||||||
(r"\boutfit-check\b", "creator-shot"),
|
|
||||||
(r"\boutfit framing\b", "body framing"),
|
|
||||||
(r"\bfull outfits\b", "full bodies"),
|
|
||||||
(r"\bcoordinated outfits\b", "coordinated posing"),
|
|
||||||
)
|
|
||||||
for pattern, replacement in replacements:
|
|
||||||
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r"\bwith,\s*", "with ", text, flags=re.IGNORECASE)
|
|
||||||
text = re.sub(r",\s*,", ",", text)
|
|
||||||
return _clean_prompt_punctuation(text)
|
|
||||||
|
|
||||||
|
|
||||||
def _slot_softcore_outfit(slot: dict[str, Any] | None, rng: random.Random | None = None) -> str:
|
def _slot_softcore_outfit(slot: dict[str, Any] | None, rng: random.Random | None = None) -> str:
|
||||||
if not slot:
|
if not slot:
|
||||||
return ""
|
return ""
|
||||||
@@ -2524,53 +2498,6 @@ def _slot_hardcore_clothing(slot: dict[str, Any] | None, rng: random.Random | No
|
|||||||
return _characteristic_choice(_parse_characteristics_config(slot.get("characteristics")), "hardcore_clothing", rng)
|
return _characteristic_choice(_parse_characteristics_config(slot.get("characteristics")), "hardcore_clothing", rng)
|
||||||
|
|
||||||
|
|
||||||
def _softcore_outfit_sentence(label: str, outfit: str) -> str:
|
|
||||||
outfit = str(outfit or "").strip()
|
|
||||||
if not outfit:
|
|
||||||
return ""
|
|
||||||
lower = outfit.lower()
|
|
||||||
if lower.startswith(("wears ", "wearing ", "in ")):
|
|
||||||
return f"{label} {outfit}"
|
|
||||||
return f"{label} wears {outfit}"
|
|
||||||
|
|
||||||
|
|
||||||
def _hardcore_clothing_sentence(label: str, clothing: str) -> str:
|
|
||||||
clothing = str(clothing or "").strip().rstrip(".")
|
|
||||||
if not clothing:
|
|
||||||
return ""
|
|
||||||
lower = clothing.lower()
|
|
||||||
if lower.startswith(("fully nude", "nude")):
|
|
||||||
return f"{label}'s body is fully exposed, bare skin unobstructed"
|
|
||||||
if lower.startswith("partly nude"):
|
|
||||||
return f"{label}'s body is partly exposed"
|
|
||||||
if lower.startswith(("is ", "wears ", "wearing ", "keeps ", "has ", "with ")):
|
|
||||||
return f"{label} {clothing}"
|
|
||||||
return f"{label}'s clothing: {clothing}"
|
|
||||||
|
|
||||||
|
|
||||||
def _character_hardcore_clothing_entries(
|
|
||||||
label_map: dict[str, dict[str, Any]],
|
|
||||||
women_count: int,
|
|
||||||
men_count: int,
|
|
||||||
pov_labels: list[str] | None = None,
|
|
||||||
rng: random.Random | None = None,
|
|
||||||
) -> list[str]:
|
|
||||||
pov_set = set(pov_labels or [])
|
|
||||||
labels = [
|
|
||||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
|
||||||
*[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))],
|
|
||||||
]
|
|
||||||
entries: list[str] = []
|
|
||||||
for label in labels:
|
|
||||||
if label in pov_set:
|
|
||||||
continue
|
|
||||||
clothing = _slot_hardcore_clothing(label_map.get(label), rng)
|
|
||||||
sentence = _hardcore_clothing_sentence(label, clothing)
|
|
||||||
if sentence:
|
|
||||||
entries.append(sentence)
|
|
||||||
return entries
|
|
||||||
|
|
||||||
|
|
||||||
def _context_from_character_slot(
|
def _context_from_character_slot(
|
||||||
rng: random.Random,
|
rng: random.Random,
|
||||||
slot: dict[str, Any],
|
slot: dict[str, Any],
|
||||||
@@ -4052,7 +3979,7 @@ def _insta_of_partner_styling(
|
|||||||
label = chr(ord("B") + index)
|
label = chr(ord("B") + index)
|
||||||
full_label = f"Woman {label}"
|
full_label = f"Woman {label}"
|
||||||
outfit = _slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS)
|
outfit = _slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS)
|
||||||
sentence = _softcore_outfit_sentence(full_label, outfit)
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
||||||
if sentence:
|
if sentence:
|
||||||
outfits.append(sentence)
|
outfits.append(sentence)
|
||||||
for index in range(max(0, men_count)):
|
for index in range(max(0, men_count)):
|
||||||
@@ -4061,7 +3988,7 @@ def _insta_of_partner_styling(
|
|||||||
if full_label in pov_set:
|
if full_label in pov_set:
|
||||||
continue
|
continue
|
||||||
outfit = _slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS)
|
outfit = _slot_softcore_outfit((label_map or {}).get(full_label), content_rng) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS)
|
||||||
sentence = _softcore_outfit_sentence(full_label, outfit)
|
sentence = pair_clothing.softcore_outfit_sentence(full_label, outfit)
|
||||||
if sentence:
|
if sentence:
|
||||||
outfits.append(sentence)
|
outfits.append(sentence)
|
||||||
return {
|
return {
|
||||||
@@ -4143,7 +4070,6 @@ def build_insta_of_pair(
|
|||||||
softcore_outfit=_insta_of_softcore_outfit,
|
softcore_outfit=_insta_of_softcore_outfit,
|
||||||
softcore_pose=_insta_of_softcore_pose,
|
softcore_pose=_insta_of_softcore_pose,
|
||||||
softcore_item_prompt_label=_insta_of_softcore_item_prompt_label,
|
softcore_item_prompt_label=_insta_of_softcore_item_prompt_label,
|
||||||
body_exposure_scene_text=_body_exposure_scene_text,
|
|
||||||
pov_prompt_directive=_pov_prompt_directive,
|
pov_prompt_directive=_pov_prompt_directive,
|
||||||
pov_composition_prompt=_pov_composition_prompt,
|
pov_composition_prompt=_pov_composition_prompt,
|
||||||
)
|
)
|
||||||
@@ -4219,12 +4145,13 @@ def build_insta_of_pair(
|
|||||||
soft_cast_presence = cast_context["soft_cast_presence"]
|
soft_cast_presence = cast_context["soft_cast_presence"]
|
||||||
soft_cast_styling_sentence = cast_context["soft_cast_styling_sentence"]
|
soft_cast_styling_sentence = cast_context["soft_cast_styling_sentence"]
|
||||||
hard_cast = cast_context["hard_cast"]
|
hard_cast = cast_context["hard_cast"]
|
||||||
character_hardcore_clothing_entries = _character_hardcore_clothing_entries(
|
character_hardcore_clothing_entries = pair_clothing.character_hardcore_clothing_entries(
|
||||||
character_slot_map,
|
character_slot_map,
|
||||||
hard_women_count,
|
hard_women_count,
|
||||||
hard_men_count,
|
hard_men_count,
|
||||||
pov_character_labels,
|
pov_character_labels,
|
||||||
hard_content_rng,
|
hard_content_rng,
|
||||||
|
_slot_hardcore_clothing,
|
||||||
)
|
)
|
||||||
clothing_route = pair_clothing.resolve_hardcore_pair_clothing(
|
clothing_route = pair_clothing.resolve_hardcore_pair_clothing(
|
||||||
hard_row=hard_row,
|
hard_row=hard_row,
|
||||||
@@ -4236,13 +4163,12 @@ def build_insta_of_pair(
|
|||||||
rng=hard_content_rng,
|
rng=hard_content_rng,
|
||||||
continuity_map=INSTA_OF_HARDCORE_CLOTHING_CONTINUITY,
|
continuity_map=INSTA_OF_HARDCORE_CLOTHING_CONTINUITY,
|
||||||
choose=g.choose,
|
choose=g.choose,
|
||||||
sentence_builder=_hardcore_clothing_sentence,
|
|
||||||
)
|
)
|
||||||
default_man_hardcore_clothing_entries = clothing_route["default_man_hardcore_clothing"]
|
default_man_hardcore_clothing_entries = clothing_route["default_man_hardcore_clothing"]
|
||||||
hard_clothing_state = clothing_route["hardcore_clothing_state"]
|
hard_clothing_state = clothing_route["hardcore_clothing_state"]
|
||||||
hard_clothing_sentence = clothing_route["hardcore_clothing_sentence"]
|
hard_clothing_sentence = clothing_route["hardcore_clothing_sentence"]
|
||||||
if clothing_route["requires_body_exposure_scene"]:
|
if clothing_route["requires_body_exposure_scene"]:
|
||||||
hard_scene = _body_exposure_scene_text(hard_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["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
|
||||||
hard_row["scene_text"] = hard_scene
|
hard_row["scene_text"] = hard_scene
|
||||||
hard_detail_density = options["hardcore_detail_density"]
|
hard_detail_density = options["hardcore_detail_density"]
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import krea_cast # noqa: E402
|
|||||||
import krea_formatter # noqa: E402
|
import krea_formatter # noqa: E402
|
||||||
import location_config # noqa: E402
|
import location_config # noqa: E402
|
||||||
import loop_nodes # noqa: E402
|
import loop_nodes # noqa: E402
|
||||||
|
import pair_clothing # noqa: E402
|
||||||
import prompt_builder as pb # noqa: E402
|
import prompt_builder as pb # noqa: E402
|
||||||
import pov_policy # noqa: E402
|
import pov_policy # noqa: E402
|
||||||
import row_normalization # noqa: E402
|
import row_normalization # noqa: E402
|
||||||
@@ -1645,6 +1646,41 @@ def smoke_pair_options_policy() -> None:
|
|||||||
pb.pair_options.hardcore_detail_directive("bad") == "",
|
pb.pair_options.hardcore_detail_directive("bad") == "",
|
||||||
"invalid hardcore detail density directive should be empty",
|
"invalid hardcore detail density directive should be empty",
|
||||||
)
|
)
|
||||||
|
_expect(
|
||||||
|
"scattered clothes" not in pair_clothing.body_exposure_scene_text(
|
||||||
|
"mirror corner, scattered clothes, outfit-check framing"
|
||||||
|
),
|
||||||
|
"Pair clothing body exposure scene cleanup should remove clothing clutter",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
"creator-shot" in pair_clothing.body_exposure_scene_text("outfit-check framing"),
|
||||||
|
"Pair clothing body exposure scene cleanup should replace outfit-check wording",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pair_clothing.softcore_outfit_sentence("Man A", "wears hoodie and joggers")
|
||||||
|
== "Man A wears hoodie and joggers",
|
||||||
|
"Pair clothing softcore outfit sentence formatting changed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pair_clothing.hardcore_clothing_sentence("Woman A", "fully nude")
|
||||||
|
== "Woman A's body is fully exposed, bare skin unobstructed",
|
||||||
|
"Pair clothing hardcore fully nude sentence formatting changed",
|
||||||
|
)
|
||||||
|
_expect(
|
||||||
|
pair_clothing.character_hardcore_clothing_entries(
|
||||||
|
{
|
||||||
|
"Woman A": {"hardcore_clothing": "fully nude"},
|
||||||
|
"Man A": {"hardcore_clothing": "wears jeans"},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
["Man A"],
|
||||||
|
random.Random(1),
|
||||||
|
lambda slot, _rng: str((slot or {}).get("hardcore_clothing") or ""),
|
||||||
|
)
|
||||||
|
== ["Woman A's body is fully exposed, bare skin unobstructed"],
|
||||||
|
"Pair clothing character entries should skip POV labels",
|
||||||
|
)
|
||||||
options = json.loads(
|
options = json.loads(
|
||||||
pb.build_insta_of_options_json(
|
pb.build_insta_of_options_json(
|
||||||
softcore_expression_enabled="false",
|
softcore_expression_enabled="false",
|
||||||
|
|||||||
Reference in New Issue
Block a user